hoi4-agent-tools 2.2.0 → 2.3.3
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 +28 -0
- package/README.md +30 -21
- package/dist/hoi4_agent_tools/core/clausewitz-evaluation.js +90 -0
- package/dist/hoi4_agent_tools/core/engine.js +5 -0
- package/dist/hoi4_agent_tools/core/index.js +57 -1
- 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/algorithms.js +178 -1
- package/dist/hoi4_agent_tools/event/graph.js +24 -9
- package/dist/hoi4_agent_tools/event/limits.js +5 -2
- package/dist/hoi4_agent_tools/event/service.js +182 -45
- package/dist/hoi4_agent_tools/mcp/server/create.js +3 -1
- package/dist/hoi4_agent_tools/mcp/tools/chaosx.js +13 -63
- package/dist/hoi4_agent_tools/mcp/tools/probability.js +413 -0
- package/dist/hoi4_agent_tools/mcp/tools/technology.js +4 -1
- 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/runtime.js +109 -7
- package/dist/hoi4_agent_tools/schemas/probability.js +930 -0
- package/dist/hoi4_agent_tools/technology/graph.js +30 -3
- package/dist/hoi4_agent_tools/technology/queries.js +70 -3
- package/dist/hoi4_agent_tools/technology/service.js +38 -2
- package/dist/hoi4_agent_tools/version.js +1 -1
- package/docs/README.md +1 -0
- package/docs/development.md +2 -0
- package/docs/events.md +2 -2
- package/docs/probability.md +80 -0
- package/docs/research/probability-adapter-evidence.md +64 -0
- package/docs/setup.md +2 -0
- package/docs/technology.md +4 -2
- 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 +19 -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
|
@@ -315,9 +315,30 @@ function stronglyConnectedComponents(nodeIds, edges, signal) {
|
|
|
315
315
|
}
|
|
316
316
|
return components.sort((left, right) => compareCodeUnits(left[0] ?? '', right[0] ?? ''));
|
|
317
317
|
}
|
|
318
|
-
function expandHelperReferences(references, calls, unresolved, signal) {
|
|
318
|
+
function expandHelperReferences(references, calls, unresolved, analysisMode, signal) {
|
|
319
319
|
const direct = references.filter(({ sourceKind }) => sourceKind === 'scripted_effect');
|
|
320
320
|
const publicReferences = references.filter(({ sourceKind }) => sourceKind !== 'scripted_effect');
|
|
321
|
+
if (analysisMode === 'focused') {
|
|
322
|
+
if (calls.length > 0) {
|
|
323
|
+
unresolved.push({
|
|
324
|
+
id: 'tech-unresolved-helper-expansion-deferred',
|
|
325
|
+
kind: 'unsupported_construct',
|
|
326
|
+
expression: 'scripted_effect expansion',
|
|
327
|
+
confidence: 'unresolved',
|
|
328
|
+
blockers: [
|
|
329
|
+
{
|
|
330
|
+
code: 'TECH_HELPER_EXPANSION_DEFERRED',
|
|
331
|
+
message: 'Large-workspace technology analysis retains scripted-effect calls and direct references without materialising the full helper projection.',
|
|
332
|
+
},
|
|
333
|
+
],
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
const deduplicated = new Map();
|
|
337
|
+
for (const reference of [...publicReferences, ...direct])
|
|
338
|
+
if (!deduplicated.has(reference.id))
|
|
339
|
+
deduplicated.set(reference.id, reference);
|
|
340
|
+
return [...deduplicated.values()].sort((left, right) => compareCodeUnits(left.id, right.id));
|
|
341
|
+
}
|
|
321
342
|
const refsByHelper = new Map();
|
|
322
343
|
for (const reference of direct) {
|
|
323
344
|
const group = refsByHelper.get(reference.sourceId) ?? [];
|
|
@@ -1085,7 +1106,7 @@ export function buildTechnologyGraph(snapshot, options) {
|
|
|
1085
1106
|
const unlocks = resolveUnlocks(selectOwnedRecords(fragments.flatMap(({ unlocks }) => unlocks), ({ technologyId }) => technologyId, ({ location }) => location, technologyById).sort((left, right) => compareCodeUnits(left.id, right.id)), unlockTargets, snapshot.complete);
|
|
1086
1107
|
const unresolved = fragments.flatMap(({ unresolved }) => unresolved);
|
|
1087
1108
|
const helperCalls = fragments.flatMap(({ helperCalls }) => helperCalls);
|
|
1088
|
-
const externalReferences = expandHelperReferences(fragments.flatMap(({ externalReferences }) => externalReferences), helperCalls, unresolved, options.signal);
|
|
1109
|
+
const externalReferences = expandHelperReferences(fragments.flatMap(({ externalReferences }) => externalReferences), helperCalls, unresolved, options.analysisMode ?? 'full', options.signal);
|
|
1089
1110
|
const issues = diagnose(technologies, folders, placements, gridboxes, edges, categories, doctrineDefinitions, unlocks, externalReferences, selectedTechnologies.duplicates.map((group) => group.map(({ iconSprite, effectSignatureFields, ...technology }) => ({
|
|
1090
1111
|
...technology,
|
|
1091
1112
|
icon: iconStatus(iconSprite, snapshot.index, assets),
|
|
@@ -1111,10 +1132,13 @@ export function buildTechnologyGraph(snapshot, options) {
|
|
|
1111
1132
|
const graph = {
|
|
1112
1133
|
schemaVersion: TECHNOLOGY_GRAPH_SCHEMA_VERSION,
|
|
1113
1134
|
parserVersion: TECHNOLOGY_PARSER_VERSION,
|
|
1135
|
+
analysisMode: options.analysisMode ?? 'full',
|
|
1114
1136
|
workspaceId: snapshot.workspaceId,
|
|
1115
1137
|
workspaceIdentity: options.workspaceIdentity,
|
|
1116
1138
|
revision,
|
|
1117
|
-
complete: snapshot.complete &&
|
|
1139
|
+
complete: snapshot.complete &&
|
|
1140
|
+
unresolved.every(({ kind }) => kind !== 'partial_source') &&
|
|
1141
|
+
!(options.analysisMode === 'focused' && helperCalls.length > 0),
|
|
1118
1142
|
analysisBoundary: {
|
|
1119
1143
|
staticAnalysis: true,
|
|
1120
1144
|
language: 'l_english',
|
|
@@ -1125,6 +1149,9 @@ export function buildTechnologyGraph(snapshot, options) {
|
|
|
1125
1149
|
'meta-generated or variable technology identifiers',
|
|
1126
1150
|
'exact AI research choice and runtime research time',
|
|
1127
1151
|
'dynamic scripted localisation without supplied runtime values',
|
|
1152
|
+
...(options.analysisMode === 'focused' && helperCalls.length > 0
|
|
1153
|
+
? ['large-workspace scripted-effect helper projections are deferred']
|
|
1154
|
+
: []),
|
|
1128
1155
|
],
|
|
1129
1156
|
assumptions: [
|
|
1130
1157
|
'classic technology paths point from the defining technology to leads_to_tech',
|
|
@@ -8,17 +8,42 @@ function requireTechnology(graph, technologyId) {
|
|
|
8
8
|
technologyId,
|
|
9
9
|
});
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
const TECHNOLOGY_SCAN_FULL_GRAPH_RECORD_LIMIT = 100_000;
|
|
12
|
+
function technologyRecordCount(graph) {
|
|
13
|
+
return (graph.technologies.length +
|
|
14
|
+
graph.folders.length +
|
|
15
|
+
graph.categories.length +
|
|
16
|
+
graph.placements.length +
|
|
17
|
+
graph.gridboxes.length +
|
|
18
|
+
graph.edges.length +
|
|
19
|
+
graph.unlocks.length +
|
|
20
|
+
graph.unlockTargets.length +
|
|
21
|
+
graph.externalReferences.length +
|
|
22
|
+
graph.helperCalls.length +
|
|
23
|
+
graph.issues.length +
|
|
24
|
+
graph.diagnostics.length +
|
|
25
|
+
graph.unresolved.length +
|
|
26
|
+
graph.doctrineDefinitions.length);
|
|
27
|
+
}
|
|
28
|
+
function groupedCounts(values, key) {
|
|
29
|
+
const counts = new Map();
|
|
30
|
+
for (const value of values)
|
|
31
|
+
counts.set(key(value), (counts.get(key(value)) ?? 0) + 1);
|
|
32
|
+
return Object.fromEntries([...counts].sort(([left], [right]) => compareCodeUnits(left, right)));
|
|
33
|
+
}
|
|
34
|
+
export function technologyScanReport(graph, fullGraphRecordLimit = TECHNOLOGY_SCAN_FULL_GRAPH_RECORD_LIMIT) {
|
|
35
|
+
const recordCount = technologyRecordCount(graph);
|
|
12
36
|
const classifications = Object.fromEntries(['confirmed_error', 'probable_defect', 'design_warning', 'unresolved_analysis'].map((classification) => [
|
|
13
37
|
classification,
|
|
14
38
|
graph.issues.filter((issue) => issue.classification === classification).length,
|
|
15
39
|
]));
|
|
16
|
-
|
|
40
|
+
const summary = {
|
|
17
41
|
schemaVersion: graph.schemaVersion,
|
|
18
42
|
parserVersion: graph.parserVersion,
|
|
19
43
|
workspaceId: graph.workspaceId,
|
|
20
44
|
workspaceIdentity: graph.workspaceIdentity,
|
|
21
45
|
revision: graph.revision,
|
|
46
|
+
analysisMode: graph.analysisMode ?? 'full',
|
|
22
47
|
complete: graph.complete,
|
|
23
48
|
statistics: graph.statistics,
|
|
24
49
|
issueClassifications: classifications,
|
|
@@ -31,7 +56,49 @@ export function technologyScanReport(graph) {
|
|
|
31
56
|
analysisBoundary: graph.analysisBoundary,
|
|
32
57
|
issueSamples: graph.issues.slice(0, 100),
|
|
33
58
|
unresolvedSamples: graph.unresolved.slice(0, 100),
|
|
34
|
-
|
|
59
|
+
recordCounts: {
|
|
60
|
+
technologies: graph.technologies.length,
|
|
61
|
+
folders: graph.folders.length,
|
|
62
|
+
categories: graph.categories.length,
|
|
63
|
+
placements: graph.placements.length,
|
|
64
|
+
gridboxes: graph.gridboxes.length,
|
|
65
|
+
edges: graph.edges.length,
|
|
66
|
+
unlocks: graph.unlocks.length,
|
|
67
|
+
unlockTargets: graph.unlockTargets.length,
|
|
68
|
+
externalReferences: graph.externalReferences.length,
|
|
69
|
+
issues: graph.issues.length,
|
|
70
|
+
diagnostics: graph.diagnostics.length,
|
|
71
|
+
unresolved: graph.unresolved.length,
|
|
72
|
+
doctrineDefinitions: graph.doctrineDefinitions.length,
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
if (recordCount <= fullGraphRecordLimit) {
|
|
76
|
+
return {
|
|
77
|
+
...summary,
|
|
78
|
+
authoritativeGraphIncluded: true,
|
|
79
|
+
graph,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
...summary,
|
|
84
|
+
authoritativeGraphIncluded: false,
|
|
85
|
+
graphSummary: {
|
|
86
|
+
revision: graph.revision,
|
|
87
|
+
recordCounts: summary.recordCounts,
|
|
88
|
+
technologyKinds: groupedCounts(graph.technologies, ({ kind }) => kind),
|
|
89
|
+
edgeKinds: groupedCounts(graph.edges, ({ kind }) => kind),
|
|
90
|
+
issueCodes: groupedCounts(graph.issues, ({ code }) => code),
|
|
91
|
+
issueSeverities: groupedCounts(graph.issues, ({ severity }) => severity),
|
|
92
|
+
diagnosticCodes: groupedCounts(graph.diagnostics, ({ code }) => code),
|
|
93
|
+
diagnosticSeverities: groupedCounts(graph.diagnostics, ({ severity }) => severity),
|
|
94
|
+
},
|
|
95
|
+
diagnosticSamples: graph.diagnostics.slice(0, 100),
|
|
96
|
+
artifactProjection: {
|
|
97
|
+
mode: 'large-scan-summary',
|
|
98
|
+
fullGraphRecordCount: recordCount,
|
|
99
|
+
fullGraphRecordLimit,
|
|
100
|
+
detailQueries: ['folders', 'trace', 'explain', 'unlocks', 'bonus_coverage', 'lint', 'impact'],
|
|
101
|
+
},
|
|
35
102
|
};
|
|
36
103
|
}
|
|
37
104
|
export function discoverTechnologyFolders(graph, folderId) {
|
|
@@ -18,6 +18,7 @@ const GRAPH_ARTIFACT_BYTES = 134_217_728;
|
|
|
18
18
|
const GRAPH_ARTIFACT_CHUNKS = 2_048;
|
|
19
19
|
const HISTORY_ENTRIES = 12;
|
|
20
20
|
const FOCUSED_RENDER_LIMIT = 32;
|
|
21
|
+
const FOCUSED_GRAPH_FILE_THRESHOLD = 1_000;
|
|
21
22
|
class BoundedTechnologyFragmentCache {
|
|
22
23
|
#values = new Map();
|
|
23
24
|
#bytes = 0;
|
|
@@ -58,6 +59,36 @@ function sharedState(engine) {
|
|
|
58
59
|
}
|
|
59
60
|
return state;
|
|
60
61
|
}
|
|
62
|
+
function technologyScanPatterns(workspace) {
|
|
63
|
+
const roots = workspace.registration.roots;
|
|
64
|
+
const under = (values, glob) => values.map((root) => `${root.replaceAll('\\', '/').replace(/\/$/u, '')}/${glob}`);
|
|
65
|
+
return [
|
|
66
|
+
'common/technologies/**/*.txt',
|
|
67
|
+
'common/technology_tags/**/*.txt',
|
|
68
|
+
'common/technology_sharing/**/*.txt',
|
|
69
|
+
'common/doctrines/**/*.txt',
|
|
70
|
+
'common/units/**/*.txt',
|
|
71
|
+
'common/buildings/**/*.txt',
|
|
72
|
+
'common/abilities/**/*.txt',
|
|
73
|
+
'common/decisions/**/*.txt',
|
|
74
|
+
'common/scripted_effects/**/*.txt',
|
|
75
|
+
'common/on_actions/**/*.txt',
|
|
76
|
+
'common/ideas/**/*.txt',
|
|
77
|
+
'common/characters/**/*.txt',
|
|
78
|
+
'common/combat_tactics.txt',
|
|
79
|
+
'events/**/*.txt',
|
|
80
|
+
'history/countries/**/*.txt',
|
|
81
|
+
...under(roots.focus, '**/*.txt'),
|
|
82
|
+
...under(roots.interface, '**/*.gui'),
|
|
83
|
+
...under(roots.localisation, 'english/**/*.{yml,yaml}'),
|
|
84
|
+
...under(roots.localisation, '*.{yml,yaml}'),
|
|
85
|
+
].sort(compareCodeUnits);
|
|
86
|
+
}
|
|
87
|
+
function technologyAnalysisMode(workspace, snapshot) {
|
|
88
|
+
return workspace.gameRoot !== undefined || snapshot.files.length > FOCUSED_GRAPH_FILE_THRESHOLD
|
|
89
|
+
? 'focused'
|
|
90
|
+
: 'full';
|
|
91
|
+
}
|
|
61
92
|
function rememberGraph(state, graph) {
|
|
62
93
|
const history = state.history.get(graph.workspaceId) ?? new Map();
|
|
63
94
|
history.delete(graph.revision);
|
|
@@ -79,6 +110,7 @@ function safeSlug(value) {
|
|
|
79
110
|
function graphHash(graph) {
|
|
80
111
|
return hashCanonical({
|
|
81
112
|
schemaVersion: graph.schemaVersion,
|
|
113
|
+
analysisMode: graph.analysisMode ?? 'full',
|
|
82
114
|
workspaceIdentity: graph.workspaceIdentity,
|
|
83
115
|
revision: graph.revision,
|
|
84
116
|
statistics: graph.statistics,
|
|
@@ -322,10 +354,12 @@ export class TechnologyTreeViewer {
|
|
|
322
354
|
const cached = this.#state.current.get(workspaceId);
|
|
323
355
|
if (options.refresh !== true && cached?.generation === generation)
|
|
324
356
|
return cached.graph;
|
|
325
|
-
const snapshot = await this.engine.scan(workspaceId, {}, options.principal, options.signal);
|
|
357
|
+
const snapshot = await this.engine.scan(workspaceId, { patterns: technologyScanPatterns(workspace) }, options.principal, options.signal);
|
|
358
|
+
const analysisMode = technologyAnalysisMode(workspace, snapshot);
|
|
326
359
|
const preliminary = buildTechnologyGraph(snapshot, {
|
|
327
360
|
workspaceIdentity: workspace.workspaceIdentity,
|
|
328
361
|
cache: this.#state.fragments,
|
|
362
|
+
analysisMode,
|
|
329
363
|
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
330
364
|
});
|
|
331
365
|
const patterns = technologyAssetPatterns(preliminary);
|
|
@@ -339,6 +373,7 @@ export class TechnologyTreeViewer {
|
|
|
339
373
|
workspaceIdentity: workspace.workspaceIdentity,
|
|
340
374
|
cache: this.#state.fragments,
|
|
341
375
|
assetFiles,
|
|
376
|
+
analysisMode,
|
|
342
377
|
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
343
378
|
});
|
|
344
379
|
this.#state.current.set(workspaceId, { generation, snapshot, assetFiles, graph });
|
|
@@ -354,7 +389,7 @@ export class TechnologyTreeViewer {
|
|
|
354
389
|
let report;
|
|
355
390
|
switch (input.mode) {
|
|
356
391
|
case 'scan':
|
|
357
|
-
report =
|
|
392
|
+
report = technologyScanReport(graph);
|
|
358
393
|
break;
|
|
359
394
|
case 'folders':
|
|
360
395
|
report = discoverTechnologyFolders(graph, input.folderId);
|
|
@@ -588,6 +623,7 @@ export class TechnologyTreeViewer {
|
|
|
588
623
|
workspaceIdentity: workspace.workspaceIdentity,
|
|
589
624
|
cache: this.#state.fragments,
|
|
590
625
|
assetFiles: cached.assetFiles,
|
|
626
|
+
analysisMode: cached.graph.analysisMode ?? 'full',
|
|
591
627
|
...(signal === undefined ? {} : { signal }),
|
|
592
628
|
});
|
|
593
629
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LATEST_PROTOCOL_VERSION } from '@modelcontextprotocol/sdk/types.js';
|
|
2
2
|
export const PACKAGE_NAME = 'hoi4-agent-tools';
|
|
3
|
-
export const PACKAGE_VERSION = '2.
|
|
3
|
+
export const PACKAGE_VERSION = '2.3.3';
|
|
4
4
|
export const MCP_PROTOCOL_VERSION = LATEST_PROTOCOL_VERSION;
|
|
5
5
|
export const CONFIG_VERSION = 1;
|
|
6
6
|
export const TRANSACTION_VERSION = 1;
|
package/docs/README.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
- [Setup](setup.md): install the server, configure HOI4 paths, and connect an MCP client.
|
|
4
4
|
- [Event chains](events.md): inspect, trace, lint, render, and compare event chains.
|
|
5
5
|
- [Technology trees](technology.md): inspect, trace, lint, render, and compare technologies and doctrines.
|
|
6
|
+
- [AI and MTTH analysis](probability.md): evaluate weighted logic, timing, uncertainty, sequences, and patches.
|
|
6
7
|
- [Focus trees](focus.md): inspect, create, render, and clean national or continuous focus content.
|
|
7
8
|
- [Scripted GUIs](gui.md): inspect linked UI source, render states, and create or repair GUIs.
|
|
8
9
|
- [Maps](map.md): inspect and edit provinces, states, regions, adjacency, supply, and railways.
|
package/docs/development.md
CHANGED
|
@@ -23,6 +23,8 @@ The event-chain acceptance fixture contains more than 300 project-owned event de
|
|
|
23
23
|
|
|
24
24
|
The technology acceptance fixture contains 1,040 project-owned technologies across 13 folders and exercises classic and current doctrines, prerequisites, exclusive branches, multiple placements, unlocks, bonuses, grants, assets, unresolved references, rendering, and comparison.
|
|
25
25
|
|
|
26
|
+
The probability fixture contains more than 150 weighted blocks, 250 scenarios, and a declared stateful pool. Run `npm run benchmark` after analyzer changes. On the July 22, 2026 reference run, cold indexing took 28 ms, a cached block evaluation 29 ms, the 250-row matrix 85 ms, one million draws 9 ms, 3/12/25-candidate sequence cases 20/2,079/356 ms, and retrieval of a 1.84 MB result 3 ms. Review budgets are 250 ms, 150 ms, 750 ms, 100 ms, 200/10,000/3,000 ms, and 100 ms respectively; accuracy, method metadata, and omitted probability must not be weakened to meet them.
|
|
27
|
+
|
|
26
28
|
Local opt-in tests can also read an installed game and an external mod without copying their sources:
|
|
27
29
|
|
|
28
30
|
```bash
|
package/docs/events.md
CHANGED
|
@@ -16,9 +16,9 @@ Use the three event tools directly from the target mod.
|
|
|
16
16
|
- `lint`: find missing targets, unreachable events, suspicious cycles, conflicting definitions, invalid timing, and unresolved dynamic calls.
|
|
17
17
|
- `impact`: identify callers, descendants, state dependencies, and files affected by a proposed event change.
|
|
18
18
|
|
|
19
|
-
Use narrow identifiers, direction, and depth limits when the task concerns one chain. Broad scans build the structural event graph without expanding every scripted helper into duplicate paths. Focused trace
|
|
19
|
+
Use narrow identifiers, direction, and depth limits when the task concerns one chain. Broad scans build the structural event graph without expanding every scripted helper into duplicate paths. Focused trace and path calls traverse helper nodes and collapse only the selected bounded route, while state, impact, and render calls expand helper relationships when their workspace-wide evidence requires it.
|
|
20
20
|
|
|
21
|
-
The compact response summarizes the result. Small scan resources contain the complete graph; very large scan resources contain exact totals, grouped inventories, representative diagnostics, and the revision needed for focused follow-up queries. This keeps an agent's prompt and artifact storage bounded without reducing the results of those focused queries.
|
|
21
|
+
The compact response summarizes the result. Small scan resources contain the complete graph; very large scan resources contain exact totals, grouped inventories, representative diagnostics, and the revision needed for focused follow-up queries. The viewer retains the complete in-memory graph for those follow-up queries, and the event issue ceiling is high enough for installed-game-scale diagnostic inventories. This keeps an agent's prompt and artifact storage bounded without reducing the results of those focused queries.
|
|
22
22
|
|
|
23
23
|
Selectors use one of these forms:
|
|
24
24
|
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# AI and MTTH scenario analysis
|
|
2
|
+
|
|
3
|
+
The probability tools explain weighted HOI4 logic under explicit world-state scenarios. They inspect real mod source or proposed in-memory source, show which conditions and modifiers apply, calculate only probabilities supported by the selected HOI4 surface, and keep every unresolved input visible.
|
|
4
|
+
|
|
5
|
+
Use them for event timing and option weights, decision and mission scores, focus selection, technology and doctrine selection, direct random chances, `random_list`, supported AI strategy factors, and declared custom weighted pools.
|
|
6
|
+
|
|
7
|
+
| Tool | Use |
|
|
8
|
+
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
9
|
+
| `hoi4.probability_inspect` | Discover weighted blocks, adapters, candidates, provenance, capabilities, and unsupported constructs. |
|
|
10
|
+
| `hoi4.probability_evaluate` | Evaluate eligibility, modifier traces, raw values, proven probabilities, and MTTH horizon chances across scenarios. |
|
|
11
|
+
| `hoi4.probability_sweep` | Sweep declared ranges and locate sensitivity changes, breakpoints, and rank reversals. |
|
|
12
|
+
| `hoi4.probability_simulate` | Sample declared distributions with a deterministic seed and confidence intervals. |
|
|
13
|
+
| `hoi4.probability_sequence` | Analyze only recovery, caps, cooldowns, removal, resets, timers, and terminal states declared by a custom pool manifest. |
|
|
14
|
+
| `hoi4.probability_compare` | Attribute changes in eligibility, modifiers, values, probabilities, timing, ranks, and unresolved analysis to a proposed patch. |
|
|
15
|
+
| `hoi4.probability_render` | Render cached ranking, matrix, waterfall, timing, sensitivity, sequence, comparison, and unresolved views. |
|
|
16
|
+
|
|
17
|
+
All seven tools are read-only. Proposed source is parsed in memory and never written. When an installed game root is configured, evaluation fails closed unless `launcher-settings.json` identifies the supported HOI4 build and checksum. Results otherwise state that they target the adapter version without claiming local-game verification.
|
|
18
|
+
|
|
19
|
+
## Scenarios
|
|
20
|
+
|
|
21
|
+
A scenario contains only state the caller is willing to declare. Missing values stay unresolved. Alternatives and ranges produce exact branches or bounds; probability distributions require `hoi4.probability_simulate`.
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"schemaVersion": "1.0",
|
|
26
|
+
"id": "route_states",
|
|
27
|
+
"scenarios": [
|
|
28
|
+
{
|
|
29
|
+
"id": "defensive_war",
|
|
30
|
+
"actor": "EXM",
|
|
31
|
+
"date": "1939.9.1",
|
|
32
|
+
"state": {
|
|
33
|
+
"has_war": true,
|
|
34
|
+
"variable.foreign_influence": 45,
|
|
35
|
+
"focus.external_factors_complete": true
|
|
36
|
+
},
|
|
37
|
+
"flags": ["route_independent"]
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Focus, technology, and doctrine probabilities require a complete candidate pool. Their engine rule is an independent uniform score race, not weight divided by total weight. Focus scenarios use `focus.external_factors_complete: true` only after the caller has supplied every relevant prerequisite and strategy factor. Technology and doctrine scenarios use `technology.external_factors_complete: true` only after cost, date, bonus, strategy, and candidate effects are accounted for.
|
|
44
|
+
|
|
45
|
+
Decision and mission adapters intentionally return scores and ranks without inventing normalized probabilities. Event-option and `random_list` adapters normalize only their complete local pools. Direct random remains an independent percentage. MTTH horizon chance uses the versioned game timing model and returns a bound when an inactive-to-active polling phase is unknown.
|
|
46
|
+
|
|
47
|
+
Named acceptance bands and configurable diagnostic thresholds let a test suite state intended probability, timing, starvation, dominance, prevalence, and sensitivity limits. Evaluate, sweep, and simulation requests can name the metrics of interest; that set is retained in result metadata while the authoritative result keeps the eligibility and trace context required to explain them. Sweeps enumerate declared alternatives exactly, add trigger breakpoints and their adjacent values for continuous ranges, and report local elasticities, pairwise interactions, rank reversals, cliffs, and missed target bands. Sweep expansion is bounded and rejected before it can silently truncate the requested analysis.
|
|
48
|
+
|
|
49
|
+
## Results
|
|
50
|
+
|
|
51
|
+
Authoritative JSON keeps these fields separate:
|
|
52
|
+
|
|
53
|
+
- eligibility;
|
|
54
|
+
- raw value or raw interval;
|
|
55
|
+
- conditional selection probability or deterministic probability interval;
|
|
56
|
+
- effective MTTH and cumulative time chance;
|
|
57
|
+
- sampled frequency and statistical confidence interval;
|
|
58
|
+
- scenario prevalence.
|
|
59
|
+
|
|
60
|
+
Every candidate includes source provenance with a stable AST path, an ordered modifier trace, support level, and unresolved analysis. `external` support means the parsed candidate is valid but its surrounding game factors were not fully declared. The tool response stays compact; complete matrices, traces, simulations, comparisons, and visuals are linked MCP resources.
|
|
61
|
+
|
|
62
|
+
Nested `random_list` entries report both their conditional share inside the immediate list and their full path probability through every enclosing list. Dynamic parent paths remain explicit unresolved evidence.
|
|
63
|
+
|
|
64
|
+
Deterministic simulation uses constant-memory Latin hypercube sampling by default, with seeded pseudo-random sampling available when requested. Numeric distributions can use a Gaussian copula correlation matrix. Discrete or categorical correlation requests are sampled independently and reported as unresolved instead of being approximated silently. Simulation reports Wilson intervals, effective sample count, global input importance, and HOI4 daily-hazard MTTH samples. Timing quantiles use a deterministic bounded reservoir and include their confidence basis and retained sample count.
|
|
65
|
+
|
|
66
|
+
## Proposed patches
|
|
67
|
+
|
|
68
|
+
Pass `inlineClausewitz` or `virtualPatch` in a source selector to analyze text without writing it. `hoi4.probability_compare` evaluates the before and after source under the same scenarios and attributes each changed rank, score, probability, or uncertainty to its modifier trace.
|
|
69
|
+
|
|
70
|
+
## Declared sequences
|
|
71
|
+
|
|
72
|
+
Sequence analysis accepts a `customPoolManifest`. The manifest is the complete model boundary: candidates, selection mode, cadence, state, recovery, caps, cooldowns, removals, resets, timer changes, and terminal states. Small finite systems use exact state distributions, bounded systems expose omitted beam mass, and large systems use deterministic seeded Monte Carlo. Results include per-candidate and per-category next-choice probability, expected selections, ever-selected probability, starvation, and expected first-selection day. The analyzer never executes event effects or infers wider campaign state.
|
|
73
|
+
|
|
74
|
+
## Visual review
|
|
75
|
+
|
|
76
|
+
`hoi4.probability_render` produces deterministic ranking, matrix, waterfall, timing-survival, sensitivity, threshold, sequence, comparison, and unresolved views. Filters can select scenarios, candidates, and metrics. Pass the scenario hash returned by evaluation as `expectedScenarioHash` when a caller needs a render bound to that exact analysis; a stale hash returns a structured stale-result diagnostic and no visual artifact.
|
|
77
|
+
|
|
78
|
+
Adapter evidence and known boundaries are recorded in [probability-adapter-evidence.md](research/probability-adapter-evidence.md).
|
|
79
|
+
|
|
80
|
+
Callable argument examples are in [`examples/probability`](../examples/probability). Generated JSON Schemas for every operation, scenario sets, custom pools, and authoritative results are in [`schemas`](../schemas).
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Probability adapter evidence
|
|
2
|
+
|
|
3
|
+
The AI and MTTH Scenario Analyzer targets Hearts of Iron IV **Operation Postern 1.19.2.0 (checksum d245)**. Adapter behavior is versioned as `hoi4-1.19.2.v1`; results always include that adapter version and game build.
|
|
4
|
+
|
|
5
|
+
This review used the installed game's documentation, vanilla source examples, and an offline Paradox wiki snapshot. Paths below are relative to a Hearts of Iron IV installation or to the offline snapshot. SHA-256 values identify the exact files reviewed on 2026-07-22.
|
|
6
|
+
|
|
7
|
+
## Adapter semantics
|
|
8
|
+
|
|
9
|
+
| Adapter | Proven interpretation | Exact probability conditions | Deliberate boundary |
|
|
10
|
+
| ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
11
|
+
| `event_mean_time_to_happen` | Ordered `base`, `add`, and `factor` operations produce an effective duration. When the trigger is active, `M` days is the median of daily checks, so daily hazard is `1 - 2^(-1/M)`. Inactive event triggers are polled every 20 days. | A time-horizon chance is exact only while trigger state, every modifier input, scheduled change, and any inactive-to-active polling phase are declared. | It does not execute events or infer state changes. Missing poll phase produces a 0–20 day activation bound. |
|
|
12
|
+
| `event_option_ai_chance` | Options form one proportional categorical pool. An omitted block has weight 1. An all-zero pool selects the first option. | Every option and eligibility result for the event must be present. | The engine's d100 selection granularity is reported for effective shares below one percent. |
|
|
13
|
+
| `decision_ai_will_do` and `mission_ai_will_do` | Ordered source modifiers produce a raw desirability score after eligibility. | No normalized probability is claimed. | Local documentation does not prove a complete categorical denominator or a universal selection formula. |
|
|
14
|
+
| `national_focus_ai_will_do` | Each eligible focus receives an independent uniform draw from zero to its evaluated score; the maximum wins. A just-completed prerequisite and AI strategy focus factors can change the score. | The complete eligible candidate pool and every external focus factor must be declared. Exact probabilities use the independent-uniform maximum race, not `weight / sum(weight)`. | Ordered AI strategy plans can override weighted choice and are reported rather than simulated. |
|
|
15
|
+
| `technology_ai_will_do` and `doctrine_ai_will_do` | Eligible candidates use the independent-uniform maximum-score race. Research factors, cost, date, bonuses, and forced strategy choices can alter or override ranking. | The complete eligible pool and all external research factors must be declared. | Research duration is not selection probability. Forced research strategies and wider research-slot scheduling are not simulated. |
|
|
16
|
+
| `direct_random` | `chance` is an independent percentage in `[0,100]`. | The chance expression must resolve. | It is never normalized against neighboring blocks. |
|
|
17
|
+
| `random_list` | Entries form the complete local proportional weight pool. Nested entries retain their immediate conditional share and multiply through enclosing lists for a full path probability. | Every entry, enclosing list, and modifier on the selected path must resolve. | Dynamic entry identifiers, dynamic parent paths, or effect-derived values remain unresolved unless supplied explicitly. |
|
|
18
|
+
| `ai_strategy_factor` | Supported strategy types modify a target score. `research_weight_factor` is a percentage factor; positive `research_tech` forces prioritization. | Raw factor evaluation is exact when the strategy and target are explicit. | A strategy definition is not itself normalized into a probability pool. |
|
|
19
|
+
| `custom_weighted_pool` | The manifest declares categorical or independent selection plus recovery, caps, cooldowns, removals, resets, timer changes, and terminal states. | Exact finite-state analysis is used while the state space stays bounded; otherwise the result is explicitly beam-bounded or seeded Monte Carlo. | Only declared state transitions run. Gameplay effects and campaign state are never inferred or executed. |
|
|
20
|
+
|
|
21
|
+
## Installed documentation reviewed
|
|
22
|
+
|
|
23
|
+
| Relative path | SHA-256 | Used for |
|
|
24
|
+
| ----------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------- |
|
|
25
|
+
| `launcher-settings.json` | `f99265973bba12f30906cc98629d8520218ff8cd90a5f9d1d82e7f00fb737b9e` | Installed game version and checksum |
|
|
26
|
+
| `common/decisions/_documentation.md` | `5e7e686b5582f32c38d898d805e17171e88f3bfeee4a030deb1884f9726de019` | Decision scopes, eligibility surfaces, and evaluation cadence |
|
|
27
|
+
| `common/doctrines/_documentation.md` | `f0d1b7ad1f1dc8afe59942a406784a2d72c0c73dd42963c2502f89961eea85d4` | Current doctrine definitions and AI fields |
|
|
28
|
+
| `common/script_constants/documentation.md` | `e2812efbddc7961038e1f1f2b6dd6b059bdc70704ec87d1fe530835d9141be1c` | `constant:` schema and nested constant paths |
|
|
29
|
+
| `documentation/script_concept_documentation.md` | `884bfe9f9207ea4b25b36b5129d0f77d11d4b2f7eb0592587767ec58d24930c7` | Script constants and dynamic script concepts |
|
|
30
|
+
| `documentation/effects_documentation.md` | `2cdc93d26775990468eed167442f7a416b13059fb8fe07d139b54250b79209af` | `random_list` and AI strategy effect syntax |
|
|
31
|
+
| `common/ai_strategy/_documentation.md` | `70f5d755ae7ed4060e49cc64a393d1a0dc5611b3acc8abc6dfb4538781c915a1` | `research_tech` and `research_weight_factor` behavior |
|
|
32
|
+
|
|
33
|
+
The offline wiki pages `AI modding`, `Data structures`, `Decision modding`, `Event modding`, `National focus modding`, `Scopes`, and `Technology modding` were reviewed in parallel. Their respective SHA-256 values were `f1fdb9fd…a14f`, `20a03385…a14f`, `500e8161…68c`, `b0186d9b…e00`, `b1c7f624…6faf`, `04866815…fdc`, and `48050261…fdc`.
|
|
34
|
+
|
|
35
|
+
## Vanilla examples reviewed
|
|
36
|
+
|
|
37
|
+
| Surface | Relative path and line | SHA-256 |
|
|
38
|
+
| ----------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------ |
|
|
39
|
+
| Event MTTH | `events/Yugoslavia.txt:49` | `138279b294ef0d567e477efe2f0ebb72c5c0a7f2920ed3fd03d2792749624dbc` |
|
|
40
|
+
| Event option `ai_chance` | `events/AAT_Generic_Events.txt:44` | `e718d19edba2d56c75a5bbf1fa0c51bbb6cda54e0919434c4490fec9dabcea69` |
|
|
41
|
+
| Decision `ai_will_do` | `common/decisions/_generic_decisions.txt:56` | `83cd276f8fd2c0c90470225c996d7afee7b8c08437289dd23d819a4a29edb187` |
|
|
42
|
+
| Focus `ai_will_do` | `common/national_focus/austria.txt:122` | `0f197e321cb9e0c267a11c78bccb570ef42d1fc251e7af05fb4f385de27a1059` |
|
|
43
|
+
| Technology `ai_will_do` | `common/technologies/industry.txt:30` | `06d45f11a0bfdee35ef72b935b2bee3b0083a49eb771e5c6b9391c087ed7d94f` |
|
|
44
|
+
| Current doctrine `ai_will_do` | `common/doctrines/grand_doctrines/land_grand_doctrines.txt:14` | `cd81a877366c582f096422320cd7fcf9fb259afcc8d45d3dbb887b37792b329d` |
|
|
45
|
+
| `random_list` | `events/AAT_Sweden.txt:1760` | `8fad5c10d147afab27eb42c78f3261c804cd6149f1c81927418ab0bbe3b8bf29` |
|
|
46
|
+
| Direct `random` | `events/BBA_Switzerland.txt:1197` | `4331bbe2ea7d0844eb357ecd73f915c1ba3e5a0b115ae62ea5b95a8011e1145e` |
|
|
47
|
+
| AI research strategy factor | `common/ai_strategy/ENG.txt:3206` | `f6fed5347c68ab2a3b79e4864dc5d72490c9cabe46a16e865932d2e5a37716d9` |
|
|
48
|
+
|
|
49
|
+
The installed vanilla inventory contained 71 event files with MTTH, 101 with `ai_chance`, 86 decision files with `ai_will_do`, 76 national-focus files, 13 technology files, 19 doctrine files, 38 event files with `random_list`, and 9 AI strategy files with `research_weight_factor`. These counts were used to select examples and to test discovery breadth; they are not runtime assumptions.
|
|
50
|
+
|
|
51
|
+
## Unsupported constructs
|
|
52
|
+
|
|
53
|
+
When a game root is configured, adapter execution verifies `launcher-settings.json` against the supported raw version and checksum and fails closed on a missing, unreadable, or different build. Without a configured game root, results identify the adapter target without claiming that an installed build was verified.
|
|
54
|
+
|
|
55
|
+
The analyzer reports, but does not guess or execute:
|
|
56
|
+
|
|
57
|
+
- arbitrary effects used to calculate later weights;
|
|
58
|
+
- unprovided scopes, event targets, game rules, DLC state, dynamic identifiers, meta effects, or scripted-localisation results;
|
|
59
|
+
- parameterized scripted triggers whose arguments cannot be resolved from the selected source;
|
|
60
|
+
- recursive MTTH variables or helper cycles;
|
|
61
|
+
- incomplete focus, technology, doctrine, event-option, or random-list candidate pools;
|
|
62
|
+
- categorical or discrete correlation requests; numeric ranges and numeric distributions are supported by the correlation sampler;
|
|
63
|
+
- invalid or non-positive-semidefinite correlation matrices;
|
|
64
|
+
- full strategic AI planning, research-slot scheduling, event-effect execution, or campaign simulation.
|
package/docs/setup.md
CHANGED
|
@@ -12,6 +12,8 @@ Set the MCP process working directory to the mod being edited. The server create
|
|
|
12
12
|
|
|
13
13
|
When the MCP server starts with its working directory inside a mod, every focus, GUI, map, event, and technology call uses that mod automatically. Configured multi-mod or remote deployments can use explicit workspace selection.
|
|
14
14
|
|
|
15
|
+
Local startup accepts normal descriptor-based mods and sparse descriptor-less mods that contain standard HOI4 content folders. Empty roots need a `descriptor.mod` or an explicit configuration so the server does not guess an unrelated directory.
|
|
16
|
+
|
|
15
17
|
## Optional persistent configuration
|
|
16
18
|
|
|
17
19
|
Use `--init` only when you need several mod roots, a fixed game path, or a shared/remote process:
|
package/docs/technology.md
CHANGED
|
@@ -8,7 +8,7 @@ Use the three technology tools directly from the target mod.
|
|
|
8
8
|
|
|
9
9
|
`hoi4.tech_inspect` provides eight focused modes:
|
|
10
10
|
|
|
11
|
-
- `scan`: build the
|
|
11
|
+
- `scan`: build the technology graph and report its inventory, diagnostics, analysis limits, and revision. Small graphs include the complete graph in the report; large graphs return exact counts, grouped findings, samples, and the revision for focused follow-up queries.
|
|
12
12
|
- `folders`: list folder roots and source placements, or inspect one `folderId`.
|
|
13
13
|
- `trace`: follow prerequisites, descendants, or both from a `technologyId` with bounded depth and node limits.
|
|
14
14
|
- `explain`: collect one technology's definition, placements, paths, exclusivity, metadata, effects, unlocks, grants, bonuses, and unresolved references.
|
|
@@ -43,7 +43,9 @@ Impact mode takes a subject with `kind` set to `technology`, `category`, `folder
|
|
|
43
43
|
}
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
Findings are separated into confirmed errors, probable defects, design warnings, and unresolved analysis. Roots, grant-only technologies, early dates, zero AI weights, repeated effects, and routing nodes are evaluated in context instead of being treated as automatic defects.
|
|
46
|
+
Findings are separated into confirmed errors, probable defects, design warnings, and unresolved analysis. Roots, grant-only technologies, early dates, zero AI weights, repeated effects, and routing nodes are evaluated in context instead of being treated as automatic defects. The viewer keeps its authoritative graph in memory while the linked scan artifact remains a bounded summary; use its revision with the focused inspect modes or a render resource to inspect the relevant records.
|
|
47
|
+
|
|
48
|
+
Game-backed and otherwise large workspaces automatically use focused analysis mode. It keeps direct grants, bonuses, unlocks, definitions, and the source-linked scripted-effect call index while deferring the full helper projection that can otherwise make a vanilla-plus-mod scan take minutes or exhaust the server process. The result is marked partial and names this boundary in `analysisBoundary`; no direct source evidence is discarded.
|
|
47
49
|
|
|
48
50
|
## Render
|
|
49
51
|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "1.0",
|
|
3
|
+
"id": "adaptive_event_pool_example",
|
|
4
|
+
"description": "Declared pool with recovery, repeat caps, category growth, reset, removal, and timer compression.",
|
|
5
|
+
"selection": {
|
|
6
|
+
"mode": "categorical_weighted",
|
|
7
|
+
"cadence": "timer",
|
|
8
|
+
"timerMinDays": 45,
|
|
9
|
+
"timerMaxDays": 60,
|
|
10
|
+
"rounding": "nearest"
|
|
11
|
+
},
|
|
12
|
+
"state": {
|
|
13
|
+
"minor_events_since_major": 0,
|
|
14
|
+
"major_gain": 150,
|
|
15
|
+
"timer_compression": 0
|
|
16
|
+
},
|
|
17
|
+
"candidates": [
|
|
18
|
+
{
|
|
19
|
+
"id": "minor_fire_once",
|
|
20
|
+
"category": "fire_once",
|
|
21
|
+
"weight": 1000,
|
|
22
|
+
"cap": 1000,
|
|
23
|
+
"eligibleWhen": "not selected.minor_fire_once",
|
|
24
|
+
"oneTime": true
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"id": "minor_repeatable",
|
|
28
|
+
"category": "repeatable",
|
|
29
|
+
"weight": 1000,
|
|
30
|
+
"cap": 1000,
|
|
31
|
+
"eligibleWhen": "true",
|
|
32
|
+
"cooldownDays": 0
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "major_crisis",
|
|
36
|
+
"category": "major",
|
|
37
|
+
"weight": 0,
|
|
38
|
+
"cap": 1000,
|
|
39
|
+
"eligibleWhen": "not selected.major_crisis",
|
|
40
|
+
"oneTime": true
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"recovery": [
|
|
44
|
+
{
|
|
45
|
+
"cadence": "monthly",
|
|
46
|
+
"target": "candidate.minor_repeatable.weight",
|
|
47
|
+
"amount": 20,
|
|
48
|
+
"cap": "candidate.minor_repeatable.cap"
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
"transitions": [
|
|
52
|
+
{
|
|
53
|
+
"when": "selected.category != major",
|
|
54
|
+
"actions": [
|
|
55
|
+
{ "operation": "add", "target": "state.minor_events_since_major", "value": 1 },
|
|
56
|
+
{
|
|
57
|
+
"operation": "add",
|
|
58
|
+
"target": "candidate.major_crisis.weight",
|
|
59
|
+
"value": "state.major_gain"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"operation": "compress_timer",
|
|
63
|
+
"target": "selection.timer_max_days",
|
|
64
|
+
"value": "floor(state.minor_events_since_major / 3)"
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"when": "selected.id == minor_repeatable",
|
|
70
|
+
"actions": [
|
|
71
|
+
{ "operation": "multiply", "target": "candidate.minor_repeatable.cap", "value": 0.5 },
|
|
72
|
+
{
|
|
73
|
+
"operation": "cap",
|
|
74
|
+
"target": "candidate.minor_repeatable.weight",
|
|
75
|
+
"value": "candidate.minor_repeatable.cap"
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"when": "selected.category == major",
|
|
81
|
+
"actions": [
|
|
82
|
+
{ "operation": "set", "target": "state.minor_events_since_major", "value": 0 },
|
|
83
|
+
{ "operation": "reset_category", "target": "category.major", "value": 0 },
|
|
84
|
+
{ "operation": "reset_timer", "target": "selection", "value": null }
|
|
85
|
+
]
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"when": "selected.one_time == true",
|
|
89
|
+
"actions": [{ "operation": "remove", "target": "selected.candidate", "value": null }]
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
}
|