@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.
Files changed (163) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +334 -0
  3. package/THIRD-PARTY-NOTICES.md +83 -0
  4. package/data/ck3/dataTypes.json +2195 -0
  5. package/data/ck3/data_types/data_types_common.txt +2040 -0
  6. package/data/ck3/data_types/data_types_gui.txt +5264 -0
  7. package/data/ck3/data_types/data_types_internalclausewitzgui.txt +14843 -0
  8. package/data/ck3/data_types/data_types_script.txt +4251 -0
  9. package/data/ck3/data_types/data_types_uncategorized.txt +109984 -0
  10. package/data/ck3/freqs.json +1 -0
  11. package/data/ck3/guiSchema.json +6344 -0
  12. package/data/ck3/script_docs/effects.log +16059 -0
  13. package/data/ck3/script_docs/event_targets.log +2098 -0
  14. package/data/ck3/script_docs/modifiers.log +2228 -0
  15. package/data/ck3/script_docs/on_actions.log +5275 -0
  16. package/data/ck3/script_docs/triggers.log +11991 -0
  17. package/data/ck3/structures.json +9743 -0
  18. package/data/ck3/wikidocs/ATTRIBUTION.md +18 -0
  19. package/data/ck3/wikidocs/Data_types.md +2568 -0
  20. package/data/ck3/wikidocs/Effects_list.md +1176 -0
  21. package/data/ck3/wikidocs/Scopes_list.md +341 -0
  22. package/data/ck3/wikidocs/Triggers_list.md +1097 -0
  23. package/data/eu5/data_types/data_types_common.txt +2087 -0
  24. package/data/eu5/data_types/data_types_gui.txt +6732 -0
  25. package/data/eu5/data_types/data_types_internalclausewitzgui.txt +19276 -0
  26. package/data/eu5/data_types/data_types_script.txt +5688 -0
  27. package/data/eu5/data_types/data_types_uncategorized.txt +135569 -0
  28. package/data/vic3/data_types/data_types_common.txt +2021 -0
  29. package/data/vic3/data_types/data_types_gui.txt +5592 -0
  30. package/data/vic3/data_types/data_types_internalclausewitzgui.txt +17304 -0
  31. package/data/vic3/data_types/data_types_script.txt +2817 -0
  32. package/data/vic3/data_types/data_types_uncategorized.txt +84354 -0
  33. package/data/vic3/freqs.json +1 -0
  34. package/data/vic3/guiSchema.json +5578 -0
  35. package/data/vic3/script_docs/effects.log +38135 -0
  36. package/data/vic3/script_docs/event_targets.log +2028 -0
  37. package/data/vic3/script_docs/modifiers.log +18954 -0
  38. package/data/vic3/script_docs/on_actions.log +1561 -0
  39. package/data/vic3/script_docs/triggers.log +15738 -0
  40. package/data/vic3/structures.json +10189 -0
  41. package/dist/server.js +63668 -0
  42. package/media/px-lsp.svg +12 -0
  43. package/package.json +50 -0
  44. package/src/clientMode.ts +60 -0
  45. package/src/coa/coa.ts +184 -0
  46. package/src/coa/coaParse.ts +267 -0
  47. package/src/context.ts +78 -0
  48. package/src/contextKeywords.ts +224 -0
  49. package/src/data/dataBindingMacros.ts +82 -0
  50. package/src/data/dataFnDocs.ts +152 -0
  51. package/src/data/dataFnUsage.ts +431 -0
  52. package/src/data/dataTypes.ts +279 -0
  53. package/src/data/defines.ts +123 -0
  54. package/src/data/docsParser.ts +453 -0
  55. package/src/data/keywordDocs.ts +98 -0
  56. package/src/data/modifierTemplates.ts +143 -0
  57. package/src/data/textFormatting.ts +165 -0
  58. package/src/data/wikiDocs.ts +187 -0
  59. package/src/dds/decoder.ts +1007 -0
  60. package/src/dds/encode.ts +235 -0
  61. package/src/dds/index.ts +58 -0
  62. package/src/dds/png.ts +96 -0
  63. package/src/dds/tga.ts +62 -0
  64. package/src/documents.ts +35 -0
  65. package/src/features/assetPaths.ts +169 -0
  66. package/src/features/codeActions.ts +148 -0
  67. package/src/features/colors.ts +244 -0
  68. package/src/features/completion.ts +961 -0
  69. package/src/features/datafunction.ts +729 -0
  70. package/src/features/definition.ts +84 -0
  71. package/src/features/diagnostics.ts +244 -0
  72. package/src/features/folding.ts +106 -0
  73. package/src/features/formatting.ts +60 -0
  74. package/src/features/guiLanguage.ts +366 -0
  75. package/src/features/guiNavigation.ts +140 -0
  76. package/src/features/guiTree.ts +97 -0
  77. package/src/features/hover.ts +817 -0
  78. package/src/features/hoverRender.ts +222 -0
  79. package/src/features/inlayHints.ts +147 -0
  80. package/src/features/locFormatting.ts +127 -0
  81. package/src/features/references.ts +70 -0
  82. package/src/features/rename.ts +135 -0
  83. package/src/features/scopeAt.ts +65 -0
  84. package/src/features/semanticTokens.ts +188 -0
  85. package/src/features/signatureHelp.ts +72 -0
  86. package/src/features/symbols.ts +241 -0
  87. package/src/features/textureHover.ts +143 -0
  88. package/src/features/workspaceSymbols.ts +69 -0
  89. package/src/games/active.ts +19 -0
  90. package/src/games/ck3/ambientScopes.ts +273 -0
  91. package/src/games/ck3/index.ts +38 -0
  92. package/src/games/ck3/meta.ts +28 -0
  93. package/src/games/ck3/modifierPlaceholders.ts +61 -0
  94. package/src/games/ck3/saveSchema.ts +134 -0
  95. package/src/games/ck3/scaffolds.ts +197 -0
  96. package/src/games/ck3/schema.ts +422 -0
  97. package/src/games/ck3/structures.ts +887 -0
  98. package/src/games/eu5/index.ts +75 -0
  99. package/src/games/eu5/meta.ts +44 -0
  100. package/src/games/eu5/scaffolds.ts +49 -0
  101. package/src/games/eu5/schema.generated.ts +1043 -0
  102. package/src/games/jomini/variables.ts +134 -0
  103. package/src/games/profile.ts +205 -0
  104. package/src/games/registry.ts +27 -0
  105. package/src/games/vic3/index.ts +52 -0
  106. package/src/games/vic3/meta.ts +55 -0
  107. package/src/games/vic3/saveSchema.ts +77 -0
  108. package/src/games/vic3/scaffolds.ts +135 -0
  109. package/src/games/vic3/schema.ts +650 -0
  110. package/src/games/vic3/structures.ts +33 -0
  111. package/src/gui/anchorSpec.ts +66 -0
  112. package/src/gui/declMarkers.ts +30 -0
  113. package/src/gui/fillGeometry.ts +101 -0
  114. package/src/gui/guiDefs.ts +386 -0
  115. package/src/gui/guiDependencies.ts +352 -0
  116. package/src/gui/guiLinks.ts +64 -0
  117. package/src/gui/layoutEngine.ts +1998 -0
  118. package/src/gui/layoutService.ts +221 -0
  119. package/src/gui/measuredMetrics.ts +21 -0
  120. package/src/gui/previewService.ts +89 -0
  121. package/src/gui/saveSchema.ts +220 -0
  122. package/src/gui/saveValues.ts +399 -0
  123. package/src/gui/saveZip.ts +60 -0
  124. package/src/gui/sourceEdit.ts +535 -0
  125. package/src/gui/sourceEditService.ts +439 -0
  126. package/src/gui/sourceModel.ts +603 -0
  127. package/src/gui/textResolve.ts +145 -0
  128. package/src/gui/textureInfo.ts +106 -0
  129. package/src/gui/vocabulary.ts +149 -0
  130. package/src/gui/widgetEdit.ts +52 -0
  131. package/src/gui/widgetInfo.ts +245 -0
  132. package/src/index/docComments.ts +103 -0
  133. package/src/index/extract.ts +252 -0
  134. package/src/index/indexer.ts +369 -0
  135. package/src/index/intern.ts +101 -0
  136. package/src/index/lazyRefs.ts +145 -0
  137. package/src/index/modOrigin.ts +69 -0
  138. package/src/index/references.ts +534 -0
  139. package/src/overview/dependencies.ts +240 -0
  140. package/src/overview/eventBanner.ts +95 -0
  141. package/src/overview/eventDetail.ts +482 -0
  142. package/src/overview/eventGraph.ts +617 -0
  143. package/src/overview/eventVocabulary.ts +214 -0
  144. package/src/overview/locCoverage.ts +138 -0
  145. package/src/overview/modOverview.ts +29 -0
  146. package/src/overview/overrides.ts +89 -0
  147. package/src/parseCache.ts +81 -0
  148. package/src/parser/cst.ts +257 -0
  149. package/src/parser/encoding.ts +106 -0
  150. package/src/parser/index.ts +7 -0
  151. package/src/parser/lexer.ts +245 -0
  152. package/src/parser/locParser.ts +276 -0
  153. package/src/parser/parser.ts +360 -0
  154. package/src/schema/freqs.ts +70 -0
  155. package/src/schema/loader.ts +113 -0
  156. package/src/schema/types.ts +142 -0
  157. package/src/scopes/inference.ts +478 -0
  158. package/src/scopes/model.ts +148 -0
  159. package/src/scopes/varTypes.ts +290 -0
  160. package/src/server.ts +1894 -0
  161. package/src/serverData.ts +98 -0
  162. package/src/structure.ts +56 -0
  163. package/src/wordAt.ts +49 -0
@@ -0,0 +1,273 @@
1
+ /**
2
+ * Ambient (engine-provided) saved scopes per definition kind (update plan v1.1
3
+ * §B3). These names are referenced via `scope:x` but are never `save_scope_as`'d
4
+ * in script — the engine binds them — so a save-site index can't explain them.
5
+ *
6
+ * SOURCE: hand-curated from the game's `_*.info` files (2026-07 scope audit:
7
+ * every folder's documented `scope:<name>` lines were swept and typed). Scope
8
+ * type names are the canonical ones from event_scopes.log. Some ambient scopes
9
+ * only exist in specific blocks of the kind; per AD-5 they are offered for the
10
+ * whole file (rank/annotate, never hide) with the block noted in the doc.
11
+ */
12
+ import type { AmbientScope } from "../../schema/types";
13
+
14
+ const CHAR = "character";
15
+
16
+ function s(name: string, type: string, doc: string): AmbientScope {
17
+ return { name, type, doc };
18
+ }
19
+
20
+ /** Ambient scopes keyed by schema kind. */
21
+ export const AMBIENT_SCOPES: Record<string, AmbientScope[]> = {
22
+ // common/character_interactions/_character_interactions.info
23
+ character_interaction: [
24
+ s("actor", CHAR, "The character sending the interaction (the initiator)."),
25
+ s("recipient", CHAR, "The character receiving the interaction."),
26
+ s(
27
+ "secondary_actor",
28
+ CHAR,
29
+ "Present when the interaction declares a secondary actor (e.g. marriage) or a redirect saves one."
30
+ ),
31
+ s(
32
+ "secondary_recipient",
33
+ CHAR,
34
+ "As secondary_actor, for the recipient side (e.g. the character being married off)."
35
+ ),
36
+ s(
37
+ "intermediary",
38
+ CHAR,
39
+ "The intermediary who forwards the interaction, when one is used. Set/replaced via redirect."
40
+ ),
41
+ ],
42
+
43
+ // common/activities/activity_types/_activity_type.info
44
+ activity_type: [
45
+ s("activity", "activity", "The activity (is_valid, on_* effect blocks, phases…)."),
46
+ s("host", CHAR, "Host of the activity."),
47
+ s(
48
+ "special_option",
49
+ "flag",
50
+ "Flag of the selected special option; unset when the activity has no special option category."
51
+ ),
52
+ s("province", "province", "Phase blocks: the location the phase is at / being evaluated in."),
53
+ ],
54
+ // common/activities/guest_invite_rules/_invite_rules.info (root = host character)
55
+ guest_invite_rule: [
56
+ s(
57
+ "special_option",
58
+ "flag",
59
+ "Flag of the selected special option; unset when the activity has no special option category."
60
+ ),
61
+ ],
62
+ // common/activities/intents/_intents.info
63
+ activity_intent: [
64
+ s("target", CHAR, "The target of the intent."),
65
+ s("activity", "activity", "The activity the intent applies to."),
66
+ s("special_option", "flag", "Flag of the selected special option."),
67
+ ],
68
+ // common/activities/pulse_actions/_pulse_actions.info
69
+ activity_pulse_action: [
70
+ s("activity", "activity", "The activity."),
71
+ s("host", CHAR, "Host of the activity."),
72
+ s("province", "province", "Current activity location."),
73
+ s("first", CHAR, "First saved character, shown in the pulse-action entry."),
74
+ s("second", CHAR, "Second saved character, shown in the pulse-action entry."),
75
+ ],
76
+ // common/activities/activity_locales/_activity_locales.info
77
+ activity_locale: [s("activity", "activity", "The activity."), s("host", CHAR, "Host of the activity.")],
78
+
79
+ // common/schemes/scheme_types/_schemes.info
80
+ scheme_type: [
81
+ s("scheme", "scheme", "The scheme itself."),
82
+ s(
83
+ "owner",
84
+ CHAR,
85
+ "The scheme owner (same as scheme_owner, valid in an unbroken chain from the scheme scope)."
86
+ ),
87
+ s(
88
+ "target",
89
+ CHAR,
90
+ "Whatever the scheme targets — a character for character-targeted schemes (title/culture/faith schemes target those types)."
91
+ ),
92
+ s(
93
+ "target_title",
94
+ "landed_title",
95
+ "The targeted title, when the scheme targets a title (otherwise unset)."
96
+ ),
97
+ s("agent", CHAR, "An agent in the scheme (on_agent_exposed and similar per-agent effects)."),
98
+ ],
99
+ // common/schemes/agent_types/_agent_types.info
100
+ scheme_agent_type: [
101
+ s("owner", CHAR, "The owner of the scheme."),
102
+ s("scheme", "scheme", "The scheme."),
103
+ s("target", CHAR, "The target of the scheme."),
104
+ ],
105
+ // common/schemes/pulse_actions/_pulse_actions.info
106
+ scheme_pulse_action: [
107
+ s("activity", "scheme", "The scheme (the docs reuse the activity slot name)."),
108
+ s("owner", CHAR, "Owner of the scheme."),
109
+ s("first", CHAR, "First saved character, shown in the pulse-action entry."),
110
+ s("second", CHAR, "Second saved character, shown in the pulse-action entry."),
111
+ ],
112
+
113
+ // common/secret_types/_secret_types.info (root = the secret)
114
+ secret: [
115
+ s("secret_owner", CHAR, "The character the secret is about."),
116
+ s("secret_target", CHAR, "Optional character related to the secret (e.g. the lover in a lover secret)."),
117
+ s(
118
+ "target",
119
+ CHAR,
120
+ "is_shunned/is_criminal: the character checking whether they view the secret as shunned/criminal."
121
+ ),
122
+ s("discoverer", CHAR, "on_discover: the character who discovered the secret."),
123
+ s("secret_exposer", CHAR, "on_expose: the character who exposed the secret."),
124
+ ],
125
+
126
+ // common/story_cycles/_story_cycles.info (root = the story)
127
+ story_cycle: [s("story", "story", "The story cycle (same as root in its effect blocks).")],
128
+
129
+ // common/situation/situations/_situations.info
130
+ situation: [
131
+ s("situation", "situation", "The situation."),
132
+ s("situation_sub_region", "situation_sub_region", "The situation sub-region of the current block/phase."),
133
+ s(
134
+ "situation_participant_group",
135
+ "situation_participant_group",
136
+ "The participant group (participant blocks)."
137
+ ),
138
+ ],
139
+
140
+ // common/casus_belli_types/_casus_belli.info
141
+ casus_belli: [
142
+ s("attacker", CHAR, "The attacker in the war."),
143
+ s("defender", CHAR, "The defender in the war."),
144
+ s("claimant", CHAR, "The claimant, for claim CBs."),
145
+ s("target", "landed_title", "The target title, when the CB has one."),
146
+ ],
147
+
148
+ // common/council_tasks/_council_tasks.info
149
+ council_task: [
150
+ s("councillor", CHAR, "The councillor performing the task."),
151
+ s("councillor_liege", CHAR, "The councillor's liege."),
152
+ s("county", "landed_title", "County tasks: the county being worked."),
153
+ s("province", "province", "Province tasks: the province being worked."),
154
+ s("target_character", CHAR, "Character-targeted tasks: the target."),
155
+ ],
156
+
157
+ // common/buildings/_buildings.info (root = province)
158
+ building: [s("holder", CHAR, "The holder of the province the building is in.")],
159
+
160
+ // common/legends/legend_types/_legends.info
161
+ legend_type: [
162
+ s("legend", "legend", "The legend."),
163
+ s("creator", CHAR, "The creator of the legend."),
164
+ s("protagonist", CHAR, "The protagonist of the legend."),
165
+ ],
166
+
167
+ // common/epidemics/_epidemics.info
168
+ epidemic: [
169
+ s("epidemic", "epidemic", "The epidemic."),
170
+ s("epidemic_type", "epidemic_type", "The epidemic's type."),
171
+ ],
172
+
173
+ // common/inspirations/_inspirations.info (root = inspiration)
174
+ inspiration: [
175
+ s("inspiration", "inspiration", "The inspiration."),
176
+ s("inspiration_owner", CHAR, "The character who has the inspiration."),
177
+ s("inspiration_sponsor", CHAR, "The sponsoring character, once sponsored."),
178
+ ],
179
+
180
+ // common/travel/travel_options/_travel_options.info (root = travel owner / plan)
181
+ travel_option: [s("owner", CHAR, "The travel plan owner.")],
182
+ // common/travel/point_of_interest_types/_travel_point_of_interest_types.info
183
+ point_of_interest: [s("province", "province", "The point-of-interest province.")],
184
+
185
+ // common/court_positions/types/_court_positions.info (root = character)
186
+ court_position: [
187
+ s("liege", CHAR, "The court owner employing the position."),
188
+ s("employee", CHAR, "The character employed in the position."),
189
+ ],
190
+ court_position_task: [
191
+ s("liege", CHAR, "The court owner employing the position."),
192
+ s("employee", CHAR, "The character employed in the position."),
193
+ ],
194
+
195
+ // common/character_memory_types/_character_memories.info
196
+ character_memory_type: [
197
+ s("memory", "character_memory", "The memory."),
198
+ s("new_memory", "character_memory", "The newly created memory (creation blocks)."),
199
+ s("owner", CHAR, "The memory owner."),
200
+ ],
201
+
202
+ // common/great_projects/types/_great_project_types.info
203
+ great_project: [
204
+ s("great_project", "great_project", "The funded project (ongoing projects)."),
205
+ s("owner", CHAR, "The project owner."),
206
+ s("founder", CHAR, "The project founder."),
207
+ s("province", "province", "The project's province."),
208
+ ],
209
+
210
+ // common/domiciles (root = owner character / the domicile)
211
+ domicile_building: [s("owner", CHAR, "The domicile owner.")],
212
+ domicile_type: [s("owner", CHAR, "The domicile owner.")],
213
+
214
+ // common/task_contracts/_task_contracts.info
215
+ task_contract: [s("employer", CHAR, "The character offering the contract.")],
216
+
217
+ // common/subject_contracts + tax slots
218
+ subject_contract: [
219
+ s("liege", CHAR, "The liege side of the contract."),
220
+ s("subject", CHAR, "The subject side of the contract."),
221
+ s("vassal", CHAR, "The vassal (tax blocks)."),
222
+ s("tax_collector", CHAR, "The tax collector."),
223
+ s("tax_slot", "tax_slot", "The tax slot."),
224
+ ],
225
+ tax_obligation: [
226
+ s("liege", CHAR, "The liege."),
227
+ s("vassal", CHAR, "The vassal."),
228
+ s("tax_collector", CHAR, "The tax collector."),
229
+ s("tax_slot", "tax_slot", "The tax slot."),
230
+ ],
231
+ tax_slot_type: [
232
+ s("liege", CHAR, "The liege."),
233
+ s("vassal", CHAR, "The vassal."),
234
+ s("tax_collector", CHAR, "The tax collector."),
235
+ s("tax_slot", "tax_slot", "The tax slot."),
236
+ ],
237
+
238
+ // common/succession_election/_succession_election.info (root = the title)
239
+ succession_election: [
240
+ s("candidate", CHAR, "The candidate being evaluated."),
241
+ s("holder", CHAR, "The current title holder."),
242
+ s("holder_candidate", CHAR, "The holder as a candidate."),
243
+ s("title", "landed_title", "The elective title."),
244
+ ],
245
+ succession_appointment: [s("title", "landed_title", "The title being appointed.")],
246
+
247
+ // Misc single-scope kinds
248
+ vassal_stance: [s("liege", CHAR, "The vassal's liege.")],
249
+ legitimacy_level: [
250
+ s("target", CHAR, "The character whose legitimacy is checked."),
251
+ s("liege", CHAR, "The target's liege."),
252
+ ],
253
+ scripted_relation: [s("target", CHAR, "The other character of the relation.")],
254
+ suggestion: [s("recipient", CHAR, "The character receiving the suggestion.")],
255
+ important_action: [s("recipient", CHAR, "The character the alert is for (the player).")],
256
+ house_aspiration: [s("house", "dynasty_house", "The house the aspiration belongs to.")],
257
+ combat_phase_event: [s("combat_side", "combat_side", "The combat side of the phase event.")],
258
+ culture_creation_name: [
259
+ s("culture", "culture", "The culture being created."),
260
+ s("other_culture", "culture", "The other parent culture (hybrid cultures)."),
261
+ ],
262
+ innovation: [s("character", CHAR, "The innovating culture's ruler (specific trigger blocks).")],
263
+
264
+ // common/artifacts
265
+ artifact_template: [s("artifact", "artifact", "The artifact.")],
266
+ artifact_visual: [s("artifact", "artifact", "The artifact.")],
267
+
268
+ // common/accolade_names/_accolade_name.info (root = the accolade)
269
+ accolade_name: [
270
+ s("accolade_type", "accolade_type", "The accolade's primary type."),
271
+ s("owner", CHAR, "The accolade owner (the liege)."),
272
+ ],
273
+ };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The Crusader Kings III game profile: identity plus every CK3 knowledge table
3
+ * and bundled-data import, assembled behind the GameProfile interface.
4
+ */
5
+ import type { GameProfile } from "../profile";
6
+ import { ck3Meta } from "./meta";
7
+ import { BLOCK_REF_FIELDS, CK3_SCHEMA, PREFIX_REFS, REF_FIELDS } from "./schema";
8
+ import { STRUCTURE_SOURCES } from "./structures";
9
+ import { CK3_MODIFIER_PLACEHOLDERS } from "./modifierPlaceholders";
10
+ import { CK3_SAVE_SCHEMA } from "./saveSchema";
11
+ // Bundled baseline data (packages/server/data/ck3/), imported at build time so
12
+ // completion/hover work without gamePath set. See scripts/build-data-types-json.ts
13
+ // and scripts/build-gui-schema.ts for regeneration.
14
+ import BUNDLED_DATA_TYPES from "../../../data/ck3/dataTypes.json";
15
+ import GUI_SCHEMA from "../../../data/ck3/guiSchema.json";
16
+
17
+ export const ck3Profile: GameProfile = {
18
+ ...ck3Meta,
19
+ schema: CK3_SCHEMA,
20
+ refFields: REF_FIELDS,
21
+ prefixRefs: PREFIX_REFS,
22
+ blockRefFields: BLOCK_REF_FIELDS,
23
+ structureSources: STRUCTURE_SOURCES,
24
+ // Events declare `scope = character|landed_title|…` and default to character
25
+ // when the key is absent (events/_events.info); custom loc `type = all` → any
26
+ // scope; scripted_guis declare `scope = X`.
27
+ defRootKeys: {
28
+ event: { key: "scope", default: "character" },
29
+ customizable_localization: { key: "type" },
30
+ scripted_gui: { key: "scope", default: "character" },
31
+ },
32
+ modifierPlaceholders: CK3_MODIFIER_PLACEHOLDERS,
33
+ bundledDataTypes: BUNDLED_DATA_TYPES,
34
+ guiSchema: GUI_SCHEMA,
35
+ saveSchema: CK3_SAVE_SCHEMA,
36
+ wikiNote: "Source: CK3 wiki (may lag behind the current game version)",
37
+ diagnosticSource: "ck3-script",
38
+ };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Crusader Kings III identity and conventions. Data only — safe for the VSCode
3
+ * client to import without pulling the knowledge tables into its bundle.
4
+ */
5
+ import type { GameMeta } from "../profile";
6
+ import { GITAN_MEASURED_METRICS } from "../../gui/measuredMetrics";
7
+ import { CK3_SCAFFOLDS } from "./scaffolds";
8
+
9
+ export const ck3Meta: GameMeta = {
10
+ id: "ck3",
11
+ name: "Crusader Kings III",
12
+ shortName: "CK3",
13
+ engine: "jomini",
14
+ descriptor: "mod",
15
+ configDirName: ".ck3modding",
16
+ docsFolderName: "Crusader Kings III",
17
+ steamAppId: 1158310,
18
+ eventNamespaces: true,
19
+ // The engine's own default metrics: they were measured on this game's font,
20
+ // and the layout engine reuses them for games whose probe has not run.
21
+ uiFont: "fonts/Gitan/GitanLatin-Regular.otf",
22
+ guiTextMetrics: GITAN_MEASURED_METRICS,
23
+ scaffolds: CK3_SCAFFOLDS,
24
+ tiger: { binaryName: "ck3-tiger", repoSlug: "amtep/tiger", confName: "ck3-tiger.conf" },
25
+ // "" keeps the pre-profile cache filenames (docsCache.json, vanillaIndex-*.json)
26
+ // so existing users' caches survive the M2 restructure.
27
+ cacheSuffix: "",
28
+ };
@@ -0,0 +1,61 @@
1
+ /**
2
+ * CK3's templated-modifier placeholder table ($CULTURE$_opinion, …), consumed
3
+ * by data/modifierTemplates.ts via the game profile.
4
+ *
5
+ * Every placeholder mapping below was verified against vanilla 1.19:
6
+ * `common/modifier_definition_formats/` enumerates the generated names, and
7
+ * script usage (traditions, buildings, perks…) confirms the join rule is
8
+ * `<definition name>` spliced verbatim into the template. Unverifiable
9
+ * placeholders are deliberately absent — a wrong expansion is worse than a
10
+ * missing one:
11
+ * - GEOGRAPHICAL_REGION (map_data) and TRAIT_TRACK (nested in traits) have no
12
+ * definition-index kind.
13
+ * - SUBJECT_SALARY expands to title tiers, but 40 of the 220 tier combinations
14
+ * do not exist in vanilla's formats.
15
+ */
16
+ import type { PlaceholderSpec } from "../../data/modifierTemplates";
17
+
18
+ /**
19
+ * MEN_AT_ARMS_TYPE expands to the engine's hardcoded base types (`type = …` in
20
+ * a men-at-arms definition), NOT to common/men_at_arms_types names: vanilla
21
+ * uses `heavy_infantry_damage_mult`, never `armored_footmen_damage_mult`.
22
+ */
23
+ const MAA_BASE_TYPES = [
24
+ "archer_cavalry",
25
+ "archers",
26
+ "camel_cavalry",
27
+ "elephant_cavalry",
28
+ "gunpowder",
29
+ "heavy_cavalry",
30
+ "heavy_infantry",
31
+ "light_cavalry",
32
+ "nomadic_horde",
33
+ "pikemen",
34
+ "siege_weapon",
35
+ "skirmishers",
36
+ ] as const;
37
+
38
+ export const CK3_MODIFIER_PLACEHOLDERS: Record<string, PlaceholderSpec> = {
39
+ CULTURE: { kind: "culture", label: "culture" }, // akan_opinion
40
+ // Faiths are nested under `faiths = { … }` and not indexed yet (see
41
+ // schema.ts); the mapping is inert until they are, which is correct.
42
+ FAITH: { kind: "faith", label: "faith" }, // mutazila_opinion
43
+ GOVERNMENT_TYPE: { kind: "government", label: "government" }, // feudal_government_opinion
44
+ HOLDING_TYPE: { kind: "holding_type", label: "holding type" }, // castle_holding_build_speed
45
+ LIFESTYLE: { kind: "lifestyle", label: "lifestyle" }, // diplomacy_lifestyle_xp_gain_add
46
+ MEN_AT_ARMS_TYPE: {
47
+ values: MAA_BASE_TYPES,
48
+ label: "men-at-arms base type",
49
+ // Verified exception: stationed_* variants exist for every base type
50
+ // EXCEPT nomadic_horde (absent from vanilla formats and script usage).
51
+ excludeValues: { stationed_: ["nomadic_horde"] },
52
+ },
53
+ RELIGIOUS: { kind: "religion", label: "religion" }, // christianity_religion_opinion
54
+ RELIGIOUS_FAMILY: { kind: "religion_family", label: "religion family" }, // rf_pagan_opinion
55
+ SCHEME_TYPE: { kind: "scheme_type", label: "scheme type" }, // abduct_scheme_phase_duration_add
56
+ SCRIPTED_RELATION: { kind: "scripted_relation", label: "scripted relation" }, // scheme_phase_duration_against_rival_add
57
+ SITUATION_TYPE: { kind: "situation", label: "situation" }, // the_great_steppe_supply_limit_add
58
+ TAX_SLOT_TYPE: { kind: "tax_slot_type", label: "tax slot type" }, // clan_tax_slot_add
59
+ TERRAIN_TYPE: { kind: "terrain_type", label: "terrain type" }, // plains_advantage
60
+ VASSAL_STANCE: { kind: "vassal_stance", label: "vassal stance" }, // courtly_opinion
61
+ };
@@ -0,0 +1,134 @@
1
+ /**
2
+ * How a Crusader Kings III save is read for GUI preview values.
3
+ *
4
+ * CK3 packs its script into a zip holding one `gamestate` entry (gui/saveZip.ts
5
+ * finds it; `-debug_mode` writes the same script as plain text instead), and
6
+ * names the played character LAST, in `played_character`, long after the
7
+ * `living` record a preview wants. That is what `record` below describes: find
8
+ * the id first, then cut out `living.<id>`.
9
+ *
10
+ * CK3's player is a character, so most rows read that record; the three names
11
+ * the load-game screen shows (character, primary title, house) the save already
12
+ * carries ready-localized in `meta_data`, which is why no title or dynasty
13
+ * block has to be cut out of a 70 MB gamestate to render them.
14
+ */
15
+ import type { BlockNode } from "../../parser";
16
+ import {
17
+ age,
18
+ block,
19
+ named,
20
+ scalar,
21
+ statements,
22
+ thousands,
23
+ type SaveContext,
24
+ type SaveSchema,
25
+ type ValueMapping,
26
+ } from "../../gui/saveSchema";
27
+
28
+ /** The living character's own state (currencies, variables) sits under here. */
29
+ function aliveData(ctx: SaveContext): BlockNode | undefined {
30
+ return block(ctx.character, "alive_data");
31
+ }
32
+
33
+ function firstName(ctx: SaveContext): string | undefined {
34
+ return named(ctx, scalar(ctx.character, "first_name"));
35
+ }
36
+
37
+ const CK3_MAPPINGS: ValueMapping[] = [
38
+ {
39
+ // `first_name` is a loc key (`Alp_Arslan`), which is what CK3's own
40
+ // `GetName` renders for a character.
41
+ chains: ["GetPlayer.GetName", "GetPlayer.GetFirstName"],
42
+ read: (ctx) => firstName(ctx),
43
+ },
44
+ {
45
+ chains: ["GetPlayer.GetFullName"],
46
+ read: (ctx) => {
47
+ const first = firstName(ctx);
48
+ const house = scalar(ctx.meta, "meta_house_name");
49
+ return first && house ? `${first} ${house}` : first;
50
+ },
51
+ },
52
+ {
53
+ chains: ["GetPlayer.GetAge"],
54
+ read: (ctx) => age(scalar(ctx.character, "birth"), scalar(ctx.meta, "meta_date")),
55
+ },
56
+ {
57
+ chains: ["GetPlayer.GetGold"],
58
+ read: (ctx) => thousands(scalar(block(aliveData(ctx), "gold"), "value")),
59
+ },
60
+ {
61
+ chains: ["GetPlayer.GetPrestige"],
62
+ read: (ctx) => thousands(scalar(block(aliveData(ctx), "prestige"), "currency")),
63
+ },
64
+ {
65
+ chains: ["GetPlayer.GetPiety"],
66
+ read: (ctx) => thousands(scalar(block(aliveData(ctx), "piety"), "currency")),
67
+ },
68
+ {
69
+ // `meta_title_name` IS the primary title's name, article and all.
70
+ chains: ["GetPlayer.GetPrimaryTitle.GetName"],
71
+ read: (ctx) => scalar(ctx.meta, "meta_title_name"),
72
+ },
73
+ {
74
+ // The meta carries the HOUSE name; for most characters the dynasty reads
75
+ // the same, and a preview shows what is knowable.
76
+ chains: ["GetPlayer.GetDynasty.GetName", "GetPlayer.GetHouse.GetName"],
77
+ read: (ctx) => scalar(ctx.meta, "meta_house_name"),
78
+ },
79
+ {
80
+ chains: ["GetCurrentDate", "GetGameDate"],
81
+ read: (ctx) => ctx.date,
82
+ },
83
+ ];
84
+
85
+ /**
86
+ * The character variables the save holds, as the two chains a modder reads them
87
+ * back with. `alive_data.variables.data` is a list of
88
+ * `{ flag=<name> data={ type=… identity=… } }`; a script value is conventionally
89
+ * named exactly like the variable it mirrors, so both chains answer from it.
90
+ * A variable the save stores without a value (a bare flag) stays absent.
91
+ */
92
+ function characterVariables(ctx: SaveContext): Record<string, string> {
93
+ const out: Record<string, string> = {};
94
+ const data = block(block(aliveData(ctx), "variables"), "data");
95
+ for (const s of statements(data)) {
96
+ if (s.kind !== "value" || s.value.kind !== "block") continue;
97
+ const name = scalar(s.value, "flag");
98
+ const text = variableValue(block(s.value, "data"));
99
+ if (!name || text === undefined) continue;
100
+ out[`GetPlayer.MakeScope.Var('${name}').GetValue`] = text;
101
+ out[`GetPlayer.MakeScope.ScriptValue('${name}')`] = text;
102
+ }
103
+ return out;
104
+ }
105
+
106
+ /** One variable's stored value; `type=value` is fixed point, x100000. */
107
+ function variableValue(data: BlockNode | undefined): string | undefined {
108
+ const identity = scalar(data, "identity");
109
+ if (identity === undefined) return undefined;
110
+ switch (scalar(data, "type")) {
111
+ case "value":
112
+ return fixedPoint(identity);
113
+ case "boolean":
114
+ return identity === "0" ? "no" : "yes";
115
+ default:
116
+ // A character/title/… variable: the id is all the save holds about it.
117
+ return identity;
118
+ }
119
+ }
120
+
121
+ /** `2700000` -> `27`, `25000` -> `0.25`. */
122
+ function fixedPoint(raw: string): string | undefined {
123
+ const n = Number(raw);
124
+ if (!Number.isFinite(n)) return undefined;
125
+ return String(Number((n / 100000).toFixed(5)));
126
+ }
127
+
128
+ export const CK3_SAVE_SCHEMA: SaveSchema = {
129
+ dateKey: "meta_date",
130
+ nameKey: "meta_player_name",
131
+ mappings: CK3_MAPPINGS,
132
+ expand: characterVariables,
133
+ record: { marker: "\nplayed_character={", idKey: "character", block: "living" },
134
+ };