Skip to content

Commit 630440a

Browse files
authored
[Mirror/Ports] Research queue (#5918)
## About The Pull Request Mirrors/Ports tgstation/tgstation#84731 (I dont really know lingo tbh I just say what feels right) ![image](https://github.com/user-attachments/assets/0eb406bc-9bbf-458a-ba1b-1f08a58fbfb6) - 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 2.5k points node can't be researched if someone queued up a 10k 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. ## 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:
1 parent cd24e9a commit 630440a

File tree

6 files changed

+202
-35
lines changed

6 files changed

+202
-35
lines changed

code/controllers/subsystem/research.dm

+8
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,14 @@ SUBSYSTEM_DEF(research)
118118

119119
techweb_list.last_income = world.time
120120

121+
if(length(techweb_list.research_queue_nodes))
122+
techweb_list.research_node_id(techweb_list.research_queue_nodes[1]) // Attempt to research the first node in queue if possible
123+
124+
for(var/node_id in techweb_list.research_queue_nodes)
125+
var/datum/techweb_node/node = SSresearch.techweb_node_by_id(node_id)
126+
if(node.is_free(techweb_list)) // Automatically research all free nodes in queue if any
127+
techweb_list.research_node(node)
128+
121129
for(var/core_type in slime_core_prices)
122130
var/obj/item/slime_extract/core = core_type
123131
var/price_mod = rand(SLIME_RANDOM_MODIFIER_MIN * 1000000, SLIME_RANDOM_MODIFIER_MAX * 1000000) / 1000000

code/modules/modular_computers/file_system/programs/techweb.dm

+29-1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
return data
4848
data += list(
4949
"nodes" = list(),
50+
"queue_nodes" = stored_research.research_queue_nodes,
5051
"experiments" = list(),
5152
"researched_designs" = stored_research.researched_designs,
5253
"points" = stored_research.research_points,
@@ -61,15 +62,22 @@
6162
// Serialize all nodes to display
6263
for(var/tier in stored_research.tiers)
6364
var/datum/techweb_node/node = SSresearch.techweb_node_by_id(tier)
65+
var/enqueued_by_user = FALSE
66+
67+
if((tier in stored_research.research_queue_nodes) && stored_research.research_queue_nodes[tier] == user)
68+
enqueued_by_user = TRUE
6469

6570
// Ensure node is supposed to be visible
6671
if (stored_research.hidden_nodes[tier])
6772
continue
6873

6974
data["nodes"] += list(list(
7075
"id" = node.id,
76+
"is_free" = node.is_free(stored_research),
7177
"can_unlock" = stored_research.can_unlock_node(node),
72-
"tier" = stored_research.tiers[node.id]
78+
"have_experiments_done" = stored_research.have_experiments_for_node(node),
79+
"tier" = stored_research.tiers[node.id],
80+
"enqueued_by_user" = enqueued_by_user
7381
))
7482

7583
// Get experiments and serialize them
@@ -107,6 +115,12 @@
107115
if ("researchNode")
108116
research_node(params["node_id"], usr)
109117
return TRUE
118+
if ("enqueueNode")
119+
enqueue_node(params["node_id"], usr)
120+
return TRUE
121+
if ("dequeueNode")
122+
dequeue_node(params["node_id"], usr)
123+
return TRUE
110124

111125
/datum/computer_file/program/science/ui_static_data(mob/user)
112126
. = list(
@@ -183,6 +197,20 @@
183197
id_cache_seq += 1
184198
return id_cache[id]
185199

200+
/datum/computer_file/program/science/proc/enqueue_node(id, mob/user)
201+
if(!stored_research || !stored_research.available_nodes[id] || stored_research.researched_nodes[id])
202+
computer.say("Node enqueue failed: Either no techweb is found, node is already researched or is not available!")
203+
return FALSE
204+
stored_research.enqueue_node(id, user)
205+
return TRUE
206+
207+
/datum/computer_file/program/science/proc/dequeue_node(id, mob/user)
208+
if(!stored_research || !stored_research.available_nodes[id] || stored_research.researched_nodes[id])
209+
computer.say("Node dequeue failed: Either no techweb is found, node is already researched or is not available!")
210+
return FALSE
211+
stored_research.dequeue_node(id, user)
212+
return TRUE
213+
186214
/datum/computer_file/program/science/proc/research_node(id, mob/user)
187215
if(!stored_research || !stored_research.available_nodes[id] || stored_research.researched_nodes[id])
188216
computer.say("Node unlock failed: Either no techweb is found, node is already researched or is not available!")

code/modules/research/rdconsole.dm

+28
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,20 @@ Nothing else in the console has ID requirements.
9696
stored_research = tool.buffer
9797
return TRUE
9898

99+
/obj/machinery/computer/rdconsole/proc/enqueue_node(id, mob/user)
100+
if(!stored_research || !stored_research.available_nodes[id] || stored_research.researched_nodes[id])
101+
say("Node enqueue failed: Either no techweb is found, node is already researched or is not available!")
102+
return FALSE
103+
stored_research.enqueue_node(id, user)
104+
return TRUE
105+
106+
/obj/machinery/computer/rdconsole/proc/dequeue_node(id, mob/user)
107+
if(!stored_research || !stored_research.available_nodes[id] || stored_research.researched_nodes[id])
108+
say("Node dequeue failed: Either no techweb is found, node is already researched or is not available!")
109+
return FALSE
110+
stored_research.dequeue_node(id, user)
111+
return TRUE
112+
99113
/obj/machinery/computer/rdconsole/proc/research_node(id, mob/user)
100114
if(!stored_research || !stored_research.available_nodes[id] || stored_research.researched_nodes[id])
101115
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.
171185
return data
172186
data += list(
173187
"nodes" = list(),
188+
"queue_nodes" = stored_research.research_queue_nodes,
174189
"experiments" = list(),
175190
"researched_designs" = stored_research.researched_designs,
176191
"points" = stored_research.research_points,
@@ -194,15 +209,22 @@ Nothing else in the console has ID requirements.
194209
// Serialize all nodes to display
195210
for(var/v in stored_research.tiers)
196211
var/datum/techweb_node/n = SSresearch.techweb_node_by_id(v)
212+
var/enqueued_by_user = FALSE
213+
214+
if((v in stored_research.research_queue_nodes) && stored_research.research_queue_nodes[v] == user)
215+
enqueued_by_user = TRUE
197216

198217
// Ensure node is supposed to be visible
199218
if (stored_research.hidden_nodes[v])
200219
continue
201220

202221
data["nodes"] += list(list(
203222
"id" = n.id,
223+
"is_free" = n.is_free(stored_research),
204224
"can_unlock" = stored_research.can_unlock_node(n),
225+
"have_experiments_done" = stored_research.have_experiments_for_node(n),
205226
"tier" = stored_research.tiers[n.id],
227+
"enqueued_by_user" = enqueued_by_user
206228
))
207229

208230
// Get experiments and serialize them
@@ -321,6 +343,12 @@ Nothing else in the console has ID requirements.
321343
if ("researchNode")
322344
research_node(params["node_id"], usr)
323345
return TRUE
346+
if ("enqueueNode")
347+
enqueue_node(params["node_id"], usr)
348+
return TRUE
349+
if ("dequeueNode")
350+
dequeue_node(params["node_id"], usr)
351+
return TRUE
324352
if ("ejectDisk")
325353
eject_disk(params["type"])
326354
return TRUE

code/modules/research/techweb/_techweb.dm

+43-1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@
7272
* Filled with nulls on init, populated only on publication.
7373
*/
7474
var/list/published_papers
75+
/**
76+
* Assoc list of nodes queued for automatic research when there are enough points available
77+
* research_queue_nodes[node_id] = user_enqueued
78+
*/
79+
var/list/research_queue_nodes = list()
7580

7681
/datum/techweb/New()
7782
SSresearch.techwebs += src
@@ -326,6 +331,40 @@
326331
/datum/techweb/proc/printout_points()
327332
return techweb_point_display_generic(research_points)
328333

334+
/datum/techweb/proc/enqueue_node(id, mob/user)
335+
var/mob/living/carbon/human/human_user = user
336+
var/is_rd = FALSE
337+
if(human_user.wear_id)
338+
var/list/access = human_user.wear_id.GetAccess()
339+
if(ACCESS_RD in access)
340+
is_rd = TRUE
341+
342+
if(id in research_queue_nodes)
343+
if(is_rd)
344+
research_queue_nodes.Remove(id)
345+
else
346+
return FALSE
347+
348+
for(var/node_id in research_queue_nodes)
349+
if(research_queue_nodes[node_id] == user)
350+
research_queue_nodes.Remove(node_id)
351+
352+
if (is_rd)
353+
research_queue_nodes.Insert(1, id)
354+
research_queue_nodes[id] = user
355+
356+
return TRUE
357+
358+
/datum/techweb/proc/dequeue_node(id, mob/user)
359+
if(!(id in research_queue_nodes))
360+
return FALSE
361+
if(research_queue_nodes[id] != user)
362+
return FALSE
363+
364+
research_queue_nodes.Remove(id)
365+
366+
return TRUE
367+
329368
/datum/techweb/proc/research_node_id(id, force, auto_update_points, get_that_dosh_id)
330369
return research_node(SSresearch.techweb_node_by_id(id), force, auto_update_points, get_that_dosh_id)
331370

@@ -373,7 +412,10 @@
373412
// Avoid logging the same 300+ lines at the beginning of every round
374413
if (MC_RUNNING())
375414
log_research(log_message)
376-
415+
416+
// Dequeue
417+
if(node.id in research_queue_nodes)
418+
research_queue_nodes.Remove(node.id)
377419
return TRUE
378420

379421
/datum/techweb/proc/unresearch_node_id(id)

code/modules/research/techweb/_techweb_node.dm

+12-1
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,20 @@
8888
if(actual_costs[booster])
8989
var/delta = max(0, actual_costs[booster] - 250)
9090
actual_costs[booster] -= min(boostlist[booster], delta)
91-
91+
9292
return actual_costs
9393

94+
/datum/techweb_node/proc/is_free(datum/techweb/host)
95+
var/list/costs = get_price(host)
96+
var/total_points = 0
97+
98+
for(var/point_type in costs)
99+
total_points += costs[point_type]
100+
101+
if(total_points == 0)
102+
return TRUE
103+
return FALSE
104+
94105
/datum/techweb_node/proc/price_display(datum/techweb/TN)
95106
return techweb_point_display_generic(get_price(TN))
96107

tgui/packages/tgui/interfaces/Techweb.jsx

+82-32
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
Tabs,
88
Box,
99
Input,
10+
LabeledList,
1011
Flex,
1112
ProgressBar,
1213
Collapsible,
@@ -152,6 +153,8 @@ export const TechwebContent = (props) => {
152153
t_disk,
153154
d_disk,
154155
locked,
156+
queue_nodes = [],
157+
node_cache,
155158
} = data;
156159
const [techwebRoute, setTechwebRoute] = useLocalState('techwebRoute', null);
157160
const [lastPoints, setLastPoints] = useLocalState('lastPoints', {});
@@ -161,27 +164,35 @@ export const TechwebContent = (props) => {
161164
<Flex.Item className="Techweb__HeaderSection">
162165
<Flex className="Techweb__HeaderContent">
163166
<Flex.Item>
164-
<Box>
165-
Available points:
166-
<ul className="Techweb__PointSummary">
167-
{Object.keys(points).map((k) => (
168-
<li key={k}>
169-
<b>{k}</b>: {points[k]}
170-
{!!points_last_tick[k] && ` (+${points_last_tick[k]}/sec)`}
171-
</li>
172-
))}
173-
</ul>
174-
</Box>
175-
<Box>
176-
Security protocols:
177-
<span
178-
className={`Techweb__SecProtocol ${
179-
!!sec_protocols && 'engaged'
180-
}`}
181-
>
182-
{sec_protocols ? 'Engaged' : 'Disengaged'}
183-
</span>
184-
</Box>
167+
<LabeledList>
168+
<LabeledList.Item label="Security">
169+
<span
170+
className={`Techweb__SecProtocol ${
171+
!!sec_protocols && 'engaged'
172+
}`}
173+
>
174+
{sec_protocols ? 'Engaged' : 'Disengaged'}
175+
</span>
176+
</LabeledList.Item>
177+
{Object.keys(points).map((k) => (
178+
<LabeledList.Item key={k}>
179+
<b>{k}</b>: {points[k]}
180+
{!!points_last_tick[k] && ` (+${points_last_tick[k]}/sec)`}
181+
</LabeledList.Item>
182+
))}
183+
<LabeledList.Item label="Queue">
184+
{queue_nodes.length !== 0
185+
? Object.keys(queue_nodes).map((node_id) => (
186+
<Button
187+
key={node_id}
188+
tooltip={`Added by: ${queue_nodes[node_id]}`}
189+
>
190+
{node_cache[node_id].name}
191+
</Button>
192+
))
193+
: 'Empty'}
194+
</LabeledList.Item>
195+
</LabeledList>
185196
</Flex.Item>
186197
<Flex.Item grow={1} />
187198
<Flex.Item>
@@ -488,9 +499,23 @@ const TechNodeDetail = (props) => {
488499

489500
const TechNode = (props) => {
490501
const { act, data } = useRemappedBackend();
491-
const { node_cache, design_cache, experiments, points, nodes } = data;
502+
const {
503+
node_cache,
504+
design_cache,
505+
experiments,
506+
points,
507+
nodes,
508+
queue_nodes = [],
509+
} = data;
492510
const { node, nodetails, nocontrols } = props;
493-
const { id, can_unlock, tier } = node;
511+
const {
512+
id,
513+
can_unlock,
514+
have_experiments_done,
515+
tier,
516+
enqueued_by_user,
517+
is_free,
518+
} = node;
494519
const {
495520
name,
496521
description,
@@ -550,6 +575,40 @@ const TechNode = (props) => {
550575
buttons={
551576
!nocontrols && (
552577
<>
578+
{tier > 0 &&
579+
(!!can_unlock && (is_free || queue_nodes.length === 0) ? (
580+
<Button
581+
icon="lightbulb"
582+
disabled={!can_unlock || tier > 1 || queue_nodes.length > 0}
583+
onClick={() => act('researchNode', { node_id: id })}
584+
>
585+
Research
586+
</Button>
587+
) : enqueued_by_user ? (
588+
<Button
589+
icon="trash"
590+
color="bad"
591+
onClick={() => act('dequeueNode', { node_id: id })}
592+
>
593+
Dequeue
594+
</Button>
595+
) : id in queue_nodes && !enqueued_by_user ? (
596+
<Button icon="check" color="good">
597+
Queued
598+
</Button>
599+
) : (
600+
<Button
601+
icon="lightbulb"
602+
disabled={
603+
!have_experiments_done ||
604+
id in queue_nodes ||
605+
techcompl < prereq_ids.length
606+
}
607+
onClick={() => act('enqueueNode', { node_id: id })}
608+
>
609+
Enqueue
610+
</Button>
611+
))}
553612
{!nodetails && (
554613
<Button
555614
icon="tasks"
@@ -561,15 +620,6 @@ const TechNode = (props) => {
561620
Details
562621
</Button>
563622
)}
564-
{tier > 0 && (
565-
<Button
566-
icon="lightbulb"
567-
disabled={!can_unlock || tier > 1}
568-
onClick={() => act('researchNode', { node_id: id })}
569-
>
570-
Research
571-
</Button>
572-
)}
573623
</>
574624
)
575625
}

0 commit comments

Comments
 (0)