hoi4-agent-tools 3.3.0 → 3.4.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 +12 -0
- package/dist/hoi4_agent_tools/core/condition-evaluator.js +13 -8
- package/dist/hoi4_agent_tools/core/helper-expansion.js +3 -0
- package/dist/hoi4_agent_tools/event/comparison-selection.js +78 -0
- package/dist/hoi4_agent_tools/event/helper-expansion.js +48 -0
- package/dist/hoi4_agent_tools/event/operations.js +16 -0
- package/dist/hoi4_agent_tools/event/service.js +21 -2
- package/dist/hoi4_agent_tools/focus/compact.js +241 -27
- package/dist/hoi4_agent_tools/focus/layout.js +119 -8
- package/dist/hoi4_agent_tools/focus/render.js +62 -0
- package/dist/hoi4_agent_tools/focus/scenario.js +99 -0
- package/dist/hoi4_agent_tools/focus/tool-operations.js +30 -4
- package/dist/hoi4_agent_tools/gui/assets.js +54 -4
- package/dist/hoi4_agent_tools/gui/layout.js +935 -104
- package/dist/hoi4_agent_tools/gui/renderer.js +47 -17
- package/dist/hoi4_agent_tools/gui/shader.js +515 -0
- package/dist/hoi4_agent_tools/gui/source-graph.js +166 -6
- package/dist/hoi4_agent_tools/gui/studio.js +245 -19
- package/dist/hoi4_agent_tools/gui/tool-operations.js +9 -0
- package/dist/hoi4_agent_tools/gui/validators.js +38 -12
- package/dist/hoi4_agent_tools/map/operations.js +70 -1
- package/dist/hoi4_agent_tools/map/render.js +70 -1
- package/dist/hoi4_agent_tools/map/service.js +30 -6
- package/dist/hoi4_agent_tools/map/tool-operations.js +67 -1
- package/dist/hoi4_agent_tools/mcp/tools/event.js +10 -1
- package/dist/hoi4_agent_tools/mcp/tools/focus.js +14 -4
- package/dist/hoi4_agent_tools/mcp/tools/gui.js +5 -1
- package/dist/hoi4_agent_tools/mcp/tools/map.js +12 -2
- package/dist/hoi4_agent_tools/mcp/tools/probability.js +5 -0
- package/dist/hoi4_agent_tools/probability/evaluation.js +3 -0
- package/dist/hoi4_agent_tools/probability/operations.js +8 -0
- package/dist/hoi4_agent_tools/probability/service.js +33 -12
- package/dist/hoi4_agent_tools/probability/source-analysis.js +1 -0
- package/dist/hoi4_agent_tools/schemas/event.js +8 -0
- package/dist/hoi4_agent_tools/schemas/focus-requests.js +40 -1
- package/dist/hoi4_agent_tools/schemas/gui-requests.js +8 -0
- package/dist/hoi4_agent_tools/schemas/map-requests.js +22 -1
- package/dist/hoi4_agent_tools/schemas/technology.js +7 -0
- package/dist/hoi4_agent_tools/technology/graph.js +25 -10
- package/dist/hoi4_agent_tools/technology/render.js +286 -31
- package/dist/hoi4_agent_tools/technology/service.js +55 -8
- package/dist/hoi4_agent_tools/technology/source-analysis.js +166 -3
- package/dist/hoi4_agent_tools/version.js +1 -1
- package/docs/events.md +3 -1
- package/docs/focus.md +10 -0
- package/docs/gui.md +49 -3
- package/docs/map.md +8 -0
- package/docs/probability.md +3 -1
- package/docs/technology.md +7 -2
- package/package.json +1 -1
- package/schemas/configuration.schema.json +1 -1
- package/schemas/continuous-focus-palette.schema.json +1 -1
- package/schemas/custom-weighted-pool.schema.json +1 -1
- package/schemas/decision-inspect-input.schema.json +1 -1
- package/schemas/event-feature-manifest.schema.json +1 -1
- package/schemas/focus-plan.schema.json +1 -1
- package/schemas/focus-planning-sidecar.schema.json +1 -1
- package/schemas/gui-animation-source.schema.json +1 -1
- package/schemas/gui-helper.schema.json +1 -1
- package/schemas/gui-scenario.schema.json +1 -1
- package/schemas/helper-expansion-request.schema.json +1 -1
- package/schemas/helper-expansion-summary.schema.json +1 -1
- package/schemas/impact-inspect-input.schema.json +1 -1
- package/schemas/map-operation.schema.json +1 -1
- package/schemas/mechanic-test-input.schema.json +1 -1
- package/schemas/operation-result.schema.json +1 -1
- package/schemas/package-check-input.schema.json +1 -1
- package/schemas/package-manifest.schema.json +1 -1
- package/schemas/probability-analysis-result.schema.json +1 -1
- package/schemas/probability-compare-input.schema.json +1 -1
- package/schemas/probability-evaluate-input.schema.json +1 -1
- package/schemas/probability-inspect-input.schema.json +1 -1
- package/schemas/probability-render-input.schema.json +1 -1
- package/schemas/probability-scenario-set.schema.json +1 -1
- package/schemas/probability-sequence-input.schema.json +1 -1
- package/schemas/probability-simulate-input.schema.json +1 -1
- package/schemas/probability-sweep-input.schema.json +1 -1
- package/schemas/scenario-suite.schema.json +1 -1
- package/schemas/scenario-test-input.schema.json +1 -1
- package/schemas/shared-scenario.schema.json +1 -1
- package/server.json +2 -2
|
@@ -29,7 +29,7 @@ const explicitGuiElementTypes = new Set([
|
|
|
29
29
|
'scrollableTextBoxType',
|
|
30
30
|
'browserType',
|
|
31
31
|
'mapIconType',
|
|
32
|
-
]);
|
|
32
|
+
].map((type) => type.toLowerCase()));
|
|
33
33
|
const spriteTypes = new Set([
|
|
34
34
|
'spritetype',
|
|
35
35
|
'textspritetype',
|
|
@@ -65,11 +65,18 @@ const modelledElementAttributes = new Set([
|
|
|
65
65
|
'spacing',
|
|
66
66
|
'slotsize',
|
|
67
67
|
'max_slots_horizontal',
|
|
68
|
+
'max_slots_vertical',
|
|
68
69
|
'priority',
|
|
69
70
|
'minValue',
|
|
70
71
|
'maxValue',
|
|
71
72
|
'startValue',
|
|
72
73
|
'centerposition',
|
|
74
|
+
'vertical_alignment',
|
|
75
|
+
'hide',
|
|
76
|
+
'hidden',
|
|
77
|
+
'visible',
|
|
78
|
+
'enabled',
|
|
79
|
+
'context_aware_text',
|
|
73
80
|
]);
|
|
74
81
|
const ignoredElementAttributes = new Set([
|
|
75
82
|
'margin',
|
|
@@ -105,7 +112,16 @@ const ignoredElementAttributes = new Set([
|
|
|
105
112
|
'web_link',
|
|
106
113
|
'cost',
|
|
107
114
|
]);
|
|
108
|
-
export function guiElementAttributeFidelity(key) {
|
|
115
|
+
export function guiElementAttributeFidelity(key, elementType) {
|
|
116
|
+
if (elementType?.toLowerCase() === 'extendedscrollbartype' &&
|
|
117
|
+
['horizontal', 'slider', 'track', 'decreaseButton', 'increaseButton'].some((candidate) => candidate.toLowerCase() === key.toLowerCase()))
|
|
118
|
+
return 'modelled';
|
|
119
|
+
if (/^(?:containerwindowtype|windowtype)$/iu.test(elementType ?? '') &&
|
|
120
|
+
['verticalScrollbar', 'horizontalScrollbar', 'autohide_scrollbars', 'margin'].some((candidate) => candidate.toLowerCase() === key.toLowerCase()))
|
|
121
|
+
return 'structural';
|
|
122
|
+
if (elementType?.toLowerCase() === 'scrollbartype' &&
|
|
123
|
+
['horizontal', 'slider', 'track', 'leftbutton', 'rightbutton', 'borderSize'].some((candidate) => candidate.toLowerCase() === key.toLowerCase()))
|
|
124
|
+
return 'modelled';
|
|
109
125
|
const modelled = [...modelledElementAttributes].find((candidate) => candidate.toLowerCase() === key.toLowerCase());
|
|
110
126
|
if (modelled !== undefined)
|
|
111
127
|
return 'modelled';
|
|
@@ -282,9 +298,9 @@ function raw(document, node) {
|
|
|
282
298
|
function isGuiElement(assignment) {
|
|
283
299
|
if (assignment.value.type !== 'block')
|
|
284
300
|
return false;
|
|
285
|
-
if (explicitGuiElementTypes.has(assignment.key.value))
|
|
301
|
+
if (explicitGuiElementTypes.has(assignment.key.value.toLowerCase()))
|
|
286
302
|
return true;
|
|
287
|
-
return (assignment.key.value
|
|
303
|
+
return (/type$/iu.test(assignment.key.value) && firstScalar(assignment.value, 'name') !== undefined);
|
|
288
304
|
}
|
|
289
305
|
function fileKind(file) {
|
|
290
306
|
const lower = file.relativePath.toLowerCase();
|
|
@@ -296,6 +312,8 @@ function fileKind(file) {
|
|
|
296
312
|
return 'gfx_file';
|
|
297
313
|
if (lower.includes('common/scripted_guis/') && lower.endsWith('.txt'))
|
|
298
314
|
return 'scripted_gui_file';
|
|
315
|
+
if (lower.includes('common/focus_inlay_windows/') && lower.endsWith('.txt'))
|
|
316
|
+
return 'scripted_gui_file';
|
|
299
317
|
if (lower.includes('common/scripted_localisation/') && lower.endsWith('.txt'))
|
|
300
318
|
return 'scripted_localisation_file';
|
|
301
319
|
if (lower.endsWith('.txt')) {
|
|
@@ -367,7 +385,7 @@ function indexGuiElements(document, file, fileNodeId, nodes, edges, elements) {
|
|
|
367
385
|
unsupportedAttributes: assignments(assignment.value)
|
|
368
386
|
.filter((child) => !isGuiElement(child))
|
|
369
387
|
.map(({ key }) => key.value)
|
|
370
|
-
.filter((key) => guiElementAttributeFidelity(key) === 'unsupported')
|
|
388
|
+
.filter((key) => guiElementAttributeFidelity(key, assignment.key.value) === 'unsupported')
|
|
371
389
|
.sort((a, b) => compareCodeUnits(a, b)),
|
|
372
390
|
rawSource: raw(document, assignment),
|
|
373
391
|
definitionOrder: order,
|
|
@@ -419,12 +437,18 @@ function indexSpritesAndFonts(document, file, fileNodeId, nodes, edges, sprites,
|
|
|
419
437
|
const id = deterministicId('gui_sprite', { path: file.displayPath, name });
|
|
420
438
|
const texturePath = firstScalarInsensitive(child, 'texturefile', 'textureFile', 'textureFile1');
|
|
421
439
|
const texturePath2 = firstScalarInsensitive(child, 'textureFile2');
|
|
440
|
+
const primaryColour = colourFrom(child, 'color', 'colour');
|
|
441
|
+
const secondaryColour = colourFrom(child, 'colortwo', 'colourtwo');
|
|
422
442
|
const staticFallback = firstScalarInsensitive(child, 'static_fallback', 'staticFallback');
|
|
423
443
|
const animationRateFps = numberScalar(child, 'animation_rate_fps');
|
|
424
444
|
const looping = boolScalar(child, 'looping');
|
|
425
445
|
const playOnShow = boolScalar(child, 'play_on_show');
|
|
426
446
|
const pauseOnLoop = numberScalar(child, 'pause_on_loop');
|
|
427
447
|
const effectFile = firstScalarInsensitive(child, 'effectFile');
|
|
448
|
+
const shaderFeatures = assignments(child)
|
|
449
|
+
.filter(({ key, value }) => /^(?:animation|masking_texture|maskingtexture|shader_defines)$/iu.test(key.value) &&
|
|
450
|
+
(value.type === 'block' || !/^(?:no|false|0)$/iu.test(value.value)))
|
|
451
|
+
.map(({ key }) => key.value);
|
|
428
452
|
const declaredSize = sizeFrom(child);
|
|
429
453
|
const borderSize = nestedSizeFrom(child, 'borderSize');
|
|
430
454
|
const tilingCenter = boolScalar(child, 'tilingCenter');
|
|
@@ -438,6 +462,8 @@ function indexSpritesAndFonts(document, file, fileNodeId, nodes, edges, sprites,
|
|
|
438
462
|
spriteType: assignment.key.value,
|
|
439
463
|
...(texturePath === undefined ? {} : { texturePath }),
|
|
440
464
|
...(texturePath2 === undefined ? {} : { texturePath2 }),
|
|
465
|
+
...(primaryColour === undefined ? {} : { primaryColour }),
|
|
466
|
+
...(secondaryColour === undefined ? {} : { secondaryColour }),
|
|
441
467
|
frameCount: Math.trunc(numberScalar(child, 'noOfFrames', 'noofframes') ?? 1),
|
|
442
468
|
frameAnimated: spriteType === 'frameanimatedspritetype',
|
|
443
469
|
...(animationRateFps === undefined ? {} : { animationRateFps }),
|
|
@@ -445,6 +471,7 @@ function indexSpritesAndFonts(document, file, fileNodeId, nodes, edges, sprites,
|
|
|
445
471
|
...(playOnShow === undefined ? {} : { playOnShow }),
|
|
446
472
|
...(pauseOnLoop === undefined ? {} : { pauseOnLoop }),
|
|
447
473
|
...(effectFile === undefined ? {} : { effectFile }),
|
|
474
|
+
...(shaderFeatures.length === 0 ? {} : { shaderFeatures }),
|
|
448
475
|
...(staticFallback === undefined ? {} : { staticFallback }),
|
|
449
476
|
...(declaredSize === undefined ? {} : { declaredSize }),
|
|
450
477
|
...(borderSize === undefined ? {} : { borderSize }),
|
|
@@ -594,6 +621,128 @@ function constantTriggerResult(value) {
|
|
|
594
621
|
return false;
|
|
595
622
|
return undefined;
|
|
596
623
|
}
|
|
624
|
+
function indexFocusInlays(document, file, fileNodeId, nodes, edges, scriptedGuis) {
|
|
625
|
+
for (const assignment of assignments(document.root)) {
|
|
626
|
+
if (assignment.value.type !== 'block')
|
|
627
|
+
continue;
|
|
628
|
+
const block = assignment.value;
|
|
629
|
+
const windowName = firstScalarInsensitive(block, 'window_name');
|
|
630
|
+
if (windowName === undefined)
|
|
631
|
+
continue;
|
|
632
|
+
const name = assignment.key.value;
|
|
633
|
+
const id = deterministicId('scripted_gui', { path: file.displayPath, name });
|
|
634
|
+
const location = nodeLocation(document, assignment, name);
|
|
635
|
+
const imageDefinitions = namedAssignments(block, 'scripted_images').flatMap((image) => image.value.type !== 'block'
|
|
636
|
+
? []
|
|
637
|
+
: [
|
|
638
|
+
{
|
|
639
|
+
elementName: image.key.value,
|
|
640
|
+
location: nodeLocation(document, image, image.key.value),
|
|
641
|
+
choices: assignments(image.value).map((choice) => ({
|
|
642
|
+
spriteName: choice.key.value,
|
|
643
|
+
conditionExpression: choice.value.type === 'block'
|
|
644
|
+
? raw(document, choice.value)
|
|
645
|
+
: `{ always = ${choice.value.value} }`,
|
|
646
|
+
location: nodeLocation(document, choice, choice.key.value),
|
|
647
|
+
})),
|
|
648
|
+
},
|
|
649
|
+
]);
|
|
650
|
+
const triggerDefinitions = [];
|
|
651
|
+
const effectDefinitions = [];
|
|
652
|
+
for (const button of namedAssignments(block, 'scripted_buttons')) {
|
|
653
|
+
if (button.value.type !== 'block')
|
|
654
|
+
continue;
|
|
655
|
+
const available = childBlocks(button.value, 'available')[0];
|
|
656
|
+
const effect = childBlocks(button.value, 'click_effect')[0];
|
|
657
|
+
if (available !== undefined) {
|
|
658
|
+
const triggerName = `${button.key.value}_click_enabled`;
|
|
659
|
+
const constantResult = constantTriggerResult(available);
|
|
660
|
+
triggerDefinitions.push({
|
|
661
|
+
name: triggerName,
|
|
662
|
+
elementName: button.key.value,
|
|
663
|
+
rawSource: `${triggerName} = ${raw(document, available)}`,
|
|
664
|
+
conditionExpression: raw(document, available),
|
|
665
|
+
location: nodeLocation(document, button, button.key.value),
|
|
666
|
+
...(constantResult === undefined ? {} : { constantResult }),
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
if (effect !== undefined)
|
|
670
|
+
effectDefinitions.push({
|
|
671
|
+
name: `${button.key.value}_click`,
|
|
672
|
+
elementName: button.key.value,
|
|
673
|
+
costs: directEffectCosts(effect),
|
|
674
|
+
rawSource: raw(document, effect),
|
|
675
|
+
location: nodeLocation(document, button, button.key.value),
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
const propertyDefinitions = namedAssignments(block, 'scripted_progressbars').map((bar) => ({
|
|
679
|
+
elementName: bar.key.value,
|
|
680
|
+
attributes: bar.value.type === 'block'
|
|
681
|
+
? sourceValueToProperty(bar.value, constantsFor(document))
|
|
682
|
+
: {},
|
|
683
|
+
rawSource: raw(document, bar),
|
|
684
|
+
location: nodeLocation(document, bar, bar.key.value),
|
|
685
|
+
}));
|
|
686
|
+
const visible = childBlocks(block, 'visible')[0];
|
|
687
|
+
const definition = {
|
|
688
|
+
id,
|
|
689
|
+
name,
|
|
690
|
+
sourcePath: file.displayPath,
|
|
691
|
+
location,
|
|
692
|
+
windowName,
|
|
693
|
+
contextType: 'country_context',
|
|
694
|
+
interfaceKind: 'focus-inlay',
|
|
695
|
+
internal: boolScalar(block, 'internal') ?? false,
|
|
696
|
+
...(visible === undefined ? {} : { visibleExpression: raw(document, visible) }),
|
|
697
|
+
effects: effectDefinitions.map(({ name }) => name),
|
|
698
|
+
effectDefinitions,
|
|
699
|
+
triggers: triggerDefinitions.map(({ name }) => name),
|
|
700
|
+
triggerDefinitions,
|
|
701
|
+
properties: [
|
|
702
|
+
...new Set([
|
|
703
|
+
...imageDefinitions.map(({ elementName }) => elementName),
|
|
704
|
+
...propertyDefinitions.map(({ elementName }) => elementName),
|
|
705
|
+
]),
|
|
706
|
+
],
|
|
707
|
+
propertyDefinitions,
|
|
708
|
+
dynamicLists: [],
|
|
709
|
+
dynamicListDefinitions: [],
|
|
710
|
+
aiWeights: [],
|
|
711
|
+
aiEnabled: false,
|
|
712
|
+
rawSource: raw(document, assignment),
|
|
713
|
+
imageDefinitions,
|
|
714
|
+
};
|
|
715
|
+
addDomainEntry(scriptedGuis, definition, 'focus inlay');
|
|
716
|
+
addNode(nodes, {
|
|
717
|
+
id,
|
|
718
|
+
name,
|
|
719
|
+
kind: 'scripted_gui',
|
|
720
|
+
path: file.displayPath,
|
|
721
|
+
location,
|
|
722
|
+
metadata: { interfaceKind: 'focus-inlay', internal: definition.internal, windowName },
|
|
723
|
+
});
|
|
724
|
+
addEdge(edges, 'contains', fileNodeId, id, true, {}, location);
|
|
725
|
+
for (const [kind, actions] of [
|
|
726
|
+
['scripted_effect', definition.effects],
|
|
727
|
+
['scripted_trigger', definition.triggers],
|
|
728
|
+
]) {
|
|
729
|
+
for (const action of actions) {
|
|
730
|
+
const childId = deterministicId(kind === 'scripted_effect' ? 'gui_effect' : 'gui_trigger', {
|
|
731
|
+
id,
|
|
732
|
+
[kind === 'scripted_effect' ? 'effect' : 'trigger']: action,
|
|
733
|
+
});
|
|
734
|
+
addNode(nodes, {
|
|
735
|
+
id: childId,
|
|
736
|
+
name: action,
|
|
737
|
+
kind,
|
|
738
|
+
path: file.displayPath,
|
|
739
|
+
metadata: { scriptedGui: name, element: actionElementName(action) },
|
|
740
|
+
});
|
|
741
|
+
addEdge(edges, 'contains', id, childId, true);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
597
746
|
function indexScriptedGuis(document, file, fileNodeId, nodes, edges, scriptedGuis) {
|
|
598
747
|
for (const root of childBlocks(document.root, 'scripted_gui')) {
|
|
599
748
|
for (const assignment of assignments(root)) {
|
|
@@ -618,6 +767,7 @@ function indexScriptedGuis(document, file, fileNodeId, nodes, edges, scriptedGui
|
|
|
618
767
|
elementName: actionElementName(trigger.key.value),
|
|
619
768
|
...(constantResult === undefined ? {} : { constantResult }),
|
|
620
769
|
rawSource: raw(document, trigger),
|
|
770
|
+
conditionExpression: raw(document, trigger.value),
|
|
621
771
|
location: nodeLocation(document, trigger, trigger.key.value),
|
|
622
772
|
};
|
|
623
773
|
});
|
|
@@ -985,6 +1135,12 @@ function linkGraph(nodes, edges, elements, sprites, fonts, scriptedGuis, scripte
|
|
|
985
1135
|
}
|
|
986
1136
|
}
|
|
987
1137
|
for (const gui of scriptedGuis) {
|
|
1138
|
+
for (const image of gui.imageDefinitions ?? []) {
|
|
1139
|
+
for (const choice of image.choices) {
|
|
1140
|
+
const sprite = spriteByName.get(choice.spriteName.toLocaleLowerCase('en-US'));
|
|
1141
|
+
addEdge(edges, 'uses_sprite', gui.id, sprite?.id ?? `sprite:${choice.spriteName}`, sprite !== undefined, { elementName: image.elementName, spriteName: choice.spriteName }, choice.location);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
988
1144
|
if (gui.windowName !== undefined) {
|
|
989
1145
|
const window = elementByName.get(gui.windowName);
|
|
990
1146
|
addEdge(edges, 'window', gui.id, window?.id ?? `gui_element:${gui.windowName}`, window !== undefined, { windowName: gui.windowName }, gui.location);
|
|
@@ -1043,6 +1199,7 @@ function linkGraph(nodes, edges, elements, sprites, fonts, scriptedGuis, scripte
|
|
|
1043
1199
|
}
|
|
1044
1200
|
const localisationAttributeNames = [
|
|
1045
1201
|
'text',
|
|
1202
|
+
'context_aware_text',
|
|
1046
1203
|
'buttonText',
|
|
1047
1204
|
'pdx_tooltip',
|
|
1048
1205
|
'pdx_tooltip_delayed',
|
|
@@ -1267,7 +1424,10 @@ export function buildGuiSourceGraph(files, sharedIndex) {
|
|
|
1267
1424
|
indexSpritesAndFonts(document, file, fileNodeId, nodes, edges, sprites, fonts, textColours);
|
|
1268
1425
|
}
|
|
1269
1426
|
if (kind === 'scripted_gui_file') {
|
|
1270
|
-
|
|
1427
|
+
if (normalized.startsWith('common/focus_inlay_windows/'))
|
|
1428
|
+
indexFocusInlays(document, file, fileNodeId, nodes, edges, scriptedGuis);
|
|
1429
|
+
else
|
|
1430
|
+
indexScriptedGuis(document, file, fileNodeId, nodes, edges, scriptedGuis);
|
|
1271
1431
|
}
|
|
1272
1432
|
if (kind === 'scripted_localisation_file') {
|
|
1273
1433
|
indexScriptedLocalisation(document, file, fileNodeId, nodes, edges, scriptedLocalisation);
|