@px-lsp/server 0.3.0 → 0.3.1

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 (51) hide show
  1. package/README.md +1 -1
  2. package/data/ck3/skeletons.json +1 -0
  3. package/data/eu5/skeletons.json +1 -0
  4. package/data/vic3/skeletons.json +1 -0
  5. package/dist/browser-data/ck3/docs.json +1 -1
  6. package/dist/browser-data/ck3/tokens.json +1 -1
  7. package/dist/browser-data/vic3/tokens.json +1 -1
  8. package/dist/browser.js +50 -37
  9. package/dist/server.js +2000 -352
  10. package/dist/types/packages/server/src/features/blockSnippets.d.ts +20 -4
  11. package/dist/types/packages/server/src/features/completion.d.ts +11 -0
  12. package/dist/types/packages/server/src/features/definitionSkeletons.d.ts +24 -0
  13. package/dist/types/packages/server/src/games/ck3/schema.d.ts +1 -1
  14. package/dist/types/packages/server/src/games/eu5/index.d.ts +1 -1
  15. package/dist/types/packages/server/src/games/profile.d.ts +85 -1
  16. package/dist/types/packages/server/src/schema/skeletons.d.ts +102 -0
  17. package/dist/types/packages/server/src/schema/types.d.ts +30 -0
  18. package/package.json +3 -3
  19. package/src/coa/coa.ts +31 -0
  20. package/src/coa/coaDesigner.ts +131 -0
  21. package/src/coa/coaParse.ts +3 -0
  22. package/src/creators/definitionEdit.ts +198 -0
  23. package/src/creators/definitionForm.ts +479 -0
  24. package/src/creators/modifierFormats.ts +0 -0
  25. package/src/data/docsParser.ts +21 -4
  26. package/src/features/blockSnippets.ts +151 -20
  27. package/src/features/calendarDates.ts +8 -5
  28. package/src/features/completion.ts +38 -4
  29. package/src/features/definitionSkeletons.ts +109 -0
  30. package/src/features/inlayHints.ts +5 -2
  31. package/src/features/locText.ts +223 -0
  32. package/src/features/snippetList.ts +83 -0
  33. package/src/games/ck3/index.ts +16 -0
  34. package/src/games/ck3/meta.ts +81 -1
  35. package/src/games/ck3/schema.ts +83 -7
  36. package/src/games/ck3/structures.ts +37 -0
  37. package/src/games/eu5/index.ts +7 -1
  38. package/src/games/eu5/meta.ts +5 -1
  39. package/src/games/profile.ts +77 -1
  40. package/src/games/vic3/index.ts +4 -0
  41. package/src/games/vic3/meta.ts +5 -1
  42. package/src/gui/sourceModel.ts +51 -8
  43. package/src/gui/textResolve.ts +3 -3
  44. package/src/overview/dynastyTree.ts +493 -0
  45. package/src/overview/eventGraph.ts +21 -1
  46. package/src/overview/eventVocabulary.ts +33 -33
  47. package/src/overview/exampleWiki.ts +4 -1
  48. package/src/schema/loader.ts +2 -1
  49. package/src/schema/skeletons.ts +182 -0
  50. package/src/schema/types.ts +30 -0
  51. package/src/server.ts +147 -6
package/dist/server.js CHANGED
@@ -1670,8 +1670,8 @@ var require_messageWriter = __commonJS({
1670
1670
  get onError() {
1671
1671
  return this.errorEmitter.event;
1672
1672
  }
1673
- fireError(error, message, count) {
1674
- this.errorEmitter.fire([this.asError(error), message, count]);
1673
+ fireError(error, message, count2) {
1674
+ this.errorEmitter.fire([this.asError(error), message, count2]);
1675
1675
  }
1676
1676
  get onClose() {
1677
1677
  return this.closeEmitter.event;
@@ -8097,12 +8097,12 @@ var require_server = __commonJS({
8097
8097
  * @param message The message to add.
8098
8098
  */
8099
8099
  add(message) {
8100
- let count = this._messages[message];
8101
- if (!count) {
8102
- count = 0;
8100
+ let count2 = this._messages[message];
8101
+ if (!count2) {
8102
+ count2 = 0;
8103
8103
  }
8104
- count++;
8105
- this._messages[message] = count;
8104
+ count2++;
8105
+ this._messages[message] = count2;
8106
8106
  }
8107
8107
  /**
8108
8108
  * Send all tracked messages to the connection's window.
@@ -8852,8 +8852,8 @@ var require_files = __commonJS({
8852
8852
  exports2.resolveGlobalYarnPath = resolveGlobalYarnPath;
8853
8853
  exports2.resolveModulePath = resolveModulePath;
8854
8854
  var url = __importStar(require("url"));
8855
- var path34 = __importStar(require("path"));
8856
- var fs32 = __importStar(require("fs"));
8855
+ var path37 = __importStar(require("path"));
8856
+ var fs37 = __importStar(require("fs"));
8857
8857
  var child_process_1 = require("child_process");
8858
8858
  function uriToFilePath(uri) {
8859
8859
  const parsed = url.parse(uri);
@@ -8871,7 +8871,7 @@ var require_files = __commonJS({
8871
8871
  segments.shift();
8872
8872
  }
8873
8873
  }
8874
- return path34.normalize(segments.join("/"));
8874
+ return path37.normalize(segments.join("/"));
8875
8875
  }
8876
8876
  function isWindows() {
8877
8877
  return process.platform === "win32";
@@ -8899,9 +8899,9 @@ var require_files = __commonJS({
8899
8899
  const env = process.env;
8900
8900
  const newEnv = /* @__PURE__ */ Object.create(null);
8901
8901
  Object.keys(env).forEach((key) => newEnv[key] = env[key]);
8902
- if (nodePath && fs32.existsSync(nodePath)) {
8902
+ if (nodePath && fs37.existsSync(nodePath)) {
8903
8903
  if (newEnv[nodePathKey]) {
8904
- newEnv[nodePathKey] = nodePath + path34.delimiter + newEnv[nodePathKey];
8904
+ newEnv[nodePathKey] = nodePath + path37.delimiter + newEnv[nodePathKey];
8905
8905
  } else {
8906
8906
  newEnv[nodePathKey] = nodePath;
8907
8907
  }
@@ -8973,9 +8973,9 @@ var require_files = __commonJS({
8973
8973
  }
8974
8974
  if (prefix.length > 0) {
8975
8975
  if (isWindows()) {
8976
- return path34.join(prefix, "node_modules");
8976
+ return path37.join(prefix, "node_modules");
8977
8977
  } else {
8978
- return path34.join(prefix, "lib", "node_modules");
8978
+ return path37.join(prefix, "lib", "node_modules");
8979
8979
  }
8980
8980
  }
8981
8981
  return void 0;
@@ -9014,7 +9014,7 @@ var require_files = __commonJS({
9014
9014
  try {
9015
9015
  const yarn = JSON.parse(line);
9016
9016
  if (yarn.type === "log") {
9017
- return path34.join(yarn.data, "node_modules");
9017
+ return path37.join(yarn.data, "node_modules");
9018
9018
  }
9019
9019
  } catch (e) {
9020
9020
  }
@@ -9036,24 +9036,24 @@ var require_files = __commonJS({
9036
9036
  if (process.platform === "win32") {
9037
9037
  _isCaseSensitive = false;
9038
9038
  } else {
9039
- _isCaseSensitive = !fs32.existsSync(__filename.toUpperCase()) || !fs32.existsSync(__filename.toLowerCase());
9039
+ _isCaseSensitive = !fs37.existsSync(__filename.toUpperCase()) || !fs37.existsSync(__filename.toLowerCase());
9040
9040
  }
9041
9041
  return _isCaseSensitive;
9042
9042
  }
9043
9043
  FileSystem2.isCaseSensitive = isCaseSensitive;
9044
9044
  function isParent(parent, child) {
9045
9045
  if (isCaseSensitive()) {
9046
- return path34.normalize(child).indexOf(path34.normalize(parent)) === 0;
9046
+ return path37.normalize(child).indexOf(path37.normalize(parent)) === 0;
9047
9047
  } else {
9048
- return path34.normalize(child).toLowerCase().indexOf(path34.normalize(parent).toLowerCase()) === 0;
9048
+ return path37.normalize(child).toLowerCase().indexOf(path37.normalize(parent).toLowerCase()) === 0;
9049
9049
  }
9050
9050
  }
9051
9051
  FileSystem2.isParent = isParent;
9052
9052
  })(FileSystem || (exports2.FileSystem = FileSystem = {}));
9053
9053
  function resolveModulePath(workspaceRoot, moduleName, nodePath, tracer) {
9054
9054
  if (nodePath) {
9055
- if (!path34.isAbsolute(nodePath)) {
9056
- nodePath = path34.join(workspaceRoot, nodePath);
9055
+ if (!path37.isAbsolute(nodePath)) {
9056
+ nodePath = path37.join(workspaceRoot, nodePath);
9057
9057
  }
9058
9058
  return resolve2(moduleName, nodePath, nodePath, tracer).then((value) => {
9059
9059
  if (FileSystem.isParent(nodePath, value)) {
@@ -9286,9 +9286,9 @@ var require_main = __commonJS({
9286
9286
  exports2.createMessageConnection = createMessageConnection;
9287
9287
  var ril_1 = __importDefault(require_ril());
9288
9288
  ril_1.default.install();
9289
- var path34 = __importStar(require("path"));
9289
+ var path37 = __importStar(require("path"));
9290
9290
  var os2 = __importStar(require("os"));
9291
- var fs32 = __importStar(require("fs"));
9291
+ var fs37 = __importStar(require("fs"));
9292
9292
  var crypto_1 = require("crypto");
9293
9293
  var net_1 = require("net");
9294
9294
  var api_1 = require_api();
@@ -9429,7 +9429,7 @@ var require_main = __commonJS({
9429
9429
  }
9430
9430
  let randomLength = 32;
9431
9431
  const fixedLength = "/lsp-.sock".length;
9432
- const tmpDir = fs32.realpathSync(XDG_RUNTIME_DIR ?? os2.tmpdir());
9432
+ const tmpDir = fs37.realpathSync(XDG_RUNTIME_DIR ?? os2.tmpdir());
9433
9433
  const limit = safeIpcPathLengths.get(process.platform);
9434
9434
  if (limit !== void 0) {
9435
9435
  randomLength = Math.min(limit - tmpDir.length - fixedLength, randomLength);
@@ -9438,7 +9438,7 @@ var require_main = __commonJS({
9438
9438
  throw new Error(`Unable to generate a random pipe name with ${randomLength} characters.`);
9439
9439
  }
9440
9440
  const randomSuffix = (0, crypto_1.randomBytes)(Math.floor(randomLength / 2)).toString("hex");
9441
- return path34.join(tmpDir, `lsp-${randomSuffix}.sock`);
9441
+ return path37.join(tmpDir, `lsp-${randomSuffix}.sock`);
9442
9442
  }
9443
9443
  function createClientPipeTransport(pipeName, encoding = "utf-8") {
9444
9444
  let connectResolve;
@@ -9837,7 +9837,7 @@ var require_main3 = __commonJS({
9837
9837
  logger.error(`Assertion failed: ${message} ${serialize2(rest)}`);
9838
9838
  }
9839
9839
  };
9840
- console.count = function count(label = "default") {
9840
+ console.count = function count2(label = "default") {
9841
9841
  const message = String(label);
9842
9842
  let counter = counters.get(message) ?? 0;
9843
9843
  counter += 1;
@@ -10107,13 +10107,13 @@ function getWellformedEdit(textEdit) {
10107
10107
  }
10108
10108
 
10109
10109
  // src/server.ts
10110
- var fs31 = __toESM(require("fs"));
10110
+ var fs36 = __toESM(require("fs"));
10111
10111
  var os = __toESM(require("os"));
10112
- var path33 = __toESM(require("path"));
10112
+ var path36 = __toESM(require("path"));
10113
10113
  var import_crypto = require("crypto");
10114
10114
 
10115
10115
  // package.json
10116
- var version = "0.3.0";
10116
+ var version = "0.3.1";
10117
10117
 
10118
10118
  // ../protocol/src/arrays.ts
10119
10119
  function pushAll(target, source) {
@@ -10203,6 +10203,7 @@ var modFileChangedNotification = "paradox/modFileChanged";
10203
10203
  var reloadDocsRequest = "paradox/reloadDocs";
10204
10204
  var indexStatsRequest = "paradox/indexStats";
10205
10205
  var lookupLocRequest = "paradox/lookupLoc";
10206
+ var locTextRequest = "paradox/locText";
10206
10207
  var statusNotification = "paradox/status";
10207
10208
  var indexChangedNotification = "paradox/indexChanged";
10208
10209
  var progressNotification = "paradox/progress";
@@ -10236,9 +10237,24 @@ var eventBannerRequest = "paradox/eventBanner";
10236
10237
  var eventVocabularyRequest = "paradox/eventVocabulary";
10237
10238
  var EVENT_VOCABULARY_MAX_TOKENS = 600;
10238
10239
  var EVENT_VOCABULARY_MAX_VALUES = 400;
10240
+ var DEFINITION_FORM_MAX_SAMPLED = 80;
10241
+ var DEFINITION_FORM_MAX_EXAMPLE = 120;
10239
10242
  var eventValueOptionsRequest = "paradox/eventValueOptions";
10240
10243
  var dependenciesRequest = "paradox/dependencies";
10244
+ var snippetsRequest = "paradox/snippets";
10241
10245
  var scopeAtRequest = "paradox/scopeAt";
10246
+ var definitionFormRequest = "paradox/definitionForm";
10247
+ var definitionEditRequest = "paradox/definitionEdit";
10248
+ var modifierFormatsRequest = "paradox/modifierFormats";
10249
+ var dynastyTreeRequest = "paradox/dynastyTree";
10250
+ var DYNASTY_SKILLS = [
10251
+ "diplomacy",
10252
+ "martial",
10253
+ "stewardship",
10254
+ "intrigue",
10255
+ "learning",
10256
+ "prowess"
10257
+ ];
10242
10258
 
10243
10259
  // src/parser/cst.ts
10244
10260
  var LineIndex = class {
@@ -10358,11 +10374,11 @@ function offsetInRange(offset, range) {
10358
10374
  return offset >= range.start && offset <= range.end;
10359
10375
  }
10360
10376
  function nodeAtOffset(root, offset) {
10361
- const path34 = [];
10377
+ const path37 = [];
10362
10378
  const searchStatements = (statements2) => {
10363
10379
  for (const stmt of statements2) {
10364
10380
  if (!offsetInRange(offset, stmt.range)) continue;
10365
- path34.push(stmt);
10381
+ path37.push(stmt);
10366
10382
  const child = statementChildBlock(stmt);
10367
10383
  if (child && offsetInRange(offset, child.range)) {
10368
10384
  searchStatements(child.statements);
@@ -10372,7 +10388,7 @@ function nodeAtOffset(root, offset) {
10372
10388
  return false;
10373
10389
  };
10374
10390
  searchStatements(root.statements);
10375
- return path34.length > 0 ? { path: path34 } : null;
10391
+ return path37.length > 0 ? { path: path37 } : null;
10376
10392
  }
10377
10393
 
10378
10394
  // src/parser/lexer.ts
@@ -10743,9 +10759,12 @@ var ck3Meta = {
10743
10759
  shortName: "CK3",
10744
10760
  engine: "jomini",
10745
10761
  descriptor: "mod",
10746
- configDirName: ".ck3modding",
10762
+ configDirName: ".px-toolkit",
10763
+ legacyConfigDirName: ".ck3modding",
10747
10764
  docsFolderName: "Crusader Kings III",
10748
10765
  steamAppId: 1158310,
10766
+ // Verified on the 1.19 install: dlc_001.dds .. dlc_029.dds.
10767
+ dlcIconDir: "gfx/interface/icons/dlc",
10749
10768
  eventNamespaces: true,
10750
10769
  // The engine's own default metrics: they were measured on this game's font,
10751
10770
  // and the layout engine reuses them for games whose probe has not run.
@@ -10753,6 +10772,60 @@ var ck3Meta = {
10753
10772
  guiTextMetrics: GITAN_MEASURED_METRICS,
10754
10773
  scaffolds: CK3_SCAFFOLDS,
10755
10774
  tiger: { binaryName: "ck3-tiger", repoSlug: "amtep/tiger", confName: "ck3-tiger.conf" },
10775
+ // Visual creators. Every row is backed by a folder the schema table already
10776
+ // indexes and a shape read out of the game's own files:
10777
+ // trait common/traits, documented by common/traits/_traits.info
10778
+ // dynasty_legacy common/dynasty_legacies, per _dynasty_legacies.info
10779
+ // ("Dynasty Legacies are containers for perks"); the perks
10780
+ // themselves live in common/dynasty_perks (`legacy = <track>`)
10781
+ // culture common/culture/cultures (00_arabic.txt: color, ethos,
10782
+ // heritage, language, traditions, name_list, parents…)
10783
+ // culture_tradition common/culture/traditions, whose _traditions.info adds
10784
+ // `category` and `layers` to the shape _cultural_traits.info
10785
+ // documents for every cultural trait
10786
+ // dynasty_tree NOT a definition kind: history/characters linked by
10787
+ // father/mother/dynasty/house (history/characters/*.txt)
10788
+ creators: [
10789
+ {
10790
+ kind: "trait",
10791
+ label: "Trait Creator",
10792
+ icon: "sparkles",
10793
+ tip: "Design a character trait and write it into the mod."
10794
+ },
10795
+ {
10796
+ kind: "dynasty_legacy",
10797
+ label: "Dynasty Legacy Creator",
10798
+ icon: "layers",
10799
+ tip: "Build a legacy track and its perks."
10800
+ },
10801
+ {
10802
+ kind: "culture",
10803
+ label: "Culture Creator",
10804
+ icon: "globe",
10805
+ tip: "Compose a culture from the game's own pillars and traditions."
10806
+ },
10807
+ {
10808
+ kind: "culture_tradition",
10809
+ label: "Tradition Creator",
10810
+ icon: "flame",
10811
+ tip: "Design a culture tradition, its layered icon and its modifiers."
10812
+ },
10813
+ {
10814
+ kind: "dynasty_tree",
10815
+ label: "Dynasty Tree",
10816
+ icon: "users",
10817
+ tip: "See and edit a dynasty's characters across history."
10818
+ }
10819
+ ],
10820
+ // Measured on the 1.19 install: common/modifier_definition_formats/ holds 13
10821
+ // files documented by _definitions.info in the same folder (decimals, color,
10822
+ // prefix/suffix/negative_suffix as loc keys, percent, already_percent,
10823
+ // hidden, no_difference_sign), and gui/texticons.gui holds the `texticon`
10824
+ // blocks a `[gold_i]` in one of those loc values resolves to.
10825
+ modifierFormats: {
10826
+ folder: "common/modifier_definition_formats",
10827
+ textIcons: "gui/texticons.gui"
10828
+ },
10756
10829
  // Date-format keys verified in ck3.exe 1.19 (the gamedate.cpp string block);
10757
10830
  // formats mirror game/localization/english/core_l_english.yml. Vanilla
10758
10831
  // appends $ERA$/$ERA_BCE$ itself - dropped here, the {era} slot replaces it.
@@ -10794,7 +10867,30 @@ var ck3Meta = {
10794
10867
  ],
10795
10868
  // "" keeps the pre-profile cache filenames (docsCache.json, vanillaIndex-*.json)
10796
10869
  // so existing users' caches survive the M2 restructure.
10797
- cacheSuffix: ""
10870
+ cacheSuffix: "",
10871
+ // Coat-of-arms coverage measured against this install (1.19.0.6, 2026-09-03):
10872
+ // parseCoaFile reads all 2992 vanilla definitions in the 10 files of
10873
+ // common/coat_of_arms/coat_of_arms with 0 parse errors, and 100% of both the
10874
+ // 7800 texture references (1629 files, every one decoding) and the 15327
10875
+ // colors resolve, the named ones out of the 112 in common/named_colors. 239
10876
+ // flags (8.0%) carried something the model dropped, 387 of those 399 keys
10877
+ // being `depth` on an instance, which the model now carries; Vic3, which has
10878
+ // shipped the builder since 0.3.2, drops something on 8.3% of its flags, so
10879
+ // CK3 is no worse off.
10880
+ flagBuilder: true,
10881
+ // Measured on this install (1.19.0.6, 2026-09-03): the in-game designer's
10882
+ // own files are all present and parse - 38 visible patterns of 42 rows in
10883
+ // gfx/coat_of_arms/patterns/50_coa_designer_patterns.txt, 1576 visible
10884
+ // emblems of 1578 rows in 13 categories in
10885
+ // colored_emblems/50_coa_designer_emblems.txt, 13 palette colors in
10886
+ // color_palettes/50_coa_designer_palettes.txt, 35 whole layouts in
10887
+ // emblem_layouts/50_coa_designer_emblem_layouts.txt, the
10888
+ // `coa_designer_blank_default` template in
10889
+ // common/coat_of_arms/coat_of_arms/99_coa_designer_templates.txt and 35
10890
+ // preview frames under gfx/interface/coat_of_arms. Victoria 3 ships no
10891
+ // gfx/coat_of_arms/color_palettes or emblem_layouts at all, and EU5 no coa
10892
+ // designer either, so neither sets this.
10893
+ coaDesigner: true
10798
10894
  };
10799
10895
 
10800
10896
  // src/games/jomini/variables.ts
@@ -21412,7 +21508,44 @@ var CURATED = {
21412
21508
  };
21413
21509
  var KEY_PATCHES = {
21414
21510
  trait: {
21415
- valid_sex: { values: "enum:all|male|female", doc: "Which sex can have the trait. Default: all." }
21511
+ valid_sex: { values: "enum:all|male|female", doc: "Which sex can have the trait. Default: all." },
21512
+ // `opposites = { chaste }` in game/common/traits/00_traits.txt: the entries
21513
+ // are trait names. Deliberately NOT a global REF_FIELD - scripted_relations
21514
+ // spells the same key over relation names (00_scripted_relations.txt,
21515
+ // `friend = { opposites = { rival ... } }`), and a global row would
21516
+ // mis-resolve there.
21517
+ opposites: { refKinds: ["trait"] }
21518
+ },
21519
+ culture: {
21520
+ // game/common/culture/cultures/00_arabic.txt: `traditions = {
21521
+ // tradition_tribe_unity ... }` names definitions of
21522
+ // common/culture/traditions, and `parents = { bedouin assyrian }` names
21523
+ // other cultures (bedouin is the top-level key of the same file).
21524
+ traditions: { refKinds: ["culture_tradition"] },
21525
+ parents: { refKinds: ["culture"] },
21526
+ // The five pillar keys all name definitions of common/culture/pillars
21527
+ // (00_arabic.txt: `ethos = ethos_stoic heritage = heritage_arabic
21528
+ // language = language_arabic martial_custom = martial_custom_male_only
21529
+ // head_determination = head_determination_domain`, each of them a
21530
+ // top-level key in common/culture/pillars/00_*.txt). One folder, so one
21531
+ // ref kind; the pillar's own `type` sorts the five pickers apart (the
21532
+ // schema entry's groupKey).
21533
+ ethos: { refKinds: ["culture_pillar"] },
21534
+ heritage: { refKinds: ["culture_pillar"] },
21535
+ language: { refKinds: ["culture_pillar"] },
21536
+ martial_custom: { refKinds: ["culture_pillar"] },
21537
+ head_determination: { refKinds: ["culture_pillar"] },
21538
+ // `name_list = name_list_bedouin` (00_arabic.txt) names a top-level key of
21539
+ // common/culture/name_lists (00_arabic.txt line 1 there).
21540
+ name_list: { refKinds: ["name_list"] }
21541
+ },
21542
+ dynasty_perk: {
21543
+ // _dynasty_perks.info spells the block `traits = { trait_name = int }`, and
21544
+ // game/common/dynasty_perks/00_dynasty_perks.txt writes it that way
21545
+ // (blood_legacy_4: `beauty_good_1 = 100`, `fecund = 50`), so the entry keys
21546
+ // are trait names and the numbers are AI chances. Per-KIND and not a global
21547
+ // RefField: `traits` elsewhere is a history list, a filter, a count.
21548
+ traits: { refKinds: ["trait"] }
21416
21549
  },
21417
21550
  scheme_type: {
21418
21551
  category: { values: "enum:personal|contract|hostile" }
@@ -21710,7 +21843,20 @@ var CK3_SCHEMA_BASE = [
21710
21843
  { path: "events", kind: "event", extraction: "event-id", rootScopes: ["character"] },
21711
21844
  { path: "localization", kind: "loc_key", ext: ".yml", extraction: "loc-key" },
21712
21845
  // --- Characters, traits, interactions ---
21713
- { path: "common/traits", kind: "trait", requiredLoc: ["trait_$"], rootScopes: ["character"] },
21846
+ // locPatterns and iconFolder verbatim from common/traits/_traits.info
21847
+ // ("== Loc/icon ==": "the name key is trait_<key>, and the desc key is
21848
+ // trait_<key>_desc", "The default icon path is
21849
+ // gfx/interface/icons/traits/<trait>.dds"). trait_$_desc stays OUT of
21850
+ // requiredLoc: schemaVanilla.test.ts measures it at 90.4% of vanilla traits,
21851
+ // under the 95% bar a missing-loc diagnostic needs.
21852
+ {
21853
+ path: "common/traits",
21854
+ kind: "trait",
21855
+ requiredLoc: ["trait_$"],
21856
+ locPatterns: ["trait_$", "trait_$_desc"],
21857
+ iconFolder: "gfx/interface/icons/traits",
21858
+ rootScopes: ["character"]
21859
+ },
21714
21860
  {
21715
21861
  path: "common/character_interactions",
21716
21862
  kind: "character_interaction",
@@ -21773,13 +21919,43 @@ var CK3_SCHEMA_BASE = [
21773
21919
  { path: "common/court_amenities", kind: "court_amenity" },
21774
21920
  { path: "common/court_types", kind: "court_type" },
21775
21921
  // --- Culture ---
21776
- { path: "common/culture/cultures", kind: "culture", requiredLoc: ["$"] },
21777
- { path: "common/culture/pillars", kind: "culture_pillar" },
21922
+ {
21923
+ path: "common/culture/cultures",
21924
+ kind: "culture",
21925
+ requiredLoc: ["$"],
21926
+ // Every one of the 244 vanilla cultures defines all three keys in
21927
+ // game/localization/english/culture/cultures_l_english.yml (`bedouin`,
21928
+ // `bedouin_prefix`, `bedouin_collective_noun`), measured 244/244 in
21929
+ // 2026-09. requiredLoc keeps only `$` because the diagnostic that hangs
21930
+ // off it is the conservative one; a creator writes the whole set.
21931
+ locPatterns: ["$", "$_prefix", "$_collective_noun"]
21932
+ },
21933
+ {
21934
+ path: "common/culture/pillars",
21935
+ kind: "culture_pillar",
21936
+ // One folder, five families: game/common/culture/pillars/_pillars.info
21937
+ // documents `type = ethos/heritage/language/martial` on top of the common
21938
+ // cultural-trait shape, and the files spell it that way
21939
+ // (00_ethos.txt `type = ethos`, 00_martial_custom.txt `type =
21940
+ // martial_custom`, 00_head_determination.txt `type = head_determination`).
21941
+ groupKey: "type",
21942
+ // Measured 2026-09-04 in game/localization/english against the 162 vanilla
21943
+ // pillars: `$_name` 160, `$_desc` 9, bare `$` 4. The _pillars.info doc
21944
+ // names only the generic-desc fallback, so the name pattern is a measurement.
21945
+ requiredLoc: ["$_name"],
21946
+ locPatterns: ["$_name", "$_desc"]
21947
+ },
21778
21948
  {
21779
21949
  path: "common/culture/traditions",
21780
21950
  kind: "culture_tradition",
21781
21951
  // def names already carry the `tradition_` prefix; loc key is `<name>_name`.
21782
21952
  requiredLoc: ["$_name"],
21953
+ // All 196 vanilla traditions also define `$_desc`, measured 196/196 in
21954
+ // game/localization/english against common/culture/traditions in 2026-09
21955
+ // (`tradition_winter_warriors_name` / `_desc`). requiredLoc keeps only
21956
+ // `$_name` because the diagnostic that hangs off it is the conservative
21957
+ // one; a creator writes the whole set.
21958
+ locPatterns: ["$_name", "$_desc"],
21783
21959
  rootScopes: ["culture"]
21784
21960
  },
21785
21961
  { path: "common/culture/innovations", kind: "innovation", requiredLoc: ["$"], rootScopes: ["culture"] },
@@ -21799,8 +21975,33 @@ var CK3_SCHEMA_BASE = [
21799
21975
  // dynasties top-level keys are numeric ids; huge and never typed → not completable.
21800
21976
  { path: "common/dynasties", kind: "dynasty", completable: false },
21801
21977
  { path: "common/dynasty_houses", kind: "dynasty_house", completable: false },
21802
- { path: "common/dynasty_legacies", kind: "dynasty_legacy" },
21803
- { path: "common/dynasty_perks", kind: "dynasty_perk" },
21978
+ // _dynasty_legacies.info, "Generated loc keys: key + "_name"". All 21 vanilla
21979
+ // legacies define it (measured 2026-09-03), so it clears the requiredLoc bar.
21980
+ // $_desc is not in the .info doc, but all 21 vanilla tracks define it
21981
+ // (localization/english/dynasty_legacies/legacies_l_english.yml, e.g.
21982
+ // blood_legacy_track_desc), so a creator asks for it too; it stays out of
21983
+ // requiredLoc, where only the keys the doc itself states belong.
21984
+ // iconFolder: window_dynasty_legacy.gui draws the picture from
21985
+ // "[DynastyLegacy.GetIcon]", a code-side path with no script key behind it,
21986
+ // and the files it finds are gfx/interface/icons/dynasty/<track key>.dds
21987
+ // (22 files for the 21 tracks). Name-derived, so a creator writes the
21988
+ // picture under the track's own name and never a path into the block.
21989
+ // A track reads a SECOND name-derived picture, the window's illustration at
21990
+ // gfx/interface/illustrations/legacy_tracks/<key>.dds ("[DynastyLegacy.
21991
+ // GetTrackIcon]", 21 files); iconFolder holds one path, so that one is left
21992
+ // to ck3-tiger, which names it as a missing-file warning on the track.
21993
+ {
21994
+ path: "common/dynasty_legacies",
21995
+ kind: "dynasty_legacy",
21996
+ requiredLoc: ["$_name"],
21997
+ locPatterns: ["$_name", "$_desc"],
21998
+ iconFolder: "gfx/interface/icons/dynasty"
21999
+ },
22000
+ // _dynasty_perks.info, "Generated loc keys: key + "_name"" — and only that:
22001
+ // all 105 vanilla perks define $_name, none defines $_desc (measured
22002
+ // 2026-09-03 over localization/english). No iconFolder: gfx/interface/icons
22003
+ // has a dynasty folder for the tracks and nothing for the perks.
22004
+ { path: "common/dynasty_perks", kind: "dynasty_perk", locPatterns: ["$_name"] },
21804
22005
  // --- Activities & schemes ---
21805
22006
  { path: "common/activities/activity_types", kind: "activity_type", rootScopes: ["character"] },
21806
22007
  {
@@ -21853,7 +22054,15 @@ var CK3_SCHEMA_BASE = [
21853
22054
  // remaining common/ folder with a standard top-level `name = { ... }`
21854
22055
  // layout, verified by parsing vanilla files. Ordered by definition count.
21855
22056
  { path: "common/game_concepts", kind: "game_concept", requiredLoc: ["game_concept_$"] },
21856
- { path: "common/domiciles/buildings", kind: "domicile_building", rootScopes: ["character", "domicile"] },
22057
+ {
22058
+ path: "common/domiciles/buildings",
22059
+ kind: "domicile_building",
22060
+ rootScopes: ["character", "domicile"],
22061
+ // Measured 2026-09-04: all 1620 vanilla domicile buildings define
22062
+ // `$_domicile_building` (109 also `$_domicile_building_desc`), none a bare `$`.
22063
+ requiredLoc: ["$_domicile_building"],
22064
+ locPatterns: ["$_domicile_building", "$_domicile_building_desc"]
22065
+ },
21857
22066
  { path: "common/domiciles/types", kind: "domicile_type", rootScopes: ["character"] },
21858
22067
  { path: "common/artifacts/features", kind: "artifact_feature" },
21859
22068
  { path: "common/artifacts/feature_groups", kind: "artifact_feature_group" },
@@ -30798,6 +31007,9 @@ var guiSchema_default = {
30798
31007
  ]
30799
31008
  };
30800
31009
 
31010
+ // data/ck3/skeletons.json
31011
+ var skeletons_default = { meta: { generated: "2026-09-03", sources: ["vanilla (3077 files)"], majority: 0.5 }, kinds: { accolade_name: { sampled: 16, keys: [{ key: "key" }, { key: "num_options", choices: ["2", "1"] }, { key: "option", block: [{ key: "random_valid", block: [] }] }, { key: "potential", block: [] }], blocks: { option: { sampled: 26, keys: [{ key: "random_valid", block: [{ key: "triggered_desc", block: [] }, { key: "desc" }] }] } } }, accolade_type: { sampled: 31, keys: [{ key: "adjective" }, { key: "noun" }, { key: "icon" }, { key: "portrait_pose" }, { key: "accolade_categories", block: [] }, { key: "squire_perfect_fit", block: [] }, { key: "squire_acceptable_fit", block: [] }, { key: "weight", block: [{ key: "value", choices: ["accolade_uninteresting_base_value", "accolade_standard_base_value", "accolade_fascinating_base_value"] }, { key: "if", block: [] }] }, { key: "ranks", block: [{ key: "1", block: [] }, { key: "2", block: [] }, { key: "3", block: [] }] }], blocks: { ranks: { sampled: 31, keys: [{ key: "1", block: [{ key: "liege_modifier", block: [] }] }, { key: "2", block: [{ key: "liege_modifier", block: [] }, { key: "knight_army_modifier", block: [] }] }, { key: "3", block: [{ key: "liege_modifier", block: [] }, { key: "knight_army_modifier", block: [] }] }] }, weight: { sampled: 31, keys: [{ key: "value", choices: ["accolade_uninteresting_base_value", "accolade_standard_base_value", "accolade_fascinating_base_value"] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "add", placeholder: "50" }] }] } } }, achievement: { sampled: 188, keys: [{ key: "possible", block: [] }, { key: "happened", block: [{ key: "custom_description", block: [] }] }], blocks: { happened: { sampled: 188, keys: [{ key: "custom_description", block: [{ key: "text" }] }] } } }, activity_intent: { sampled: 59, keys: [{ key: "icon" }, { key: "auto_complete", choices: ["yes"] }, { key: "on_invalidated", block: [{ key: "trigger_event", choices: ["activity_system.0090"] }] }, { key: "scripted_animation", block: [{ key: "animation" }] }, { key: "ai_will_do", block: [{ key: "value" }, { key: "if", block: [] }] }], blocks: { ai_will_do: { sampled: 49, keys: [{ key: "value" }, { key: "if", block: [{ key: "limit", block: [] }, { key: "add" }] }] }, on_invalidated: { sampled: 31, keys: [{ key: "trigger_event", choices: ["activity_system.0090"] }] }, scripted_animation: { sampled: 59, keys: [{ key: "animation" }] } } }, activity_pulse_action: { sampled: 307, keys: [{ key: "icon" }, { key: "is_valid", block: [{ key: "is_current_phase_active", choices: ["yes"] }, { key: "any_attending_character", block: [] }] }, { key: "weight", block: [{ key: "value" }] }, { key: "effect", block: [{ key: "random_attending_character", block: [] }, { key: "add_activity_log_entry", block: [] }] }], blocks: { effect: { sampled: 307, keys: [{ key: "random_attending_character", block: [{ key: "limit", block: [] }, { key: "save_scope_as", choices: ["first", "second", "hunter", "eggman", "hunter_2", "poet"] }] }, { key: "add_activity_log_entry", block: [{ key: "key" }, { key: "tags", block: [] }, { key: "character" }] }] }, is_valid: { sampled: 307, keys: [{ key: "is_current_phase_active", choices: ["yes"] }, { key: "any_attending_character", block: [] }] }, weight: { sampled: 255, keys: [{ key: "value" }] } } }, activity_type: { sampled: 21, keys: [{ key: "is_shown", block: [{ key: "highest_held_title_tier", choices: ["tier_barony", "tier_county", "tier_empire"] }, { key: "trigger_if", block: [] }] }, { key: "can_start_showing_failures_only", block: [{ key: "NOT", block: [] }, { key: "trigger_if", block: [] }] }, { key: "is_valid", block: [{ key: "trigger_if", block: [] }] }, { key: "on_invalidated", block: [{ key: "if", block: [] }] }, { key: "on_host_death", block: [] }, { key: "is_location_valid", block: [] }, { key: "ai_check_interval_by_tier", block: [{ key: "barony", choices: ["0"] }, { key: "county", choices: ["60", "0", "4", "72"] }, { key: "duchy", choices: ["60", "0", "4"] }, { key: "kingdom", choices: ["60", "0", "4"] }, { key: "empire", choices: ["60", "0", "100", "4"] }, { key: "hegemony", choices: ["60", "0", "12", "4"] }] }, { key: "ai_will_do", block: [{ key: "value", choices: ["0", "50", "100", "1000", "20", "30"] }, { key: "if", block: [] }, { key: "else_if", block: [] }] }, { key: "is_single_location", choices: ["yes", "no"] }, { key: "province_filter" }, { key: "province_description", block: [{ key: "desc" }, { key: "triggered_desc", block: [] }] }, { key: "ai_province_filter", choices: ["capital", "domicile_domain", "domain", "holy_sites", "holy_sites_realm", "realm", "top_realm"] }, { key: "max_province_icons", choices: ["5", "3", "1", "10000"] }, { key: "cooldown", block: [{ key: "years" }] }, { key: "province_score", block: [{ key: "add", choices: ["camp_party_province_reference_value", "coronation_province_reference_value", "hunt_province_reference_value", "journey_province_reference_value", "roaming_province_reference_value", "survey_province_reference_value"] }, { key: "if", block: [] }] }, { key: "ai_will_select_province", block: [{ key: "value", choices: ["10", "50", "0", "1", "5", "scope:score"] }, { key: "if", block: [] }] }, { key: "cost", block: [{ key: "gold", block: [] }] }, { key: "ui_predicted_cost", block: [{ key: "gold", block: [] }] }, { key: "options", block: [] }, { key: "phases", block: [] }, { key: "max_guest_arrival_delay_time", block: [{ key: "months", choices: ["5", "1", "2", "4", "8"] }] }, { key: "max_guests", placeholder: "20" }, { key: "host_intents", block: [{ key: "intents", block: [] }, { key: "default" }] }, { key: "can_be_activity_guest", block: [{ key: "in_diplomatic_range", choices: ["scope:host"] }] }, { key: "guest_invite_rules", block: [{ key: "defaults", block: [] }, { key: "rules", block: [] }] }, { key: "guest_intents", block: [{ key: "intents", block: [] }, { key: "default" }] }, { key: "guest_join_chance", block: [{ key: "base", choices: ["10", "5", "-25", "50"] }, { key: "base_activity_modifier", choices: ["yes"] }, { key: "activity_guest_shared_ai_accept_modifier", choices: ["yes"] }, { key: "modifier", block: [] }] }, { key: "pulse_actions", block: [{ key: "entries", block: [] }, { key: "chance_of_no_event", choices: ["5", "0", "3", "2", "10"] }] }, { key: "travel_entourage_selection", block: [{ key: "weight", block: [] }, { key: "max", choices: ["15", "10", "18", "2", "20", "5", "3"] }, { key: "invite_rule_order", choices: ["1", "3"] }, { key: "ai_max", choices: ["3", "15", "2", "7"] }] }, { key: "on_start", block: [{ key: "if", block: [] }] }, { key: "on_enter_active_state", block: [{ key: "surveyor_no_stone_unturned_perk_effect", choices: ["yes"] }] }, { key: "on_enter_passive_state", block: [{ key: "if", block: [] }] }, { key: "background", block: [{ key: "trigger", block: [] }, { key: "texture" }, { key: "environment" }, { key: "ambience" }] }, { key: "on_complete", block: [{ key: "if", block: [] }] }, { key: "window_characters", block: [{ key: "guest", block: [] }, { key: "host", block: [] }, { key: "travel_host", block: [] }] }, { key: "activity_window_widgets", block: [] }], blocks: { ai_check_interval_by_tier: { sampled: 15, keys: [{ key: "barony", choices: ["0"] }, { key: "county", choices: ["60", "0", "4", "72"] }, { key: "duchy", choices: ["60", "0", "4"] }, { key: "kingdom", choices: ["60", "0", "4"] }, { key: "empire", choices: ["60", "0", "100", "4"] }, { key: "hegemony", choices: ["60", "0", "12", "4"] }] }, ai_will_do: { sampled: 21, keys: [{ key: "value", choices: ["0", "50", "100", "1000", "20", "30"] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "add", block: [] }] }, { key: "else_if", block: [{ key: "limit", block: [] }, { key: "add", block: [] }] }] }, ai_will_select_province: { sampled: 20, keys: [{ key: "value", choices: ["10", "50", "0", "1", "5", "scope:score"] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "add", block: [] }] }] }, background: { sampled: 551, keys: [{ key: "trigger", block: [] }, { key: "texture" }, { key: "environment" }, { key: "ambience" }] }, can_be_activity_guest: { sampled: 20, keys: [{ key: "in_diplomatic_range", choices: ["scope:host"] }] }, can_start_showing_failures_only: { sampled: 21, keys: [{ key: "NOT", block: [{ key: "is_activity_type_on_cooldown" }] }, { key: "trigger_if", block: [{ key: "limit", block: [] }, { key: "custom_tooltip", block: [] }] }] }, cooldown: { sampled: 19, keys: [{ key: "years" }] }, cost: { sampled: 21, keys: [{ key: "gold", block: [{ key: "add", block: [] }, { key: "if", block: [] }] }] }, guest_intents: { sampled: 21, keys: [{ key: "intents", block: [] }, { key: "default" }] }, guest_invite_rules: { sampled: 15, keys: [{ key: "defaults", block: [{ key: "1" }, { key: "2" }, { key: "3" }] }, { key: "rules", block: [{ key: "2", choices: ["activity_invite_rule_lovers", "activity_invite_rule_rivals", "activity_invite_rule_potential_lovers", "activity_invite_rule_rivals_if_appropriate", "activity_invite_rule_extended_family", "activity_invite_rule_friends"] }, { key: "3", choices: ["activity_invite_rule_extended_family", "activity_invite_mp", "activity_invite_rule_close_family", "activity_invite_rule_vassals_children"] }, { key: "6", choices: ["activity_invite_mp"] }] }] }, guest_join_chance: { sampled: 19, keys: [{ key: "base", choices: ["10", "5", "-25", "50"] }, { key: "base_activity_modifier", choices: ["yes"] }, { key: "activity_guest_shared_ai_accept_modifier", choices: ["yes"] }, { key: "modifier", block: [{ key: "add" }, { key: "desc" }] }] }, host_intents: { sampled: 21, keys: [{ key: "intents", block: [] }, { key: "default" }] }, is_shown: { sampled: 21, keys: [{ key: "highest_held_title_tier", choices: ["tier_barony", "tier_county", "tier_empire"] }, { key: "trigger_if", block: [{ key: "limit", block: [] }, { key: "NOT", block: [] }] }] }, is_valid: { sampled: 21, keys: [{ key: "trigger_if", block: [{ key: "limit", block: [] }, { key: "has_attending_activity_guests", choices: ["yes"] }] }] }, max_guest_arrival_delay_time: { sampled: 19, keys: [{ key: "months", choices: ["5", "1", "2", "4", "8"] }] }, on_complete: { sampled: 17, keys: [{ key: "if", block: [{ key: "limit", block: [] }] }] }, on_enter_active_state: { sampled: 19, keys: [{ key: "surveyor_no_stone_unturned_perk_effect", choices: ["yes"] }] }, on_enter_passive_state: { sampled: 14, keys: [{ key: "if", block: [{ key: "limit", block: [] }] }] }, on_invalidated: { sampled: 20, keys: [{ key: "if", block: [{ key: "limit", block: [] }, { key: "every_attending_character", block: [] }] }] }, on_start: { sampled: 18, keys: [{ key: "if", block: [{ key: "limit", block: [] }] }] }, province_description: { sampled: 17, keys: [{ key: "desc" }, { key: "triggered_desc", block: [{ key: "trigger", block: [] }, { key: "desc" }] }] }, province_score: { sampled: 13, keys: [{ key: "add", choices: ["camp_party_province_reference_value", "coronation_province_reference_value", "hunt_province_reference_value", "journey_province_reference_value", "roaming_province_reference_value", "survey_province_reference_value"] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "add", placeholder: "100" }] }] }, pulse_actions: { sampled: 21, keys: [{ key: "entries", block: [] }, { key: "chance_of_no_event", choices: ["5", "0", "3", "2", "10"] }] }, travel_entourage_selection: { sampled: 16, keys: [{ key: "weight", block: [{ key: "value", choices: ["standard_travel_entourage_additions", "10", "0"] }, { key: "if", block: [] }] }, { key: "max", choices: ["15", "10", "18", "2", "20", "5", "3"] }, { key: "invite_rule_order", choices: ["1", "3"] }, { key: "ai_max", choices: ["3", "15", "2", "7"] }] }, ui_predicted_cost: { sampled: 21, keys: [{ key: "gold", block: [{ key: "if", block: [] }, { key: "divide", choices: ["5"] }, { key: "ceiling", choices: ["yes"] }, { key: "multiply", choices: ["5"] }] }] }, window_characters: { sampled: 21, keys: [{ key: "guest", block: [{ key: "camera", choices: ["camera_event_center", "camera_body_left", "camera_body", "camera_body_right", "camera_body_left_slight", "camera_event_left", "camera_event_right", "camera_event_left_forward"] }, { key: "effect", block: [] }, { key: "scripted_animation", block: [] }] }, { key: "host", block: [{ key: "camera", choices: ["camera_body", "camera_event_center", "camera_activity_horse", "camera_event_left"] }, { key: "effect", block: [] }, { key: "scripted_animation", block: [] }] }, { key: "travel_host", block: [{ key: "camera", choices: ["camera_event_horse_left", "camera_activity_horse", "camera_event_center"] }, { key: "effect", block: [] }, { key: "scripted_animation", block: [] }] }] } } }, artifact_feature: { sampled: 632, keys: [{ key: "group" }, { key: "trigger", block: [] }, { key: "weight", placeholder: "1" }] }, artifact_feature_group: { sampled: 59, keys: [] }, artifact_slot: { sampled: 24, keys: [{ key: "type" }, { key: "category", choices: ["court", "inventory"] }] }, artifact_template: { sampled: 71, keys: [{ key: "can_equip", block: [{ key: "always", choices: ["yes"] }] }, { key: "can_benefit", block: [] }, { key: "fallback", block: [{ key: "court_grandeur_baseline_add", choices: ["3", "6", "4", "5", "2", "10"] }] }, { key: "unique", choices: ["yes"] }], blocks: { can_equip: { sampled: 70, keys: [{ key: "always", choices: ["yes"] }] }, fallback: { sampled: 71, keys: [{ key: "court_grandeur_baseline_add", choices: ["3", "6", "4", "5", "2", "10"] }] } } }, artifact_type: { sampled: 65, keys: [{ key: "slot" }, { key: "required_features", block: [] }] }, artifact_visual: { sampled: 175, keys: [{ key: "icon" }, { key: "asset", block: [{ key: "trigger", block: [] }, { key: "reference" }] }], blocks: { asset: { sampled: 681, keys: [{ key: "trigger", block: [{ key: "OR", block: [] }] }, { key: "reference" }] } } }, bookmark: { sampled: 19, keys: [{ key: "start_date", choices: ["1066.9.15", "867.1.1", "1178.10.1"] }, { key: "is_playable", choices: ["yes"] }, { key: "group", choices: ["bm_group_1066", "bm_group_867", "bm_group_1178"] }, { key: "character", block: [{ key: "name" }, { key: "dynasty" }, { key: "dynasty_splendor_level", choices: ["2", "1", "3", "5", "4"] }, { key: "type", choices: ["male", "female"] }, { key: "birth" }, { key: "title" }, { key: "government" }, { key: "culture" }, { key: "religion" }, { key: "difficulty", choices: ["BOOKMARK_CHARACTER_DIFFICULTY_HARD", "BOOKMARK_CHARACTER_DIFFICULTY_MEDIUM", "BOOKMARK_CHARACTER_DIFFICULTY_EASY"] }, { key: "history_id" }, { key: "position", block: [] }, { key: "animation" }, { key: "character", block: [] }] }, { key: "weight", block: [{ key: "value", choices: ["0", "100", "200", "50", "90"] }] }], blocks: { character: { sampled: 110, keys: [{ key: "name" }, { key: "dynasty" }, { key: "dynasty_splendor_level", choices: ["2", "1", "3", "5", "4"] }, { key: "type", choices: ["male", "female"] }, { key: "birth" }, { key: "title" }, { key: "government" }, { key: "culture" }, { key: "religion" }, { key: "difficulty", choices: ["BOOKMARK_CHARACTER_DIFFICULTY_HARD", "BOOKMARK_CHARACTER_DIFFICULTY_MEDIUM", "BOOKMARK_CHARACTER_DIFFICULTY_EASY"] }, { key: "history_id" }, { key: "position", block: [] }, { key: "animation" }, { key: "character", block: [{ key: "name" }, { key: "relation" }, { key: "dynasty" }, { key: "type", choices: ["male", "female"] }, { key: "birth" }, { key: "culture" }, { key: "religion" }, { key: "history_id" }, { key: "animation" }] }] }, weight: { sampled: 15, keys: [{ key: "value", choices: ["0", "100", "200", "50", "90"] }] } } }, building: { sampled: 981, keys: [{ key: "construction_time", choices: ["slow_construction_time", "standard_construction_time", "very_slow_construction_time", "quick_construction_time", "500", "0"] }, { key: "effect_desc" }, { key: "can_construct_potential", block: [{ key: "building_requirement_tribal", choices: ["no"] }] }, { key: "type_icon" }, { key: "can_construct", block: [{ key: "building_requirement_castle_city_church", block: [] }] }, { key: "cost_gold" }, { key: "character_modifier", block: [] }, { key: "province_modifier", block: [{ key: "monthly_income" }] }, { key: "county_modifier", block: [{ key: "development_growth_factor" }] }, { key: "next_building" }, { key: "on_complete", block: [{ key: "creator_mandala_built_regular_building_piety_effect", choices: ["yes"] }] }, { key: "ai_value", block: [{ key: "base", placeholder: "100" }, { key: "modifier", block: [] }] }, { key: "type", choices: ["special", "duchy_capital", "great_building"] }, { key: "flag" }], blocks: { ai_value: { sampled: 959, keys: [{ key: "base", placeholder: "100" }, { key: "modifier", block: [{ key: "factor", placeholder: "0" }, { key: "free_building_slots", choices: ["0", "1", "2", "3"] }] }] }, can_construct: { sampled: 654, keys: [{ key: "building_requirement_castle_city_church", block: [{ key: "LEVEL", choices: ["02", "03", "04", "01"] }] }] }, can_construct_potential: { sampled: 847, keys: [{ key: "building_requirement_tribal", choices: ["no"] }] }, county_modifier: { sampled: 741, keys: [{ key: "development_growth_factor" }] }, on_complete: { sampled: 505, keys: [{ key: "creator_mandala_built_regular_building_piety_effect", choices: ["yes"] }] }, province_modifier: { sampled: 870, keys: [{ key: "monthly_income" }] } } }, casus_belli: { sampled: 121, keys: [{ key: "icon" }, { key: "group" }, { key: "combine_into_one", choices: ["yes", "no"] }, { key: "should_show_war_goal_subview", choices: ["yes", "no"] }, { key: "allow_hostages", choices: ["no", "yes"] }, { key: "mutually_exclusive_titles", block: [{ key: "always", choices: ["yes", "no"] }] }, { key: "allowed_for_character", block: [] }, { key: "allowed_against_character", block: [] }, { key: "target_titles", choices: ["all", "neighbor_land_or_water", "neighbor_land", "claim", "none", "de_jure", "independence_domain"] }, { key: "target_de_jure_regions_above", choices: ["yes", "no"] }, { key: "target_title_tier", choices: ["all", "county", "duchy", "kingdom", "empire"] }, { key: "ignore_effect", choices: ["change_title_holder"] }, { key: "valid_to_start", block: [] }, { key: "should_invalidate", block: [] }, { key: "on_invalidated_desc" }, { key: "on_declaration", block: [{ key: "on_declared_war", choices: ["yes"] }] }, { key: "cost", block: [{ key: "prestige", block: [] }] }, { key: "on_victory_desc", block: [{ key: "first_valid", block: [] }] }, { key: "on_victory", block: [{ key: "add_legitimacy_attacker_victory_effect", choices: ["yes"] }, { key: "create_title_and_vassal_change", block: [] }, { key: "every_in_list", block: [] }, { key: "modify_allies_of_participants_fame_values", block: [] }, { key: "add_truce_attacker_victory_effect", choices: ["yes"] }, { key: "laamp_as_mercenary_payout_tooltip_effect", choices: ["yes"] }, { key: "mandala_war_victory_effects", choices: ["yes"] }] }, { key: "on_invalidated", block: [] }, { key: "on_white_peace_desc", block: [{ key: "first_valid", block: [] }] }, { key: "on_white_peace", block: [{ key: "modify_allies_of_participants_fame_values", block: [] }, { key: "add_truce_white_peace_effect", choices: ["yes"] }, { key: "laamp_as_mercenary_payout_tooltip_effect", choices: ["yes"] }] }, { key: "on_defeat", block: [{ key: "add_legitimacy_attacker_defeat_effect", choices: ["yes"] }, { key: "modify_allies_of_participants_fame_values", block: [] }, { key: "add_truce_attacker_defeat_effect", choices: ["yes"] }, { key: "on_lost_aggression_war_discontent_loss", choices: ["yes"] }, { key: "laamp_as_mercenary_payout_tooltip_effect", choices: ["yes"] }, { key: "mandala_war_defeat_effects", choices: ["yes"] }] }, { key: "on_defeat_desc", block: [{ key: "first_valid", block: [] }] }, { key: "on_primary_attacker_death", choices: ["inherit", "invalidate", "inherit_faction"] }, { key: "on_primary_defender_death", choices: ["inherit", "invalidate"] }, { key: "transfer_behavior", choices: ["transfer"] }, { key: "attacker_allies_inherit", choices: ["yes", "no"] }, { key: "defender_allies_inherit", choices: ["yes", "no"] }, { key: "war_name" }, { key: "interface_priority", placeholder: "80" }, { key: "ai_score_mult", block: [{ key: "value" }, { key: "add", choices: ["frankokratia_leader_protection_value", "house_relation_ai_score_value", "1"] }] }, { key: "war_name_base" }, { key: "cb_name" }, { key: "attacker_wargoal_percentage", placeholder: "0.8" }, { key: "max_ai_diplo_distance_to_title", choices: ["500", "1000", "10000", "2000", "1500", "750", "850"] }, { key: "use_de_jure_wargoal_only", choices: ["yes", "no"] }, { key: "max_defender_score_from_occupation", choices: ["150", "50", "100", "0", "25", "75"] }, { key: "max_attacker_score_from_occupation", choices: ["150", "50", "1", "100", "200", "450"] }], blocks: { ai_score_mult: { sampled: 77, keys: [{ key: "value" }, { key: "add", choices: ["frankokratia_leader_protection_value", "house_relation_ai_score_value", "1"] }] }, cost: { sampled: 92, keys: [{ key: "prestige", block: [{ key: "add", block: [] }, { key: "value" }, { key: "multiply", choices: ["common_cb_prestige_cost_multiplier", "common_claim_reduction_cost_multiplier_core", "common_cb_prestige_cost_multiplier_sans_struggles"] }] }] }, mutually_exclusive_titles: { sampled: 62, keys: [{ key: "always", choices: ["yes", "no"] }] }, on_declaration: { sampled: 117, keys: [{ key: "on_declared_war", choices: ["yes"] }] }, on_defeat: { sampled: 121, keys: [{ key: "add_legitimacy_attacker_defeat_effect", choices: ["yes"] }, { key: "modify_allies_of_participants_fame_values", block: [{ key: "WINNER", choices: ["scope:defender"] }, { key: "FAME_BASE", choices: ["medium_prestige_value", "major_prestige_value", "minor_prestige_value", "mongol_invasion_cb_ally_prestige", "religious_cb_ally_prestige_county", "religious_cb_ally_prestige_duchy", "religious_cb_ally_prestige_kingdom"] }, { key: "IS_RELIGIOUS_WAR", choices: ["no"] }, { key: "WINNER_ALLY_FAME_SCALE", choices: ["1", "0", "10"] }, { key: "LOSER_ALLY_FAME_SCALE", choices: ["1", "10"] }] }, { key: "add_truce_attacker_defeat_effect", choices: ["yes"] }, { key: "on_lost_aggression_war_discontent_loss", choices: ["yes"] }, { key: "laamp_as_mercenary_payout_tooltip_effect", choices: ["yes"] }, { key: "mandala_war_defeat_effects", choices: ["yes"] }] }, on_defeat_desc: { sampled: 121, keys: [{ key: "first_valid", block: [{ key: "triggered_desc", block: [] }, { key: "desc" }] }] }, on_victory: { sampled: 121, keys: [{ key: "add_legitimacy_attacker_victory_effect", choices: ["yes"] }, { key: "create_title_and_vassal_change", block: [{ key: "type", choices: ["conquest", "conquest_holy_war", "independency", "swear_fealty", "conquest_claim"] }, { key: "save_scope_as", choices: ["change", "title_change"] }, { key: "add_claim_on_loss", choices: ["yes", "no"] }] }, { key: "every_in_list", block: [{ key: "list", choices: ["target_titles", "titles_taken", "vassals_taken", "target_title_taken", "defender_player_vassals", "titles_to_destroy", "anti_gok_attackers", "anti_gok_defenders"] }] }, { key: "modify_allies_of_participants_fame_values", block: [{ key: "WINNER", choices: ["scope:attacker", "scope:defender"] }, { key: "FAME_BASE", choices: ["medium_prestige_value", "major_prestige_value", "minor_prestige_value", "mongol_invasion_cb_ally_prestige", "religious_cb_ally_prestige_county", "religious_cb_ally_prestige_duchy", "religious_cb_ally_prestige_kingdom"] }, { key: "IS_RELIGIOUS_WAR", choices: ["no"] }, { key: "WINNER_ALLY_FAME_SCALE", choices: ["1", "0", "10"] }, { key: "LOSER_ALLY_FAME_SCALE", choices: ["1", "3"] }] }, { key: "add_truce_attacker_victory_effect", choices: ["yes"] }, { key: "laamp_as_mercenary_payout_tooltip_effect", choices: ["yes"] }, { key: "mandala_war_victory_effects", choices: ["yes"] }] }, on_victory_desc: { sampled: 120, keys: [{ key: "first_valid", block: [{ key: "triggered_desc", block: [] }, { key: "desc" }] }] }, on_white_peace: { sampled: 112, keys: [{ key: "modify_allies_of_participants_fame_values", block: [{ key: "WINNER", choices: ["scope:attacker", "scope:defender"] }, { key: "FAME_BASE" }, { key: "IS_RELIGIOUS_WAR", choices: ["no"] }, { key: "WINNER_ALLY_FAME_SCALE", choices: ["1", "100", "10", "0"] }, { key: "LOSER_ALLY_FAME_SCALE", choices: ["1", "100", "10", "5"] }] }, { key: "add_truce_white_peace_effect", choices: ["yes"] }, { key: "laamp_as_mercenary_payout_tooltip_effect", choices: ["yes"] }] }, on_white_peace_desc: { sampled: 121, keys: [{ key: "first_valid", block: [{ key: "triggered_desc", block: [] }, { key: "desc" }] }] } } }, casus_belli_group: { sampled: 19, keys: [{ key: "allowed_for_character", block: [{ key: "herders_and_tributary_constraints", choices: ["yes"] }, { key: "tgp_dynastic_cycle_offensive_wars_ban_trigger", choices: ["yes"] }, { key: "tgp_war_ban_all_ministers_trigger", choices: ["yes"] }] }], blocks: { allowed_for_character: { sampled: 17, keys: [{ key: "herders_and_tributary_constraints", choices: ["yes"] }, { key: "tgp_dynastic_cycle_offensive_wars_ban_trigger", choices: ["yes"] }, { key: "tgp_war_ban_all_ministers_trigger", choices: ["yes"] }] } } }, challenge_character: { sampled: 52, keys: [{ key: "start_date", choices: ["1066.9.15", "867.1.1", "1178.10.1"] }, { key: "achievements", block: [] }, { key: "character", block: [{ key: "name" }, { key: "dynasty" }, { key: "dynasty_splendor_level", choices: ["1", "3", "4", "2", "5", "6"] }, { key: "type", choices: ["male"] }, { key: "birth" }, { key: "title" }, { key: "government", choices: ["feudal_government", "clan_government", "tribal_government"] }, { key: "culture" }, { key: "religion" }, { key: "difficulty", choices: ["BOOKMARK_CHARACTER_DIFFICULTY_MEDIUM", "BOOKMARK_CHARACTER_DIFFICULTY_HARD", "BOOKMARK_CHARACTER_DIFFICULTY_EASY"] }, { key: "history_id" }, { key: "animation" }] }], blocks: { character: { sampled: 52, keys: [{ key: "name" }, { key: "dynasty" }, { key: "dynasty_splendor_level", choices: ["1", "3", "4", "2", "5", "6"] }, { key: "type", choices: ["male"] }, { key: "birth" }, { key: "title" }, { key: "government", choices: ["feudal_government", "clan_government", "tribal_government"] }, { key: "culture" }, { key: "religion" }, { key: "difficulty", choices: ["BOOKMARK_CHARACTER_DIFFICULTY_MEDIUM", "BOOKMARK_CHARACTER_DIFFICULTY_HARD", "BOOKMARK_CHARACTER_DIFFICULTY_EASY"] }, { key: "history_id" }, { key: "animation" }] } } }, character: { sampled: 71142, keys: [{ key: "name" }, { key: "dynasty" }, { key: "religion" }, { key: "culture" }, { key: "father" }] }, character_interaction: { sampled: 470, keys: [{ key: "category" }, { key: "icon" }, { key: "interface_priority", placeholder: "60" }, { key: "common_interaction", choices: ["yes", "no"] }, { key: "desc" }, { key: "is_shown", block: [] }, { key: "auto_accept", choices: ["yes", "no"] }, { key: "is_valid_showing_failures_only", block: [] }, { key: "on_accept", block: [] }, { key: "ai_targets", block: [{ key: "ai_recipients" }] }, { key: "ai_frequency_by_tier", block: [{ key: "barony", choices: ["0", "72", "120", "180", "36", "60"] }, { key: "county", placeholder: "0" }, { key: "duchy", placeholder: "36" }, { key: "kingdom", placeholder: "12" }, { key: "empire", placeholder: "12" }, { key: "hegemony", placeholder: "0" }] }, { key: "ai_potential", block: [] }, { key: "ai_will_do", block: [{ key: "base" }, { key: "modifier", block: [] }] }], blocks: { ai_frequency_by_tier: { sampled: 271, keys: [{ key: "barony", choices: ["0", "72", "120", "180", "36", "60"] }, { key: "county", placeholder: "0" }, { key: "duchy", placeholder: "36" }, { key: "kingdom", placeholder: "12" }, { key: "empire", placeholder: "12" }, { key: "hegemony", placeholder: "0" }] }, ai_targets: { sampled: 389, keys: [{ key: "ai_recipients" }] }, ai_will_do: { sampled: 297, keys: [{ key: "base" }, { key: "modifier", block: [{ key: "add" }] }] } } }, character_interaction_category: { sampled: 15, keys: [{ key: "index", placeholder: "0" }, { key: "desc" }, { key: "color", block: [] }] }, character_memory_type: { sampled: 382, keys: [{ key: "categories", block: [] }, { key: "icon" }, { key: "description", block: [] }, { key: "second_perspective_description", block: [] }, { key: "third_perspective_description", block: [] }, { key: "participants", block: [] }, { key: "duration", block: [{ key: "years", choices: ["memory_default_duration", "100", "20"] }] }], blocks: { duration: { sampled: 382, keys: [{ key: "years", choices: ["memory_default_duration", "100", "20"] }] } } }, chronicle: { sampled: 33, keys: [{ key: "name", block: [{ key: "first_valid", block: [] }] }, { key: "description", block: [{ key: "first_valid", block: [] }] }, { key: "portrait_animation" }, { key: "properties", block: [] }, { key: "chapters", block: [{ key: "opening" }, { key: "motivation" }, { key: "famous_deed" }, { key: "extra_deed" }, { key: "conclusion" }] }], blocks: { chapters: { sampled: 33, keys: [{ key: "opening" }, { key: "motivation" }, { key: "famous_deed" }, { key: "extra_deed" }, { key: "conclusion" }] }, description: { sampled: 21, keys: [{ key: "first_valid", block: [{ key: "triggered_desc", block: [] }, { key: "desc" }] }] }, name: { sampled: 21, keys: [{ key: "first_valid", block: [{ key: "triggered_desc", block: [] }, { key: "desc" }] }] } } }, coat_of_arms: { sampled: 2995, keys: [{ key: "pattern" }, { key: "color1" }, { key: "color2" }, { key: "colored_emblem", block: [{ key: "texture" }, { key: "color1" }, { key: "color2" }, { key: "instance", block: [] }] }], blocks: { colored_emblem: { sampled: 4823, keys: [{ key: "texture" }, { key: "color1" }, { key: "color2" }, { key: "instance", block: [{ key: "position", block: [] }, { key: "scale", block: [] }] }] } } }, combat_effect: { sampled: 31, keys: [{ key: "advantage", placeholder: "-10" }, { key: "image", choices: ["owner_in_debt"] }] }, combat_phase_event: { sampled: 13, keys: [{ key: "type", choices: ["knight", "commander"] }, { key: "is_valid", block: [] }, { key: "chance", block: [{ key: "base", placeholder: "30" }, { key: "modifier", block: [] }] }, { key: "effect", block: [{ key: "save_scope_as", choices: ["knight", "commander", "acclaimed_knight"] }, { key: "if", block: [] }, { key: "else", block: [] }] }], blocks: { chance: { sampled: 13, keys: [{ key: "base", placeholder: "30" }, { key: "modifier", block: [{ key: "factor" }] }] }, effect: { sampled: 11, keys: [{ key: "save_scope_as", choices: ["knight", "commander", "acclaimed_knight"] }, { key: "if", block: [{ key: "limit", block: [] }] }, { key: "else", block: [] }] } } }, council_position: { sampled: 15, keys: [{ key: "skill", choices: ["diplomacy", "learning", "martial", "stewardship", "intrigue"] }, { key: "name", choices: ["councillor_kurultai", "councillor_vizier", "minister_grand_marshal", "minister_justice", "minister_personnel", "minister_works"] }, { key: "tooltip", choices: ["game_concept_kurultai_desc", "councillor_vizier_desc", "game_concept_minister_grand_marshal_desc", "game_concept_minister_of_justice_desc", "game_concept_minister_of_personnel_desc", "game_concept_minister_of_works_desc", "game_concept_spouse_desc"] }, { key: "auto_fill", block: [] }, { key: "valid_position", block: [] }, { key: "modifier", block: [{ key: "name" }, { key: "scale", choices: ["council_scaled_by_liege_tier", "council_scaled_monthly_income", "council_scaled_admin_value", "council_scaled_by_liege_tier_nomad", "council_scaled_by_liege_tier_not_nomad", "council_scaled_celestial_ministry_value", "council_scaled_by_liege_tier_not_admin", "council_scaled_celestial_ministry_favored_value"] }] }, { key: "council_owner_modifier", block: [{ key: "name", choices: ["learn_on_the_job_modifier", "reliable_house_modifier"] }, { key: "scale" }] }, { key: "valid_character", block: [{ key: "exists", choices: ["root.liege_or_court_owner", "liege", "liege_or_court_owner"] }] }, { key: "on_get_position", block: [{ key: "got_council_position_effect", choices: ["yes"] }, { key: "got_minister_position_effect", block: [] }] }, { key: "on_fired_from_position", block: [{ key: "fired_from_council_position_effect", choices: ["yes"] }] }, { key: "on_lose_position", block: [{ key: "fired_minister_position_effect", choices: ["yes"] }] }, { key: "portrait_animation" }, { key: "barbershop_data", block: [{ key: "position", block: [] }, { key: "click_to_front", choices: ["no", "yes"] }] }], blocks: { barbershop_data: { sampled: 15, keys: [{ key: "position", block: [] }, { key: "click_to_front", choices: ["no", "yes"] }] }, council_owner_modifier: { sampled: 34, keys: [{ key: "name", choices: ["learn_on_the_job_modifier", "reliable_house_modifier"] }, { key: "scale" }] }, modifier: { sampled: 45, keys: [{ key: "name" }, { key: "scale", choices: ["council_scaled_by_liege_tier", "council_scaled_monthly_income", "council_scaled_admin_value", "council_scaled_by_liege_tier_nomad", "council_scaled_by_liege_tier_not_nomad", "council_scaled_celestial_ministry_value", "council_scaled_by_liege_tier_not_admin", "council_scaled_celestial_ministry_favored_value"] }] }, on_fired_from_position: { sampled: 15, keys: [{ key: "fired_from_council_position_effect", choices: ["yes"] }] }, on_get_position: { sampled: 15, keys: [{ key: "got_council_position_effect", choices: ["yes"] }, { key: "got_minister_position_effect", block: [{ key: "MINISTER_TITLE" }, { key: "MINISTER_POSITION" }] }] }, on_lose_position: { sampled: 11, keys: [{ key: "fired_minister_position_effect", choices: ["yes"] }] }, valid_character: { sampled: 15, keys: [{ key: "exists", choices: ["root.liege_or_court_owner", "liege", "liege_or_court_owner"] }] } } }, council_task: { sampled: 72, keys: [{ key: "position" }, { key: "task_type", choices: ["task_type_general", "task_type_county", "task_type_court"] }, { key: "task_progress", choices: ["task_progress_infinite", "task_progress_value", "task_progress_percentage"] }, { key: "asset", block: [{ key: "icon" }, { key: "background" }, { key: "frame" }, { key: "glow" }] }, { key: "effect_desc", block: [{ key: "desc" }, { key: "triggered_desc", block: [] }] }, { key: "council_owner_modifier", block: [{ key: "name" }, { key: "scale" }] }, { key: "is_valid_showing_failures_only", block: [{ key: "tgp_natural_disaster_councillor_available_for_task_trigger", choices: ["yes"] }] }, { key: "monthly_on_action" }, { key: "ai_will_do", block: [{ key: "value", choices: ["0", "1", "500", "1000", "100", "250", "10", "2"] }, { key: "if", block: [] }] }], blocks: { ai_will_do: { sampled: 67, keys: [{ key: "value", choices: ["0", "1", "500", "1000", "100", "250", "10", "2"] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "add" }] }] }, asset: { sampled: 56, keys: [{ key: "icon" }, { key: "background" }, { key: "frame" }, { key: "glow" }] }, council_owner_modifier: { sampled: 84, keys: [{ key: "name" }, { key: "scale" }] }, effect_desc: { sampled: 55, keys: [{ key: "desc" }, { key: "triggered_desc", block: [{ key: "trigger", block: [] }, { key: "desc" }] }] }, is_valid_showing_failures_only: { sampled: 40, keys: [{ key: "tgp_natural_disaster_councillor_available_for_task_trigger", choices: ["yes"] }] } } }, court_position: { sampled: 81, keys: [{ key: "sort_order", placeholder: "1200" }, { key: "max_available_positions", choices: ["1", "2", "3"] }, { key: "skill", choices: ["learning", "diplomacy", "martial", "stewardship", "prowess", "intrigue"] }, { key: "court_position_asset", block: [{ key: "trigger", block: [] }, { key: "animation" }, { key: "background" }] }, { key: "opinion", block: [{ key: "value", choices: ["regular_court_position_opinion", "regular_camp_officer_opinion", "high_camp_officer_opinion", "ceremonial_court_position_opinion", "10", "-5", "20", "court_jester_position_opinion"] }] }, { key: "aptitude_level_breakpoints", block: [] }, { key: "aptitude", block: [{ key: "value", choices: ["1", "0", "25", "10", "15", "40"] }, { key: "add", choices: ["general_aptitude_bonus", "court_position_aptitude_high_penalty_value", "court_position_aptitude_low_penalty_value", "general_aptitude_bonus_exclude_other_court_position", "aptitude_commander_trait_value", "kurultai_task_value"] }, { key: "if", block: [] }] }, { key: "is_shown", block: [] }, { key: "valid_position", block: [] }, { key: "is_shown_character", block: [] }, { key: "valid_character", block: [] }, { key: "revoke_cost", block: [{ key: "prestige", block: [] }] }, { key: "salary", block: [{ key: "gold", block: [] }, { key: "treasury", block: [] }, { key: "prestige", block: [] }, { key: "round", choices: ["no"] }] }, { key: "scaling_employer_modifiers", block: [{ key: "terrible", block: [] }, { key: "poor", block: [] }, { key: "average", block: [] }, { key: "good", block: [] }, { key: "excellent", block: [] }] }, { key: "received_salary", block: [{ key: "gold", block: [] }, { key: "prestige", block: [] }, { key: "round", choices: ["no"] }] }, { key: "modifier", block: [] }, { key: "on_court_position_received", block: [] }, { key: "on_court_position_revoked", block: [] }, { key: "on_court_position_invalidated", block: [] }, { key: "on_court_position_vacated", block: [{ key: "court_position_vacated_event_effect", block: [] }] }, { key: "ai_candidate_score", block: [{ key: "value" }, { key: "add", choices: ["court_position_candidate_score_base_value", "court_position_candidate_aptitude_value", "scope:base_value", "adjust_court_position_score_for_unity", "court_position_debt_considerations_value"] }, { key: "if", block: [] }] }, { key: "ai_position_score", block: [{ key: "value" }, { key: "add", choices: ["court_position_debt_considerations_value", "court_position_seneschal_debt_considerations_value"] }] }], blocks: { ai_candidate_score: { sampled: 81, keys: [{ key: "value" }, { key: "add", choices: ["court_position_candidate_score_base_value", "court_position_candidate_aptitude_value", "scope:base_value", "adjust_court_position_score_for_unity", "court_position_debt_considerations_value"] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "add", choices: ["1000", "100", "200"] }] }] }, ai_position_score: { sampled: 73, keys: [{ key: "value" }, { key: "add", choices: ["court_position_debt_considerations_value", "court_position_seneschal_debt_considerations_value"] }] }, aptitude: { sampled: 81, keys: [{ key: "value", choices: ["1", "0", "25", "10", "15", "40"] }, { key: "add", choices: ["general_aptitude_bonus", "court_position_aptitude_high_penalty_value", "court_position_aptitude_low_penalty_value", "general_aptitude_bonus_exclude_other_court_position", "aptitude_commander_trait_value", "kurultai_task_value"] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "add", block: [] }] }] }, court_position_asset: { sampled: 164, keys: [{ key: "trigger", block: [] }, { key: "animation" }, { key: "background" }] }, on_court_position_vacated: { sampled: 71, keys: [{ key: "court_position_vacated_event_effect", block: [{ key: "COURT_POSITION_TYPE" }] }] }, opinion: { sampled: 81, keys: [{ key: "value", choices: ["regular_court_position_opinion", "regular_camp_officer_opinion", "high_camp_officer_opinion", "ceremonial_court_position_opinion", "10", "-5", "20", "court_jester_position_opinion"] }] }, received_salary: { sampled: 53, keys: [{ key: "gold", block: [{ key: "value" }] }, { key: "prestige", block: [{ key: "value" }] }, { key: "round", choices: ["no"] }] }, revoke_cost: { sampled: 81, keys: [{ key: "prestige", block: [{ key: "value", choices: ["major_court_position_prestige_revoke_cost", "minor_court_position_prestige_revoke_cost"] }, { key: "if", block: [] }] }] }, salary: { sampled: 81, keys: [{ key: "gold", block: [{ key: "value", choices: ["0"] }, { key: "if", block: [] }] }, { key: "treasury", block: [{ key: "value", choices: ["0"] }, { key: "if", block: [] }] }, { key: "prestige", block: [{ key: "value" }] }, { key: "round", choices: ["no"] }] }, scaling_employer_modifiers: { sampled: 78, keys: [{ key: "terrible", block: [] }, { key: "poor", block: [] }, { key: "average", block: [] }, { key: "good", block: [] }, { key: "excellent", block: [] }] } } }, court_position_task: { sampled: 85, keys: [{ key: "court_position_types", block: [] }, { key: "cost", block: [{ key: "round", choices: ["no"] }, { key: "gold", block: [] }, { key: "treasury", block: [] }] }, { key: "scaling_employer_modifiers", block: [{ key: "terrible", block: [] }, { key: "poor", block: [] }, { key: "average", block: [] }, { key: "good", block: [] }, { key: "excellent", block: [] }] }, { key: "on_start", block: [] }, { key: "ai_will_do", block: [{ key: "value" }, { key: "if", block: [] }] }], blocks: { ai_will_do: { sampled: 85, keys: [{ key: "value" }, { key: "if", block: [{ key: "limit", block: [] }, { key: "add" }] }] }, cost: { sampled: 83, keys: [{ key: "round", choices: ["no"] }, { key: "gold", block: [{ key: "value", choices: ["0"] }, { key: "if", block: [] }] }, { key: "treasury", block: [{ key: "value", choices: ["0"] }, { key: "if", block: [] }] }] }, scaling_employer_modifiers: { sampled: 55, keys: [{ key: "terrible", block: [] }, { key: "poor", block: [] }, { key: "average", block: [] }, { key: "good", block: [] }, { key: "excellent", block: [] }] } } }, court_scene_culture: { sampled: 10, keys: [{ key: "trigger", block: [{ key: "NOT", block: [] }, { key: "capital_county", block: [] }, { key: "trigger_if", block: [] }] }], blocks: { trigger: { sampled: 10, keys: [{ key: "NOT", block: [{ key: "government_has_flag", choices: ["government_is_nomadic"] }] }, { key: "capital_county", block: [{ key: "OR", block: [] }] }, { key: "trigger_if", block: [{ key: "limit", block: [] }, { key: "NOT", block: [] }] }] } } }, court_scene_role: { sampled: 41, keys: [{ key: "effect", block: [] }, { key: "scripted_animation", block: [{ key: "triggered_animation", block: [] }] }], blocks: { scripted_animation: { sampled: 35, keys: [{ key: "triggered_animation", block: [{ key: "trigger", block: [] }, { key: "animation", block: [] }] }] } } }, culture: { sampled: 244, keys: [{ key: "color", block: [] }, { key: "ethos", choices: ["ethos_communal", "ethos_bellicose", "ethos_stoic", "ethos_spiritual", "ethos_courtly", "ethos_egalitarian", "ethos_bureaucratic"] }, { key: "heritage" }, { key: "language" }, { key: "martial_custom", choices: ["martial_custom_male_only", "martial_custom_equal"] }, { key: "head_determination", choices: ["head_determination_domain", "head_determination_herd"] }, { key: "traditions", block: [] }, { key: "name_list" }, { key: "coa_gfx", block: [] }, { key: "building_gfx", block: [] }, { key: "clothing_gfx", block: [] }, { key: "unit_gfx", block: [] }, { key: "house_coa_frame" }, { key: "house_coa_mask_offset", block: [] }, { key: "house_coa_mask_scale", block: [] }, { key: "ethnicities", block: [{ key: "100" }] }], blocks: { ethnicities: { sampled: 244, keys: [{ key: "100" }] } } }, culture_creation_name: { sampled: 137, keys: [{ key: "trigger", block: [{ key: "OR", block: [] }, { key: "any_sub_realm_county", block: [] }] }], blocks: { trigger: { sampled: 137, keys: [{ key: "OR", block: [{ key: "capital_county", block: [] }] }, { key: "any_sub_realm_county", block: [{ key: "OR", block: [] }] }] } } }, culture_pillar: { sampled: 162, keys: [{ key: "type", choices: ["language", "heritage", "ethos", "martial_custom", "head_determination"] }, { key: "is_shown", block: [{ key: "language_is_shown_trigger", block: [] }] }, { key: "ai_will_do", block: [{ key: "value", choices: ["10", "1", "0"] }, { key: "if", block: [] }] }, { key: "color" }], blocks: { ai_will_do: { sampled: 101, keys: [{ key: "value", choices: ["10", "1", "0"] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "multiply", choices: ["10", "2", "0"] }] }] }, is_shown: { sampled: 150, keys: [{ key: "language_is_shown_trigger", block: [{ key: "LANGUAGE" }] }] } } }, culture_tradition: { sampled: 198, keys: [{ key: "category", choices: ["regional", "realm", "societal", "combat", "ritual"] }, { key: "layers", block: [{ key: "0", choices: ["martial", "diplo", "steward", "intrigue", "learning"] }, { key: "1", choices: ["mena", "mediterranean", "western", "indian", "asian", "steppe"] }, { key: "4" }] }, { key: "is_shown", block: [] }, { key: "can_pick", block: [] }, { key: "parameters", block: [] }, { key: "character_modifier", block: [] }, { key: "cost", block: [{ key: "prestige", block: [] }] }, { key: "ai_will_do", block: [{ key: "value", placeholder: "100" }, { key: "if", block: [] }] }], blocks: { ai_will_do: { sampled: 195, keys: [{ key: "value", placeholder: "100" }, { key: "if", block: [{ key: "limit", block: [] }, { key: "multiply" }] }] }, cost: { sampled: 197, keys: [{ key: "prestige", block: [{ key: "add", block: [] }, { key: "if", block: [] }, { key: "multiply", choices: ["tradition_replacement_cost_if_relevant"] }] }] }, layers: { sampled: 199, keys: [{ key: "0", choices: ["martial", "diplo", "steward", "intrigue", "learning"] }, { key: "1", choices: ["mena", "mediterranean", "western", "indian", "asian", "steppe"] }, { key: "4" }] } } }, customizable_localization: { sampled: 2651, keys: [{ key: "type" }, { key: "text", block: [{ key: "trigger", block: [] }, { key: "localization_key" }] }], blocks: { text: { sampled: 17486, keys: [{ key: "trigger", block: [] }, { key: "localization_key" }] } } }, death_reason: { sampled: 313, keys: [{ key: "public_knowledge", choices: ["yes"] }, { key: "icon", choices: ["death_murder.dds", "death_natural.dds", "death_disease.dds", "death_unknown.dds"] }] }, decision: { sampled: 431, keys: [{ key: "picture", block: [{ key: "reference" }] }, { key: "decision_group_type" }, { key: "desc" }, { key: "ai_check_interval_by_tier", block: [{ key: "barony", choices: ["0"] }, { key: "county", placeholder: "0" }, { key: "duchy", placeholder: "0" }, { key: "kingdom", placeholder: "120" }, { key: "empire", placeholder: "120" }, { key: "hegemony", placeholder: "0" }] }, { key: "selection_tooltip" }, { key: "is_shown", block: [] }, { key: "is_valid", block: [] }, { key: "is_valid_showing_failures_only", block: [] }, { key: "effect", block: [] }, { key: "cost", block: [{ key: "prestige" }] }, { key: "ai_potential", block: [] }, { key: "ai_will_do", block: [{ key: "base", placeholder: "100" }] }], blocks: { ai_check_interval_by_tier: { sampled: 353, keys: [{ key: "barony", choices: ["0"] }, { key: "county", placeholder: "0" }, { key: "duchy", placeholder: "0" }, { key: "kingdom", placeholder: "120" }, { key: "empire", placeholder: "120" }, { key: "hegemony", placeholder: "0" }] }, ai_will_do: { sampled: 393, keys: [{ key: "base", placeholder: "100" }] }, cost: { sampled: 264, keys: [{ key: "prestige" }] }, picture: { sampled: 526, keys: [{ key: "reference" }] } } }, decision_group_type: { sampled: 17, keys: [{ key: "sort_order", placeholder: "50" }, { key: "gui_tags", block: [] }] }, doctrine: { sampled: 183, keys: [{ key: "icon" }, { key: "piety_cost", block: [{ key: "value", choices: ["faith_doctrine_cost_mid", "faith_doctrine_cost_low", "faith_doctrine_cost_high", "faith_tenet_cost_low", "faith_tenet_cost_mid", "faith_doctrine_cost_massive", "faith_tenet_cost_high", "faith_tenet_cost_massive"] }, { key: "if", block: [] }, { key: "else_if", block: [] }] }, { key: "parameters", block: [] }], blocks: { piety_cost: { sampled: 157, keys: [{ key: "value", choices: ["faith_doctrine_cost_mid", "faith_doctrine_cost_low", "faith_doctrine_cost_high", "faith_tenet_cost_low", "faith_tenet_cost_mid", "faith_doctrine_cost_massive", "faith_tenet_cost_high", "faith_tenet_cost_massive"] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "multiply", choices: ["faith_unchanged_doctrine_cost_mult", "0", "1.5", "3", "faith_changed_doctrine_cost_mult_three_step", "faith_tenet_discount_mountain_background_value"] }] }, { key: "else_if", block: [{ key: "limit", block: [] }, { key: "value", choices: ["faith_tenet_cost_high", "faith_tenet_cost_low", "faith_tenet_cost_mid", "faith_tenet_cost_massive"] }] }] } } }, doctrine_group: { sampled: 44, keys: [{ key: "category", choices: ["special", "main_group", "clergy", "crimes", "marriage", "not_creatable", "core_tenets"] }, { key: "doctrine_types", block: [] }] }, domicile_building: { sampled: 1620, keys: [{ key: "construction_time" }, { key: "allowed_domicile_types", block: [] }, { key: "slot_type", choices: ["internal", "main"] }, { key: "previous_building" }, { key: "can_construct", block: [{ key: "domicile", block: [] }] }, { key: "cost", block: [{ key: "gold", block: [] }] }, { key: "character_modifier", block: [] }, { key: "parameters", block: [] }, { key: "ai_value", block: [{ key: "value" }, { key: "add", choices: ["estate_building_ai_modifier_value", "japanese_manor_building_ai_modifier_value"] }] }, { key: "asset", block: [{ key: "icon" }, { key: "texture" }, { key: "intersectionmask_texture" }, { key: "soundeffect" }] }], blocks: { ai_value: { sampled: 1620, keys: [{ key: "value" }, { key: "add", choices: ["estate_building_ai_modifier_value", "japanese_manor_building_ai_modifier_value"] }] }, asset: { sampled: 2153, keys: [{ key: "icon" }, { key: "texture" }, { key: "intersectionmask_texture" }, { key: "soundeffect" }] }, can_construct: { sampled: 1068, keys: [{ key: "domicile", block: [{ key: "has_domicile_building_or_higher" }] }] }, cost: { sampled: 1619, keys: [{ key: "gold", block: [{ key: "value" }] }] } } }, dynasty: { sampled: 10338, keys: [{ key: "name" }, { key: "culture" }] }, dynasty_house: { sampled: 558, keys: [{ key: "name" }, { key: "dynasty" }] }, dynasty_legacy: { sampled: 21, keys: [{ key: "is_shown", block: [{ key: "has_dlc_feature" }, { key: "OR", block: [] }] }], blocks: { is_shown: { sampled: 14, keys: [{ key: "has_dlc_feature" }, { key: "OR", block: [{ key: "has_game_rule", choices: ["unrestricted_dynasty_legacies_all"] }, { key: "AND", block: [] }, { key: "dynasty", block: [] }, { key: "government_has_flag", choices: ["government_has_merit", "government_is_administrative", "government_is_japan_administrative", "government_is_japan_feudal", "government_is_mandala", "government_is_nomadic", "government_is_wanua"] }] }] } } }, dynasty_perk: { sampled: 105, keys: [{ key: "legacy" }, { key: "can_be_picked", block: [] }, { key: "effect", block: [{ key: "custom_description_no_bullet", block: [] }] }, { key: "character_modifier", block: [] }], blocks: { effect: { sampled: 82, keys: [{ key: "custom_description_no_bullet", block: [{ key: "text" }] }] } } }, effect_localization: { sampled: 989, keys: [{ key: "global" }, { key: "first" }, { key: "third" }] }, event: { sampled: 9791, keys: [{ key: "type", choices: ["character_event", "activity_event", "letter_event", "court_event"] }, { key: "title" }, { key: "desc" }, { key: "theme" }, { key: "left_portrait", block: [{ key: "character" }, { key: "animation" }] }, { key: "right_portrait", block: [{ key: "character" }, { key: "animation" }] }, { key: "trigger", block: [] }, { key: "immediate", block: [] }, { key: "option", block: [{ key: "name" }, { key: "ai_chance", block: [] }] }], nameFromHeader: "namespace", blocks: { left_portrait: { sampled: 7009, keys: [{ key: "character" }, { key: "animation" }] }, option: { sampled: 21957, keys: [{ key: "name" }, { key: "ai_chance", block: [{ key: "base" }] }] }, right_portrait: { sampled: 5097, keys: [{ key: "character" }, { key: "animation" }] } } }, event_background: { sampled: 298, keys: [{ key: "background", block: [{ key: "trigger", block: [] }, { key: "reference" }, { key: "environment" }, { key: "ambience" }] }], blocks: { background: { sampled: 1253, keys: [{ key: "trigger", block: [] }, { key: "reference" }, { key: "environment" }, { key: "ambience" }] } } }, event_theme: { sampled: 191, keys: [{ key: "icon", block: [{ key: "reference" }] }, { key: "header_background", block: [{ key: "reference" }] }, { key: "sound", block: [{ key: "reference" }] }, { key: "background", block: [{ key: "reference" }] }], blocks: { background: { sampled: 239, keys: [{ key: "reference" }] }, header_background: { sampled: 142, keys: [{ key: "reference" }] }, icon: { sampled: 204, keys: [{ key: "reference" }] }, sound: { sampled: 197, keys: [{ key: "reference" }] } } }, event_transition: { sampled: 33, keys: [{ key: "transition", block: [{ key: "reference" }, { key: "duration", choices: ["7", "5"] }] }], blocks: { transition: { sampled: 33, keys: [{ key: "reference" }, { key: "duration", choices: ["7", "5"] }] } } }, faction: { sampled: 12, keys: [{ key: "casus_belli" }, { key: "short_effect_desc", choices: ["claimant_faction_short_effect_desc", "escalated_peasant_faction_short_effect_desc", "imperial_policy_faction_short_effect_desc", "independence_faction_short_effect_desc", "nation_fracturing_faction_short_effect_desc", "nomadic_faction_short_effect_desc", "peasant_faction_short_effect_desc", "restore_ceremonial_liege_faction_short_effect_desc"] }, { key: "sort_order", choices: ["4", "3", "1", "0", "2", "5"] }, { key: "name", choices: ["FACTION_CEREMONIAL_CLAIMANT_DYNAMIC_NAME", "FACTION_CLAMAINT_DYNAMIC_NAME", "FACTION_ESCALATED_PEASANT_DYNAMIC_NAME", "FACTION_JAPANESE_REPLACE_REGENT_NAME_DYNAMIC", "FACTION_JAPANESE_RESTORE_EMPEROR_NAME_DYNAMIC", "FACTION_NOMAD_DYNAMIC_NAME", "FACTION_PEASANT_NAME", "FACTION_POPULIST_DYNAMIC_NAME"] }, { key: "discontent_progress", block: [{ key: "base" }, { key: "common_discontent_progress_modifier", choices: ["yes"] }] }, { key: "is_shown", block: [{ key: "NAND", block: [] }] }, { key: "power_threshold", block: [{ key: "base", choices: ["80"] }, { key: "modifier", block: [] }, { key: "dynamic_power_threshold_scripted_modifier", block: [] }] }, { key: "demand", block: [{ key: "save_scope_as", choices: ["faction"] }, { key: "faction_leader", block: [] }, { key: "faction_target", block: [] }, { key: "every_faction_member", block: [] }] }, { key: "is_valid", block: [{ key: "trigger_if", block: [] }] }, { key: "ai_demand_chance", block: [{ key: "base", choices: ["0", "100"] }, { key: "modifier", block: [] }, { key: "compare_modifier", block: [] }] }, { key: "is_character_valid", block: [{ key: "common_character_validity_trigger", block: [] }, { key: "has_valid_faction_members_trigger", choices: ["yes"] }] }, { key: "can_character_create_ui", block: [{ key: "base_faction_trigger", block: [] }, { key: "common_can_character_create_trigger", block: [] }, { key: "japan_faction_cohesion_trigger", choices: ["yes"] }] }, { key: "on_war_start", block: [{ key: "every_faction_member", block: [] }] }, { key: "is_county_valid", block: [{ key: "title_province", block: [] }] }, { key: "can_character_join", block: [{ key: "common_can_character_join_trigger", block: [] }] }, { key: "can_county_join", block: [{ key: "title_province", block: [] }] }, { key: "can_character_become_leader", block: [{ key: "can_become_leader_of_faction_trigger", choices: ["yes"] }] }, { key: "can_character_create", block: [{ key: "common_can_character_create_trigger", block: [] }, { key: "trigger_if", block: [] }] }, { key: "county_join_score", block: [{ key: "base", choices: ["0", "-80", "-120"] }, { key: "compare_modifier", block: [] }, { key: "modifier", block: [] }, { key: "legalism_virtue_and_sin_modifier", block: [] }] }, { key: "ai_join_score", block: [{ key: "base", choices: ["0", "-25", "-150"] }, { key: "common_join_faction_blockers", block: [] }, { key: "common_faction_modifiers", block: [] }, { key: "struggle_faction_modifiers", choices: ["yes"] }, { key: "modifier", block: [] }] }, { key: "ai_create_score", block: [{ key: "base", choices: ["0", "-25", "-10000", "-175", "-200"] }, { key: "common_create_faction_blockers", block: [] }, { key: "common_faction_modifiers", block: [] }, { key: "modifier", block: [] }] }, { key: "county_allow_join", choices: ["no", "yes"] }, { key: "county_power", choices: ["county_levies_to_raise", "county_horde_riders_to_raise_power"] }, { key: "on_creation", block: [{ key: "save_scope_as", choices: ["faction"] }, { key: "tgp_bloc_members_join_faction_effect", choices: ["yes"] }, { key: "if", block: [] }] }, { key: "county_allow_create", choices: ["no"] }, { key: "character_allow_create", choices: ["no", "yes"] }, { key: "special_character_title", choices: ["FACTIONS_WINDOW_LEADER", "FACTIONS_WINDOW_CLAIMANT", "FACTIONS_WINDOW_JAPANESE_REGENT", "FACTIONS_WINDOW_JAPANESE_RESTORE_EMPEROR"] }, { key: "multiple_targeting", choices: ["yes", "no"] }], blocks: { ai_demand_chance: { sampled: 12, keys: [{ key: "base", choices: ["0", "100"] }, { key: "modifier", block: [{ key: "add", choices: ["-1000", "-75", "100", "-50", "-500"] }, { key: "faction_target", block: [] }] }, { key: "compare_modifier", block: [{ key: "value", choices: ["faction_power"] }, { key: "trigger", block: [] }, { key: "multiplier", choices: ["0.375", "0.5", "0.75", "1"] }] }] }, ai_join_score: { sampled: 12, keys: [{ key: "base", choices: ["0", "-25", "-150"] }, { key: "common_join_faction_blockers", block: [{ key: "FACTION_TARGET", choices: ["scope:faction.faction_target"] }] }, { key: "common_faction_modifiers", block: [{ key: "FACTION_TARGET", choices: ["scope:faction.faction_target"] }, { key: "OPINION_MULTIPLIER", choices: ["-1", "-0.4", "-2"] }, { key: "MAX_OPINION", choices: ["0", "100"] }, { key: "POWER", choices: ["scope:faction.faction_power"] }, { key: "THRESHOLD", choices: ["scope:faction.faction_power_threshold"] }] }, { key: "struggle_faction_modifiers", choices: ["yes"] }, { key: "modifier", block: [{ key: "desc" }, { key: "add", placeholder: "25" }] }] }, can_character_become_leader: { sampled: 12, keys: [{ key: "can_become_leader_of_faction_trigger", choices: ["yes"] }] }, can_character_join: { sampled: 12, keys: [{ key: "common_can_character_join_trigger", block: [{ key: "FACTION_TARGET", choices: ["scope:faction.faction_target"] }] }] }, demand: { sampled: 12, keys: [{ key: "save_scope_as", choices: ["faction"] }, { key: "faction_leader", block: [{ key: "save_scope_as", choices: ["faction_leader"] }] }, { key: "faction_target", block: [{ key: "save_scope_as", choices: ["faction_target"] }, { key: "trigger_event", block: [] }] }, { key: "every_faction_member", block: [{ key: "limit", block: [] }, { key: "trigger_event", choices: ["faction_demand.0005", "faction_demand.0105", "faction_demand.0225", "faction_demand.1005", "faction_demand.2005", "faction_demand.2015", "tgp_faction_events.0105", "tgp_faction_events.0205"] }] }] }, discontent_progress: { sampled: 12, keys: [{ key: "base" }, { key: "common_discontent_progress_modifier", choices: ["yes"] }] }, is_character_valid: { sampled: 11, keys: [{ key: "common_character_validity_trigger", block: [{ key: "FACTION_TARGET", choices: ["scope:faction.faction_target"] }] }, { key: "has_valid_faction_members_trigger", choices: ["yes"] }] }, on_creation: { sampled: 12, keys: [{ key: "save_scope_as", choices: ["faction"] }, { key: "tgp_bloc_members_join_faction_effect", choices: ["yes"] }, { key: "if", block: [{ key: "limit", block: [] }] }] }, on_war_start: { sampled: 10, keys: [{ key: "every_faction_member", block: [{ key: "limit", block: [] }, { key: "spawn_army", block: [] }] }] } } }, flavorization: { sampled: 1274, keys: [{ key: "type", choices: ["character", "title", "domicile"] }, { key: "gender", choices: ["male", "female"] }, { key: "special", choices: ["holder", "ruler_child", "domicile", "queen_mother", "head_of_faith", "councillor"] }, { key: "tier", choices: ["duchy", "kingdom", "county", "empire", "barony", "hegemony"] }, { key: "priority", placeholder: "47" }, { key: "governments", block: [] }, { key: "flavourization_rules", block: [{ key: "top_liege", choices: ["no", "yes"] }] }], blocks: { flavourization_rules: { sampled: 1057, keys: [{ key: "top_liege", choices: ["no", "yes"] }] } } }, focus: { sampled: 28, keys: [{ key: "lifestyle", choices: ["diplomacy_lifestyle", "intrigue_lifestyle", "learning_lifestyle", "martial_lifestyle", "stewardship_lifestyle", "wanderer_lifestyle"] }, { key: "is_shown", block: [{ key: "NOT", block: [] }] }, { key: "is_valid", block: [{ key: "NOT", block: [] }] }, { key: "desc", block: [{ key: "desc" }] }, { key: "modifier", block: [] }, { key: "auto_selection_weight", block: [{ key: "value", choices: ["11", "100", "5000", "0"] }, { key: "if", block: [] }] }], blocks: { auto_selection_weight: { sampled: 28, keys: [{ key: "value", choices: ["11", "100", "5000", "0"] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "multiply", choices: ["0", "2", "5", "0.1", "0.2", "1.5", "3", "7"] }] }] }, desc: { sampled: 28, keys: [{ key: "desc" }] }, is_shown: { sampled: 21, keys: [{ key: "NOT", block: [{ key: "government_has_flag", choices: ["government_is_landless_adventurer"] }] }] }, is_valid: { sampled: 21, keys: [{ key: "NOT", block: [{ key: "government_has_flag", choices: ["government_is_landless_adventurer"] }] }] } } }, game_concept: { sampled: 1047, keys: [{ key: "texture" }, { key: "alias", block: [] }, { key: "parent" }] }, game_rule: { sampled: 81, keys: [{ key: "categories", block: [] }, { key: "default" }] }, government: { sampled: 18, keys: [{ key: "government_rules", block: [{ key: "create_cadet_branches", choices: ["yes", "no"] }, { key: "rulers_should_have_dynasty", choices: ["yes", "no"] }, { key: "dynasty_named_realms", choices: ["no", "yes"] }, { key: "legitimacy", choices: ["yes"] }, { key: "use_as_base_on_landed", choices: ["yes"] }, { key: "inherit_from_dynastic_government", choices: ["no"] }, { key: "allow_accolades", choices: ["yes", "no"] }] }, { key: "royal_court", choices: ["any"] }, { key: "primary_holding", choices: ["castle_holding", "tribal_holding", "church_holding", "city_holding", "herder_holding", "nomad_holding", "temple_citadel_holding"] }, { key: "valid_holdings", block: [] }, { key: "required_county_holdings", block: [] }, { key: "ai", block: [{ key: "use_legends", choices: ["no", "yes"] }] }, { key: "vassal_contract_group" }, { key: "can_get_government", block: [{ key: "has_tgp_dlc_trigger", choices: ["yes"] }] }, { key: "mechanic_type" }, { key: "character_modifier", block: [{ key: "knight_limit", choices: ["-5", "2", "-100"] }, { key: "men_at_arms_limit", choices: ["-2"] }, { key: "vassal_limit", choices: ["20", "50", "100"] }, { key: "title_creation_cost_mult", choices: ["-0.5"] }] }, { key: "is_mechanic_type_default", choices: ["yes"] }, { key: "flags", block: [] }, { key: "color", block: [] }, { key: "realm_mask_offset", block: [] }, { key: "realm_mask_scale", block: [] }], blocks: { ai: { sampled: 11, keys: [{ key: "use_legends", choices: ["no", "yes"] }] }, can_get_government: { sampled: 10, keys: [{ key: "has_tgp_dlc_trigger", choices: ["yes"] }] }, character_modifier: { sampled: 12, keys: [{ key: "knight_limit", choices: ["-5", "2", "-100"] }, { key: "men_at_arms_limit", choices: ["-2"] }, { key: "vassal_limit", choices: ["20", "50", "100"] }, { key: "title_creation_cost_mult", choices: ["-0.5"] }] }, government_rules: { sampled: 18, keys: [{ key: "create_cadet_branches", choices: ["yes", "no"] }, { key: "rulers_should_have_dynasty", choices: ["yes", "no"] }, { key: "dynasty_named_realms", choices: ["no", "yes"] }, { key: "legitimacy", choices: ["yes"] }, { key: "use_as_base_on_landed", choices: ["yes"] }, { key: "inherit_from_dynastic_government", choices: ["no"] }, { key: "allow_accolades", choices: ["yes", "no"] }] } } }, great_project: { sampled: 20, keys: [{ key: "icon", block: [{ key: "reference" }] }, { key: "illustration", block: [{ key: "reference" }] }, { key: "name", block: [{ key: "desc" }] }, { key: "is_shown", block: [{ key: "has_tgp_dlc_trigger", choices: ["yes"] }, { key: "OR", block: [] }, { key: "NOT", block: [] }] }, { key: "can_start_planning", block: [{ key: "trigger_if", block: [] }] }, { key: "is_valid", block: [] }, { key: "cost", block: [{ key: "treasury_or_gold", block: [] }] }, { key: "construction_time", block: [{ key: "value", choices: ["great_project_base_construction_time", "1825", "3650"] }, { key: "multiply", choices: ["common_great_project_construction_time_multiplier_value", "7", "5"] }] }, { key: "contributor_cooldown", choices: ["standard_contributor_cooldown_value", "0"] }, { key: "allowed_contributor_filter", block: [{ key: "owner", choices: ["yes"] }, { key: "vassals", choices: ["yes"] }, { key: "tributaries", choices: ["yes"] }] }, { key: "on_complete", block: [] }, { key: "on_plan_build", block: [] }, { key: "on_start_build", block: [] }, { key: "on_cancel", block: [{ key: "every_contribution", block: [] }] }, { key: "project_contributions", block: [] }, { key: "ai_target_quick_trigger", block: [{ key: "adult", choices: ["yes"] }, { key: "rank", choices: ["empire", "county"] }] }, { key: "ai_check_interval_by_tier", block: [{ key: "barony", choices: ["0"] }, { key: "county", choices: ["0", "1"] }, { key: "duchy", choices: ["0", "1"] }, { key: "kingdom", choices: ["0", "1"] }, { key: "empire", choices: ["1", "10", "6", "0", "24", "5"] }, { key: "hegemony", choices: ["6", "1", "24", "3"] }] }, { key: "ai_will_do", block: [{ key: "value", choices: ["1000", "500", "30", "10", "100", "15"] }, { key: "if", block: [] }] }, { key: "completion_sound_effect" }], blocks: { ai_check_interval_by_tier: { sampled: 18, keys: [{ key: "barony", choices: ["0"] }, { key: "county", choices: ["0", "1"] }, { key: "duchy", choices: ["0", "1"] }, { key: "kingdom", choices: ["0", "1"] }, { key: "empire", choices: ["1", "10", "6", "0", "24", "5"] }, { key: "hegemony", choices: ["6", "1", "24", "3"] }] }, ai_target_quick_trigger: { sampled: 18, keys: [{ key: "adult", choices: ["yes"] }, { key: "rank", choices: ["empire", "county"] }] }, ai_will_do: { sampled: 18, keys: [{ key: "value", choices: ["1000", "500", "30", "10", "100", "15"] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "add", placeholder: "10" }] }] }, allowed_contributor_filter: { sampled: 20, keys: [{ key: "owner", choices: ["yes"] }, { key: "vassals", choices: ["yes"] }, { key: "tributaries", choices: ["yes"] }] }, can_start_planning: { sampled: 20, keys: [{ key: "trigger_if", block: [{ key: "limit", block: [] }] }] }, construction_time: { sampled: 13, keys: [{ key: "value", choices: ["great_project_base_construction_time", "1825", "3650"] }, { key: "multiply", choices: ["common_great_project_construction_time_multiplier_value", "7", "5"] }] }, cost: { sampled: 18, keys: [{ key: "treasury_or_gold", block: [{ key: "value", choices: ["great_project_base_starting_cost", "great_project_base_contribution_cost"] }, { key: "multiply", choices: ["2", "0.2", "0.3", "1.5", "4"] }, { key: "if", block: [] }] }] }, icon: { sampled: 22, keys: [{ key: "reference" }] }, illustration: { sampled: 25, keys: [{ key: "reference" }] }, is_shown: { sampled: 20, keys: [{ key: "has_tgp_dlc_trigger", choices: ["yes"] }, { key: "OR", block: [{ key: "has_title", choices: ["title:h_china", "title:e_minister_of_works", "title:e_minister_grand_marshal", "title:e_minister_of_rites", "title:e_minister_of_war", "title:e_minister_of_justice", "title:e_minister_of_revenue"] }, { key: "AND", block: [] }] }, { key: "NOT", block: [{ key: "is_planning_great_project" }] }] }, name: { sampled: 18, keys: [{ key: "desc" }] }, on_cancel: { sampled: 11, keys: [{ key: "every_contribution", block: [{ key: "limit", block: [] }, { key: "contributor", block: [] }] }] } } }, guest_invite_rule: { sampled: 56, keys: [{ key: "effect", block: [] }] }, holy_site: { sampled: 326, keys: [{ key: "county" }, { key: "character_modifier", block: [{ key: "name" }] }], blocks: { character_modifier: { sampled: 321, keys: [{ key: "name" }] } } }, hook_type: { sampled: 44, keys: [{ key: "expiration_days", choices: ["3650", "7300", "-1", "1825", "7200"] }] }, house_aspiration: { sampled: 29, keys: [{ key: "show_in_main_hud", choices: ["yes"] }, { key: "is_shown", block: [{ key: "house_head", block: [] }] }, { key: "level", block: [{ key: "cost", block: [] }, { key: "powerful_family_member_modifier", block: [] }, { key: "any_house_member_modifier", block: [] }, { key: "parameters", block: [] }, { key: "ai_score", block: [] }] }, { key: "cooldown", block: [{ key: "years" }] }, { key: "illustration" }], blocks: { cooldown: { sampled: 28, keys: [{ key: "years" }] }, is_shown: { sampled: 29, keys: [{ key: "house_head", block: [{ key: "government_has_flag", choices: ["government_has_powerful_families", "government_is_mandala", "government_uses_merit_family_aspirations"] }] }] }, level: { sampled: 99, keys: [{ key: "cost", block: [{ key: "influence" }] }, { key: "powerful_family_member_modifier", block: [] }, { key: "any_house_member_modifier", block: [] }, { key: "parameters", block: [] }, { key: "ai_score", block: [{ key: "value" }] }] } } }, house_motto: { sampled: 74, keys: [{ key: "insert" }, { key: "weight", block: [{ key: "value", placeholder: "1500" }] }, { key: "trigger", block: [] }], blocks: { weight: { sampled: 74, keys: [{ key: "value", placeholder: "1500" }] } } }, house_motto_insert: { sampled: 23, keys: [] }, important_action: { sampled: 269, keys: [{ key: "check_create_action", block: [{ key: "if", block: [] }] }, { key: "effect", block: [] }], blocks: { check_create_action: { sampled: 269, keys: [{ key: "if", block: [{ key: "limit", block: [] }, { key: "try_create_important_action", block: [] }] }] } } }, innovation: { sampled: 108, keys: [{ key: "skill", choices: ["stewardship", "martial", "learning", "diplomacy", "intrigue"] }, { key: "group", choices: ["culture_group_civic", "culture_group_military"] }, { key: "culture_era", choices: ["culture_era_tribal", "culture_era_late_medieval", "culture_era_early_medieval", "culture_era_high_medieval"] }, { key: "icon" }, { key: "character_modifier", block: [] }, { key: "flag" }, { key: "ai_weight_for_fascination", block: [{ key: "value", choices: ["100"] }, { key: "if", block: [] }] }], blocks: { ai_weight_for_fascination: { sampled: 108, keys: [{ key: "value", choices: ["100"] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "multiply", choices: ["0"] }] }] } } }, inspiration: { sampled: 11, keys: [{ key: "gold", block: [{ key: "value", choices: ["basic_fund_inspiration_cost"] }, { key: "if", block: [] }, { key: "round", choices: ["yes"] }] }, { key: "progress_chance", choices: ["75", "100", "adventure_progress_chance_svalue"] }, { key: "on_creation", block: [{ key: "hidden_effect", block: [] }] }, { key: "is_valid", block: [] }, { key: "is_sponsor_valid", block: [{ key: "ep1_is_sponsor_valid_inspiration_basic", choices: ["yes"] }] }, { key: "can_sponsor", block: [{ key: "ep1_can_sponsor_inspiration_basic", choices: ["yes"] }] }, { key: "on_monthly", block: [] }, { key: "on_complete", block: [] }, { key: "on_sponsor", block: [] }, { key: "on_owner_death", block: [{ key: "save_scope_value_as", block: [] }, { key: "if", block: [] }] }, { key: "on_sponsor_invalidated", block: [{ key: "save_scope_value_as", block: [] }, { key: "if", block: [] }] }], blocks: { on_owner_death: { sampled: 11, keys: [{ key: "save_scope_value_as", block: [{ key: "name" }, { key: "value", choices: ["yes"] }] }, { key: "if", block: [{ key: "limit", block: [] }] }] }, on_sponsor_invalidated: { sampled: 11, keys: [{ key: "save_scope_value_as", block: [{ key: "name" }, { key: "value", choices: ["yes"] }] }, { key: "if", block: [{ key: "limit", block: [] }] }] } } }, law_group: { sampled: 27, keys: [{ key: "default" }, { key: "cumulative", choices: ["no", "yes"] }, { key: "flag" }] }, legend_seed: { sampled: 38, keys: [{ key: "type", choices: ["legitimizing", "heroic", "holy"] }, { key: "quality", choices: ["famed"] }, { key: "is_shown", block: [{ key: "has_dlc_feature", choices: ["legends"] }, { key: "NOT", block: [] }] }, { key: "is_valid", block: [{ key: "is_playable_character", choices: ["yes"] }, { key: "prestige_level", choices: ["high_prestige_level", "medium_prestige_level", "very_high_prestige_level"] }] }, { key: "chronicle" }, { key: "chronicle_properties", block: [{ key: "title" }] }], blocks: { chronicle_properties: { sampled: 38, keys: [{ key: "title" }] }, is_shown: { sampled: 38, keys: [{ key: "has_dlc_feature", choices: ["legends"] }, { key: "NOT", block: [{ key: "has_global_variable" }] }] }, is_valid: { sampled: 38, keys: [{ key: "is_playable_character", choices: ["yes"] }, { key: "prestige_level", choices: ["high_prestige_level", "medium_prestige_level", "very_high_prestige_level"] }] } } }, men_at_arms: { sampled: 110, keys: [{ key: "type" }, { key: "damage", placeholder: "30" }, { key: "toughness", placeholder: "12" }, { key: "pursuit", placeholder: "0" }, { key: "screen", placeholder: "0" }, { key: "terrain_bonus", block: [{ key: "hills", block: [] }, { key: "mountains", block: [] }, { key: "desert_mountains", block: [] }] }, { key: "counters", block: [] }, { key: "can_recruit", block: [] }, { key: "buy_cost", block: [{ key: "gold" }] }, { key: "low_maintenance_cost", block: [{ key: "gold" }] }, { key: "high_maintenance_cost", block: [{ key: "gold" }] }, { key: "provision_cost" }, { key: "stack", choices: ["100", "50", "10", "25"] }, { key: "ai_quality", block: [{ key: "value" }] }, { key: "icon" }], blocks: { ai_quality: { sampled: 100, keys: [{ key: "value" }] }, buy_cost: { sampled: 109, keys: [{ key: "gold" }] }, high_maintenance_cost: { sampled: 109, keys: [{ key: "gold" }] }, low_maintenance_cost: { sampled: 109, keys: [{ key: "gold" }] }, terrain_bonus: { sampled: 90, keys: [{ key: "hills", block: [{ key: "damage", placeholder: "10" }, { key: "toughness", choices: ["4", "6", "8", "10", "5", "16", "20", "3"] }] }, { key: "mountains", block: [{ key: "damage", placeholder: "-50" }] }, { key: "desert_mountains", block: [{ key: "damage", placeholder: "-30" }] }] } } }, message: { sampled: 682, keys: [{ key: "icon" }, { key: "title" }, { key: "desc" }, { key: "style", choices: ["bad", "good", "neutral"] }, { key: "message_filter_type" }] }, message_filter_type: { sampled: 180, keys: [{ key: "display", choices: ["feed", "toast", "hidden", "popup"] }, { key: "group" }] }, message_group_type: { sampled: 31, keys: [{ key: "sort_order", placeholder: "0" }] }, modifier_definition_format: { sampled: 1952, keys: [{ key: "decimals", choices: ["0", "2", "1", "3"] }, { key: "prefix" }] }, name_equivalency: { sampled: 1433, keys: [] }, name_list: { sampled: 218, keys: [{ key: "cadet_dynasty_names", block: [] }, { key: "dynasty_names", block: [] }, { key: "male_names", block: [] }, { key: "female_names", block: [] }, { key: "dynasty_of_location_prefix" }] }, nickname: { sampled: 683, keys: [] }, on_action: { sampled: 880, keys: [] }, opinion_modifier: { sampled: 1532, keys: [{ key: "opinion", placeholder: "-20" }, { key: "decaying", choices: ["yes", "no"] }, { key: "years", placeholder: "10" }] }, perk: { sampled: 162, keys: [{ key: "lifestyle", choices: ["diplomacy_lifestyle", "intrigue_lifestyle", "learning_lifestyle", "martial_lifestyle", "stewardship_lifestyle", "wanderer_lifestyle"] }, { key: "tree" }, { key: "position", block: [] }, { key: "icon" }, { key: "parent" }, { key: "effect", block: [{ key: "if", block: [] }] }], blocks: { effect: { sampled: 138, keys: [{ key: "if", block: [{ key: "limit", block: [] }, { key: "custom_description_no_bullet", block: [] }] }] } } }, playable_difficulty_info: { sampled: 14, keys: [{ key: "is_shown", block: [] }] }, point_of_interest: { sampled: 17, keys: [{ key: "build_province_list", block: [{ key: "every_special_building_province", block: [] }] }, { key: "on_visit", block: [{ key: "send_interface_toast", block: [] }, { key: "visiting_poi_effect", choices: ["yes"] }] }], blocks: { build_province_list: { sampled: 15, keys: [{ key: "every_special_building_province", block: [{ key: "limit", block: [] }, { key: "add_to_list", choices: ["provinces"] }] }] }, on_visit: { sampled: 17, keys: [{ key: "send_interface_toast", block: [{ key: "title" }, { key: "left_icon", choices: ["root"] }, { key: "if", block: [] }, { key: "wanderer_lifestyle_destination_effect", choices: ["yes"] }, { key: "traveler_travel_xp_effect", block: [] }] }, { key: "visiting_poi_effect", choices: ["yes"] }] } } }, portrait_animation: { sampled: 272, keys: [{ key: "default", block: [{ key: "default", block: [] }, { key: "portrait_modifier", block: [] }] }], blocks: { default: { sampled: 270, keys: [{ key: "default", block: [{ key: "head" }, { key: "torso" }] }, { key: "portrait_modifier", block: [{ key: "animations" }] }] } } }, portrait_modifier_group: { sampled: 47, keys: [{ key: "usage", choices: ["game", "customization", "none"] }] }, province_history: { sampled: 10887, keys: [{ key: "holding", choices: ["none", "castle_holding", "city_holding", "church_holding", "tribal_holding", "nomad_holding", "temple_citadel_holding", "herder_holding"] }] }, religion: { sampled: 49, keys: [{ key: "family", choices: ["rf_pagan", "rf_abrahamic", "rf_eastern", "rf_sinitic"] }, { key: "graphical_faith" }, { key: "doctrine" }, { key: "pagan_roots", choices: ["yes"] }, { key: "traits", block: [{ key: "virtues", block: [] }, { key: "sins", block: [] }] }, { key: "custom_faith_icons", block: [] }, { key: "holy_order_names", block: [] }, { key: "localization", block: [{ key: "HighGodName" }, { key: "HighGodName2" }, { key: "HighGodNamePossessive" }, { key: "HighGodNameSheHe" }, { key: "HighGodHerselfHimself" }, { key: "HighGodHerHis" }, { key: "HighGodNameAlternate" }, { key: "HighGodNameAlternatePossessive" }, { key: "CreatorName" }, { key: "CreatorNamePossessive" }, { key: "CreatorSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_IT", "CHARACTER_SHEHE_SHE", "CHARACTER_SHEHE_THEY"] }, { key: "CreatorHerHis", choices: ["CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_ITS", "CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_THEIR", "CHARACTER_HERHIM_THEM"] }, { key: "CreatorHerHim", choices: ["CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_IT", "CHARACTER_HERHIM_HER", "CHARACTER_HERHIM_THEM", "CHARACTER_HERHIS_THEIR"] }, { key: "HealthGodName" }, { key: "HealthGodNamePossessive" }, { key: "HealthGodSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_SHE", "CHARACTER_SHEHE_THEY", "CHARACTER_SHEHE_IT", "tani_high_god_shehe"] }, { key: "HealthGodHerHis", choices: ["CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_THEIR", "CHARACTER_HERHIS_ITS", "tani_high_god_herselfhimself"] }, { key: "HealthGodHerHim", choices: ["CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_HER", "CHARACTER_HERHIM_THEM", "CHARACTER_HERHIM_IT", "tani_high_god_herhis"] }, { key: "FertilityGodName" }, { key: "FertilityGodNamePossessive" }, { key: "FertilityGodSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_SHE", "CHARACTER_SHEHE_THEY", "CHARACTER_SHEHE_IT", "tani_high_god_shehe"] }, { key: "FertilityGodHerHis", choices: ["CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_THEIR", "CHARACTER_HERHIS_ITS", "tani_high_god_herselfhimself"] }, { key: "FertilityGodHerHim", choices: ["CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_HER", "CHARACTER_HERHIM_THEM", "CHARACTER_HERHIM_IT", "tani_high_god_herhis"] }, { key: "WealthGodName" }, { key: "WealthGodNamePossessive" }, { key: "WealthGodSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_THEY", "CHARACTER_SHEHE_IT", "CHARACTER_SHEHE_SHE", "tani_high_god_shehe"] }, { key: "WealthGodHerHis", choices: ["CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_THEIR", "CHARACTER_HERHIS_ITS", "tani_high_god_herselfhimself"] }, { key: "WealthGodHerHim", choices: ["CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_HER", "CHARACTER_HERHIM_THEM", "CHARACTER_HERHIM_IT", "tani_high_god_herhis"] }, { key: "HouseholdGodName" }, { key: "HouseholdGodNamePossessive" }, { key: "HouseholdGodSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_SHE", "CHARACTER_SHEHE_IT", "CHARACTER_SHEHE_THEY"] }, { key: "HouseholdGodHerHis", choices: ["CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_ITS", "CHARACTER_HERHIS_THEIR"] }, { key: "HouseholdGodHerHim", choices: ["CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_HER", "CHARACTER_HERHIM_IT", "CHARACTER_HERHIM_THEM"] }, { key: "FateGodName" }, { key: "FateGodNamePossessive" }, { key: "FateGodSheHe", choices: ["CHARACTER_SHEHE_IT", "CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_THEY", "CHARACTER_SHEHE_SHE", "tani_high_god_shehe"] }, { key: "FateGodHerHis", choices: ["CHARACTER_HERHIS_ITS", "CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_THEIR", "CHARACTER_HERHIS_HER", "tani_high_god_herselfhimself"] }, { key: "FateGodHerHim", choices: ["CHARACTER_HERHIM_IT", "CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_THEM", "CHARACTER_HERHIM_HER", "tani_high_god_herhis"] }, { key: "KnowledgeGodName" }, { key: "KnowledgeGodNamePossessive" }, { key: "KnowledgeGodSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_SHE", "CHARACTER_SHEHE_THEY", "CHARACTER_SHEHE_IT", "tani_high_god_shehe"] }, { key: "KnowledgeGodHerHis", choices: ["CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_THEIR", "CHARACTER_HERHIS_ITS", "tani_high_god_herselfhimself"] }, { key: "KnowledgeGodHerHim", choices: ["CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_HER", "CHARACTER_HERHIM_THEM", "CHARACTER_HERHIM_IT", "tani_high_god_herhis"] }, { key: "WarGodName" }, { key: "WarGodNamePossessive" }, { key: "WarGodSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_SHE", "CHARACTER_SHEHE_IT", "CHARACTER_SHEHE_THEY", "tani_high_god_shehe"] }, { key: "WarGodHerHis", choices: ["CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_ITS", "CHARACTER_HERHIS_THEIR", "tani_high_god_herselfhimself"] }, { key: "WarGodHerHim", choices: ["CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_HER", "CHARACTER_HERHIM_IT", "CHARACTER_HERHIM_THEM", "tani_high_god_herhis"] }, { key: "TricksterGodName" }, { key: "TricksterGodNamePossessive" }, { key: "TricksterGodSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_SHE", "CHARACTER_SHEHE_THEY", "CHARACTER_SHEHE_IT"] }, { key: "TricksterGodHerHis", choices: ["CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_THEIR", "CHARACTER_HERHIS_ITS"] }, { key: "TricksterGodHerHim", choices: ["CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_HER", "CHARACTER_HERHIM_THEM", "CHARACTER_HERHIM_IT"] }, { key: "NightGodName" }, { key: "NightGodNamePossessive" }, { key: "NightGodSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_SHE", "CHARACTER_SHEHE_THEY", "CHARACTER_SHEHE_IT", "tani_high_god_shehe"] }, { key: "NightGodHerHis", choices: ["CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_THEIR", "CHARACTER_HERHIS_ITS", "tani_high_god_herselfhimself"] }, { key: "NightGodHerHim", choices: ["CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_HER", "CHARACTER_HERHIM_THEM", "CHARACTER_HERHIM_IT", "tani_high_god_herhis"] }, { key: "WaterGodName" }, { key: "WaterGodNamePossessive" }, { key: "WaterGodSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_SHE", "CHARACTER_SHEHE_IT", "CHARACTER_SHEHE_THEY", "biliku_high_god_shehe", "tani_high_god_shehe"] }, { key: "WaterGodHerHis", choices: ["CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_ITS", "CHARACTER_HERHIS_THEIR", "biliku_high_god_herselfhimself", "tani_high_god_herselfhimself"] }, { key: "WaterGodHerHim", choices: ["CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_HER", "CHARACTER_HERHIM_IT", "CHARACTER_HERHIM_THEM", "biliku_high_god_herhis", "tani_high_god_herhis"] }, { key: "PantheonTerm" }, { key: "PantheonTerm2" }, { key: "PantheonTerm3" }, { key: "PantheonTermHasHave", choices: ["pantheon_term_have", "pantheon_term_has"] }, { key: "GoodGodNames", block: [] }, { key: "DevilName" }, { key: "DevilNamePossessive" }, { key: "DevilSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_IT", "CHARACTER_SHEHE_SHE", "CHARACTER_SHEHE_THEY"] }, { key: "DevilHerHis", choices: ["CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_ITS", "CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_THEIR"] }, { key: "DevilHerselfHimself" }, { key: "EvilGodNames", block: [] }, { key: "HouseOfWorship" }, { key: "HouseOfWorship2" }, { key: "HouseOfWorship3" }, { key: "HouseOfWorshipPlural" }, { key: "ReligiousSymbol" }, { key: "ReligiousSymbol2" }, { key: "ReligiousSymbol3" }, { key: "ReligiousText" }, { key: "ReligiousText2" }, { key: "ReligiousText3" }, { key: "ReligiousHeadName" }, { key: "ReligiousHeadTitleName" }, { key: "DevoteeMale" }, { key: "DevoteeMalePlural" }, { key: "DevoteeFemale" }, { key: "DevoteeFemalePlural" }, { key: "DevoteeNeuter" }, { key: "DevoteeNeuterPlural" }, { key: "PriestMale" }, { key: "PriestMalePlural" }, { key: "PriestFemale" }, { key: "PriestFemalePlural" }, { key: "PriestNeuter" }, { key: "PriestNeuterPlural" }, { key: "AltPriestTermPlural" }, { key: "BishopMale" }, { key: "BishopMalePlural" }, { key: "BishopFemale" }, { key: "BishopFemalePlural" }, { key: "BishopNeuter" }, { key: "BishopNeuterPlural" }, { key: "DivineRealm" }, { key: "DivineRealm2" }, { key: "DivineRealm3" }, { key: "PositiveAfterLife" }, { key: "PositiveAfterLife2" }, { key: "PositiveAfterLife3" }, { key: "NegativeAfterLife" }, { key: "NegativeAfterLife2" }, { key: "NegativeAfterLife3" }, { key: "DeathDeityName" }, { key: "DeathDeityNamePossessive" }, { key: "DeathDeitySheHe" }, { key: "DeathDeityHerHis" }, { key: "DeathDeityHerHim", choices: ["CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_IT", "CHARACTER_HERHIM_HER"] }, { key: "WitchGodName" }, { key: "WitchGodNamePossessive" }, { key: "WitchGodHerHis", choices: ["CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_ITS", "CHARACTER_HERHIS_THEIR", "witchgod_herhis_their"] }, { key: "WitchGodSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_SHE", "CHARACTER_SHEHE_IT", "CHARACTER_SHEHE_THEY", "witchgod_shehe_they"] }, { key: "WitchGodHerHim", choices: ["CHARACTER_HERHIM_HER", "CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_IT", "CHARACTER_HERHIM_THEM", "witchgod_herhim_them"] }, { key: "WitchGodMistressMaster", choices: ["witch_spirit", "master", "mistress", "witch_master"] }, { key: "WitchGodMotherFather", choices: ["witch_source", "father", "mother", "witch_father"] }, { key: "GHWName", choices: ["ghw_great_holy_war", "ghw_purification", "ghw_crusade", "ghw_jihad"] }, { key: "GHWNamePlural", choices: ["ghw_great_holy_wars", "ghw_purifications", "ghw_crusades", "ghw_jihads"] }] }, { key: "faiths", block: [] }], blocks: { localization: { sampled: 49, keys: [{ key: "HighGodName" }, { key: "HighGodName2" }, { key: "HighGodNamePossessive" }, { key: "HighGodNameSheHe" }, { key: "HighGodHerselfHimself" }, { key: "HighGodHerHis" }, { key: "HighGodNameAlternate" }, { key: "HighGodNameAlternatePossessive" }, { key: "CreatorName" }, { key: "CreatorNamePossessive" }, { key: "CreatorSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_IT", "CHARACTER_SHEHE_SHE", "CHARACTER_SHEHE_THEY"] }, { key: "CreatorHerHis", choices: ["CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_ITS", "CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_THEIR", "CHARACTER_HERHIM_THEM"] }, { key: "CreatorHerHim", choices: ["CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_IT", "CHARACTER_HERHIM_HER", "CHARACTER_HERHIM_THEM", "CHARACTER_HERHIS_THEIR"] }, { key: "HealthGodName" }, { key: "HealthGodNamePossessive" }, { key: "HealthGodSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_SHE", "CHARACTER_SHEHE_THEY", "CHARACTER_SHEHE_IT", "tani_high_god_shehe"] }, { key: "HealthGodHerHis", choices: ["CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_THEIR", "CHARACTER_HERHIS_ITS", "tani_high_god_herselfhimself"] }, { key: "HealthGodHerHim", choices: ["CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_HER", "CHARACTER_HERHIM_THEM", "CHARACTER_HERHIM_IT", "tani_high_god_herhis"] }, { key: "FertilityGodName" }, { key: "FertilityGodNamePossessive" }, { key: "FertilityGodSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_SHE", "CHARACTER_SHEHE_THEY", "CHARACTER_SHEHE_IT", "tani_high_god_shehe"] }, { key: "FertilityGodHerHis", choices: ["CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_THEIR", "CHARACTER_HERHIS_ITS", "tani_high_god_herselfhimself"] }, { key: "FertilityGodHerHim", choices: ["CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_HER", "CHARACTER_HERHIM_THEM", "CHARACTER_HERHIM_IT", "tani_high_god_herhis"] }, { key: "WealthGodName" }, { key: "WealthGodNamePossessive" }, { key: "WealthGodSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_THEY", "CHARACTER_SHEHE_IT", "CHARACTER_SHEHE_SHE", "tani_high_god_shehe"] }, { key: "WealthGodHerHis", choices: ["CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_THEIR", "CHARACTER_HERHIS_ITS", "tani_high_god_herselfhimself"] }, { key: "WealthGodHerHim", choices: ["CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_HER", "CHARACTER_HERHIM_THEM", "CHARACTER_HERHIM_IT", "tani_high_god_herhis"] }, { key: "HouseholdGodName" }, { key: "HouseholdGodNamePossessive" }, { key: "HouseholdGodSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_SHE", "CHARACTER_SHEHE_IT", "CHARACTER_SHEHE_THEY"] }, { key: "HouseholdGodHerHis", choices: ["CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_ITS", "CHARACTER_HERHIS_THEIR"] }, { key: "HouseholdGodHerHim", choices: ["CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_HER", "CHARACTER_HERHIM_IT", "CHARACTER_HERHIM_THEM"] }, { key: "FateGodName" }, { key: "FateGodNamePossessive" }, { key: "FateGodSheHe", choices: ["CHARACTER_SHEHE_IT", "CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_THEY", "CHARACTER_SHEHE_SHE", "tani_high_god_shehe"] }, { key: "FateGodHerHis", choices: ["CHARACTER_HERHIS_ITS", "CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_THEIR", "CHARACTER_HERHIS_HER", "tani_high_god_herselfhimself"] }, { key: "FateGodHerHim", choices: ["CHARACTER_HERHIM_IT", "CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_THEM", "CHARACTER_HERHIM_HER", "tani_high_god_herhis"] }, { key: "KnowledgeGodName" }, { key: "KnowledgeGodNamePossessive" }, { key: "KnowledgeGodSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_SHE", "CHARACTER_SHEHE_THEY", "CHARACTER_SHEHE_IT", "tani_high_god_shehe"] }, { key: "KnowledgeGodHerHis", choices: ["CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_THEIR", "CHARACTER_HERHIS_ITS", "tani_high_god_herselfhimself"] }, { key: "KnowledgeGodHerHim", choices: ["CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_HER", "CHARACTER_HERHIM_THEM", "CHARACTER_HERHIM_IT", "tani_high_god_herhis"] }, { key: "WarGodName" }, { key: "WarGodNamePossessive" }, { key: "WarGodSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_SHE", "CHARACTER_SHEHE_IT", "CHARACTER_SHEHE_THEY", "tani_high_god_shehe"] }, { key: "WarGodHerHis", choices: ["CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_ITS", "CHARACTER_HERHIS_THEIR", "tani_high_god_herselfhimself"] }, { key: "WarGodHerHim", choices: ["CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_HER", "CHARACTER_HERHIM_IT", "CHARACTER_HERHIM_THEM", "tani_high_god_herhis"] }, { key: "TricksterGodName" }, { key: "TricksterGodNamePossessive" }, { key: "TricksterGodSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_SHE", "CHARACTER_SHEHE_THEY", "CHARACTER_SHEHE_IT"] }, { key: "TricksterGodHerHis", choices: ["CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_THEIR", "CHARACTER_HERHIS_ITS"] }, { key: "TricksterGodHerHim", choices: ["CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_HER", "CHARACTER_HERHIM_THEM", "CHARACTER_HERHIM_IT"] }, { key: "NightGodName" }, { key: "NightGodNamePossessive" }, { key: "NightGodSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_SHE", "CHARACTER_SHEHE_THEY", "CHARACTER_SHEHE_IT", "tani_high_god_shehe"] }, { key: "NightGodHerHis", choices: ["CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_THEIR", "CHARACTER_HERHIS_ITS", "tani_high_god_herselfhimself"] }, { key: "NightGodHerHim", choices: ["CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_HER", "CHARACTER_HERHIM_THEM", "CHARACTER_HERHIM_IT", "tani_high_god_herhis"] }, { key: "WaterGodName" }, { key: "WaterGodNamePossessive" }, { key: "WaterGodSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_SHE", "CHARACTER_SHEHE_IT", "CHARACTER_SHEHE_THEY", "biliku_high_god_shehe", "tani_high_god_shehe"] }, { key: "WaterGodHerHis", choices: ["CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_ITS", "CHARACTER_HERHIS_THEIR", "biliku_high_god_herselfhimself", "tani_high_god_herselfhimself"] }, { key: "WaterGodHerHim", choices: ["CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_HER", "CHARACTER_HERHIM_IT", "CHARACTER_HERHIM_THEM", "biliku_high_god_herhis", "tani_high_god_herhis"] }, { key: "PantheonTerm" }, { key: "PantheonTerm2" }, { key: "PantheonTerm3" }, { key: "PantheonTermHasHave", choices: ["pantheon_term_have", "pantheon_term_has"] }, { key: "GoodGodNames", block: [] }, { key: "DevilName" }, { key: "DevilNamePossessive" }, { key: "DevilSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_IT", "CHARACTER_SHEHE_SHE", "CHARACTER_SHEHE_THEY"] }, { key: "DevilHerHis", choices: ["CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_ITS", "CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_THEIR"] }, { key: "DevilHerselfHimself" }, { key: "EvilGodNames", block: [] }, { key: "HouseOfWorship" }, { key: "HouseOfWorship2" }, { key: "HouseOfWorship3" }, { key: "HouseOfWorshipPlural" }, { key: "ReligiousSymbol" }, { key: "ReligiousSymbol2" }, { key: "ReligiousSymbol3" }, { key: "ReligiousText" }, { key: "ReligiousText2" }, { key: "ReligiousText3" }, { key: "ReligiousHeadName" }, { key: "ReligiousHeadTitleName" }, { key: "DevoteeMale" }, { key: "DevoteeMalePlural" }, { key: "DevoteeFemale" }, { key: "DevoteeFemalePlural" }, { key: "DevoteeNeuter" }, { key: "DevoteeNeuterPlural" }, { key: "PriestMale" }, { key: "PriestMalePlural" }, { key: "PriestFemale" }, { key: "PriestFemalePlural" }, { key: "PriestNeuter" }, { key: "PriestNeuterPlural" }, { key: "AltPriestTermPlural" }, { key: "BishopMale" }, { key: "BishopMalePlural" }, { key: "BishopFemale" }, { key: "BishopFemalePlural" }, { key: "BishopNeuter" }, { key: "BishopNeuterPlural" }, { key: "DivineRealm" }, { key: "DivineRealm2" }, { key: "DivineRealm3" }, { key: "PositiveAfterLife" }, { key: "PositiveAfterLife2" }, { key: "PositiveAfterLife3" }, { key: "NegativeAfterLife" }, { key: "NegativeAfterLife2" }, { key: "NegativeAfterLife3" }, { key: "DeathDeityName" }, { key: "DeathDeityNamePossessive" }, { key: "DeathDeitySheHe" }, { key: "DeathDeityHerHis" }, { key: "DeathDeityHerHim", choices: ["CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_IT", "CHARACTER_HERHIM_HER"] }, { key: "WitchGodName" }, { key: "WitchGodNamePossessive" }, { key: "WitchGodHerHis", choices: ["CHARACTER_HERHIS_HER", "CHARACTER_HERHIS_HIS", "CHARACTER_HERHIS_ITS", "CHARACTER_HERHIS_THEIR", "witchgod_herhis_their"] }, { key: "WitchGodSheHe", choices: ["CHARACTER_SHEHE_HE", "CHARACTER_SHEHE_SHE", "CHARACTER_SHEHE_IT", "CHARACTER_SHEHE_THEY", "witchgod_shehe_they"] }, { key: "WitchGodHerHim", choices: ["CHARACTER_HERHIM_HER", "CHARACTER_HERHIM_HIM", "CHARACTER_HERHIM_IT", "CHARACTER_HERHIM_THEM", "witchgod_herhim_them"] }, { key: "WitchGodMistressMaster", choices: ["witch_spirit", "master", "mistress", "witch_master"] }, { key: "WitchGodMotherFather", choices: ["witch_source", "father", "mother", "witch_father"] }, { key: "GHWName", choices: ["ghw_great_holy_war", "ghw_purification", "ghw_crusade", "ghw_jihad"] }, { key: "GHWNamePlural", choices: ["ghw_great_holy_wars", "ghw_purifications", "ghw_crusades", "ghw_jihads"] }] }, traits: { sampled: 49, keys: [{ key: "virtues", block: [] }, { key: "sins", block: [] }] } } }, scheme_agent_type: { sampled: 65, keys: [{ key: "contribution_type", choices: ["success_chance_max", "speed", "secrecy", "success_chance", "success_chance_growth"] }, { key: "contribution", block: [{ key: "value", choices: ["scheme_agent_general_bonuses_contribution_score_value", "100", "-100", "10"] }, { key: "add", block: [] }, { key: "if", block: [] }, { key: "max", choices: ["agent_scheme_contributor_max"] }, { key: "min", choices: ["agent_scheme_contributor_min"] }] }], blocks: { contribution: { sampled: 65, keys: [{ key: "value", choices: ["scheme_agent_general_bonuses_contribution_score_value", "100", "-100", "10"] }, { key: "add", block: [{ key: "value", choices: ["intrigue", "diplomacy", "learning", "stewardship", "prowess", "martial"] }, { key: "multiply", choices: ["agent_3to1_3_skill_mult_value", "agent_3to1_1_skill_mult_value", "agent_1to1_1_skill_mult_value", "agent_2to1to1_1_skill_mult_value", "agent_2to1to1_2_skill_mult_value", "1.75"] }, { key: "ceiling", choices: ["yes"] }, { key: "max", choices: ["agent_max_skill_value", "political_agent_max_skill_value", "50"] }, { key: "desc", choices: ["scheme_agent_aptitude.skill.intrigue", "scheme_agent_aptitude.skill.diplomacy", "scheme_agent_aptitude.skill.learning", "scheme_agent_aptitude.skill.stewardship", "scheme_agent_aptitude.skill.prowess", "scheme_agent_aptitude.skill.martial", "court_position_skill_learning"] }] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "add", block: [] }] }, { key: "max", choices: ["agent_scheme_contributor_max"] }, { key: "min", choices: ["agent_scheme_contributor_min"] }] } } }, scheme_countermeasure: { sampled: 24, keys: [{ key: "frame" }, { key: "is_shown", block: [{ key: "scheme_countermeasure_access_select_best_tier_trigger", block: [] }] }, { key: "is_valid_showing_failures_only", block: [{ key: "can_set_own_countermeasures_trigger", choices: ["yes"] }] }, { key: "on_activate", block: [{ key: "inform_player_of_relevant_countermeasure_activated_effect", block: [] }] }, { key: "owner_modifier", block: [] }, { key: "ai_will_do", block: [{ key: "add", choices: ["ai_will_do_withdraw_from_view_value", "ai_will_do_arbitrary_arrests_value", "ai_will_do_bounties_for_whispers_value", "ai_will_do_redouble_guards_for_courtier_value", "ai_will_do_redouble_guards_value", "ai_will_do_strengthen_sentries_for_courtier_value", "ai_will_do_strengthen_sentries_value"] }] }, { key: "parameters", block: [{ key: "has_any_countermeasure_active", choices: ["yes"] }, { key: "has_preemptive_countermeasure_active", choices: ["yes"] }] }], blocks: { ai_will_do: { sampled: 23, keys: [{ key: "add", choices: ["ai_will_do_withdraw_from_view_value", "ai_will_do_arbitrary_arrests_value", "ai_will_do_bounties_for_whispers_value", "ai_will_do_redouble_guards_for_courtier_value", "ai_will_do_redouble_guards_value", "ai_will_do_strengthen_sentries_for_courtier_value", "ai_will_do_strengthen_sentries_value"] }] }, is_shown: { sampled: 24, keys: [{ key: "scheme_countermeasure_access_select_best_tier_trigger", block: [{ key: "COUNTERMEASURE", choices: ["arbitrary_arrests", "bounties_for_whispers", "redouble_guards", "strengthen_sentries", "withdraw_from_view", "bribe_officials"] }, { key: "TIER", choices: ["1", "2", "3", "4"] }] }] }, is_valid_showing_failures_only: { sampled: 23, keys: [{ key: "can_set_own_countermeasures_trigger", choices: ["yes"] }] }, on_activate: { sampled: 23, keys: [{ key: "inform_player_of_relevant_countermeasure_activated_effect", block: [{ key: "COUNTERMEASURE", choices: ["arbitrary_arrests", "bounties_for_whispers", "redouble_guards", "strengthen_sentries", "withdraw_from_view", "bribe_officials"] }] }] }, parameters: { sampled: 24, keys: [{ key: "has_any_countermeasure_active", choices: ["yes"] }, { key: "has_preemptive_countermeasure_active", choices: ["yes"] }] } } }, scheme_pulse_action: { sampled: 315, keys: [{ key: "icon", choices: ["intrigue_positive", "intrigue_negative"] }, { key: "is_valid", block: [{ key: "scheme_apa_apply_modifier_trigger", block: [] }] }, { key: "weight", choices: ["scheme_owner_aptitude_weight_up_positive_value", "scheme_owner_aptitude_weight_up_negative_value"] }, { key: "effect", block: [{ key: "custom_tooltip", block: [] }, { key: "scheme_pulse_action_sort_cooldown_effect", choices: ["yes"] }] }], blocks: { effect: { sampled: 315, keys: [{ key: "custom_tooltip", block: [{ key: "text", choices: ["scheme_apa.0000.effect_notice.positive.success_chance", "scheme_apa.0000.effect_notice.negative.success_chance", "scheme_apa.0000.effect_notice.negative.speed", "scheme_apa.0000.effect_notice.positive.speed", "scheme_apa.0000.effect_notice.positive.secrecy", "scheme_apa.0000.effect_notice.negative.secrecy"] }, { key: "add_scheme_modifier", block: [] }] }, { key: "scheme_pulse_action_sort_cooldown_effect", choices: ["yes"] }] }, is_valid: { sampled: 315, keys: [{ key: "scheme_apa_apply_modifier_trigger", block: [{ key: "MODIFIER" }] }] } } }, scheme_type: { sampled: 76, keys: [{ key: "skill", choices: ["intrigue", "diplomacy", "learning", "stewardship", "prowess", "martial"] }, { key: "desc" }, { key: "success_desc" }, { key: "icon" }, { key: "illustration" }, { key: "target_type", choices: ["character", "title"] }, { key: "category", choices: ["contract", "political", "hostile", "personal"] }, { key: "is_secret", choices: ["yes", "no"] }, { key: "speed_per_skill_point", choices: ["t2_spsp_owner_value", "-2.5", "t3_spsp_owner_value", "-2", "-1", "t1_spsp_owner_value"] }, { key: "phases_per_agent_charge" }, { key: "base_progress_goal" }, { key: "spymaster_speed_per_skill_point", choices: ["0", "t2_spsp_owner_spy_value", "t3_spsp_owner_spy_value", "t2_spsp_target_spy_value"] }, { key: "success_chance_growth_per_skill_point", choices: ["laamp_contract_scgpsp_value", "t2_scgpsp_value", "t3_scgpsp_value"] }, { key: "base_maximum_success", choices: ["t3_base_max_success_value", "t2_base_max_success_value", "95", "t1_base_max_success_value"] }, { key: "uses_resistance", choices: ["no"] }, { key: "agent_leave_threshold", choices: ["-25", "-10"] }, { key: "valid", block: [] }, { key: "agent_join_chance", block: [{ key: "base", choices: ["0"] }, { key: "ai_agent_join_chance_laamp_contract_modifier", choices: ["yes"] }] }, { key: "valid_agent", block: [{ key: "is_valid_agent_standard_trigger", choices: ["yes"] }] }, { key: "agent_groups_owner_perspective", block: [] }, { key: "allow", block: [{ key: "is_adult", choices: ["yes"] }, { key: "is_imprisoned", choices: ["no"] }] }, { key: "base_success_chance", block: [{ key: "base", choices: ["0", "5", "10", "-25", "15", "30"] }, { key: "scheme_type_skill_success_chance_modifier", block: [] }, { key: "modifier", block: [] }] }, { key: "on_phase_completed", block: [{ key: "suppress_scheme_follow_up_event_till_input_given_effect", choices: ["yes"] }, { key: "cap_schemes_and_fire_reminders_effect", choices: ["yes"] }] }, { key: "on_hud_click", block: [] }, { key: "on_monthly", block: [{ key: "fire_generic_contract_scheme_on_going_events_effect", choices: ["yes"] }] }, { key: "on_semiyearly", block: [{ key: "if", block: [] }] }, { key: "odds_prediction", block: [{ key: "add" }, { key: "min", choices: ["0"] }] }, { key: "on_invalidated", block: [{ key: "save_scope_as", choices: ["scheme"] }, { key: "scheme_owner", block: [] }] }, { key: "on_start", block: [{ key: "set_variable", block: [] }, { key: "if", block: [] }] }], blocks: { agent_join_chance: { sampled: 63, keys: [{ key: "base", choices: ["0"] }, { key: "ai_agent_join_chance_laamp_contract_modifier", choices: ["yes"] }] }, allow: { sampled: 40, keys: [{ key: "is_adult", choices: ["yes"] }, { key: "is_imprisoned", choices: ["no"] }] }, base_success_chance: { sampled: 76, keys: [{ key: "base", choices: ["0", "5", "10", "-25", "15", "30"] }, { key: "scheme_type_skill_success_chance_modifier", block: [{ key: "SKILL", choices: ["INTRIGUE", "DIPLOMACY", "LEARNING", "STEWARDSHIP", "PROWESS", "MARTIAL"] }] }, { key: "modifier", block: [{ key: "add" }, { key: "desc" }] }] }, odds_prediction: { sampled: 40, keys: [{ key: "add" }, { key: "min", choices: ["0"] }] }, on_invalidated: { sampled: 76, keys: [{ key: "save_scope_as", choices: ["scheme"] }, { key: "scheme_owner", block: [] }] }, on_monthly: { sampled: 68, keys: [{ key: "fire_generic_contract_scheme_on_going_events_effect", choices: ["yes"] }] }, on_phase_completed: { sampled: 75, keys: [{ key: "suppress_scheme_follow_up_event_till_input_given_effect", choices: ["yes"] }, { key: "cap_schemes_and_fire_reminders_effect", choices: ["yes"] }] }, on_semiyearly: { sampled: 63, keys: [{ key: "if", block: [{ key: "limit", block: [] }] }] }, on_start: { sampled: 45, keys: [{ key: "set_variable", block: [{ key: "name", choices: ["apply_countermeasures", "targets_movement", "secrets_found"] }, { key: "value", choices: ["flag:calculating", "flag:indirect", "flag:opportunistic", "flag:political", "scope:targets_movement", "0"] }] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "add_agent_slot" }] }] }, valid_agent: { sampled: 63, keys: [{ key: "is_valid_agent_standard_trigger", choices: ["yes"] }] } } }, script_value: { sampled: 3477, keys: [{ key: "value" }] }, scripted_animation: { sampled: 10, keys: [{ key: "triggered_animation", block: [{ key: "trigger", block: [] }, { key: "animation" }] }, { key: "animation", choices: ["aggressive_sword", "celebrate_sword", "hunting_shortbow_aim_arrow_default", "hunting_shortbow_aim_bluntarrow_default", "hunting_shortbow_rest_arrow_default", "hunting_shortbow_rest_bluntarrow_default", "idle", "personality_bold"] }], blocks: { triggered_animation: { sampled: 54, keys: [{ key: "trigger", block: [] }, { key: "animation" }] } } }, scripted_character_template: { sampled: 403, keys: [{ key: "age", block: [] }, { key: "dynasty", choices: ["none", "generate"] }, { key: "random_traits", choices: ["yes", "no"] }, { key: "faith" }, { key: "culture" }, { key: "random_traits_list", block: [{ key: "count", choices: ["1", "3", "2", "squire_quality_bad_traits", "squire_quality_good_traits"] }] }, { key: "gender_female_chance" }, { key: "after_creation", block: [] }], blocks: { random_traits_list: { sampled: 761, keys: [{ key: "count", choices: ["1", "3", "2", "squire_quality_bad_traits", "squire_quality_good_traits"] }] } } }, scripted_cost: { sampled: 12, keys: [{ key: "prestige", block: [{ key: "value", block: [] }, { key: "if", block: [] }] }] }, scripted_effect: { sampled: 3465, keys: [] }, scripted_gui: { sampled: 33, keys: [{ key: "scope", choices: ["character", "innovation_type"] }, { key: "is_valid", block: [] }] }, scripted_illustration: { sampled: 18, keys: [{ key: "texture", block: [{ key: "reference" }, { key: "trigger", block: [] }] }], blocks: { texture: { sampled: 546, keys: [{ key: "reference" }, { key: "trigger", block: [] }] } } }, scripted_modifier: { sampled: 378, keys: [{ key: "modifier", block: [{ key: "add" }, { key: "desc" }] }], blocks: { modifier: { sampled: 2070, keys: [{ key: "add" }, { key: "desc" }] } } }, scripted_relation: { sampled: 38, keys: [{ key: "hidden", choices: ["yes"] }, { key: "corresponding" }] }, scripted_rule: { sampled: 62, keys: [] }, scripted_trigger: { sampled: 3152, keys: [] }, secret: { sampled: 18, keys: [{ key: "category", choices: ["deviancy", "murder", "adultery", "religious", "civil", "merit", "raid"] }, { key: "is_valid", block: [] }, { key: "is_shunned", block: [] }, { key: "is_criminal", block: [] }, { key: "on_discover", block: [] }, { key: "on_expose", block: [{ key: "save_scope_as", choices: ["secret"] }, { key: "secret_exposed_notification_effect", choices: ["yes"] }] }], blocks: { on_expose: { sampled: 18, keys: [{ key: "save_scope_as", choices: ["secret"] }, { key: "secret_exposed_notification_effect", choices: ["yes"] }] } } }, situation: { sampled: 11, keys: [{ key: "window", choices: ["the_great_steppe", "dynastic_cycle", "silk_road"] }, { key: "gui_window_name", choices: ["window_the_great_steppe", "window_tgp_natural_disaster", "window_silk_road", "window_situation", "window_tgp_dynastic_cycle"] }, { key: "illustration" }, { key: "situation_group_type", choices: ["major", "natural_disasters", "minor"] }, { key: "map_mode", choices: ["sub_regions", "dynastic_cycle", "silk_road"] }, { key: "is_unique", choices: ["yes", "no"] }, { key: "migration", choices: ["yes"] }, { key: "sub_regions", block: [] }, { key: "on_monthly", block: [] }, { key: "on_yearly", block: [{ key: "trigger_event", block: [] }] }, { key: "participant_groups", block: [{ key: "nomad_rulers_capital", block: [] }, { key: "nomad_rulers_realm", block: [] }, { key: "herders", block: [] }, { key: "settled_rulers", block: [] }] }, { key: "start_phase", choices: ["situation_steppe_abundant_grazing_season", "warning", "phase_steady_trading", "plentiful_harvest", "situation_dynastic_cycle_phase_instability"] }, { key: "phases", block: [{ key: "situation_steppe_abundant_grazing_season", block: [] }, { key: "situation_steppe_severe_drought_season", block: [] }, { key: "situation_steppe_warm_nights_season", block: [] }] }], blocks: { participant_groups: { sampled: 11, keys: [{ key: "nomad_rulers_capital", block: [{ key: "require_capital_in_sub_region", choices: ["yes"] }, { key: "is_character_valid", block: [] }, { key: "map_color", block: [] }] }, { key: "nomad_rulers_realm", block: [{ key: "require_capital_in_sub_region", choices: ["no"] }, { key: "require_realm_in_sub_region", choices: ["yes"] }, { key: "is_character_valid", block: [] }, { key: "map_color", block: [] }] }, { key: "herders", block: [{ key: "is_character_valid", block: [] }, { key: "map_color", block: [] }] }, { key: "settled_rulers", block: [{ key: "is_character_valid", block: [] }, { key: "map_color", block: [] }] }] }, phases: { sampled: 11, keys: [{ key: "situation_steppe_abundant_grazing_season", block: [{ key: "icon" }, { key: "illustration" }, { key: "max_duration", block: [] }, { key: "on_start", block: [] }, { key: "max_duration_next_phase", choices: ["random_non_takeover"] }, { key: "future_phases", block: [] }, { key: "modifier_sets", block: [] }] }, { key: "situation_steppe_severe_drought_season", block: [{ key: "icon" }, { key: "illustration" }, { key: "max_duration", block: [] }, { key: "map_province_effect", choices: ["drought"] }, { key: "on_start", block: [] }, { key: "max_duration_next_phase", choices: ["random_non_takeover"] }, { key: "future_phases", block: [] }, { key: "modifier_sets", block: [] }] }, { key: "situation_steppe_warm_nights_season", block: [{ key: "icon" }, { key: "illustration" }, { key: "max_duration", block: [] }, { key: "map_province_effect", choices: ["summer"] }, { key: "map_province_effect_intensity", choices: ["0.1", "0.6"] }, { key: "on_start", block: [] }, { key: "max_duration_next_phase", choices: ["random_non_takeover"] }, { key: "future_phases", block: [] }, { key: "modifier_sets", block: [] }] }] } } }, situation_catalyst: { sampled: 121, keys: [] }, static_modifier: { sampled: 6011, keys: [{ key: "icon" }] }, story_cycle: { sampled: 51, keys: [{ key: "on_setup", block: [{ key: "story_owner", block: [] }] }, { key: "on_owner_death", block: [] }, { key: "effect_group", block: [{ key: "days", block: [] }, { key: "trigger", block: [] }, { key: "triggered_effect", block: [] }] }, { key: "on_end", block: [] }], blocks: { effect_group: { sampled: 158, keys: [{ key: "days", block: [] }, { key: "trigger", block: [{ key: "story_owner", block: [] }] }, { key: "triggered_effect", block: [{ key: "trigger", block: [] }, { key: "effect", block: [] }] }] }, on_setup: { sampled: 49, keys: [{ key: "story_owner", block: [] }] } } }, struggle_catalyst: { sampled: 127, keys: [] }, subject_contract: { sampled: 64, keys: [{ key: "display_mode", choices: ["tree", "checkbox", "radiobutton", "hidden"] }, { key: "obligation_levels", block: [] }] }, subject_contract_group: { sampled: 23, keys: [{ key: "contracts", block: [] }] }, succession_appointment: { sampled: 11, keys: [{ key: "default_candidates", block: [] }, { key: "allow_same_tier_candidates", choices: ["yes"] }, { key: "candidate_score", block: [{ key: "value", block: [] }] }], blocks: { candidate_score: { sampled: 11, keys: [{ key: "value", block: [{ key: "add", block: [] }, { key: "if", block: [] }, { key: "else_if", block: [] }, { key: "save_temporary_value_as", choices: ["temp_total", "pre_gender_law_calc"] }] }] } } }, suggestion: { sampled: 13, keys: [{ key: "weight", block: [{ key: "base", choices: ["0", "10", "5"] }, { key: "modifier", block: [] }] }, { key: "check_create_suggestion", block: [] }, { key: "effect", block: [] }, { key: "score", block: [{ key: "base", choices: ["100", "50", "-200"] }, { key: "modifier", block: [] }] }, { key: "is_valid", block: [] }], blocks: { weight: { sampled: 13, keys: [{ key: "base", choices: ["0", "10", "5"] }, { key: "modifier", block: [{ key: "add", placeholder: "0.5" }] }] } } }, task_contract: { sampled: 159, keys: [{ key: "group" }, { key: "icon" }, { key: "travel", choices: ["no", "yes"] }, { key: "use_diplomatic_range", choices: ["no", "yes"] }, { key: "valid_to_create", block: [] }, { key: "valid_to_accept", block: [{ key: "is_available", choices: ["yes"] }] }, { key: "valid_to_continue", block: [{ key: "task_contract_taker", block: [] }] }, { key: "valid_to_keep", block: [{ key: "task_contract_taker", block: [] }] }, { key: "on_create", block: [] }, { key: "on_accepted", block: [{ key: "task_contract_taker", block: [] }, { key: "save_scope_as", choices: ["task_contract"] }] }, { key: "on_completed", block: [] }, { key: "on_invalidated", block: [{ key: "if", block: [] }] }, { key: "task_contract_reward", block: [{ key: "failure_standard", block: [] }] }], blocks: { on_accepted: { sampled: 159, keys: [{ key: "task_contract_taker", block: [{ key: "play_sound_effect" }] }, { key: "save_scope_as", choices: ["task_contract"] }] }, on_invalidated: { sampled: 120, keys: [{ key: "if", block: [{ key: "limit", block: [] }, { key: "governor_contract_invalidated_message_effect", choices: ["yes"] }] }] }, task_contract_reward: { sampled: 159, keys: [{ key: "failure_standard", block: [{ key: "visible", choices: ["no"] }, { key: "effect", block: [] }] }] }, valid_to_accept: { sampled: 158, keys: [{ key: "is_available", choices: ["yes"] }] }, valid_to_continue: { sampled: 158, keys: [{ key: "task_contract_taker", block: [{ key: "valid_governor_contract_trigger", choices: ["yes"] }] }] }, valid_to_keep: { sampled: 156, keys: [{ key: "task_contract_taker", block: [{ key: "valid_governor_contract_trigger", choices: ["yes"] }] }] } } }, terrain_type: { sampled: 17, keys: [{ key: "color", block: [] }, { key: "travel_danger_color", block: [] }, { key: "travel_danger_score" }, { key: "provision_cost" }, { key: "county_fertility", choices: ["20", "30", "15", "10", "25", "40"] }, { key: "province_modifier", block: [{ key: "supply_limit_mult", placeholder: "0" }, { key: "travel_danger" }, { key: "county_fertility_growth_add" }] }, { key: "county_capital_modifier", block: [{ key: "development_growth_factor", choices: ["-0.5", "-0.05", "-0.25", "0.1", "0.2", "-0.1", "-0.4"] }] }, { key: "movement_speed", choices: ["0.8", "0.5", "0.7", "1"] }, { key: "audio_parameter", placeholder: "1.0" }, { key: "combat_width", choices: ["0.8", "0.5", "0.6", "0.7", "0.75", "0.9"] }], blocks: { county_capital_modifier: { sampled: 13, keys: [{ key: "development_growth_factor", choices: ["-0.5", "-0.05", "-0.25", "0.1", "0.2", "-0.1", "-0.4"] }] }, province_modifier: { sampled: 17, keys: [{ key: "supply_limit_mult", placeholder: "0" }, { key: "travel_danger" }, { key: "county_fertility_growth_add" }] } } }, trait: { sampled: 301, keys: [{ key: "category" }, { key: "ruler_designer_cost", placeholder: "50" }, { key: "desc", block: [{ key: "first_valid", block: [] }] }], blocks: { desc: { sampled: 285, keys: [{ key: "first_valid", block: [{ key: "triggered_desc", block: [] }, { key: "desc" }] }] } } }, trait_portrait_modifier_group: { sampled: 24, keys: [] }, travel_option: { sampled: 29, keys: [{ key: "is_shown", block: [{ key: "is_ruler", choices: ["yes"] }] }, { key: "is_valid", block: [] }, { key: "travel_modifier", block: [{ key: "travel_speed", choices: ["5", "-5", "10", "20", "-10", "-20"] }, { key: "travel_safety", choices: ["-5", "-10", "10", "20", "-20", "5"] }] }, { key: "owner_modifier", block: [] }, { key: "ai_will_do", block: [{ key: "value", choices: ["0", "100", "50", "1", "90"] }] }, { key: "on_applied_effect", block: [{ key: "custom_tooltip" }] }], blocks: { ai_will_do: { sampled: 29, keys: [{ key: "value", choices: ["0", "100", "50", "1", "90"] }] }, is_shown: { sampled: 29, keys: [{ key: "is_ruler", choices: ["yes"] }] }, on_applied_effect: { sampled: 21, keys: [{ key: "custom_tooltip" }] }, travel_modifier: { sampled: 17, keys: [{ key: "travel_speed", choices: ["5", "-5", "10", "20", "-10", "-20"] }, { key: "travel_safety", choices: ["-5", "-10", "10", "20", "-20", "5"] }] } } }, trigger_localization: { sampled: 1640, keys: [{ key: "global" }] }, tutorial_lesson: { sampled: 111, keys: [{ key: "chain" }, { key: "delay", choices: ["0", "5"] }, { key: "start_automatically", choices: ["no", "yes"] }] }, tutorial_lesson_chain: { sampled: 21, keys: [{ key: "save_progress_in_gamestate", choices: ["yes"] }, { key: "trigger", block: [{ key: "is_tutorial_lesson_chain_completed" }] }], blocks: { trigger: { sampled: 21, keys: [{ key: "is_tutorial_lesson_chain_completed" }] } } } } };
31012
+
30801
31013
  // src/games/ck3/index.ts
30802
31014
  var ck3Profile = {
30803
31015
  ...ck3Meta,
@@ -30815,8 +31027,21 @@ var ck3Profile = {
30815
31027
  scripted_gui: { key: "scope", default: "character" }
30816
31028
  },
30817
31029
  modifierPlaceholders: CK3_MODIFIER_PLACEHOLDERS,
31030
+ // The three triggers the measured condition shapes need. Over the game's own
31031
+ // 21 dynasty legacy tracks every `is_shown` opens with `has_dlc_feature`, and
31032
+ // over its 105 dynasty perks every `can_be_picked` is either
31033
+ // `has_dlc_feature = <feature>` (10) or `<scripted trigger> = yes` (44).
31034
+ conditionValues: {
31035
+ // triggers.log enumerates the whole set on the trigger itself.
31036
+ has_dlc_feature: { from: "docList" },
31037
+ // `has_game_rule = unrestricted_dynasty_legacies_all` names a SETTING, and
31038
+ // a setting is an inner block of a game_rule (00_game_rules.txt).
31039
+ has_game_rule: { from: "innerKeys", kind: "game_rule", except: ["categories", "default"] },
31040
+ scripted_trigger: { from: "kind", kind: "scripted_trigger" }
31041
+ },
30818
31042
  bundledDataTypes: dataTypes_default,
30819
31043
  guiSchema: guiSchema_default,
31044
+ skeletons: skeletons_default.kinds,
30820
31045
  saveSchema: CK3_SAVE_SCHEMA,
30821
31046
  wikiNote: "Source: CK3 wiki (may lag behind the current game version)",
30822
31047
  diagnosticSource: "ck3-script"
@@ -30949,13 +31174,17 @@ var vic3Meta = {
30949
31174
  engine: "jomini",
30950
31175
  // Vic3 mods carry .metadata/metadata.json instead of a launcher .mod file.
30951
31176
  descriptor: "metadata",
30952
- configDirName: ".vic3modding",
31177
+ configDirName: ".px-toolkit",
31178
+ legacyConfigDirName: ".vic3modding",
30953
31179
  docsFolderName: "Victoria 3",
30954
31180
  // Vic3's `script_docs` writes to Documents/.../docs (like EU5), while
30955
31181
  // `dump_data_types` writes to logs/data_types — verified on a live install.
30956
31182
  scriptDocsSubdir: "docs",
30957
31183
  dataTypesCommand: "dump_data_types",
30958
31184
  steamAppId: 529340,
31185
+ // Verified on the live install: a different folder name from CK3's, and only
31186
+ // some DLC have a file there (the rest fall back to their thumbnail.png).
31187
+ dlcIconDir: "gfx/interface/icons/dlc_icons",
30959
31188
  eventNamespaces: true,
30960
31189
  scaffolds: VIC3_SCAFFOLDS,
30961
31190
  // uiFont deliberately absent: this game ships no Gitan (its fonts/ holds
@@ -47417,6 +47646,9 @@ var guiSchema_default2 = {
47417
47646
  ]
47418
47647
  };
47419
47648
 
47649
+ // data/vic3/skeletons.json
47650
+ var skeletons_default2 = { meta: { generated: "2026-09-03", sources: ["vanilla (2140 files)"], majority: 0.5 }, kinds: { achievement: { sampled: 141, keys: [{ key: "possible", block: [] }, { key: "happened", block: [] }] }, ai_strategy: { sampled: 35, keys: [{ key: "icon" }, { key: "type", choices: ["diplomatic", "political", "administrative"] }, { key: "undesirable_infamy_level", block: [{ key: "value", choices: ["50", "25", "10", "75"] }] }, { key: "unacceptable_infamy_level", block: [{ key: "value", choices: ["100", "50", "25", "75"] }] }, { key: "diplomatic_play_boldness", block: [{ key: "value", choices: ["50", "75", "-25", "25", "-10", "0", "-15"] }] }, { key: "recklessness", block: [{ key: "value", choices: ["0.5", "-0.25", "-0.5", "0.25", "-0.1", "0.1", "1", "1.0"] }] }, { key: "aggression", block: [{ key: "value", choices: ["0.25", "0.5", "1", "0", "-2.0"] }, { key: "if", block: [] }, { key: "multiply", block: [] }] }, { key: "building_group_weights", block: [{ key: "bg_army", choices: ["1.5", "1.25"] }] }, { key: "possible", block: [{ key: "is_subject", choices: ["no", "yes"] }] }, { key: "weight", block: [{ key: "value" }, { key: "if", block: [] }] }], blocks: { aggression: { sampled: 18, keys: [{ key: "value", choices: ["0.25", "0.5", "1", "0", "-2.0"] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "add", placeholder: "10" }] }, { key: "multiply", block: [{ key: "value" }, { key: "multiply", choices: ["-0.1"] }, { key: "add", choices: ["1"] }, { key: "max", choices: ["2.0"] }, { key: "min", choices: ["0.5"] }] }] }, building_group_weights: { sampled: 24, keys: [{ key: "bg_army", choices: ["1.5", "1.25"] }] }, diplomatic_play_boldness: { sampled: 19, keys: [{ key: "value", choices: ["50", "75", "-25", "25", "-10", "0", "-15"] }] }, possible: { sampled: 34, keys: [{ key: "is_subject", choices: ["no", "yes"] }] }, recklessness: { sampled: 19, keys: [{ key: "value", choices: ["0.5", "-0.25", "-0.5", "0.25", "-0.1", "0.1", "1", "1.0"] }] }, unacceptable_infamy_level: { sampled: 19, keys: [{ key: "value", choices: ["100", "50", "25", "75"] }] }, undesirable_infamy_level: { sampled: 19, keys: [{ key: "value", choices: ["50", "25", "10", "75"] }] }, weight: { sampled: 35, keys: [{ key: "value" }, { key: "if", block: [{ key: "limit", block: [] }, { key: "add", placeholder: "10" }] }] } } }, alert_group: { sampled: 24, keys: [] }, alert_type: { sampled: 66, keys: [{ key: "script_context" }, { key: "texture" }, { key: "valid", block: [] }, { key: "open_panel" }, { key: "type", choices: ["alert", "important_action", "angry_important_action"] }, { key: "color", choices: ["red", "yellow", "green"] }] }, amendment: { sampled: 67, keys: [{ key: "parent" }, { key: "allowed_laws", block: [] }, { key: "modifier", block: [] }, { key: "possible", block: [{ key: "always", choices: ["yes"] }] }, { key: "would_sponsor", block: [] }, { key: "ai_will_revoke", block: [{ key: "always", choices: ["no", "yes"] }] }, { key: "can_repeal", block: [{ key: "legitimacy", choices: ["legitimacy_to_repeal_amendment"] }] }, { key: "amendment_activism_multiplier", choices: ["0.5", "0.25", "1", "0.75", "0", "0.1", "0.3", "0.50"] }], blocks: { ai_will_revoke: { sampled: 67, keys: [{ key: "always", choices: ["no", "yes"] }] }, can_repeal: { sampled: 67, keys: [{ key: "legitimacy", choices: ["legitimacy_to_repeal_amendment"] }] }, possible: { sampled: 67, keys: [{ key: "always", choices: ["yes"] }] } } }, battle_condition: { sampled: 21, keys: [{ key: "icon" }, { key: "modifier", block: [{ key: "unit_morale_loss_mult", placeholder: "-0.1" }] }, { key: "possible", block: [{ key: "exists", choices: ["scope:character"] }] }, { key: "weight", block: [{ key: "value", choices: ["1", "0.5", "100", "25", "50"] }, { key: "multiply", block: [] }, { key: "if", block: [] }] }], blocks: { modifier: { sampled: 21, keys: [{ key: "unit_morale_loss_mult", placeholder: "-0.1" }] }, possible: { sampled: 18, keys: [{ key: "exists", choices: ["scope:character"] }] }, weight: { sampled: 20, keys: [{ key: "value", choices: ["1", "0.5", "100", "25", "50"] }, { key: "multiply", block: [{ key: "value", choices: ["1"] }, { key: "min", choices: ["0"] }] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "add", choices: ["2", "1", "0.5"] }] }] } } }, building: { sampled: 115, keys: [{ key: "building_group" }, { key: "icon" }, { key: "city_type", choices: ["city", "farm", "mine", "port", "wood", "none"] }, { key: "expandable", choices: ["no"] }, { key: "levels_per_mesh", choices: ["50", "1", "0", "5"] }, { key: "downsizeable", choices: ["no"] }, { key: "unlocking_technologies", block: [] }, { key: "production_method_groups", block: [] }, { key: "required_construction" }, { key: "ownership_type", choices: ["self", "other"] }, { key: "background" }] }, building_group: { sampled: 69, keys: [{ key: "parent_group" }, { key: "default_building" }] }, buy_package: { sampled: 99, keys: [{ key: "political_strength", placeholder: "0.03" }, { key: "goods", block: [{ key: "popneed_heating", placeholder: "26" }, { key: "popneed_services", placeholder: "103468" }, { key: "popneed_intoxicants", placeholder: "216" }, { key: "popneed_luxury_drinks", placeholder: "101001" }, { key: "popneed_free_movement", placeholder: "101001" }, { key: "popneed_communication", placeholder: "101001" }, { key: "popneed_luxury_food", placeholder: "1009" }, { key: "popneed_luxury_items", placeholder: "107055" }, { key: "popneed_leisure", placeholder: "102" }] }], blocks: { goods: { sampled: 99, keys: [{ key: "popneed_heating", placeholder: "26" }, { key: "popneed_services", placeholder: "103468" }, { key: "popneed_intoxicants", placeholder: "216" }, { key: "popneed_luxury_drinks", placeholder: "101001" }, { key: "popneed_free_movement", placeholder: "101001" }, { key: "popneed_communication", placeholder: "101001" }, { key: "popneed_luxury_food", placeholder: "1009" }, { key: "popneed_luxury_items", placeholder: "107055" }, { key: "popneed_leisure", placeholder: "102" }] } } }, character_interaction: { sampled: 21, keys: [{ key: "clicksound" }, { key: "icon" }, { key: "potential", block: [{ key: "owner", choices: ["scope:actor"] }, { key: "is_ruler_of_own_country", choices: ["no", "yes"] }] }, { key: "possible", block: [{ key: "custom_tooltip", block: [] }] }, { key: "effect", block: [{ key: "owner", block: [] }] }, { key: "cooldown", block: [{ key: "days", choices: ["normal_modifier_time", "short_modifier_time", "long_modifier_time"] }] }, { key: "show_confirmation_box", choices: ["yes"] }, { key: "ai_chance", block: [{ key: "value", choices: ["-50", "0", "1", "-25", "-85", "25"] }, { key: "if", block: [] }] }], blocks: { ai_chance: { sampled: 21, keys: [{ key: "value", choices: ["-50", "0", "1", "-25", "-85", "25"] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "add", placeholder: "25" }] }] }, cooldown: { sampled: 15, keys: [{ key: "days", choices: ["normal_modifier_time", "short_modifier_time", "long_modifier_time"] }] }, effect: { sampled: 21, keys: [{ key: "owner", block: [{ key: "add_modifier", block: [] }] }] }, possible: { sampled: 21, keys: [{ key: "custom_tooltip", block: [{ key: "text" }] }] }, potential: { sampled: 21, keys: [{ key: "owner", choices: ["scope:actor"] }, { key: "is_ruler_of_own_country", choices: ["no", "yes"] }] } } }, character_role: { sampled: 10, keys: [{ key: "texture" }, { key: "type" }, { key: "priority", choices: ["50", "10", "1", "100", "2", "25", "99"] }, { key: "auto_assigned", choices: ["yes"] }, { key: "title_format", choices: ["from_rank", "custom", "from_name", "heir", "ruler"] }, { key: "title_preposition", choices: ["PREP_IN", "PREP_OF"] }, { key: "career_length", block: [] }, { key: "character_modifier", block: [{ key: "character_prominence_add", choices: ["50", "-10", "10", "100", "25"] }] }] }, character_template: { sampled: 2010, keys: [{ key: "first_name" }, { key: "last_name" }, { key: "historical", choices: ["yes", "no"] }, { key: "culture" }, { key: "birth_date" }, { key: "interest_group" }, { key: "ideology" }, { key: "home_region" }, { key: "traits", block: [] }] }, character_trait: { sampled: 121, keys: [{ key: "type", choices: ["skill", "personality", "condition"] }, { key: "texture" }, { key: "character_modifier", block: [{ key: "character_popularity_add", placeholder: "25" }] }, { key: "command_modifier", block: [] }, { key: "country_modifier", block: [] }, { key: "agitator_modifier", block: [] }, { key: "interest_group_modifier", block: [] }, { key: "possible", block: [] }, { key: "replace", block: [] }, { key: "weight", block: [{ key: "value" }, { key: "if", block: [] }] }, { key: "value", choices: ["3", "2", "1"] }], blocks: { character_modifier: { sampled: 91, keys: [{ key: "character_popularity_add", placeholder: "25" }] }, weight: { sampled: 65, keys: [{ key: "value" }, { key: "if", block: [{ key: "limit", block: [] }, { key: "multiply", placeholder: "2" }] }] } } }, combat_unit_type: { sampled: 19, keys: [{ key: "group", choices: ["combat_unit_group_infantry", "combat_unit_group_artillery", "combat_unit_group_cavalry", "combat_unit_group_marines"] }, { key: "max_manpower", choices: ["1000"] }, { key: "supply_capacity", placeholder: "40" }, { key: "upkeep_modifier", block: [{ key: "goods_input_small_arms_add", choices: ["2", "3", "1", "1.5", "2.5", "4"] }, { key: "goods_input_ammunition_add", choices: ["3", "2", "4", "1", "1.5"] }] }, { key: "battle_modifier", block: [{ key: "unit_offense_add", placeholder: "25" }, { key: "unit_defense_add", placeholder: "25" }, { key: "unit_morale_loss_add", choices: ["10", "8", "6", "4", "15"] }, { key: "unit_naval_invasion_efficiency_mult", choices: ["-0.25"] }] }, { key: "unlocking_technologies", block: [] }, { key: "combat_unit_image", block: [{ key: "trigger", block: [] }, { key: "texture" }] }, { key: "upgrades", block: [] }], blocks: { battle_modifier: { sampled: 19, keys: [{ key: "unit_offense_add", placeholder: "25" }, { key: "unit_defense_add", placeholder: "25" }, { key: "unit_morale_loss_add", choices: ["10", "8", "6", "4", "15"] }, { key: "unit_naval_invasion_efficiency_mult", choices: ["-0.25"] }] }, combat_unit_image: { sampled: 77, keys: [{ key: "trigger", block: [{ key: "has_culture_graphics", choices: ["african", "arabic", "east_asian", "south_asian", "decentralised_americas", "polynesian"] }] }, { key: "texture" }] }, upkeep_modifier: { sampled: 19, keys: [{ key: "goods_input_small_arms_add", choices: ["2", "3", "1", "1.5", "2.5", "4"] }, { key: "goods_input_ammunition_add", choices: ["3", "2", "4", "1", "1.5"] }] } } }, commander_order: { sampled: 12, keys: [{ key: "texture" }, { key: "military_type", choices: ["army"] }, { key: "behavior", choices: ["advance", "defend"] }, { key: "visible", block: [{ key: "has_role_of_type", choices: ["general"] }] }, { key: "possible", block: [{ key: "has_role_of_type", choices: ["general"] }] }, { key: "modifier", block: [{ key: "character_advancement_speed_add", choices: ["2", "7", "3", "4", "5"] }] }, { key: "entity_animation", choices: ["advance", "defend"] }, { key: "clicksound" }, { key: "experience", choices: ["0.1"] }, { key: "ai_weight", block: [{ key: "value", choices: ["2", "1"] }] }], blocks: { ai_weight: { sampled: 12, keys: [{ key: "value", choices: ["2", "1"] }] }, modifier: { sampled: 12, keys: [{ key: "character_advancement_speed_add", choices: ["2", "7", "3", "4", "5"] }] }, possible: { sampled: 12, keys: [{ key: "has_role_of_type", choices: ["general"] }] }, visible: { sampled: 12, keys: [{ key: "has_role_of_type", choices: ["general"] }] } } }, company_type: { sampled: 221, keys: [{ key: "icon" }, { key: "background" }, { key: "flavored_company", choices: ["yes", "no"] }, { key: "preferred_headquarters", block: [] }, { key: "building_types", block: [] }, { key: "extension_building_types", block: [] }, { key: "possible_prestige_goods", block: [] }, { key: "potential", block: [{ key: "has_interest_marker_in_region" }] }, { key: "attainable", block: [{ key: "hidden_trigger", block: [] }] }, { key: "possible", block: [{ key: "any_scope_state", block: [] }] }, { key: "prosperity_modifier", block: [] }, { key: "ai_will_do", block: [{ key: "any_scope_state", block: [] }] }, { key: "ai_construction_targets", block: [] }, { key: "ai_weight", block: [{ key: "value", choices: ["3", "100"] }] }], blocks: { ai_weight: { sampled: 195, keys: [{ key: "value", choices: ["3", "100"] }] }, ai_will_do: { sampled: 221, keys: [{ key: "any_scope_state", block: [{ key: "state_region" }, { key: "is_incorporated", choices: ["yes"] }] }] }, attainable: { sampled: 181, keys: [{ key: "hidden_trigger", block: [{ key: "any_scope_state", block: [] }] }] }, possible: { sampled: 221, keys: [{ key: "any_scope_state", block: [{ key: "state_region" }, { key: "is_incorporated", choices: ["yes"] }, { key: "any_scope_building", block: [] }] }] }, potential: { sampled: 196, keys: [{ key: "has_interest_marker_in_region" }] } } }, country_definition: { sampled: 830, keys: [{ key: "color", block: [] }, { key: "country_type", choices: ["unrecognized", "recognized", "decentralized", "colonial", "company"] }, { key: "tier", choices: ["principality", "kingdom", "empire", "grand_principality", "city_state", "hegemony"] }, { key: "cultures", block: [] }, { key: "capital" }] }, culture: { sampled: 317, keys: [{ key: "color", block: [] }, { key: "religion" }, { key: "heritage" }, { key: "language" }, { key: "traditions", block: [] }, { key: "male_common_first_names", block: [] }, { key: "female_common_first_names", block: [] }, { key: "noble_last_names", block: [] }, { key: "common_last_names", block: [] }, { key: "ethnicities", block: [{ key: "1" }] }, { key: "graphics" }, { key: "seal_and_signature_texture" }], blocks: { ethnicities: { sampled: 317, keys: [{ key: "1" }] } } }, culture_graphics: { sampled: 10, keys: [] }, customizable_localization: { sampled: 480, keys: [{ key: "type" }, { key: "text", block: [{ key: "trigger", block: [] }, { key: "localization_key" }] }, { key: "log_loc_errors", choices: ["no"] }], blocks: { text: { sampled: 14256, keys: [{ key: "trigger", block: [] }, { key: "localization_key" }] } } }, decision: { sampled: 60, keys: [{ key: "is_shown", block: [{ key: "NOT", block: [] }] }, { key: "possible", block: [] }, { key: "when_taken", block: [] }, { key: "ai_chance", block: [{ key: "value", choices: ["0", "10", "1", "100", "-10", "5", "-100", "50"] }] }], blocks: { ai_chance: { sampled: 60, keys: [{ key: "value", choices: ["0", "10", "1", "100", "-10", "5", "-100", "50"] }] }, is_shown: { sampled: 60, keys: [{ key: "NOT", block: [{ key: "has_variable" }] }] } } }, decree: { sampled: 11, keys: [{ key: "texture" }, { key: "unlocking_technologies", block: [] }, { key: "state_trigger", block: [] }, { key: "modifier", block: [] }, { key: "cost", choices: ["100", "50"] }, { key: "ai_weight", block: [{ key: "value", choices: ["0", "100", "25"] }, { key: "if", block: [] }] }], blocks: { ai_weight: { sampled: 11, keys: [{ key: "value", choices: ["0", "100", "25"] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "add", choices: ["50", "500", "100", "1000", "75", "25"] }] }] } } }, diplomatic_action: { sampled: 55, keys: [{ key: "groups", block: [] }, { key: "requires_approval", choices: ["no", "yes"] }, { key: "should_notify_third_parties", choices: ["yes", "no"] }, { key: "potential", block: [] }, { key: "selectable", block: [] }, { key: "pact", block: [{ key: "cost", choices: ["10", "0", "100", "50", "25", "150"] }, { key: "relations_progress_per_day", choices: ["1", "-1", "-3", "-4", "3"] }, { key: "actor_can_break", block: [] }, { key: "max_target_involvement", choices: ["1000", "5000", "8000", "2500"] }, { key: "requirement_to_maintain", block: [] }, { key: "target_involvement_applies_to", choices: ["source_country", "mutual"] }, { key: "second_modifier", block: [] }, { key: "manual_break_effect", block: [] }, { key: "auto_break_effect", block: [] }] }, { key: "possible", block: [] }, { key: "ai", block: [{ key: "evaluation_chance", block: [] }, { key: "propose_score", block: [] }, { key: "will_propose", block: [] }] }, { key: "accept_effect", block: [] }], blocks: { ai: { sampled: 52, keys: [{ key: "evaluation_chance", block: [{ key: "value", choices: ["0.25", "0", "0.0", "0.025", "0.1", "0.5", "0.01"] }] }, { key: "propose_score", block: [{ key: "value", choices: ["10", "100", "5", "0", "1", "20", "1000"] }] }, { key: "will_propose", block: [{ key: "OR", block: [] }] }] }, pact: { sampled: 30, keys: [{ key: "cost", choices: ["10", "0", "100", "50", "25", "150"] }, { key: "relations_progress_per_day", choices: ["1", "-1", "-3", "-4", "3"] }, { key: "actor_can_break", block: [{ key: "is_diplomatic_play_committed_participant", choices: ["no"] }] }, { key: "max_target_involvement", choices: ["1000", "5000", "8000", "2500"] }, { key: "requirement_to_maintain", block: [{ key: "trigger", block: [] }] }, { key: "target_involvement_applies_to", choices: ["source_country", "mutual"] }, { key: "second_modifier", block: [] }, { key: "manual_break_effect", block: [] }, { key: "auto_break_effect", block: [{ key: "create_bidirectional_truce", block: [] }] }] } } }, diplomatic_catalyst: { sampled: 77, keys: [{ key: "category" }, { key: "political_lobby_creation", block: [{ key: "lobby_anti_country", block: [] }] }, { key: "ai_country_goal_recalculation", block: [{ key: "type", choices: ["more_hostile", "more_friendly", "only_hostile", "all", "only_friendly", "not_hostile"] }, { key: "chance", block: [] }] }], blocks: { ai_country_goal_recalculation: { sampled: 60, keys: [{ key: "type", choices: ["more_hostile", "more_friendly", "only_hostile", "all", "only_friendly", "not_hostile"] }, { key: "chance", block: [{ key: "value" }, { key: "if", block: [] }, { key: "else_if", block: [] }] }] }, political_lobby_creation: { sampled: 44, keys: [{ key: "lobby_anti_country", block: [{ key: "value", placeholder: "0.05" }, { key: "if", block: [] }, { key: "else_if", block: [] }] }] } } }, diplomatic_catalyst_category: { sampled: 35, keys: [{ key: "lobby_formation_reason", choices: ["diplomacy", "aggression", "defense", "ideology", "economy", "funded"] }, { key: "duration", block: [{ key: "years", choices: ["5", "10", "2", "20", "1", "100"] }] }], blocks: { duration: { sampled: 35, keys: [{ key: "years", choices: ["5", "10", "2", "20", "1", "100"] }] } } }, diplomatic_play: { sampled: 52, keys: [{ key: "war_goal" }, { key: "texture" }, { key: "possible", block: [{ key: "NOT", block: [] }] }, { key: "ai_acceptance_max", choices: ["100", "90", "75"] }, { key: "selectable_in_lens", block: [{ key: "always", choices: ["no"] }] }, { key: "on_weekly_pulse", block: [] }, { key: "on_war_begins", block: [] }, { key: "on_war_end", block: [] }], blocks: { possible: { sampled: 52, keys: [{ key: "NOT", block: [{ key: "is_country_type", choices: ["decentralized"] }] }] }, selectable_in_lens: { sampled: 28, keys: [{ key: "always", choices: ["no"] }] } } }, discrimination_trait: { sampled: 324, keys: [{ key: "type", choices: ["language", "heritage", "tradition"] }, { key: "trait_group" }] }, discrimination_trait_group: { sampled: 89, keys: [{ key: "type", choices: ["language", "heritage"] }] }, dna: { sampled: 583, keys: [{ key: "portrait_info", block: [{ key: "genes", block: [] }] }, { key: "enabled", choices: ["yes"] }], blocks: { portrait_info: { sampled: 583, keys: [{ key: "genes", block: [{ key: "hair_color", block: [] }, { key: "skin_color", block: [] }, { key: "eye_color", block: [] }, { key: "gene_cheek_fat", block: [] }, { key: "gene_cheek_forward", block: [] }, { key: "gene_cheek_height", block: [] }, { key: "gene_cheek_prom", block: [] }, { key: "gene_cheek_width", block: [] }, { key: "gene_chin_forward", block: [] }, { key: "gene_chin_height", block: [] }, { key: "gene_chin_width", block: [] }, { key: "gene_ear_angle", block: [] }, { key: "gene_ear_inner_shape", block: [] }, { key: "gene_ear_lower_bend", block: [] }, { key: "gene_ear_out", block: [] }, { key: "gene_ear_size", block: [] }, { key: "gene_ear_upper_bend", block: [] }, { key: "gene_eye_angle", block: [] }, { key: "gene_eye_depth", block: [] }, { key: "gene_eye_distance", block: [] }, { key: "gene_eye_height", block: [] }, { key: "gene_eye_shut", block: [] }, { key: "gene_eye_corner_def", block: [] }, { key: "gene_eye_corner_depth_min", block: [] }, { key: "gene_eye_fold_droop", block: [] }, { key: "gene_eye_fold_shape", block: [] }, { key: "gene_eye_size", block: [] }, { key: "gene_eye_upper_lid_size", block: [] }, { key: "gene_forehead_angle", block: [] }, { key: "gene_forehead_brow_curve", block: [] }, { key: "gene_forehead_brow_forward", block: [] }, { key: "gene_forehead_brow_height", block: [] }, { key: "gene_forehead_brow_inner_height", block: [] }, { key: "gene_forehead_brow_outer_height", block: [] }, { key: "gene_forehead_brow_width", block: [] }, { key: "gene_forehead_height", block: [] }, { key: "gene_forehead_roundness", block: [] }, { key: "gene_forehead_width", block: [] }, { key: "gene_head_height", block: [] }, { key: "gene_head_profile", block: [] }, { key: "gene_head_top_height", block: [] }, { key: "gene_head_top_width", block: [] }, { key: "gene_head_width", block: [] }, { key: "gene_jaw_angle", block: [] }, { key: "gene_jaw_def", block: [] }, { key: "gene_jaw_forward", block: [] }, { key: "gene_jaw_height", block: [] }, { key: "gene_jaw_width", block: [] }, { key: "gene_mouth_corner_height", block: [] }, { key: "gene_mouth_forward", block: [] }, { key: "gene_mouth_height", block: [] }, { key: "gene_mouth_lower_lip_def", block: [] }, { key: "gene_mouth_lower_lip_full", block: [] }, { key: "gene_mouth_lower_lip_pads", block: [] }, { key: "gene_mouth_lower_lip_size", block: [] }, { key: "gene_mouth_lower_lip_width", block: [] }, { key: "gene_mouth_open", block: [] }, { key: "gene_mouth_philtrum_curve", block: [] }, { key: "gene_mouth_philtrum_def", block: [] }, { key: "gene_mouth_philtrum_width", block: [] }, { key: "gene_mouth_upper_lip_curve", block: [] }, { key: "gene_mouth_upper_lip_def", block: [] }, { key: "gene_mouth_upper_lip_full", block: [] }, { key: "gene_mouth_upper_lip_width", block: [] }, { key: "gene_mouth_upper_lip_size", block: [] }, { key: "gene_mouth_width", block: [] }, { key: "gene_nose_curve", block: [] }, { key: "gene_nose_forward", block: [] }, { key: "gene_nose_hawk", block: [] }, { key: "gene_nose_height", block: [] }, { key: "gene_nose_length", block: [] }, { key: "gene_nose_nostril_angle", block: [] }, { key: "gene_nose_nostril_height", block: [] }, { key: "gene_nose_nostril_width", block: [] }, { key: "gene_nose_ridge_angle", block: [] }, { key: "gene_nose_ridge_def", block: [] }, { key: "gene_nose_ridge_def_min", block: [] }, { key: "gene_nose_ridge_width", block: [] }, { key: "gene_nose_size", block: [] }, { key: "gene_nose_tip_angle", block: [] }, { key: "gene_nose_tip_forward", block: [] }, { key: "gene_nose_tip_width", block: [] }, { key: "gene_neck_length", block: [] }, { key: "gene_neck_width", block: [] }, { key: "gene_bs_body_type", block: [] }, { key: "gene_height", block: [] }, { key: "gene_age", block: [] }, { key: "gene_old_ears", block: [] }, { key: "gene_old_eyes", block: [] }, { key: "gene_old_forehead", block: [] }, { key: "gene_old_mouth", block: [] }, { key: "gene_old_nose", block: [] }, { key: "gene_complexion", block: [] }, { key: "gene_stubble", block: [] }, { key: "gene_crowfeet", block: [] }, { key: "gene_face_dacals", block: [] }, { key: "gene_frown", block: [] }, { key: "gene_surprise", block: [] }, { key: "gene_eyebrows_shape", block: [] }, { key: "gene_eyebrows_fullness", block: [] }, { key: "hairstyles", block: [] }, { key: "beards", block: [] }, { key: "mustaches", block: [] }, { key: "props", block: [] }, { key: "eye_accessory", block: [] }, { key: "eye_lashes_accessory", block: [] }, { key: "teeth_accessory", block: [] }] }] } } }, dynamic_company_name: { sampled: 10, keys: [{ key: "uses_plural_naming", choices: ["no", "yes"] }, { key: "use_for_flavored_companies", choices: ["no"] }, { key: "weight", block: [{ key: "value", choices: ["1"] }] }], blocks: { weight: { sampled: 10, keys: [{ key: "value", choices: ["1"] }] } } }, dynamic_country_map_color: { sampled: 75, keys: [{ key: "color" }, { key: "possible", block: [{ key: "exists" }] }], blocks: { possible: { sampled: 75, keys: [{ key: "exists" }] } } }, dynamic_treaty_name: { sampled: 34, keys: [{ key: "trigger", block: [{ key: "any_scope_article_option", block: [] }] }, { key: "weight", block: [{ key: "value", choices: ["10", "100", "300", "200", "10000"] }] }], blocks: { trigger: { sampled: 34, keys: [{ key: "any_scope_article_option", block: [{ key: "OR", block: [] }] }] }, weight: { sampled: 33, keys: [{ key: "value", choices: ["10", "100", "300", "200", "10000"] }] } } }, effect_localization: { sampled: 297, keys: [{ key: "global" }, { key: "third" }] }, ethnicity: { sampled: 36, keys: [{ key: "template", choices: ["ethnicity_template", "caucasian_base", "slavic", "caucasian_brown_hair", "caucasian_dark_hair", "caucasian_ginger", "indian"] }, { key: "skin_color", block: [{ key: "10", block: [] }] }, { key: "eye_color", block: [] }, { key: "hair_color", block: [] }, { key: "gene_chin_forward", block: [{ key: "84", block: [] }] }, { key: "gene_chin_height", block: [{ key: "84", block: [] }] }, { key: "gene_chin_width", block: [{ key: "64", block: [] }] }, { key: "gene_forehead_angle", block: [{ key: "64", block: [] }] }, { key: "gene_forehead_height", block: [{ key: "40", block: [] }] }, { key: "gene_head_top_width", block: [{ key: "5", block: [] }, { key: "25", block: [] }] }, { key: "gene_jaw_angle", block: [{ key: "64", block: [] }] }, { key: "gene_jaw_forward", block: [{ key: "84", block: [] }] }, { key: "gene_jaw_height", block: [{ key: "84", block: [] }] }, { key: "gene_jaw_width", block: [{ key: "64", block: [] }] }, { key: "gene_mouth_corner_height", block: [{ key: "74", block: [] }] }, { key: "gene_mouth_lower_lip_size", block: [{ key: "84", block: [] }] }, { key: "gene_mouth_upper_lip_size", block: [{ key: "84", block: [] }] }, { key: "gene_cheek_fat", block: [{ key: "64", block: [] }] }, { key: "gene_cheek_height", block: [{ key: "84", block: [] }] }, { key: "gene_cheek_width", block: [{ key: "84", block: [] }] }, { key: "gene_jaw_def", block: [{ key: "64", block: [] }] }, { key: "gene_mouth_lower_lip_def", block: [{ key: "64", block: [] }] }, { key: "gene_mouth_lower_lip_full", block: [] }, { key: "gene_mouth_lower_lip_pads", block: [{ key: "64", block: [] }] }, { key: "gene_mouth_lower_lip_width", block: [{ key: "84", block: [] }] }, { key: "gene_mouth_philtrum_curve", block: [{ key: "64", block: [] }] }, { key: "gene_mouth_philtrum_def", block: [{ key: "64", block: [] }] }, { key: "gene_mouth_philtrum_width", block: [{ key: "84", block: [] }] }, { key: "gene_mouth_upper_lip_curve", block: [{ key: "64", block: [] }] }, { key: "gene_mouth_upper_lip_def", block: [{ key: "64", block: [] }] }, { key: "gene_mouth_upper_lip_full", block: [] }, { key: "gene_nose_curve", block: [{ key: "74", block: [] }] }, { key: "gene_nose_forward", block: [{ key: "20", block: [] }] }, { key: "gene_nose_hawk", block: [{ key: "84", block: [] }] }, { key: "gene_nose_height", block: [{ key: "1", block: [] }] }, { key: "gene_nose_length", block: [{ key: "84", block: [] }] }, { key: "gene_nose_nostril_height", block: [{ key: "1", block: [] }, { key: "84", block: [] }] }, { key: "gene_nose_nostril_width", block: [] }, { key: "gene_nose_ridge_angle", block: [{ key: "1", block: [] }] }, { key: "gene_nose_ridge_def", block: [{ key: "84", block: [] }] }, { key: "gene_nose_ridge_width", block: [] }, { key: "gene_nose_size", block: [] }, { key: "gene_nose_tip_angle", block: [] }, { key: "gene_nose_tip_forward", block: [] }, { key: "gene_nose_tip_width", block: [{ key: "84", block: [] }] }, { key: "gene_bs_body_type", block: [{ key: "20", block: [] }] }, { key: "gene_height", block: [{ key: "84", block: [] }] }, { key: "gene_old_eyes", block: [{ key: "10", block: [] }] }, { key: "gene_old_forehead", block: [{ key: "10", block: [] }] }, { key: "gene_old_mouth", block: [{ key: "10", block: [] }] }, { key: "gene_complexion", block: [{ key: "10", block: [] }] }, { key: "gene_crowfeet", block: [{ key: "9", block: [] }, { key: "1", block: [] }] }, { key: "gene_eyebrows_shape", block: [{ key: "10", block: [] }, { key: "5", block: [] }] }, { key: "gene_frown", block: [{ key: "8", block: [] }, { key: "2", block: [] }] }, { key: "gene_eyebrows_fullness", block: [{ key: "10", block: [] }, { key: "15", block: [] }, { key: "5", block: [] }] }, { key: "gene_surprise", block: [{ key: "9", block: [] }, { key: "1", block: [] }] }, { key: "hairstyles", block: [{ key: "10", block: [] }] }, { key: "beards", block: [{ key: "10", block: [] }] }, { key: "mustaches", block: [{ key: "10", block: [] }] }], blocks: { beards: { sampled: 21, keys: [{ key: "10", block: [{ key: "name", choices: ["european_beards", "african_beards", "all_beards", "arabic_beards", "asian_beards", "no_beard", "frankenstein_scars", "indian_beards"] }, { key: "range", block: [] }] }] }, gene_bs_body_type: { sampled: 20, keys: [{ key: "20", block: [{ key: "name", choices: ["body_fat_head_fat_full", "body_fat_head_fat_low", "body_fat_head_fat_medium"] }, { key: "range", block: [] }] }] }, gene_cheek_fat: { sampled: 18, keys: [{ key: "64", block: [{ key: "name", choices: ["cheek_fat"] }, { key: "range", block: [] }] }] }, gene_cheek_height: { sampled: 18, keys: [{ key: "84", block: [{ key: "name", choices: ["cheek_height"] }, { key: "range", block: [] }] }] }, gene_cheek_width: { sampled: 18, keys: [{ key: "84", block: [{ key: "name", choices: ["cheek_width"] }, { key: "range", block: [] }] }] }, gene_chin_forward: { sampled: 18, keys: [{ key: "84", block: [{ key: "name", choices: ["chin_forward"] }, { key: "range", block: [] }] }] }, gene_chin_height: { sampled: 18, keys: [{ key: "84", block: [{ key: "name", choices: ["chin_height"] }, { key: "range", block: [] }] }] }, gene_chin_width: { sampled: 18, keys: [{ key: "64", block: [{ key: "name", choices: ["chin_width"] }, { key: "range", block: [] }] }] }, gene_complexion: { sampled: 20, keys: [{ key: "10", block: [{ key: "name", choices: ["complexion_01", "complexion_02", "complexion_03"] }, { key: "range", block: [] }] }] }, gene_crowfeet: { sampled: 20, keys: [{ key: "9", block: [{ key: "name", choices: ["crowfeet_01", "crowfeet_02", "crowfeet_03"] }, { key: "range", block: [] }] }, { key: "1", block: [{ key: "name", choices: ["crowfeet_01", "crowfeet_02", "crowfeet_03"] }, { key: "range", block: [] }] }] }, gene_eyebrows_fullness: { sampled: 29, keys: [{ key: "10", block: [{ key: "name", choices: ["layer_2_avg_thickness", "layer_2_low_thickness", "layer_2_lower_thickness"] }, { key: "range", block: [] }] }, { key: "15", block: [{ key: "name", choices: ["layer_2_avg_thickness", "layer_2_low_thickness", "layer_2_lower_thickness"] }, { key: "range", block: [] }] }, { key: "5", block: [{ key: "name", choices: ["layer_2_high_thickness"] }, { key: "range", block: [] }] }] }, gene_eyebrows_shape: { sampled: 29, keys: [{ key: "10", block: [{ key: "name" }, { key: "range", block: [] }] }, { key: "5", block: [{ key: "name", choices: ["avg_spacing_high_thickness", "far_spacing_high_thickness", "avg_spacing_low_thickness", "avg_spacing_lower_thickness", "close_spacing_avg_thickness", "far_spacing_lower_thickness", "avg_spacing_avg_thickness"] }, { key: "range", block: [] }] }] }, gene_forehead_angle: { sampled: 18, keys: [{ key: "64", block: [{ key: "name", choices: ["forehead_angle"] }, { key: "range", block: [] }] }] }, gene_forehead_height: { sampled: 20, keys: [{ key: "40", block: [{ key: "name", choices: ["forehead_height"] }, { key: "range", block: [] }] }] }, gene_frown: { sampled: 20, keys: [{ key: "8", block: [{ key: "name", choices: ["frown_01", "frown_02", "frown_03"] }, { key: "range", block: [] }] }, { key: "2", block: [{ key: "name", choices: ["frown_01", "frown_02", "frown_03"] }, { key: "range", block: [] }] }] }, gene_head_top_width: { sampled: 18, keys: [{ key: "5", block: [{ key: "name", choices: ["head_top_width"] }, { key: "range", block: [] }] }, { key: "25", block: [{ key: "name", choices: ["head_top_width"] }, { key: "range", block: [] }] }] }, gene_height: { sampled: 20, keys: [{ key: "84", block: [{ key: "name", choices: ["normal_height"] }, { key: "range", block: [] }] }] }, gene_jaw_angle: { sampled: 18, keys: [{ key: "64", block: [{ key: "name", choices: ["jaw_angle"] }, { key: "range", block: [] }] }] }, gene_jaw_def: { sampled: 18, keys: [{ key: "64", block: [{ key: "name", choices: ["jaw_def"] }, { key: "range", block: [] }] }] }, gene_jaw_forward: { sampled: 18, keys: [{ key: "84", block: [{ key: "name", choices: ["jaw_forward"] }, { key: "range", block: [] }] }] }, gene_jaw_height: { sampled: 18, keys: [{ key: "84", block: [{ key: "name", choices: ["jaw_height"] }, { key: "range", block: [] }] }] }, gene_jaw_width: { sampled: 18, keys: [{ key: "64", block: [{ key: "name", choices: ["jaw_width"] }, { key: "range", block: [] }] }] }, gene_mouth_corner_height: { sampled: 18, keys: [{ key: "74", block: [{ key: "name", choices: ["mouth_corner_height"] }, { key: "range", block: [] }] }] }, gene_mouth_lower_lip_def: { sampled: 18, keys: [{ key: "64", block: [{ key: "name", choices: ["mouth_lower_lip_def"] }, { key: "range", block: [] }] }] }, gene_mouth_lower_lip_pads: { sampled: 18, keys: [{ key: "64", block: [{ key: "name", choices: ["mouth_lower_lip_pads"] }, { key: "range", block: [] }] }] }, gene_mouth_lower_lip_size: { sampled: 19, keys: [{ key: "84", block: [{ key: "name", choices: ["mouth_lower_lip_size"] }, { key: "range", block: [] }] }] }, gene_mouth_lower_lip_width: { sampled: 18, keys: [{ key: "84", block: [{ key: "name", choices: ["mouth_lower_lip_width"] }, { key: "range", block: [] }] }] }, gene_mouth_philtrum_curve: { sampled: 18, keys: [{ key: "64", block: [{ key: "name", choices: ["mouth_philtrum_curve"] }, { key: "range", block: [] }] }] }, gene_mouth_philtrum_def: { sampled: 18, keys: [{ key: "64", block: [{ key: "name", choices: ["mouth_philtrum_def"] }, { key: "range", block: [] }] }] }, gene_mouth_philtrum_width: { sampled: 18, keys: [{ key: "84", block: [{ key: "name", choices: ["mouth_philtrum_width"] }, { key: "range", block: [] }] }] }, gene_mouth_upper_lip_curve: { sampled: 18, keys: [{ key: "64", block: [{ key: "name", choices: ["mouth_upper_lip_curve"] }, { key: "range", block: [] }] }] }, gene_mouth_upper_lip_def: { sampled: 18, keys: [{ key: "64", block: [{ key: "name", choices: ["mouth_upper_lip_def"] }, { key: "range", block: [] }] }] }, gene_mouth_upper_lip_size: { sampled: 19, keys: [{ key: "84", block: [{ key: "name", choices: ["mouth_upper_lip_size"] }, { key: "range", block: [] }] }] }, gene_nose_curve: { sampled: 18, keys: [{ key: "74", block: [{ key: "name", choices: ["nose_curve"] }, { key: "range", block: [] }] }] }, gene_nose_forward: { sampled: 18, keys: [{ key: "20", block: [{ key: "name", choices: ["nose_forward"] }, { key: "range", block: [] }] }] }, gene_nose_hawk: { sampled: 18, keys: [{ key: "84", block: [{ key: "name", choices: ["nose_hawk"] }, { key: "range", block: [] }] }] }, gene_nose_height: { sampled: 18, keys: [{ key: "1", block: [{ key: "name", choices: ["nose_height"] }, { key: "range", block: [] }] }] }, gene_nose_length: { sampled: 18, keys: [{ key: "84", block: [{ key: "name", choices: ["nose_length"] }, { key: "range", block: [] }] }] }, gene_nose_nostril_height: { sampled: 18, keys: [{ key: "1", block: [{ key: "name", choices: ["nose_nostril_height"] }, { key: "range", block: [] }] }, { key: "84", block: [{ key: "name", choices: ["nose_nostril_height"] }, { key: "range", block: [] }] }] }, gene_nose_ridge_angle: { sampled: 20, keys: [{ key: "1", block: [{ key: "name", choices: ["nose_ridge_angle"] }, { key: "range", block: [] }] }] }, gene_nose_ridge_def: { sampled: 20, keys: [{ key: "84", block: [{ key: "name", choices: ["nose_ridge_def"] }, { key: "range", block: [] }] }] }, gene_nose_tip_width: { sampled: 20, keys: [{ key: "84", block: [{ key: "name", choices: ["nose_tip_width"] }, { key: "range", block: [] }] }] }, gene_old_eyes: { sampled: 20, keys: [{ key: "10", block: [{ key: "name", choices: ["old_eyes_01", "old_eyes_02", "old_eyes_03"] }, { key: "range", block: [] }] }] }, gene_old_forehead: { sampled: 20, keys: [{ key: "10", block: [{ key: "name", choices: ["old_forehead_01", "old_forehead_02", "old_forehead_03"] }, { key: "range", block: [] }] }] }, gene_old_mouth: { sampled: 20, keys: [{ key: "10", block: [{ key: "name", choices: ["old_mouth_01", "old_mouth_02", "old_mouth_03"] }, { key: "range", block: [] }] }] }, gene_surprise: { sampled: 20, keys: [{ key: "9", block: [{ key: "name", choices: ["surprise_01", "surprise_02", "surprise_03"] }, { key: "range", block: [] }] }, { key: "1", block: [{ key: "name", choices: ["surprise_01", "surprise_02", "surprise_03"] }, { key: "range", block: [] }] }] }, hairstyles: { sampled: 22, keys: [{ key: "10", block: [{ key: "name" }, { key: "range", block: [] }] }] }, mustaches: { sampled: 20, keys: [{ key: "10", block: [{ key: "name", choices: ["european_mustaches", "asian_mustaches", "no_mustache", "african_mustaches", "arabic_mustaches", "indian_mustaches"] }, { key: "range", block: [] }] }] }, skin_color: { sampled: 21, keys: [{ key: "10", block: [] }] } } }, event: { sampled: 2264, keys: [{ key: "type", choices: ["country_event", "state_event"] }, { key: "placement" }, { key: "title" }, { key: "desc" }, { key: "flavor" }, { key: "duration", choices: ["3", "1", "6", "4", "2", "9"] }, { key: "event_image", block: [{ key: "video" }] }, { key: "on_created_soundeffect" }, { key: "icon" }, { key: "trigger", block: [] }, { key: "cooldown", block: [{ key: "days" }] }, { key: "immediate", block: [] }, { key: "option", block: [{ key: "name" }] }], nameFromHeader: "namespace", blocks: { cooldown: { sampled: 1403, keys: [{ key: "days" }] }, event_image: { sampled: 2156, keys: [{ key: "video" }] }, option: { sampled: 4840, keys: [{ key: "name" }] } } }, game_concept: { sampled: 613, keys: [] }, game_rule_category: { sampled: 15, keys: [{ key: "default" }] }, geographic_region: { sampled: 165, keys: [{ key: "short_key" }, { key: "state_regions", block: [] }] }, good: { sampled: 53, keys: [{ key: "texture" }, { key: "cost", placeholder: "30" }, { key: "category", choices: ["industrial", "luxury", "staple", "military"] }, { key: "prestige_factor", choices: ["4", "3", "5", "6", "10", "8", "2"] }, { key: "traded_quantity", placeholder: "5" }, { key: "convoy_cost_multiplier", choices: ["0.5", "0.15", "0.25", "1.0", "1.5", "0.05"] }] }, government_type: { sampled: 444, keys: [{ key: "transfer_of_power", choices: ["hereditary", "dictatorial", "presidential_elective", "parliamentary_elective"] }, { key: "male_ruler" }, { key: "female_ruler" }, { key: "male_heir" }, { key: "female_heir" }, { key: "possible", block: [{ key: "has_law", choices: ["law_type:law_social_monarchy", "law_type:law_monarchy", "law_type:law_colonial_administration", "law_type:law_corporate_state", "law_type:law_autocracy", "law_type:law_technocracy", "law_type:law_bakufu"] }, { key: "NOT", block: [] }, { key: "has_gov_regency", choices: ["no", "yes"] }] }, { key: "on_government_type_change", block: [{ key: "change_to_hereditary", choices: ["yes"] }] }, { key: "on_post_government_type_change", block: [{ key: "post_change_to_hereditary", choices: ["yes"] }] }], blocks: { on_government_type_change: { sampled: 443, keys: [{ key: "change_to_hereditary", choices: ["yes"] }] }, on_post_government_type_change: { sampled: 438, keys: [{ key: "post_change_to_hereditary", choices: ["yes"] }] }, possible: { sampled: 444, keys: [{ key: "has_law", choices: ["law_type:law_social_monarchy", "law_type:law_monarchy", "law_type:law_colonial_administration", "law_type:law_corporate_state", "law_type:law_autocracy", "law_type:law_technocracy", "law_type:law_bakufu"] }, { key: "NOT", block: [{ key: "is_subject_type", choices: ["subject_type_crown_land"] }] }, { key: "has_gov_regency", choices: ["no", "yes"] }] } } }, harvest_condition_type: { sampled: 22, keys: [{ key: "trigger", block: [] }, { key: "incompatible_with" }, { key: "range", block: [{ key: "integer_range", block: [] }] }, { key: "duration", block: [{ key: "fixed_range", block: [] }] }, { key: "modifier", block: [{ key: "building_group_bg_agriculture_throughput_add", choices: ["-0.05", "0.05"] }, { key: "building_group_bg_plantations_throughput_add", choices: ["-0.05", "0.05", "0.1"] }, { key: "building_group_bg_ranching_throughput_add", choices: ["-0.05", "-0.03", "0.03"] }, { key: "state_peasants_consumption_multiplier_add", choices: ["0.03", "0.025", "-0.01", "0.05"] }] }, { key: "intensity", block: [{ key: "fixed_range", block: [] }] }, { key: "chance", block: [{ key: "value", choices: ["0.0025", "0", "0.05", "0.003", "0.0008", "0.0015"] }, { key: "if", block: [] }] }, { key: "color", block: [] }, { key: "icon" }, { key: "map_texture" }, { key: "graphics" }], blocks: { chance: { sampled: 22, keys: [{ key: "value", choices: ["0.0025", "0", "0.05", "0.003", "0.0008", "0.0015"] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "multiply", choices: ["1.5", "1.2", "1.3", "1.4", "1.1", "100", "25"] }] }] }, duration: { sampled: 22, keys: [{ key: "fixed_range", block: [{ key: "min", choices: ["36", "24", "12"] }, { key: "max", choices: ["104", "48", "96", "24", "78", "94"] }] }] }, intensity: { sampled: 22, keys: [{ key: "fixed_range", block: [{ key: "min", choices: ["2", "0.5", "3", "0.25", "0.1", "1", "4"] }, { key: "max", choices: ["1", "4", "5", "6", "7", "8"] }] }] }, modifier: { sampled: 22, keys: [{ key: "building_group_bg_agriculture_throughput_add", choices: ["-0.05", "0.05"] }, { key: "building_group_bg_plantations_throughput_add", choices: ["-0.05", "0.05", "0.1"] }, { key: "building_group_bg_ranching_throughput_add", choices: ["-0.05", "-0.03", "0.03"] }, { key: "state_peasants_consumption_multiplier_add", choices: ["0.03", "0.025", "-0.01", "0.05"] }] }, range: { sampled: 22, keys: [{ key: "integer_range", block: [{ key: "min", choices: ["1"] }, { key: "max", choices: ["2", "1", "5"] }] }] } } }, ideology: { sampled: 172, keys: [{ key: "icon" }] }, interest_group_trait: { sampled: 99, keys: [{ key: "icon" }, { key: "min_approval", choices: ["loyal", "happy"] }, { key: "modifier", block: [] }] }, journal_entry: { sampled: 419, keys: [{ key: "icon" }, { key: "group" }, { key: "immediate", block: [] }, { key: "complete", block: [] }, { key: "on_complete", block: [{ key: "trigger_event", block: [] }] }, { key: "weight", placeholder: "1000" }, { key: "should_be_pinned_by_default_uninvolved_or_context", choices: ["yes", "no"] }], blocks: { on_complete: { sampled: 368, keys: [{ key: "trigger_event", block: [{ key: "id" }] }] } } }, journal_entry_group: { sampled: 27, keys: [{ key: "context", choices: ["country", "none"] }, { key: "updates_strategic_region_stances", choices: ["yes"] }] }, law: { sampled: 138, keys: [{ key: "group" }, { key: "icon" }, { key: "progressiveness", placeholder: "0" }, { key: "modifier", block: [] }, { key: "ai_enact_weight_modifier", block: [{ key: "value", choices: ["0"] }, { key: "if", block: [] }] }], blocks: { ai_enact_weight_modifier: { sampled: 136, keys: [{ key: "value", choices: ["0"] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "add", placeholder: "750" }] }] } } }, law_group: { sampled: 26, keys: [{ key: "law_group_category", choices: ["power_structure", "economy", "human_rights"] }, { key: "ideological_opinion_impact", choices: ["0.25", "0.5", "1.0", "2.0", "0.0"] }] }, map_interaction_type: { sampled: 32, keys: [{ key: "mapmode" }, { key: "clicksound" }, { key: "show_interaction_text_on_click", choices: ["yes", "no"] }] }, map_notification_type: { sampled: 23, keys: [{ key: "message" }] }, message: { sampled: 469, keys: [{ key: "type" }, { key: "texture" }, { key: "notification_type", choices: ["feed", "toast", "popup", "none"] }] }, military_formation_flag: { sampled: 30, keys: [{ key: "icon" }, { key: "type", choices: ["army", "navy"] }] }, mobilization_option: { sampled: 18, keys: [{ key: "texture" }, { key: "possible", block: [{ key: "market", block: [] }] }, { key: "on_activate_while_mobilized", block: [{ key: "custom_tooltip", block: [] }] }, { key: "on_deactivate", block: [{ key: "custom_tooltip", block: [] }] }, { key: "unlocking_technologies", block: [] }, { key: "upkeep_modifier", block: [] }, { key: "unit_modifier", block: [] }, { key: "ai_weight", block: [{ key: "value", choices: ["1", "0"] }] }, { key: "group", choices: ["supplements", "reconnaissance", "special_weapons", "supplies", "transport", "medic_support"] }], blocks: { ai_weight: { sampled: 18, keys: [{ key: "value", choices: ["1", "0"] }] }, on_activate_while_mobilized: { sampled: 11, keys: [{ key: "custom_tooltip", block: [{ key: "text", choices: ["mobilization_option_it_hurts_organization_when_you_adjust_equipment_tt", "mobilization_option_it_hurts_organization_to_add_medic_support_tt", "mobilization_option_it_hurts_morale_when_you_force_people_to_march_faster_while_in_combat_tt"] }, { key: "add_organization", block: [] }] }] }, on_deactivate: { sampled: 10, keys: [{ key: "custom_tooltip", block: [{ key: "text", choices: ["mobilization_option_it_hurts_organization_when_you_adjust_equipment_tt", "mobilization_option_it_hurts_morale_and_organization_when_you_remove_medic_support_tt"] }, { key: "add_organization", block: [] }] }] }, possible: { sampled: 17, keys: [{ key: "market", block: [] }] } } }, modifier_type: { sampled: 2362, keys: [{ key: "decimals", choices: ["1", "0", "2"] }, { key: "color", choices: ["good", "neutral", "bad"] }, { key: "game_data", block: [{ key: "ai_value", placeholder: "0" }] }], blocks: { game_data: { sampled: 2188, keys: [{ key: "ai_value", placeholder: "0" }] } } }, objective_subgoal: { sampled: 86, keys: [{ key: "category", choices: ["sgcat_tutorial", "sgcat_great_game", "sgcat_economic_dominance", "sgcat_hegemon", "sgcat_egalitarian_society"] }, { key: "is_repeatable", choices: ["no", "yes"] }, { key: "trigger", block: [] }, { key: "on_start", block: [{ key: "add_journal_entry", block: [] }] }], blocks: { on_start: { sampled: 86, keys: [{ key: "add_journal_entry", block: [{ key: "type" }, { key: "objective_subgoal" }] }] } } }, on_action: { sampled: 262, keys: [{ key: "effect", block: [] }] }, party: { sampled: 12, keys: [{ key: "color", block: [] }, { key: "name", block: [{ key: "first_valid", block: [] }] }, { key: "icon", block: [{ key: "default" }] }, { key: "unlocking_technologies", block: [] }, { key: "available_for_interest_group", block: [{ key: "owner", block: [] }] }, { key: "valid_for_country", block: [{ key: "always", choices: ["yes"] }] }, { key: "join_weight", block: [{ key: "value", block: [] }, { key: "if", block: [] }, { key: "else_if", block: [] }] }], blocks: { available_for_interest_group: { sampled: 12, keys: [{ key: "owner", block: [{ key: "country_has_voting_franchise", choices: ["yes"] }, { key: "OR", block: [] }] }] }, icon: { sampled: 12, keys: [{ key: "default" }] }, join_weight: { sampled: 12, keys: [{ key: "value", block: [{ key: "value", choices: ["-5"] }, { key: "desc", choices: ["base_value"] }] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "add", block: [] }] }, { key: "else_if", block: [{ key: "limit", block: [] }, { key: "multiply", block: [] }] }] }, name: { sampled: 12, keys: [{ key: "first_valid", block: [{ key: "triggered_desc", block: [] }] }] }, valid_for_country: { sampled: 11, keys: [{ key: "always", choices: ["yes"] }] } } }, political_lobby_appeasement: { sampled: 49, keys: [{ key: "duration_to_show", block: [{ key: "months", choices: ["60", "12"] }] }], blocks: { duration_to_show: { sampled: 49, keys: [{ key: "months", choices: ["60", "12"] }] } } }, political_movement: { sampled: 39, keys: [{ key: "category", choices: ["movement_category_ideological", "movement_category_pan_national", "movement_category_religious", "movement_category_cultural", "movement_category_cultural_majority"] }, { key: "ideology" }, { key: "character_ideologies", block: [] }, { key: "creation_trigger", block: [] }, { key: "creation_weight", block: [{ key: "value", choices: ["100", "1000", "10", "200", "50", "5000"] }, { key: "if", block: [] }] }, { key: "on_created", block: [{ key: "add_movement_enthusiasm_modifier", choices: ["yes"] }] }, { key: "disband_trigger", block: [] }, { key: "character_support_trigger", block: [{ key: "OR", block: [] }] }, { key: "character_support_weight", block: [{ key: "value", choices: ["200", "100", "50", "250"] }, { key: "if", block: [] }] }, { key: "pop_support_trigger", block: [] }, { key: "pop_support_factors", block: [] }, { key: "pop_support_weight", block: [{ key: "if", block: [] }, { key: "add", block: [] }, { key: "else_if", block: [] }, { key: "multiply", block: [] }] }, { key: "revolution", block: [{ key: "possible", block: [] }, { key: "weight", block: [] }, { key: "state_weight", block: [] }] }, { key: "secession", block: [{ key: "possible", block: [] }, { key: "weight", block: [] }, { key: "state_weight", block: [] }] }, { key: "additional_radicalism_factors", block: [{ key: "if", block: [] }] }], blocks: { additional_radicalism_factors: { sampled: 39, keys: [{ key: "if", block: [{ key: "limit", block: [] }, { key: "add", block: [] }] }] }, character_support_trigger: { sampled: 39, keys: [{ key: "OR", block: [{ key: "has_ideology" }] }] }, character_support_weight: { sampled: 39, keys: [{ key: "value", choices: ["200", "100", "50", "250"] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "multiply", block: [] }] }] }, creation_weight: { sampled: 39, keys: [{ key: "value", choices: ["100", "1000", "10", "200", "50", "5000"] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "multiply", block: [] }] }] }, on_created: { sampled: 38, keys: [{ key: "add_movement_enthusiasm_modifier", choices: ["yes"] }] }, pop_support_weight: { sampled: 39, keys: [{ key: "if", block: [{ key: "limit", block: [] }] }, { key: "add", block: [{ key: "desc", choices: ["URBAN_STATE", "POP_BASE_SUPPORT", "POP_LOYALISTS", "SLAVE_STATE"] }, { key: "value", choices: ["10", "0.5", "5", "1", "3", "9", "pop_loyalist_fraction"] }, { key: "multiply", choices: ["state.state_urbanization_rate", "10"] }] }, { key: "else_if", block: [{ key: "limit", block: [] }] }, { key: "multiply", block: [{ key: "desc", choices: ["POP_LITERACY", "POP_RADICALS", "POP_CULTURE_FERVOR", "ACCEPTANCE", "BACKWARDSNESS", "LOYALTY_OF_STATE_DAIMYO", "POP_ACCEPTANCE"] }, { key: "value" }, { key: "add", choices: ["0.10", "1", "0.2", "0.15", "0.20", "0.25", "0.50", "state.state_daimyo_loyalty"] }] }] }, revolution: { sampled: 35, keys: [{ key: "possible", block: [{ key: "political_movement_support", choices: ["0.05", "0.10"] }] }, { key: "weight", block: [{ key: "value", choices: ["100", "1000", "50"] }] }, { key: "state_weight", block: [{ key: "value", choices: ["1", "0.25", "state_urbanization_rate", "0.5", "0"] }, { key: "add", choices: ["scope:clout"] }, { key: "subtract", choices: ["this.loyalists", "state_urbanization_rate"] }, { key: "if", block: [] }] }] }, secession: { sampled: 36, keys: [{ key: "possible", block: [{ key: "political_movement_support", choices: ["0.05", "0.15"] }] }, { key: "weight", block: [{ key: "value", choices: ["1000", "50", "10", "150"] }] }, { key: "state_weight", block: [{ key: "value", choices: ["1", "state_urbanization_rate", "0", "0.25", "1.5"] }, { key: "if", block: [] }, { key: "subtract", choices: ["this.loyalists", "state_urbanization_rate"] }] }] } } }, political_movement_pop_support: { sampled: 60, keys: [{ key: "name" }] }, pop_need: { sampled: 15, keys: [{ key: "default" }, { key: "entry", block: [{ key: "goods" }, { key: "weight", placeholder: "1" }, { key: "max_supply_share", choices: ["0.75", "0.5", "1.0", "0.25", "0.9", "0.2", "0.33", "0.8"] }, { key: "min_supply_share", choices: ["0.0", "0.1", "0.10"] }] }], blocks: { entry: { sampled: 52, keys: [{ key: "goods" }, { key: "weight", placeholder: "1" }, { key: "max_supply_share", choices: ["0.75", "0.5", "1.0", "0.25", "0.9", "0.2", "0.33", "0.8"] }, { key: "min_supply_share", choices: ["0.0", "0.1", "0.10"] }] } } }, pop_type: { sampled: 15, keys: [{ key: "texture" }, { key: "color", block: [] }, { key: "start_quality_of_life", choices: ["10", "7", "20", "5", "1"] }, { key: "wage_weight", choices: ["1.5", "3", "5", "4", "0", "0.2", "1", "2"] }, { key: "paid_private_wage", choices: ["yes", "no"] }, { key: "literacy_target", choices: ["0.80", "0.20", "0.10", "0.40", "0.50", "0.60", "0.70"] }, { key: "dependent_wage", choices: ["0.5", "0.0", "0.25"] }, { key: "unemployment", choices: ["yes", "no"] }, { key: "political_engagement_base", choices: ["0.7", "0.1", "0.5", "0.20", "0.25", "0.3", "0.40"] }, { key: "political_engagement_literacy_factor", choices: ["0.3", "0.6", "0.5", "0.40", "0.7", "0.8", "1.0"] }, { key: "political_engagement_mult", block: [{ key: "value", choices: ["1", "1.0"] }, { key: "add", block: [] }] }, { key: "qualifications_growth_desc" }, { key: "qualifications", block: [{ key: "if", block: [] }, { key: "else_if", block: [] }, { key: "else", block: [] }] }, { key: "portrait_age", block: [{ key: "integer_range", block: [] }] }, { key: "portrait_pose", block: [{ key: "value", choices: ["0"] }] }, { key: "portrait_is_female", block: [{ key: "always", choices: ["yes"] }] }], blocks: { political_engagement_mult: { sampled: 15, keys: [{ key: "value", choices: ["1", "1.0"] }, { key: "add", block: [{ key: "desc", choices: ["POP_STARVATION"] }, { key: "if", block: [] }, { key: "else_if", block: [] }] }] }, portrait_age: { sampled: 15, keys: [{ key: "integer_range", block: [{ key: "min" }, { key: "max" }] }] }, portrait_is_female: { sampled: 15, keys: [{ key: "always", choices: ["yes"] }] }, portrait_pose: { sampled: 15, keys: [{ key: "value", choices: ["0"] }] }, qualifications: { sampled: 11, keys: [{ key: "if", block: [{ key: "limit", block: [] }, { key: "multiply", block: [] }] }, { key: "else_if", block: [{ key: "limit", block: [] }, { key: "value", block: [] }] }, { key: "else", block: [{ key: "value", block: [] }] }] } } }, power_bloc_map_texture: { sampled: 18, keys: [{ key: "texture" }] }, power_bloc_name: { sampled: 200, keys: [{ key: "trigger", block: [] }] }, power_bloc_principle: { sampled: 69, keys: [{ key: "icon" }, { key: "background" }, { key: "member_modifier", block: [] }, { key: "ai_weight", block: [{ key: "value", choices: ["1000", "100", "200", "0"] }] }], blocks: { ai_weight: { sampled: 69, keys: [{ key: "value", choices: ["1000", "100", "200", "0"] }] } } }, power_bloc_principle_group: { sampled: 23, keys: [{ key: "primary_for_identity", choices: ["identity_cultural", "identity_ideological_union", "identity_military_treaty_organization", "identity_religious", "identity_sovereign_empire", "identity_trade_league"] }, { key: "levels", block: [] }] }, prestige_good: { sampled: 72, keys: [{ key: "possible", block: [{ key: "has_dlc_feature", choices: ["mp1_content"] }] }, { key: "base_good" }, { key: "prestige_bonus", choices: ["0.1"] }, { key: "texture" }], blocks: { possible: { sampled: 72, keys: [{ key: "has_dlc_feature", choices: ["mp1_content"] }] } } }, production_method: { sampled: 436, keys: [{ key: "texture" }, { key: "building_modifiers", block: [{ key: "workforce_scaled", block: [] }, { key: "level_scaled", block: [] }] }], blocks: { building_modifiers: { sampled: 393, keys: [{ key: "workforce_scaled", block: [] }, { key: "level_scaled", block: [{ key: "building_employment_laborers_add", placeholder: "3000" }] }] } } }, production_method_group: { sampled: 197, keys: [{ key: "texture" }, { key: "production_methods", block: [] }] }, proposal_type: { sampled: 10, keys: [{ key: "has_dynamic_texture", choices: ["yes"] }, { key: "open_popup", choices: ["diplomatic_proposal", "diplomatic_proposal_break", "sway_offer", "diplomatic_demand", "treaty_proposal"] }, { key: "dynamic_texture_postfix", choices: ["break", "obligation", "break_obligation"] }, { key: "days" }] }, religion: { sampled: 17, keys: [{ key: "icon" }, { key: "heritage", choices: ["heritage_dharmic", "heritage_christian", "heritage_islamic", "heritage_taoic", "heritage_indigenous", "heritage_jewish", "heritage_materialist"] }, { key: "color", block: [] }] }, script_value: { sampled: 246, keys: [{ key: "value" }, { key: "if", block: [{ key: "limit", block: [] }, { key: "add" }] }], blocks: { if: { sampled: 419, keys: [{ key: "limit", block: [] }, { key: "add" }] } } }, scripted_button: { sampled: 218, keys: [{ key: "name" }, { key: "desc" }, { key: "visible", block: [] }, { key: "possible", block: [] }, { key: "effect", block: [{ key: "custom_tooltip" }] }, { key: "ai_chance", block: [{ key: "value", placeholder: "10" }] }], blocks: { ai_chance: { sampled: 209, keys: [{ key: "value", placeholder: "10" }] }, effect: { sampled: 218, keys: [{ key: "custom_tooltip" }] } } }, scripted_effect: { sampled: 721, keys: [{ key: "if", block: [{ key: "limit", block: [] }] }], blocks: { if: { sampled: 1033, keys: [{ key: "limit", block: [] }] } } }, scripted_gui: { sampled: 22, keys: [{ key: "scope", choices: ["country", "character", "political_movement", "state"] }, { key: "effect", block: [{ key: "if", block: [] }] }], blocks: { effect: { sampled: 20, keys: [{ key: "if", block: [{ key: "limit", block: [] }] }] } } }, scripted_progress_bar: { sampled: 42, keys: [{ key: "name" }, { key: "desc" }, { key: "monthly_progress", block: [{ key: "if", block: [] }] }, { key: "start_value", placeholder: "10" }, { key: "min_value", choices: ["0", "1", "-10", "-40"] }, { key: "max_value", placeholder: "100" }], blocks: { monthly_progress: { sampled: 35, keys: [{ key: "if", block: [{ key: "limit", block: [] }, { key: "add", block: [] }] }] } } }, scripted_rule: { sampled: 18, keys: [] }, scripted_trigger: { sampled: 735, keys: [] }, ship_modification: { sampled: 259, keys: [{ key: "type", choices: ["ship_mod_slot_armor", "ship_mod_slot_propulsion", "ship_mod_slot_guns", "ship_mod_slot_range", "ship_mod_slot_utility_1"] }, { key: "icon" }, { key: "clicksound" }, { key: "modifier", block: [] }, { key: "construction_goods", block: [] }, { key: "ai_weight", block: [{ key: "value", choices: ["1", "2", "3", "10", "100"] }, { key: "multiply", choices: ["ship_mod_market_multiplier", "ship_mod_coastal_defence_doctrine", "ship_mod_jeune_ecole_doctrine", "ship_mod_power_projection_doctrine", "ship_mod_merchant_navy_doctrine", "ship_mod_capital_doctrine"] }] }], blocks: { ai_weight: { sampled: 251, keys: [{ key: "value", choices: ["1", "2", "3", "10", "100"] }, { key: "multiply", choices: ["ship_mod_market_multiplier", "ship_mod_coastal_defence_doctrine", "ship_mod_jeune_ecole_doctrine", "ship_mod_power_projection_doctrine", "ship_mod_merchant_navy_doctrine", "ship_mod_capital_doctrine"] }] } } }, ship_name_definition: { sampled: 86, keys: [{ key: "name" }, { key: "selection_weight", choices: ["10", "1", "15", "3", "4", "5", "9"] }, { key: "country" }, { key: "allowed_ship_types", block: [] }, { key: "properties", block: [] }] }, ship_type: { sampled: 21, keys: [{ key: "ship_group", choices: ["ship_group_capital_ships", "ship_group_cruisers", "ship_group_torpedo_craft", "ship_group_supply_ships"] }, { key: "modification_construction_cost", placeholder: "80" }, { key: "can_be_flagship", choices: ["yes"] }, { key: "is_obsolete", block: [{ key: "has_technology_researched", choices: ["pre_dreadnought_tech", "dreadnought_tech", "ironclad_tech", "monitor_tech", "sea_lane_strategies", "battleship_tech", "carrier_tech", "destroyer"] }] }, { key: "modifier", block: [{ key: "ship_construction_progress_max_add", choices: ["25", "5"] }, { key: "ship_hit_points_max_add", placeholder: "2200" }, { key: "ship_crew_max_add", placeholder: "200" }, { key: "ship_movement_speed_add", choices: ["4.5", "5", "4", "4.0", "8", "10", "3", "3.0"] }, { key: "ship_supply_capacity_add", placeholder: "40" }, { key: "ship_hull_damage_add", placeholder: "130" }, { key: "ship_crew_damage_add", placeholder: "30" }, { key: "ship_armor_add", placeholder: "20" }, { key: "ship_critical_hit_chance_add", choices: ["0.05", "0.15", "0.1", "0.10"] }, { key: "ship_critical_hit_multiplier_add", choices: ["0.25", "0.1", "0.5"] }, { key: "ship_vulnerability_add", placeholder: "100" }, { key: "ship_screening_add", placeholder: "10" }, { key: "ship_readiness_gain_add", choices: ["10"] }, { key: "ship_accuracy_add", choices: ["5", "15", "20", "6", "7"] }, { key: "ship_visibility_add", placeholder: "10" }, { key: "ship_detection_add", placeholder: "20" }, { key: "ship_blockade_strength_add", placeholder: "100" }, { key: "ship_carrying_capacity_add", choices: ["0.15", "0.3", "2.5", "1", "1.5", "1.8", "3"] }, { key: "ship_marine_capacity_add", placeholder: "0.075" }] }, { key: "is_very_obsolete", block: [{ key: "has_technology_researched", choices: ["battleship_tech", "dreadnought_tech", "monitor_tech", "pre_dreadnought_tech", "destroyer"] }] }, { key: "construction_goods", block: [{ key: "goods_input_steel_add", placeholder: "1300" }, { key: "goods_input_engines_add", placeholder: "650" }, { key: "goods_input_iron_add", choices: ["650", "2500", "2600", "900", "2000", "325", "780"] }] }, { key: "materiel_goods", block: [{ key: "goods_input_groceries_add", choices: ["1"] }, { key: "goods_input_coal_add", choices: ["1"] }, { key: "goods_input_steel_add", choices: ["0.1"] }] }, { key: "unlocking_technologies", block: [] }, { key: "icon" }, { key: "profile_texture" }, { key: "ai_weight", choices: ["2", "1.5", "0.1"] }, { key: "modifications", block: [{ key: "ship_mod_slot_armor", block: [] }, { key: "ship_mod_slot_guns", block: [] }, { key: "ship_mod_slot_propulsion", block: [] }, { key: "ship_mod_slot_range", block: [] }, { key: "ship_mod_slot_utility_1", block: [] }] }], blocks: { construction_goods: { sampled: 21, keys: [{ key: "goods_input_steel_add", placeholder: "1300" }, { key: "goods_input_engines_add", placeholder: "650" }, { key: "goods_input_iron_add", choices: ["650", "2500", "2600", "900", "2000", "325", "780"] }] }, is_obsolete: { sampled: 14, keys: [{ key: "has_technology_researched", choices: ["pre_dreadnought_tech", "dreadnought_tech", "ironclad_tech", "monitor_tech", "sea_lane_strategies", "battleship_tech", "carrier_tech", "destroyer"] }] }, is_very_obsolete: { sampled: 11, keys: [{ key: "has_technology_researched", choices: ["battleship_tech", "dreadnought_tech", "monitor_tech", "pre_dreadnought_tech", "destroyer"] }] }, materiel_goods: { sampled: 21, keys: [{ key: "goods_input_groceries_add", choices: ["1"] }, { key: "goods_input_coal_add", choices: ["1"] }, { key: "goods_input_steel_add", choices: ["0.1"] }] }, modifications: { sampled: 21, keys: [{ key: "ship_mod_slot_armor", block: [] }, { key: "ship_mod_slot_guns", block: [] }, { key: "ship_mod_slot_propulsion", block: [] }, { key: "ship_mod_slot_range", block: [] }, { key: "ship_mod_slot_utility_1", block: [] }] }, modifier: { sampled: 21, keys: [{ key: "ship_construction_progress_max_add", choices: ["25", "5"] }, { key: "ship_hit_points_max_add", placeholder: "2200" }, { key: "ship_crew_max_add", placeholder: "200" }, { key: "ship_movement_speed_add", choices: ["4.5", "5", "4", "4.0", "8", "10", "3", "3.0"] }, { key: "ship_supply_capacity_add", placeholder: "40" }, { key: "ship_hull_damage_add", placeholder: "130" }, { key: "ship_crew_damage_add", placeholder: "30" }, { key: "ship_armor_add", placeholder: "20" }, { key: "ship_critical_hit_chance_add", choices: ["0.05", "0.15", "0.1", "0.10"] }, { key: "ship_critical_hit_multiplier_add", choices: ["0.25", "0.1", "0.5"] }, { key: "ship_vulnerability_add", placeholder: "100" }, { key: "ship_screening_add", placeholder: "10" }, { key: "ship_readiness_gain_add", choices: ["10"] }, { key: "ship_accuracy_add", choices: ["5", "15", "20", "6", "7"] }, { key: "ship_visibility_add", placeholder: "10" }, { key: "ship_detection_add", placeholder: "20" }, { key: "ship_blockade_strength_add", placeholder: "100" }, { key: "ship_carrying_capacity_add", choices: ["0.15", "0.3", "2.5", "1", "1.5", "1.8", "3"] }, { key: "ship_marine_capacity_add", placeholder: "0.075" }] } } }, social_class: { sampled: 14, keys: [{ key: "social_hierarchy", choices: ["edo_system", "british_indian_caste_system", "default_social_hierarchy"] }, { key: "strata", choices: ["lower", "middle", "upper"] }, { key: "allowed_professions", block: [] }] }, state_trait: { sampled: 239, keys: [{ key: "icon" }, { key: "modifier", block: [] }] }, static_modifier: { sampled: 6127, keys: [{ key: "icon" }] }, strait_definition: { sampled: 12, keys: [{ key: "type", choices: ["natural", "artificial"] }, { key: "total_block_requires_full_control", choices: ["no", "yes"] }, { key: "military_block_requires_full_control", choices: ["no", "yes"] }, { key: "first_land_endpoint" }, { key: "second_land_endpoint" }, { key: "first_sea_endpoint" }, { key: "second_sea_endpoint" }] }, strategic_region: { sampled: 142, keys: [{ key: "states", block: [] }] }, technology: { sampled: 179, keys: [{ key: "era", choices: ["era_3", "era_1", "era_2", "era_4", "era_5"] }, { key: "texture" }, { key: "category", choices: ["society", "military", "production"] }, { key: "modifier", block: [] }, { key: "unlocking_technologies", block: [] }, { key: "ai_weight", block: [{ key: "value", choices: ["1", "2", "1.5", "3"] }] }], blocks: { ai_weight: { sampled: 179, keys: [{ key: "value", choices: ["1", "2", "1.5", "3"] }] } } }, terrain: { sampled: 25, keys: [{ key: "label", choices: ["label_developed", "label_flat", "label_hazardous", "label_travel_harsh_environment", "label_forested", "label_water", "label_elevated"] }, { key: "weight", choices: ["2", "1", "1.5"] }, { key: "textures", block: [] }, { key: "combat_width", choices: ["1.5", "0.5", "0.8", "0.6", "0.7", "1", "0.4"] }, { key: "risk", choices: ["0.1", "0.2", "0.5", "0.15", "0.3", "0.6"] }, { key: "debug_color", block: [] }, { key: "pollution_mask_strength", choices: ["1.0", "0.1", "0.25", "0.5"] }, { key: "devastation_mask_strength", choices: ["1.0", "0.0"] }] }, terrain_manipulator: { sampled: 14, keys: [{ key: "created_terrain" }, { key: "terrain_mask", choices: ["mask_dynamic_farmland", "mask_dynamic_forestry", "mask_dynamic_mining"] }, { key: "preferred_terrain", choices: ["plains", "forest", "hills"] }, { key: "city_type", choices: ["farm", "city", "mine", "port", "wood"] }, { key: "toggle_map_object_layers", block: [] }] }, theme: { sampled: 60, keys: [{ key: "category" }, { key: "dlc", choices: ["dlc004", "dlc011", "dlc016", "dlc018", "dlc007", "dlc014", "dlc010", "dlc013"] }, { key: "bulk_selection_group", choices: ["default", "ep2", "ip2", "votp", "ap1", "ip3", "ip4"] }, { key: "map_object", block: [{ key: "pdxmesh" }, { key: "locator" }] }, { key: "dynamic_weight", block: [{ key: "value", choices: ["0", "root.gdp"] }, { key: "if", block: [] }, { key: "else_if", block: [] }] }], blocks: { dynamic_weight: { sampled: 38, keys: [{ key: "value", choices: ["0", "root.gdp"] }, { key: "if", block: [{ key: "limit", block: [] }, { key: "add", choices: ["100", "250"] }] }, { key: "else_if", block: [{ key: "limit", block: [] }, { key: "add", choices: ["10"] }] }] }, map_object: { sampled: 64, keys: [{ key: "pdxmesh" }, { key: "locator" }] } } }, treaty_article: { sampled: 34, keys: [{ key: "kind", choices: ["directed", "mutual"] }, { key: "cost", choices: ["25", "10", "0", "50", "100", "150"] }, { key: "relations_progress_per_day", choices: ["0.5", "-0.25", "1", "-0.5", "1.5"] }, { key: "icon" }, { key: "max_target_involvement", choices: ["1000", "2000", "250", "500", "3000"] }, { key: "flags", block: [] }, { key: "target_involvement_applies_to", choices: ["source_country", "target_country", "mutual"] }, { key: "usage_limit", choices: ["once_per_side", "once_per_side_with_same_inputs", "once_per_treaty"] }, { key: "maintenance_paid_by", choices: ["target_country", "source_country"] }, { key: "visible", block: [] }, { key: "unlocked_by_technologies", block: [] }, { key: "possible", block: [] }, { key: "can_ratify", block: [{ key: "custom_tooltip", block: [] }] }, { key: "on_entry_into_force", block: [] }, { key: "on_withdrawal", block: [] }, { key: "on_break", block: [] }, { key: "ai", block: [{ key: "treaty_categories", block: [] }, { key: "evaluation_chance", block: [] }, { key: "article_ai_usage", block: [] }, { key: "inherent_accept_score", block: [] }] }, { key: "wargoal", block: [{ key: "execution_priority", choices: ["21", "71"] }, { key: "contestion_type", choices: ["control_any_target_country_state", "control_any_target_incorporated_state", "control_target_country_capital", "control_target_state"] }, { key: "maneuvers", block: [] }, { key: "infamy", block: [] }] }], blocks: { ai: { sampled: 34, keys: [{ key: "treaty_categories", block: [] }, { key: "evaluation_chance", block: [{ key: "value", choices: ["0.1", "0", "0.0", "0.25", "0.01", "0.025", "0.05", "0.2"] }, { key: "if", block: [] }] }, { key: "article_ai_usage", block: [] }, { key: "inherent_accept_score", block: [{ key: "value", choices: ["0"] }, { key: "if", block: [] }, { key: "else", block: [] }] }] }, can_ratify: { sampled: 34, keys: [{ key: "custom_tooltip", block: [{ key: "text", choices: ["DUPLICATE_ARTICLE_SAME_INPUTS_TREATY", "DUPLICATE_ARTICLE_SAME_INPUTS", "STATE_TRANSFER_NOT_OWNER", "TRANSFER_SUBJECT_NOT_OVERLORD", "TREATY_SHIP_IN_BATTLE", "alliance_multilateral_alliances_first_country_tt", "alliance_multilateral_alliances_second_country_tt", "military_assistance_limit_tt"] }, { key: "NOT", block: [] }] }] }, wargoal: { sampled: 21, keys: [{ key: "execution_priority", choices: ["21", "71"] }, { key: "contestion_type", choices: ["control_any_target_country_state", "control_any_target_incorporated_state", "control_target_country_capital", "control_target_state"] }, { key: "maneuvers", block: [{ key: "value", choices: ["0", "2.5"] }, { key: "add", block: [] }] }, { key: "infamy", block: [{ key: "value", choices: ["0", "0.25"] }, { key: "add", block: [] }] }] } } }, trigger_localization: { sampled: 1672, keys: [{ key: "global" }, { key: "global_not" }, { key: "third" }] }, tutorial_lesson: { sampled: 66, keys: [{ key: "start_automatically", choices: ["no"] }, { key: "chain", choices: ["lesson_chain_journal_guides", "lesson_chain_intro", "lesson_chain_pops"] }] }, war_goal_type: { sampled: 34, keys: [{ key: "icon" }, { key: "kind" }, { key: "settings", block: [] }, { key: "execution_priority", placeholder: "12" }, { key: "contestion_type", choices: ["control_half_target_country_states", "control_all_own_states", "control_any_target_incorporated_state", "control_target_state", "control_any_target_country_state", "control_all_target_country_claims", "control_any_releasable_state", "control_own_state"] }, { key: "target_type", choices: ["country", "treaty_article", "state"] }, { key: "possible", block: [] }, { key: "valid", block: [] }, { key: "maneuvers", block: [{ key: "value", choices: ["0"] }, { key: "add", block: [] }] }, { key: "infamy", block: [{ key: "value", choices: ["0", "5"] }, { key: "add", block: [] }] }, { key: "on_enforced", block: [] }, { key: "ai", block: [{ key: "is_significant_demand", choices: ["no", "yes"] }] }], blocks: { ai: { sampled: 34, keys: [{ key: "is_significant_demand", choices: ["no", "yes"] }] }, infamy: { sampled: 34, keys: [{ key: "value", choices: ["0", "5"] }, { key: "add", block: [{ key: "desc", choices: ["INFAMY_BASE_VALUE", "INFAMY_STATE_GDP_FACTOR"] }, { key: "value", choices: ["2", "0.25", "0.5", "0.10", "5", "0.1", "scope:target_state.gdp"] }] }] }, maneuvers: { sampled: 34, keys: [{ key: "value", choices: ["0"] }, { key: "add", block: [{ key: "desc", choices: ["MANEUVERS_BASE_VALUE"] }, { key: "value", choices: ["10", "1", "20", "2.5", "30", "5", "8"] }] }] } } } } };
47651
+
47420
47652
  // src/games/vic3/index.ts
47421
47653
  var vic3Profile = {
47422
47654
  ...vic3Meta,
@@ -47425,6 +47657,7 @@ var vic3Profile = {
47425
47657
  prefixRefs: VIC3_PREFIX_REFS,
47426
47658
  blockRefFields: VIC3_BLOCK_REF_FIELDS,
47427
47659
  guiSchema: guiSchema_default2,
47660
+ skeletons: skeletons_default2.kinds,
47428
47661
  // guiTextMetrics live on the meta (the client reads them too, for the GUI
47429
47662
  // editor gate and its canvas line height).
47430
47663
  // Vic3 keeps an authored size on an EMPTY container (px_probe_c C5: the
@@ -47458,6 +47691,9 @@ var vic3Profile = {
47458
47691
  diagnosticSource: "vic3-script"
47459
47692
  };
47460
47693
 
47694
+ // data/eu5/skeletons.json
47695
+ var skeletons_default3 = { meta: { generated: "2026-09-03", sources: [], majority: 0.5 }, kinds: {} };
47696
+
47461
47697
  // src/games/eu5/scaffolds.ts
47462
47698
  var EU5_SCAFFOLDS = [
47463
47699
  {
@@ -47501,12 +47737,16 @@ var eu5Meta = {
47501
47737
  // EU5 mods carry .metadata/metadata.json (plus a required thumbnail.png);
47502
47738
  // descriptor.mod is at most a vestigial launcher artifact.
47503
47739
  descriptor: "metadata",
47504
- configDirName: ".eu5modding",
47740
+ configDirName: ".px-toolkit",
47741
+ legacyConfigDirName: ".eu5modding",
47505
47742
  docsFolderName: "Europa Universalis V",
47506
47743
  // EU5's `script_docs` console command writes to Documents/.../docs, not logs/.
47507
47744
  scriptDocsSubdir: "docs",
47508
47745
  dataTypesCommand: "dump_data_types",
47509
47746
  steamAppId: 3450310,
47747
+ // dlcIconDir deliberately absent: no live install has been checked, so the
47748
+ // Workshop panel shows this game's DLC (read from `<gameDir>/dlc/`) with the
47749
+ // folder's own thumbnail.png, and falls back to Steam when there is none.
47510
47750
  eventNamespaces: true,
47511
47751
  scaffolds: EU5_SCAFFOLDS,
47512
47752
  // uiFont and guiTextMetrics deliberately absent: neither the font file nor
@@ -48514,6 +48754,10 @@ var eu5Profile = {
48514
48754
  blockRefFields: { ...JOMINI_VARIABLE_BLOCK_REFS },
48515
48755
  // No `_*.info` docs ship with EU5, so the structures layer has no source.
48516
48756
  structureSources: {},
48757
+ // Nobody here owns EU5, so its vanilla tree has never been measured and
48758
+ // data/eu5/skeletons.json is empty. Wired anyway: a harvest run on a machine
48759
+ // that has the game turns skeletons on with no code change.
48760
+ skeletons: skeletons_default3.kinds,
48517
48761
  modifierPlaceholders: {},
48518
48762
  // No bundled wiki tokens in the preview cut, so nothing ever renders this.
48519
48763
  wikiNote: "",
@@ -48712,19 +48956,19 @@ var Parser = class {
48712
48956
  if (after.kind === "op") {
48713
48957
  return null;
48714
48958
  }
48715
- const scalar2 = this.makeScalar(this.advance());
48959
+ const scalar3 = this.makeScalar(this.advance());
48716
48960
  const next = this.peek();
48717
48961
  if (next.kind === "lbrace") {
48718
48962
  const block2 = this.parseBlock();
48719
48963
  const tb = {
48720
48964
  kind: "tagged-block",
48721
- tag: scalar2,
48965
+ tag: scalar3,
48722
48966
  block: block2,
48723
- range: { start: scalar2.range.start, end: block2.range.end }
48967
+ range: { start: scalar3.range.start, end: block2.range.end }
48724
48968
  };
48725
48969
  return tb;
48726
48970
  }
48727
- return scalar2;
48971
+ return scalar3;
48728
48972
  }
48729
48973
  return null;
48730
48974
  }
@@ -49085,7 +49329,7 @@ var STATE_BLOCKS = /* @__PURE__ */ new Set(["state", "animation", "attachanimati
49085
49329
  function buildGuiTree(text) {
49086
49330
  const result = parseScript(text);
49087
49331
  const lineIndex = new LineIndex(text);
49088
- let count = 0;
49332
+ let count2 = 0;
49089
49333
  const walk2 = (statements2) => {
49090
49334
  const nodes2 = [];
49091
49335
  let pendingDecl = null;
@@ -49123,14 +49367,14 @@ function buildGuiTree(text) {
49123
49367
  else if (ck === "using") (node.using ??= []).push(child.value.text);
49124
49368
  }
49125
49369
  node.children = walk2(block2.statements);
49126
- count++;
49370
+ count2++;
49127
49371
  nodes2.push(node);
49128
49372
  pendingDecl = null;
49129
49373
  }
49130
49374
  return nodes2;
49131
49375
  };
49132
49376
  const nodes = walk2(result.root.statements);
49133
- return { nodes, count };
49377
+ return { nodes, count: count2 };
49134
49378
  }
49135
49379
 
49136
49380
  // src/gui/textResolve.ts
@@ -49764,8 +50008,8 @@ ${stmt.value.text}`
49764
50008
  node.invisible = resolveVisibility(node.props.get("visible")?.text, ctx);
49765
50009
  if (node.itemTemplate && node.itemTemplate.children.length > 0) {
49766
50010
  markGhost(node.itemTemplate);
49767
- const count = ghostCount(node);
49768
- for (let i = 0; i < count; i++) node.children.push(node.itemTemplate);
50011
+ const count2 = ghostCount(node);
50012
+ for (let i = 0; i < count2; i++) node.children.push(node.itemTemplate);
49769
50013
  }
49770
50014
  return node;
49771
50015
  }
@@ -50951,10 +51195,10 @@ async function scanBody(file, entry, schema2, loc, playerId) {
50951
51195
  const closed = stack.pop();
50952
51196
  if (capture && stack.length === capture.path.length - 1) {
50953
51197
  const tail = line.slice(Math.max(captureStart, 0), i + 1);
50954
- const { path: path34, lines } = capture;
51198
+ const { path: path37, lines } = capture;
50955
51199
  capture = null;
50956
51200
  captureStart = -1;
50957
- keep(path34, [...lines, tail].join("\n"));
51201
+ keep2(path37, [...lines, tail].join("\n"));
50958
51202
  } else if (!capture && stack.length === 0 && registry && closed === registry.countryBlock) {
50959
51203
  finishCountries();
50960
51204
  }
@@ -50984,19 +51228,19 @@ async function scanBody(file, entry, schema2, loc, playerId) {
50984
51228
  if (def) candidate.tag = def[1];
50985
51229
  else if (mainLine.test(line)) candidate.main = true;
50986
51230
  }
50987
- function keep(path34, text) {
50988
- if (path34[0] === META_BLOCK) {
51231
+ function keep2(path37, text) {
51232
+ if (path37[0] === META_BLOCK) {
50989
51233
  out.meta = parseBlock(text);
50990
51234
  if (!registry && !record) done = true;
50991
51235
  return;
50992
51236
  }
50993
- if (record && path34[0] === record.block) {
51237
+ if (record && path37[0] === record.block) {
50994
51238
  out.character = parseBlock(text);
50995
51239
  done = true;
50996
51240
  return;
50997
51241
  }
50998
51242
  if (!registry) return;
50999
- if (path34[0] === registry.countryBlock) {
51243
+ if (path37[0] === registry.countryBlock) {
51000
51244
  const name = scalar(out.meta, schema2.nameKey ?? DEFAULT_NAME_KEY);
51001
51245
  const tag = candidate?.tag;
51002
51246
  if (tag && (loc(tag) === name || tag === name)) playerCountry = text;
@@ -51005,7 +51249,7 @@ async function scanBody(file, entry, schema2, loc, playerId) {
51005
51249
  return;
51006
51250
  }
51007
51251
  for (const link of registry.links) {
51008
- if (link.block === path34[0] && linkIds[link.slot] === path34[2]) out[link.slot] = parseBlock(text);
51252
+ if (link.block === path37[0] && linkIds[link.slot] === path37[2]) out[link.slot] = parseBlock(text);
51009
51253
  }
51010
51254
  if (registry.links.every((l) => !linkIds[l.slot] || out[l.slot])) done = true;
51011
51255
  }
@@ -51021,10 +51265,20 @@ function keyBefore(line, brace) {
51021
51265
  }
51022
51266
 
51023
51267
  // src/gui/sourceModel.ts
51268
+ var GUI_DIALECT = {
51269
+ declMarkers: DECL_MARKERS3,
51270
+ slotKeys: SLOT_KEYS,
51271
+ propertyBlocks: PROPERTY_BLOCKS2
51272
+ };
51273
+ var SCRIPT_DIALECT = {
51274
+ declMarkers: /* @__PURE__ */ new Set(),
51275
+ slotKeys: /* @__PURE__ */ new Set(),
51276
+ propertyBlocks: null
51277
+ };
51024
51278
  var LINE_BLANK = 0;
51025
51279
  var LINE_COMMENT = 1;
51026
51280
  var LINE_CODE = 2;
51027
- function parseGuiSource(text) {
51281
+ function parseGuiSource(text, dialect = GUI_DIALECT) {
51028
51282
  const parsed = parseScript(text);
51029
51283
  const lines = new LineIndex(text);
51030
51284
  const commentsByLine = /* @__PURE__ */ new Map();
@@ -51038,7 +51292,8 @@ function parseGuiSource(text) {
51038
51292
  lines,
51039
51293
  lineKind: classifyLines(text, lines, commentsByLine),
51040
51294
  commentsByLine,
51041
- entries: []
51295
+ entries: [],
51296
+ dialect
51042
51297
  };
51043
51298
  const root = buildBody(parsed.root.statements, -1, null, { start: 0, end: text.length }, null, ctx);
51044
51299
  return {
@@ -51072,7 +51327,7 @@ function buildBody(statements2, open, close, inner, owner, ctx) {
51072
51327
  let marker = null;
51073
51328
  for (const stmt of statements2) {
51074
51329
  if (stmt.kind === "value") {
51075
- marker = stmt.value.kind === "scalar" && DECL_MARKERS3.has(stmt.value.text.toLowerCase()) ? { text: stmt.value.text.toLowerCase(), span: stmt.value.range } : null;
51330
+ marker = stmt.value.kind === "scalar" && ctx.dialect.declMarkers.has(stmt.value.text.toLowerCase()) ? { text: stmt.value.text.toLowerCase(), span: stmt.value.range } : null;
51076
51331
  continue;
51077
51332
  }
51078
51333
  const own = marker;
@@ -51125,7 +51380,7 @@ function appendPoint(children, close, closeLine2, closeOwnLine, ctx) {
51125
51380
  function buildEntry(stmt, declMarker, parent, ctx) {
51126
51381
  const { text, lines } = ctx;
51127
51382
  const opSpan = operatorSpan(text, stmt);
51128
- const slot = slotForm(stmt, declMarker);
51383
+ const slot = slotForm(stmt, declMarker, ctx.dialect);
51129
51384
  const marker = slot?.marker ?? declMarker;
51130
51385
  const key = slot?.key ?? stmt.key;
51131
51386
  const value = slot?.value ?? stmt.value;
@@ -51152,7 +51407,7 @@ function buildEntry(stmt, declMarker, parent, ctx) {
51152
51407
  }
51153
51408
  const ownLine = startsLine && endsLine;
51154
51409
  const entry = {
51155
- kind: classify2(key, marker, block2),
51410
+ kind: classify2(key, marker, block2, ctx.dialect),
51156
51411
  key: key.text,
51157
51412
  keyLower: key.text.toLowerCase(),
51158
51413
  keySpan: key.range,
@@ -51202,17 +51457,18 @@ function buildEntry(stmt, declMarker, parent, ctx) {
51202
51457
  }
51203
51458
  return entry;
51204
51459
  }
51205
- function classify2(key, marker, block2) {
51460
+ function classify2(key, marker, block2, dialect) {
51206
51461
  if (marker) return "decl";
51207
51462
  if (!block2) return "property";
51463
+ if (!dialect.propertyBlocks) return "property";
51208
51464
  const lower = key.text.toLowerCase();
51209
- if (PROPERTY_BLOCKS2.has(lower) || lower.startsWith("@")) return "property";
51465
+ if (dialect.propertyBlocks.has(lower) || lower.startsWith("@")) return "property";
51210
51466
  return "widget";
51211
51467
  }
51212
- function slotForm(stmt, marker) {
51468
+ function slotForm(stmt, marker, dialect) {
51213
51469
  if (marker || stmt.value?.kind !== "tagged-block") return null;
51214
51470
  const lower = stmt.key.text.toLowerCase();
51215
- if (!SLOT_KEYS.has(lower)) return null;
51471
+ if (!dialect.slotKeys.has(lower)) return null;
51216
51472
  return {
51217
51473
  marker: { text: lower, span: stmt.key.range },
51218
51474
  key: stmt.value.tag,
@@ -53443,15 +53699,15 @@ function blockStackFromParse(parse, offset) {
53443
53699
  function blockPathFromParse(parse, offset) {
53444
53700
  const hit = nodeAtOffset(parse.root, offset);
53445
53701
  if (!hit) return [];
53446
- const path34 = [];
53702
+ const path37 = [];
53447
53703
  for (const stmt of hit.path) {
53448
53704
  const block2 = childBlock2(stmt);
53449
53705
  if (!block2) continue;
53450
53706
  if (offset <= block2.openBrace) continue;
53451
53707
  if (block2.closeBrace !== null && offset > block2.closeBrace) continue;
53452
- path34.push(stmt.kind === "assignment" ? stmt : null);
53708
+ path37.push(stmt.kind === "assignment" ? stmt : null);
53453
53709
  }
53454
- return path34;
53710
+ return path37;
53455
53711
  }
53456
53712
  function detectContextFromParse(parse, offset) {
53457
53713
  const stack = blockStackFromParse(parse, offset);
@@ -53592,7 +53848,7 @@ function valueScopeType(valueText) {
53592
53848
  return "value";
53593
53849
  }
53594
53850
  function inferScopeAt(parse, offset, model, rootScopes, savedScopes, ctx) {
53595
- const path34 = blockPathFromParse(parse, offset);
53851
+ const path37 = blockPathFromParse(parse, offset);
53596
53852
  const chain = [];
53597
53853
  const effectiveRoot = rootScopesAt(parse, offset, rootScopes, ctx);
53598
53854
  const rootSet = effectiveRoot ? new Set(effectiveRoot) : null;
@@ -53691,7 +53947,7 @@ function inferScopeAt(parse, offset, model, rootScopes, savedScopes, ctx) {
53691
53947
  }
53692
53948
  if (isScopeTransparent(lower)) return;
53693
53949
  };
53694
- for (const node of path34) {
53950
+ for (const node of path37) {
53695
53951
  apply(node && node.kind === "assignment" ? node.key.text : "<anon>", node);
53696
53952
  }
53697
53953
  return { scopes: current2, chain };
@@ -53776,8 +54032,8 @@ function fmt2(scopes) {
53776
54032
  var scriptCache = /* @__PURE__ */ new Map();
53777
54033
  var locCache = /* @__PURE__ */ new Map();
53778
54034
  function getParse(document) {
53779
- const cached = scriptCache.get(document.uri);
53780
- if (cached && cached.version === document.version) return cached;
54035
+ const cached2 = scriptCache.get(document.uri);
54036
+ if (cached2 && cached2.version === document.version) return cached2;
53781
54037
  const text = document.getText();
53782
54038
  const entry = {
53783
54039
  version: document.version,
@@ -53788,8 +54044,8 @@ function getParse(document) {
53788
54044
  return entry;
53789
54045
  }
53790
54046
  function getLocParse(document) {
53791
- const cached = locCache.get(document.uri);
53792
- if (cached && cached.version === document.version) return cached;
54047
+ const cached2 = locCache.get(document.uri);
54048
+ if (cached2 && cached2.version === document.version) return cached2;
53793
54049
  const text = document.getText();
53794
54050
  const entry = {
53795
54051
  version: document.version,
@@ -54107,8 +54363,8 @@ function inferenceContextFor(data2, entry) {
54107
54363
  var callSiteCache = /* @__PURE__ */ new WeakMap();
54108
54364
  function callSiteScopes(data2, rootScopesForFile2) {
54109
54365
  const revision = `${data2.index.revision}:${data2.refIndex.revision}`;
54110
- const cached = callSiteCache.get(data2);
54111
- if (cached && cached.revision === revision) return cached.map;
54366
+ const cached2 = callSiteCache.get(data2);
54367
+ if (cached2 && cached2.revision === revision) return cached2.map;
54112
54368
  const map = buildCallSiteScopes(data2.refIndex.chainedCalls(), data2.scopeModel, rootScopesForFile2);
54113
54369
  callSiteCache.set(data2, { revision, map });
54114
54370
  return map;
@@ -54140,8 +54396,8 @@ function buildCallSiteScopes(refs, model, rootScopesForFile2) {
54140
54396
  return map;
54141
54397
  }
54142
54398
  function variableTypes(data2, rootScopesForFile2) {
54143
- const cached = cache2.get(data2);
54144
- if (cached && cached.index === data2.index && cached.revision === data2.index.revision) return cached.info;
54399
+ const cached2 = cache2.get(data2);
54400
+ if (cached2 && cached2.index === data2.index && cached2.revision === data2.index.revision) return cached2.info;
54145
54401
  const info = buildVariableTypes(
54146
54402
  data2.index.entries(
54147
54403
  (d) => d.kind in VAR_KIND_PREFIX || d.kind in LIST_KIND_PREFIX || d.kind === "list" || d.kind === "saved_scope"
@@ -54398,18 +54654,67 @@ function blockTemplateFor(token) {
54398
54654
  }
54399
54655
  var KEY = /^[a-z_][a-z0-9_]*$/;
54400
54656
  var TRUNCATE = ["(optional)", "..."];
54657
+ var OPTIONAL_COMMENT = /^#[ \t]*optional/i;
54658
+ var WRAPPER_HEAD = /^\s*<[^>]+>\s*=\s*\{/;
54401
54659
  function extractBlockTemplate(name, usage) {
54402
54660
  if (!usage) return null;
54403
- const text = usage.replace(/\r/g, "");
54404
- if (text.includes("#")) return null;
54405
- const head = /^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\{/.exec(text);
54661
+ const stripped = stripOptionalComments(usage.replace(/\r/g, ""));
54662
+ if (!stripped) return null;
54663
+ let { text } = stripped;
54664
+ const { optionalLines } = stripped;
54665
+ const wrapper = WRAPPER_HEAD.test(text) ? unwrap(text) : text;
54666
+ if (wrapper === null) return null;
54667
+ text = wrapper;
54668
+ const head = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\{/.exec(text);
54406
54669
  if (!head || head[1] !== name) return null;
54407
54670
  const open = text.indexOf("{");
54408
54671
  const close = matchingBrace(text, open);
54409
54672
  if (close < 0 || text.slice(close + 1).trim() !== "") return null;
54410
- const body = new BodyParser(text, open + 1).parseBody();
54673
+ const parser = new BodyParser(text, open + 1, optionalLines);
54674
+ const body = parser.parseBody();
54411
54675
  if (body === null) return null;
54412
- return { snippet: render(name, body, true), plain: render(name, body, false) };
54676
+ for (const line of optionalLines) if (!parser.markedLines.has(line)) return null;
54677
+ const minimal = { snippet: render(name, body, true, false), plain: render(name, body, false, false) };
54678
+ if (!hasOptional(body)) return minimal;
54679
+ return {
54680
+ ...minimal,
54681
+ full: { snippet: render(name, body, true, true), plain: render(name, body, false, true) }
54682
+ };
54683
+ }
54684
+ function stripOptionalComments(text) {
54685
+ const lines = text.split("\n");
54686
+ const out = [];
54687
+ const optionalLines = /* @__PURE__ */ new Set();
54688
+ let wrapping = false;
54689
+ for (let i = 0; i < lines.length; i++) {
54690
+ const hash = lines[i].indexOf("#");
54691
+ if (hash < 0) {
54692
+ out.push(lines[i]);
54693
+ wrapping = false;
54694
+ continue;
54695
+ }
54696
+ const before = lines[i].slice(0, hash);
54697
+ if (wrapping && before.trim() === "") {
54698
+ out.push("");
54699
+ continue;
54700
+ }
54701
+ if (!OPTIONAL_COMMENT.test(lines[i].slice(hash))) return null;
54702
+ if (before.trim() === "") return null;
54703
+ out.push(before.trimEnd());
54704
+ optionalLines.add(i);
54705
+ wrapping = true;
54706
+ }
54707
+ return { text: out.join("\n"), optionalLines };
54708
+ }
54709
+ function hasOptional(body) {
54710
+ return body.items.some((item) => item.optional || "items" in item.value && hasOptional(item.value));
54711
+ }
54712
+ function unwrap(text) {
54713
+ const open = text.indexOf("{");
54714
+ const close = matchingBrace(text, open);
54715
+ if (close < 0 || text.slice(close + 1).trim() !== "") return null;
54716
+ const blank = (s2) => s2.replace(/[^\n]/g, " ");
54717
+ return blank(text.slice(0, open + 1)) + text.slice(open + 1, close) + blank(text.slice(close));
54413
54718
  }
54414
54719
  function matchingBrace(text, open) {
54415
54720
  let depth = 0;
@@ -54420,15 +54725,33 @@ function matchingBrace(text, open) {
54420
54725
  return -1;
54421
54726
  }
54422
54727
  var BodyParser = class {
54423
- constructor(text, i) {
54728
+ constructor(text, i, optionalLines = /* @__PURE__ */ new Set()) {
54424
54729
  this.text = text;
54425
54730
  this.i = i;
54731
+ this.optionalLines = optionalLines;
54732
+ this.lineAt = new Array(text.length);
54733
+ let line = 0;
54734
+ for (let n = 0; n < text.length; n++) {
54735
+ this.lineAt[n] = line;
54736
+ if (text[n] === "\n") line++;
54737
+ }
54426
54738
  }
54427
54739
  text;
54428
54740
  i;
54741
+ optionalLines;
54742
+ /** Lines of `optionalLines` an item actually ended on. */
54743
+ markedLines = /* @__PURE__ */ new Set();
54744
+ /** Line number per character offset, so an item can be tied to its comment. */
54745
+ lineAt;
54429
54746
  /** Items of the block whose `{` was just consumed; null rejects the example. */
54430
54747
  parseBody() {
54431
54748
  const items = [];
54749
+ const push = (item, end = this.i) => {
54750
+ const line = this.lineAt[end - 1] ?? 0;
54751
+ const optional = this.optionalLines.has(line);
54752
+ if (optional) this.markedLines.add(line);
54753
+ items.push({ ...item, optional });
54754
+ };
54432
54755
  for (; ; ) {
54433
54756
  this.skipSpace();
54434
54757
  if (this.i >= this.text.length) return null;
@@ -54444,10 +54767,11 @@ var BodyParser = class {
54444
54767
  }
54445
54768
  const first = this.readLeaf();
54446
54769
  if (!first) return null;
54770
+ const firstEnd = this.i;
54447
54771
  this.skipSpace();
54448
54772
  if (this.text[this.i] !== "=") {
54449
54773
  if (!first.placeholder) return null;
54450
- items.push({ key: null, value: first });
54774
+ push({ key: null, value: first }, firstEnd);
54451
54775
  continue;
54452
54776
  }
54453
54777
  if (first.placeholder) return null;
@@ -54458,12 +54782,12 @@ var BodyParser = class {
54458
54782
  this.i++;
54459
54783
  const nested = this.parseBody();
54460
54784
  if (nested === null) return null;
54461
- items.push({ key: first, value: nested });
54785
+ push({ key: first, value: nested });
54462
54786
  continue;
54463
54787
  }
54464
54788
  const value = this.readLeaf();
54465
54789
  if (!value) return null;
54466
- items.push({ key: first, value });
54790
+ push({ key: first, value });
54467
54791
  }
54468
54792
  }
54469
54793
  /** Start of the innermost still-open `{` at or before the cursor. */
@@ -54494,13 +54818,14 @@ var BodyParser = class {
54494
54818
  return { text: raw, placeholder: false, alts: parts.length > 1 ? parts : null };
54495
54819
  }
54496
54820
  };
54497
- function render(name, body, snippet) {
54821
+ function render(name, body, snippet, all) {
54498
54822
  return `${name} = {
54499
- ${renderBody(body, " ", snippet, { n: 0 }).join("")}}`;
54823
+ ${renderBody(body, " ", snippet, { n: 0 }, all).join("")}}`;
54500
54824
  }
54501
- function renderBody(body, indent, snippet, counter) {
54825
+ function renderBody(body, indent, snippet, counter, all) {
54502
54826
  const lines = [];
54503
54827
  for (const item of body.items) {
54828
+ if (item.optional && !all) continue;
54504
54829
  if (item.key === null) {
54505
54830
  lines.push(`${indent}${leaf(item.value, snippet, counter)}
54506
54831
  `);
@@ -54510,7 +54835,7 @@ function renderBody(body, indent, snippet, counter) {
54510
54835
  if ("items" in item.value) {
54511
54836
  lines.push(`${indent}${key} = {
54512
54837
  `);
54513
- lines.push(...renderBody(item.value, indent + " ", snippet, counter));
54838
+ lines.push(...renderBody(item.value, indent + " ", snippet, counter, all));
54514
54839
  lines.push(`${indent}}
54515
54840
  `);
54516
54841
  continue;
@@ -54531,6 +54856,110 @@ function escapeSnippet(text) {
54531
54856
  return text.replace(/[\\$}]/g, "\\$&");
54532
54857
  }
54533
54858
 
54859
+ // src/schema/skeletons.ts
54860
+ function escapeSnippet2(text) {
54861
+ return text.replace(/[\\$}]/g, "\\$&");
54862
+ }
54863
+ function leafValue(spec, snippet, c2) {
54864
+ const filled = spec.choices?.[0] ?? spec.placeholder ?? spec.key;
54865
+ if (!snippet) return filled;
54866
+ if (spec.choices && spec.choices.length > 1) return `\${${++c2.n}|${spec.choices.join(",")}|}`;
54867
+ return `\${${++c2.n}:${escapeSnippet2(filled)}}`;
54868
+ }
54869
+ function renderKeys(keys, indent, snippet, c2) {
54870
+ let out = "";
54871
+ for (const spec of keys) {
54872
+ if (spec.block) {
54873
+ out += `${indent}${spec.key} = {
54874
+ `;
54875
+ out += spec.block.length > 0 ? renderKeys(spec.block, indent + " ", snippet, c2) : `${indent} ${snippet ? `$${++c2.n}` : ""}
54876
+ `;
54877
+ out += `${indent}}
54878
+ `;
54879
+ continue;
54880
+ }
54881
+ out += `${indent}${spec.key} = ${leafValue(spec, snippet, c2)}
54882
+ `;
54883
+ }
54884
+ return out;
54885
+ }
54886
+ function renderDefinitionSkeleton(kind, skel, opts = {}) {
54887
+ const one = (snippet) => {
54888
+ const c2 = { n: 0 };
54889
+ const mirrored = skel.nameFromHeader !== void 0 && opts.headerValue === void 0;
54890
+ if (mirrored) c2.n = 1;
54891
+ const prefix = mirrored ? snippet ? `\${1:my_${escapeSnippet2(skel.nameFromHeader)}}` : `my_${skel.nameFromHeader}` : opts.headerValue;
54892
+ const out = opts.withHeader && skel.nameFromHeader ? `${skel.nameFromHeader} = ${prefix}
54893
+
54894
+ ` : "";
54895
+ const name = skel.nameFromHeader ? snippet ? `${prefix}.\${${++c2.n}:1}` : `${prefix}.1` : snippet ? `\${${++c2.n}:my_${escapeSnippet2(kind)}}` : `my_${kind}`;
54896
+ const body = skel.keys.length > 0 ? renderKeys(skel.keys, " ", snippet, c2) : ` ${snippet ? `$${++c2.n}` : ""}
54897
+ `;
54898
+ return `${out}${name} = {
54899
+ ${body}}`;
54900
+ };
54901
+ return { snippet: one(true), plain: one(false) };
54902
+ }
54903
+ function renderBlockSkeleton(name, block2) {
54904
+ const one = (snippet) => {
54905
+ const body = block2.keys.length > 0 ? renderKeys(block2.keys, " ", snippet, { n: 0 }) : ` ${snippet ? "$1" : ""}
54906
+ `;
54907
+ return `${name} = {
54908
+ ${body}}`;
54909
+ };
54910
+ return { snippet: one(true), plain: one(false) };
54911
+ }
54912
+
54913
+ // src/features/definitionSkeletons.ts
54914
+ function definitionDepth(parse, offset) {
54915
+ return blockStackFromParse(parse, offset).filter((s2) => s2 !== "<anon>").length;
54916
+ }
54917
+ function count(n) {
54918
+ return n.toLocaleString("en-US");
54919
+ }
54920
+ function headerValue(parse, key) {
54921
+ for (const stmt of parse.root.statements) {
54922
+ if (stmt.kind !== "assignment" || stmt.key.quoted || stmt.key.text !== key) continue;
54923
+ if (stmt.value?.kind === "scalar") return stmt.value.text;
54924
+ }
54925
+ return void 0;
54926
+ }
54927
+ function skeletonsAt(parse, offset, kind, skeletons) {
54928
+ const skel = skeletons?.[kind];
54929
+ if (!skel) return [];
54930
+ const depth = definitionDepth(parse, offset);
54931
+ if (depth === 0) return [definitionOffer(parse, kind, skel)];
54932
+ if (depth !== 1) return [];
54933
+ return blockOffers(kind, skel);
54934
+ }
54935
+ function skeletonsFor(parse, kind, skeletons) {
54936
+ const skel = skeletons?.[kind];
54937
+ if (!skel) return [];
54938
+ return [definitionOffer(parse, kind, skel), ...blockOffers(kind, skel)];
54939
+ }
54940
+ function definitionOffer(parse, kind, skel) {
54941
+ const existing = skel.nameFromHeader ? headerValue(parse, skel.nameFromHeader) : void 0;
54942
+ return {
54943
+ id: kind,
54944
+ label: `new ${kind.replace(/_/g, " ")}`,
54945
+ detail: `skeleton measured over ${count(skel.sampled)} vanilla definitions`,
54946
+ form: "definition",
54947
+ text: renderDefinitionSkeleton(kind, skel, {
54948
+ headerValue: existing,
54949
+ withHeader: skel.nameFromHeader !== void 0 && existing === void 0
54950
+ })
54951
+ };
54952
+ }
54953
+ function blockOffers(kind, skel) {
54954
+ return Object.entries(skel.blocks ?? {}).map(([name, block2]) => ({
54955
+ id: `${kind}.${name}`,
54956
+ label: `${name} block`,
54957
+ detail: `skeleton measured over ${count(block2.sampled)} vanilla ${name} blocks`,
54958
+ form: "block",
54959
+ text: renderBlockSkeleton(name, block2)
54960
+ }));
54961
+ }
54962
+
54534
54963
  // src/features/completion.ts
54535
54964
  var MAX_ITEMS = 1e3;
54536
54965
  function itemKind(kind) {
@@ -54544,9 +54973,9 @@ var TIER_OTHER = "4";
54544
54973
  var SRC_MOD = "0";
54545
54974
  var SRC_OTHER = "1";
54546
54975
  var SNIPPET_KINDS = /* @__PURE__ */ new Set(["scripted_effect", "scripted_trigger", "scripted_modifier"]);
54547
- function freqBucket(count) {
54548
- if (!count || count <= 0) return "99";
54549
- const b = 99 - Math.min(99, Math.round(Math.log2(count + 1) * 6));
54976
+ function freqBucket(count2) {
54977
+ if (!count2 || count2 <= 0) return "99";
54978
+ const b = 99 - Math.min(99, Math.round(Math.log2(count2 + 1) * 6));
54550
54979
  return String(Math.max(0, b)).padStart(2, "0");
54551
54980
  }
54552
54981
  function rankBucket(rank3) {
@@ -54793,7 +55222,10 @@ var CompletionFeature = class {
54793
55222
  }
54794
55223
  ranked.push(out);
54795
55224
  }
54796
- const structured = structureItems.length > 0 ? [...structureItems.filter((s2) => matchesTypedWord(wordLow, s2.label)), ...ranked] : ranked;
55225
+ const lead = [...structureItems, ...this.skeletonItems(result, offset, entry, lineSuffix)].filter(
55226
+ (s2) => matchesTypedWord(wordLow, s2.label)
55227
+ );
55228
+ const structured = lead.length > 0 ? [...lead, ...ranked] : ranked;
54797
55229
  return finalize(
54798
55230
  structured,
54799
55231
  typedWord,
@@ -54802,6 +55234,33 @@ var CompletionFeature = class {
54802
55234
  true
54803
55235
  );
54804
55236
  }
55237
+ /**
55238
+ * Definition and child-block skeletons (schema/skeletons.ts) as items of their
55239
+ * own kind, ranked right after the block's structure keys.
55240
+ *
55241
+ * Gated on a BLANK line tail: a multi-line insert must not land inside an
55242
+ * existing statement, and half a definition pasted over `= { … }` is worse
55243
+ * than no offer. That gate also keeps these items out of every rank-eval
55244
+ * sample, which always leaves the `= …` in place when it strips a key, so the
55245
+ * ranking of existing items is unchanged by construction.
55246
+ */
55247
+ skeletonItems(result, offset, entry, lineSuffix) {
55248
+ if (!entry?.kind || lineSuffix.trim() !== "") return [];
55249
+ return skeletonsAt(result, offset, entry.kind, activeProfile().skeletons).map((offer) => {
55250
+ const item = {
55251
+ label: offer.label,
55252
+ kind: import_node2.CompletionItemKind.Snippet,
55253
+ detail: offer.detail,
55254
+ // Structure tier, coldest rank: after every structure key of the block
55255
+ // and ahead of the token list. At a file's top level there are no
55256
+ // structure keys, so the skeleton leads a list that otherwise has
55257
+ // nothing to offer outside a definition body.
55258
+ sortText: TIER_STRUCTURE + rankBucket(99) + SRC_MOD + offer.label
55259
+ };
55260
+ setInsert(item, offer.text.snippet, offer.text.plain);
55261
+ return item;
55262
+ });
55263
+ }
54805
55264
  /**
54806
55265
  * Completing a scripted effect/trigger/modifier inserts a ready-to-fill
54807
55266
  * block: one `PARAM = <tabstop>` line per $PARAM$ the definition's body
@@ -54880,8 +55339,8 @@ ${plain}
54880
55339
  this.cache.clear();
54881
55340
  this.cacheRevision = this.data.index.revision;
54882
55341
  }
54883
- const cached = this.cache.get(context);
54884
- if (cached) return cached;
55342
+ const cached2 = this.cache.get(context);
55343
+ if (cached2) return cached2;
54885
55344
  const items = [];
54886
55345
  const tokens = [];
54887
55346
  const sources = [];
@@ -55428,11 +55887,11 @@ function insertMember(data2, map, name, member) {
55428
55887
  data2.count++;
55429
55888
  return;
55430
55889
  }
55431
- const keep = prev.ret !== null && member.ret === null ? prev : member;
55432
- const other = keep === prev ? member : prev;
55433
- if (!keep.desc && other.desc) keep.desc = other.desc;
55434
- if (!keep.args && other.args) keep.args = other.args;
55435
- map.set(name, keep);
55890
+ const keep2 = prev.ret !== null && member.ret === null ? prev : member;
55891
+ const other = keep2 === prev ? member : prev;
55892
+ if (!keep2.desc && other.desc) keep2.desc = other.desc;
55893
+ if (!keep2.args && other.args) keep2.args = other.args;
55894
+ map.set(name, keep2);
55436
55895
  }
55437
55896
  function dumpFilesIn(dir) {
55438
55897
  const files = [];
@@ -56113,16 +56572,16 @@ function openLiteralPrefix(expr) {
56113
56572
  }
56114
56573
  return open === -1 ? null : expr.slice(open + 1);
56115
56574
  }
56116
- function freq3(count) {
56117
- return String(999 - Math.min(998, Math.round(Math.log2(1 + count) * 55))).padStart(3, "0");
56575
+ function freq3(count2) {
56576
+ return String(999 - Math.min(998, Math.round(Math.log2(1 + count2) * 55))).padStart(3, "0");
56118
56577
  }
56119
56578
  function memberDetail(member, owner) {
56120
56579
  const args = member.args && member.args.length > 0 ? `( ${member.args.join(", ")} )` : "";
56121
56580
  const ret = member.ret ? ` \u2192 ${member.ret}` : "";
56122
56581
  return `${owner ? owner + " " : ""}${member.kind}${args}${ret}`;
56123
56582
  }
56124
- function usesSuffix(count) {
56125
- return count > 0 ? ` \xB7 ${count.toLocaleString("en-US")}\xD7 in vanilla` : "";
56583
+ function usesSuffix(count2) {
56584
+ return count2 > 0 ? ` \xB7 ${count2.toLocaleString("en-US")}\xD7 in vanilla` : "";
56126
56585
  }
56127
56586
  var SOURCE_LABEL = {
56128
56587
  dump: "your DumpDataTypes log",
@@ -56189,14 +56648,14 @@ function provideDataFnCompletion(data2, usage, linePrefix, index, cursor) {
56189
56648
  }
56190
56649
  const lits = usage.literals.get(fn);
56191
56650
  if (lits) {
56192
- for (const [value, count] of lits) {
56651
+ for (const [value, count2] of lits) {
56193
56652
  if (seen.has(value)) continue;
56194
56653
  seen.add(value);
56195
56654
  items2.push({
56196
56655
  label: value,
56197
56656
  kind: import_node3.CompletionItemKind.Value,
56198
- detail: `argument of ${fn}${usesSuffix(count)}`,
56199
- sortText: "1" + freq3(count) + value,
56657
+ detail: `argument of ${fn}${usesSuffix(count2)}`,
56658
+ sortText: "1" + freq3(count2) + value,
56200
56659
  data: { t: "dfn" }
56201
56660
  });
56202
56661
  }
@@ -56205,11 +56664,11 @@ function provideDataFnCompletion(data2, usage, linePrefix, index, cursor) {
56205
56664
  }
56206
56665
  const fmt3 = /\|([A-Za-z0-9+\-=*%.]*)$/.exec(expr);
56207
56666
  if (fmt3) {
56208
- const items2 = [...usage.formats.entries()].filter(([, count]) => count >= 3).map(([suffix, count]) => ({
56667
+ const items2 = [...usage.formats.entries()].filter(([, count2]) => count2 >= 3).map(([suffix, count2]) => ({
56209
56668
  label: suffix,
56210
56669
  kind: import_node3.CompletionItemKind.EnumMember,
56211
- detail: `format suffix${usesSuffix(count)}`,
56212
- sortText: freq3(count) + suffix,
56670
+ detail: `format suffix${usesSuffix(count2)}`,
56671
+ sortText: freq3(count2) + suffix,
56213
56672
  data: { t: "dfn" }
56214
56673
  }));
56215
56674
  return finish(items2, fmt3[1]);
@@ -56221,30 +56680,30 @@ function provideDataFnCompletion(data2, usage, linePrefix, index, cursor) {
56221
56680
  const seen = /* @__PURE__ */ new Set();
56222
56681
  for (const type of data2.types.keys()) {
56223
56682
  seen.add(type);
56224
- const count = usage.starts.get(type) ?? 0;
56683
+ const count2 = usage.starts.get(type) ?? 0;
56225
56684
  items.push({
56226
56685
  label: type,
56227
56686
  kind: import_node3.CompletionItemKind.Class,
56228
- detail: `data type${usesSuffix(count)}`,
56229
- sortText: freq3(count) + type,
56687
+ detail: `data type${usesSuffix(count2)}`,
56688
+ sortText: freq3(count2) + type,
56230
56689
  data: { t: "dfn" }
56231
56690
  });
56232
56691
  }
56233
56692
  for (const [name, member] of data2.globals) {
56234
56693
  if (seen.has(name)) continue;
56235
56694
  seen.add(name);
56236
- const count = usage.starts.get(name) ?? 0;
56695
+ const count2 = usage.starts.get(name) ?? 0;
56237
56696
  const doc = member.desc ?? describeDataFn(name, member);
56238
56697
  items.push({
56239
56698
  label: name,
56240
56699
  kind: member.kind === "promote" ? import_node3.CompletionItemKind.Variable : import_node3.CompletionItemKind.Function,
56241
- detail: `global ${memberDetail(member)}${usesSuffix(count)}`,
56700
+ detail: `global ${memberDetail(member)}${usesSuffix(count2)}`,
56242
56701
  ...doc ? { documentation: doc } : {},
56243
- sortText: freq3(count) + name,
56702
+ sortText: freq3(count2) + name,
56244
56703
  data: { t: "dfn" }
56245
56704
  });
56246
56705
  }
56247
- for (const [name, count] of usage.starts) {
56706
+ for (const [name, count2] of usage.starts) {
56248
56707
  if (seen.has(name)) continue;
56249
56708
  const called = usage.argCounts.has(name);
56250
56709
  const hasMembers = usage.pairs.has(name);
@@ -56252,9 +56711,9 @@ function provideDataFnCompletion(data2, usage, linePrefix, index, cursor) {
56252
56711
  items.push({
56253
56712
  label: name,
56254
56713
  kind: hasMembers && !called ? import_node3.CompletionItemKind.Class : import_node3.CompletionItemKind.Function,
56255
- detail: `vanilla usage${usesSuffix(count)} (not in the data-type tables)`,
56714
+ detail: `vanilla usage${usesSuffix(count2)} (not in the data-type tables)`,
56256
56715
  ...doc ? { documentation: doc } : {},
56257
- sortText: freq3(count) + name,
56716
+ sortText: freq3(count2) + name,
56258
56717
  data: { t: "dfn" }
56259
56718
  });
56260
56719
  }
@@ -56279,15 +56738,15 @@ function provideDataFnCompletion(data2, usage, linePrefix, index, cursor) {
56279
56738
  });
56280
56739
  }
56281
56740
  const harvested = usage.pairs.get(ownerType) ?? (pairOwner ? usage.pairs.get(pairOwner) : void 0);
56282
- for (const [name, count] of harvested ?? []) {
56741
+ for (const [name, count2] of harvested ?? []) {
56283
56742
  if (seen.has(name)) continue;
56284
56743
  const doc = describeDataFn(name, null);
56285
56744
  items.push({
56286
56745
  label: name,
56287
56746
  kind: import_node3.CompletionItemKind.Method,
56288
- detail: `vanilla usage on ${pairOwner ?? ownerType}${usesSuffix(count)}`,
56747
+ detail: `vanilla usage on ${pairOwner ?? ownerType}${usesSuffix(count2)}`,
56289
56748
  ...doc ? { documentation: doc } : {},
56290
- sortText: freq3(count) + name,
56749
+ sortText: freq3(count2) + name,
56291
56750
  data: { t: "dfn" }
56292
56751
  });
56293
56752
  }
@@ -56295,25 +56754,25 @@ function provideDataFnCompletion(data2, usage, linePrefix, index, cursor) {
56295
56754
  }
56296
56755
  const harvestedPairs = pairOwner ? usage.pairs.get(pairOwner) : void 0;
56297
56756
  if (harvestedPairs && harvestedPairs.size > 0) {
56298
- for (const [name, count] of harvestedPairs) {
56757
+ for (const [name, count2] of harvestedPairs) {
56299
56758
  const doc = describeDataFn(name, null);
56300
56759
  items.push({
56301
56760
  label: name,
56302
56761
  kind: import_node3.CompletionItemKind.Method,
56303
- detail: `vanilla usage on ${pairOwner}${usesSuffix(count)}`,
56762
+ detail: `vanilla usage on ${pairOwner}${usesSuffix(count2)}`,
56304
56763
  ...doc ? { documentation: doc } : {},
56305
- sortText: freq3(count) + name,
56764
+ sortText: freq3(count2) + name,
56306
56765
  data: { t: "dfn" }
56307
56766
  });
56308
56767
  }
56309
56768
  return finish(items, typed);
56310
56769
  }
56311
- for (const [name, count] of usage.memberPool) {
56770
+ for (const [name, count2] of usage.memberPool) {
56312
56771
  items.push({
56313
56772
  label: name,
56314
56773
  kind: import_node3.CompletionItemKind.Method,
56315
- detail: `vanilla usage${usesSuffix(count)} (chain not resolved)`,
56316
- sortText: freq3(count) + name,
56774
+ detail: `vanilla usage${usesSuffix(count2)} (chain not resolved)`,
56775
+ sortText: freq3(count2) + name,
56317
56776
  data: { t: "dfn" }
56318
56777
  });
56319
56778
  }
@@ -56551,10 +57010,10 @@ function provideDataFnSignature(data2, usage, lineText, character) {
56551
57010
  if (!arities || arities.size === 0) return null;
56552
57011
  let best = 0;
56553
57012
  let bestCount = -1;
56554
- for (const [arity, count] of arities) {
56555
- if (count > bestCount) {
57013
+ for (const [arity, count2] of arities) {
57014
+ if (count2 > bestCount) {
56556
57015
  best = arity;
56557
- bestCount = count;
57016
+ bestCount = count2;
56558
57017
  }
56559
57018
  }
56560
57019
  if (best === 0) return null;
@@ -56821,13 +57280,13 @@ function provideGuiCompletion(data2, document, offset, settings2) {
56821
57280
  const typeInfo = enclosing ? guiSchema().types[enclosing] : null;
56822
57281
  const props = typeInfo?.props ?? (enclosing ? guiSchema().globalProps : {});
56823
57282
  const ranked = Object.entries(props).sort((a, b) => b[1] - a[1]);
56824
- ranked.forEach(([key, count], i) => {
57283
+ ranked.forEach(([key, count2], i) => {
56825
57284
  seen.add(key);
56826
57285
  const alsoType = key in guiSchema().types;
56827
57286
  items.push({
56828
57287
  label: key,
56829
57288
  kind: itemKind(alsoType ? "gui_type" : "gui_property"),
56830
- detail: typeInfo ? `${alsoType ? "child widget" : "property"} of ${enclosing} \xB7 ${count}\xD7 in vanilla` : `gui ${alsoType ? "widget" : "property"} \xB7 ${count}\xD7 in vanilla`,
57289
+ detail: typeInfo ? `${alsoType ? "child widget" : "property"} of ${enclosing} \xB7 ${count2}\xD7 in vanilla` : `gui ${alsoType ? "widget" : "property"} \xB7 ${count2}\xD7 in vanilla`,
56831
57290
  sortText: TIER_PROP + rank2(i) + key
56832
57291
  });
56833
57292
  });
@@ -57020,14 +57479,14 @@ function computeEventDetail(data2, schema2, id) {
57020
57479
  for (const child of block2.statements) {
57021
57480
  if (child.kind !== "assignment") continue;
57022
57481
  const key = child.key.text.toLowerCase();
57023
- const scalar2 = child.value?.kind === "scalar" ? child.value.text : null;
57482
+ const scalar3 = child.value?.kind === "scalar" ? child.value.text : null;
57024
57483
  const sub = childBlock4(child);
57025
- if (key === "type" && scalar2) detail2.type = scalar2;
57026
- else if (key === "hidden" && scalar2) detail2.hidden = scalar2 === "yes";
57027
- else if (key === "theme" && scalar2) detail2.theme = scalar2;
57028
- else if (key === "title") detail2.title = scalar2 ? locField(data2, scalar2) : { key: "", dynamic: true };
57029
- else if (key === "desc") detail2.desc = scalar2 ? locField(data2, scalar2) : { key: "", dynamic: true };
57030
- else if (key === "flavor") detail2.flavor = scalar2 ? locField(data2, scalar2) : { key: "", dynamic: true };
57484
+ if (key === "type" && scalar3) detail2.type = scalar3;
57485
+ else if (key === "hidden" && scalar3) detail2.hidden = scalar3 === "yes";
57486
+ else if (key === "theme" && scalar3) detail2.theme = scalar3;
57487
+ else if (key === "title") detail2.title = scalar3 ? locField(data2, scalar3) : { key: "", dynamic: true };
57488
+ else if (key === "desc") detail2.desc = scalar3 ? locField(data2, scalar3) : { key: "", dynamic: true };
57489
+ else if (key === "flavor") detail2.flavor = scalar3 ? locField(data2, scalar3) : { key: "", dynamic: true };
57031
57490
  else if (SECTION_KEYS.has(key) && sub)
57032
57491
  detail2.sections.push(section(data2, schema2, child.key.text, sub, lineOf));
57033
57492
  else if (key === "option" && sub) detail2.options.push(option(data2, schema2, sub, lineOf));
@@ -57484,7 +57943,9 @@ function buildExampleWikiIndex(src) {
57484
57943
  }
57485
57944
  }
57486
57945
  entries.sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));
57487
- const sources = [`Triggers, effects, event targets and modifiers come from ${src.tokenSource}.`];
57946
+ const sources = [
57947
+ `Triggers, effects, event targets and modifiers come from ${src.tokenSource.replace(/\.$/, "")}.`
57948
+ ];
57488
57949
  sources.push(
57489
57950
  src.dataTypes.source === "data_types.log" ? "Datafunctions and data types come from your own DumpDataTypes output." : "Datafunctions and data types come from the bundled wiki tables. Run DumpDataTypes in the game console for the list your game version really has."
57490
57951
  );
@@ -57521,11 +57982,11 @@ function memberProvenance(member) {
57521
57982
  function usageBlock(token) {
57522
57983
  return token.usage && token.usage.trim() !== "" ? token.usage : void 0;
57523
57984
  }
57524
- function emptyDetail(name, kind, count) {
57985
+ function emptyDetail(name, kind, count2) {
57525
57986
  return {
57526
57987
  name,
57527
57988
  kind,
57528
- count,
57989
+ count: count2,
57529
57990
  doc: "",
57530
57991
  scopes: [],
57531
57992
  literals: [],
@@ -57661,8 +58122,8 @@ function dataFnDetail(src, name, kind, lines) {
57661
58122
  const short2 = owner ? name.slice(dot + 1) : name;
57662
58123
  const member = owner ? membersOf(src.dataTypes, owner)?.get(short2) : src.dataTypes.globals.get(short2);
57663
58124
  if (!member) return null;
57664
- const count = owner ? memberCount(src.usage, owner, short2) : src.usage.starts.get(short2) ?? 0;
57665
- const detail2 = emptyDetail(name, kind, count);
58125
+ const count2 = owner ? memberCount(src.usage, owner, short2) : src.usage.starts.get(short2) ?? 0;
58126
+ const detail2 = emptyDetail(name, kind, count2);
57666
58127
  if (owner) detail2.owner = owner;
57667
58128
  detail2.doc = member.desc ?? "";
57668
58129
  if (member.ret) detail2.ret = member.ret;
@@ -57860,12 +58321,14 @@ var SYNTAX_LINE = /^(?:<[^>]+>|[A-Za-z_][A-Za-z0-9_]*)\s*(?:[<>]=?|!=|=)/;
57860
58321
  var TAG_LINE = /^Tag:\s*([A-Za-z0-9_.$]+)\s*(?:,\s*(.*))?$/;
57861
58322
  var SCOPE_LINE = /^(Supported [Ss]copes|Input [Ss]copes|Output [Ss]copes):\s*(.*)$/;
57862
58323
  var META_LINE = /^(Supported [Tt]argets|Targets?|Traits|Categories|Use [Aa]reas|Requires [Dd]ata|Wild[ _]?[Cc]ard|Global [Ll]ink):\s*(.*)$/;
58324
+ var braceDelta = (s2) => (s2.match(/\{/g) ?? []).length - (s2.match(/\}/g) ?? []).length;
57863
58325
  function parseLog(content, kind) {
57864
58326
  const tokens = [];
57865
58327
  const seen = /* @__PURE__ */ new Set();
57866
58328
  const lines = content.split(/\r?\n/);
57867
58329
  let current2 = null;
57868
58330
  let inUsage = false;
58331
+ let openBraces = 0;
57869
58332
  const flush = () => {
57870
58333
  if (current2 && current2.name && !seen.has(current2.name)) {
57871
58334
  current2.doc = current2.doc.trim();
@@ -57877,6 +58340,7 @@ function parseLog(content, kind) {
57877
58340
  }
57878
58341
  current2 = null;
57879
58342
  inUsage = false;
58343
+ openBraces = 0;
57880
58344
  };
57881
58345
  for (const rawLine of lines) {
57882
58346
  const line = rawLine.trimEnd();
@@ -57886,7 +58350,7 @@ function parseLog(content, kind) {
57886
58350
  }
57887
58351
  const trimmed = line.trim();
57888
58352
  if (trimmed === "") {
57889
- if (current2 && inUsage && current2.usage) current2.usage += "\n";
58353
+ if (current2 && (inUsage || openBraces > 0) && current2.usage) current2.usage += "\n";
57890
58354
  continue;
57891
58355
  }
57892
58356
  if (trimmed.startsWith("Tag:")) {
@@ -57914,6 +58378,15 @@ function parseLog(content, kind) {
57914
58378
  }
57915
58379
  continue;
57916
58380
  }
58381
+ if (openBraces > 0) {
58382
+ if (applyMetaLine(current2, trimmed)) {
58383
+ openBraces = 0;
58384
+ continue;
58385
+ }
58386
+ current2.usage += "\n" + line;
58387
+ openBraces += braceDelta(line);
58388
+ continue;
58389
+ }
57917
58390
  if (applyMetaLine(current2, trimmed)) {
57918
58391
  inUsage = false;
57919
58392
  continue;
@@ -57928,6 +58401,7 @@ function parseLog(content, kind) {
57928
58401
  }
57929
58402
  if (current2.usage === void 0 && SYNTAX_LINE.test(trimmed)) {
57930
58403
  current2.usage = trimmed;
58404
+ openBraces = Math.max(0, braceDelta(trimmed));
57931
58405
  continue;
57932
58406
  }
57933
58407
  current2.doc = current2.doc === "" ? trimmed : current2.doc + "\n" + trimmed;
@@ -57957,7 +58431,6 @@ var MD_BOLD_LABEL = /^\*\*([^*]+)\*\*\s*:/;
57957
58431
  var MASKED_NAME = /^([A-Za-z0-9_.$]+):\s*$/;
57958
58432
  var MASKED_FIELD = /^(Mask|Name|Description):\s*(.*)$/;
57959
58433
  var SECTION_BANNER = /^-{3,}/;
57960
- var braceDelta = (s2) => (s2.match(/\{/g) ?? []).length - (s2.match(/\}/g) ?? []).length;
57961
58434
  function parseMarkdownLog(content, kind) {
57962
58435
  const tokens = [];
57963
58436
  const seen = /* @__PURE__ */ new Set();
@@ -58127,13 +58600,13 @@ function loadTokenData(logsDir, cacheFile, forceReparse = false) {
58127
58600
  return result;
58128
58601
  };
58129
58602
  if (forceReparse) return fresh();
58130
- let cached;
58603
+ let cached2;
58131
58604
  try {
58132
- cached = JSON.parse(fs12.readFileSync(cacheFile, "utf8"));
58605
+ cached2 = JSON.parse(fs12.readFileSync(cacheFile, "utf8"));
58133
58606
  } catch {
58134
58607
  return fresh();
58135
58608
  }
58136
- if (cached.cacheFormat !== DOCS_CACHE_FORMAT || !cached.tokens || !cached.templates || !cached.mtimes)
58609
+ if (cached2.cacheFormat !== DOCS_CACHE_FORMAT || !cached2.tokens || !cached2.templates || !cached2.mtimes)
58137
58610
  return fresh();
58138
58611
  const currentMtimes = {};
58139
58612
  const missing = [];
@@ -58144,14 +58617,14 @@ function loadTokenData(logsDir, cacheFile, forceReparse = false) {
58144
58617
  missing.push(file);
58145
58618
  }
58146
58619
  }
58147
- const cachedKeys = Object.keys(cached.mtimes).sort().join(",");
58620
+ const cachedKeys = Object.keys(cached2.mtimes).sort().join(",");
58148
58621
  const currentKeys = Object.keys(currentMtimes).sort().join(",");
58149
- const same = cachedKeys === currentKeys && Object.entries(currentMtimes).every(([f, t]) => cached.mtimes[f] === t);
58622
+ const same = cachedKeys === currentKeys && Object.entries(currentMtimes).every(([f, t]) => cached2.mtimes[f] === t);
58150
58623
  if (!same) return fresh();
58151
58624
  return {
58152
- tokens: cached.tokens,
58153
- templates: cached.templates,
58154
- mtimes: cached.mtimes,
58625
+ tokens: cached2.tokens,
58626
+ templates: cached2.templates,
58627
+ mtimes: cached2.mtimes,
58155
58628
  missing,
58156
58629
  fromCache: true
58157
58630
  };
@@ -58676,18 +59149,18 @@ function scanRoots(gamePath, locLanguage) {
58676
59149
  }
58677
59150
  var CACHE_VERSION = 1;
58678
59151
  function usageStamp(gamePath, locLanguage) {
58679
- let count = 0;
59152
+ let count2 = 0;
58680
59153
  let size = 0;
58681
59154
  for (const [root, ext] of scanRoots(gamePath, locLanguage)) {
58682
59155
  for (const file of listFiles(root, ext)) {
58683
- count++;
59156
+ count2++;
58684
59157
  try {
58685
59158
  size += fs16.statSync(file).size;
58686
59159
  } catch {
58687
59160
  }
58688
59161
  }
58689
59162
  }
58690
- return `${CACHE_VERSION}:${gamePath}:${locLanguage}:${count}:${size}`;
59163
+ return `${CACHE_VERSION}:${gamePath}:${locLanguage}:${count2}:${size}`;
58691
59164
  }
58692
59165
  function toObj(map) {
58693
59166
  return Object.fromEntries(map);
@@ -58714,29 +59187,29 @@ function serialize(usage, stamp) {
58714
59187
  exprs: usage.exprs
58715
59188
  };
58716
59189
  }
58717
- function deserialize(cache5) {
59190
+ function deserialize(cache6) {
58718
59191
  return {
58719
- starts: toMap(cache5.starts),
58720
- pairs: new Map(Object.entries(cache5.pairs ?? {}).map(([k, v]) => [k, toMap(v)])),
58721
- memberPool: toMap(cache5.memberPool),
59192
+ starts: toMap(cache6.starts),
59193
+ pairs: new Map(Object.entries(cache6.pairs ?? {}).map(([k, v]) => [k, toMap(v)])),
59194
+ memberPool: toMap(cache6.memberPool),
58722
59195
  argCounts: new Map(
58723
- Object.entries(cache5.argCounts ?? {}).map(([k, v]) => [
59196
+ Object.entries(cache6.argCounts ?? {}).map(([k, v]) => [
58724
59197
  k,
58725
59198
  new Map(Object.entries(v).map(([n, c2]) => [Number(n), c2]))
58726
59199
  ])
58727
59200
  ),
58728
- literals: new Map(Object.entries(cache5.literals ?? {}).map(([k, v]) => [k, toMap(v)])),
58729
- formats: toMap(cache5.formats),
58730
- examples: toMap(cache5.examples),
58731
- files: cache5.files ?? 0,
58732
- exprs: cache5.exprs ?? 0
59201
+ literals: new Map(Object.entries(cache6.literals ?? {}).map(([k, v]) => [k, toMap(v)])),
59202
+ formats: toMap(cache6.formats),
59203
+ examples: toMap(cache6.examples),
59204
+ files: cache6.files ?? 0,
59205
+ exprs: cache6.exprs ?? 0
58733
59206
  };
58734
59207
  }
58735
59208
  async function loadDataFnUsageAsync(gamePath, locLanguage, cacheFile, force = false) {
58736
59209
  if (!gamePath) return { usage: emptyUsage(), fromCache: false };
58737
59210
  const stamp = usageStamp(gamePath, locLanguage);
58738
- const cached = readCache(cacheFile, stamp, force);
58739
- if (cached) return { usage: cached, fromCache: true };
59211
+ const cached2 = readCache(cacheFile, stamp, force);
59212
+ if (cached2) return { usage: cached2, fromCache: true };
58740
59213
  const usage = emptyUsage();
58741
59214
  const yieldNow3 = () => new Promise((resolve2) => setImmediate(resolve2));
58742
59215
  for (const [root, ext] of scanRoots(gamePath, locLanguage)) {
@@ -58751,8 +59224,8 @@ async function loadDataFnUsageAsync(gamePath, locLanguage, cacheFile, force = fa
58751
59224
  function readCache(cacheFile, stamp, force) {
58752
59225
  if (!cacheFile || force) return null;
58753
59226
  try {
58754
- const cache5 = JSON.parse(fs16.readFileSync(cacheFile, "utf8"));
58755
- if (cache5.version === CACHE_VERSION && cache5.stamp === stamp) return deserialize(cache5);
59227
+ const cache6 = JSON.parse(fs16.readFileSync(cacheFile, "utf8"));
59228
+ if (cache6.version === CACHE_VERSION && cache6.stamp === stamp) return deserialize(cache6);
58756
59229
  } catch {
58757
59230
  }
58758
59231
  return null;
@@ -59457,16 +59930,16 @@ function extractReferencesParsed(root, lines, file, source, schema2, isEngineTok
59457
59930
  if (chain !== void 0) ref.chain = chain;
59458
59931
  references.push(ref);
59459
59932
  };
59460
- const scanValueScalar = (scalar2) => {
59461
- if (scalar2.quoted) return;
59462
- const text = scalar2.text;
59933
+ const scanValueScalar = (scalar3) => {
59934
+ if (scalar3.quoted) return;
59935
+ const text = scalar3.text;
59463
59936
  const m = PREFIX_VALUE.exec(text);
59464
59937
  if (!m) return;
59465
59938
  const prefix = m[1];
59466
59939
  let name = m[2];
59467
59940
  const dot = name.indexOf(".");
59468
59941
  if (dot >= 0) name = name.slice(0, dot);
59469
- const offset = scalar2.range.start + prefix.length + 1;
59942
+ const offset = scalar3.range.start + prefix.length + 1;
59470
59943
  if (VAR_PREFIXES.has(prefix)) {
59471
59944
  pushRef(name, VAR_PREFIX_KINDS[prefix], offset);
59472
59945
  } else if (prefix === "scope") {
@@ -60202,8 +60675,20 @@ var ModOriginResolver = class {
60202
60675
  };
60203
60676
 
60204
60677
  // src/schema/loader.ts
60678
+ var fs23 = __toESM(require("fs"));
60679
+ var path26 = __toESM(require("path"));
60680
+
60681
+ // ../protocol/src/configDir.ts
60205
60682
  var fs22 = __toESM(require("fs"));
60206
60683
  var path25 = __toESM(require("path"));
60684
+ function resolveConfigDir(root, names) {
60685
+ const current2 = path25.join(root, names.configDirName);
60686
+ if (!names.legacyConfigDirName || fs22.existsSync(current2)) return current2;
60687
+ const legacy = path25.join(root, names.legacyConfigDirName);
60688
+ return fs22.existsSync(legacy) ? legacy : current2;
60689
+ }
60690
+
60691
+ // src/schema/loader.ts
60207
60692
  function buildStructureIndex(profile, entries) {
60208
60693
  const keysByKindBlock = /* @__PURE__ */ new Map();
60209
60694
  const ambientByKind = /* @__PURE__ */ new Map();
@@ -60240,13 +60725,13 @@ function loadSchema(modPath, log2) {
60240
60725
  const prefixRefs = { ...profile.prefixRefs };
60241
60726
  const roots = modPath === null ? [] : Array.isArray(modPath) ? modPath : [modPath];
60242
60727
  for (const root of roots) {
60243
- const overlayFile = path25.join(root, profile.configDirName, "schema.json");
60728
+ const overlayFile = path26.join(resolveConfigDir(root, profile), "schema.json");
60244
60729
  try {
60245
- if (!fs22.existsSync(overlayFile)) continue;
60246
- const overlay = JSON.parse(fs22.readFileSync(overlayFile, "utf8"));
60730
+ if (!fs23.existsSync(overlayFile)) continue;
60731
+ const overlay = JSON.parse(fs23.readFileSync(overlayFile, "utf8"));
60247
60732
  for (const e of overlay.entries ?? []) {
60248
60733
  if (typeof e?.path !== "string" || typeof e?.kind !== "string") continue;
60249
- if (path25.isAbsolute(e.path) || e.path.includes("\\") || e.path.split("/").some((s2) => s2 === ".." || s2 === "")) {
60734
+ if (path26.isAbsolute(e.path) || e.path.includes("\\") || e.path.split("/").some((s2) => s2 === ".." || s2 === "")) {
60250
60735
  log2?.(`schema overlay entry ignored (unsafe path): ${e.path}`);
60251
60736
  continue;
60252
60737
  }
@@ -60457,16 +60942,16 @@ var ServerData = class {
60457
60942
 
60458
60943
  // src/features/hover.ts
60459
60944
  var import_node6 = __toESM(require_main3());
60460
- var path26 = __toESM(require("path"));
60945
+ var path27 = __toESM(require("path"));
60461
60946
 
60462
60947
  // src/features/definitionBody.ts
60463
- var fs23 = __toESM(require("fs"));
60948
+ var fs24 = __toESM(require("fs"));
60464
60949
  var CACHE_MAX = 32;
60465
60950
  var cache3 = /* @__PURE__ */ new Map();
60466
60951
  function entryFor(file) {
60467
60952
  let stat;
60468
60953
  try {
60469
- stat = fs23.statSync(file);
60954
+ stat = fs24.statSync(file);
60470
60955
  } catch {
60471
60956
  return null;
60472
60957
  }
@@ -60474,7 +60959,7 @@ function entryFor(file) {
60474
60959
  if (hit && hit.mtimeMs === stat.mtimeMs) return hit;
60475
60960
  let text;
60476
60961
  try {
60477
- text = fs23.readFileSync(file, "utf8");
60962
+ text = fs24.readFileSync(file, "utf8");
60478
60963
  } catch {
60479
60964
  return null;
60480
60965
  }
@@ -60780,7 +61265,7 @@ function definitionCard(data2, def, at, withRefs = true) {
60780
61265
  return card;
60781
61266
  }
60782
61267
  function provenance2(def) {
60783
- return fileLink(def.file, `${path26.basename(def.file)}:${def.line + 1}`, {
61268
+ return fileLink(def.file, `${path27.basename(def.file)}:${def.line + 1}`, {
60784
61269
  fragment: String(def.line + 1)
60785
61270
  });
60786
61271
  }
@@ -60803,14 +61288,14 @@ function savedScopeCard(data2, document, name, rootScopes, entry) {
60803
61288
  const doc = [];
60804
61289
  if (ambient) doc.push(`${ambient.doc}${entry ? ` *(${entry.kind.replace(/_/g, " ")})*` : ""}`);
60805
61290
  const site = firstSaveSite(document, name);
60806
- if (site !== null) doc.push(`Saved in this file: ${path26.basename(URIToPath(document.uri))}:${site + 1}`);
61291
+ if (site !== null) doc.push(`Saved in this file: ${path27.basename(URIToPath(document.uri))}:${site + 1}`);
60807
61292
  else if (ambient) doc.push(`Engine-provided (not saved in this file).`);
60808
61293
  else if (!inferred) {
60809
61294
  const sites = data2.index.lookup(name).filter((d) => d.kind === "saved_scope").slice(0, 3);
60810
61295
  if (sites.length > 0) {
60811
61296
  doc.push(
60812
61297
  sites.map(
60813
- (d) => `saved in ${fileLink(d.file, `${path26.basename(d.file)}:${d.line + 1}`, { fragment: `L${d.line + 1}` })}`
61298
+ (d) => `saved in ${fileLink(d.file, `${path27.basename(d.file)}:${d.line + 1}`, { fragment: `L${d.line + 1}` })}`
60814
61299
  ).join(" \n")
60815
61300
  );
60816
61301
  } else {
@@ -60905,7 +61390,7 @@ function relationTriggerCard(data2, word) {
60905
61390
  kind: m[1] === "has" ? "trigger" : "effect",
60906
61391
  name: word,
60907
61392
  headTail: `\xB7 generated from \`${m[2]}\``,
60908
- doc: `${verb} the scripted relation \`${m[2]}\` (${path26.basename(def.file)}:${def.line + 1}) with the target character.`
61393
+ doc: `${verb} the scripted relation \`${m[2]}\` (${path27.basename(def.file)}:${def.line + 1}) with the target character.`
60909
61394
  };
60910
61395
  }
60911
61396
  function templatedModifierCard(data2, word) {
@@ -60975,7 +61460,7 @@ function definesCard(data2, namespace, name) {
60975
61460
  function defineSourceLink(e) {
60976
61461
  const norm2 = e.file.replace(/\\/g, "/");
60977
61462
  const i = norm2.toLowerCase().lastIndexOf("/common/defines/");
60978
- const rel = i >= 0 ? `${norm2.slice(i + 1)}:${e.line + 1}` : `${path26.basename(e.file)}:${e.line + 1}`;
61463
+ const rel = i >= 0 ? `${norm2.slice(i + 1)}:${e.line + 1}` : `${path27.basename(e.file)}:${e.line + 1}`;
60979
61464
  return `${e.layer} \xB7 ${fileLink(e.file, rel, { fragment: String(e.line + 1) })}`;
60980
61465
  }
60981
61466
  function scopeWordCard(word) {
@@ -61036,14 +61521,14 @@ function scopeInference(data2, document, position, rootScopes, entry) {
61036
61521
  );
61037
61522
  }
61038
61523
  function URIToPath(uri) {
61039
- return uri.replace(/^file:\/\/\/?/, "").replace(/\//g, path26.sep);
61524
+ return uri.replace(/^file:\/\/\/?/, "").replace(/\//g, path27.sep);
61040
61525
  }
61041
61526
 
61042
61527
  // src/features/calendarDates.ts
61043
61528
  var import_node7 = __toESM(require_main3());
61044
61529
 
61045
61530
  // ../protocol/src/calendar.ts
61046
- var DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
61531
+ var ENGINE_MONTH_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
61047
61532
  var GREGORIAN_MONTHS = [
61048
61533
  "January",
61049
61534
  "February",
@@ -61057,8 +61542,8 @@ var GREGORIAN_MONTHS = [
61057
61542
  "October",
61058
61543
  "November",
61059
61544
  "December"
61060
- ].map((name, i) => ({ name, days: DAYS[i] }));
61061
- function monthsOf(cal) {
61545
+ ];
61546
+ function monthNames(cal) {
61062
61547
  return cal.months ?? GREGORIAN_MONTHS;
61063
61548
  }
61064
61549
  function sanitizeCalendar(raw) {
@@ -61072,36 +61557,34 @@ function sanitizeCalendar(raw) {
61072
61557
  if (typeof o.before === "string" && o.before.trim() !== "") cal.before = o.before.trim();
61073
61558
  if (cal.before && cal.before.toLowerCase() === cal.after.toLowerCase()) return void 0;
61074
61559
  if (Array.isArray(o.months) && o.months.length > 0) {
61560
+ if (o.months.length !== GREGORIAN_MONTHS.length) return void 0;
61075
61561
  const months = [];
61076
61562
  const seen = /* @__PURE__ */ new Set();
61077
61563
  for (const m of o.months) {
61078
- if (typeof m !== "object" || m === null) return void 0;
61079
- const { name, days } = m;
61564
+ const name = typeof m === "string" ? m : m?.name ?? null;
61080
61565
  if (typeof name !== "string" || name.trim() === "") return void 0;
61081
- if (typeof days !== "number" || !Number.isInteger(days) || days < 1 || days > 999) return void 0;
61082
61566
  const key = name.trim().toLowerCase();
61083
61567
  if (seen.has(key)) return void 0;
61084
61568
  seen.add(key);
61085
- months.push({ name: name.trim(), days });
61569
+ months.push(name.trim());
61086
61570
  }
61087
61571
  cal.months = months;
61088
61572
  }
61089
61573
  return cal;
61090
61574
  }
61091
- function isValidScriptDate(cal, y, m, d) {
61092
- const months = monthsOf(cal);
61093
- return y >= 1 && m >= 1 && m <= months.length && d >= 1 && d <= months[m - 1].days;
61575
+ function isValidScriptDate(y, m, d) {
61576
+ return y >= 1 && m >= 1 && m <= ENGINE_MONTH_DAYS.length && d >= 1 && d <= ENGINE_MONTH_DAYS[m - 1];
61094
61577
  }
61095
61578
  function displayYear(cal, y) {
61096
61579
  if (y >= cal.epoch) return `${y - cal.epoch + 1} ${cal.after}`;
61097
61580
  return cal.before ? `${cal.epoch - y} ${cal.before}` : null;
61098
61581
  }
61099
61582
  function displayDate(cal, y, m, d) {
61100
- if (!isValidScriptDate(cal, y, m, d)) return null;
61583
+ if (!isValidScriptDate(y, m, d)) return null;
61101
61584
  const year = displayYear(cal, y);
61102
61585
  if (!year) return null;
61103
61586
  if (m === 1 && d === 1) return year;
61104
- return `${d} ${monthsOf(cal)[m - 1].name} ${year}`;
61587
+ return `${d} ${monthNames(cal)[m - 1]} ${year}`;
61105
61588
  }
61106
61589
 
61107
61590
  // src/features/calendarDates.ts
@@ -61120,7 +61603,7 @@ function dateTokensOnLine(cal, lineText) {
61120
61603
  const quotesBefore = (code.slice(0, match.index).match(/"/g) ?? []).length;
61121
61604
  if (quotesBefore % 2 === 1) continue;
61122
61605
  const [y, m, d] = [Number(match[1]), Number(match[2]), Number(match[3])];
61123
- if (!isValidScriptDate(cal, y, m, d)) continue;
61606
+ if (!isValidScriptDate(y, m, d)) continue;
61124
61607
  tokens.push({ start: match.index, end: match.index + match[0].length, y, m, d });
61125
61608
  }
61126
61609
  return tokens;
@@ -61142,7 +61625,7 @@ function calendarHints(cal, document, range) {
61142
61625
  }
61143
61626
  return hints;
61144
61627
  }
61145
- function provideDateHover(cal, document, position) {
61628
+ function provideDateHover(cal, document, position, source = "px.calendar") {
61146
61629
  if (!cal || !isScriptLanguage(document.languageId)) return null;
61147
61630
  const lineText = getLineText(document, position.line);
61148
61631
  const token = dateTokensOnLine(cal, lineText).find(
@@ -61155,7 +61638,7 @@ function provideDateHover(cal, document, position) {
61155
61638
  const eras = cal.before ? `${cal.before} / ${cal.after}` : cal.after;
61156
61639
  const value = `\`${script}\` \u2192 **${display}**
61157
61640
 
61158
- *px.calendar: epoch ${cal.epoch} (${eras}), ${monthsOf(cal).length} months*`;
61641
+ *${source}: epoch ${cal.epoch} (${eras})${cal.months ? ", the mod's month names" : ""}*`;
61159
61642
  return {
61160
61643
  contents: { kind: import_node7.MarkupKind.Markdown, value },
61161
61644
  range: {
@@ -61165,10 +61648,47 @@ function provideDateHover(cal, document, position) {
61165
61648
  };
61166
61649
  }
61167
61650
 
61651
+ // ../protocol/src/calendarFile.ts
61652
+ var fs25 = __toESM(require("fs"));
61653
+ var path28 = __toESM(require("path"));
61654
+ var CALENDAR_FILE = "calendar.json";
61655
+ function calendarFilePath(modRoot, names) {
61656
+ return path28.join(resolveConfigDir(modRoot, names), CALENDAR_FILE);
61657
+ }
61658
+ function readCalendarFile(modRoot, names) {
61659
+ const file = calendarFilePath(modRoot, names);
61660
+ let text;
61661
+ try {
61662
+ text = fs25.readFileSync(file, "utf8");
61663
+ } catch {
61664
+ return null;
61665
+ }
61666
+ let raw;
61667
+ try {
61668
+ raw = JSON.parse(text.replace(/^\uFEFF/, ""));
61669
+ } catch (err) {
61670
+ return { file, error: `not valid JSON (${err.message})` };
61671
+ }
61672
+ const calendar = sanitizeCalendar(raw);
61673
+ if (!calendar) {
61674
+ return {
61675
+ file,
61676
+ error: 'not a usable calendar: needs a whole-number "epoch" (1 or more), a non-empty "after" era label, a "before" label different from "after" when present, and, when "months" is given, exactly twelve distinct names'
61677
+ };
61678
+ }
61679
+ return { file, calendar };
61680
+ }
61681
+ function isCalendarFile(fsPath, names) {
61682
+ const parts = fsPath.split(/[\\/]/);
61683
+ if (parts.length < 2 || parts[parts.length - 1].toLowerCase() !== CALENDAR_FILE) return false;
61684
+ const dir = parts[parts.length - 2].toLowerCase();
61685
+ return dir === names.configDirName.toLowerCase() || dir === names.legacyConfigDirName?.toLowerCase();
61686
+ }
61687
+
61168
61688
  // src/features/textureHover.ts
61169
61689
  var import_node8 = __toESM(require_main3());
61170
- var fs24 = __toESM(require("fs"));
61171
- var path27 = __toESM(require("path"));
61690
+ var fs26 = __toESM(require("fs"));
61691
+ var path29 = __toESM(require("path"));
61172
61692
 
61173
61693
  // src/dds/png.ts
61174
61694
  var import_node_zlib = require("node:zlib");
@@ -61291,7 +61811,7 @@ var cache4 = /* @__PURE__ */ new Map();
61291
61811
  function cachedPreview(fsPath) {
61292
61812
  let stat;
61293
61813
  try {
61294
- stat = fs24.statSync(fsPath);
61814
+ stat = fs26.statSync(fsPath);
61295
61815
  } catch {
61296
61816
  return null;
61297
61817
  }
@@ -61299,7 +61819,7 @@ function cachedPreview(fsPath) {
61299
61819
  if (cache4.has(key)) return cache4.get(key) ?? null;
61300
61820
  let entry = null;
61301
61821
  try {
61302
- const buf = fs24.readFileSync(fsPath);
61822
+ const buf = fs26.readFileSync(fsPath);
61303
61823
  const info = ddsFormatInfo(buf);
61304
61824
  if (info) {
61305
61825
  let uri;
@@ -61344,16 +61864,16 @@ function provideTextureHover(settings2, document, position, entryKind) {
61344
61864
  let resolved = null;
61345
61865
  for (const { root, label } of candidates) {
61346
61866
  if (!root) continue;
61347
- const full = path27.join(root, ...rel.split("/"));
61348
- if (fs24.existsSync(full)) {
61867
+ const full = path29.join(root, ...rel.split("/"));
61868
+ if (fs26.existsSync(full)) {
61349
61869
  resolved = { fsPath: full, label };
61350
61870
  break;
61351
61871
  }
61352
61872
  }
61353
61873
  if (!resolved) {
61354
- const docDir = path27.dirname(URI2.parse(document.uri).fsPath);
61355
- const full = path27.join(docDir, ...rel.split("/"));
61356
- if (fs24.existsSync(full)) resolved = { fsPath: full, label: "relative" };
61874
+ const docDir = path29.dirname(URI2.parse(document.uri).fsPath);
61875
+ const full = path29.join(docDir, ...rel.split("/"));
61876
+ if (fs26.existsSync(full)) resolved = { fsPath: full, label: "relative" };
61357
61877
  }
61358
61878
  if (!resolved && !rel.includes("/")) {
61359
61879
  const keyMatch = /([A-Za-z_][A-Za-z0-9_]*)\s*=\s*"?$/.exec(lineText.slice(0, hit.start));
@@ -61361,8 +61881,8 @@ function provideTextureHover(settings2, document, position, entryKind) {
61361
61881
  if (dirs) {
61362
61882
  outer: for (const { root, label } of assetRoots(settings2)) {
61363
61883
  for (const dir of dirs) {
61364
- const full = path27.join(root, ...dir.split("/"), rel);
61365
- if (fs24.existsSync(full)) {
61884
+ const full = path29.join(root, ...dir.split("/"), rel);
61885
+ if (fs26.existsSync(full)) {
61366
61886
  resolved = { fsPath: full, label };
61367
61887
  break outer;
61368
61888
  }
@@ -61380,7 +61900,7 @@ function provideTextureHover(settings2, document, position, entryKind) {
61380
61900
  const card = {
61381
61901
  kind: "texture",
61382
61902
  badgeLabel: "texture",
61383
- name: path27.basename(rel),
61903
+ name: path29.basename(rel),
61384
61904
  headTail: `\xB7 ${resolved.label}`,
61385
61905
  provenance: open
61386
61906
  };
@@ -61490,16 +62010,16 @@ function provideSemanticTokens(data2, document, refFields, entry, structures) {
61490
62010
  const { result, lineIndex } = getParse(document);
61491
62011
  const builder = new import_node9.SemanticTokensBuilder();
61492
62012
  const byBlock = entry?.kind && structures ? structures.keysByKindBlock.get(entry.kind) : void 0;
61493
- const pushScalar = (scalar2, expectedKinds) => {
61494
- if (scalar2.quoted) return;
61495
- let word = scalar2.text;
62013
+ const pushScalar = (scalar3, expectedKinds) => {
62014
+ if (scalar3.quoted) return;
62015
+ let word = scalar3.text;
61496
62016
  if (!IDENTIFIER_START.test(word)) return;
61497
62017
  const colon = word.indexOf(":");
61498
62018
  if (colon >= 0) word = word.slice(0, colon);
61499
62019
  if (word === "") return;
61500
62020
  const classified = classify3(data2, word, colon >= 0 ? void 0 : expectedKinds);
61501
62021
  if (!classified) return;
61502
- const pos = lineIndex.positionAt(scalar2.range.start);
62022
+ const pos = lineIndex.positionAt(scalar3.range.start);
61503
62023
  builder.push(pos.line, pos.character, word.length, classified.type, classified.modifiers);
61504
62024
  };
61505
62025
  const fieldKinds = (key, form) => {
@@ -61519,9 +62039,9 @@ function provideSemanticTokens(data2, document, refFields, entry, structures) {
61519
62039
  if (!spec?.values?.startsWith("enum:")) return false;
61520
62040
  return spec.values.slice(5).split("|").includes(value.text);
61521
62041
  };
61522
- const pushAs = (scalar2, type, modifiers) => {
61523
- const pos = lineIndex.positionAt(scalar2.range.start);
61524
- builder.push(pos.line, pos.character, scalar2.text.length, TYPE_INDEX[type], modifiers);
62042
+ const pushAs = (scalar3, type, modifiers) => {
62043
+ const pos = lineIndex.positionAt(scalar3.range.start);
62044
+ builder.push(pos.line, pos.character, scalar3.text.length, TYPE_INDEX[type], modifiers);
61525
62045
  };
61526
62046
  walkStatements(result.root, (stmt, ancestors) => {
61527
62047
  if (stmt.kind === "assignment") {
@@ -61575,7 +62095,7 @@ function classify3(data2, word, expectedKinds) {
61575
62095
 
61576
62096
  // src/features/inlayHints.ts
61577
62097
  var import_node10 = __toESM(require_main3());
61578
- var path28 = __toESM(require("path"));
62098
+ var path30 = __toESM(require("path"));
61579
62099
 
61580
62100
  // ../protocol/src/locRefs.ts
61581
62101
  var PROP_VALUE = /([A-Za-z_][A-Za-z0-9_.-]*)\s*=\s*("?)([A-Za-z_][A-Za-z0-9_.-]*)\2/g;
@@ -61623,11 +62143,11 @@ var HINT_MAX_LEN = 60;
61623
62143
  function truncate(text, max) {
61624
62144
  return text.length > max ? text.slice(0, max - 1) + "\u2026" : text;
61625
62145
  }
61626
- function provideInlayHints(data2, settings2, document, range, rootScopes, entry) {
62146
+ function provideInlayHints(data2, settings2, document, range, rootScopes, entry, calendar = settings2.calendar) {
61627
62147
  if (document.languageId === "paradox-loc") return translationOverlayHints(data2, settings2, document, range);
61628
62148
  const hints = locPreviewHints(data2, document, range);
61629
62149
  if (settings2.scopeInlayHints) hints.push(...scopeHints(data2, document, range, rootScopes, entry));
61630
- if (settings2.calendar) hints.push(...calendarHints(settings2.calendar, document, range));
62150
+ if (calendar) hints.push(...calendarHints(calendar, document, range));
61631
62151
  return hints;
61632
62152
  }
61633
62153
  function scopeHints(data2, document, range, rootScopes, entry) {
@@ -61676,7 +62196,7 @@ function locPreviewHints(data2, document, range) {
61676
62196
  kind: import_node10.MarkupKind.Markdown,
61677
62197
  value: `${def.value ?? ""}
61678
62198
 
61679
- *${path28.basename(def.file)}:${def.line + 1} (${def.source})*`
62199
+ *${path30.basename(def.file)}:${def.line + 1} (${def.source})*`
61680
62200
  }
61681
62201
  });
61682
62202
  } else if (ref.strictness === "strict") {
@@ -61711,7 +62231,7 @@ function translationOverlayHints(data2, settings2, document, range) {
61711
62231
  kind: import_node10.MarkupKind.Markdown,
61712
62232
  value: `**${settings2.locLanguage}**: ${def.value}
61713
62233
 
61714
- *${path28.basename(def.file)}:${def.line + 1} (${def.source})*`
62234
+ *${path30.basename(def.file)}:${def.line + 1} (${def.source})*`
61715
62235
  }
61716
62236
  });
61717
62237
  }
@@ -61751,8 +62271,8 @@ function list(scopes) {
61751
62271
 
61752
62272
  // src/features/codeActions.ts
61753
62273
  var import_node11 = __toESM(require_main3());
61754
- var fs25 = __toESM(require("fs"));
61755
- var path29 = __toESM(require("path"));
62274
+ var fs27 = __toESM(require("fs"));
62275
+ var path31 = __toESM(require("path"));
61756
62276
  function locKeyRefAt(lineText, character) {
61757
62277
  const refs = findLocKeyRefs(lineText);
61758
62278
  return refs.find((r) => character >= r.start - 1 && character <= r.end + 1) ?? refs[0] ?? null;
@@ -61762,12 +62282,12 @@ function locCreateEdit(key, docFsPath, ctx) {
61762
62282
  if (!modRoot) return null;
61763
62283
  const lang = ctx.locLanguage;
61764
62284
  const locRoot = ctx.locRoots[0] ?? "localization";
61765
- const target = path29.join(modRoot, locRoot, lang, `zzz_px_lsp_edits_l_${lang}.yml`);
62285
+ const target = path31.join(modRoot, locRoot, lang, `zzz_px_lsp_edits_l_${lang}.yml`);
61766
62286
  const uri = URI2.file(target).toString();
61767
62287
  const entryLine = ` ${key}: ""`;
61768
62288
  let content = null;
61769
62289
  try {
61770
- content = fs25.readFileSync(target, "utf8");
62290
+ content = fs27.readFileSync(target, "utf8");
61771
62291
  } catch {
61772
62292
  content = null;
61773
62293
  }
@@ -62527,9 +63047,9 @@ function computeLocDiagnostics(loc, lines, ctx) {
62527
63047
  }
62528
63048
 
62529
63049
  // src/features/references.ts
62530
- var path30 = __toESM(require("path"));
63050
+ var path32 = __toESM(require("path"));
62531
63051
  function siteKey(file, line) {
62532
- const n = path30.normalize(file);
63052
+ const n = path32.normalize(file);
62533
63053
  return `${process.platform === "win32" ? n.toLowerCase() : n}|${line}`;
62534
63054
  }
62535
63055
  async function provideReferences(data2, document, position, includeDeclaration, lazyRefs2) {
@@ -62568,7 +63088,7 @@ function stripPrefix(word) {
62568
63088
 
62569
63089
  // src/features/rename.ts
62570
63090
  var import_node16 = __toESM(require_main3());
62571
- var fs26 = __toESM(require("fs"));
63091
+ var fs28 = __toESM(require("fs"));
62572
63092
 
62573
63093
  // ../protocol/src/regex.ts
62574
63094
  function escapeRegExp(literal) {
@@ -62650,7 +63170,7 @@ function readLine(def, readOpenDocument) {
62650
63170
  const open = readOpenDocument(uri);
62651
63171
  if (open) return getLineText(open, def.line);
62652
63172
  try {
62653
- const lines = fs26.readFileSync(def.file, "utf8").replace(/^/, "").split(/\r?\n/);
63173
+ const lines = fs28.readFileSync(def.file, "utf8").replace(/^/, "").split(/\r?\n/);
62654
63174
  return lines[def.line] ?? null;
62655
63175
  } catch {
62656
63176
  return null;
@@ -62699,6 +63219,52 @@ function provideWorkspaceSymbols(data2, query) {
62699
63219
  }));
62700
63220
  }
62701
63221
 
63222
+ // src/features/snippetList.ts
63223
+ var MAX_TOKENS = 60;
63224
+ function buildSnippetList(parse, offset, kind, skeletons, tokens, counts) {
63225
+ const out = kind ? skeletonsFor(parse, kind, skeletons).map((offer) => ({
63226
+ id: offer.id,
63227
+ label: offer.label,
63228
+ detail: offer.detail,
63229
+ form: offer.form,
63230
+ snippet: offer.text.snippet,
63231
+ plain: offer.text.plain
63232
+ })) : [];
63233
+ const { context } = detectContextFromParse(parse, offset);
63234
+ const seen = /* @__PURE__ */ new Set();
63235
+ const candidates = [];
63236
+ for (const token of tokens) {
63237
+ if (context === "trigger" && (token.kind === "effect" || token.kind === "modifier")) continue;
63238
+ if (context === "effect" && (token.kind === "trigger" || token.kind === "modifier")) continue;
63239
+ if (seen.has(token.name)) continue;
63240
+ if (!blockTemplateFor(token)) continue;
63241
+ seen.add(token.name);
63242
+ candidates.push({ token, count: counts[token.name] ?? 0 });
63243
+ }
63244
+ candidates.sort((a, b) => b.count - a.count || (a.token.name < b.token.name ? -1 : 1));
63245
+ for (const { token } of candidates.slice(0, MAX_TOKENS)) {
63246
+ const template = blockTemplateFor(token);
63247
+ out.push({
63248
+ id: token.name,
63249
+ label: token.name,
63250
+ detail: `${token.kind} block, from the game's own usage example`,
63251
+ form: "token",
63252
+ snippet: template.snippet,
63253
+ plain: template.plain
63254
+ });
63255
+ if (template.full)
63256
+ out.push({
63257
+ id: `${token.name}.full`,
63258
+ label: `${token.name} (all fields)`,
63259
+ detail: `${token.kind} block with its optional fields, from the game's own usage example`,
63260
+ form: "token",
63261
+ snippet: template.full.snippet,
63262
+ plain: template.full.plain
63263
+ });
63264
+ }
63265
+ return out;
63266
+ }
63267
+
62702
63268
  // ../protocol/src/suppression.ts
62703
63269
  function globMatch(pattern, filePath) {
62704
63270
  const p = pattern.replace(/\\/g, "/").toLowerCase();
@@ -62815,8 +63381,8 @@ function computeModOverview(data2, inFocus = () => true) {
62815
63381
  }
62816
63382
 
62817
63383
  // src/overview/locCoverage.ts
62818
- var path31 = __toESM(require("path"));
62819
- var fs27 = __toESM(require("fs"));
63384
+ var path33 = __toESM(require("path"));
63385
+ var fs29 = __toESM(require("fs"));
62820
63386
  var ISSUE_CAP = 500;
62821
63387
  function locRoots(schemaEntries) {
62822
63388
  const roots = schemaEntries.filter((e) => e.kind === "loc_key").map((e) => e.path);
@@ -62825,13 +63391,13 @@ function locRoots(schemaEntries) {
62825
63391
  function modLocByLanguage(modPath, schemaEntries) {
62826
63392
  const byLang = /* @__PURE__ */ new Map();
62827
63393
  for (const root of locRoots(schemaEntries)) {
62828
- const locDir = path31.join(modPath, root);
63394
+ const locDir = path33.join(modPath, root);
62829
63395
  for (const file of listFiles(locDir, ".yml")) {
62830
63396
  const lang = detectLocFileLanguage(file);
62831
63397
  if (!lang) continue;
62832
63398
  let content;
62833
63399
  try {
62834
- content = fs27.readFileSync(file, "utf8");
63400
+ content = fs29.readFileSync(file, "utf8");
62835
63401
  } catch {
62836
63402
  continue;
62837
63403
  }
@@ -62962,7 +63528,7 @@ function computeOverrides(data2, gamePath, inFocus = () => true) {
62962
63528
  }
62963
63529
 
62964
63530
  // src/overview/eventGraph.ts
62965
- var fs28 = __toESM(require("fs"));
63531
+ var fs30 = __toESM(require("fs"));
62966
63532
  var DEFAULT_MAX_NODES = 400;
62967
63533
  var GRAPH_KINDS = /* @__PURE__ */ new Set(["event", "on_action", "decision"]);
62968
63534
  var MAX_SUGGESTIONS = 2e3;
@@ -62998,7 +63564,7 @@ function labelEdges(data2, edges, sites) {
62998
63564
  const key = file.toLowerCase();
62999
63565
  if (!parses.has(key)) {
63000
63566
  try {
63001
- const text = decode(fs28.readFileSync(file)).text;
63567
+ const text = decode(fs30.readFileSync(file)).text;
63002
63568
  parses.set(key, { result: parseScript(text), li: new LineIndex(text) });
63003
63569
  } catch {
63004
63570
  parses.set(key, null);
@@ -63215,6 +63781,19 @@ function computeEventGraph(data2, params, inFocus = () => true) {
63215
63781
  sites.set(out, e);
63216
63782
  }
63217
63783
  labelEdges(data2, graphEdges, sites);
63784
+ let pruned = 0;
63785
+ if (params.connectedOnly ?? true) {
63786
+ const linked = /* @__PURE__ */ new Set();
63787
+ for (const e of graphEdges) {
63788
+ linked.add(e.from);
63789
+ linked.add(e.to);
63790
+ }
63791
+ for (const id of [...selected]) {
63792
+ if (linked.has(id) || id === params.root) continue;
63793
+ selected.delete(id);
63794
+ pruned++;
63795
+ }
63796
+ }
63218
63797
  const firesCount = /* @__PURE__ */ new Map();
63219
63798
  for (const e of graphEdges) firesCount.set(e.from, (firesCount.get(e.from) ?? 0) + 1);
63220
63799
  const nodes = [];
@@ -63254,7 +63833,7 @@ function computeEventGraph(data2, params, inFocus = () => true) {
63254
63833
  nodes.sort((a, b) => a.id.localeCompare(b.id));
63255
63834
  const graph = { nodes, edges: graphEdges, truncated, suggestions: suggestionsOf(vocabulary) };
63256
63835
  if (nodes.length === 0) {
63257
- const reason = emptyReason(data2, params, inFocus);
63836
+ const reason = pruned > 0 ? `${pruned} definition(s) are here but none is connected to another. Turn "Connected only" off to show them.` : emptyReason(data2, params, inFocus);
63258
63837
  if (reason) graph.emptyReason = reason;
63259
63838
  }
63260
63839
  return graph;
@@ -63294,7 +63873,7 @@ function fileFacts() {
63294
63873
  facts = /* @__PURE__ */ new Map();
63295
63874
  byFile.set(key, facts);
63296
63875
  try {
63297
- const text = decode(fs28.readFileSync(file)).text;
63876
+ const text = decode(fs30.readFileSync(file)).text;
63298
63877
  const root = parseScript(text).root;
63299
63878
  const li = new LineIndex(text);
63300
63879
  for (const stmt of root.statements) {
@@ -63379,6 +63958,324 @@ function suggestionsOf(vocabulary) {
63379
63958
  return { ids: ids.slice(0, MAX_SUGGESTIONS), namespaces: [...namespaces].sort() };
63380
63959
  }
63381
63960
 
63961
+ // src/overview/dynastyTree.ts
63962
+ var fs31 = __toESM(require("fs"));
63963
+ var DYNASTY_KIND = "dynasty";
63964
+ var HOUSE_KIND = "dynasty_house";
63965
+ var CHARACTER_KIND = "character";
63966
+ var SOURCE_RANK2 = { mod: 2, parent: 1, vanilla: 0 };
63967
+ var DATE_RE = /^-?\d+\.\d+\.\d+$/;
63968
+ var SKILL_KEYS = new Set(DYNASTY_SKILLS);
63969
+ var NONE = [];
63970
+ function interner() {
63971
+ const pool = /* @__PURE__ */ new Map();
63972
+ return (value) => {
63973
+ const known = pool.get(value);
63974
+ if (known !== void 0) return known;
63975
+ pool.set(value, value);
63976
+ return value;
63977
+ };
63978
+ }
63979
+ var cached = null;
63980
+ var IDLE_RELEASE_MS = 10 * 60 * 1e3;
63981
+ var idleTimer = null;
63982
+ function clearDynastyModel() {
63983
+ cached = null;
63984
+ if (idleTimer) {
63985
+ clearTimeout(idleTimer);
63986
+ idleTimer = null;
63987
+ }
63988
+ }
63989
+ function keepAlive() {
63990
+ if (idleTimer) clearTimeout(idleTimer);
63991
+ idleTimer = setTimeout(clearDynastyModel, IDLE_RELEASE_MS);
63992
+ idleTimer.unref?.();
63993
+ }
63994
+ function blockOf5(stmt) {
63995
+ if (stmt.kind !== "assignment") return null;
63996
+ const v = stmt.value;
63997
+ if (v?.kind === "block") return v;
63998
+ if (v?.kind === "tagged-block") return v.block;
63999
+ return null;
64000
+ }
64001
+ function scalarOf(stmt) {
64002
+ if (stmt.kind !== "assignment") return null;
64003
+ return stmt.value?.kind === "scalar" ? stmt.value.text : null;
64004
+ }
64005
+ function readCharacterBlock(id, block2, where, intern2 = (v) => v) {
64006
+ const traits = [];
64007
+ const spouses = [];
64008
+ const out = { id, name: id, female: false, traits, spouses, ...where };
64009
+ for (const stmt of block2.statements) {
64010
+ if (stmt.kind !== "assignment") continue;
64011
+ const key = stmt.key.text;
64012
+ if (DATE_RE.test(key)) {
64013
+ const dated = blockOf5(stmt);
64014
+ if (!dated) continue;
64015
+ for (const inner of dated.statements) {
64016
+ if (inner.kind !== "assignment") continue;
64017
+ const ikey = inner.key.text;
64018
+ if (ikey === "birth") out.birth ??= key;
64019
+ else if (ikey === "death") out.death ??= key;
64020
+ else if (ikey === "add_spouse") {
64021
+ const spouse = scalarOf(inner);
64022
+ if (spouse) spouses.push(intern2(spouse));
64023
+ }
64024
+ }
64025
+ continue;
64026
+ }
64027
+ const value = scalarOf(stmt);
64028
+ if (value === null) continue;
64029
+ if (SKILL_KEYS.has(key)) {
64030
+ const number = Number(value);
64031
+ if (Number.isFinite(number)) (out.skills ??= {})[key] = number;
64032
+ continue;
64033
+ }
64034
+ switch (key) {
64035
+ case "name":
64036
+ out.name = value;
64037
+ break;
64038
+ case "female":
64039
+ out.female = value === "yes";
64040
+ break;
64041
+ case "dynasty":
64042
+ out.dynasty = intern2(value);
64043
+ break;
64044
+ case "dynasty_house":
64045
+ out.house = intern2(value);
64046
+ break;
64047
+ case "father":
64048
+ out.father = value;
64049
+ break;
64050
+ case "mother":
64051
+ out.mother = value;
64052
+ break;
64053
+ case "culture":
64054
+ out.culture = intern2(value);
64055
+ break;
64056
+ case "religion":
64057
+ out.religion = intern2(value);
64058
+ break;
64059
+ case "dna":
64060
+ out.dna = value;
64061
+ break;
64062
+ case "trait":
64063
+ traits.push(intern2(value));
64064
+ break;
64065
+ case "add_spouse":
64066
+ spouses.push(intern2(value));
64067
+ break;
64068
+ }
64069
+ }
64070
+ if (traits.length === 0) out.traits = NONE;
64071
+ if (spouses.length === 0) out.spouses = NONE;
64072
+ return out;
64073
+ }
64074
+ function readDefinitionBlock(id, block2, where) {
64075
+ const out = { id, nameKey: "", dynasty: "", ...where };
64076
+ for (const stmt of block2.statements) {
64077
+ const value = scalarOf(stmt);
64078
+ if (value === null || stmt.kind !== "assignment") continue;
64079
+ if (stmt.key.text === "name") out.nameKey = value;
64080
+ else if (stmt.key.text === "culture") out.culture = value;
64081
+ else if (stmt.key.text === "dynasty") out.dynasty = value;
64082
+ }
64083
+ return out;
64084
+ }
64085
+ function eachTopLevelBlock(file, onBlock) {
64086
+ let text;
64087
+ try {
64088
+ text = decode(fs31.readFileSync(file)).text;
64089
+ } catch {
64090
+ return;
64091
+ }
64092
+ const { root } = parseScript(text);
64093
+ const li = new LineIndex(text);
64094
+ for (const stmt of root.statements) {
64095
+ if (stmt.kind !== "assignment") continue;
64096
+ const block2 = blockOf5(stmt);
64097
+ if (!block2) continue;
64098
+ onBlock(stmt.key.text, block2, li.positionAt(stmt.key.range.start).line);
64099
+ }
64100
+ }
64101
+ function scanIndex(data2) {
64102
+ const files = /* @__PURE__ */ new Map([
64103
+ [DYNASTY_KIND, []],
64104
+ [HOUSE_KIND, []],
64105
+ [CHARACTER_KIND, []]
64106
+ ]);
64107
+ const seen = /* @__PURE__ */ new Set();
64108
+ let maxCharacterId = 0;
64109
+ let maxDynastyId = 0;
64110
+ const bump2 = (def) => {
64111
+ const n = Number(def.name);
64112
+ if (!Number.isInteger(n)) return;
64113
+ if (def.kind === CHARACTER_KIND) maxCharacterId = Math.max(maxCharacterId, n);
64114
+ else maxDynastyId = Math.max(maxDynastyId, n);
64115
+ };
64116
+ for (const def of data2.index.allDefinitions()) {
64117
+ const bucket = files.get(def.kind);
64118
+ if (!bucket) continue;
64119
+ bump2(def);
64120
+ const key = `${def.kind}\0${def.file}`;
64121
+ if (seen.has(key)) continue;
64122
+ seen.add(key);
64123
+ bucket.push({ file: def.file, source: def.source });
64124
+ }
64125
+ return { files, maxCharacterId, maxDynastyId };
64126
+ }
64127
+ function keep(map, id, rec) {
64128
+ const prev = map.get(id);
64129
+ if (prev && SOURCE_RANK2[prev.source] > SOURCE_RANK2[rec.source]) return;
64130
+ map.set(id, rec);
64131
+ }
64132
+ function buildModel(data2) {
64133
+ const { files, maxCharacterId, maxDynastyId } = scanIndex(data2);
64134
+ const dynasties = /* @__PURE__ */ new Map();
64135
+ const houses = /* @__PURE__ */ new Map();
64136
+ const characters = /* @__PURE__ */ new Map();
64137
+ const ordered = (kind) => [...files.get(kind) ?? []].sort((a, b) => SOURCE_RANK2[a.source] - SOURCE_RANK2[b.source]);
64138
+ for (const ref of ordered(DYNASTY_KIND)) {
64139
+ eachTopLevelBlock(ref.file, (id, block2, line) => {
64140
+ keep(dynasties, id, readDefinitionBlock(id, block2, { source: ref.source, file: ref.file, line }));
64141
+ });
64142
+ }
64143
+ for (const ref of ordered(HOUSE_KIND)) {
64144
+ eachTopLevelBlock(ref.file, (id, block2, line) => {
64145
+ keep(houses, id, readDefinitionBlock(id, block2, { source: ref.source, file: ref.file, line }));
64146
+ });
64147
+ }
64148
+ const intern2 = interner();
64149
+ for (const ref of ordered(CHARACTER_KIND)) {
64150
+ eachTopLevelBlock(ref.file, (id, block2, line) => {
64151
+ keep(
64152
+ characters,
64153
+ id,
64154
+ readCharacterBlock(id, block2, { source: ref.source, file: ref.file, line }, intern2)
64155
+ );
64156
+ });
64157
+ }
64158
+ const members = /* @__PURE__ */ new Map();
64159
+ for (const char of characters.values()) {
64160
+ const dynasty = char.dynasty ?? (char.house ? houses.get(char.house)?.dynasty : void 0);
64161
+ if (!dynasty) continue;
64162
+ const list2 = members.get(dynasty);
64163
+ if (list2) list2.push(char.id);
64164
+ else members.set(dynasty, [char.id]);
64165
+ }
64166
+ return {
64167
+ revision: data2.index.revision,
64168
+ dynasties,
64169
+ houses,
64170
+ characters,
64171
+ members,
64172
+ nextCharacterId: String(maxCharacterId + 1),
64173
+ nextDynastyId: String(maxDynastyId + 1)
64174
+ };
64175
+ }
64176
+ function modelFor(data2) {
64177
+ keepAlive();
64178
+ if (cached && cached.revision === data2.index.revision) return cached;
64179
+ cached = buildModel(data2);
64180
+ return cached;
64181
+ }
64182
+ function locText(data2, key) {
64183
+ if (key === "") return "";
64184
+ return data2.index.lookup(key).find((d) => d.kind === "loc_key" && d.value !== void 0)?.value ?? key;
64185
+ }
64186
+ function summaryOf(data2, model, rec, houseCount) {
64187
+ return {
64188
+ id: rec.id,
64189
+ nameKey: rec.nameKey,
64190
+ name: locText(data2, rec.nameKey),
64191
+ culture: rec.culture,
64192
+ source: rec.source,
64193
+ file: rec.file,
64194
+ line: rec.line,
64195
+ characterCount: model.members.get(rec.id)?.length ?? 0,
64196
+ houseCount
64197
+ };
64198
+ }
64199
+ function computeDynastyTree(data2, params, inFocus = () => true) {
64200
+ const supported = activeProfile().schema.some((entry) => entry.kind === DYNASTY_KIND);
64201
+ if (!supported) return { supported: false, dynasties: [] };
64202
+ const model = modelFor(data2);
64203
+ const visible = (rec2) => rec2.source !== "mod" || inFocus(rec2.file);
64204
+ const houseCounts = /* @__PURE__ */ new Map();
64205
+ for (const house of model.houses.values()) {
64206
+ if (!visible(house)) continue;
64207
+ houseCounts.set(house.dynasty, (houseCounts.get(house.dynasty) ?? 0) + 1);
64208
+ }
64209
+ const wanted = params.dynasty;
64210
+ if (wanted === void 0) {
64211
+ const mod = [];
64212
+ const rest = [];
64213
+ for (const rec2 of model.dynasties.values()) {
64214
+ if (!visible(rec2)) continue;
64215
+ const summary = summaryOf(data2, model, rec2, houseCounts.get(rec2.id) ?? 0);
64216
+ (rec2.source === "mod" ? mod : rest).push(summary);
64217
+ }
64218
+ const byName = (a, b) => a.name.localeCompare(b.name);
64219
+ mod.sort(byName);
64220
+ rest.sort(byName);
64221
+ return {
64222
+ supported: true,
64223
+ dynasties: [...mod, ...rest],
64224
+ nextCharacterId: model.nextCharacterId,
64225
+ nextDynastyId: model.nextDynastyId
64226
+ };
64227
+ }
64228
+ const rec = model.dynasties.get(wanted);
64229
+ if (!rec) {
64230
+ return {
64231
+ supported: true,
64232
+ dynasties: [],
64233
+ nextCharacterId: model.nextCharacterId,
64234
+ nextDynastyId: model.nextDynastyId
64235
+ };
64236
+ }
64237
+ const houses = [];
64238
+ for (const house of model.houses.values()) {
64239
+ if (house.dynasty !== wanted || !visible(house)) continue;
64240
+ houses.push({
64241
+ id: house.id,
64242
+ nameKey: house.nameKey,
64243
+ name: locText(data2, house.nameKey),
64244
+ dynasty: house.dynasty,
64245
+ source: house.source,
64246
+ file: house.file,
64247
+ line: house.line
64248
+ });
64249
+ }
64250
+ houses.sort((a, b) => a.name.localeCompare(b.name));
64251
+ const characters = [];
64252
+ const taken = /* @__PURE__ */ new Set();
64253
+ for (const id of model.members.get(wanted) ?? []) {
64254
+ const char = model.characters.get(id);
64255
+ if (!char || !visible(char) || taken.has(id)) continue;
64256
+ taken.add(id);
64257
+ characters.push(char);
64258
+ }
64259
+ for (const char of [...characters]) {
64260
+ for (const other of [char.father, char.mother, ...char.spouses]) {
64261
+ if (!other || taken.has(other)) continue;
64262
+ const rel = model.characters.get(other);
64263
+ if (!rel) continue;
64264
+ taken.add(other);
64265
+ characters.push({ ...rel, external: true });
64266
+ }
64267
+ }
64268
+ return {
64269
+ supported: true,
64270
+ dynasties: [],
64271
+ dynasty: summaryOf(data2, model, rec, houses.length),
64272
+ houses,
64273
+ characters,
64274
+ nextCharacterId: model.nextCharacterId,
64275
+ nextDynastyId: model.nextDynastyId
64276
+ };
64277
+ }
64278
+
63382
64279
  // src/overview/eventVocabulary.ts
63383
64280
  var MAX_DOC = 220;
63384
64281
  function short(doc) {
@@ -63396,6 +64293,24 @@ function keyItems(specs) {
63396
64293
  hint: spec.values === "block" ? "block" : spec.values === "bool" ? "yes/no" : spec.values === "loc" ? "loc" : void 0
63397
64294
  }));
63398
64295
  }
64296
+ function definitionsOfKind(data2, kind, inFocus = () => true) {
64297
+ const seen = /* @__PURE__ */ new Set();
64298
+ const mod = [];
64299
+ const rest = [];
64300
+ for (const def of data2.index.allDefinitions()) {
64301
+ if (def.kind !== kind || seen.has(def.name)) continue;
64302
+ if (def.source === "mod" && !inFocus(def.file)) continue;
64303
+ seen.add(def.name);
64304
+ (def.source === "mod" ? mod : rest).push({
64305
+ value: def.name,
64306
+ doc: short(def.doc),
64307
+ hint: def.source === "mod" ? "this mod" : def.source
64308
+ });
64309
+ }
64310
+ mod.sort((a, b) => a.value.localeCompare(b.value));
64311
+ rest.sort((a, b) => a.value.localeCompare(b.value));
64312
+ return [...mod, ...rest].slice(0, EVENT_VOCABULARY_MAX_VALUES);
64313
+ }
63399
64314
  function computeEventVocabulary(data2, schema2, inFocus = () => true) {
63400
64315
  const blocks = schema2.structures.keysByKindBlock.get("event");
63401
64316
  const eventKeys = keyItems(blocks?.get(""));
@@ -63403,26 +64318,11 @@ function computeEventVocabulary(data2, schema2, inFocus = () => true) {
63403
64318
  const values = {};
63404
64319
  const byKind = /* @__PURE__ */ new Map();
63405
64320
  const definitionsOf = (kind) => {
63406
- let cached = byKind.get(kind);
63407
- if (cached) return cached;
63408
- const seen = /* @__PURE__ */ new Set();
63409
- const mod = [];
63410
- const rest = [];
63411
- for (const def of data2.index.allDefinitions()) {
63412
- if (def.kind !== kind || seen.has(def.name)) continue;
63413
- if (def.source === "mod" && !inFocus(def.file)) continue;
63414
- seen.add(def.name);
63415
- (def.source === "mod" ? mod : rest).push({
63416
- value: def.name,
63417
- doc: short(def.doc),
63418
- hint: def.source === "mod" ? "this mod" : def.source
63419
- });
63420
- }
63421
- mod.sort((a, b) => a.value.localeCompare(b.value));
63422
- rest.sort((a, b) => a.value.localeCompare(b.value));
63423
- cached = [...mod, ...rest].slice(0, EVENT_VOCABULARY_MAX_VALUES);
63424
- byKind.set(kind, cached);
63425
- return cached;
64321
+ let cached2 = byKind.get(kind);
64322
+ if (cached2) return cached2;
64323
+ cached2 = definitionsOfKind(data2, kind, inFocus);
64324
+ byKind.set(kind, cached2);
64325
+ return cached2;
63426
64326
  };
63427
64327
  for (const specs of [blocks?.get(""), blocks?.get("option")]) {
63428
64328
  for (const spec of specs?.values() ?? []) {
@@ -63495,27 +64395,12 @@ function computeValueOptions(data2, value, inFocus = () => true) {
63495
64395
  if (/^-?[\d.]+$/.test(name) || name.includes(":") || name.includes(".")) return null;
63496
64396
  const def = data2.index.lookup(name).find((d) => !VALUE_KIND_SKIP.has(d.kind));
63497
64397
  if (!def) return null;
63498
- const seen = /* @__PURE__ */ new Set();
63499
- const mod = [];
63500
- const rest = [];
63501
- for (const d of data2.index.allDefinitions()) {
63502
- if (d.kind !== def.kind || seen.has(d.name)) continue;
63503
- if (d.source === "mod" && !inFocus(d.file)) continue;
63504
- seen.add(d.name);
63505
- (d.source === "mod" ? mod : rest).push({
63506
- value: d.name,
63507
- doc: short(d.doc),
63508
- hint: d.source === "mod" ? "this mod" : d.source
63509
- });
63510
- }
63511
- mod.sort((a, b) => a.value.localeCompare(b.value));
63512
- rest.sort((a, b) => a.value.localeCompare(b.value));
63513
- const items = [...mod, ...rest].slice(0, EVENT_VOCABULARY_MAX_VALUES);
64398
+ const items = definitionsOfKind(data2, def.kind, inFocus);
63514
64399
  return items.length > 1 ? { kind: def.kind, items } : null;
63515
64400
  }
63516
64401
 
63517
64402
  // src/overview/eventBanner.ts
63518
- var fs29 = __toESM(require("fs"));
64403
+ var fs32 = __toESM(require("fs"));
63519
64404
  var TEXTURE_PATH = /\.(dds|tga|png)$/i;
63520
64405
  function computeEventBanner(data2, theme) {
63521
64406
  if (TEXTURE_PATH.test(theme)) return { theme, texture: theme };
@@ -63537,7 +64422,7 @@ function referenceOf(data2, name, kind) {
63537
64422
  if (!def) return null;
63538
64423
  let block2 = null;
63539
64424
  try {
63540
- const text = decode(fs29.readFileSync(def.file)).text;
64425
+ const text = decode(fs32.readFileSync(def.file)).text;
63541
64426
  const stmt = parseScript(text).root.statements.find(
63542
64427
  (s2) => s2.kind === "assignment" && s2.key.text === name
63543
64428
  );
@@ -63575,9 +64460,691 @@ function childBlock5(stmt) {
63575
64460
  return null;
63576
64461
  }
63577
64462
 
64463
+ // src/creators/definitionForm.ts
64464
+ var fs33 = __toESM(require("fs"));
64465
+ var EXISTING_CAP = 500;
64466
+ function formKeys(specs) {
64467
+ if (!specs) return [];
64468
+ return [...specs.values()].map((spec) => ({
64469
+ key: spec.key,
64470
+ ...short(spec.doc) ? { doc: short(spec.doc) } : {},
64471
+ ...spec.values ? { values: spec.values } : {},
64472
+ ...spec.freq !== void 0 ? { freq: spec.freq } : {},
64473
+ ...spec.refKinds?.length ? { refKinds: spec.refKinds } : {}
64474
+ }));
64475
+ }
64476
+ function parsedFile(file, cache6) {
64477
+ const seen = cache6.get(file);
64478
+ if (seen) return seen;
64479
+ const defs = /* @__PURE__ */ new Map();
64480
+ cache6.set(file, defs);
64481
+ let text;
64482
+ try {
64483
+ text = decode(fs33.readFileSync(file)).text;
64484
+ } catch {
64485
+ return defs;
64486
+ }
64487
+ for (const s2 of parseScript(text).root.statements) {
64488
+ if (s2.kind !== "assignment" || s2.value?.kind !== "block") continue;
64489
+ defs.set(s2.key.text, {
64490
+ block: s2.value,
64491
+ source: text.slice(s2.key.range.start, s2.value.range.end),
64492
+ start: s2.key.range.start
64493
+ });
64494
+ }
64495
+ return defs;
64496
+ }
64497
+ function definitionFiles(data2, kind) {
64498
+ const out = [];
64499
+ for (const def of data2.index.allDefinitions()) {
64500
+ if (def.kind === kind) out.push({ name: def.name, file: def.file });
64501
+ }
64502
+ return out;
64503
+ }
64504
+ function scalarOf2(block2, key) {
64505
+ for (const s2 of block2.statements) {
64506
+ if (s2.kind === "assignment" && s2.key.text === key && s2.value?.kind === "scalar") return s2.value.text;
64507
+ }
64508
+ return null;
64509
+ }
64510
+ function offerable(text) {
64511
+ return text !== "" && !/^-?\d+(\.\d+)?$/.test(text) && !text.includes(":") && !text.startsWith("@");
64512
+ }
64513
+ function namesIn(value, into) {
64514
+ if (value.kind === "scalar") {
64515
+ if (!value.quoted && offerable(value.text)) into(value.text);
64516
+ return;
64517
+ }
64518
+ if (value.kind !== "block") return;
64519
+ for (const s2 of value.statements) {
64520
+ const inner = s2.kind === "value" ? s2.value : s2.value;
64521
+ if (inner?.kind === "scalar" && !inner.quoted && offerable(inner.text)) into(inner.text);
64522
+ }
64523
+ }
64524
+ function groupOptions(data2, schema2, kind, items, cache6) {
64525
+ const groupKey = schema2.entries.find((e) => e.kind === kind)?.groupKey;
64526
+ if (!groupKey) return;
64527
+ const groups = /* @__PURE__ */ new Map();
64528
+ for (const { name, file } of definitionFiles(data2, kind)) {
64529
+ const def = parsedFile(file, cache6).get(name);
64530
+ const group = def ? scalarOf2(def.block, groupKey) : null;
64531
+ if (group !== null) groups.set(name, group);
64532
+ }
64533
+ for (const item of items) {
64534
+ const group = groups.get(item.value);
64535
+ if (group !== void 0) item.group = group;
64536
+ }
64537
+ }
64538
+ function sampledValues(data2, kind, keys, cache6) {
64539
+ const wanted = keys.filter((k) => !k.refKinds?.length);
64540
+ if (wanted.length === 0) return;
64541
+ const counts = /* @__PURE__ */ new Map();
64542
+ const literals = /* @__PURE__ */ new Map();
64543
+ const bodies = /* @__PURE__ */ new Map();
64544
+ for (const key of wanted) {
64545
+ counts.set(key.key, /* @__PURE__ */ new Map());
64546
+ literals.set(key.key, /* @__PURE__ */ new Map());
64547
+ bodies.set(key.key, /* @__PURE__ */ new Map());
64548
+ }
64549
+ let read = 0;
64550
+ for (const { name, file } of definitionFiles(data2, kind)) {
64551
+ if (read >= EVENT_VOCABULARY_MAX_VALUES) break;
64552
+ const def = parsedFile(file, cache6).get(name);
64553
+ if (!def) continue;
64554
+ read++;
64555
+ for (const s2 of def.block.statements) {
64556
+ if (s2.kind !== "assignment" || !s2.value) continue;
64557
+ const bucket = counts.get(s2.key.text);
64558
+ if (!bucket) continue;
64559
+ namesIn(s2.value, (v) => bucket.set(v, (bucket.get(v) ?? 0) + 1));
64560
+ if (s2.value.kind === "scalar" && s2.value.text !== "") {
64561
+ const seen = literals.get(s2.key.text);
64562
+ seen.set(s2.value.text, (seen.get(s2.value.text) ?? 0) + 1);
64563
+ } else if (s2.value.kind === "block") {
64564
+ const body = oneLineBody(
64565
+ def.source.slice(s2.value.range.start - def.start, s2.value.range.end - def.start)
64566
+ );
64567
+ if (body !== "") {
64568
+ const seen = bodies.get(s2.key.text);
64569
+ seen.set(body, (seen.get(body) ?? 0) + 1);
64570
+ }
64571
+ }
64572
+ }
64573
+ }
64574
+ const mostUsed = (bucket) => [...bucket.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));
64575
+ for (const key of wanted) {
64576
+ const bucket = counts.get(key.key);
64577
+ const stated = key.values === "bool" || key.values?.startsWith("enum:") === true;
64578
+ if (!stated && bucket.size > 0 && bucket.size <= DEFINITION_FORM_MAX_SAMPLED) {
64579
+ key.sampled = mostUsed(bucket).map(([value]) => value);
64580
+ }
64581
+ const example = mostUsed(literals.get(key.key))[0] ?? mostUsed(bodies.get(key.key))[0];
64582
+ if (example) key.example = example[0];
64583
+ }
64584
+ }
64585
+ function oneLineBody(text) {
64586
+ const flat = text.replace(/#[^\n]*/g, " ").replace(/\s+/g, " ").trim();
64587
+ return flat.length > DEFINITION_FORM_MAX_EXAMPLE ? `${flat.slice(0, DEFINITION_FORM_MAX_EXAMPLE - 1).trimEnd()}\u2026` : flat;
64588
+ }
64589
+ function docListValues(traits) {
64590
+ for (const line of traits.split("\n")) {
64591
+ const names = line.slice(line.lastIndexOf(":") + 1).split(",").map(
64592
+ (part) => part.trim().replace(/^and\s+/i, "").replace(/\.$/, "")
64593
+ ).filter((name) => /^[A-Za-z][A-Za-z0-9_]*$/.test(name));
64594
+ if (names.length > 0) return [...new Set(names)];
64595
+ }
64596
+ return [];
64597
+ }
64598
+ function innerKeysOf(data2, kind, except, cache6) {
64599
+ const skip = new Set(except);
64600
+ const names = [];
64601
+ for (const { name, file } of definitionFiles(data2, kind)) {
64602
+ const def = parsedFile(file, cache6).get(name);
64603
+ if (!def) continue;
64604
+ for (const s2 of def.block.statements) {
64605
+ if (s2.kind !== "assignment" || s2.value?.kind !== "block") continue;
64606
+ if (skip.has(s2.key.text) || names.includes(s2.key.text)) continue;
64607
+ names.push(s2.key.text);
64608
+ }
64609
+ }
64610
+ return names;
64611
+ }
64612
+ function conditionItems(data2, source, trigger, inFocus, cache6) {
64613
+ if (source.from === "kind") return definitionsOfKind(data2, source.kind, inFocus);
64614
+ const values = source.from === "docList" ? docListValues(data2.tokenMap.get(trigger)?.find((token) => token.traits)?.traits ?? "") : innerKeysOf(data2, source.kind, source.except ?? [], cache6);
64615
+ return values.slice(0, EVENT_VOCABULARY_MAX_VALUES).map((value) => ({ value }));
64616
+ }
64617
+ function labeller(data2, schema2) {
64618
+ const patterns = /* @__PURE__ */ new Map();
64619
+ const patternsFor = (kind) => {
64620
+ let list2 = patterns.get(kind);
64621
+ if (!list2) {
64622
+ const entry = schema2.entries.find((e) => e.kind === kind);
64623
+ const own = entry?.locPatterns ?? entry?.requiredLoc ?? [];
64624
+ list2 = own.length > 0 ? [own[0]] : ["$_name", "$"];
64625
+ patterns.set(kind, list2);
64626
+ }
64627
+ return list2;
64628
+ };
64629
+ const locValue2 = (key) => data2.index.lookup(key).find((d) => d.kind === "loc_key" && d.value !== void 0)?.value;
64630
+ return (kind, name) => {
64631
+ for (const pattern of patternsFor(kind)) {
64632
+ const value = locValue2(pattern.replace("$", name));
64633
+ if (value === void 0 || value === "") continue;
64634
+ const alias = /^\$([\w.-]+)\$$/.exec(value);
64635
+ const resolved = alias ? locValue2(alias[1]) : void 0;
64636
+ return resolved !== void 0 && resolved !== "" ? resolved : value;
64637
+ }
64638
+ return void 0;
64639
+ };
64640
+ }
64641
+ function computeDefinitionForm(data2, schema2, params, inFocus = () => true) {
64642
+ const kind = params?.kind?.trim() ?? "";
64643
+ const entry = schema2.entries.find((e) => e.kind === kind);
64644
+ if (kind === "" || !entry) return null;
64645
+ const blocks = schema2.structures.keysByKindBlock.get(kind);
64646
+ const keys = formKeys(blocks?.get(""));
64647
+ const subBlocks = {};
64648
+ for (const [name, specs] of blocks ?? []) {
64649
+ if (name !== "") subBlocks[name] = formKeys(specs);
64650
+ }
64651
+ const files = /* @__PURE__ */ new Map();
64652
+ const labelOf = labeller(data2, schema2);
64653
+ const options = {};
64654
+ for (const key of keys) {
64655
+ for (const refKind of key.refKinds ?? []) {
64656
+ if (options[refKind]) continue;
64657
+ options[refKind] = definitionsOfKind(data2, refKind, inFocus);
64658
+ groupOptions(data2, schema2, refKind, options[refKind], files);
64659
+ for (const item of options[refKind]) {
64660
+ const label = labelOf(refKind, item.value);
64661
+ if (label !== void 0) item.label = label;
64662
+ }
64663
+ }
64664
+ }
64665
+ sampledValues(data2, kind, keys, files);
64666
+ const conditions = {};
64667
+ for (const [trigger, source] of Object.entries(activeProfile().conditionValues ?? {})) {
64668
+ const items = conditionItems(data2, source, trigger, inFocus, files);
64669
+ if (items.length > 0) conditions[trigger] = items;
64670
+ }
64671
+ const byName = /* @__PURE__ */ new Map();
64672
+ for (const def of data2.index.allDefinitions()) {
64673
+ if (def.kind !== kind) continue;
64674
+ if (def.source === "mod" && !inFocus(def.file)) continue;
64675
+ const seen = byName.get(def.name);
64676
+ if (seen && !(def.source === "mod" && seen.source !== "mod")) continue;
64677
+ const label = labelOf(kind, def.name);
64678
+ byName.set(def.name, {
64679
+ name: def.name,
64680
+ file: def.file,
64681
+ line: def.line,
64682
+ source: def.source,
64683
+ ...label !== void 0 ? { label } : {}
64684
+ });
64685
+ }
64686
+ const existing = [...byName.values()].sort((a, b) => Number(b.source === "mod") - Number(a.source === "mod") || a.name.localeCompare(b.name)).slice(0, EXISTING_CAP);
64687
+ const modifiers = data2.tokens.filter((t) => t.kind === "modifier").sort((a, b) => data2.refIndex.usageCount(b.name) - data2.refIndex.usageCount(a.name)).slice(0, EVENT_VOCABULARY_MAX_VALUES).map((t) => ({ name: t.name, ...short(t.doc) ? { doc: short(t.doc) } : {} }));
64688
+ const form = {
64689
+ kind,
64690
+ folder: entry.path,
64691
+ // The whole set the game generates, which is a superset of the
64692
+ // conservative requiredLoc a diagnostic is allowed to demand.
64693
+ locPatterns: entry.locPatterns ?? entry.requiredLoc ?? [],
64694
+ ...entry.iconFolder ? { iconFolder: entry.iconFolder } : {},
64695
+ keys,
64696
+ ...Object.keys(subBlocks).length > 0 ? { blocks: subBlocks } : {},
64697
+ options,
64698
+ ...Object.keys(conditions).length > 0 ? { conditions } : {},
64699
+ modifiers,
64700
+ existing
64701
+ };
64702
+ const wanted = params.name?.trim();
64703
+ if (wanted) {
64704
+ const defs = data2.index.lookup(wanted).filter((d) => d.kind === kind);
64705
+ const def = defs.find((d) => d.source === "mod" && inFocus(d.file)) ?? defs[0];
64706
+ const parsed = def ? parsedFile(def.file, files).get(wanted) : void 0;
64707
+ if (def && parsed) {
64708
+ form.current = { file: def.file, line: def.line, source: def.source, text: parsed.source };
64709
+ }
64710
+ }
64711
+ return form;
64712
+ }
64713
+
64714
+ // src/creators/definitionEdit.ts
64715
+ function topLevelBlock(file, name) {
64716
+ for (const entry of file.root.entries) {
64717
+ if (entry.key === name && entry.valueKind === "block") return entry;
64718
+ }
64719
+ return null;
64720
+ }
64721
+ function toFileNewline(text, file) {
64722
+ return text.replace(/\r?\n/g, file.newline);
64723
+ }
64724
+ function upsertBlock(file, name, blockText2, appended) {
64725
+ const trimmed = blockText2.trim();
64726
+ if (name.trim() === "" || trimmed === "") {
64727
+ return "an upsert needs both a definition name and the block text to write.";
64728
+ }
64729
+ const body = toFileNewline(trimmed, file);
64730
+ const existing = topLevelBlock(file, name);
64731
+ if (existing) return { start: existing.span.start, end: existing.span.end, newText: body };
64732
+ const text = file.text + appended;
64733
+ const eol = file.newline;
64734
+ const at = file.text.length;
64735
+ if (text.trim() === "") return { start: at, end: at, newText: body + eol };
64736
+ const closeLastLine = text.endsWith("\n") ? "" : eol;
64737
+ return { start: at, end: at, newText: closeLastLine + eol + body + eol };
64738
+ }
64739
+ function setProperties2(file, name, properties2) {
64740
+ const target = topLevelBlock(file, name);
64741
+ if (!target) {
64742
+ return `this file has no top-level \`${name} = { \u2026 }\` to change: write the whole block instead.`;
64743
+ }
64744
+ const edits = [];
64745
+ const missing = [];
64746
+ for (const { key, value } of properties2 ?? []) {
64747
+ if (typeof key !== "string" || key.trim() === "") continue;
64748
+ const existing = target.body ? findEntry(target.body, key) : null;
64749
+ if (value === null) {
64750
+ const edit = existing ? removeProperty(file, existing) : null;
64751
+ if (edit) edits.push(edit);
64752
+ continue;
64753
+ }
64754
+ if (existing) {
64755
+ const edit = setValue(file, existing, value);
64756
+ if (edit) edits.push(edit);
64757
+ } else {
64758
+ missing.push([key, value]);
64759
+ }
64760
+ }
64761
+ const insert = missing.length > 0 ? insertProperties(file, target, missing) : null;
64762
+ if (insert) edits.push(insert);
64763
+ return edits;
64764
+ }
64765
+ function overlaps(a, b) {
64766
+ return a.some((x) => b.some((y) => x.start < y.end && y.start < x.end));
64767
+ }
64768
+ function computeDefinitionEdits(params) {
64769
+ const text = params?.text ?? "";
64770
+ const ops = Array.isArray(params?.ops) ? params.ops : [];
64771
+ if (ops.length === 0) return { edits: [], ops: [] };
64772
+ const file = parseGuiSource(text, SCRIPT_DIALECT);
64773
+ if (file.errors.length > 0) {
64774
+ const refused = `this file has ${file.errors.length} parse error(s), so no offset in it can be trusted: fix the syntax first.`;
64775
+ return { edits: [], ops: ops.map(() => ({ refused })) };
64776
+ }
64777
+ const edits = [];
64778
+ const verdicts = [];
64779
+ const endOfFile = text.length;
64780
+ let appended = "";
64781
+ let appendEdit = null;
64782
+ for (const op of ops) {
64783
+ const answer = runOp2(file, op, appended);
64784
+ if (typeof answer === "string") {
64785
+ verdicts.push({ refused: answer });
64786
+ continue;
64787
+ }
64788
+ if (answer.length === 0) {
64789
+ verdicts.push({});
64790
+ continue;
64791
+ }
64792
+ if (overlaps(edits, answer)) {
64793
+ verdicts.push({
64794
+ refused: "another change in the same save already rewrites those bytes, so this one was left out: make it on its own."
64795
+ });
64796
+ continue;
64797
+ }
64798
+ for (const edit of answer) {
64799
+ if (edit.start === endOfFile && edit.end === endOfFile) {
64800
+ appended += edit.newText;
64801
+ if (appendEdit) appendEdit.newText += edit.newText;
64802
+ else {
64803
+ appendEdit = { ...edit };
64804
+ edits.push(appendEdit);
64805
+ }
64806
+ continue;
64807
+ }
64808
+ edits.push(edit);
64809
+ }
64810
+ verdicts.push({});
64811
+ }
64812
+ return { edits, ops: verdicts };
64813
+ }
64814
+ function runOp2(file, op, appended) {
64815
+ if (!op || typeof op.op !== "string") return "the server has no such edit.";
64816
+ switch (op.op) {
64817
+ case "setProperties": {
64818
+ const answer = setProperties2(file, op.name ?? "", op.properties ?? []);
64819
+ return typeof answer === "string" ? answer : answer;
64820
+ }
64821
+ case "upsertBlock": {
64822
+ const answer = upsertBlock(file, op.name ?? "", op.text ?? "", appended);
64823
+ return typeof answer === "string" ? answer : [answer];
64824
+ }
64825
+ default:
64826
+ return "the server has no such edit.";
64827
+ }
64828
+ }
64829
+
64830
+ // src/creators/modifierFormats.ts
64831
+ var fs34 = __toESM(require("fs"));
64832
+ var path34 = __toESM(require("path"));
64833
+ var cache5 = /* @__PURE__ */ new Map();
64834
+ function readText(file) {
64835
+ try {
64836
+ return decode(fs34.readFileSync(file)).text;
64837
+ } catch {
64838
+ return null;
64839
+ }
64840
+ }
64841
+ function scalar2(block2, key) {
64842
+ for (const s2 of block2.statements) {
64843
+ if (s2.kind === "assignment" && s2.key.text === key && s2.value?.kind === "scalar") return s2.value.text;
64844
+ }
64845
+ return null;
64846
+ }
64847
+ function flag(block2, key) {
64848
+ const value = scalar2(block2, key);
64849
+ return value === null ? void 0 : value === "yes";
64850
+ }
64851
+ function uvOf(block2, key) {
64852
+ for (const s2 of block2.statements) {
64853
+ if (s2.kind !== "assignment" || s2.key.text !== key || s2.value?.kind !== "block") continue;
64854
+ const numbers2 = [];
64855
+ for (const inner of s2.value.statements) {
64856
+ if (inner.kind === "value" && inner.value.kind === "scalar") numbers2.push(Number(inner.value.text));
64857
+ }
64858
+ if (numbers2.length === 4 && numbers2.every((n) => Number.isFinite(n))) {
64859
+ return [numbers2[0], numbers2[1], numbers2[2], numbers2[3]];
64860
+ }
64861
+ }
64862
+ return void 0;
64863
+ }
64864
+ function readFormats(dir) {
64865
+ const out = /* @__PURE__ */ new Map();
64866
+ let files;
64867
+ try {
64868
+ files = fs34.readdirSync(dir).filter((f) => f.endsWith(".txt"));
64869
+ } catch {
64870
+ return out;
64871
+ }
64872
+ for (const file of files.sort()) {
64873
+ const text = readText(path34.join(dir, file));
64874
+ if (text === null) continue;
64875
+ for (const s2 of parseScript(text).root.statements) {
64876
+ if (s2.kind !== "assignment" || s2.value?.kind !== "block") continue;
64877
+ const written = scalar2(s2.value, "decimals");
64878
+ const decimals = written === null ? NaN : Number(written);
64879
+ const color = scalar2(s2.value, "color");
64880
+ const prefix = scalar2(s2.value, "prefix");
64881
+ const suffix = scalar2(s2.value, "suffix");
64882
+ const negativeSuffix = scalar2(s2.value, "negative_suffix");
64883
+ const percent = flag(s2.value, "percent");
64884
+ const alreadyPercent = flag(s2.value, "already_percent");
64885
+ const hidden2 = flag(s2.value, "hidden");
64886
+ const noSign = flag(s2.value, "no_difference_sign");
64887
+ out.set(s2.key.text, {
64888
+ ...Number.isFinite(decimals) ? { decimals } : {},
64889
+ ...color !== null ? { color } : {},
64890
+ ...percent !== void 0 ? { percent } : {},
64891
+ ...alreadyPercent !== void 0 ? { alreadyPercent } : {},
64892
+ ...hidden2 !== void 0 ? { hidden: hidden2 } : {},
64893
+ ...noSign !== void 0 ? { noSign } : {},
64894
+ ...prefix !== null ? { prefix } : {},
64895
+ ...suffix !== null ? { suffix } : {},
64896
+ ...negativeSuffix !== null ? { negativeSuffix } : {}
64897
+ });
64898
+ }
64899
+ }
64900
+ return out;
64901
+ }
64902
+ function readTextIcons(file) {
64903
+ const out = /* @__PURE__ */ new Map();
64904
+ const text = readText(file);
64905
+ if (text === null) return out;
64906
+ for (const s2 of parseScript(text).root.statements) {
64907
+ if (s2.kind !== "assignment" || s2.key.text !== "texticon" || s2.value?.kind !== "block") continue;
64908
+ const name = scalar2(s2.value, "icon");
64909
+ if (name === null) continue;
64910
+ for (const inner of s2.value.statements) {
64911
+ if (inner.kind !== "assignment" || inner.key.text !== "iconsize" || inner.value?.kind !== "block") {
64912
+ continue;
64913
+ }
64914
+ const texture = scalar2(inner.value, "texture");
64915
+ if (texture === null) continue;
64916
+ const uv = uvOf(inner.value, "uv");
64917
+ out.set(name, { texture, ...uv ? { uv } : {} });
64918
+ }
64919
+ }
64920
+ return out;
64921
+ }
64922
+ function sourcesFor(gamePath, source) {
64923
+ const key = `${gamePath}\0${source.folder}\0${source.textIcons}`;
64924
+ const seen = cache5.get(key);
64925
+ if (seen) return seen;
64926
+ const built = {
64927
+ formats: readFormats(path34.join(gamePath, source.folder)),
64928
+ icons: readTextIcons(path34.join(gamePath, source.textIcons))
64929
+ };
64930
+ cache5.set(key, built);
64931
+ return built;
64932
+ }
64933
+ function stripCodes(text) {
64934
+ return text.replace(/#!/g, "").replace(/#[A-Za-z0-9_;]+ /g, "");
64935
+ }
64936
+ function titleCase(name) {
64937
+ return name.split("_").filter((word) => word !== "").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
64938
+ }
64939
+ function toParts(raw, icons, loc) {
64940
+ const parts = [];
64941
+ let text = "";
64942
+ const flush = () => {
64943
+ if (text !== "") parts.push({ text });
64944
+ text = "";
64945
+ };
64946
+ const pushIcon = (name) => {
64947
+ const icon = icons.get(name);
64948
+ if (!icon) return false;
64949
+ flush();
64950
+ parts.push({ icon: { texture: icon.texture, ...icon.uv ? { uv: icon.uv } : {} } });
64951
+ return true;
64952
+ };
64953
+ const source = stripCodes(raw);
64954
+ for (let i = 0; i < source.length; ) {
64955
+ const char = source[i];
64956
+ if (char === "@") {
64957
+ const end = source.indexOf("!", i + 1);
64958
+ const name = end > i ? source.slice(i + 1, end) : "";
64959
+ if (/^[A-Za-z0-9_]+$/.test(name) && pushIcon(name)) {
64960
+ i = end + 1;
64961
+ continue;
64962
+ }
64963
+ }
64964
+ if (char === "[") {
64965
+ const end = source.indexOf("]", i + 1);
64966
+ if (end > i) {
64967
+ const inner = source.slice(i + 1, end);
64968
+ const bar = inner.indexOf("|");
64969
+ const word = (bar >= 0 ? inner.slice(0, bar) : inner).trim();
64970
+ const concept = /^[A-Za-z0-9_]+$/.test(word) ? loc(`game_concept_${word}`) : void 0;
64971
+ if (concept !== void 0) {
64972
+ const direct = /^@([A-Za-z0-9_]+)!$/.exec(concept.trim());
64973
+ if (direct && pushIcon(direct[1])) {
64974
+ i = end + 1;
64975
+ continue;
64976
+ }
64977
+ if (!direct) {
64978
+ text += stripCodes(concept);
64979
+ i = end + 1;
64980
+ continue;
64981
+ }
64982
+ }
64983
+ text += bar >= 0 ? word : source.slice(i, end + 1);
64984
+ i = end + 1;
64985
+ continue;
64986
+ }
64987
+ }
64988
+ text += char;
64989
+ i++;
64990
+ }
64991
+ flush();
64992
+ return parts;
64993
+ }
64994
+ function toText(raw, icons, loc) {
64995
+ return toParts(raw, icons, loc).map((part) => "text" in part ? part.text : "").join("").trim();
64996
+ }
64997
+ function computeModifierFormats(data2, source, gamePath, lines) {
64998
+ if (!source || !gamePath) return null;
64999
+ const { formats, icons } = sourcesFor(gamePath, source);
65000
+ const loc = (key) => data2.index.lookup(key).find((d) => d.kind === "loc_key" && d.value !== void 0)?.value;
65001
+ const parts = (key) => {
65002
+ if (key === void 0) return void 0;
65003
+ const value = loc(key);
65004
+ if (value === void 0) return void 0;
65005
+ const list2 = toParts(value, icons, loc);
65006
+ return list2.length > 0 ? list2 : void 0;
65007
+ };
65008
+ const out = {};
65009
+ for (const token of data2.tokens) {
65010
+ if (token.kind !== "modifier" || out[token.name]) continue;
65011
+ const raw = formats.get(token.name);
65012
+ const own = loc(token.name) ?? loc(`MOD_${token.name.toUpperCase()}`);
65013
+ const prefix = parts(raw?.prefix);
65014
+ const suffix = parts(raw?.suffix);
65015
+ const negativeSuffix = parts(raw?.negativeSuffix);
65016
+ out[token.name] = {
65017
+ label: (own !== void 0 ? toText(own, icons, loc) : "") || titleCase(token.name),
65018
+ // _definitions.info: "decimals = int # Defaults to 2".
65019
+ decimals: raw?.decimals ?? 2,
65020
+ // _definitions.info says "Defaults to bad", but the game's own file
65021
+ // contradicts it: of 667 blocks in 00_definitions.txt, 113 write
65022
+ // `color = bad` and 2 write `color = good` (measured 1.19), and the
65023
+ // player sees `diplomacy = 1` green. A doc that matched the data would
65024
+ // never need 113 explicit `bad` lines, so the working default is good.
65025
+ color: raw?.color === "bad" || raw?.color === "neutral" ? raw.color : "good",
65026
+ ...raw?.percent ? { percent: true } : {},
65027
+ ...raw?.alreadyPercent ? { alreadyPercent: true } : {},
65028
+ ...raw?.noSign ? { noSign: true } : {},
65029
+ ...raw?.hidden ? { hidden: true } : {},
65030
+ ...prefix ? { prefix } : {},
65031
+ ...suffix ? { suffix } : {},
65032
+ ...negativeSuffix ? { negativeSuffix } : {}
65033
+ };
65034
+ }
65035
+ if (!lines) return { formats: out };
65036
+ const rendered = {};
65037
+ for (const key of lines) {
65038
+ const list2 = parts(key);
65039
+ if (list2) rendered[key] = list2;
65040
+ }
65041
+ return { formats: out, lines: rendered };
65042
+ }
65043
+
65044
+ // src/features/locText.ts
65045
+ var MAX_DEPTH2 = 2;
65046
+ var NESTED_KEY = /\$([A-Za-z0-9_.\-']+)(?:\|[^$]*)?\$/g;
65047
+ var ICON_TAG = /^[a-z0-9_]+_i$/;
65048
+ var CONCEPT_LINK = /^([A-Za-z0-9_]+)\|[A-Za-z]+$/;
65049
+ var LOCALIZE_CALL = /^(?:Localize|Concept)\s*\(\s*'([^']*)'(?:\s*,\s*'([^']*)')?\s*\)$/;
65050
+ var SELECT_CALL = /^SelectLocalization\s*\(.*?,\s*'([^']*)'\s*,\s*'([^']*)'\s*\)$/s;
65051
+ var DEF_CHAIN = /^Get(\w+)\s*\(\s*'([^']+)'\s*\)\s*((?:\.\s*\w+\s*(?:\([^()]*\))?\s*)*)$/;
65052
+ var NAME_MEMBERS = /* @__PURE__ */ new Set(["GetName", "GetTypeName", "GetNameNoTooltip"]);
65053
+ var CONCEPT_KIND = "game_concept";
65054
+ function pascal(kind) {
65055
+ return kind.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
65056
+ }
65057
+ function patternsWithFallback(kind, deps) {
65058
+ const own = kind === null ? [] : deps.patternsOf(kind);
65059
+ return [.../* @__PURE__ */ new Set([...own, "$"])];
65060
+ }
65061
+ function locOfName(name, kind, deps) {
65062
+ for (const pattern of patternsWithFallback(kind, deps)) {
65063
+ const value = deps.loc(pattern.replace("$", name));
65064
+ if (value !== void 0 && value !== "") return value;
65065
+ }
65066
+ return void 0;
65067
+ }
65068
+ function candidateKinds(head, name, deps) {
65069
+ const kinds = deps.kindsOf(name);
65070
+ const named2 = kinds.filter((kind) => head.includes(pascal(kind)));
65071
+ const rest = kinds.filter((kind) => !named2.includes(kind));
65072
+ return [...named2, ...rest, null];
65073
+ }
65074
+ function inline(value, deps, depth) {
65075
+ if (depth + 1 >= MAX_DEPTH2) return { text: stripFormatting(value).trim(), resolved: true };
65076
+ return render2(value, deps, depth + 1);
65077
+ }
65078
+ function renderFn(fn, deps, depth) {
65079
+ const concept = CONCEPT_LINK.exec(fn.trim());
65080
+ if (concept) {
65081
+ const value = locOfName(concept[1], CONCEPT_KIND, deps);
65082
+ return value === void 0 ? { text: concept[1], resolved: false } : inline(value, deps, depth);
65083
+ }
65084
+ const source = fn.split("|")[0].trim();
65085
+ if (ICON_TAG.test(source)) return { text: "", resolved: true };
65086
+ const localize = LOCALIZE_CALL.exec(source);
65087
+ if (localize) {
65088
+ const key = localize[2] ?? localize[1];
65089
+ const value = deps.loc(key);
65090
+ return value === void 0 ? { text: key, resolved: false } : inline(value, deps, depth);
65091
+ }
65092
+ const select = SELECT_CALL.exec(source);
65093
+ if (select) {
65094
+ const value = deps.loc(select[1]);
65095
+ return value === void 0 ? { text: select[1], resolved: false } : inline(value, deps, depth);
65096
+ }
65097
+ const chain = DEF_CHAIN.exec(source);
65098
+ const members = chain ? [...chain[3].matchAll(/\.\s*(\w+)/g)].map((m) => m[1]) : [];
65099
+ if (chain && members.length > 0 && NAME_MEMBERS.has(members[members.length - 1])) {
65100
+ const name = chain[2];
65101
+ for (const kind of candidateKinds(chain[1], name, deps)) {
65102
+ const value = locOfName(name, kind, deps);
65103
+ if (value !== void 0) return inline(value, deps, depth);
65104
+ }
65105
+ return { text: name, resolved: false };
65106
+ }
65107
+ return { text: chipFor(source), resolved: false };
65108
+ }
65109
+ function substituteKeys(s2, deps, resolved) {
65110
+ return s2.replace(NESTED_KEY, (whole, key) => {
65111
+ const value = deps.loc(key);
65112
+ if (value === void 0) {
65113
+ resolved.ok = false;
65114
+ return whole;
65115
+ }
65116
+ return value;
65117
+ });
65118
+ }
65119
+ function render2(raw, deps, depth) {
65120
+ const state = { ok: true };
65121
+ const parts = tokenize2(substituteKeys(raw, deps, state)).map((part) => {
65122
+ if (part.fn === void 0) return { text: stripFormatting(part.literal ?? ""), resolved: true };
65123
+ const out = renderFn(part.fn, deps, depth);
65124
+ if (!out.resolved) state.ok = false;
65125
+ return out;
65126
+ });
65127
+ const text = parts.map((part) => part.text).join("").replace(/[ \t]{2,}/g, " ").trim();
65128
+ return { text, resolved: state.ok };
65129
+ }
65130
+ function renderLocValue(raw, deps) {
65131
+ const out = render2(raw, deps, 0);
65132
+ return { raw, text: out.text, resolved: out.resolved };
65133
+ }
65134
+ function computeLocText(keys, deps) {
65135
+ const values = {};
65136
+ for (const key of keys) {
65137
+ if (key in values) continue;
65138
+ const raw = deps.loc(key);
65139
+ if (raw === void 0) continue;
65140
+ values[key] = renderLocValue(raw, deps);
65141
+ }
65142
+ return { values };
65143
+ }
65144
+
63578
65145
  // src/overview/dependencies.ts
63579
- var fs30 = __toESM(require("fs"));
63580
- var path32 = __toESM(require("path"));
65146
+ var fs35 = __toESM(require("fs"));
65147
+ var path35 = __toESM(require("path"));
63581
65148
  var CALL_KINDS2 = /* @__PURE__ */ new Set(["scripted_effect", "scripted_trigger"]);
63582
65149
  function computeDependencies(data2, schema2, name, kind, guiUses) {
63583
65150
  const resolved = data2.index.lookup(name);
@@ -63598,7 +65165,7 @@ function collectDependents(data2, def) {
63598
65165
  for (const site of sites) {
63599
65166
  const container = containingDef(data2, site.file, site.line);
63600
65167
  const groupKind = container ? container.kind : "file";
63601
- const itemName = container ? container.name : path32.basename(site.file);
65168
+ const itemName = container ? container.name : path35.basename(site.file);
63602
65169
  const dedupe = `${itemName} ${site.file}`;
63603
65170
  let byItem = groups.get(groupKind);
63604
65171
  if (!byItem) groups.set(groupKind, byItem = /* @__PURE__ */ new Map());
@@ -63612,7 +65179,7 @@ function scanCallSites(data2, def) {
63612
65179
  for (const file of modFiles(data2)) {
63613
65180
  let text;
63614
65181
  try {
63615
- text = decode(fs30.readFileSync(file)).text;
65182
+ text = decode(fs35.readFileSync(file)).text;
63616
65183
  } catch {
63617
65184
  continue;
63618
65185
  }
@@ -63649,7 +65216,7 @@ function containingDef(data2, file, line) {
63649
65216
  function collectDependencies(data2, schema2, def) {
63650
65217
  let text;
63651
65218
  try {
63652
- text = decode(fs30.readFileSync(def.file)).text;
65219
+ text = decode(fs35.readFileSync(def.file)).text;
63653
65220
  } catch {
63654
65221
  return [];
63655
65222
  }
@@ -63729,7 +65296,7 @@ if (process.argv.includes("--version") || process.argv.includes("-v")) {
63729
65296
  process.exit(0);
63730
65297
  }
63731
65298
  var TRANSPORT_FLAGS = ["--node-ipc", "--stdio", "--socket", "--pipe"];
63732
- if (!process.send && !process.argv.some((arg) => TRANSPORT_FLAGS.some((flag) => arg === flag || arg.startsWith(flag + "=")))) {
65299
+ if (!process.send && !process.argv.some((arg) => TRANSPORT_FLAGS.some((flag2) => arg === flag2 || arg.startsWith(flag2 + "=")))) {
63733
65300
  process.argv.push("--stdio");
63734
65301
  }
63735
65302
  var connection = (0, import_node17.createConnection)(import_node17.ProposedFeatures.all);
@@ -63949,8 +65516,8 @@ function clearPathCaches() {
63949
65516
  }
63950
65517
  function engineRoots() {
63951
65518
  if (!settings.gamePath) return [];
63952
- const jomini = path33.join(path33.dirname(settings.gamePath), "jomini");
63953
- return fs31.existsSync(jomini) ? [jomini] : [];
65519
+ const jomini = path36.join(path36.dirname(settings.gamePath), "jomini");
65520
+ return fs36.existsSync(jomini) ? [jomini] : [];
63954
65521
  }
63955
65522
  function workspaceModRoots() {
63956
65523
  const mods = [];
@@ -63965,6 +65532,21 @@ function workspaceRootOf(fsPath) {
63965
65532
  if (settings.modPath && lower.startsWith(settings.modPath.toLowerCase())) return settings.modPath;
63966
65533
  return workspaceModRoots().find((r) => lower.startsWith(r.toLowerCase())) ?? null;
63967
65534
  }
65535
+ var calendarByRoot = /* @__PURE__ */ new Map();
65536
+ function calendarFor(fsPath) {
65537
+ const root = workspaceRootOf(fsPath);
65538
+ if (root) {
65539
+ const key = root.toLowerCase();
65540
+ let cached2 = calendarByRoot.get(key);
65541
+ if (cached2 === void 0) {
65542
+ const read = readCalendarFile(root, activeProfile());
65543
+ cached2 = read?.calendar ? { calendar: read.calendar, source: path36.relative(root, read.file).replace(/\\/g, "/") } : null;
65544
+ calendarByRoot.set(key, cached2);
65545
+ }
65546
+ if (cached2) return cached2;
65547
+ }
65548
+ return { calendar: settings.calendar, source: "px.calendar" };
65549
+ }
63968
65550
  function schemaEntryForFile(fsPath) {
63969
65551
  const lower = fsPath.toLowerCase();
63970
65552
  for (const root of contentRoots()) {
@@ -64071,7 +65653,7 @@ function loadDocs(force) {
64071
65653
  let modifierTemplates = [];
64072
65654
  if (settings.logsPath) {
64073
65655
  const t0 = Date.now();
64074
- const docsCacheFile = path33.join(storageDir, `docsCache${activeProfile().cacheSuffix}.json`);
65656
+ const docsCacheFile = path36.join(storageDir, `docsCache${activeProfile().cacheSuffix}.json`);
64075
65657
  const result = loadTokenData(settings.logsPath, docsCacheFile, force);
64076
65658
  scriptTokens = result.tokens;
64077
65659
  modifierTemplates = result.templates;
@@ -64087,7 +65669,7 @@ function loadDocs(force) {
64087
65669
  }
64088
65670
  if (scriptTokens.length === 0 && bundledDumpsDir) {
64089
65671
  const t0 = Date.now();
64090
- const cacheFile = path33.join(storageDir, `docsCacheBundled${activeProfile().cacheSuffix}.json`);
65672
+ const cacheFile = path36.join(storageDir, `docsCacheBundled${activeProfile().cacheSuffix}.json`);
64091
65673
  const result = loadTokenData(bundledDumpsDir, cacheFile, force);
64092
65674
  scriptTokens = result.tokens;
64093
65675
  modifierTemplates = result.templates;
@@ -64115,13 +65697,13 @@ function loadDocs(force) {
64115
65697
  log(
64116
65698
  `wiki docs: ${wikiTokens.length} tokens, ${merged.enriched} usage examples merged in, ${merged.added} added${merged.dropped > 0 ? `, ${merged.dropped} dropped as absent from your script_docs` : ""}, total ${merged.tokens.length} (${Date.now() - t1}ms)`
64117
65699
  );
64118
- const onActionsDir = settings.logsPath && fs31.existsSync(path33.join(settings.logsPath, "on_actions.log")) ? settings.logsPath : bundledDumpsDir || settings.logsPath;
65700
+ const onActionsDir = settings.logsPath && fs36.existsSync(path36.join(settings.logsPath, "on_actions.log")) ? settings.logsPath : bundledDumpsDir || settings.logsPath;
64119
65701
  data.onActionScopes = onActionsDir ? parseOnActionsLog(onActionsDir) : /* @__PURE__ */ new Map();
64120
65702
  if (data.onActionScopes.size > 0) log(`on_actions.log: ${data.onActionScopes.size} on_action root scopes`);
64121
65703
  const dataTypeDirs = [bundledDataTypesDir || null];
64122
65704
  if (settings.logsPath) {
64123
- const sibling = path33.resolve(settings.logsPath, "..", "logs");
64124
- if (sibling.toLowerCase() !== path33.resolve(settings.logsPath).toLowerCase()) dataTypeDirs.push(sibling);
65705
+ const sibling = path36.resolve(settings.logsPath, "..", "logs");
65706
+ if (sibling.toLowerCase() !== path36.resolve(settings.logsPath).toLowerCase()) dataTypeDirs.push(sibling);
64125
65707
  dataTypeDirs.push(settings.logsPath);
64126
65708
  }
64127
65709
  data.dataTypes = loadDataTypes(dataTypeDirs);
@@ -64141,7 +65723,7 @@ function loadDocs(force) {
64141
65723
  log(`data types: ${data.dataTypes.count} entries incl. data-type dumps`);
64142
65724
  }
64143
65725
  const t2 = Date.now();
64144
- const usageCache = storageDir ? path33.join(storageDir, `dataFnUsage${activeProfile().cacheSuffix}.json`) : null;
65726
+ const usageCache = storageDir ? path36.join(storageDir, `dataFnUsage${activeProfile().cacheSuffix}.json`) : null;
64145
65727
  const generation = ++usageGeneration;
64146
65728
  void loadDataFnUsageAsync(settings.gamePath, settings.locLanguage, usageCache, force).then((result) => {
64147
65729
  if (generation !== usageGeneration) return;
@@ -64189,7 +65771,7 @@ function harvestEngineData() {
64189
65771
  var yieldNow2 = () => new Promise((resolve2) => setImmediate(resolve2));
64190
65772
  function readFileStripBom(file) {
64191
65773
  try {
64192
- return fs31.readFileSync(file, "utf8").replace(/^/, "");
65774
+ return fs36.readFileSync(file, "utf8").replace(/^/, "");
64193
65775
  } catch {
64194
65776
  return null;
64195
65777
  }
@@ -64204,7 +65786,7 @@ async function readBatchStripBom(files) {
64204
65786
  const i = next++;
64205
65787
  if (i >= files.length) return;
64206
65788
  try {
64207
- const content = await fs31.promises.readFile(files[i], "utf8");
65789
+ const content = await fs36.promises.readFile(files[i], "utf8");
64208
65790
  out[i] = content.replace(/^/, "");
64209
65791
  } catch {
64210
65792
  out[i] = null;
@@ -64221,7 +65803,7 @@ async function scanRootChunked(root, source, generation, onProgress) {
64221
65803
  const work = [];
64222
65804
  let totalFiles = 0;
64223
65805
  for (const entry of schema.entries) {
64224
- const dir = path33.join(root, ...entry.path.split("/"));
65806
+ const dir = path36.join(root, ...entry.path.split("/"));
64225
65807
  let files = [];
64226
65808
  for (const file of iterFiles(dir, entry.ext ?? ".txt")) {
64227
65809
  if (file === null) {
@@ -64232,12 +65814,12 @@ async function scanRootChunked(root, source, generation, onProgress) {
64232
65814
  }
64233
65815
  }
64234
65816
  if (entry.kind === "loc_key") {
64235
- files = files.filter((f) => isWantedLocFile(path33.relative(root, f), settings.locLanguage));
65817
+ files = files.filter((f) => isWantedLocFile(path36.relative(root, f), settings.locLanguage));
64236
65818
  }
64237
65819
  work.push({ entry, files });
64238
65820
  totalFiles += files.length;
64239
65821
  }
64240
- perf(`scan ${path33.basename(root)} listed ${totalFiles} files ${Date.now() - tList}ms`);
65822
+ perf(`scan ${path36.basename(root)} listed ${totalFiles} files ${Date.now() - tList}ms`);
64241
65823
  const tRead = Date.now();
64242
65824
  let done = 0;
64243
65825
  const BATCH3 = 150;
@@ -64257,7 +65839,7 @@ async function scanRootChunked(root, source, generation, onProgress) {
64257
65839
  }
64258
65840
  }
64259
65841
  perf(
64260
- `scan ${path33.basename(root)} (${source}) read+extract ${defs.length} defs from ${totalFiles} files ${Date.now() - tRead}ms`
65842
+ `scan ${path36.basename(root)} (${source}) read+extract ${defs.length} defs from ${totalFiles} files ${Date.now() - tRead}ms`
64261
65843
  );
64262
65844
  return defs;
64263
65845
  }
@@ -64280,12 +65862,12 @@ async function scanModRootBoth(root, generation) {
64280
65862
  data.index.addAll(result.defs);
64281
65863
  if (result.implicitDefs.length > 0) data.index.addAll(result.implicitDefs);
64282
65864
  rebuildModNamespaces();
64283
- perf(`scan ${path33.basename(root)} listed ${result.files} files ${result.listMs}ms`);
65865
+ perf(`scan ${path36.basename(root)} listed ${result.files} files ${result.listMs}ms`);
64284
65866
  perf(
64285
- `scan ${path33.basename(root)} (mod) read+extract ${result.defs.length} defs and ${result.references} refs from ${result.files} files ${Date.now() - t0 - result.listMs}ms`
65867
+ `scan ${path36.basename(root)} (mod) read+extract ${result.defs.length} defs and ${result.references} refs from ${result.files} files ${Date.now() - t0 - result.listMs}ms`
64286
65868
  );
64287
65869
  log(
64288
- `indexed ${path33.basename(root)} references: ${result.references} usage sites in ${result.scriptFiles} files (${Date.now() - t0}ms)`
65870
+ `indexed ${path36.basename(root)} references: ${result.references} usage sites in ${result.scriptFiles} files (${Date.now() - t0}ms)`
64289
65871
  );
64290
65872
  return result.defs.length;
64291
65873
  }
@@ -64296,16 +65878,16 @@ function rebuildModNamespaces() {
64296
65878
  }
64297
65879
  }
64298
65880
  function readPlayset(modPath) {
64299
- const file = path33.join(modPath, activeProfile().configDirName, "playset.json");
65881
+ const file = path36.join(resolveConfigDir(modPath, activeProfile()), "playset.json");
64300
65882
  try {
64301
- if (!fs31.existsSync(file)) return [];
64302
- const parsed = JSON.parse(fs31.readFileSync(file, "utf8"));
65883
+ if (!fs36.existsSync(file)) return [];
65884
+ const parsed = JSON.parse(fs36.readFileSync(file, "utf8"));
64303
65885
  const list2 = Array.isArray(parsed) ? parsed : Array.isArray(parsed?.parents) ? parsed.parents : [];
64304
65886
  const roots = [];
64305
65887
  for (const p of list2) {
64306
65888
  if (typeof p !== "string") continue;
64307
- const abs = path33.isAbsolute(p) ? p : path33.join(modPath, p);
64308
- if (fs31.existsSync(abs)) roots.push(abs);
65889
+ const abs = path36.isAbsolute(p) ? p : path36.join(modPath, p);
65890
+ if (fs36.existsSync(abs)) roots.push(abs);
64309
65891
  else log(`playset parent not found, skipped: ${p}`);
64310
65892
  }
64311
65893
  return roots;
@@ -64318,6 +65900,7 @@ async function buildIndex() {
64318
65900
  const tBuild = Date.now();
64319
65901
  const generation = ++scanGeneration;
64320
65902
  clearPathCaches();
65903
+ calendarByRoot.clear();
64321
65904
  schema = loadSchema([...settings.modPath ? [settings.modPath] : [], ...workspaceModRoots()], log);
64322
65905
  data.completableKinds = /* @__PURE__ */ new Set([
64323
65906
  ...schema.entries.filter((e) => e.completable !== false).map((e) => e.kind),
@@ -64341,34 +65924,34 @@ async function buildIndex() {
64341
65924
  try {
64342
65925
  if (settings.modPath) {
64343
65926
  const t0 = Date.now();
64344
- const count = await scanModRootBoth(settings.modPath, generation);
64345
- if (count === null) return;
65927
+ const count2 = await scanModRootBoth(settings.modPath, generation);
65928
+ if (count2 === null) return;
64346
65929
  indexChanged("mod scan");
64347
- log(`indexed mod: ${count} definitions (${Date.now() - t0}ms)`);
65930
+ log(`indexed mod: ${count2} definitions (${Date.now() - t0}ms)`);
64348
65931
  }
64349
65932
  const wsMods = new Set(workspaceModRoots().map((r) => r.toLowerCase()));
64350
65933
  for (const parent of parentRoots()) {
64351
65934
  const t1 = Date.now();
64352
65935
  const isWorkspaceMod = wsMods.has(parent.toLowerCase());
64353
- let count;
65936
+ let count2;
64354
65937
  if (isWorkspaceMod) {
64355
- count = await scanModRootBoth(parent, generation);
65938
+ count2 = await scanModRootBoth(parent, generation);
64356
65939
  } else {
64357
65940
  const parentDefs = await scanRootChunked(parent, "parent", generation);
64358
- count = parentDefs === null ? null : parentDefs.length;
65941
+ count2 = parentDefs === null ? null : parentDefs.length;
64359
65942
  if (parentDefs !== null) data.index.addAll(parentDefs);
64360
65943
  }
64361
- if (count === null) return;
64362
- indexChanged(`parent scan ${path33.basename(parent)}`);
65944
+ if (count2 === null) return;
65945
+ indexChanged(`parent scan ${path36.basename(parent)}`);
64363
65946
  log(
64364
- `indexed ${isWorkspaceMod ? "workspace mod" : "parent mod"} ${path33.basename(parent)}: ${count} definitions (${Date.now() - t1}ms)`
65947
+ `indexed ${isWorkspaceMod ? "workspace mod" : "parent mod"} ${path36.basename(parent)}: ${count2} definitions (${Date.now() - t1}ms)`
64365
65948
  );
64366
65949
  }
64367
65950
  if (settings.gamePath) {
64368
65951
  const gamePath = settings.gamePath;
64369
65952
  const t0 = Date.now();
64370
65953
  const version2 = detectGameVersion(gamePath);
64371
- const cacheFile = path33.join(
65954
+ const cacheFile = path36.join(
64372
65955
  storageDir,
64373
65956
  `vanillaIndex${activeProfile().cacheSuffix}-${settings.locLanguage}.json`
64374
65957
  );
@@ -64455,13 +66038,13 @@ function rescanModFile(fsPath) {
64455
66038
  const entry = classifyFile(root, fsPath, schema.entries);
64456
66039
  const isScript = lower.endsWith(".txt");
64457
66040
  if (!entry && !isScript) return;
64458
- if (entry?.kind === "loc_key" && !isWantedLocFile(path33.relative(root, fsPath), settings.locLanguage))
66041
+ if (entry?.kind === "loc_key" && !isWantedLocFile(path36.relative(root, fsPath), settings.locLanguage))
64459
66042
  return;
64460
66043
  const tParse = Date.now();
64461
- const content = fs31.existsSync(fsPath) ? readFileStripBom(fsPath) : null;
66044
+ const content = fs36.existsSync(fsPath) ? readFileStripBom(fsPath) : null;
64462
66045
  const digest = contentDigest(content);
64463
66046
  if (rescanDigests.get(lower) === digest) {
64464
- perf(`rescan ${path33.basename(fsPath)} unchanged bytes, skipped ${Date.now() - tParse}ms`);
66047
+ perf(`rescan ${path36.basename(fsPath)} unchanged bytes, skipped ${Date.now() - tParse}ms`);
64465
66048
  return;
64466
66049
  }
64467
66050
  rescanDigests.set(lower, digest);
@@ -64488,22 +66071,22 @@ function rescanModFile(fsPath) {
64488
66071
  }
64489
66072
  perfSpan("rescan namespaces", () => rebuildModNamespaces());
64490
66073
  }
64491
- perf(`rescan ${path33.basename(fsPath)} parse+extract ${Date.now() - tParse}ms`);
64492
- indexChanged(`rescan ${path33.basename(fsPath)}`);
64493
- log(`re-indexed ${path33.basename(fsPath)}`);
66074
+ perf(`rescan ${path36.basename(fsPath)} parse+extract ${Date.now() - tParse}ms`);
66075
+ indexChanged(`rescan ${path36.basename(fsPath)}`);
66076
+ log(`re-indexed ${path36.basename(fsPath)}`);
64494
66077
  }
64495
66078
  function deriveBundledDataDirs() {
64496
- const gameDir = path33.join(clientDataDir || path33.resolve(__dirname, "..", "data"), activeProfile().id);
66079
+ const gameDir = path36.join(clientDataDir || path36.resolve(__dirname, "..", "data"), activeProfile().id);
64497
66080
  wikidocsDir = clientWikidocsDir;
64498
66081
  if (!wikidocsDir) {
64499
- const bundled = path33.join(gameDir, "wikidocs");
64500
- if (fs31.existsSync(bundled)) wikidocsDir = bundled;
66082
+ const bundled = path36.join(gameDir, "wikidocs");
66083
+ if (fs36.existsSync(bundled)) wikidocsDir = bundled;
64501
66084
  }
64502
- freqsDir = fs31.existsSync(path33.join(gameDir, "freqs.json")) ? gameDir : "";
64503
- const dumps = path33.join(gameDir, "script_docs");
64504
- bundledDumpsDir = fs31.existsSync(dumps) ? dumps : "";
64505
- const dataTypes = path33.join(gameDir, "data_types");
64506
- bundledDataTypesDir = fs31.existsSync(dataTypes) ? dataTypes : "";
66085
+ freqsDir = fs36.existsSync(path36.join(gameDir, "freqs.json")) ? gameDir : "";
66086
+ const dumps = path36.join(gameDir, "script_docs");
66087
+ bundledDumpsDir = fs36.existsSync(dumps) ? dumps : "";
66088
+ const dataTypes = path36.join(gameDir, "data_types");
66089
+ bundledDataTypesDir = fs36.existsSync(dataTypes) ? dataTypes : "";
64507
66090
  }
64508
66091
  var clientDataDir = "";
64509
66092
  var clientWikidocsDir = "";
@@ -64526,9 +66109,9 @@ connection.onInitialize((params) => {
64526
66109
  setActiveProfile(resolveProfile(settings.gameId));
64527
66110
  deriveBundledDataDirs();
64528
66111
  if (!storageDir) {
64529
- storageDir = path33.join(os.tmpdir(), "px-lsp");
66112
+ storageDir = path36.join(os.tmpdir(), "px-lsp");
64530
66113
  try {
64531
- fs31.mkdirSync(storageDir, { recursive: true });
66114
+ fs36.mkdirSync(storageDir, { recursive: true });
64532
66115
  } catch {
64533
66116
  storageDir = "";
64534
66117
  }
@@ -64576,7 +66159,11 @@ connection.onInitialized(() => {
64576
66159
  }
64577
66160
  if (!clientOwnFileWatcher && clientWatchedFilesDynamic) {
64578
66161
  void connection.client.register(import_node17.DidChangeWatchedFilesNotification.type, {
64579
- watchers: [{ globPattern: "**/*.{txt,yml,gui,mod}" }, { globPattern: "**/metadata.json" }]
66162
+ watchers: [
66163
+ { globPattern: "**/*.{txt,yml,gui,mod}" },
66164
+ { globPattern: "**/metadata.json" },
66165
+ { globPattern: "**/calendar.json" }
66166
+ ]
64580
66167
  });
64581
66168
  }
64582
66169
  applyFreqs();
@@ -64643,7 +66230,7 @@ function indexRead(label, fn) {
64643
66230
  }
64644
66231
  function mentionsTextFormatting(fsPath) {
64645
66232
  try {
64646
- return fs31.readFileSync(fsPath, "utf8").includes("textformatting");
66233
+ return fs36.readFileSync(fsPath, "utf8").includes("textformatting");
64647
66234
  } catch {
64648
66235
  return false;
64649
66236
  }
@@ -64653,6 +66240,7 @@ function applyModFileChange(fsPath) {
64653
66240
  const lower = fsPath.toLowerCase();
64654
66241
  if (lower.endsWith(".mod") || lower.endsWith("metadata.json")) refreshModOrigin();
64655
66242
  if (lower.endsWith(".gui")) invalidateGuiDefsCache();
66243
+ if (isCalendarFile(fsPath, activeProfile())) calendarByRoot.clear();
64656
66244
  if (lower.replace(/\\/g, "/").includes("common/defines/") || lower.endsWith(".gui") && mentionsTextFormatting(fsPath))
64657
66245
  harvestEngineData();
64658
66246
  }
@@ -64680,6 +66268,10 @@ connection.onRequest(
64680
66268
  eventGraphRequest,
64681
66269
  (params) => computeEventGraph(data, params ?? {}, focusFilter(params?.modRoot))
64682
66270
  );
66271
+ connection.onRequest(
66272
+ dynastyTreeRequest,
66273
+ (params) => computeDynastyTree(data, params ?? {}, focusFilter(params?.modRoot))
66274
+ );
64683
66275
  connection.onRequest(
64684
66276
  eventVocabularyRequest,
64685
66277
  (params) => computeEventVocabulary(data, schema, focusFilter(params?.modRoot))
@@ -64692,6 +66284,18 @@ connection.onRequest(
64692
66284
  eventBannerRequest,
64693
66285
  (params) => computeEventBanner(data, params?.theme ?? "")
64694
66286
  );
66287
+ connection.onRequest(
66288
+ definitionFormRequest,
66289
+ (params) => computeDefinitionForm(data, schema, params ?? { kind: "" }, focusFilter(params?.modRoot))
66290
+ );
66291
+ connection.onRequest(
66292
+ definitionEditRequest,
66293
+ (params) => computeDefinitionEdits(params)
66294
+ );
66295
+ connection.onRequest(
66296
+ modifierFormatsRequest,
66297
+ (params) => computeModifierFormats(data, activeProfile().modifierFormats, settings.gamePath, params?.lines)
66298
+ );
64695
66299
  connection.onRequest(guiTreeRequest, (params) => buildGuiTree(params.text ?? ""));
64696
66300
  function locValue(key) {
64697
66301
  return data.index.lookup(key).find((d) => d.kind === "loc_key" && d.value !== void 0)?.value;
@@ -64759,7 +66363,7 @@ connection.onRequest(
64759
66363
  const file = params?.path ?? "";
64760
66364
  let key = file;
64761
66365
  try {
64762
- key = `${file}:${fs31.statSync(file).mtimeMs}`;
66366
+ key = `${file}:${fs36.statSync(file).mtimeMs}`;
64763
66367
  } catch {
64764
66368
  }
64765
66369
  if (saveValues?.key === key) return saveValues.result;
@@ -64818,9 +66422,43 @@ connection.onRequest(scopeAtRequest, (params) => {
64818
66422
  entry
64819
66423
  );
64820
66424
  });
66425
+ connection.onRequest(snippetsRequest, (params) => {
66426
+ const doc = params?.uri ? documents.get(params.uri) : void 0;
66427
+ if (!doc || !isScriptLanguage(doc.languageId) || !params.position) return { snippets: [] };
66428
+ const entry = schemaEntryForFile(URI2.parse(doc.uri).fsPath);
66429
+ const { result } = getParse(doc);
66430
+ return {
66431
+ snippets: buildSnippetList(
66432
+ result,
66433
+ doc.offsetAt(params.position),
66434
+ entry?.kind ?? null,
66435
+ activeProfile().skeletons,
66436
+ data.tokens,
66437
+ freqs.tokens
66438
+ )
66439
+ };
66440
+ });
64821
66441
  connection.onRequest(lookupLocRequest, (params) => {
64822
66442
  return data.index.lookup(params.key).filter((d) => d.kind === "loc_key").map((d) => ({ file: d.file, line: d.line, source: d.source, value: d.value }));
64823
66443
  });
66444
+ function locTextDeps() {
66445
+ return {
66446
+ loc: locValue,
66447
+ kindsOf: (name) => [
66448
+ ...new Set(
66449
+ data.index.lookup(name).filter((d) => d.kind !== "loc_key").map((d) => d.kind)
66450
+ )
66451
+ ],
66452
+ patternsOf: (kind) => {
66453
+ const entry = schema.entries.find((e) => e.kind === kind);
66454
+ return [entry?.requiredLoc?.[0], entry?.locPatterns?.[0]].filter((p) => !!p);
66455
+ }
66456
+ };
66457
+ }
66458
+ connection.onRequest(
66459
+ locTextRequest,
66460
+ (params) => computeLocText(params?.keys ?? [], locTextDeps())
66461
+ );
64824
66462
  connection.onCompletion(
64825
66463
  (params) => indexRead(`completion ${perfName(params.textDocument.uri)}`, () => {
64826
66464
  const doc = documents.get(params.textDocument.uri);
@@ -64883,7 +66521,8 @@ connection.onHover(
64883
66521
  if (!isScriptLanguage(doc.languageId)) return null;
64884
66522
  const fsPath = URI2.parse(doc.uri).fsPath;
64885
66523
  const entry = schemaEntryForFile(fsPath);
64886
- const dateHover = provideDateHover(settings.calendar, doc, params.position);
66524
+ const { calendar, source } = calendarFor(fsPath);
66525
+ const dateHover = provideDateHover(calendar, doc, params.position, source);
64887
66526
  if (dateHover) return dateHover;
64888
66527
  const texture = provideTextureHover(settings, doc, params.position, entry?.kind);
64889
66528
  if (texture) return texture;
@@ -64958,9 +66597,18 @@ connection.languages.inlayHint.on(
64958
66597
  (params) => indexRead(`inlayHint ${perfName(params.textDocument.uri)}`, () => {
64959
66598
  const doc = documents.get(params.textDocument.uri);
64960
66599
  if (!doc) return [];
64961
- const entry = schemaEntryForFile(URI2.parse(doc.uri).fsPath);
66600
+ const fsPath = URI2.parse(doc.uri).fsPath;
66601
+ const entry = schemaEntryForFile(fsPath);
64962
66602
  const rootScopes = entry?.rootScopes?.length ? new Set(entry.rootScopes.map((s2) => s2.toLowerCase())) : null;
64963
- return provideInlayHints(data, settings, doc, params.range, rootScopes, entry);
66603
+ return provideInlayHints(
66604
+ data,
66605
+ settings,
66606
+ doc,
66607
+ params.range,
66608
+ rootScopes,
66609
+ entry,
66610
+ calendarFor(fsPath).calendar
66611
+ );
64964
66612
  })
64965
66613
  );
64966
66614
  connection.languages.semanticTokens.on(
@@ -65035,13 +66683,13 @@ var validatedAt = /* @__PURE__ */ new Map();
65035
66683
  function readBomFromDisk(uri) {
65036
66684
  try {
65037
66685
  const fsPath = URI2.parse(uri).fsPath;
65038
- const fd = fs31.openSync(fsPath, "r");
66686
+ const fd = fs36.openSync(fsPath, "r");
65039
66687
  try {
65040
66688
  const buf = Buffer.alloc(3);
65041
- const n = fs31.readSync(fd, buf, 0, 3, 0);
66689
+ const n = fs36.readSync(fd, buf, 0, 3, 0);
65042
66690
  return n >= 3 && buf[0] === 239 && buf[1] === 187 && buf[2] === 191;
65043
66691
  } finally {
65044
- fs31.closeSync(fd);
66692
+ fs36.closeSync(fd);
65045
66693
  }
65046
66694
  } catch {
65047
66695
  return null;
@@ -65106,11 +66754,11 @@ function filterSuppressed(diagnostics, fsPath, text) {
65106
66754
  ignorePatterns: settings.diagnosticsIgnorePatterns
65107
66755
  };
65108
66756
  const rel = relForPatterns(fsPath);
65109
- const inline = scanInlineSuppressions(text);
66757
+ const inline2 = scanInlineSuppressions(text);
65110
66758
  return diagnostics.filter((d) => {
65111
66759
  const code = typeof d.code === "string" ? d.code : d.code !== void 0 ? String(d.code) : void 0;
65112
66760
  if (isIgnoredByConfig(cfg, code, rel)) return false;
65113
- if (isSuppressedInline(inline, d.range.start.line, code)) return false;
66761
+ if (isSuppressedInline(inline2, d.range.start.line, code)) return false;
65114
66762
  return true;
65115
66763
  });
65116
66764
  }