Fix two skill-tree nodes drawn on top of each other (#70)

Squadron Doctrine and Power Systems both sat at depth 3, x=0.50,
landing on the exact same screen position - Squadron's own
prerequisite chain (coordination -> wing -> squadron -> armada) zigzags
x from 0.50 to 0.82 back to 0.50, colliding with Freight's branch at
that depth. Kept the whole fleet-size doctrine branch at x=0.82 (wing's
own lane) instead, so it runs straight.

Added a structural regression test: no two SKILL_TREE nodes may share
the same (skill_depth, x), computed the same way SkillTreeView itself
places nodes.
This commit is contained in:
Loïc Wisniewski 2026-08-17 19:46:33 +02:00
commit eaeb6bf7a9
2 changed files with 26 additions and 2 deletions

View file

@ -329,13 +329,19 @@ const SKILL_TREE: Array = [
"desc": "Permanent: +1 vessel in the fleet.",
},
{
# #70: was x=0.50 - the same lane freight's branch already occupies at
# this same depth (power_systems), so the two nodes were drawn
# directly on top of each other. Kept in line with wing (its own
# prerequisite, also x=0.82) instead, so the whole fleet-size
# doctrine branch now runs straight rather than zigzagging into
# someone else's lane.
"id": "squadron", "name": "Squadron Doctrine", "cost": 70, "requires": "wing",
"x": 0.50, "fleet_bonus": 1, "research_points": 2,
"x": 0.82, "fleet_bonus": 1, "research_points": 2,
"desc": "Permanent: +1 vessel in the fleet.",
},
{
"id": "armada", "name": "Armada Doctrine", "cost": 150, "requires": "squadron",
"x": 0.50, "fleet_bonus": 1, "research_points": 3,
"x": 0.82, "fleet_bonus": 1, "research_points": 3,
"desc": "Permanent: +1 vessel in the fleet.",
},
]

View file

@ -29,6 +29,24 @@ var elapsed: float = 0.0
func _initialize() -> void:
game_state = root.get_node("GameState")
# --- #70: no two nodes drawn on top of each other -------------------
# SkillTreeView.node_screen_pos() places a node purely from its own
# (depth, x) - two nodes sharing both values land on the exact same
# pixel with no visual way to tell them apart. depth is derived from
# `requires` chains, not authored directly, so this has to check the
# same way the view does rather than eyeballing the data.
var seen: Dictionary = {}
for node in GameState.SKILL_TREE:
var key: String = "%d:%s" % [game_state.skill_depth(node.id), node.x]
if seen.has(key):
print("TEST_SKILL_TREE: FAIL (%s and %s are both at depth/x %s - drawn on top of each other)" \
% [seen[key], node.id, key])
quit(1)
return
seen[key] = node.id
print("no two skill-tree nodes share the same (depth, x)")
game_state.rocks = 500
game_state.buildings.clear()
game_state.extractors.clear()