@thijshakkenberg/valkyrie-mom-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/.claude-plugin/plugin.json +17 -0
  2. package/.mcp.json +9 -0
  3. package/CLAUDE.md +73 -0
  4. package/LICENSE +21 -0
  5. package/README.md +164 -0
  6. package/agents/scenario-designer.md +114 -0
  7. package/dist/catalogs/catalog-store.d.ts +20 -0
  8. package/dist/catalogs/catalog-store.d.ts.map +1 -0
  9. package/dist/catalogs/catalog-store.js +78 -0
  10. package/dist/catalogs/catalog-store.js.map +1 -0
  11. package/dist/catalogs/catalog-types.d.ts +10 -0
  12. package/dist/catalogs/catalog-types.d.ts.map +1 -0
  13. package/dist/catalogs/catalog-types.js +2 -0
  14. package/dist/catalogs/catalog-types.js.map +1 -0
  15. package/dist/catalogs/data/all-catalogs.d.ts +9 -0
  16. package/dist/catalogs/data/all-catalogs.d.ts.map +1 -0
  17. package/dist/catalogs/data/all-catalogs.js +8383 -0
  18. package/dist/catalogs/data/all-catalogs.js.map +1 -0
  19. package/dist/catalogs/data/tile-geometry.d.ts +12 -0
  20. package/dist/catalogs/data/tile-geometry.d.ts.map +1 -0
  21. package/dist/catalogs/data/tile-geometry.js +183 -0
  22. package/dist/catalogs/data/tile-geometry.js.map +1 -0
  23. package/dist/catalogs/extract-catalogs.d.ts +12 -0
  24. package/dist/catalogs/extract-catalogs.d.ts.map +1 -0
  25. package/dist/catalogs/extract-catalogs.js +128 -0
  26. package/dist/catalogs/extract-catalogs.js.map +1 -0
  27. package/dist/index.d.ts +3 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +13 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/io/ini-parser.d.ts +6 -0
  32. package/dist/io/ini-parser.d.ts.map +1 -0
  33. package/dist/io/ini-parser.js +42 -0
  34. package/dist/io/ini-parser.js.map +1 -0
  35. package/dist/io/ini-writer.d.ts +2 -0
  36. package/dist/io/ini-writer.d.ts.map +1 -0
  37. package/dist/io/ini-writer.js +36 -0
  38. package/dist/io/ini-writer.js.map +1 -0
  39. package/dist/io/localization-io.d.ts +21 -0
  40. package/dist/io/localization-io.d.ts.map +1 -0
  41. package/dist/io/localization-io.js +51 -0
  42. package/dist/io/localization-io.js.map +1 -0
  43. package/dist/io/package-builder.d.ts +2 -0
  44. package/dist/io/package-builder.d.ts.map +1 -0
  45. package/dist/io/package-builder.js +22 -0
  46. package/dist/io/package-builder.js.map +1 -0
  47. package/dist/model/component-types.d.ts +61 -0
  48. package/dist/model/component-types.d.ts.map +1 -0
  49. package/dist/model/component-types.js +71 -0
  50. package/dist/model/component-types.js.map +1 -0
  51. package/dist/model/localization-store.d.ts +15 -0
  52. package/dist/model/localization-store.d.ts.map +1 -0
  53. package/dist/model/localization-store.js +42 -0
  54. package/dist/model/localization-store.js.map +1 -0
  55. package/dist/model/scenario-model.d.ts +27 -0
  56. package/dist/model/scenario-model.d.ts.map +1 -0
  57. package/dist/model/scenario-model.js +151 -0
  58. package/dist/model/scenario-model.js.map +1 -0
  59. package/dist/resources/format-docs.d.ts +6 -0
  60. package/dist/resources/format-docs.d.ts.map +1 -0
  61. package/dist/resources/format-docs.js +261 -0
  62. package/dist/resources/format-docs.js.map +1 -0
  63. package/dist/server.d.ts +3 -0
  64. package/dist/server.d.ts.map +1 -0
  65. package/dist/server.js +206 -0
  66. package/dist/server.js.map +1 -0
  67. package/dist/tools/lifecycle.d.ts +37 -0
  68. package/dist/tools/lifecycle.d.ts.map +1 -0
  69. package/dist/tools/lifecycle.js +146 -0
  70. package/dist/tools/lifecycle.js.map +1 -0
  71. package/dist/tools/map.d.ts +14 -0
  72. package/dist/tools/map.d.ts.map +1 -0
  73. package/dist/tools/map.js +98 -0
  74. package/dist/tools/map.js.map +1 -0
  75. package/dist/tools/reference.d.ts +4 -0
  76. package/dist/tools/reference.d.ts.map +1 -0
  77. package/dist/tools/reference.js +6 -0
  78. package/dist/tools/reference.js.map +1 -0
  79. package/dist/tools/shared.d.ts +10 -0
  80. package/dist/tools/shared.d.ts.map +1 -0
  81. package/dist/tools/shared.js +22 -0
  82. package/dist/tools/shared.js.map +1 -0
  83. package/dist/tools/upsert.d.ts +15 -0
  84. package/dist/tools/upsert.d.ts.map +1 -0
  85. package/dist/tools/upsert.js +91 -0
  86. package/dist/tools/upsert.js.map +1 -0
  87. package/dist/validation/rules/catalog-references.d.ts +14 -0
  88. package/dist/validation/rules/catalog-references.d.ts.map +1 -0
  89. package/dist/validation/rules/catalog-references.js +90 -0
  90. package/dist/validation/rules/catalog-references.js.map +1 -0
  91. package/dist/validation/rules/cross-references.d.ts +9 -0
  92. package/dist/validation/rules/cross-references.d.ts.map +1 -0
  93. package/dist/validation/rules/cross-references.js +37 -0
  94. package/dist/validation/rules/cross-references.js.map +1 -0
  95. package/dist/validation/rules/event-chain-utils.d.ts +8 -0
  96. package/dist/validation/rules/event-chain-utils.d.ts.map +1 -0
  97. package/dist/validation/rules/event-chain-utils.js +30 -0
  98. package/dist/validation/rules/event-chain-utils.js.map +1 -0
  99. package/dist/validation/rules/event-flow.d.ts +14 -0
  100. package/dist/validation/rules/event-flow.d.ts.map +1 -0
  101. package/dist/validation/rules/event-flow.js +83 -0
  102. package/dist/validation/rules/event-flow.js.map +1 -0
  103. package/dist/validation/rules/event-graph.d.ts +10 -0
  104. package/dist/validation/rules/event-graph.d.ts.map +1 -0
  105. package/dist/validation/rules/event-graph.js +51 -0
  106. package/dist/validation/rules/event-graph.js.map +1 -0
  107. package/dist/validation/rules/explore-token-pattern.d.ts +10 -0
  108. package/dist/validation/rules/explore-token-pattern.d.ts.map +1 -0
  109. package/dist/validation/rules/explore-token-pattern.js +70 -0
  110. package/dist/validation/rules/explore-token-pattern.js.map +1 -0
  111. package/dist/validation/rules/format-rules.d.ts +10 -0
  112. package/dist/validation/rules/format-rules.d.ts.map +1 -0
  113. package/dist/validation/rules/format-rules.js +61 -0
  114. package/dist/validation/rules/format-rules.js.map +1 -0
  115. package/dist/validation/rules/investigator-token-pattern.d.ts +9 -0
  116. package/dist/validation/rules/investigator-token-pattern.d.ts.map +1 -0
  117. package/dist/validation/rules/investigator-token-pattern.js +39 -0
  118. package/dist/validation/rules/investigator-token-pattern.js.map +1 -0
  119. package/dist/validation/rules/localization-completeness.d.ts +11 -0
  120. package/dist/validation/rules/localization-completeness.d.ts.map +1 -0
  121. package/dist/validation/rules/localization-completeness.js +83 -0
  122. package/dist/validation/rules/localization-completeness.js.map +1 -0
  123. package/dist/validation/rules/mythos-structure.d.ts +10 -0
  124. package/dist/validation/rules/mythos-structure.d.ts.map +1 -0
  125. package/dist/validation/rules/mythos-structure.js +57 -0
  126. package/dist/validation/rules/mythos-structure.js.map +1 -0
  127. package/dist/validation/rules/required-fields.d.ts +9 -0
  128. package/dist/validation/rules/required-fields.d.ts.map +1 -0
  129. package/dist/validation/rules/required-fields.js +38 -0
  130. package/dist/validation/rules/required-fields.js.map +1 -0
  131. package/dist/validation/rules/tile-connectivity.d.ts +9 -0
  132. package/dist/validation/rules/tile-connectivity.d.ts.map +1 -0
  133. package/dist/validation/rules/tile-connectivity.js +125 -0
  134. package/dist/validation/rules/tile-connectivity.js.map +1 -0
  135. package/dist/validation/validator.d.ts +7 -0
  136. package/dist/validation/validator.d.ts.map +1 -0
  137. package/dist/validation/validator.js +35 -0
  138. package/dist/validation/validator.js.map +1 -0
  139. package/package.json +59 -0
  140. package/skills/custom-monsters/SKILL.md +228 -0
  141. package/skills/event-patterns/SKILL.md +268 -0
  142. package/skills/items-and-distribution/SKILL.md +193 -0
  143. package/skills/scenario/SKILL.md +105 -0
  144. package/skills/tile-placement/SKILL.md +163 -0
  145. package/skills/ui-and-puzzles/SKILL.md +347 -0
  146. package/skills/variables-and-mythos/SKILL.md +285 -0
  147. package/src/catalogs/catalog-store.ts +94 -0
  148. package/src/catalogs/catalog-types.ts +10 -0
  149. package/src/catalogs/data/all-catalogs.ts +8391 -0
  150. package/src/catalogs/data/tile-geometry.ts +188 -0
  151. package/src/catalogs/extract-catalogs.ts +172 -0
  152. package/src/index.ts +14 -0
  153. package/src/io/ini-parser.ts +52 -0
  154. package/src/io/ini-writer.ts +51 -0
  155. package/src/io/localization-io.ts +62 -0
  156. package/src/io/package-builder.ts +26 -0
  157. package/src/model/component-types.ts +124 -0
  158. package/src/model/localization-store.ts +52 -0
  159. package/src/model/scenario-model.ts +177 -0
  160. package/src/resources/format-docs.ts +264 -0
  161. package/src/server.ts +336 -0
  162. package/src/tools/lifecycle.ts +183 -0
  163. package/src/tools/map.ts +125 -0
  164. package/src/tools/reference.ts +7 -0
  165. package/src/tools/shared.ts +34 -0
  166. package/src/tools/upsert.ts +133 -0
  167. package/src/validation/rules/catalog-references.ts +95 -0
  168. package/src/validation/rules/cross-references.ts +42 -0
  169. package/src/validation/rules/event-chain-utils.ts +31 -0
  170. package/src/validation/rules/event-flow.ts +93 -0
  171. package/src/validation/rules/event-graph.ts +62 -0
  172. package/src/validation/rules/explore-token-pattern.ts +77 -0
  173. package/src/validation/rules/format-rules.ts +69 -0
  174. package/src/validation/rules/investigator-token-pattern.ts +47 -0
  175. package/src/validation/rules/localization-completeness.ts +93 -0
  176. package/src/validation/rules/mythos-structure.ts +64 -0
  177. package/src/validation/rules/required-fields.ts +43 -0
  178. package/src/validation/rules/tile-connectivity.ts +148 -0
  179. package/src/validation/validator.ts +38 -0
  180. package/tsconfig.json +20 -0
@@ -0,0 +1,15 @@
1
+ export declare class LocalizationStore {
2
+ private data;
3
+ private language;
4
+ constructor(language?: string);
5
+ get(key: string): string | undefined;
6
+ set(key: string, value: string): void;
7
+ delete(key: string): boolean;
8
+ has(key: string): boolean;
9
+ entries(): IterableIterator<[string, string]>;
10
+ keys(): IterableIterator<string>;
11
+ get size(): number;
12
+ toCSV(): string;
13
+ static fromCSV(content: string): LocalizationStore;
14
+ }
15
+ //# sourceMappingURL=localization-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localization-store.d.ts","sourceRoot":"","sources":["../../src/model/localization-store.ts"],"names":[],"mappings":"AAEA,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,IAAI,CAAsB;IAClC,OAAO,CAAC,QAAQ,CAAS;gBAEb,QAAQ,SAAY;IAKhC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIpC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAIrC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAI5B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzB,OAAO,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAI7C,IAAI,IAAI,gBAAgB,CAAC,MAAM,CAAC;IAIhC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,KAAK,IAAI,MAAM;IAIf,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB;CAQnD"}
@@ -0,0 +1,42 @@
1
+ import { parseLocalization, writeLocalization } from '../io/localization-io.js';
2
+ export class LocalizationStore {
3
+ data;
4
+ language;
5
+ constructor(language = 'English') {
6
+ this.data = new Map();
7
+ this.language = language;
8
+ }
9
+ get(key) {
10
+ return this.data.get(key);
11
+ }
12
+ set(key, value) {
13
+ this.data.set(key, value);
14
+ }
15
+ delete(key) {
16
+ return this.data.delete(key);
17
+ }
18
+ has(key) {
19
+ return this.data.has(key);
20
+ }
21
+ entries() {
22
+ return this.data.entries();
23
+ }
24
+ keys() {
25
+ return this.data.keys();
26
+ }
27
+ get size() {
28
+ return this.data.size;
29
+ }
30
+ toCSV() {
31
+ return writeLocalization(this.language, this.data);
32
+ }
33
+ static fromCSV(content) {
34
+ const parsed = parseLocalization(content);
35
+ const store = new LocalizationStore(parsed.language);
36
+ for (const [key, value] of parsed.entries) {
37
+ store.set(key, value);
38
+ }
39
+ return store;
40
+ }
41
+ }
42
+ //# sourceMappingURL=localization-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localization-store.js","sourceRoot":"","sources":["../../src/model/localization-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAEhF,MAAM,OAAO,iBAAiB;IACpB,IAAI,CAAsB;IAC1B,QAAQ,CAAS;IAEzB,YAAY,QAAQ,GAAG,SAAS;QAC9B,IAAI,CAAC,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC;QACtB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,KAAa;QAC5B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC5B,CAAC;IAED,MAAM,CAAC,GAAW;QAChB,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC/B,CAAC;IAED,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;IAC7B,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IAC1B,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;IACxB,CAAC;IAED,KAAK;QACH,OAAO,iBAAiB,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACrD,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,OAAe;QAC5B,MAAM,MAAM,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAC1C,MAAM,KAAK,GAAG,IAAI,iBAAiB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACrD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YAC1C,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACxB,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;CACF"}
@@ -0,0 +1,27 @@
1
+ import { type IniFile, type QuestConfig, type ScenarioComponent } from './component-types.js';
2
+ import { LocalizationStore } from './localization-store.js';
3
+ export declare class ScenarioModel {
4
+ questConfig: QuestConfig;
5
+ private components;
6
+ localization: LocalizationStore;
7
+ scenarioDir: string;
8
+ constructor(config?: Partial<QuestConfig>, scenarioDir?: string);
9
+ upsert(name: string, data: Record<string, string>): void;
10
+ get(name: string): ScenarioComponent | undefined;
11
+ delete(name: string): string[];
12
+ getAll(): ScenarioComponent[];
13
+ getByType(prefix: string): ScenarioComponent[];
14
+ getComponentsByFile(file: IniFile): ScenarioComponent[];
15
+ /** Find all components that reference the target name */
16
+ getReferencesTo(targetName: string): Array<{
17
+ from: string;
18
+ field: string;
19
+ }>;
20
+ /** Get all component names referenced by a component */
21
+ getReferencesFrom(name: string): string[];
22
+ /** Serialize all components grouped by INI file (strips undefined values) */
23
+ serializeToIniData(): Record<string, Record<string, Record<string, string>>>;
24
+ /** Load a model from raw file contents (no disk IO) */
25
+ static loadFromData(questIniContent: string, dataFileContents: Record<string, string>, localizationContent?: string): ScenarioModel;
26
+ }
27
+ //# sourceMappingURL=scenario-model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scenario-model.d.ts","sourceRoot":"","sources":["../../src/model/scenario-model.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAEZ,KAAK,WAAW,EAChB,KAAK,iBAAiB,EAKvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAG5D,qBAAa,aAAa;IACxB,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,CAAC,UAAU,CAA6C;IAC/D,YAAY,EAAE,iBAAiB,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;gBAER,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,EAAE,WAAW,SAAK;IAM3D,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAcxD,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAIhD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IA6B9B,MAAM,IAAI,iBAAiB,EAAE;IAI7B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,EAAE;IAI9C,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,iBAAiB,EAAE;IAIvD,yDAAyD;IACzD,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAe3E,wDAAwD;IACxD,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAczC,6EAA6E;IAC7E,kBAAkB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAe5E,uDAAuD;IACvD,MAAM,CAAC,YAAY,CACjB,eAAe,EAAE,MAAM,EACvB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACxC,mBAAmB,CAAC,EAAE,MAAM,GAC3B,aAAa;CAwCjB"}
@@ -0,0 +1,151 @@
1
+ import { DEFAULT_QUEST_CONFIG, REFERENCE_FIELDS, parseRefList, getIniFileForSection, } from './component-types.js';
2
+ import { LocalizationStore } from './localization-store.js';
3
+ import { parseIni } from '../io/ini-parser.js';
4
+ export class ScenarioModel {
5
+ questConfig;
6
+ components = new Map();
7
+ localization;
8
+ scenarioDir;
9
+ constructor(config, scenarioDir = '') {
10
+ this.questConfig = { ...DEFAULT_QUEST_CONFIG, ...config };
11
+ this.localization = new LocalizationStore();
12
+ this.scenarioDir = scenarioDir;
13
+ }
14
+ upsert(name, data) {
15
+ const existing = this.components.get(name);
16
+ if (existing) {
17
+ // Merge: new data overwrites, existing fields preserved
18
+ existing.data = { ...existing.data, ...data };
19
+ }
20
+ else {
21
+ this.components.set(name, {
22
+ name,
23
+ file: getIniFileForSection(name),
24
+ data: { ...data },
25
+ });
26
+ }
27
+ }
28
+ get(name) {
29
+ return this.components.get(name);
30
+ }
31
+ delete(name) {
32
+ const existed = this.components.has(name);
33
+ if (!existed)
34
+ return [];
35
+ this.components.delete(name);
36
+ // Cascade: clean references to deleted component in all other components
37
+ const cascaded = [];
38
+ for (const comp of this.components.values()) {
39
+ let changed = false;
40
+ for (const field of REFERENCE_FIELDS) {
41
+ const val = comp.data[field];
42
+ if (val === undefined)
43
+ continue;
44
+ const refs = parseRefList(val);
45
+ if (refs.includes(name)) {
46
+ const newRefs = refs.filter(r => r !== name);
47
+ comp.data[field] = newRefs.join(' ');
48
+ changed = true;
49
+ }
50
+ }
51
+ if (changed) {
52
+ cascaded.push(comp.name);
53
+ }
54
+ }
55
+ return cascaded;
56
+ }
57
+ getAll() {
58
+ return [...this.components.values()];
59
+ }
60
+ getByType(prefix) {
61
+ return this.getAll().filter(c => c.name.startsWith(prefix));
62
+ }
63
+ getComponentsByFile(file) {
64
+ return this.getAll().filter(c => c.file === file);
65
+ }
66
+ /** Find all components that reference the target name */
67
+ getReferencesTo(targetName) {
68
+ const results = [];
69
+ for (const comp of this.components.values()) {
70
+ for (const field of REFERENCE_FIELDS) {
71
+ const val = comp.data[field];
72
+ if (val === undefined)
73
+ continue;
74
+ const refs = parseRefList(val);
75
+ if (refs.includes(targetName)) {
76
+ results.push({ from: comp.name, field });
77
+ }
78
+ }
79
+ }
80
+ return results;
81
+ }
82
+ /** Get all component names referenced by a component */
83
+ getReferencesFrom(name) {
84
+ const comp = this.components.get(name);
85
+ if (!comp)
86
+ return [];
87
+ const refs = new Set();
88
+ for (const field of REFERENCE_FIELDS) {
89
+ const val = comp.data[field];
90
+ if (val === undefined)
91
+ continue;
92
+ for (const ref of parseRefList(val)) {
93
+ refs.add(ref);
94
+ }
95
+ }
96
+ return [...refs];
97
+ }
98
+ /** Serialize all components grouped by INI file (strips undefined values) */
99
+ serializeToIniData() {
100
+ const result = {};
101
+ for (const comp of this.components.values()) {
102
+ if (!result[comp.file]) {
103
+ result[comp.file] = {};
104
+ }
105
+ const clean = {};
106
+ for (const [k, v] of Object.entries(comp.data)) {
107
+ if (v !== undefined)
108
+ clean[k] = v;
109
+ }
110
+ result[comp.file][comp.name] = clean;
111
+ }
112
+ return result;
113
+ }
114
+ /** Load a model from raw file contents (no disk IO) */
115
+ static loadFromData(questIniContent, dataFileContents, localizationContent) {
116
+ const questParsed = parseIni(questIniContent);
117
+ const questSection = questParsed.sections['Quest'] ?? {};
118
+ const config = {
119
+ format: questSection.format ? parseInt(questSection.format, 10) : DEFAULT_QUEST_CONFIG.format,
120
+ type: 'MoM',
121
+ hidden: questSection.hidden?.toLowerCase() === 'true',
122
+ defaultlanguage: questSection.defaultlanguage ?? 'English',
123
+ defaultmusicon: questSection.defaultmusicon?.toLowerCase() !== 'false',
124
+ difficulty: questSection.difficulty ? parseFloat(questSection.difficulty) : 0.5,
125
+ lengthmin: questSection.lengthmin ? parseInt(questSection.lengthmin, 10) : 60,
126
+ lengthmax: questSection.lengthmax ? parseInt(questSection.lengthmax, 10) : 90,
127
+ image: questSection.image ?? '',
128
+ version: questSection.version ?? '',
129
+ };
130
+ const model = new ScenarioModel(config);
131
+ // Load components from each data file
132
+ for (const [filename, content] of Object.entries(dataFileContents)) {
133
+ const parsed = parseIni(content);
134
+ for (const [sectionName, sectionData] of Object.entries(parsed.sections)) {
135
+ // Force file assignment based on source file, not name prefix
136
+ const comp = {
137
+ name: sectionName,
138
+ file: filename,
139
+ data: { ...sectionData },
140
+ };
141
+ model.components.set(sectionName, comp);
142
+ }
143
+ }
144
+ // Load localization
145
+ if (localizationContent) {
146
+ model.localization = LocalizationStore.fromCSV(localizationContent);
147
+ }
148
+ return model;
149
+ }
150
+ }
151
+ //# sourceMappingURL=scenario-model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scenario-model.js","sourceRoot":"","sources":["../../src/model/scenario-model.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /** Static markdown documentation exposed as MCP resources */
2
+ export declare const EVENT_FORMAT_DOC = "# Valkyrie Event System\n\n## Overview\nEvents are the core quest logic mechanism. Each event is an INI section starting with \"Event\".\n\n## Fields\n| Field | Type | Description |\n|-------|------|-------------|\n| display | bool | Whether to show dialog (default: true) |\n| buttons | int | Number of buttons (1-6). **CRITICAL**: Valkyrie only parses event1..eventN where N=buttons. If buttons < highest eventN index, higher event references are silently dropped on re-save |\n| event1..6 | string | Space-separated event/spawn names triggered by each button. Only parsed up to the buttons count |\n| trigger | string | Auto-trigger condition: EventStart, Mythos, StartRound, EndRound, Eliminated, DefeatedMonster*, DefeatedCustomMonster* |\n| conditions | string | AND-only variable gating (format: \"var,comparator,value var2,comparator,value2\"). Unlike vartests, conditions are checked BEFORE the event displays \u2014 if false, the event is silently skipped |\n| highlight | bool | Camera focuses on the event's board position |\n| xposition | float | X position on board (for token placement) |\n| yposition | float | Y position on board |\n| operations | string | Space-separated variable operations (e.g., \"$end,=,1\") |\n| vartests | string | Space-separated variable tests (e.g., \"VarOperation:#round,>=,5\") |\n| add | string | Space-separated component names to show |\n| remove | string | Space-separated component names to hide |\n| audio | string | Audio clip name to play |\n| randomevents | bool | Randomly select from event list instead of showing all |\n| quota | int | Success threshold for skill tests |\n| buttoncolor1..6 | string | Skill test button color override (e.g., \"red\") |\n| mincam | bool | Set minimum camera position |\n| maxcam | bool | Set maximum camera position |\n\n## Variable Operations\nFormat: `variable,operator,value`\n- Operators: =, +, -, *, /\n- Special variables: $end (ends scenario), $mythosMinor, $mythosMajor, $mythosDeadly, #round, #heroes\n\n## Variable Tests\nFormat: `VarOperation:variable,comparator,value`\n- Comparators: ==, !=, >, <, >=, <=\n- Logical operators: `VarTestsLogicalOperator:AND`, `VarTestsLogicalOperator:OR`\n\n## Common Patterns\n\n### Buttons vs Event References\nValkyrie only parses event1 through eventN where N = buttons. This means:\n- buttons=2 \u2192 only event1 and event2 are loaded\n- If event3 is set but buttons=2, event3 is **silently ignored and dropped on re-save**\n- Even display=false auto-advancing events need buttons=1 if they have event1\n- buttons=0 is only safe when there are NO eventN fields (e.g. a terminal remove-only event)\n\n### TokenInvestigators Removal\nCommunity quests always remove TokenInvestigators after setup so the start position marker doesn't stay interactable:\n```\nEventSetup: buttons=1, add=TokenInvestigators, event1=EventRemoveInv\nEventRemoveInv: display=false, buttons=0, remove=TokenInvestigators\n```\nNote: EventRemoveInv uses buttons=0 because it has no event1. EventSetup needs buttons=1 for its event1 to be parsed.\n\n## Triggers\n- **EventStart**: Fires when scenario begins\n- **Mythos**: Fires during mythos phase. **IMPORTANT**: Must have buttons>=1, otherwise Valkyrie auto-confirms and skips sub-events entirely\n- **StartRound**: Fires at start of each round\n- **EndRound**: Fires at end of each round\n- **Eliminated**: Fires when all investigators eliminated\n- **DefeatedMonster***: Fires when a specific monster type is defeated (e.g., DefeatedMonsterCultist)\n- **DefeatedCustomMonster***: Fires when a custom monster is defeated (e.g., DefeatedCustomMonsterBoss)\n\n## Button Labels\nSet via localization: `EventName.button1`, `EventName.button2`, etc.\nCommon patterns: `{qst:CONTINUE}`, `{qst:PASS}`, `{qst:FAIL}`\n";
3
+ export declare const LOCALIZATION_FORMAT_DOC = "# Valkyrie Localization Format\n\n## File Format\nCSV-like format with header line.\n\n```\n.,English\nkey,value\nkey,\"value with, commas\"\n```\n\n## Key Patterns\n| Pattern | Example | Description |\n|---------|---------|-------------|\n| quest.name | quest.name | Scenario display name |\n| quest.description | quest.description | Scenario description |\n| quest.authors | quest.authors | Author credits |\n| Component.text | EventStart.text | Main text for component |\n| Component.button1 | EventStart.button1 | Button label |\n| Component.uitext | UIBG.uitext | UI element text |\n| KEYWORD | CONTINUE | Reusable text referenced with {qst:KEYWORD} |\n\n## Substitution Tags\n| Tag | Description |\n|-----|-------------|\n| {qst:KEY} | Reference another localization key |\n| {ffg:KEY} | Reference game content localization |\n| {c:ComponentName} | Reference component display name |\n| {action} | Action icon |\n| {clue} | Clue icon |\n| {strength} | Strength skill icon |\n| {agility} | Agility skill icon |\n| {observation} | Observation skill icon |\n| {lore} | Lore skill icon |\n| {influence} | Influence skill icon |\n| {will} | Will skill icon |\n\n## Newlines\nUse \\n for newlines within values.\n";
4
+ export declare const PATTERN_REFERENCE_DOC = "# Valkyrie Scenario Pattern Reference\n\n## Standard Tile Placement Chain\nEvery tile reveal follows this sequence \u2014 each phase ALWAYS calls the next:\n```\nPlaceTile \u2192 PlaceDecoration \u2192 PlaceConnections \u2192 PlaceItems \u2192 PlacePeople \u2192 MoveOneSpace\n```\n- PlaceTile: add=TileX, display=false, buttons=1\n- PlaceDecoration: add walls/barriers\n- PlaceConnections: add explore tokens to adjacent areas\n- PlaceItems: add search/interact tokens\n- PlacePeople: add spawns/NPCs\n- MoveOneSpace: operations=moveOneSpace,=,1, buttons=0 (terminal)\n\n## Silent Event Rules\n- `display=false` auto-advances without dialog\n- If `event1` is set, MUST have `buttons>=1`\n- `buttons=0` only safe when NO eventN fields exist\n\n## Mythos Initialization Formula\n```\ndeadlyRound = 20 - #heroes\nmajorRound = deadlyRound / 2\n```\nInitialize in EventStart chain. Use StartRound triggers with `>=` (not `==`) for round checks.\n\n## Event Loop Structure\n```\nInit (set counter=0) \u2192 Controller (vartest counter>=limit) \u2192 Body (increment, loop back) \u2192 Exit\n```\n- Controller event1 (test FAILS) \u2192 Body\n- Controller event2 (test PASSES) \u2192 Exit\n\n## Token Swap Pattern\nReplace tokens in a single silent event:\n```\ndisplay=false, buttons=1, remove=OldToken, add=NewToken, event1=NextEvent\n```\n\n## Variable Types Quick Reference\n| Prefix | Type | Examples |\n|--------|------|----------|\n| (none) | Quest variable | cluesFound, doorUnlocked |\n| $ | System variable | $end, $mythosMinor, $mythosMajor, $mythosDeadly |\n| # | Read-only | #round, #heroes, #heroName, #rand6, #BtT |\n| @ | Trigger | Set by game events |\n\n## Vartests Button Mapping\n- `event1` = test **FAILS** (condition not met)\n- `event2` = test **PASSES** (condition met)\n\n## conditions vs vartests\n| | conditions | vartests |\n|---|-----------|----------|\n| Timing | Before display | After display |\n| False | Silently skipped | Routes to event1 |\n| True | Proceeds | Routes to event2 |\n| Format | var,op,val (AND) | VarOperation:var,op,val |\n\n## UI Layering Order\nElements render in add order. Buttons MUST be added LAST to remain clickable.\nAlways use `vunits=True` for resolution independence.\n\n## Item Distribution Modes\n| Mode | Fields | Behavior |\n|------|--------|----------|\n| Random by trait | traits=weapon | Random item matching trait |\n| Specific item | itemname=ItemCommonKnife | Exact item |\n| Pool | itemname=Item1 Item2 Item3 | Random from list |\n| Trait with exclusion | traits=common, itemname=Excluded1 | Random trait, exclude listed |\n| Starting | starting=True | Given at scenario start |\n\n## Multi-Question Dialogue Structure\nN questions with pass/fail \u2192 2^N permutation events.\nFor 3 questions: Q1 \u2192 Q2(pass/fail) \u2192 Q3(PP/PF/FP/FF) \u2192 8 outcomes.\nAlternative: track successes with a variable, check threshold at end.\n\n## One-Shot Events\nUse `conditions=fired,==,0` to gate, `operations=fired,=,1` in the fire event.\n";
5
+ export declare const COMPONENT_FORMAT_DOC = "# Valkyrie Component Types\n\n## Tiles (tiles.ini)\nPrefix: `Tile`\n| Field | Required | Description |\n|-------|----------|-------------|\n| side | Yes | TileSide name from game content. **Must be a valid catalog ID** \u2014 invalid sides crash Valkyrie |\n| xposition | Yes | X board position |\n| yposition | Yes | Y board position |\n| rotation | No | 0, 90, 180, or 270 degrees |\n\n## Tokens (tokens.ini)\nPrefix: `Token`\n| Field | Required | Description |\n|-------|----------|-------------|\n| type | Yes | TokenSearch, TokenExplore, TokenInteract, TokenInvestigators, TokenWallOutside, TokenWallInside |\n| xposition | Yes | X position |\n| yposition | Yes | Y position |\n| buttons | No | Number of interaction buttons |\n| event1 | No | Event triggered on interaction |\n| conditions | No | AND-only variable gating \u2014 token hidden when false |\n| rotation | No | Rotation for wall tokens |\n\n## Spawns (spawns.ini)\nPrefix: `Spawn`\n| Field | Required | Description |\n|-------|----------|-------------|\n| monster | Yes | Monster type name(s), space-separated |\n| buttons | No | Button count |\n| event1 | No | Next event |\n| conditions | No | AND-only variable gating \u2014 spawn hidden when false |\n| add | No | Space-separated component names to show |\n| uniquehealth | No | Base health override |\n| uniquehealthhero | No | Per-hero health modifier |\n\n## Items (items.ini)\nPrefix: `QItem`\n| Field | Required | Description |\n|-------|----------|-------------|\n| itemname | No | Space-separated catalog item IDs (e.g., ItemCommonKnife, ItemCommonKeroseneLantern). Must use catalog IDs, not display names |\n| starting | No | True if given at start |\n| traits | No | Space-separated: weapon, lightsource, equipment, common, spell |\n| traitpool | No | Alternative trait matching |\n| inspect | No | Event reference triggered on item inspection |\n\n## Puzzles (other.ini)\nPrefix: `Puzzle`\n| Field | Required | Description |\n|-------|----------|-------------|\n| class | No | code, slide, image, tower (default: slide) |\n| skill | No | Skill test: {observation}, {agility}, etc. |\n| conditions | No | AND-only variable gating \u2014 puzzle hidden when false |\n| puzzlelevel | No | Puzzle difficulty level |\n| puzzlealtlevel | No | Alternative difficulty level |\n\n## UI Elements (ui.ini)\nPrefix: `UI`\n| Field | Required | Description |\n|-------|----------|-------------|\n| image | No | Image filename or library ref |\n| size | No | Display size multiplier |\n| vunits | No | Use vertical units |\n\n## Custom Monsters (spawns.ini or monsters.ini)\nPrefix: `CustomMonster`\n| Field | Required | Description |\n|-------|----------|-------------|\n| base | Yes | Base monster type from catalog |\n| health | No | Base health |\n| healthperhero | No | Per-hero health modifier |\n| traits | No | Monster traits |\n";
6
+ //# sourceMappingURL=format-docs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-docs.d.ts","sourceRoot":"","sources":["../../src/resources/format-docs.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAE7D,eAAO,MAAM,gBAAgB,qsHAkE5B,CAAC;AAEF,eAAO,MAAM,uBAAuB,ksCAuCnC,CAAC;AAEF,eAAO,MAAM,qBAAqB,k8FA+EjC,CAAC;AAEF,eAAO,MAAM,oBAAoB,2yFAuEhC,CAAC"}
@@ -0,0 +1,261 @@
1
+ /** Static markdown documentation exposed as MCP resources */
2
+ export const EVENT_FORMAT_DOC = `# Valkyrie Event System
3
+
4
+ ## Overview
5
+ Events are the core quest logic mechanism. Each event is an INI section starting with "Event".
6
+
7
+ ## Fields
8
+ | Field | Type | Description |
9
+ |-------|------|-------------|
10
+ | display | bool | Whether to show dialog (default: true) |
11
+ | buttons | int | Number of buttons (1-6). **CRITICAL**: Valkyrie only parses event1..eventN where N=buttons. If buttons < highest eventN index, higher event references are silently dropped on re-save |
12
+ | event1..6 | string | Space-separated event/spawn names triggered by each button. Only parsed up to the buttons count |
13
+ | trigger | string | Auto-trigger condition: EventStart, Mythos, StartRound, EndRound, Eliminated, DefeatedMonster*, DefeatedCustomMonster* |
14
+ | conditions | string | AND-only variable gating (format: "var,comparator,value var2,comparator,value2"). Unlike vartests, conditions are checked BEFORE the event displays — if false, the event is silently skipped |
15
+ | highlight | bool | Camera focuses on the event's board position |
16
+ | xposition | float | X position on board (for token placement) |
17
+ | yposition | float | Y position on board |
18
+ | operations | string | Space-separated variable operations (e.g., "$end,=,1") |
19
+ | vartests | string | Space-separated variable tests (e.g., "VarOperation:#round,>=,5") |
20
+ | add | string | Space-separated component names to show |
21
+ | remove | string | Space-separated component names to hide |
22
+ | audio | string | Audio clip name to play |
23
+ | randomevents | bool | Randomly select from event list instead of showing all |
24
+ | quota | int | Success threshold for skill tests |
25
+ | buttoncolor1..6 | string | Skill test button color override (e.g., "red") |
26
+ | mincam | bool | Set minimum camera position |
27
+ | maxcam | bool | Set maximum camera position |
28
+
29
+ ## Variable Operations
30
+ Format: \`variable,operator,value\`
31
+ - Operators: =, +, -, *, /
32
+ - Special variables: $end (ends scenario), $mythosMinor, $mythosMajor, $mythosDeadly, #round, #heroes
33
+
34
+ ## Variable Tests
35
+ Format: \`VarOperation:variable,comparator,value\`
36
+ - Comparators: ==, !=, >, <, >=, <=
37
+ - Logical operators: \`VarTestsLogicalOperator:AND\`, \`VarTestsLogicalOperator:OR\`
38
+
39
+ ## Common Patterns
40
+
41
+ ### Buttons vs Event References
42
+ Valkyrie only parses event1 through eventN where N = buttons. This means:
43
+ - buttons=2 → only event1 and event2 are loaded
44
+ - If event3 is set but buttons=2, event3 is **silently ignored and dropped on re-save**
45
+ - Even display=false auto-advancing events need buttons=1 if they have event1
46
+ - buttons=0 is only safe when there are NO eventN fields (e.g. a terminal remove-only event)
47
+
48
+ ### TokenInvestigators Removal
49
+ Community quests always remove TokenInvestigators after setup so the start position marker doesn't stay interactable:
50
+ \`\`\`
51
+ EventSetup: buttons=1, add=TokenInvestigators, event1=EventRemoveInv
52
+ EventRemoveInv: display=false, buttons=0, remove=TokenInvestigators
53
+ \`\`\`
54
+ Note: EventRemoveInv uses buttons=0 because it has no event1. EventSetup needs buttons=1 for its event1 to be parsed.
55
+
56
+ ## Triggers
57
+ - **EventStart**: Fires when scenario begins
58
+ - **Mythos**: Fires during mythos phase. **IMPORTANT**: Must have buttons>=1, otherwise Valkyrie auto-confirms and skips sub-events entirely
59
+ - **StartRound**: Fires at start of each round
60
+ - **EndRound**: Fires at end of each round
61
+ - **Eliminated**: Fires when all investigators eliminated
62
+ - **DefeatedMonster***: Fires when a specific monster type is defeated (e.g., DefeatedMonsterCultist)
63
+ - **DefeatedCustomMonster***: Fires when a custom monster is defeated (e.g., DefeatedCustomMonsterBoss)
64
+
65
+ ## Button Labels
66
+ Set via localization: \`EventName.button1\`, \`EventName.button2\`, etc.
67
+ Common patterns: \`{qst:CONTINUE}\`, \`{qst:PASS}\`, \`{qst:FAIL}\`
68
+ `;
69
+ export const LOCALIZATION_FORMAT_DOC = `# Valkyrie Localization Format
70
+
71
+ ## File Format
72
+ CSV-like format with header line.
73
+
74
+ \`\`\`
75
+ .,English
76
+ key,value
77
+ key,"value with, commas"
78
+ \`\`\`
79
+
80
+ ## Key Patterns
81
+ | Pattern | Example | Description |
82
+ |---------|---------|-------------|
83
+ | quest.name | quest.name | Scenario display name |
84
+ | quest.description | quest.description | Scenario description |
85
+ | quest.authors | quest.authors | Author credits |
86
+ | Component.text | EventStart.text | Main text for component |
87
+ | Component.button1 | EventStart.button1 | Button label |
88
+ | Component.uitext | UIBG.uitext | UI element text |
89
+ | KEYWORD | CONTINUE | Reusable text referenced with {qst:KEYWORD} |
90
+
91
+ ## Substitution Tags
92
+ | Tag | Description |
93
+ |-----|-------------|
94
+ | {qst:KEY} | Reference another localization key |
95
+ | {ffg:KEY} | Reference game content localization |
96
+ | {c:ComponentName} | Reference component display name |
97
+ | {action} | Action icon |
98
+ | {clue} | Clue icon |
99
+ | {strength} | Strength skill icon |
100
+ | {agility} | Agility skill icon |
101
+ | {observation} | Observation skill icon |
102
+ | {lore} | Lore skill icon |
103
+ | {influence} | Influence skill icon |
104
+ | {will} | Will skill icon |
105
+
106
+ ## Newlines
107
+ Use \\n for newlines within values.
108
+ `;
109
+ export const PATTERN_REFERENCE_DOC = `# Valkyrie Scenario Pattern Reference
110
+
111
+ ## Standard Tile Placement Chain
112
+ Every tile reveal follows this sequence — each phase ALWAYS calls the next:
113
+ \`\`\`
114
+ PlaceTile → PlaceDecoration → PlaceConnections → PlaceItems → PlacePeople → MoveOneSpace
115
+ \`\`\`
116
+ - PlaceTile: add=TileX, display=false, buttons=1
117
+ - PlaceDecoration: add walls/barriers
118
+ - PlaceConnections: add explore tokens to adjacent areas
119
+ - PlaceItems: add search/interact tokens
120
+ - PlacePeople: add spawns/NPCs
121
+ - MoveOneSpace: operations=moveOneSpace,=,1, buttons=0 (terminal)
122
+
123
+ ## Silent Event Rules
124
+ - \`display=false\` auto-advances without dialog
125
+ - If \`event1\` is set, MUST have \`buttons>=1\`
126
+ - \`buttons=0\` only safe when NO eventN fields exist
127
+
128
+ ## Mythos Initialization Formula
129
+ \`\`\`
130
+ deadlyRound = 20 - #heroes
131
+ majorRound = deadlyRound / 2
132
+ \`\`\`
133
+ Initialize in EventStart chain. Use StartRound triggers with \`>=\` (not \`==\`) for round checks.
134
+
135
+ ## Event Loop Structure
136
+ \`\`\`
137
+ Init (set counter=0) → Controller (vartest counter>=limit) → Body (increment, loop back) → Exit
138
+ \`\`\`
139
+ - Controller event1 (test FAILS) → Body
140
+ - Controller event2 (test PASSES) → Exit
141
+
142
+ ## Token Swap Pattern
143
+ Replace tokens in a single silent event:
144
+ \`\`\`
145
+ display=false, buttons=1, remove=OldToken, add=NewToken, event1=NextEvent
146
+ \`\`\`
147
+
148
+ ## Variable Types Quick Reference
149
+ | Prefix | Type | Examples |
150
+ |--------|------|----------|
151
+ | (none) | Quest variable | cluesFound, doorUnlocked |
152
+ | $ | System variable | $end, $mythosMinor, $mythosMajor, $mythosDeadly |
153
+ | # | Read-only | #round, #heroes, #heroName, #rand6, #BtT |
154
+ | @ | Trigger | Set by game events |
155
+
156
+ ## Vartests Button Mapping
157
+ - \`event1\` = test **FAILS** (condition not met)
158
+ - \`event2\` = test **PASSES** (condition met)
159
+
160
+ ## conditions vs vartests
161
+ | | conditions | vartests |
162
+ |---|-----------|----------|
163
+ | Timing | Before display | After display |
164
+ | False | Silently skipped | Routes to event1 |
165
+ | True | Proceeds | Routes to event2 |
166
+ | Format | var,op,val (AND) | VarOperation:var,op,val |
167
+
168
+ ## UI Layering Order
169
+ Elements render in add order. Buttons MUST be added LAST to remain clickable.
170
+ Always use \`vunits=True\` for resolution independence.
171
+
172
+ ## Item Distribution Modes
173
+ | Mode | Fields | Behavior |
174
+ |------|--------|----------|
175
+ | Random by trait | traits=weapon | Random item matching trait |
176
+ | Specific item | itemname=ItemCommonKnife | Exact item |
177
+ | Pool | itemname=Item1 Item2 Item3 | Random from list |
178
+ | Trait with exclusion | traits=common, itemname=Excluded1 | Random trait, exclude listed |
179
+ | Starting | starting=True | Given at scenario start |
180
+
181
+ ## Multi-Question Dialogue Structure
182
+ N questions with pass/fail → 2^N permutation events.
183
+ For 3 questions: Q1 → Q2(pass/fail) → Q3(PP/PF/FP/FF) → 8 outcomes.
184
+ Alternative: track successes with a variable, check threshold at end.
185
+
186
+ ## One-Shot Events
187
+ Use \`conditions=fired,==,0\` to gate, \`operations=fired,=,1\` in the fire event.
188
+ `;
189
+ export const COMPONENT_FORMAT_DOC = `# Valkyrie Component Types
190
+
191
+ ## Tiles (tiles.ini)
192
+ Prefix: \`Tile\`
193
+ | Field | Required | Description |
194
+ |-------|----------|-------------|
195
+ | side | Yes | TileSide name from game content. **Must be a valid catalog ID** — invalid sides crash Valkyrie |
196
+ | xposition | Yes | X board position |
197
+ | yposition | Yes | Y board position |
198
+ | rotation | No | 0, 90, 180, or 270 degrees |
199
+
200
+ ## Tokens (tokens.ini)
201
+ Prefix: \`Token\`
202
+ | Field | Required | Description |
203
+ |-------|----------|-------------|
204
+ | type | Yes | TokenSearch, TokenExplore, TokenInteract, TokenInvestigators, TokenWallOutside, TokenWallInside |
205
+ | xposition | Yes | X position |
206
+ | yposition | Yes | Y position |
207
+ | buttons | No | Number of interaction buttons |
208
+ | event1 | No | Event triggered on interaction |
209
+ | conditions | No | AND-only variable gating — token hidden when false |
210
+ | rotation | No | Rotation for wall tokens |
211
+
212
+ ## Spawns (spawns.ini)
213
+ Prefix: \`Spawn\`
214
+ | Field | Required | Description |
215
+ |-------|----------|-------------|
216
+ | monster | Yes | Monster type name(s), space-separated |
217
+ | buttons | No | Button count |
218
+ | event1 | No | Next event |
219
+ | conditions | No | AND-only variable gating — spawn hidden when false |
220
+ | add | No | Space-separated component names to show |
221
+ | uniquehealth | No | Base health override |
222
+ | uniquehealthhero | No | Per-hero health modifier |
223
+
224
+ ## Items (items.ini)
225
+ Prefix: \`QItem\`
226
+ | Field | Required | Description |
227
+ |-------|----------|-------------|
228
+ | itemname | No | Space-separated catalog item IDs (e.g., ItemCommonKnife, ItemCommonKeroseneLantern). Must use catalog IDs, not display names |
229
+ | starting | No | True if given at start |
230
+ | traits | No | Space-separated: weapon, lightsource, equipment, common, spell |
231
+ | traitpool | No | Alternative trait matching |
232
+ | inspect | No | Event reference triggered on item inspection |
233
+
234
+ ## Puzzles (other.ini)
235
+ Prefix: \`Puzzle\`
236
+ | Field | Required | Description |
237
+ |-------|----------|-------------|
238
+ | class | No | code, slide, image, tower (default: slide) |
239
+ | skill | No | Skill test: {observation}, {agility}, etc. |
240
+ | conditions | No | AND-only variable gating — puzzle hidden when false |
241
+ | puzzlelevel | No | Puzzle difficulty level |
242
+ | puzzlealtlevel | No | Alternative difficulty level |
243
+
244
+ ## UI Elements (ui.ini)
245
+ Prefix: \`UI\`
246
+ | Field | Required | Description |
247
+ |-------|----------|-------------|
248
+ | image | No | Image filename or library ref |
249
+ | size | No | Display size multiplier |
250
+ | vunits | No | Use vertical units |
251
+
252
+ ## Custom Monsters (spawns.ini or monsters.ini)
253
+ Prefix: \`CustomMonster\`
254
+ | Field | Required | Description |
255
+ |-------|----------|-------------|
256
+ | base | Yes | Base monster type from catalog |
257
+ | health | No | Base health |
258
+ | healthperhero | No | Per-hero health modifier |
259
+ | traits | No | Monster traits |
260
+ `;
261
+ //# sourceMappingURL=format-docs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-docs.js","sourceRoot":"","sources":["../../src/resources/format-docs.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAE7D,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkE/B,CAAC;AAEF,MAAM,CAAC,MAAM,uBAAuB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuCtC,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+EpC,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuEnC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function createServer(): McpServer;
3
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA+DpE,wBAAgB,YAAY,IAAI,SAAS,CA+QxC"}