Skip to content

Commit

Permalink
[MIRROR] Research queue [MDB IGNORE] (#4488)
Browse files Browse the repository at this point in the history
* Research queue (#84731)

## About The Pull Request

<img alt="2dZbpr8MK1"
src="https://github.com/tgstation/tgstation/assets/3625094/dd78feba-224a-41a1-8d4a-83af3a8b68df">


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: NovaBot <[email protected]>
Co-authored-by: Andrew <[email protected]>
Co-authored-by: NovaBot13 <[email protected]>
  • Loading branch information
4 people authored Jul 15, 2024
1 parent 21b50eb commit 38561f7
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 33 deletions.
9 changes: 8 additions & 1 deletion code/controllers/subsystem/research.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -103,6 +103,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]
Expand Down
30 changes: 29 additions & 1 deletion code/modules/modular_computers/file_system/programs/techweb.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -64,15 +65,22 @@
// 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])
continue

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
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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!")
Expand Down
28 changes: 28 additions & 0 deletions code/modules/research/rdconsole.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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!")
Expand Down Expand Up @@ -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,
Expand All @@ -194,15 +209,22 @@ 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])
continue

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
Expand Down Expand Up @@ -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
Expand Down
44 changes: 44 additions & 0 deletions code/modules/research/techweb/_techweb.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand Down
11 changes: 11 additions & 0 deletions code/modules/research/techweb/_techweb_node.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
106 changes: 75 additions & 31 deletions tgui/packages/tgui/interfaces/Techweb.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Flex,
Icon,
Input,
LabeledList,
Modal,
ProgressBar,
Section,
Expand Down Expand Up @@ -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({});
Expand All @@ -156,27 +159,35 @@ export const TechwebContent = (props) => {
<Flex.Item className="Techweb__HeaderSection">
<Flex className="Techweb__HeaderContent">
<Flex.Item>
<Box>
Available points:
<ul className="Techweb__PointSummary">
{Object.keys(points).map((k) => (
<li key={k}>
<b>{k}</b>: {points[k]}
{!!points_last_tick[k] && ` (+${points_last_tick[k]}/sec)`}
</li>
))}
</ul>
</Box>
<Box>
Security protocols:
<span
className={`Techweb__SecProtocol ${
!!sec_protocols && 'engaged'
}`}
>
{sec_protocols ? 'Engaged' : 'Disengaged'}
</span>
</Box>
<LabeledList>
<LabeledList.Item label="Security">
<span
className={`Techweb__SecProtocol ${
!!sec_protocols && 'engaged'
}`}
>
{sec_protocols ? 'Engaged' : 'Disengaged'}
</span>
</LabeledList.Item>
{Object.keys(points).map((k) => (
<LabeledList.Item key={k} label="Points">
<b>{points[k]}</b>
{!!points_last_tick[k] && ` (+${points_last_tick[k]}/sec)`}
</LabeledList.Item>
))}
<LabeledList.Item label="Queue">
{queue_nodes.length !== 0
? Object.keys(queue_nodes).map((node_id) => (
<Button
key={node_id}
tooltip={`Added by: ${queue_nodes[node_id]}`}
>
{node_cache[node_id].name}
</Button>
))
: 'Empty'}
</LabeledList.Item>
</LabeledList>
</Flex.Item>
<Flex.Item grow={1} />
<Flex.Item>
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -555,6 +574,40 @@ const TechNode = (props) => {
buttons={
!nocontrols && (
<>
{tier > 0 &&
(!!can_unlock && (is_free || queue_nodes.length === 0) ? (
<Button
icon="lightbulb"
disabled={!can_unlock || tier > 1 || queue_nodes.length > 0}
onClick={() => act('researchNode', { node_id: id })}
>
Research
</Button>
) : enqueued_by_user ? (
<Button
icon="trash"
color="bad"
onClick={() => act('dequeueNode', { node_id: id })}
>
Dequeue
</Button>
) : id in queue_nodes && !enqueued_by_user ? (
<Button icon="check" color="good">
Queued
</Button>
) : (
<Button
icon="lightbulb"
disabled={
!have_experiments_done ||
id in queue_nodes ||
techcompl < prereq_ids.length
}
onClick={() => act('enqueueNode', { node_id: id })}
>
Enqueue
</Button>
))}
{!nodetails && (
<Button
icon="tasks"
Expand All @@ -566,15 +619,6 @@ const TechNode = (props) => {
Details
</Button>
)}
{tier > 0 && (
<Button
icon="lightbulb"
disabled={!can_unlock || tier > 1}
onClick={() => act('researchNode', { node_id: id })}
>
Research
</Button>
)}
</>
)
}
Expand Down

0 comments on commit 38561f7

Please sign in to comment.