hoi4-agent-tools 2.1.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README.md +32 -18
- package/dist/bin/stdio.js +2 -1
- package/dist/hoi4_agent_tools/core/clausewitz-evaluation.js +90 -0
- package/dist/hoi4_agent_tools/core/engine.js +14 -0
- package/dist/hoi4_agent_tools/core/index.js +349 -2
- package/dist/hoi4_agent_tools/core/source/ast-path.js +41 -0
- package/dist/hoi4_agent_tools/core/source/index.js +1 -0
- package/dist/hoi4_agent_tools/event/limits.js +1 -1
- package/dist/hoi4_agent_tools/focus/render.js +37 -13
- package/dist/hoi4_agent_tools/mcp/server/create.js +5 -1
- package/dist/hoi4_agent_tools/mcp/server/result.js +4 -4
- package/dist/hoi4_agent_tools/mcp/tools/focus.js +39 -2
- package/dist/hoi4_agent_tools/mcp/tools/probability.js +413 -0
- package/dist/hoi4_agent_tools/mcp/tools/technology.js +368 -0
- package/dist/hoi4_agent_tools/probability/adapters.js +344 -0
- package/dist/hoi4_agent_tools/probability/compare.js +178 -0
- package/dist/hoi4_agent_tools/probability/evaluation.js +649 -0
- package/dist/hoi4_agent_tools/probability/index.js +13 -0
- package/dist/hoi4_agent_tools/probability/model.js +5 -0
- package/dist/hoi4_agent_tools/probability/rational.js +145 -0
- package/dist/hoi4_agent_tools/probability/render.js +307 -0
- package/dist/hoi4_agent_tools/probability/sequence.js +858 -0
- package/dist/hoi4_agent_tools/probability/service.js +1168 -0
- package/dist/hoi4_agent_tools/probability/simulation.js +545 -0
- package/dist/hoi4_agent_tools/probability/source-analysis.js +458 -0
- package/dist/hoi4_agent_tools/probability/sweep.js +265 -0
- package/dist/hoi4_agent_tools/probability/trigger-evaluator.js +280 -0
- package/dist/hoi4_agent_tools/probability/value-evaluator.js +357 -0
- package/dist/hoi4_agent_tools/schemas/probability.js +930 -0
- package/dist/hoi4_agent_tools/schemas/technology.js +77 -0
- package/dist/hoi4_agent_tools/technology/compare.js +240 -0
- package/dist/hoi4_agent_tools/technology/graph.js +1196 -0
- package/dist/hoi4_agent_tools/technology/index.js +7 -0
- package/dist/hoi4_agent_tools/technology/model.js +2 -0
- package/dist/hoi4_agent_tools/technology/queries.js +300 -0
- package/dist/hoi4_agent_tools/technology/render.js +641 -0
- package/dist/hoi4_agent_tools/technology/service.js +686 -0
- package/dist/hoi4_agent_tools/technology/source-analysis.js +967 -0
- package/dist/hoi4_agent_tools/version.js +1 -1
- package/docs/README.md +2 -0
- package/docs/development.md +7 -1
- package/docs/probability.md +80 -0
- package/docs/research/probability-adapter-evidence.md +64 -0
- package/docs/setup.md +1 -1
- package/docs/technology.md +90 -0
- package/examples/clients/codex-config.toml +3 -0
- package/examples/clients/generic.json +8 -0
- package/examples/probability/adaptive-event-pool.json +92 -0
- package/examples/probability/adaptive-event-sequence-input.json +57 -0
- package/examples/probability/event-mtth-scenarios.json +57 -0
- package/examples/probability/event-mtth-simulate-input.json +37 -0
- package/examples/probability/focus-route-scenarios.json +61 -0
- package/examples/probability/focus-route-sweep-input.json +36 -0
- package/package.json +26 -8
- package/schemas/configuration.schema.json +747 -0
- package/schemas/continuous-focus-palette.schema.json +325 -0
- package/schemas/custom-weighted-pool.schema.json +298 -0
- package/schemas/event-feature-manifest.schema.json +63 -0
- package/schemas/focus-plan.schema.json +927 -0
- package/schemas/focus-planning-sidecar.schema.json +407 -0
- package/schemas/gui-animation-source.schema.json +176 -0
- package/schemas/gui-helper.schema.json +301 -0
- package/schemas/gui-scenario.schema.json +298 -0
- package/schemas/map-operation.schema.json +2284 -0
- package/schemas/operation-result.schema.json +327 -0
- package/schemas/probability-analysis-result.schema.json +2211 -0
- package/schemas/probability-compare-input.schema.json +664 -0
- package/schemas/probability-evaluate-input.schema.json +665 -0
- package/schemas/probability-inspect-input.schema.json +97 -0
- package/schemas/probability-render-input.schema.json +141 -0
- package/schemas/probability-scenario-set.schema.json +430 -0
- package/schemas/probability-sequence-input.schema.json +886 -0
- package/schemas/probability-simulate-input.schema.json +691 -0
- package/schemas/probability-sweep-input.schema.json +702 -0
- package/server.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.3.0 - 2026-07-22
|
|
4
|
+
|
|
5
|
+
- Added a read-only AI and MTTH Scenario Analyzer with inspect, evaluate, sweep, simulate, sequence, compare, and render tools.
|
|
6
|
+
- Added versioned adapters for event MTTH and options, decisions, missions, focuses, technologies, doctrines, direct random chance, `random_list`, supported AI strategy factors, and declared custom weighted pools without treating unlike HOI4 systems as one probability formula.
|
|
7
|
+
- Added exact and bounded scenario evaluation, AST-path provenance, nested-random path probabilities, ranges, alternatives, distributions, numeric correlations, configurable diagnostics, named acceptance bands, and explicit external-factor support.
|
|
8
|
+
- Added threshold-aware sweeps with trigger-adjacent breakpoints, local elasticities, pairwise interactions, rank reversals, cliffs, and missed target bands.
|
|
9
|
+
- Added constant-memory Latin hypercube and seeded pseudo-random simulation, global input importance, Wilson intervals, and discrete daily-hazard MTTH samples with bounded quantile retention and uncertainty evidence.
|
|
10
|
+
- Added declared-manifest sequence analysis for recovery, caps, cooldowns, removal, resets, timer changes, terminal states, and per-category outcomes without executing effects or inferring campaign state.
|
|
11
|
+
- Added scenario-hash-bound deterministic ranking, matrix, waterfall, timing-survival, sensitivity, threshold, sequence, comparison, and unresolved renders with scenario, candidate, and metric filters.
|
|
12
|
+
- Added fail-closed installed-game build verification for the versioned probability adapters.
|
|
13
|
+
- Added generated public schemas, callable examples, source evidence, deterministic artifacts, and a project-owned fixture containing more than 150 weighted blocks, 250 scenarios, exact expectations, unresolved cases, and a stateful pool.
|
|
14
|
+
- Verified analyzer discovery and workflows over stdio and authenticated Streamable HTTP, package installation, resource retrieval, cancellation, stale-result handling, workspace isolation, large candidate pools, and the official MCP Inspector.
|
|
15
|
+
- Expanded event-graph capacity for installed-game and large-mod analysis beyond 100,000 nodes while retaining bounded artifact validation.
|
|
16
|
+
- Reconfirmed MCP protocol `2025-11-25`, TypeScript SDK 1.29.0, Inspector 1.0.0, Registry schema `2025-12-11`, and Registry publisher 1.8.0 for this release.
|
|
17
|
+
|
|
18
|
+
## 2.2.0 - 2026-07-22
|
|
19
|
+
|
|
20
|
+
- Added a read-only Technology Tree Viewer for classic technologies, legacy and current doctrines, source folder layouts, prerequisites, exclusive choices, categories, tags, unlocks, bonuses, grants, metadata, localisation, icons, and cross-file references.
|
|
21
|
+
- Added `hoi4.tech_inspect`, `hoi4.tech_render`, and `hoi4.tech_compare`, with complete graph resources, bounded large-tree overviews, focused folder renders, in-memory source comparison, and rename or removal impact analysis.
|
|
22
|
+
- Added source-linked diagnostic classification for structural, placement, reference, unlock, bonus, doctrine, AI, localisation, icon, and unresolved static-analysis findings.
|
|
23
|
+
- Added a deterministic 1,040-technology acceptance workspace with 13 folders, expected graph and reference manifests, stable SVG and PNG evidence, cancellation, incremental indexing, resource retrieval, and stdio, secured Streamable HTTP, package, and agent-workflow coverage.
|
|
24
|
+
- Pinned patched transitive HTTP, URI, and shell parsing dependencies; the release dependency audit reports no known vulnerabilities.
|
|
25
|
+
- Reconfirmed MCP protocol `2025-11-25`, TypeScript SDK 1.29.0, Registry schema `2025-12-11`, and Registry publisher 1.7.9 for this release.
|
|
26
|
+
|
|
3
27
|
## 2.1.1 - 2026-07-18
|
|
4
28
|
|
|
5
29
|
- Updated container attestation verification for current in-toto `Statement/v1` documents emitted by BuildKit.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# HOI4 Agent Tools
|
|
2
2
|
|
|
3
|
-
HOI4 Agent Tools is an MCP server for coding agents to
|
|
3
|
+
HOI4 Agent Tools is an MCP server for coding agents to understand Hearts of Iron IV event chains, technology systems, AI weights, and MTTH timing and to inspect, create, or clean up focus trees, scripted GUIs, and maps. Start it in a mod folder and it works there immediately, with offline renders for review.
|
|
4
4
|
|
|
5
5
|
## What it does
|
|
6
6
|
|
|
@@ -8,6 +8,8 @@ HOI4 Agent Tools is an MCP server for coding agents to analyze Hearts of Iron IV
|
|
|
8
8
|
- Scripted GUIs: trace GUI, GFX, scripted-GUI, and localisation links; render states; create or repair interface source.
|
|
9
9
|
- Maps: inspect provinces, states, regions, adjacency, supply, and railways; render layers; create and repair exact map data.
|
|
10
10
|
- Event chains: scan definitions and call sites, trace routes and state flow, lint references, render graphs, and compare revisions without editing event source.
|
|
11
|
+
- Technology trees: reconstruct technology and doctrine paths, folder layouts, unlocks, bonuses, grants, metadata, assets, and structural changes.
|
|
12
|
+
- AI and MTTH: evaluate weighted choices and timing across explicit scenarios, sweep uncertain inputs, simulate distributions, compare patches, and analyze declared stateful pools.
|
|
11
13
|
|
|
12
14
|
## Setup
|
|
13
15
|
|
|
@@ -29,29 +31,39 @@ Paste the printed Codex or generic global-install entry into your MCP client, th
|
|
|
29
31
|
|
|
30
32
|
## Tools
|
|
31
33
|
|
|
32
|
-
| Tool
|
|
33
|
-
|
|
|
34
|
-
| `hoi4.focus_inspect`
|
|
35
|
-
| `hoi4.focus_render`
|
|
36
|
-
| `hoi4.focus_raster`
|
|
37
|
-
| `hoi4.focus_rewrite`
|
|
38
|
-
| `hoi4.gui_inspect`
|
|
39
|
-
| `hoi4.gui_render`
|
|
40
|
-
| `hoi4.gui_rewrite`
|
|
41
|
-
| `hoi4.map_inspect`
|
|
42
|
-
| `hoi4.map_render`
|
|
43
|
-
| `hoi4.map_rewrite`
|
|
44
|
-
| `hoi4.event_inspect`
|
|
45
|
-
| `hoi4.event_render`
|
|
46
|
-
| `hoi4.event_compare`
|
|
34
|
+
| Tool | Purpose |
|
|
35
|
+
| --------------------------- | --------------------------------------------------------------------------------------- |
|
|
36
|
+
| `hoi4.focus_inspect` | Read focus trees and report structural or reference problems. |
|
|
37
|
+
| `hoi4.focus_render` | Produce fast HTML, SVG, JSON, and source-linked layout artifacts. |
|
|
38
|
+
| `hoi4.focus_raster` | Produce a high-fidelity PNG review with decoded source icons. |
|
|
39
|
+
| `hoi4.focus_rewrite` | Create or update a focus tree. |
|
|
40
|
+
| `hoi4.gui_inspect` | Read a scripted GUI and its linked assets and logic. |
|
|
41
|
+
| `hoi4.gui_render` | Render GUI states and resolutions offline. |
|
|
42
|
+
| `hoi4.gui_rewrite` | Create or update a GUI source package. |
|
|
43
|
+
| `hoi4.map_inspect` | Read map, state, province, region, supply, and railway data. |
|
|
44
|
+
| `hoi4.map_render` | Render map layers and overlays. |
|
|
45
|
+
| `hoi4.map_rewrite` | Create or update map data from an ordered list of exact changes. |
|
|
46
|
+
| `hoi4.event_inspect` | Scan, trace, explain, lint, or assess event chains and their state flow. |
|
|
47
|
+
| `hoi4.event_render` | Render source-linked event routes, options, timing, state, scope, and unresolved edges. |
|
|
48
|
+
| `hoi4.event_compare` | Compare event-chain topology and diagnostics between revisions. |
|
|
49
|
+
| `hoi4.tech_inspect` | Scan, trace, explain, lint, and assess technology and doctrine systems. |
|
|
50
|
+
| `hoi4.tech_render` | Render source layouts, dependency paths, unlocks, grants, metadata, and asset coverage. |
|
|
51
|
+
| `hoi4.tech_compare` | Compare technology graphs, placements, references, diagnostics, and source overlays. |
|
|
52
|
+
| `hoi4.probability_inspect` | Locate weighted logic, candidates, required inputs, and supported analysis. |
|
|
53
|
+
| `hoi4.probability_evaluate` | Evaluate exact values, probabilities, timing, bounds, and unresolved inputs. |
|
|
54
|
+
| `hoi4.probability_sweep` | Find sensitivity, breakpoints, cliffs, and rank reversals across declared ranges. |
|
|
55
|
+
| `hoi4.probability_simulate` | Run deterministic sampled analysis with confidence and convergence data. |
|
|
56
|
+
| `hoi4.probability_sequence` | Analyze declared recovery, caps, cooldowns, resets, timers, and terminal states. |
|
|
57
|
+
| `hoi4.probability_compare` | Attribute AI-weight and MTTH changes between real or proposed source. |
|
|
58
|
+
| `hoi4.probability_render` | Render cached rankings, matrices, timing, sensitivity, sequence, and comparisons. |
|
|
47
59
|
|
|
48
60
|
Large outputs are linked `hoi4-agent://` resources. For resources over 1 MiB, follow the `continuationUri` returned in `_meta` until it is `null`; clients may also request byte ranges with `?offset=<bytes>&length=<bytes>`.
|
|
49
61
|
|
|
50
62
|
## Coexistence with agent workflows
|
|
51
63
|
|
|
52
|
-
HOI4 Agent Tools provides HOI4 domain operations
|
|
64
|
+
HOI4 Agent Tools provides HOI4 domain operations without replacing repository instructions such as `AGENTS.md`, skills, plans, or subagents. Its optional weighted-logic prompt helps an agent scope one probability analysis and then returns control to the normal workflow.
|
|
53
65
|
|
|
54
|
-
Connecting and listing tools does not scan mod source. Compact tool schemas and linked resources keep large diagnostics, renders, and diffs out of the agent's working context until needed.
|
|
66
|
+
Connecting and listing tools does not scan mod source. Compact tool schemas and linked resources keep large diagnostics, renders, and diffs out of the agent's working context until needed. Event, technology, and probability tools analyze source without editing it; only `hoi4.*_rewrite` calls edit mod source.
|
|
55
67
|
|
|
56
68
|
## Create or clean content
|
|
57
69
|
|
|
@@ -61,6 +73,8 @@ Ask your agent in normal task language. A typical workflow is inspect, render, r
|
|
|
61
73
|
- Scripted GUIs: "Create a scripted GUI for this mechanic," or "Render this window at common resolutions and fix clipping, missing assets, and click-region conflicts." See [Scripted GUIs](docs/gui.md).
|
|
62
74
|
- Maps: "Create a state from these exact provinces," or "Inspect this state and split these provinces while keeping supply and railway references valid." See [Maps](docs/map.md).
|
|
63
75
|
- Event chains: "Trace every route from this event and explain where its flags and variables change," or "Compare the workspace event graph with its previous revision and render the affected routes." See [Event chains](docs/events.md).
|
|
76
|
+
- Technology trees: "Explain everything this technology requires and unlocks," or "Compare this technology patch and render every affected folder and doctrine branch." See [Technology trees](docs/technology.md).
|
|
77
|
+
- AI and MTTH: "Compare these focus weights across peace, defensive-war, and low-stability scenarios," or "Show when this MTTH event becomes likely and which unknown inputs control the result." See [AI and MTTH analysis](docs/probability.md).
|
|
64
78
|
|
|
65
79
|
## HTTP
|
|
66
80
|
|
package/dist/bin/stdio.js
CHANGED
|
@@ -6,7 +6,8 @@ import { FinalProtocolTransport } from '../hoi4_agent_tools/mcp/transports/proto
|
|
|
6
6
|
import { createEngine } from '../hoi4_agent_tools/runtime.js';
|
|
7
7
|
async function main() {
|
|
8
8
|
const engine = await createEngine();
|
|
9
|
-
const
|
|
9
|
+
const context = {};
|
|
10
|
+
const server = createMcpServer(engine, context);
|
|
10
11
|
server.server.onerror = (error) => {
|
|
11
12
|
const fatalInputError = error instanceof StdioFrameLimitError || error instanceof StdioInvalidUtf8Error;
|
|
12
13
|
const rejectedMessage = error instanceof StdioInvalidMessageError;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { compareCodeUnits } from './canonical.js';
|
|
2
|
+
import { assignments, parseClausewitz, } from './source/index.js';
|
|
3
|
+
const definitionCache = new WeakMap();
|
|
4
|
+
function activeTextFiles(snapshot, prefix) {
|
|
5
|
+
return snapshot.files
|
|
6
|
+
.filter(({ relativePath, shadowedBy }) => shadowedBy === undefined &&
|
|
7
|
+
relativePath.replaceAll('\\', '/').toLowerCase().startsWith(prefix) &&
|
|
8
|
+
relativePath.toLowerCase().endsWith('.txt'))
|
|
9
|
+
.sort((left, right) => left.loadOrder - right.loadOrder || compareCodeUnits(left.displayPath, right.displayPath));
|
|
10
|
+
}
|
|
11
|
+
function scalarAssignments(block) {
|
|
12
|
+
return assignments(block).filter(({ value }) => value.type === 'scalar');
|
|
13
|
+
}
|
|
14
|
+
function flattenConstants(block, prefix, output, file, document) {
|
|
15
|
+
for (const assignment of assignments(block)) {
|
|
16
|
+
if (assignment.key.value === 'schema')
|
|
17
|
+
continue;
|
|
18
|
+
const id = prefix === '' ? assignment.key.value : `${prefix}.${assignment.key.value}`;
|
|
19
|
+
if (assignment.value.type === 'scalar') {
|
|
20
|
+
output.set(id, { id, value: assignment.value.value, node: assignment, file, document });
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
flattenConstants(assignment.value, id, output, file, document);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** Shared, source-linked definitions used by analysis domains. */
|
|
28
|
+
export class ClausewitzEvaluationDefinitions {
|
|
29
|
+
scriptConstants = new Map();
|
|
30
|
+
mtthVariables = new Map();
|
|
31
|
+
scriptedTriggers = new Map();
|
|
32
|
+
localConstants = new Map();
|
|
33
|
+
static build(snapshot) {
|
|
34
|
+
const cached = definitionCache.get(snapshot);
|
|
35
|
+
if (cached !== undefined)
|
|
36
|
+
return cached;
|
|
37
|
+
const result = new ClausewitzEvaluationDefinitions();
|
|
38
|
+
for (const file of snapshot.files.filter(({ shadowedBy, relativePath }) => shadowedBy === undefined && relativePath.toLowerCase().endsWith('.txt'))) {
|
|
39
|
+
const document = parseClausewitz(file.bytes, file.displayPath);
|
|
40
|
+
const locals = new Map();
|
|
41
|
+
for (const assignment of scalarAssignments(document.root)) {
|
|
42
|
+
if (!assignment.key.value.startsWith('@') || assignment.value.type !== 'scalar')
|
|
43
|
+
continue;
|
|
44
|
+
locals.set(assignment.key.value, {
|
|
45
|
+
id: assignment.key.value,
|
|
46
|
+
value: assignment.value.value,
|
|
47
|
+
node: assignment,
|
|
48
|
+
file,
|
|
49
|
+
document,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if (locals.size > 0)
|
|
53
|
+
result.localConstants.set(file.displayPath, locals);
|
|
54
|
+
}
|
|
55
|
+
for (const file of activeTextFiles(snapshot, 'common/script_constants/')) {
|
|
56
|
+
const document = parseClausewitz(file.bytes, file.displayPath);
|
|
57
|
+
flattenConstants(document.root, '', result.scriptConstants, file, document);
|
|
58
|
+
}
|
|
59
|
+
for (const file of activeTextFiles(snapshot, 'common/mtth/')) {
|
|
60
|
+
const document = parseClausewitz(file.bytes, file.displayPath);
|
|
61
|
+
for (const assignment of assignments(document.root)) {
|
|
62
|
+
if (assignment.value.type !== 'block' || assignment.key.value.startsWith('@'))
|
|
63
|
+
continue;
|
|
64
|
+
result.mtthVariables.set(assignment.key.value, {
|
|
65
|
+
id: assignment.key.value,
|
|
66
|
+
value: assignment.value,
|
|
67
|
+
node: assignment,
|
|
68
|
+
file,
|
|
69
|
+
document,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
for (const file of activeTextFiles(snapshot, 'common/scripted_triggers/')) {
|
|
74
|
+
const document = parseClausewitz(file.bytes, file.displayPath);
|
|
75
|
+
for (const assignment of assignments(document.root)) {
|
|
76
|
+
if (assignment.value.type !== 'block')
|
|
77
|
+
continue;
|
|
78
|
+
result.scriptedTriggers.set(assignment.key.value, {
|
|
79
|
+
id: assignment.key.value,
|
|
80
|
+
value: assignment.value,
|
|
81
|
+
node: assignment,
|
|
82
|
+
file,
|
|
83
|
+
document,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
definitionCache.set(snapshot, result);
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -33,12 +33,26 @@ function defaultPatterns(workspace) {
|
|
|
33
33
|
'common/characters/**/*.txt',
|
|
34
34
|
'common/scripted_effects/**/*.txt',
|
|
35
35
|
'common/scripted_triggers/**/*.txt',
|
|
36
|
+
'common/script_constants/**/*.txt',
|
|
37
|
+
'common/mtth/**/*.txt',
|
|
38
|
+
'common/ai_strategy/**/*.txt',
|
|
39
|
+
'common/ai_strategy_plans/**/*.txt',
|
|
40
|
+
'common/ai_focuses/**/*.txt',
|
|
36
41
|
'common/on_actions/**/*.txt',
|
|
37
42
|
'common/operations/**/*.txt',
|
|
38
43
|
'common/raids/**/*.txt',
|
|
39
44
|
'common/bop/**/*.txt',
|
|
40
45
|
'common/resistance_compliance_modifiers/**/*.txt',
|
|
41
46
|
'common/special_projects/**/*.txt',
|
|
47
|
+
'common/technologies/**/*.txt',
|
|
48
|
+
'common/technology_tags/**/*.txt',
|
|
49
|
+
'common/technology_sharing/**/*.txt',
|
|
50
|
+
'common/doctrines/**/*.txt',
|
|
51
|
+
'common/units/**/*.txt',
|
|
52
|
+
'common/unit_tags/**/*.txt',
|
|
53
|
+
'common/buildings/**/*.txt',
|
|
54
|
+
'common/abilities/**/*.txt',
|
|
55
|
+
'common/combat_tactics.txt',
|
|
42
56
|
'events/**/*.txt',
|
|
43
57
|
'history/countries/**/*.txt',
|
|
44
58
|
'history/states/**/*.txt',
|
|
@@ -67,6 +67,36 @@ function possibleSymbolKinds(file, definitionFiles) {
|
|
|
67
67
|
if (sourcePath.endsWith('.gui'))
|
|
68
68
|
kinds.add('gui_element');
|
|
69
69
|
if (sourcePath.endsWith('.txt')) {
|
|
70
|
+
if (sourcePath.startsWith('common/technologies/'))
|
|
71
|
+
kinds.add('technology');
|
|
72
|
+
if (sourcePath.startsWith('common/technology_tags/')) {
|
|
73
|
+
kinds.add('technology_folder');
|
|
74
|
+
kinds.add('technology_category');
|
|
75
|
+
kinds.add('technology_tag');
|
|
76
|
+
}
|
|
77
|
+
if (sourcePath.startsWith('common/units/equipment/modules/'))
|
|
78
|
+
kinds.add('equipment_module');
|
|
79
|
+
else if (sourcePath.startsWith('common/units/equipment/'))
|
|
80
|
+
kinds.add('equipment');
|
|
81
|
+
else if (sourcePath.startsWith('common/units/'))
|
|
82
|
+
kinds.add('sub_unit');
|
|
83
|
+
if (sourcePath.startsWith('common/unit_tags/'))
|
|
84
|
+
kinds.add('unit_category');
|
|
85
|
+
if (sourcePath.startsWith('common/buildings/'))
|
|
86
|
+
kinds.add('building');
|
|
87
|
+
if (sourcePath.startsWith('common/abilities/'))
|
|
88
|
+
kinds.add('ability');
|
|
89
|
+
if (sourcePath === 'common/combat_tactics.txt' ||
|
|
90
|
+
sourcePath.startsWith('common/combat_tactics/'))
|
|
91
|
+
kinds.add('combat_tactic');
|
|
92
|
+
if (sourcePath.startsWith('common/doctrines/folders/'))
|
|
93
|
+
kinds.add('doctrine_folder');
|
|
94
|
+
if (sourcePath.startsWith('common/doctrines/grand_doctrines/'))
|
|
95
|
+
kinds.add('grand_doctrine');
|
|
96
|
+
if (sourcePath.startsWith('common/doctrines/tracks/'))
|
|
97
|
+
kinds.add('doctrine_track');
|
|
98
|
+
if (sourcePath.startsWith('common/doctrines/subdoctrines/'))
|
|
99
|
+
kinds.add('subdoctrine');
|
|
70
100
|
if (/(?:^|\/)focus(?:es)?(?:\/|$)|national_focus|continuous_focus/u.test(sourcePath)) {
|
|
71
101
|
kinds.add('focus_tree');
|
|
72
102
|
kinds.add('focus');
|
|
@@ -79,6 +109,14 @@ function possibleSymbolKinds(file, definitionFiles) {
|
|
|
79
109
|
kinds.add('scripted_effect');
|
|
80
110
|
if (sourcePath.startsWith('common/scripted_triggers/'))
|
|
81
111
|
kinds.add('scripted_trigger');
|
|
112
|
+
if (sourcePath.startsWith('common/script_constants/'))
|
|
113
|
+
kinds.add('script_constant');
|
|
114
|
+
if (sourcePath.startsWith('common/mtth/'))
|
|
115
|
+
kinds.add('mtth_variable');
|
|
116
|
+
if (sourcePath.startsWith('common/ai_strategy/'))
|
|
117
|
+
kinds.add('ai_strategy');
|
|
118
|
+
if (sourcePath.startsWith('common/ai_strategy_plans/'))
|
|
119
|
+
kinds.add('ai_strategy_plan');
|
|
82
120
|
if (sourcePath.startsWith('common/decisions/')) {
|
|
83
121
|
kinds.add('decision');
|
|
84
122
|
kinds.add('decision_category');
|
|
@@ -437,8 +475,23 @@ export class SymbolIndex {
|
|
|
437
475
|
const sourcePath = file.relativePath.replaceAll('\\', '/').toLowerCase();
|
|
438
476
|
for (const assignment of assignments(block)) {
|
|
439
477
|
const key = assignment.key.value;
|
|
440
|
-
if (assignment.value.type !== 'block')
|
|
478
|
+
if (assignment.value.type !== 'block') {
|
|
479
|
+
if (sourcePath.startsWith('common/script_constants/') &&
|
|
480
|
+
key !== 'schema' &&
|
|
481
|
+
!ancestors.includes('schema')) {
|
|
482
|
+
const id = [...ancestors, key].join('.');
|
|
483
|
+
this.addSymbol({
|
|
484
|
+
kind: 'script_constant',
|
|
485
|
+
id,
|
|
486
|
+
path: file.displayPath,
|
|
487
|
+
rootKind: file.rootKind,
|
|
488
|
+
loadOrder: file.loadOrder,
|
|
489
|
+
location: nodeLocation(document, assignment, id),
|
|
490
|
+
metadata: { value: assignment.value.value },
|
|
491
|
+
});
|
|
492
|
+
}
|
|
441
493
|
continue;
|
|
494
|
+
}
|
|
442
495
|
const child = assignment.value;
|
|
443
496
|
const id = firstScalar(child, 'id')?.value;
|
|
444
497
|
if (key === 'focus_tree' && id !== undefined) {
|
|
@@ -463,6 +516,267 @@ export class SymbolIndex {
|
|
|
463
516
|
metadata: {},
|
|
464
517
|
});
|
|
465
518
|
}
|
|
519
|
+
else if (sourcePath.startsWith('common/technologies/') &&
|
|
520
|
+
ancestors.length === 1 &&
|
|
521
|
+
ancestors[0] === 'technologies' &&
|
|
522
|
+
!key.startsWith('@')) {
|
|
523
|
+
this.addSymbol({
|
|
524
|
+
kind: 'technology',
|
|
525
|
+
id: key,
|
|
526
|
+
path: file.displayPath,
|
|
527
|
+
rootKind: file.rootKind,
|
|
528
|
+
loadOrder: file.loadOrder,
|
|
529
|
+
location: nodeLocation(document, assignment, key),
|
|
530
|
+
metadata: {
|
|
531
|
+
startYear: firstScalar(child, 'start_year')?.value,
|
|
532
|
+
researchCost: firstScalar(child, 'research_cost')?.value,
|
|
533
|
+
doctrine: firstScalar(child, 'doctrine')?.value === 'yes',
|
|
534
|
+
icon: `GFX_${key}_medium`,
|
|
535
|
+
},
|
|
536
|
+
});
|
|
537
|
+
for (const pathBlock of childBlocks(child, 'path')) {
|
|
538
|
+
const target = firstScalar(pathBlock, 'leads_to_tech');
|
|
539
|
+
if (target !== undefined)
|
|
540
|
+
this.addReference({
|
|
541
|
+
kind: 'technology_prerequisite',
|
|
542
|
+
from: key,
|
|
543
|
+
toKind: 'technology',
|
|
544
|
+
to: target.value,
|
|
545
|
+
path: file.displayPath,
|
|
546
|
+
location: nodeLocation(document, target, key),
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
for (const xor of childBlocks(child, 'xor'))
|
|
550
|
+
for (const target of scalars(xor))
|
|
551
|
+
this.addReference({
|
|
552
|
+
kind: 'technology_exclusive',
|
|
553
|
+
from: key,
|
|
554
|
+
toKind: 'technology',
|
|
555
|
+
to: target,
|
|
556
|
+
path: file.displayPath,
|
|
557
|
+
location: nodeLocation(document, xor, key),
|
|
558
|
+
});
|
|
559
|
+
for (const folder of childBlocks(child, 'folder')) {
|
|
560
|
+
const target = firstScalar(folder, 'name');
|
|
561
|
+
if (target !== undefined)
|
|
562
|
+
this.addReference({
|
|
563
|
+
kind: 'technology_folder',
|
|
564
|
+
from: key,
|
|
565
|
+
toKind: 'technology_folder',
|
|
566
|
+
to: target.value,
|
|
567
|
+
path: file.displayPath,
|
|
568
|
+
location: nodeLocation(document, target, key),
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
for (const categories of childBlocks(child, 'categories'))
|
|
572
|
+
for (const target of scalars(categories))
|
|
573
|
+
this.addReference({
|
|
574
|
+
kind: 'technology_category',
|
|
575
|
+
from: key,
|
|
576
|
+
toKind: 'technology_category',
|
|
577
|
+
to: target,
|
|
578
|
+
path: file.displayPath,
|
|
579
|
+
location: nodeLocation(document, categories, key),
|
|
580
|
+
});
|
|
581
|
+
for (const tags of childBlocks(child, 'tags'))
|
|
582
|
+
for (const target of scalars(tags))
|
|
583
|
+
this.addReference({
|
|
584
|
+
kind: 'technology_tag',
|
|
585
|
+
from: key,
|
|
586
|
+
toKind: 'technology_tag',
|
|
587
|
+
to: target,
|
|
588
|
+
path: file.displayPath,
|
|
589
|
+
location: nodeLocation(document, tags, key),
|
|
590
|
+
});
|
|
591
|
+
for (const [field, targetKind] of [
|
|
592
|
+
['enable_equipments', 'equipment'],
|
|
593
|
+
['enable_equipment_modules', 'equipment_module'],
|
|
594
|
+
['enable_subunits', 'sub_unit'],
|
|
595
|
+
])
|
|
596
|
+
for (const unlockBlock of childBlocks(child, field))
|
|
597
|
+
for (const target of scalars(unlockBlock))
|
|
598
|
+
this.addReference({
|
|
599
|
+
kind: `technology_${field}`,
|
|
600
|
+
from: key,
|
|
601
|
+
toKind: targetKind,
|
|
602
|
+
to: target,
|
|
603
|
+
path: file.displayPath,
|
|
604
|
+
location: nodeLocation(document, unlockBlock, key),
|
|
605
|
+
});
|
|
606
|
+
for (const building of childBlocks(child, 'enable_building')) {
|
|
607
|
+
const target = firstScalar(building, 'building');
|
|
608
|
+
if (target !== undefined)
|
|
609
|
+
this.addReference({
|
|
610
|
+
kind: 'technology_enable_building',
|
|
611
|
+
from: key,
|
|
612
|
+
toKind: 'building',
|
|
613
|
+
to: target.value,
|
|
614
|
+
path: file.displayPath,
|
|
615
|
+
location: nodeLocation(document, target, key),
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
else if (sourcePath.startsWith('common/technology_tags/') &&
|
|
620
|
+
key === 'technology_categories') {
|
|
621
|
+
for (const category of child.entries.filter((entry) => entry.type === 'scalar'))
|
|
622
|
+
this.addSymbol({
|
|
623
|
+
kind: 'technology_category',
|
|
624
|
+
id: category.value,
|
|
625
|
+
path: file.displayPath,
|
|
626
|
+
rootKind: file.rootKind,
|
|
627
|
+
loadOrder: file.loadOrder,
|
|
628
|
+
location: nodeLocation(document, category, category.value),
|
|
629
|
+
metadata: {},
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
else if (sourcePath.startsWith('common/technology_tags/') && key === 'technology_tags') {
|
|
633
|
+
for (const tag of child.entries.filter((entry) => entry.type === 'scalar'))
|
|
634
|
+
this.addSymbol({
|
|
635
|
+
kind: 'technology_tag',
|
|
636
|
+
id: tag.value,
|
|
637
|
+
path: file.displayPath,
|
|
638
|
+
rootKind: file.rootKind,
|
|
639
|
+
loadOrder: file.loadOrder,
|
|
640
|
+
location: nodeLocation(document, tag, tag.value),
|
|
641
|
+
metadata: {},
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
else if (sourcePath.startsWith('common/technology_tags/') &&
|
|
645
|
+
ancestors.at(-1) === 'technology_folders') {
|
|
646
|
+
this.addSymbol({
|
|
647
|
+
kind: 'technology_folder',
|
|
648
|
+
id: key,
|
|
649
|
+
path: file.displayPath,
|
|
650
|
+
rootKind: file.rootKind,
|
|
651
|
+
loadOrder: file.loadOrder,
|
|
652
|
+
location: nodeLocation(document, assignment, key),
|
|
653
|
+
metadata: {
|
|
654
|
+
doctrine: firstScalar(child, 'doctrine')?.value === 'yes',
|
|
655
|
+
ledger: firstScalar(child, 'ledger')?.value,
|
|
656
|
+
},
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
else if (sourcePath.startsWith('common/units/equipment/modules/') &&
|
|
660
|
+
ancestors.at(-1) === 'equipment_modules' &&
|
|
661
|
+
key !== 'limit') {
|
|
662
|
+
this.addSymbol({
|
|
663
|
+
kind: 'equipment_module',
|
|
664
|
+
id: key,
|
|
665
|
+
path: file.displayPath,
|
|
666
|
+
rootKind: file.rootKind,
|
|
667
|
+
loadOrder: file.loadOrder,
|
|
668
|
+
location: nodeLocation(document, assignment, key),
|
|
669
|
+
metadata: {},
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
else if (sourcePath.startsWith('common/units/equipment/') &&
|
|
673
|
+
!sourcePath.startsWith('common/units/equipment/modules/') &&
|
|
674
|
+
ancestors.at(-1) === 'equipments' &&
|
|
675
|
+
key !== 'limit') {
|
|
676
|
+
this.addSymbol({
|
|
677
|
+
kind: 'equipment',
|
|
678
|
+
id: key,
|
|
679
|
+
path: file.displayPath,
|
|
680
|
+
rootKind: file.rootKind,
|
|
681
|
+
loadOrder: file.loadOrder,
|
|
682
|
+
location: nodeLocation(document, assignment, key),
|
|
683
|
+
metadata: {},
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
else if (sourcePath.startsWith('common/units/') &&
|
|
687
|
+
!sourcePath.startsWith('common/units/equipment/') &&
|
|
688
|
+
ancestors.at(-1) === 'sub_units') {
|
|
689
|
+
this.addSymbol({
|
|
690
|
+
kind: 'sub_unit',
|
|
691
|
+
id: key,
|
|
692
|
+
path: file.displayPath,
|
|
693
|
+
rootKind: file.rootKind,
|
|
694
|
+
loadOrder: file.loadOrder,
|
|
695
|
+
location: nodeLocation(document, assignment, key),
|
|
696
|
+
metadata: {},
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
else if (sourcePath.startsWith('common/buildings/') && ancestors.at(-1) === 'buildings') {
|
|
700
|
+
this.addSymbol({
|
|
701
|
+
kind: 'building',
|
|
702
|
+
id: key,
|
|
703
|
+
path: file.displayPath,
|
|
704
|
+
rootKind: file.rootKind,
|
|
705
|
+
loadOrder: file.loadOrder,
|
|
706
|
+
location: nodeLocation(document, assignment, key),
|
|
707
|
+
metadata: {},
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
else if (sourcePath.startsWith('common/abilities/') && ancestors.at(-1) === 'ability') {
|
|
711
|
+
this.addSymbol({
|
|
712
|
+
kind: 'ability',
|
|
713
|
+
id: key,
|
|
714
|
+
path: file.displayPath,
|
|
715
|
+
rootKind: file.rootKind,
|
|
716
|
+
loadOrder: file.loadOrder,
|
|
717
|
+
location: nodeLocation(document, assignment, key),
|
|
718
|
+
metadata: {},
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
else if ((sourcePath === 'common/combat_tactics.txt' ||
|
|
722
|
+
sourcePath.startsWith('common/combat_tactics/')) &&
|
|
723
|
+
ancestors.at(-1) === 'combat_tactics') {
|
|
724
|
+
this.addSymbol({
|
|
725
|
+
kind: 'combat_tactic',
|
|
726
|
+
id: key,
|
|
727
|
+
path: file.displayPath,
|
|
728
|
+
rootKind: file.rootKind,
|
|
729
|
+
loadOrder: file.loadOrder,
|
|
730
|
+
location: nodeLocation(document, assignment, key),
|
|
731
|
+
metadata: {},
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
else if (sourcePath.startsWith('common/doctrines/folders/') && ancestors.length === 0) {
|
|
735
|
+
this.addSymbol({
|
|
736
|
+
kind: 'doctrine_folder',
|
|
737
|
+
id: key,
|
|
738
|
+
path: file.displayPath,
|
|
739
|
+
rootKind: file.rootKind,
|
|
740
|
+
loadOrder: file.loadOrder,
|
|
741
|
+
location: nodeLocation(document, assignment, key),
|
|
742
|
+
metadata: {},
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
else if (sourcePath.startsWith('common/doctrines/grand_doctrines/') &&
|
|
746
|
+
ancestors.length === 0) {
|
|
747
|
+
this.addSymbol({
|
|
748
|
+
kind: 'grand_doctrine',
|
|
749
|
+
id: key,
|
|
750
|
+
path: file.displayPath,
|
|
751
|
+
rootKind: file.rootKind,
|
|
752
|
+
loadOrder: file.loadOrder,
|
|
753
|
+
location: nodeLocation(document, assignment, key),
|
|
754
|
+
metadata: { folder: firstScalar(child, 'folder')?.value },
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
else if (sourcePath.startsWith('common/doctrines/tracks/') && ancestors.length === 0) {
|
|
758
|
+
this.addSymbol({
|
|
759
|
+
kind: 'doctrine_track',
|
|
760
|
+
id: key,
|
|
761
|
+
path: file.displayPath,
|
|
762
|
+
rootKind: file.rootKind,
|
|
763
|
+
loadOrder: file.loadOrder,
|
|
764
|
+
location: nodeLocation(document, assignment, key),
|
|
765
|
+
metadata: {},
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
else if (sourcePath.startsWith('common/doctrines/subdoctrines/') &&
|
|
769
|
+
ancestors.length === 0) {
|
|
770
|
+
this.addSymbol({
|
|
771
|
+
kind: 'subdoctrine',
|
|
772
|
+
id: key,
|
|
773
|
+
path: file.displayPath,
|
|
774
|
+
rootKind: file.rootKind,
|
|
775
|
+
loadOrder: file.loadOrder,
|
|
776
|
+
location: nodeLocation(document, assignment, key),
|
|
777
|
+
metadata: {},
|
|
778
|
+
});
|
|
779
|
+
}
|
|
466
780
|
else if (key === 'focus' &&
|
|
467
781
|
id !== undefined &&
|
|
468
782
|
ancestors.at(-1) === 'continuous_focus_palette') {
|
|
@@ -582,6 +896,39 @@ export class SymbolIndex {
|
|
|
582
896
|
metadata: {},
|
|
583
897
|
});
|
|
584
898
|
}
|
|
899
|
+
else if (sourcePath.startsWith('common/mtth/') && ancestors.length === 0) {
|
|
900
|
+
this.addSymbol({
|
|
901
|
+
kind: 'mtth_variable',
|
|
902
|
+
id: key,
|
|
903
|
+
path: file.displayPath,
|
|
904
|
+
rootKind: file.rootKind,
|
|
905
|
+
loadOrder: file.loadOrder,
|
|
906
|
+
location: nodeLocation(document, assignment, key),
|
|
907
|
+
metadata: {},
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
else if (sourcePath.startsWith('common/ai_strategy/') && ancestors.length === 0) {
|
|
911
|
+
this.addSymbol({
|
|
912
|
+
kind: 'ai_strategy',
|
|
913
|
+
id: key,
|
|
914
|
+
path: file.displayPath,
|
|
915
|
+
rootKind: file.rootKind,
|
|
916
|
+
loadOrder: file.loadOrder,
|
|
917
|
+
location: nodeLocation(document, assignment, key),
|
|
918
|
+
metadata: {},
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
else if (sourcePath.startsWith('common/ai_strategy_plans/') && ancestors.length === 0) {
|
|
922
|
+
this.addSymbol({
|
|
923
|
+
kind: 'ai_strategy_plan',
|
|
924
|
+
id: key,
|
|
925
|
+
path: file.displayPath,
|
|
926
|
+
rootKind: file.rootKind,
|
|
927
|
+
loadOrder: file.loadOrder,
|
|
928
|
+
location: nodeLocation(document, assignment, key),
|
|
929
|
+
metadata: {},
|
|
930
|
+
});
|
|
931
|
+
}
|
|
585
932
|
else if (sourcePath.startsWith('common/decisions/') && ancestors.length === 0) {
|
|
586
933
|
this.addSymbol({
|
|
587
934
|
kind: 'decision_category',
|
|
@@ -936,7 +1283,7 @@ export class SymbolIndex {
|
|
|
936
1283
|
continue;
|
|
937
1284
|
const active = candidates[0];
|
|
938
1285
|
this.#active.set(key, active);
|
|
939
|
-
const additiveCategory = active.kind === 'decision_category';
|
|
1286
|
+
const additiveCategory = active.kind === 'decision_category' || active.kind === 'technology_category';
|
|
940
1287
|
for (const candidate of group) {
|
|
941
1288
|
candidate.overridden = additiveCategory ? candidate.sourceShadowed : candidate !== active;
|
|
942
1289
|
}
|