@px-lsp/server 0.1.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/LICENSE +674 -0
- package/README.md +334 -0
- package/THIRD-PARTY-NOTICES.md +83 -0
- package/data/ck3/dataTypes.json +2195 -0
- package/data/ck3/data_types/data_types_common.txt +2040 -0
- package/data/ck3/data_types/data_types_gui.txt +5264 -0
- package/data/ck3/data_types/data_types_internalclausewitzgui.txt +14843 -0
- package/data/ck3/data_types/data_types_script.txt +4251 -0
- package/data/ck3/data_types/data_types_uncategorized.txt +109984 -0
- package/data/ck3/freqs.json +1 -0
- package/data/ck3/guiSchema.json +6344 -0
- package/data/ck3/script_docs/effects.log +16059 -0
- package/data/ck3/script_docs/event_targets.log +2098 -0
- package/data/ck3/script_docs/modifiers.log +2228 -0
- package/data/ck3/script_docs/on_actions.log +5275 -0
- package/data/ck3/script_docs/triggers.log +11991 -0
- package/data/ck3/structures.json +9743 -0
- package/data/ck3/wikidocs/ATTRIBUTION.md +18 -0
- package/data/ck3/wikidocs/Data_types.md +2568 -0
- package/data/ck3/wikidocs/Effects_list.md +1176 -0
- package/data/ck3/wikidocs/Scopes_list.md +341 -0
- package/data/ck3/wikidocs/Triggers_list.md +1097 -0
- package/data/eu5/data_types/data_types_common.txt +2087 -0
- package/data/eu5/data_types/data_types_gui.txt +6732 -0
- package/data/eu5/data_types/data_types_internalclausewitzgui.txt +19276 -0
- package/data/eu5/data_types/data_types_script.txt +5688 -0
- package/data/eu5/data_types/data_types_uncategorized.txt +135569 -0
- package/data/vic3/data_types/data_types_common.txt +2021 -0
- package/data/vic3/data_types/data_types_gui.txt +5592 -0
- package/data/vic3/data_types/data_types_internalclausewitzgui.txt +17304 -0
- package/data/vic3/data_types/data_types_script.txt +2817 -0
- package/data/vic3/data_types/data_types_uncategorized.txt +84354 -0
- package/data/vic3/freqs.json +1 -0
- package/data/vic3/guiSchema.json +5578 -0
- package/data/vic3/script_docs/effects.log +38135 -0
- package/data/vic3/script_docs/event_targets.log +2028 -0
- package/data/vic3/script_docs/modifiers.log +18954 -0
- package/data/vic3/script_docs/on_actions.log +1561 -0
- package/data/vic3/script_docs/triggers.log +15738 -0
- package/data/vic3/structures.json +10189 -0
- package/dist/server.js +63668 -0
- package/media/px-lsp.svg +12 -0
- package/package.json +50 -0
- package/src/clientMode.ts +60 -0
- package/src/coa/coa.ts +184 -0
- package/src/coa/coaParse.ts +267 -0
- package/src/context.ts +78 -0
- package/src/contextKeywords.ts +224 -0
- package/src/data/dataBindingMacros.ts +82 -0
- package/src/data/dataFnDocs.ts +152 -0
- package/src/data/dataFnUsage.ts +431 -0
- package/src/data/dataTypes.ts +279 -0
- package/src/data/defines.ts +123 -0
- package/src/data/docsParser.ts +453 -0
- package/src/data/keywordDocs.ts +98 -0
- package/src/data/modifierTemplates.ts +143 -0
- package/src/data/textFormatting.ts +165 -0
- package/src/data/wikiDocs.ts +187 -0
- package/src/dds/decoder.ts +1007 -0
- package/src/dds/encode.ts +235 -0
- package/src/dds/index.ts +58 -0
- package/src/dds/png.ts +96 -0
- package/src/dds/tga.ts +62 -0
- package/src/documents.ts +35 -0
- package/src/features/assetPaths.ts +169 -0
- package/src/features/codeActions.ts +148 -0
- package/src/features/colors.ts +244 -0
- package/src/features/completion.ts +961 -0
- package/src/features/datafunction.ts +729 -0
- package/src/features/definition.ts +84 -0
- package/src/features/diagnostics.ts +244 -0
- package/src/features/folding.ts +106 -0
- package/src/features/formatting.ts +60 -0
- package/src/features/guiLanguage.ts +366 -0
- package/src/features/guiNavigation.ts +140 -0
- package/src/features/guiTree.ts +97 -0
- package/src/features/hover.ts +817 -0
- package/src/features/hoverRender.ts +222 -0
- package/src/features/inlayHints.ts +147 -0
- package/src/features/locFormatting.ts +127 -0
- package/src/features/references.ts +70 -0
- package/src/features/rename.ts +135 -0
- package/src/features/scopeAt.ts +65 -0
- package/src/features/semanticTokens.ts +188 -0
- package/src/features/signatureHelp.ts +72 -0
- package/src/features/symbols.ts +241 -0
- package/src/features/textureHover.ts +143 -0
- package/src/features/workspaceSymbols.ts +69 -0
- package/src/games/active.ts +19 -0
- package/src/games/ck3/ambientScopes.ts +273 -0
- package/src/games/ck3/index.ts +38 -0
- package/src/games/ck3/meta.ts +28 -0
- package/src/games/ck3/modifierPlaceholders.ts +61 -0
- package/src/games/ck3/saveSchema.ts +134 -0
- package/src/games/ck3/scaffolds.ts +197 -0
- package/src/games/ck3/schema.ts +422 -0
- package/src/games/ck3/structures.ts +887 -0
- package/src/games/eu5/index.ts +75 -0
- package/src/games/eu5/meta.ts +44 -0
- package/src/games/eu5/scaffolds.ts +49 -0
- package/src/games/eu5/schema.generated.ts +1043 -0
- package/src/games/jomini/variables.ts +134 -0
- package/src/games/profile.ts +205 -0
- package/src/games/registry.ts +27 -0
- package/src/games/vic3/index.ts +52 -0
- package/src/games/vic3/meta.ts +55 -0
- package/src/games/vic3/saveSchema.ts +77 -0
- package/src/games/vic3/scaffolds.ts +135 -0
- package/src/games/vic3/schema.ts +650 -0
- package/src/games/vic3/structures.ts +33 -0
- package/src/gui/anchorSpec.ts +66 -0
- package/src/gui/declMarkers.ts +30 -0
- package/src/gui/fillGeometry.ts +101 -0
- package/src/gui/guiDefs.ts +386 -0
- package/src/gui/guiDependencies.ts +352 -0
- package/src/gui/guiLinks.ts +64 -0
- package/src/gui/layoutEngine.ts +1998 -0
- package/src/gui/layoutService.ts +221 -0
- package/src/gui/measuredMetrics.ts +21 -0
- package/src/gui/previewService.ts +89 -0
- package/src/gui/saveSchema.ts +220 -0
- package/src/gui/saveValues.ts +399 -0
- package/src/gui/saveZip.ts +60 -0
- package/src/gui/sourceEdit.ts +535 -0
- package/src/gui/sourceEditService.ts +439 -0
- package/src/gui/sourceModel.ts +603 -0
- package/src/gui/textResolve.ts +145 -0
- package/src/gui/textureInfo.ts +106 -0
- package/src/gui/vocabulary.ts +149 -0
- package/src/gui/widgetEdit.ts +52 -0
- package/src/gui/widgetInfo.ts +245 -0
- package/src/index/docComments.ts +103 -0
- package/src/index/extract.ts +252 -0
- package/src/index/indexer.ts +369 -0
- package/src/index/intern.ts +101 -0
- package/src/index/lazyRefs.ts +145 -0
- package/src/index/modOrigin.ts +69 -0
- package/src/index/references.ts +534 -0
- package/src/overview/dependencies.ts +240 -0
- package/src/overview/eventBanner.ts +95 -0
- package/src/overview/eventDetail.ts +482 -0
- package/src/overview/eventGraph.ts +617 -0
- package/src/overview/eventVocabulary.ts +214 -0
- package/src/overview/locCoverage.ts +138 -0
- package/src/overview/modOverview.ts +29 -0
- package/src/overview/overrides.ts +89 -0
- package/src/parseCache.ts +81 -0
- package/src/parser/cst.ts +257 -0
- package/src/parser/encoding.ts +106 -0
- package/src/parser/index.ts +7 -0
- package/src/parser/lexer.ts +245 -0
- package/src/parser/locParser.ts +276 -0
- package/src/parser/parser.ts +360 -0
- package/src/schema/freqs.ts +70 -0
- package/src/schema/loader.ts +113 -0
- package/src/schema/types.ts +142 -0
- package/src/scopes/inference.ts +478 -0
- package/src/scopes/model.ts +148 -0
- package/src/scopes/varTypes.ts +290 -0
- package/src/server.ts +1894 -0
- package/src/serverData.ts +98 -0
- package/src/structure.ts +56 -0
- package/src/wordAt.ts +49 -0
|
@@ -0,0 +1,887 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Block-schema `structure` layer (update plan v1.1 §B2): the document shape of a
|
|
3
|
+
* few high-value definition kinds, the structural keys every modder types most,
|
|
4
|
+
* which come from neither script_docs tokens nor the definition index.
|
|
5
|
+
*
|
|
6
|
+
* SOURCE: hand-curated from the game's own `_*.info` files (harvested by
|
|
7
|
+
* scripts/build-structure.ts, regenerate there, curate here). Doc prose is the
|
|
8
|
+
* game's own, trimmed. `freq` values are the AGOT per-context counts from the
|
|
9
|
+
* plan's §B1 tables where available (used for ranking in Workstream C; a leading
|
|
10
|
+
* subset here). This is STATIC TypeScript on purpose: users may not have gamePath
|
|
11
|
+
* set, so nothing is harvested at runtime.
|
|
12
|
+
*
|
|
13
|
+
* Provenance strings for hover are the `.info` folder name (see STRUCTURE_SOURCES).
|
|
14
|
+
*/
|
|
15
|
+
import type { KeySpec, StructureSpec } from "../../schema/types";
|
|
16
|
+
// Bundled full harvest of every _*.info schema doc (scripts/build-structures-json.ts):
|
|
17
|
+
// ~70 kinds, ~1300 documented keys with vanilla usage counts. Merged UNDER the
|
|
18
|
+
// hand-curated specs below, curated docs always win on key collisions.
|
|
19
|
+
import HARVESTED_JSON from "../../../data/ck3/structures.json";
|
|
20
|
+
|
|
21
|
+
interface HarvestedShape {
|
|
22
|
+
sources: Record<string, string>;
|
|
23
|
+
kinds: Record<string, { topLevel: KeySpec[]; blocks?: Record<string, KeySpec[]> }>;
|
|
24
|
+
}
|
|
25
|
+
const HARVESTED = HARVESTED_JSON as unknown as HarvestedShape;
|
|
26
|
+
|
|
27
|
+
// Provenance shown in hover, keyed by schema kind (curated entries win).
|
|
28
|
+
export const STRUCTURE_SOURCES: Record<string, string> = {
|
|
29
|
+
...HARVESTED.sources,
|
|
30
|
+
character_interaction: "character_interactions",
|
|
31
|
+
decision: "decisions",
|
|
32
|
+
event: "events",
|
|
33
|
+
on_action: "on_action",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// --- dynamic description nodes (events/_events.info "Descriptions") ---
|
|
37
|
+
// desc/title/opening/option-name and the decision loc fields accept either a
|
|
38
|
+
// plain loc key or a dynamic-description block; nodes nest arbitrarily, and
|
|
39
|
+
// structureContextAt resolves by the innermost block name, so wiring each node
|
|
40
|
+
// name once per kind covers any nesting depth. `curated: true` keeps this
|
|
41
|
+
// deliberate order ahead of harvested any-depth keys.
|
|
42
|
+
const DESC_NODE: KeySpec[] = [
|
|
43
|
+
{
|
|
44
|
+
key: "desc",
|
|
45
|
+
values: "loc",
|
|
46
|
+
curated: true,
|
|
47
|
+
doc: "Appends text: a loc key, or another nested dynamic-description block.",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
key: "triggered_desc",
|
|
51
|
+
values: "block",
|
|
52
|
+
curated: true,
|
|
53
|
+
doc: "Conditional node: optional `trigger = { … }` plus `desc = …`; the desc is appended if the trigger passes.",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
key: "first_valid",
|
|
57
|
+
values: "block",
|
|
58
|
+
curated: true,
|
|
59
|
+
doc: "Checks child entries in order and uses only the first valid one. Put a plain `desc = fallback_key` last as the fallback.",
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
key: "random_valid",
|
|
63
|
+
values: "block",
|
|
64
|
+
curated: true,
|
|
65
|
+
doc: "Collects all valid children and picks randomly (default count = 1; `count = N` picks up to N unique ones).",
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
const TRIGGERED_DESC: KeySpec[] = [
|
|
69
|
+
{
|
|
70
|
+
key: "trigger",
|
|
71
|
+
values: "block",
|
|
72
|
+
curated: true,
|
|
73
|
+
doc: "Trigger: if it passes (or is omitted), this node's desc is appended.",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
key: "desc",
|
|
77
|
+
values: "loc",
|
|
78
|
+
curated: true,
|
|
79
|
+
doc: "The text: a loc key, or a nested dynamic-description block.",
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
const RANDOM_VALID: KeySpec[] = [
|
|
83
|
+
{ key: "count", curated: true, doc: "How many unique valid children to pick (default 1; no replacement)." },
|
|
84
|
+
...DESC_NODE,
|
|
85
|
+
];
|
|
86
|
+
/** The desc-node blocks shared by every kind whose loc fields support them. */
|
|
87
|
+
const DESC_NODE_BLOCKS: Record<string, KeySpec[]> = {
|
|
88
|
+
first_valid: DESC_NODE,
|
|
89
|
+
random_valid: RANDOM_VALID,
|
|
90
|
+
triggered_desc: TRIGGERED_DESC,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// --- character_interaction (common/character_interactions/_character_interactions.info) ---
|
|
94
|
+
const CHARACTER_INTERACTION: StructureSpec = {
|
|
95
|
+
topLevel: [
|
|
96
|
+
{
|
|
97
|
+
key: "category",
|
|
98
|
+
values: "loc",
|
|
99
|
+
freq: 530,
|
|
100
|
+
doc: "Required. Groups the interaction in this category in the interaction menu.",
|
|
101
|
+
},
|
|
102
|
+
{ key: "desc", values: "loc", freq: 419, doc: "Short description of the interaction (loc key)." },
|
|
103
|
+
{
|
|
104
|
+
key: "is_shown",
|
|
105
|
+
values: "block",
|
|
106
|
+
freq: 535,
|
|
107
|
+
doc: "Trigger: is the interaction available and visible. Available scopes: scope:actor, scope:recipient. Avoid actor-only or global tests here, put those in is_available.",
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
key: "is_valid",
|
|
111
|
+
values: "block",
|
|
112
|
+
doc: "Trigger: is the interaction valid to be selected in its current setup. Available scopes: scope:actor, scope:recipient.",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
key: "is_valid_showing_failures_only",
|
|
116
|
+
values: "block",
|
|
117
|
+
freq: 379,
|
|
118
|
+
doc: "Trigger: as is_valid, but only displays failures. Available scopes: scope:actor, scope:recipient.",
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
key: "is_available",
|
|
122
|
+
values: "block",
|
|
123
|
+
doc: "Trigger: is this interaction available for the actor (AI and player). Root is the actor. Prefer this over is_shown for actor-only checks.",
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
key: "is_highlighted",
|
|
127
|
+
values: "block",
|
|
128
|
+
doc: "Trigger: should the interaction be highlighted in the menu.",
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
key: "auto_accept",
|
|
132
|
+
values: "bool",
|
|
133
|
+
freq: 473,
|
|
134
|
+
doc: "Is the interaction automatically accepted (yes/no/trigger), or can the recipient decide.",
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
key: "icon",
|
|
138
|
+
values: "loc",
|
|
139
|
+
freq: 469,
|
|
140
|
+
doc: "Icon to use. Defaults to the interaction key's .dds in gfx/interface/icons/character_interactions.",
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
key: "send_option",
|
|
144
|
+
values: "block",
|
|
145
|
+
freq: 921,
|
|
146
|
+
doc: "Adds a toggleable option to the interaction window.",
|
|
147
|
+
},
|
|
148
|
+
{ key: "on_accept", values: "block", freq: 537, doc: "Effect: executes when accepted by the recipient." },
|
|
149
|
+
{ key: "on_decline", values: "block", doc: "Effect: executes when declined by the recipient." },
|
|
150
|
+
{ key: "on_send", values: "block", doc: "Effect: executes as soon as the interaction is sent." },
|
|
151
|
+
{
|
|
152
|
+
key: "ai_targets",
|
|
153
|
+
values: "block",
|
|
154
|
+
freq: 416,
|
|
155
|
+
doc: "Which characters the AI considers as recipients. Can be scripted multiple times to combine lists.",
|
|
156
|
+
},
|
|
157
|
+
{ key: "ai_accept", values: "block", doc: "MTTH: will the AI accept a request for this interaction." },
|
|
158
|
+
{
|
|
159
|
+
key: "ai_will_do",
|
|
160
|
+
values: "block",
|
|
161
|
+
doc: "MTTH: how interested the AI is in sending this interaction (0-100, clamped).",
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
key: "ai_potential",
|
|
165
|
+
values: "block",
|
|
166
|
+
doc: "Trigger: will the AI consider trying this interaction. Deprecated, use is_available.",
|
|
167
|
+
},
|
|
168
|
+
// Kept curated even though the harvest now carries it (cap fix, 2026-07):
|
|
169
|
+
// the .info comment omits the unit and the 0-disables rule.
|
|
170
|
+
{ key: "ai_frequency", doc: "How often the AI considers this interaction, in months. 0 means never." },
|
|
171
|
+
{
|
|
172
|
+
key: "cooldown",
|
|
173
|
+
values: "block",
|
|
174
|
+
doc: "How long until the interaction can be used again (e.g. { years = x }).",
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
key: "cost",
|
|
178
|
+
values: "block",
|
|
179
|
+
doc: "Scripted cost (gold/piety/prestige/renown). The interaction is disabled if the actor can't pay.",
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
key: "redirect",
|
|
183
|
+
values: "block",
|
|
184
|
+
doc: "Effect: replace any of actor/secondary_actor/recipient/secondary_recipient/intermediary with another character. This is how the ambient scopes are re-bound.",
|
|
185
|
+
},
|
|
186
|
+
{ key: "interface_priority", doc: "Sorts interactions in the menu (higher first)." },
|
|
187
|
+
{
|
|
188
|
+
key: "common_interaction",
|
|
189
|
+
values: "bool",
|
|
190
|
+
doc: "Common interactions are never placed in the More… submenu.",
|
|
191
|
+
},
|
|
192
|
+
{ key: "greeting", values: "enum:positive|negative", doc: "Sets the tone in the request text." },
|
|
193
|
+
{ key: "notification_text", values: "loc", doc: "Request text shown to the recipient (loc key)." },
|
|
194
|
+
{ key: "prompt", values: "loc", doc: 'Text shown under the portrait, e.g. "Pick a Guardian" (loc key).' },
|
|
195
|
+
{ key: "can_send", values: "block", doc: "Trigger: can the interaction be sent." },
|
|
196
|
+
{
|
|
197
|
+
key: "can_be_blocked",
|
|
198
|
+
values: "block",
|
|
199
|
+
doc: "Trigger: can the interaction be blocked by the recipient (e.g. via a hook).",
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
key: "populate_actor_list",
|
|
203
|
+
values: "block",
|
|
204
|
+
doc: "Populates the 'characters' selection list. Uses actor/recipient/secondary_actor/secondary_recipient scopes.",
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
key: "target_type",
|
|
208
|
+
values: "enum:title|artifact|men_at_arms|court_position_type|count",
|
|
209
|
+
doc: "What kind of thing the interaction targets. Default: count.",
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
key: "target_filter",
|
|
213
|
+
doc: "Which titles/artifacts/regiments populate the target list (see the .info FAQ for values).",
|
|
214
|
+
},
|
|
215
|
+
{ key: "hidden", values: "bool", doc: "Is the interaction hidden." },
|
|
216
|
+
{
|
|
217
|
+
key: "popup_on_receive",
|
|
218
|
+
values: "bool",
|
|
219
|
+
doc: "Have the interaction pop up for the recipient when received.",
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
key: "send_name",
|
|
223
|
+
values: "loc",
|
|
224
|
+
doc: "Name of the interaction once sent. Defaults to the database key.",
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
blocks: {
|
|
228
|
+
send_option: [
|
|
229
|
+
{
|
|
230
|
+
key: "is_shown",
|
|
231
|
+
values: "block",
|
|
232
|
+
doc: "Trigger: is this option shown. Independent, don't reference other option flags.",
|
|
233
|
+
},
|
|
234
|
+
{ key: "is_valid", values: "block", doc: "Trigger: is this option selectable." },
|
|
235
|
+
{ key: "localization", values: "loc", doc: "Loc key for the option label." },
|
|
236
|
+
{ key: "current_description", values: "loc", doc: "Tooltip loc for the option." },
|
|
237
|
+
{ key: "flag", doc: "If selected, scope:<flag_name> is set to yes." },
|
|
238
|
+
{
|
|
239
|
+
key: "starts_enabled",
|
|
240
|
+
values: "block",
|
|
241
|
+
doc: "Trigger: whether this option is on when the window opens. Defaults to off.",
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
key: "can_be_changed",
|
|
245
|
+
values: "block",
|
|
246
|
+
doc: "Trigger: whether this option can be changed from its default.",
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
key: "can_invalidate_interaction",
|
|
250
|
+
values: "bool",
|
|
251
|
+
doc: "If yes, picking this option re-runs the full can-send check. Use with care (AI performance).",
|
|
252
|
+
},
|
|
253
|
+
],
|
|
254
|
+
ai_targets: [
|
|
255
|
+
{
|
|
256
|
+
key: "ai_recipients",
|
|
257
|
+
values:
|
|
258
|
+
"enum:known_secrets|scheme_targets|hooked_characters|neighboring_rulers|neighboring_rulers_including_tributary_borders|neighboring_top_overlords_including_tributary_borders|neighboring_top_overlords_connected_by_land|peer_vassals|guests|dynasty|courtiers|councillors|prisoners|confederation_house_heads|sub_realm_characters|realm_characters|vassals|tributaries|liege|top_liege|suzerain|top_suzerain|self|head_of_faith|spouses|family|children|primary_war_enemies|war_enemies|war_allies|scripted_relations|activity_host|activity_guests|contacts|domicile_location_top_ruler|domicile_location_top_realm_vassals|domicile_location_neighboring_top_rulers|domicile_location_neighboring_top_realm_vassals|top_realm_domicile_owners|sub_realm_domicile_owners|nearby_domicile_owners|situation_participant_group",
|
|
259
|
+
doc: "Which characters the AI considers as recipients (the ai_targets list in _character_interactions.info).",
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
key: "max",
|
|
263
|
+
doc: "Maximum number of targets to consider; unset considers all (random subset when filtered).",
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
key: "chance",
|
|
267
|
+
doc: "0–1: chance each candidate is actually checked, a low value randomly excludes characters (performance).",
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
key: "parameter",
|
|
271
|
+
doc: "Extra detail for specific targets, for situation_participant_group, the situation key.",
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
// highlighted_reason etc. support dynamic descriptions per the .info.
|
|
275
|
+
...DESC_NODE_BLOCKS,
|
|
276
|
+
},
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
// --- decision (common/decisions/_decisions.info) ---
|
|
280
|
+
const DECISION: StructureSpec = {
|
|
281
|
+
topLevel: [
|
|
282
|
+
{
|
|
283
|
+
key: "picture",
|
|
284
|
+
values: "block",
|
|
285
|
+
freq: 470,
|
|
286
|
+
doc: "A picture for the decision. The first entry whose trigger passes is used.",
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
key: "effect",
|
|
290
|
+
values: "block",
|
|
291
|
+
freq: 397,
|
|
292
|
+
doc: "Effect executed when the decision is taken. Scope: character.",
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
key: "is_shown",
|
|
296
|
+
values: "block",
|
|
297
|
+
freq: 395,
|
|
298
|
+
doc: "Trigger: is the decision available to the character. Scope: character. Default: always = yes.",
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
key: "is_valid",
|
|
302
|
+
values: "block",
|
|
303
|
+
doc: "Trigger: can the decision be executed (shown under Requirements). Scope: character.",
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
key: "is_valid_showing_failures_only",
|
|
307
|
+
values: "block",
|
|
308
|
+
doc: "Trigger: can execute, failures shown in the confirm tooltip. Scope: character.",
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
key: "ai_will_do",
|
|
312
|
+
values: "block",
|
|
313
|
+
freq: 369,
|
|
314
|
+
doc: "% chance the AI executes the decision (0 never, 100 always). Also sets the player default option.",
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
key: "ai_potential",
|
|
318
|
+
values: "block",
|
|
319
|
+
freq: 336,
|
|
320
|
+
doc: "Trigger: whether the AI should look at this decision at all.",
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
key: "ai_check_interval",
|
|
324
|
+
freq: 343,
|
|
325
|
+
doc: "Months between AI checks. Required unless ai_goal = yes. 0 means never checked.",
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
key: "ai_check_interval_by_tier",
|
|
329
|
+
values: "block",
|
|
330
|
+
doc: "AI check interval (months) per top-title tier. All tiers required. Used instead of ai_check_interval.",
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
key: "ai_goal",
|
|
334
|
+
values: "bool",
|
|
335
|
+
doc: "The AI treats this as a goal, budgeting for it alongside major costs. Ignores ai_check_interval.",
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
key: "desc",
|
|
339
|
+
values: "loc",
|
|
340
|
+
freq: 345,
|
|
341
|
+
doc: 'Description. Default "<key>_desc". Supports dynamic descriptions. Scope: character.',
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
key: "title",
|
|
345
|
+
values: "loc",
|
|
346
|
+
doc: 'Title override. Default "<key>". Supports dynamic descriptions. Scope: character.',
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
key: "selection_tooltip",
|
|
350
|
+
values: "loc",
|
|
351
|
+
doc: 'Tooltip when selecting the decision. Default "<key>_tooltip". Scope: character.',
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
key: "confirm_text",
|
|
355
|
+
values: "loc",
|
|
356
|
+
doc: 'Confirm-button text. Default "<key>_confirm". Scope: character.',
|
|
357
|
+
},
|
|
358
|
+
{ key: "cost", values: "block", doc: "Cost values (gold/piety/prestige). Default: 0." },
|
|
359
|
+
{
|
|
360
|
+
key: "minimum_cost",
|
|
361
|
+
values: "block",
|
|
362
|
+
doc: "Like cost, but not applied, only blocks taking the decision if unaffordable.",
|
|
363
|
+
},
|
|
364
|
+
{ key: "decision_group_type", doc: "Foldable decision group to place this in. Default: decisions." },
|
|
365
|
+
{ key: "sort_order", doc: "Sort order in the decision view (higher first)." },
|
|
366
|
+
{
|
|
367
|
+
key: "should_create_alert",
|
|
368
|
+
values: "block",
|
|
369
|
+
doc: "Should this decision raise an alert, on top of the usual requirements.",
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
key: "widget",
|
|
373
|
+
values: "block",
|
|
374
|
+
doc: "Embed a custom widget in the decision (see the .info Custom Widgets section).",
|
|
375
|
+
},
|
|
376
|
+
],
|
|
377
|
+
blocks: {
|
|
378
|
+
picture: [
|
|
379
|
+
{
|
|
380
|
+
key: "trigger",
|
|
381
|
+
values: "block",
|
|
382
|
+
doc: "Trigger: receives the character scope. Empty trigger always passes.",
|
|
383
|
+
},
|
|
384
|
+
{ key: "reference", doc: "Path to the texture." },
|
|
385
|
+
{ key: "soundeffect", doc: "Sound played when showing the decision details." },
|
|
386
|
+
],
|
|
387
|
+
// title/desc/selection_tooltip/confirm_text "support dynamic descriptions
|
|
388
|
+
// like in events (first_valid, ...)" per _decisions.info.
|
|
389
|
+
title: DESC_NODE,
|
|
390
|
+
desc: DESC_NODE,
|
|
391
|
+
selection_tooltip: DESC_NODE,
|
|
392
|
+
confirm_text: DESC_NODE,
|
|
393
|
+
...DESC_NODE_BLOCKS,
|
|
394
|
+
},
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
// --- event (events/_events.info) ---
|
|
398
|
+
// Portrait blocks (left_portrait, right_portrait, …) share the `X = { ... }`
|
|
399
|
+
// shape documented once in the .info; wired to every portrait key below.
|
|
400
|
+
const EVENT_PORTRAIT_KEYS: KeySpec[] = [
|
|
401
|
+
{ key: "character", doc: "The portrait character (an event target)." },
|
|
402
|
+
{
|
|
403
|
+
key: "trigger",
|
|
404
|
+
values: "block",
|
|
405
|
+
doc: "Trigger: controls visibility of this portrait, in the scope of the portrait character.",
|
|
406
|
+
},
|
|
407
|
+
{ key: "animation", doc: "Default animation (see animations.txt). Used if no triggered_animation passes." },
|
|
408
|
+
{ key: "scripted_animation", doc: "Scripted-animation key, an alternative to animation." },
|
|
409
|
+
{
|
|
410
|
+
key: "triggered_animation",
|
|
411
|
+
values: "block",
|
|
412
|
+
doc: "First triggered animation whose trigger passes is used.",
|
|
413
|
+
},
|
|
414
|
+
{ key: "triggered_outfit", values: "block", doc: "First triggered outfit whose trigger passes is used." },
|
|
415
|
+
{ key: "camera", doc: "Camera key, overrides the portrait camera." },
|
|
416
|
+
{ key: "outfit_tags", values: "block", doc: "Outfit tags for this portrait, in ascending priority." },
|
|
417
|
+
{
|
|
418
|
+
key: "remove_default_outfit",
|
|
419
|
+
values: "bool",
|
|
420
|
+
doc: "Disable portrait-modifier categories that match no event tag.",
|
|
421
|
+
},
|
|
422
|
+
{ key: "hide_info", values: "bool", doc: "Show only the portrait, with no CoA/tooltips/clicks." },
|
|
423
|
+
{
|
|
424
|
+
key: "animate_if_dead",
|
|
425
|
+
values: "bool",
|
|
426
|
+
doc: "Allow the portrait to animate even if the character is dead.",
|
|
427
|
+
},
|
|
428
|
+
];
|
|
429
|
+
|
|
430
|
+
// The six override_* blocks share the `{ trigger reference }` shape documented
|
|
431
|
+
// in _events.info; only what `reference` names differs per block.
|
|
432
|
+
const EVENT_OVERRIDE_KEYS = (referenceDoc: string): KeySpec[] => [
|
|
433
|
+
{
|
|
434
|
+
key: "trigger",
|
|
435
|
+
values: "block",
|
|
436
|
+
doc: "Trigger: receives the event scope; the first entry whose trigger passes is used.",
|
|
437
|
+
},
|
|
438
|
+
{ key: "reference", doc: referenceDoc },
|
|
439
|
+
];
|
|
440
|
+
|
|
441
|
+
const EVENT: StructureSpec = {
|
|
442
|
+
topLevel: [
|
|
443
|
+
{
|
|
444
|
+
key: "type",
|
|
445
|
+
values: "enum:character_event|letter_event|court_event|activity_event",
|
|
446
|
+
freq: 9438,
|
|
447
|
+
doc: "Event type. Optional, defaults to character_event.",
|
|
448
|
+
},
|
|
449
|
+
{ key: "title", values: "loc", freq: 8714, doc: "Event title (loc key or dynamic description block)." },
|
|
450
|
+
{
|
|
451
|
+
key: "desc",
|
|
452
|
+
values: "loc",
|
|
453
|
+
freq: 9238,
|
|
454
|
+
doc: "Event description (loc key or dynamic description block).",
|
|
455
|
+
},
|
|
456
|
+
{ key: "theme", freq: 8719, doc: "Event theme (see 00_event_themes.txt)." },
|
|
457
|
+
{
|
|
458
|
+
key: "immediate",
|
|
459
|
+
values: "block",
|
|
460
|
+
freq: 8534,
|
|
461
|
+
doc: "Effect: runs immediately when the event fires, before options.",
|
|
462
|
+
},
|
|
463
|
+
{ key: "trigger", values: "block", freq: 5612, doc: "Trigger: gates whether the event may fire." },
|
|
464
|
+
{
|
|
465
|
+
key: "option",
|
|
466
|
+
values: "block",
|
|
467
|
+
freq: 22714,
|
|
468
|
+
doc: "An option the player/AI can pick. Effects go inline; use trigger = {} to gate it.",
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
key: "left_portrait",
|
|
472
|
+
freq: 7626,
|
|
473
|
+
doc: "Character portrait on the left. An event target, or a { character = … } block.",
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
key: "right_portrait",
|
|
477
|
+
freq: 6123,
|
|
478
|
+
doc: "Character portrait on the right. An event target, or a { character = … } block.",
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
key: "override_background",
|
|
482
|
+
values: "block",
|
|
483
|
+
freq: 5571,
|
|
484
|
+
doc: "Background shown for this event, overriding the theme's. First valid trigger wins.",
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
key: "override_transition",
|
|
488
|
+
values: "block",
|
|
489
|
+
doc: "Transition shown when the event pops up, before options and backgrounds; overrides the theme's. First valid trigger wins.",
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
key: "override_effect_2d",
|
|
493
|
+
values: "block",
|
|
494
|
+
doc: "2D effect on top of the background, overriding the theme's. First valid trigger wins.",
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
key: "override_icon",
|
|
498
|
+
values: "block",
|
|
499
|
+
doc: "Icon shown when the event pops up, overriding the theme's. First valid trigger wins.",
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
key: "override_header_background",
|
|
503
|
+
values: "block",
|
|
504
|
+
doc: "Header asset behind the event icon, overriding the theme's. First valid trigger wins.",
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
key: "override_sound",
|
|
508
|
+
values: "block",
|
|
509
|
+
doc: "Sound played when the event pops up, overriding the theme's. First valid trigger wins.",
|
|
510
|
+
},
|
|
511
|
+
{ key: "after", values: "block", doc: "Effect: runs after an option is chosen." },
|
|
512
|
+
{
|
|
513
|
+
key: "scope",
|
|
514
|
+
values: "enum:none|character|artifact",
|
|
515
|
+
doc: "Overrides the event's root scope. Optional, defaults to character.",
|
|
516
|
+
},
|
|
517
|
+
{ key: "hidden", values: "bool", doc: "If yes, no event window is shown." },
|
|
518
|
+
{ key: "major", values: "bool", doc: "Marks the event as major." },
|
|
519
|
+
{ key: "major_trigger", values: "block", doc: "Trigger: whether the event counts as major." },
|
|
520
|
+
{
|
|
521
|
+
key: "cooldown",
|
|
522
|
+
values: "block",
|
|
523
|
+
doc: "Per-event cooldown; the recipient root gets a saved variable for the duration.",
|
|
524
|
+
},
|
|
525
|
+
{ key: "window", doc: "Custom event-window name (gui/event_windows)." },
|
|
526
|
+
{ key: "content_source", doc: "DLC or mod this event belongs to, shown in the window if set." },
|
|
527
|
+
{ key: "center_portrait", doc: "Center character portrait (not used in all event types)." },
|
|
528
|
+
{ key: "lower_left_portrait", doc: "Lower-left character portrait." },
|
|
529
|
+
{ key: "lower_center_portrait", doc: "Lower-center character portrait." },
|
|
530
|
+
{ key: "lower_right_portrait", doc: "Lower-right character portrait." },
|
|
531
|
+
{ key: "sender", doc: "Sender portrait; required for letter events." },
|
|
532
|
+
{ key: "opening", values: "loc", doc: "Opening text for letter events (loc key or dynamic desc block)." },
|
|
533
|
+
{ key: "artifact", values: "block", doc: "An artifact shown in the event at a portrait position." },
|
|
534
|
+
{ key: "court_scene", values: "block", doc: "Court-scene behavior for court events." },
|
|
535
|
+
{
|
|
536
|
+
key: "on_trigger_fail",
|
|
537
|
+
values: "block",
|
|
538
|
+
doc: "Effect: runs if a queued/instant event fails its trigger checks.",
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
key: "widgets",
|
|
542
|
+
values: "block",
|
|
543
|
+
doc: "Custom widgets embedded in the event (see the .info Custom Widgets section).",
|
|
544
|
+
},
|
|
545
|
+
{
|
|
546
|
+
key: "orphan",
|
|
547
|
+
values: "bool",
|
|
548
|
+
doc: "Suppress the unreferenced-event error log. Useful for debug events.",
|
|
549
|
+
},
|
|
550
|
+
],
|
|
551
|
+
blocks: {
|
|
552
|
+
option: [
|
|
553
|
+
{ key: "name", values: "loc", freq: 24333, doc: "Option label (loc key or dynamic name block)." },
|
|
554
|
+
{ key: "trigger", values: "block", freq: 10098, doc: "Trigger: whether the option is valid." },
|
|
555
|
+
{
|
|
556
|
+
key: "ai_chance",
|
|
557
|
+
values: "block",
|
|
558
|
+
freq: 14542,
|
|
559
|
+
doc: "Weight the AI gives this option (base + modifier blocks). See _scripted_modifiers.info.",
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
key: "custom_tooltip",
|
|
563
|
+
values: "loc",
|
|
564
|
+
freq: 4518,
|
|
565
|
+
doc: "Loc key for a custom tooltip on the option.",
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
key: "first_valid",
|
|
569
|
+
values: "block",
|
|
570
|
+
freq: 3597,
|
|
571
|
+
doc: "Dynamic-description node: use the first valid child.",
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
key: "show_as_unavailable",
|
|
575
|
+
values: "block",
|
|
576
|
+
doc: "Trigger: show the option (disabled) even when invalid.",
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
key: "ai_will_select",
|
|
580
|
+
values: "block",
|
|
581
|
+
doc: "Script-value form of the AI option weight (takes priority over ai_chance).",
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
key: "highlight_portrait",
|
|
585
|
+
doc: "Highlight this character's portrait while the option is hovered (a scope).",
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
key: "skill",
|
|
589
|
+
values: "enum:diplomacy|martial|stewardship|intrigue|learning|prowess",
|
|
590
|
+
doc: "Marks the option as skill-relevant in the unlock-reason UI.",
|
|
591
|
+
},
|
|
592
|
+
{ key: "trait", doc: "Marks the option as trait-relevant in the unlock-reason UI." },
|
|
593
|
+
{ key: "flag", doc: "Arbitrary reason string, checked in the UI for special styling." },
|
|
594
|
+
{
|
|
595
|
+
key: "fallback",
|
|
596
|
+
values: "bool",
|
|
597
|
+
doc: "If no regular options are valid, fallback options are considered.",
|
|
598
|
+
},
|
|
599
|
+
{
|
|
600
|
+
key: "exclusive",
|
|
601
|
+
values: "bool",
|
|
602
|
+
doc: "If any exclusive option is valid, non-exclusive options are ignored.",
|
|
603
|
+
},
|
|
604
|
+
{ key: "clicksound", doc: "Sound played when selecting this option." },
|
|
605
|
+
],
|
|
606
|
+
left_portrait: EVENT_PORTRAIT_KEYS,
|
|
607
|
+
right_portrait: EVENT_PORTRAIT_KEYS,
|
|
608
|
+
center_portrait: EVENT_PORTRAIT_KEYS,
|
|
609
|
+
lower_left_portrait: EVENT_PORTRAIT_KEYS,
|
|
610
|
+
lower_center_portrait: EVENT_PORTRAIT_KEYS,
|
|
611
|
+
lower_right_portrait: EVENT_PORTRAIT_KEYS,
|
|
612
|
+
override_background: EVENT_OVERRIDE_KEYS(
|
|
613
|
+
"The event background to show (a key from common/event_backgrounds, e.g. wilderness_mountains)."
|
|
614
|
+
),
|
|
615
|
+
override_transition: EVENT_OVERRIDE_KEYS(
|
|
616
|
+
"The event transition to show (a key from common/event_transitions)."
|
|
617
|
+
),
|
|
618
|
+
override_effect_2d: EVENT_OVERRIDE_KEYS("The 2d effect to show (a key from common/event_2d_effects)."),
|
|
619
|
+
override_icon: EVENT_OVERRIDE_KEYS("Path to the icon texture."),
|
|
620
|
+
override_header_background: EVENT_OVERRIDE_KEYS("Path to the header texture."),
|
|
621
|
+
override_sound: EVENT_OVERRIDE_KEYS("The sound to play (an event:/ sound reference)."),
|
|
622
|
+
// desc/title/opening and option `name` accept dynamic-description blocks.
|
|
623
|
+
desc: DESC_NODE,
|
|
624
|
+
title: DESC_NODE,
|
|
625
|
+
opening: DESC_NODE,
|
|
626
|
+
name: DESC_NODE,
|
|
627
|
+
...DESC_NODE_BLOCKS,
|
|
628
|
+
},
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
// --- on_action (common/on_action/_on_actions.info) ---
|
|
632
|
+
const ON_ACTION: StructureSpec = {
|
|
633
|
+
topLevel: [
|
|
634
|
+
{
|
|
635
|
+
key: "trigger",
|
|
636
|
+
values: "block",
|
|
637
|
+
doc: "Trigger: if it returns false when the on_action fires, nothing happens.",
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
key: "events",
|
|
641
|
+
values: "block",
|
|
642
|
+
doc: "Events that always fire if their trigger passes. Supports delay = { … } entries.",
|
|
643
|
+
},
|
|
644
|
+
{ key: "random_events", values: "block", doc: "A single event is picked to fire, weighted." },
|
|
645
|
+
{ key: "first_valid", values: "block", doc: "The first event whose trigger passes is fired." },
|
|
646
|
+
{
|
|
647
|
+
key: "on_actions",
|
|
648
|
+
values: "block",
|
|
649
|
+
doc: "Further on_actions to chain (the compat-safe wiring pattern).",
|
|
650
|
+
},
|
|
651
|
+
{
|
|
652
|
+
key: "random_on_action",
|
|
653
|
+
values: "block",
|
|
654
|
+
doc: "A single chained on_action is picked to fire, weighted.",
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
key: "weight_multiplier",
|
|
658
|
+
values: "block",
|
|
659
|
+
doc: "Adjusts this on_action's weight when it is a candidate in a random_on_actions list.",
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
key: "fallback",
|
|
663
|
+
doc: "Event fired if nothing else in a random_events / random_on_action list is valid.",
|
|
664
|
+
},
|
|
665
|
+
{ key: "effect", values: "block", doc: "Effect run when the on_action fires." },
|
|
666
|
+
],
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
// --- customizable_localization (common/customizable_localization/_custom_loc.info) ---
|
|
670
|
+
const CUSTOMIZABLE_LOCALIZATION: StructureSpec = {
|
|
671
|
+
topLevel: [
|
|
672
|
+
{
|
|
673
|
+
key: "type",
|
|
674
|
+
values:
|
|
675
|
+
"enum:character|artifact|landed_title|province|activity|secret|scheme|combat|combat_side|title_and_vassal_change|faith|dynasty|all",
|
|
676
|
+
doc: "Scope type the custom loc command is used in. `all` accepts any scope, at the cost of run-time (not read-time) errors.",
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
key: "text",
|
|
680
|
+
values: "block",
|
|
681
|
+
doc: "A candidate text: trigger + localization_key. The first valid entry is used (or a random one with random_valid = yes).",
|
|
682
|
+
},
|
|
683
|
+
{ key: "random_valid", values: "bool", doc: "Pick a random valid text instead of the first valid one." },
|
|
684
|
+
{
|
|
685
|
+
key: "parent",
|
|
686
|
+
doc: "Variant: the parent custom loc key's logic runs, then `suffix` is appended to its result.",
|
|
687
|
+
},
|
|
688
|
+
{ key: "suffix", doc: "Suffix appended to the parent's resolved loc key." },
|
|
689
|
+
],
|
|
690
|
+
blocks: {
|
|
691
|
+
text: [
|
|
692
|
+
{
|
|
693
|
+
key: "trigger",
|
|
694
|
+
values: "block",
|
|
695
|
+
doc: "Interface triggers; the first text whose trigger passes returns its localization_key.",
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
key: "localization_key",
|
|
699
|
+
values: "loc",
|
|
700
|
+
doc: "The loc key this entry returns. Scopes saved in setup_scope are accessible in it.",
|
|
701
|
+
},
|
|
702
|
+
{
|
|
703
|
+
key: "setup_scope",
|
|
704
|
+
values: "block",
|
|
705
|
+
doc: "Interface effects run before the trigger, save scopes here to use in the trigger and the loc key.",
|
|
706
|
+
},
|
|
707
|
+
{ key: "fallback", values: "bool", doc: "Picked when no other entry is valid." },
|
|
708
|
+
],
|
|
709
|
+
},
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
// --- story_cycle (common/story_cycles/_story_cycles.info) ---
|
|
713
|
+
const STORY_TRIGGERED_EFFECT: KeySpec[] = [
|
|
714
|
+
{
|
|
715
|
+
key: "trigger",
|
|
716
|
+
values: "block",
|
|
717
|
+
doc: "Condition for this effect. Root: the active story cycle (scope:story).",
|
|
718
|
+
},
|
|
719
|
+
{ key: "effect", values: "block", doc: "Effect run when the trigger holds. Root: the active story cycle." },
|
|
720
|
+
];
|
|
721
|
+
|
|
722
|
+
const STORY_CYCLE: StructureSpec = {
|
|
723
|
+
topLevel: [
|
|
724
|
+
{
|
|
725
|
+
key: "on_setup",
|
|
726
|
+
values: "block",
|
|
727
|
+
doc: "Effect run when the story is created. Root: the active story cycle (scope:story).",
|
|
728
|
+
},
|
|
729
|
+
{ key: "on_end", values: "block", doc: "Effect run when the story is ended by the end_story effect." },
|
|
730
|
+
{
|
|
731
|
+
key: "on_owner_death",
|
|
732
|
+
values: "block",
|
|
733
|
+
doc: "Effect run when the story owner dies, a good time to set a new owner.",
|
|
734
|
+
},
|
|
735
|
+
{
|
|
736
|
+
key: "effect_group",
|
|
737
|
+
values: "block",
|
|
738
|
+
doc: "Recurring effects: every days/weeks/months/years interval, with optional chance, at most one triggered effect fires.",
|
|
739
|
+
},
|
|
740
|
+
],
|
|
741
|
+
blocks: {
|
|
742
|
+
effect_group: [
|
|
743
|
+
{ key: "days", doc: "Interval between attempts: a number or { min max } range." },
|
|
744
|
+
{ key: "weeks", doc: "Interval between attempts: a number or { min max } range." },
|
|
745
|
+
{ key: "months", doc: "Interval between attempts: a number or { min max } range." },
|
|
746
|
+
{ key: "years", doc: "Interval between attempts: a number or { min max } range." },
|
|
747
|
+
{
|
|
748
|
+
key: "chance",
|
|
749
|
+
doc: "Chance (0–100) the group fires on each interval tick; otherwise retried next tick.",
|
|
750
|
+
},
|
|
751
|
+
{
|
|
752
|
+
key: "trigger",
|
|
753
|
+
values: "block",
|
|
754
|
+
doc: "The group only fires while this holds. Root: the active story cycle.",
|
|
755
|
+
},
|
|
756
|
+
{
|
|
757
|
+
key: "triggered_effect",
|
|
758
|
+
values: "block",
|
|
759
|
+
doc: "trigger + effect; only one independent triggered_effect per group.",
|
|
760
|
+
},
|
|
761
|
+
{
|
|
762
|
+
key: "first_valid",
|
|
763
|
+
values: "block",
|
|
764
|
+
doc: "Runs the first inner triggered_effect whose trigger holds.",
|
|
765
|
+
},
|
|
766
|
+
{ key: "random_valid", values: "block", doc: "Runs one random valid inner triggered_effect." },
|
|
767
|
+
{ key: "fallback", values: "block", doc: "Effect run when no triggered effect fired." },
|
|
768
|
+
],
|
|
769
|
+
triggered_effect: STORY_TRIGGERED_EFFECT,
|
|
770
|
+
icon: [
|
|
771
|
+
{
|
|
772
|
+
key: "trigger",
|
|
773
|
+
values: "block",
|
|
774
|
+
doc: "Optional trigger; the first valid icon is chosen. scope:story is the story cycle.",
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
key: "reference",
|
|
778
|
+
doc: "Path to the icon texture. Defaults to gfx/interface/icons/story_cycles/<story key>.dds.",
|
|
779
|
+
},
|
|
780
|
+
],
|
|
781
|
+
background: [
|
|
782
|
+
{ key: "trigger", values: "block", doc: "Optional trigger; the first valid background is chosen." },
|
|
783
|
+
{ key: "reference", doc: "Path to the background texture." },
|
|
784
|
+
],
|
|
785
|
+
},
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
// --- modifier_definition_format (common/modifier_definition_formats/_definitions.info) ---
|
|
789
|
+
const MODIFIER_DEFINITION_FORMAT: StructureSpec = {
|
|
790
|
+
topLevel: [
|
|
791
|
+
{ key: "decimals", doc: "Decimal places shown. Default: 2." },
|
|
792
|
+
{
|
|
793
|
+
key: "color",
|
|
794
|
+
values: "enum:good|neutral|bad",
|
|
795
|
+
doc: "Which way the number is colored. Default: bad (higher = red).",
|
|
796
|
+
},
|
|
797
|
+
{ key: "prefix", values: "loc", doc: "Loc key shown before the value." },
|
|
798
|
+
{ key: "suffix", values: "loc", doc: "Loc key shown after the value." },
|
|
799
|
+
{ key: "negative_suffix", values: "loc", doc: "Used instead of suffix for negative numbers, when set." },
|
|
800
|
+
{ key: "percent", values: "bool", doc: "Format as a percentage by scaling the value up." },
|
|
801
|
+
{ key: "already_percent", values: "bool", doc: "Format as a percentage; the value is already scaled." },
|
|
802
|
+
{ key: "hidden", values: "bool", doc: "Hide this modifier from display." },
|
|
803
|
+
{ key: "no_difference_sign", values: "bool", doc: "Hide the +/- sign." },
|
|
804
|
+
{ key: "dlc_feature", doc: "Only show when the user has this DLC feature (e.g. diverge_culture)." },
|
|
805
|
+
],
|
|
806
|
+
};
|
|
807
|
+
|
|
808
|
+
const CURATED: Record<string, StructureSpec> = {
|
|
809
|
+
character_interaction: CHARACTER_INTERACTION,
|
|
810
|
+
decision: DECISION,
|
|
811
|
+
event: EVENT,
|
|
812
|
+
on_action: ON_ACTION,
|
|
813
|
+
customizable_localization: CUSTOMIZABLE_LOCALIZATION,
|
|
814
|
+
story_cycle: STORY_CYCLE,
|
|
815
|
+
modifier_definition_format: MODIFIER_DEFINITION_FORMAT,
|
|
816
|
+
};
|
|
817
|
+
|
|
818
|
+
/**
|
|
819
|
+
* Enum/doc patches over HARVESTED keys (kind → key → fields to fill in): the
|
|
820
|
+
* .info harvest rarely captures value enumerations, and re-curating the whole
|
|
821
|
+
* kind would distort the curated-first completion ranking. Patched in place
|
|
822
|
+
* (order and freq preserved); appended at the tail when the key is missing.
|
|
823
|
+
*/
|
|
824
|
+
const KEY_PATCHES: Record<string, Record<string, Partial<KeySpec> & { doc?: string }>> = {
|
|
825
|
+
trait: {
|
|
826
|
+
valid_sex: { values: "enum:all|male|female", doc: "Which sex can have the trait. Default: all." },
|
|
827
|
+
},
|
|
828
|
+
scheme_type: {
|
|
829
|
+
category: { values: "enum:personal|contract|hostile" },
|
|
830
|
+
},
|
|
831
|
+
activity_type: {
|
|
832
|
+
province_filter: {
|
|
833
|
+
values:
|
|
834
|
+
"enum:capital|domain|realm|top_realm|holy_sites|holy_sites_domain|holy_sites_realm|domicile|domicile_domain|domicile_realm|top_liege_border_inner|top_liege_border_outer|landed_title|geographical_region|all",
|
|
835
|
+
},
|
|
836
|
+
ai_province_filter: {
|
|
837
|
+
values:
|
|
838
|
+
"enum:capital|domain|realm|top_realm|holy_sites|holy_sites_domain|holy_sites_realm|domicile|domicile_domain|domicile_realm|top_liege_border_inner|top_liege_border_outer|landed_title|geographical_region|all",
|
|
839
|
+
},
|
|
840
|
+
},
|
|
841
|
+
};
|
|
842
|
+
|
|
843
|
+
/**
|
|
844
|
+
* All bundled structures, keyed by schema kind: the full harvested layer with
|
|
845
|
+
* the curated specs layered on top (curated keys keep their docs/order; extra
|
|
846
|
+
* harvested keys append after them, freq-ranked).
|
|
847
|
+
*/
|
|
848
|
+
export const STRUCTURES: Record<string, StructureSpec> = (() => {
|
|
849
|
+
const merged: Record<string, StructureSpec> = {};
|
|
850
|
+
for (const [kind, spec] of Object.entries(HARVESTED.kinds)) {
|
|
851
|
+
// Harvested sub-blocks are carried through; a curated `blocks` for the same
|
|
852
|
+
// kind replaces them wholesale below. The committed structures.json predates
|
|
853
|
+
// the harvester's sub-block pass and has none, so this is inert until it is
|
|
854
|
+
// regenerated (see scripts/build-structures-json.ts).
|
|
855
|
+
merged[kind] = { topLevel: [...spec.topLevel], ...(spec.blocks && { blocks: spec.blocks }) };
|
|
856
|
+
}
|
|
857
|
+
for (const [kind, spec] of Object.entries(CURATED)) {
|
|
858
|
+
const harvested = merged[kind]?.topLevel ?? [];
|
|
859
|
+
const have = new Set(spec.topLevel.map((k) => k.key));
|
|
860
|
+
// Keys declared in the curated NESTED blocks are known deeper-level
|
|
861
|
+
// vocabulary; a harvested top-level copy of them is depth noise from the
|
|
862
|
+
// .info prose, not evidence they belong at the top level.
|
|
863
|
+
const nested = new Set(Object.values(spec.blocks ?? {}).flatMap((keys) => keys.map((k) => k.key)));
|
|
864
|
+
merged[kind] = {
|
|
865
|
+
topLevel: [
|
|
866
|
+
// Curated keys win on docs/order, but inherit a harvested block scope
|
|
867
|
+
// (`# root = X` in the .info) when they don't declare one themselves.
|
|
868
|
+
...spec.topLevel.map((k) => {
|
|
869
|
+
const h = harvested.find((x) => x.key === k.key);
|
|
870
|
+
return { ...k, ...(h?.scope && !k.scope ? { scope: h.scope } : {}), curated: true };
|
|
871
|
+
}),
|
|
872
|
+
...harvested.filter((k) => !have.has(k.key) && !nested.has(k.key)),
|
|
873
|
+
],
|
|
874
|
+
blocks: spec.blocks,
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
for (const [kind, patches] of Object.entries(KEY_PATCHES)) {
|
|
878
|
+
const spec = merged[kind];
|
|
879
|
+
if (!spec) continue;
|
|
880
|
+
for (const [key, patch] of Object.entries(patches)) {
|
|
881
|
+
const existing = spec.topLevel.find((k) => k.key === key);
|
|
882
|
+
if (existing) Object.assign(existing, patch);
|
|
883
|
+
else spec.topLevel.push({ key, ...patch });
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
return merged;
|
|
887
|
+
})();
|