Cap tether progression at level 3 and remove tether_l4 research node
- Reduce maxLevel from 12 to 3 in data/tether.json - Remove the tether_l4 entry from exploration.json; tether_l3 now has empty unlocks - Update research-builds and tether tests to reflect the shorter chain (3 layout rows, level checks for 1–3 only) - Revise PROJECT_NOTES.md to describe the 3-level progression
This commit is contained in:
parent
cb82da4935
commit
d9fc9df829
|
|
@ -26,17 +26,8 @@
|
|||
"icon": "tether",
|
||||
"duration": 150,
|
||||
"requires": ["tether_l2"],
|
||||
"unlocks": { "builds": [], "research": ["tether_l4"] },
|
||||
"effects": { "tether": { "level": 3 } }
|
||||
},
|
||||
"tether_l4": {
|
||||
"label": "Tether Level 4",
|
||||
"description": "Fourth stage: 40,960 m. At this level every object of a ten-body system — all the worlds and stations — lies inside one ring around your anchor, and the barrier stops meaning anything out here.",
|
||||
"icon": "tether",
|
||||
"duration": 300,
|
||||
"requires": ["tether_l3"],
|
||||
"unlocks": { "builds": [], "research": [] },
|
||||
"effects": { "tether": { "level": 4 } }
|
||||
"effects": { "tether": { "level": 3 } }
|
||||
},
|
||||
"tether_anchors": {
|
||||
"label": "Tether Anchoring",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"_comment": "TETHER — the player's range. A tether anchors on a world or station and defines a circular zone: radius = distance from the anchor's center. Level 1 = 5120 px (design rule); radius(level) = level1Radius × radiusGrowth^(level−1). The ship may fly anywhere in the UNION of its tethers' zones — where zones overlap there is no line and no wall; outside all of them is a hard barrier, drawn as a thick cyberpunk glitch dotted line along the union's boundary. homeId/homeLevel/homeLabel = the tether the player starts with (anchored on the home world, which sits at the system origin). line = the barrier's look & motion (dash/gap px along the rim, thicknesses, march speed = how fast dashes flow, colors/alphas = the neon passes, glitch = ambient bursts: dash flicker, radial displacement, sparks). contact = feedback when the ship bumps the barrier (pulse = the local shudder of the line; toastCooldown = console warning throttle, ms).",
|
||||
"level1Radius": 5120,
|
||||
"radiusGrowth": 2.0,
|
||||
"maxLevel": 12,
|
||||
"maxLevel": 3,
|
||||
"homeId": "home",
|
||||
"homeLevel": 1,
|
||||
"homeLabel": null,
|
||||
|
|
|
|||
|
|
@ -116,15 +116,16 @@ for (const id of Object.keys(nodes)) {
|
|||
check('exploration: the tree is a DAG (no cycles)', !cyclic);
|
||||
check('exploration: has at least one root (a node with no requires)', Object.values(nodes).some((n) => !(n.requires ?? []).length));
|
||||
|
||||
// tether level chain: L2 is a BUILD (research unlocks it, the build
|
||||
// applies it — data/builds.json → tether-l2), so it carries no immediate
|
||||
// effect; L3/L4 still apply their tether raise on research completion.
|
||||
// tether level chain (3 levels total): L2 is a BUILD (research unlocks
|
||||
// it, the build applies it — data/builds.json → tether-l2), so it carries
|
||||
// no immediate effect; L3 still applies its tether raise on research
|
||||
// completion.
|
||||
const l2NoEffect = !nodes.tether_l2 || !nodes.tether_l2.effects?.tether;
|
||||
const levelsOk = [3, 4].every((lvl) => {
|
||||
const levelsOk = [3].every((lvl) => {
|
||||
const n = nodes[`tether_l${lvl}`];
|
||||
return n && n.effects?.tether?.level === lvl;
|
||||
});
|
||||
check('exploration: tether_l3/l4 each raise the tether to their level', levelsOk);
|
||||
check('exploration: tether_l3 raises the tether to level 3', levelsOk);
|
||||
check('exploration: tether_l2 carries no immediate tether effect (the build applies it)', l2NoEffect);
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
|
@ -145,15 +146,15 @@ check('model: issues(tree) is clean', Array.isArray(issues(tree)) && issues(tree
|
|||
// two declarations together.
|
||||
const { unlocksOf, buildDefs, unlockIssues, buildIssues } = await import('../js/research/ResearchModel.js');
|
||||
check('model: unlocksOf normalizes {builds, research}', JSON.stringify(unlocksOf(tree, 'tether_l2')) === JSON.stringify({ builds: ['tether-l2'], research: ['tether_l3', 'tether_anchors'] }));
|
||||
check('model: unlocksOf is empty for a node without unlocks', JSON.stringify(unlocksOf(tree, 'tether_l4')) === JSON.stringify({ builds: [], research: [] }));
|
||||
check('model: unlocksOf is empty for a node without unlocks', JSON.stringify(unlocksOf(tree, 'signal_amp')) === JSON.stringify({ builds: [], research: [] }));
|
||||
check('model: unlockIssues(tree) is clean (mirrors + build wiring agree)', unlockIssues(tree).length === 0);
|
||||
check('model: buildIssues() is clean (every build require resolves)', buildIssues().length === 0);
|
||||
|
||||
const layout = layoutTree(tree);
|
||||
check('model: layout rows = 4 (l1 → l2/anchor/signal → l3 → l4)', layout.rows === 4);
|
||||
check('model: layout levels — l1 root, l2/signal tier 1, l3/anchors tier 2, l4 tier 3',
|
||||
check('model: layout rows = 3 (l1 → l2/anchor/signal → l3)', layout.rows === 3);
|
||||
check('model: layout levels — l1 root, l2/signal tier 1, l3/anchors tier 2',
|
||||
layout.level.tether_l1 === 0 && layout.level.tether_l2 === 1 && layout.level.signal_amp === 1 &&
|
||||
layout.level.tether_l3 === 2 && layout.level.tether_anchors === 2 && layout.level.tether_l4 === 3);
|
||||
layout.level.tether_l3 === 2 && layout.level.tether_anchors === 2);
|
||||
check('model: layout is deterministic (same tree → same columns)', JSON.stringify(layout.col) === JSON.stringify(layoutTree(tree).col));
|
||||
|
||||
// a fresh run: starting techs pre-unlocked, the rest follow the rules
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ const dist = (x, y, t) => Math.hypot(x - t.x, y - t.y);
|
|||
const growth = config.get('tether.radiusGrowth', 2.0);
|
||||
const r2 = Tether.radiusForLevel(2);
|
||||
check(`level 2 = 5120 × growth (${growth}) (got ${r2})`, Math.abs(r2 - 5120 * growth) < 1e-9);
|
||||
const maxLevel = config.get('tether.maxLevel', 12);
|
||||
const maxLevel = config.get('tether.maxLevel', 3);
|
||||
let mono = true;
|
||||
let prev = 0;
|
||||
for (let l = 1; l <= maxLevel; l++) {
|
||||
|
|
|
|||
|
|
@ -789,15 +789,15 @@ The player holds a REPUTATION (standing) on each planet and space station:
|
|||
Pure rules/state in `js/research/` (Node-tested), the window is a
|
||||
passive view, `effects` carry the payload (e.g. `tether.level` grows
|
||||
the home tether). First category **Exploration** is live (Tether
|
||||
Level 1–4, Tether Anchoring, Signal Amplification); add a category =
|
||||
Level 1–3, Tether Anchoring, Signal Amplification); add a category =
|
||||
one JSON file + one line in the registry + one line in the manifest
|
||||
(`js/ui/ResearchWindow.js`, `js/research/*`, `data/research.json`,
|
||||
`data/research/exploration.json`, dev/research-builds.test.mjs)
|
||||
- [x] Tether progression: level 2 is a **build** — research
|
||||
`tether_l2` (blueprint, no effect) → BUILD slot on a world holding a
|
||||
level-1 tether → 200 minerals / 20 s → the world's tether strengthens
|
||||
to level 2 (10240 px), effect and all, outliving the stay. Levels 3–4
|
||||
are still research-direct (`effects.tether.level` on the home
|
||||
to level 2 (10240 px), effect and all, outliving the stay. Level 3
|
||||
is still research-direct (`effects.tether.level` on the home
|
||||
tether); anchoring extra rings on planets/stations remains
|
||||
(the `add`/`setLevel`/`onChange` seams are in place)
|
||||
(`data/builds.json → tether-l2`, `js/build/*`,
|
||||
|
|
|
|||
Loading…
Reference in New Issue