Description - if some research lines have several steps of research, and, another research line has dependency from, for example first step of "frits" lines, the result dependencies will show as a "full mesh", i.e. a mess.
It's simpler to show on example:
In vanilla game exists a several research lines related to ship mods. Will show them using their appropriate wares macros:
Code: Select all
research_mod_ship_mk1 -> research_mod_ship_mk2 -> research_mod_ship_mk3
research_mod_engine_mk1 -> research_mod_engine_mk2 -> research_mod_engine_mk3
research_mod_shield_mk1 -> research_mod_shield_mk2 -> research_mod_shield_mk3
research_mod_weapon_mk1 -> research_mod_weapon_mk2 -> research_mod_weapon_mk3

But if we will add new research dependent of one, two, all or off them - we will have a real "full mesh", i.e. full mess with displayed dependencies.
There is a ware:
Code: Select all
<ware id="research_test_ware_for_display_fix" name="Test research" description="This is simple test research for the Research dependency display fix" transport="research" volume="1"
tags="research" sortorder="800">
<price min="1" average="1" max="1" />
<research time="10">
<research>
<ware ware="research_mod_ship_mk1" />
<ware ware="research_mod_engine_mk1" />
<ware ware="research_mod_shield_mk1" />
<ware ware="research_mod_weapon_mk1"/>
</research>
</research>
</ware>
And what we have in game in this case:

As you can see - it is a real mess.
There in addition two examples with one and two dependencies:


It happens due to current logic collect "all" steps. Again, it simpler to provide a path, instead of long explanation.
There is patch(diff) as code:
Spoiler
Show
Code: Select all
--- ui.original/menu_research.lua 2025-08-18 16:40:24.000000000 +0300
+++ ui.modified/menu_research.lua 2025-11-15 21:37:59.832444800 +0200
@@ -116,7 +116,9 @@
if not GetWareData(temptechlist[i], "ismissiononly") then
-- print("found " .. temptechlist[i])
local state_completed = C.HasResearched(temptechlist[i])
- table.insert(menu.techtree, { [1] = { [1] = { tech = temptechlist[i], sortorder = sortorder, completed = state_completed } } })
+ -- start fix: added empty precursor list
+ table.insert(menu.techtree, { [1] = { [1] = { tech = temptechlist[i], sortorder = sortorder, completed = state_completed, precursors = {} } } })
+ -- end fix: added empty precursor list
end
table.remove(temptechlist, i)
else
@@ -130,7 +132,9 @@
if hasonlymissionprecursors then
-- print("found with only mission precursors" .. temptechlist[i])
local state_completed = C.HasResearched(temptechlist[i])
- table.insert(menu.techtree, { [1] = { [1] = { tech = temptechlist[i], sortorder = sortorder, completed = state_completed } } })
+ -- start fix: added empty precursor list
+ table.insert(menu.techtree, { [1] = { [1] = { tech = temptechlist[i], sortorder = sortorder, completed = state_completed, precursors = {} } } })
+ -- end fix: added empty precursor list
table.remove(temptechlist, i)
end
end
@@ -177,12 +181,26 @@
-- add this tech to the tree and remove it from the list
local state_completed = C.HasResearched(temptechlist[idx])
+ -- start fix: build new entry with precursor links
+ local newentry = { tech = temptechlist[idx], sortorder = sortorder, completed = state_completed, precursors = {} }
+ for _, precursor in ipairs(techprecursors) do
+ local precursorMainIdx, precursorCol, precursorTechIdx = menu.findTech(menu.techtree, precursor)
+ if precursorMainIdx and precursorCol and precursorTechIdx then
+ newentry.precursors[#newentry.precursors + 1] = menu.techtree[precursorMainIdx][precursorCol]
+ [precursorTechIdx]
+ end
+ end
+ -- end fix: build new entry with precursor links
if menu.techtree[smallestMainIdx][foundPrecusorCol + 1] then
-- print(" adding")
- table.insert(menu.techtree[smallestMainIdx][foundPrecusorCol + 1], { tech = temptechlist[idx], sortorder = sortorder, completed = state_completed })
+ -- start fix: adding the data with precursor links
+ table.insert(menu.techtree[smallestMainIdx][foundPrecusorCol + 1], newentry)
+ -- end fix: adding the data with precursor links
else
-- print(" new entry")
- menu.techtree[smallestMainIdx][foundPrecusorCol + 1] = { [1] = { tech = temptechlist[idx], sortorder = sortorder, completed = state_completed } }
+ -- start fix: replacing the data with precursor links
+ menu.techtree[smallestMainIdx][foundPrecusorCol + 1] = { [1] = newentry }
+ -- end fix: replacing the data with precursor links
end
-- print(" removed")
table.remove(temptechlist, idx)
@@ -365,10 +383,18 @@
menu.restoreNode = techentry.node
menu.restoreNodeTech = nil
end
-
- if col > 1 then
- for k, previousentry in ipairs(mainentry[col - 1]) do
- -- print("adding edge from node " .. previousentry.tech .. " to " .. techentry.tech)
+ -- start fix: added predecessors handling to apply dependency fix
+ local predecessors = techentry.precursors or {}
+ if (#predecessors == 0) and (col > 1) then
+ local fallbackColumn = mainentry[col - 1]
+ if fallbackColumn then
+ predecessors = fallbackColumn
+ end
+ end
+ for k = 1, #predecessors do
+ local previousentry = predecessors[k]
+ if previousentry.node then
+ --end fix: added predecessors handling to apply dependency fix
local edge = previousentry.node:addEdgeTo(techentry.node)
if not previousentry.completed then
edge.properties.sourceSlotColor = Color["research_incomplete"]
@@ -913,12 +939,23 @@
function menu.isResearchAvailable(tech, mainIdx, col)
if menu.availableresearchmodule then
if col > 1 then
- for _, techentry in ipairs(menu.techtree[mainIdx][col - 1]) do
- if not techentry.completed then
- return false
+ -- start fix: finding current tech and checking its predecessors
+ local currentColumn = menu.techtree[mainIdx][col]
+ for i = 1, #currentColumn do
+ local techentry = currentColumn[i]
+ if techentry and techentry.tech == tech then
+ if techentry.precursors and #techentry.precursors > 0 then
+ for i = 1, #techentry.precursors do
+ local precursor = techentry.precursors[i]
+ if not precursor.completed then
+ return false
+ end
+ end
+ end
end
end
+ -- end fix: finding current tech and checking its predecessors
end
return true
end
And there an image how it has to be (after patch is applied):



