hoi4-agent-tools 3.2.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 +19 -0
- package/README.md +4 -0
- package/dist/hoi4_agent_tools/core/analysis-job-operations.js +11 -1
- package/dist/hoi4_agent_tools/core/condition-evaluator.js +18 -9
- package/dist/hoi4_agent_tools/core/helper-expansion.js +3 -0
- package/dist/hoi4_agent_tools/core/job-worker-host.js +3 -0
- package/dist/hoi4_agent_tools/core/operation-tasks.js +7 -0
- package/dist/hoi4_agent_tools/core/scenario-model.js +85 -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/scenario-conditions.js +2 -23
- 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/server/instructions.js +1 -1
- package/dist/hoi4_agent_tools/mcp/server/request-lifecycle.js +3 -0
- package/dist/hoi4_agent_tools/mcp/tools/analysis.js +90 -0
- 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/mechanic/assertions.js +112 -0
- package/dist/hoi4_agent_tools/mechanic/interpreter.js +619 -0
- package/dist/hoi4_agent_tools/mechanic/service.js +145 -0
- package/dist/hoi4_agent_tools/mechanic/source.js +89 -0
- package/dist/hoi4_agent_tools/package-check/service.js +231 -0
- package/dist/hoi4_agent_tools/probability/evaluation.js +6 -1
- 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/scenario-suite/service.js +341 -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/scenarios.js +264 -0
- 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/README.md +1 -0
- 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/mechanics.md +70 -0
- package/docs/probability.md +3 -1
- package/docs/technology.md +7 -2
- package/package.json +2 -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 +650 -0
- package/schemas/operation-result.schema.json +1 -1
- package/schemas/package-check-input.schema.json +164 -0
- package/schemas/package-manifest.schema.json +142 -0
- 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 +1004 -0
- package/schemas/scenario-test-input.schema.json +1057 -0
- package/schemas/shared-scenario.schema.json +237 -0
- package/server.json +2 -2
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { compareCodeUnits } from '../core/canonical.js';
|
|
2
|
+
import { ServiceError } from '../core/result.js';
|
|
3
|
+
function routeLockSatisfied(lock, completed) {
|
|
4
|
+
const predicates = [
|
|
5
|
+
...lock.requiredFocusIds.map((id) => completed.has(id)),
|
|
6
|
+
...lock.excludedFocusIds.map((id) => !completed.has(id)),
|
|
7
|
+
...(lock.alwaysImpossible === true ? [false] : []),
|
|
8
|
+
];
|
|
9
|
+
return lock.mode === 'any' ? predicates.some(Boolean) : predicates.every(Boolean);
|
|
10
|
+
}
|
|
11
|
+
/** Reports only structural evidence. Unsupported runtime triggers remain unresolved. */
|
|
12
|
+
export function inspectFocusScenario(plan, completedFocusIds) {
|
|
13
|
+
const known = new Set(plan.focuses.map(({ id }) => id));
|
|
14
|
+
const completed = new Set(completedFocusIds);
|
|
15
|
+
const excludedBy = new Map();
|
|
16
|
+
for (const focus of plan.focuses)
|
|
17
|
+
for (const excluded of focus.mutuallyExclusive) {
|
|
18
|
+
const owners = excludedBy.get(excluded) ?? [];
|
|
19
|
+
owners.push(focus.id);
|
|
20
|
+
excludedBy.set(excluded, owners);
|
|
21
|
+
}
|
|
22
|
+
for (const id of completed)
|
|
23
|
+
if (!known.has(id))
|
|
24
|
+
throw new ServiceError('FOCUS_SCENARIO_UNKNOWN_FOCUS', 'Scenario names an unknown focus', {
|
|
25
|
+
treeId: plan.id,
|
|
26
|
+
focusId: id,
|
|
27
|
+
});
|
|
28
|
+
const focuses = [...plan.focuses]
|
|
29
|
+
.sort((left, right) => compareCodeUnits(left.id, right.id))
|
|
30
|
+
.map((focus) => {
|
|
31
|
+
const missingPrerequisiteGroups = focus.prerequisites.groups
|
|
32
|
+
.filter(({ focusIds }) => !focusIds.some((id) => completed.has(id)))
|
|
33
|
+
.map(({ focusIds }) => [...focusIds].sort(compareCodeUnits));
|
|
34
|
+
const blockedRouteLockIds = focus.routeLocks
|
|
35
|
+
.filter((lock) => !routeLockSatisfied(lock, completed))
|
|
36
|
+
.map(({ id }) => id)
|
|
37
|
+
.sort(compareCodeUnits);
|
|
38
|
+
const exclusiveWithCompletedIds = [
|
|
39
|
+
...new Set([...focus.mutuallyExclusive, ...(excludedBy.get(focus.id) ?? [])]),
|
|
40
|
+
]
|
|
41
|
+
.filter((id) => completed.has(id))
|
|
42
|
+
.sort(compareCodeUnits);
|
|
43
|
+
const unresolvedFields = [
|
|
44
|
+
...(focus.availability === undefined ? [] : ['available']),
|
|
45
|
+
...(focus.allowBranch === undefined ? [] : ['allow_branch']),
|
|
46
|
+
...(focus.bypass === undefined ? [] : ['bypass']),
|
|
47
|
+
...(focus.visibility === 'normal' ||
|
|
48
|
+
(focus.reveal?.kind === 'allow_branch' &&
|
|
49
|
+
focus.routeLocks.some(({ field }) => field === 'allow_branch'))
|
|
50
|
+
? []
|
|
51
|
+
: ['visibility']),
|
|
52
|
+
];
|
|
53
|
+
const blocked = missingPrerequisiteGroups.length > 0 ||
|
|
54
|
+
blockedRouteLockIds.length > 0 ||
|
|
55
|
+
exclusiveWithCompletedIds.length > 0;
|
|
56
|
+
const status = completed.has(focus.id)
|
|
57
|
+
? 'completed'
|
|
58
|
+
: blocked
|
|
59
|
+
? 'blocked'
|
|
60
|
+
: unresolvedFields.length > 0
|
|
61
|
+
? 'unresolved'
|
|
62
|
+
: 'structural_candidate';
|
|
63
|
+
return {
|
|
64
|
+
focusId: focus.id,
|
|
65
|
+
status,
|
|
66
|
+
missingPrerequisiteGroups,
|
|
67
|
+
blockedRouteLockIds,
|
|
68
|
+
exclusiveWithCompletedIds,
|
|
69
|
+
unresolvedFields,
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
const conflicts = new Set();
|
|
73
|
+
for (const focus of plan.focuses) {
|
|
74
|
+
if (!completed.has(focus.id))
|
|
75
|
+
continue;
|
|
76
|
+
for (const other of focus.mutuallyExclusive) {
|
|
77
|
+
if (!completed.has(other))
|
|
78
|
+
continue;
|
|
79
|
+
conflicts.add([focus.id, other].sort(compareCodeUnits).join('\0'));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const mutualExclusionConflicts = [...conflicts].sort(compareCodeUnits).map((pair) => {
|
|
83
|
+
const [leftFocusId, rightFocusId] = pair.split('\0');
|
|
84
|
+
return { leftFocusId: leftFocusId, rightFocusId: rightFocusId };
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
treeId: plan.id,
|
|
88
|
+
completedFocusIds: [...completed].sort(compareCodeUnits),
|
|
89
|
+
focuses,
|
|
90
|
+
mutualExclusionConflicts,
|
|
91
|
+
counts: {
|
|
92
|
+
completed: focuses.filter(({ status }) => status === 'completed').length,
|
|
93
|
+
blocked: focuses.filter(({ status }) => status === 'blocked').length,
|
|
94
|
+
unresolved: focuses.filter(({ status }) => status === 'unresolved').length,
|
|
95
|
+
structuralCandidates: focuses.filter(({ status }) => status === 'structural_candidate')
|
|
96
|
+
.length,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
@@ -11,6 +11,7 @@ import { setInlineFilesScanned } from '../core/operation-result.js';
|
|
|
11
11
|
import { PACKAGE_VERSION } from '../version.js';
|
|
12
12
|
import { focusPlanHash, focusPresentationEvidence, importContinuousFocusPalettes, assertCompactLayoutQuality, compactFocusTreePlanAsync, linkContinuousFocusPalettes, resolveFocusPresentation, } from './index.js';
|
|
13
13
|
import { activeContinuousFocusFiles, activeFocusFiles, activeSidecar, continuousPalettes, importFocusFile, referenceCatalog, selectContinuousPlan, selectedContinuousFocusFile, selectedFocusFile, selectPlan, underConfiguredRoot, validationFromDiagnostics, } from './tool-support.js';
|
|
14
|
+
import { inspectFocusScenario } from './scenario.js';
|
|
14
15
|
const LARGE_FOCUS_RENDER_NODE_THRESHOLD = 200;
|
|
15
16
|
const LARGE_FOCUS_RENDER_SCALE = 0.5;
|
|
16
17
|
function automaticFocusRenderScale(focusCount) {
|
|
@@ -196,6 +197,9 @@ export async function prepareFocusRewrite(engine, workbench, input, context) {
|
|
|
196
197
|
compactSourcePlan = suppliedPlan;
|
|
197
198
|
compactPlanning = await compactFocusTreePlanAsync(suppliedPlan, {
|
|
198
199
|
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
200
|
+
...(input.compactFocusIds === undefined ? {} : { focusIds: input.compactFocusIds }),
|
|
201
|
+
...(input.pinnedFocusIds === undefined ? {} : { pinnedFocusIds: input.pinnedFocusIds }),
|
|
202
|
+
...(input.symmetryGroups === undefined ? {} : { symmetryGroups: input.symmetryGroups }),
|
|
199
203
|
});
|
|
200
204
|
plan = compactPlanning.plan;
|
|
201
205
|
}
|
|
@@ -208,6 +212,9 @@ export async function prepareFocusRewrite(engine, workbench, input, context) {
|
|
|
208
212
|
compactSourcePlan = selectPlan(imported.result, input.treeId);
|
|
209
213
|
compactPlanning = await compactFocusTreePlanAsync(compactSourcePlan, {
|
|
210
214
|
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
215
|
+
...(input.compactFocusIds === undefined ? {} : { focusIds: input.compactFocusIds }),
|
|
216
|
+
...(input.pinnedFocusIds === undefined ? {} : { pinnedFocusIds: input.pinnedFocusIds }),
|
|
217
|
+
...(input.symmetryGroups === undefined ? {} : { symmetryGroups: input.symmetryGroups }),
|
|
211
218
|
});
|
|
212
219
|
plan = compactPlanning.plan;
|
|
213
220
|
}
|
|
@@ -267,7 +274,7 @@ export async function prepareFocusRewrite(engine, workbench, input, context) {
|
|
|
267
274
|
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
268
275
|
}));
|
|
269
276
|
if (input.layoutMode === 'compact')
|
|
270
|
-
assertCompactLayoutQuality(currentLayout, proposedLayout);
|
|
277
|
+
assertCompactLayoutQuality(currentLayout, proposedLayout, input.compactFocusIds !== undefined);
|
|
271
278
|
const proposed = await workbench.renderAndStore(context.workspaceId, reviewPlan, {
|
|
272
279
|
...(context.principal === undefined ? {} : { principal: context.principal }),
|
|
273
280
|
index: snapshot.index,
|
|
@@ -481,7 +488,14 @@ export async function inspectFocus(engine, workbench, input, context) {
|
|
|
481
488
|
layout,
|
|
482
489
|
});
|
|
483
490
|
diagnostics.push(...lintDiagnostics);
|
|
484
|
-
inspectedPlans.push({
|
|
491
|
+
inspectedPlans.push({
|
|
492
|
+
plan,
|
|
493
|
+
layout,
|
|
494
|
+
diagnosticCount: lintDiagnostics.length,
|
|
495
|
+
...(input.scenario === undefined
|
|
496
|
+
? {}
|
|
497
|
+
: { scenario: inspectFocusScenario(plan, input.scenario.completedFocusIds) }),
|
|
498
|
+
});
|
|
485
499
|
}
|
|
486
500
|
const presentation = await resolveFocusPresentation({
|
|
487
501
|
plans,
|
|
@@ -504,10 +518,11 @@ export async function inspectFocus(engine, workbench, input, context) {
|
|
|
504
518
|
schemaVersion: 1,
|
|
505
519
|
revision: snapshot.revision,
|
|
506
520
|
plans,
|
|
507
|
-
layouts: inspectedPlans.map(({ plan, layout, diagnosticCount }) => ({
|
|
521
|
+
layouts: inspectedPlans.map(({ plan, layout, diagnosticCount, scenario }) => ({
|
|
508
522
|
treeId: plan.id,
|
|
509
523
|
layout,
|
|
510
524
|
diagnosticCount,
|
|
525
|
+
...(scenario === undefined ? {} : { scenario }),
|
|
511
526
|
})),
|
|
512
527
|
continuousFocusPalettes: paletteImport.palettes,
|
|
513
528
|
presentation: focusPresentationEvidence(presentation),
|
|
@@ -525,7 +540,7 @@ export async function inspectFocus(engine, workbench, input, context) {
|
|
|
525
540
|
revision: snapshot.revision,
|
|
526
541
|
treeCount: plans.length,
|
|
527
542
|
paletteCount: paletteImport.palettes.length,
|
|
528
|
-
trees: inspectedPlans.slice(0, 100).map(({ plan, layout, diagnosticCount }) => ({
|
|
543
|
+
trees: inspectedPlans.slice(0, 100).map(({ plan, layout, diagnosticCount, scenario }) => ({
|
|
529
544
|
id: plan.id,
|
|
530
545
|
sourcePath: plan.provenance.sourcePath,
|
|
531
546
|
focusCount: plan.focuses.length,
|
|
@@ -539,6 +554,14 @@ export async function inspectFocus(engine, workbench, input, context) {
|
|
|
539
554
|
layoutDecisionCount: layout.decisions.length,
|
|
540
555
|
layoutMetrics: layout.metrics,
|
|
541
556
|
diagnosticCount,
|
|
557
|
+
...(scenario === undefined
|
|
558
|
+
? {}
|
|
559
|
+
: {
|
|
560
|
+
scenarioSummary: {
|
|
561
|
+
...scenario.counts,
|
|
562
|
+
choiceConflictCount: scenario.mutualExclusionConflicts.length,
|
|
563
|
+
},
|
|
564
|
+
}),
|
|
542
565
|
})),
|
|
543
566
|
palettes: [],
|
|
544
567
|
});
|
|
@@ -559,6 +582,8 @@ export async function inspectFocus(engine, workbench, input, context) {
|
|
|
559
582
|
}
|
|
560
583
|
export async function renderFocus(engine, workbench, input, context, rasterize) {
|
|
561
584
|
const outputName = rasterize ? 'raster' : 'render';
|
|
585
|
+
if (!rasterize && (input.cropFocusIds?.length ?? 0) > 0)
|
|
586
|
+
throw new ServiceError('FOCUS_CROP_REQUIRES_RASTER', 'Focus problem crops require focus_raster');
|
|
562
587
|
await context.progress(0, 4, 'Importing and indexing focus source');
|
|
563
588
|
const workspace = engine.resolver.get(context.workspaceId, context.principal);
|
|
564
589
|
const snapshot = await engine.scan(context.workspaceId, { patterns: focusDomainScanPatterns(workspace) }, context.principal, context.signal);
|
|
@@ -638,6 +663,7 @@ export async function renderFocus(engine, workbench, input, context, rasterize)
|
|
|
638
663
|
...(input.padding === undefined ? {} : { padding: input.padding }),
|
|
639
664
|
outputScale: effectiveReviewScale,
|
|
640
665
|
rasterize,
|
|
666
|
+
...(input.cropFocusIds === undefined ? {} : { cropFocusIds: input.cropFocusIds }),
|
|
641
667
|
renderProfile: {
|
|
642
668
|
sourceRevision: snapshot.revision,
|
|
643
669
|
reviewScale: effectiveReviewScale,
|
|
@@ -8,6 +8,7 @@ import { DETERMINISTIC_TOOL_FONT_HASH, shapeFontkitOutline } from '../core/svg-t
|
|
|
8
8
|
import { decodeDds } from './dds.js';
|
|
9
9
|
import { GUI_BMFONT_MAX_CHARACTERS, GUI_BINARY_FONT_MAX_BYTES, GUI_BMFONT_MAX_BYTES, GUI_BMFONT_MAX_FIELDS_PER_RECORD, GUI_BMFONT_MAX_KERNING_PAIRS, GUI_BMFONT_MAX_PAGES, GUI_BMFONT_MAX_RECORDS, GUI_TEXT_MAX_CHARACTERS, GUI_TEXT_MAX_MISSING_GLYPH_SAMPLES, } from './limits.js';
|
|
10
10
|
import { decodeTga } from './tga.js';
|
|
11
|
+
import { compileGuiProgressEffect, compileGuiShaderEffect, GUI_SHADER_MAX_BYTES, } from './shader.js';
|
|
11
12
|
function normalizeAssetPath(value) {
|
|
12
13
|
return value
|
|
13
14
|
.replaceAll('\\', '/')
|
|
@@ -157,6 +158,8 @@ export class GuiAssetCatalog {
|
|
|
157
158
|
rasters = new Map();
|
|
158
159
|
frames = new Map();
|
|
159
160
|
countryFlags = new Map();
|
|
161
|
+
shaders = new Map();
|
|
162
|
+
progressShaders = new Map();
|
|
160
163
|
glyphRasters = new Map();
|
|
161
164
|
metrics = new Map();
|
|
162
165
|
fontDefinitions = new Map();
|
|
@@ -294,11 +297,58 @@ export class GuiAssetCatalog {
|
|
|
294
297
|
loadSecondarySpriteFrame(sprite, requestedFrame) {
|
|
295
298
|
return this.loadSpriteTextureFrame(sprite, sprite.texturePath2, requestedFrame, 'secondary');
|
|
296
299
|
}
|
|
300
|
+
shaderSource(sprite) {
|
|
301
|
+
if ((sprite.shaderFeatures?.length ?? 0) > 0)
|
|
302
|
+
return {
|
|
303
|
+
supported: false,
|
|
304
|
+
reason: `Shader features require additional composition: ${sprite.shaderFeatures.join(', ')}.`,
|
|
305
|
+
};
|
|
306
|
+
const reference = sprite.effectFile;
|
|
307
|
+
if (reference === undefined)
|
|
308
|
+
return { supported: false, reason: 'Sprite has no effect source.' };
|
|
309
|
+
const file = this.resolveFile(reference, sprite.sourcePath) ??
|
|
310
|
+
(/\.lua$/iu.test(reference)
|
|
311
|
+
? this.resolveFile(reference.replace(/\.lua$/iu, '.shader'), sprite.sourcePath)
|
|
312
|
+
: undefined);
|
|
313
|
+
if (file === undefined)
|
|
314
|
+
return { supported: false, reason: `Shader source not found: ${reference}.` };
|
|
315
|
+
if (file.bytes.length > GUI_SHADER_MAX_BYTES)
|
|
316
|
+
return { supported: false, reason: 'Shader source exceeds its byte budget.' };
|
|
317
|
+
return { supported: true, file };
|
|
318
|
+
}
|
|
319
|
+
progressShader(sprite, effect) {
|
|
320
|
+
const source = this.shaderSource(sprite);
|
|
321
|
+
if (!source.supported)
|
|
322
|
+
return source;
|
|
323
|
+
const { file } = source;
|
|
324
|
+
const key = `${file.displayPath}:${file.sha256}:${effect}`;
|
|
325
|
+
let result = this.progressShaders.get(key);
|
|
326
|
+
if (result === undefined) {
|
|
327
|
+
result = compileGuiProgressEffect(file.bytes.toString('utf8'), { path: file.displayPath, sha256: file.sha256 }, effect);
|
|
328
|
+
if (this.progressShaders.size >= 1_024)
|
|
329
|
+
this.progressShaders.delete(this.progressShaders.keys().next().value);
|
|
330
|
+
this.progressShaders.set(key, result);
|
|
331
|
+
}
|
|
332
|
+
return result;
|
|
333
|
+
}
|
|
334
|
+
spriteShader(sprite, effect, uniforms) {
|
|
335
|
+
const source = this.shaderSource(sprite);
|
|
336
|
+
if (!source.supported)
|
|
337
|
+
return source;
|
|
338
|
+
const { file } = source;
|
|
339
|
+
const key = `${file.displayPath}:${file.sha256}:${effect}:${hashCanonical(uniforms)}`;
|
|
340
|
+
let result = this.shaders.get(key);
|
|
341
|
+
if (result === undefined) {
|
|
342
|
+
result = compileGuiShaderEffect(file.bytes.toString('utf8'), { path: file.displayPath, sha256: file.sha256 }, effect, uniforms);
|
|
343
|
+
// Per-render caching is bounded even with a different clock or tint for every row.
|
|
344
|
+
if (this.shaders.size >= 1_024)
|
|
345
|
+
this.shaders.delete(this.shaders.keys().next().value);
|
|
346
|
+
this.shaders.set(key, result);
|
|
347
|
+
}
|
|
348
|
+
return result;
|
|
349
|
+
}
|
|
297
350
|
loadCountryFlag(tag, sprite, ideology, slotName) {
|
|
298
|
-
const normalizedTag = tag
|
|
299
|
-
.trim()
|
|
300
|
-
.replace(/[^A-Za-z0-9_]/gu, '')
|
|
301
|
-
.toUpperCase();
|
|
351
|
+
const normalizedTag = tag.trim().toUpperCase();
|
|
302
352
|
const normalizedIdeology = ideology
|
|
303
353
|
?.trim()
|
|
304
354
|
.replace(/[^A-Za-z0-9_]/gu, '')
|