run-dmcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +79 -0
  3. package/dist/__tests__/engineVocabulary.test.d.ts +1 -0
  4. package/dist/__tests__/engineVocabulary.test.js +147 -0
  5. package/dist/db/__tests__/connection.test.d.ts +1 -0
  6. package/dist/db/__tests__/connection.test.js +72 -0
  7. package/dist/db/__tests__/testDb.d.ts +33 -0
  8. package/dist/db/__tests__/testDb.js +41 -0
  9. package/dist/db/connection.d.ts +22 -0
  10. package/dist/db/connection.js +107 -0
  11. package/dist/db/schema.d.ts +1 -0
  12. package/dist/db/schema.js +725 -0
  13. package/dist/events/emitter.d.ts +22 -0
  14. package/dist/events/emitter.js +71 -0
  15. package/dist/http/server.d.ts +3 -0
  16. package/dist/http/server.js +649 -0
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.js +92 -0
  19. package/dist/register/abilities.d.ts +2 -0
  20. package/dist/register/abilities.js +165 -0
  21. package/dist/register/audio.d.ts +2 -0
  22. package/dist/register/audio.js +326 -0
  23. package/dist/register/batch.d.ts +2 -0
  24. package/dist/register/batch.js +343 -0
  25. package/dist/register/character.d.ts +2 -0
  26. package/dist/register/character.js +324 -0
  27. package/dist/register/combat.d.ts +2 -0
  28. package/dist/register/combat.js +207 -0
  29. package/dist/register/core.d.ts +2 -0
  30. package/dist/register/core.js +1040 -0
  31. package/dist/register/display.d.ts +2 -0
  32. package/dist/register/display.js +263 -0
  33. package/dist/register/factions.d.ts +2 -0
  34. package/dist/register/factions.js +186 -0
  35. package/dist/register/images.d.ts +2 -0
  36. package/dist/register/images.js +400 -0
  37. package/dist/register/inventory.d.ts +2 -0
  38. package/dist/register/inventory.js +115 -0
  39. package/dist/register/mcp-prompts.d.ts +2 -0
  40. package/dist/register/mcp-prompts.js +684 -0
  41. package/dist/register/mcp-resources.d.ts +2 -0
  42. package/dist/register/mcp-resources.js +335 -0
  43. package/dist/register/narrative.d.ts +2 -0
  44. package/dist/register/narrative.js +242 -0
  45. package/dist/register/notes.d.ts +2 -0
  46. package/dist/register/notes.js +170 -0
  47. package/dist/register/pause.d.ts +2 -0
  48. package/dist/register/pause.js +580 -0
  49. package/dist/register/quests.d.ts +2 -0
  50. package/dist/register/quests.js +118 -0
  51. package/dist/register/relationships.d.ts +2 -0
  52. package/dist/register/relationships.js +147 -0
  53. package/dist/register/resources.d.ts +2 -0
  54. package/dist/register/resources.js +277 -0
  55. package/dist/register/secrets.d.ts +2 -0
  56. package/dist/register/secrets.js +192 -0
  57. package/dist/register/status.d.ts +2 -0
  58. package/dist/register/status.js +130 -0
  59. package/dist/register/tables.d.ts +2 -0
  60. package/dist/register/tables.js +146 -0
  61. package/dist/register/tags.d.ts +2 -0
  62. package/dist/register/tags.js +114 -0
  63. package/dist/register/time.d.ts +2 -0
  64. package/dist/register/time.js +281 -0
  65. package/dist/register/world.d.ts +2 -0
  66. package/dist/register/world.js +127 -0
  67. package/dist/schemas/index.d.ts +921 -0
  68. package/dist/schemas/index.js +121 -0
  69. package/dist/test-setup.d.ts +1 -0
  70. package/dist/test-setup.js +13 -0
  71. package/dist/tools/__tests__/audio.test.d.ts +1 -0
  72. package/dist/tools/__tests__/audio.test.js +59 -0
  73. package/dist/tools/__tests__/conserved.test.d.ts +1 -0
  74. package/dist/tools/__tests__/conserved.test.js +488 -0
  75. package/dist/tools/__tests__/constraint.test.d.ts +1 -0
  76. package/dist/tools/__tests__/constraint.test.js +212 -0
  77. package/dist/tools/__tests__/expiry-consequences.test.d.ts +1 -0
  78. package/dist/tools/__tests__/expiry-consequences.test.js +110 -0
  79. package/dist/tools/__tests__/images.test.d.ts +1 -0
  80. package/dist/tools/__tests__/images.test.js +59 -0
  81. package/dist/tools/__tests__/relationship.test.d.ts +1 -0
  82. package/dist/tools/__tests__/relationship.test.js +132 -0
  83. package/dist/tools/__tests__/resource-constraints.test.d.ts +1 -0
  84. package/dist/tools/__tests__/resource-constraints.test.js +131 -0
  85. package/dist/tools/__tests__/resource.test.d.ts +1 -0
  86. package/dist/tools/__tests__/resource.test.js +190 -0
  87. package/dist/tools/__tests__/time.test.d.ts +1 -0
  88. package/dist/tools/__tests__/time.test.js +404 -0
  89. package/dist/tools/__tests__/timers.test.d.ts +1 -0
  90. package/dist/tools/__tests__/timers.test.js +426 -0
  91. package/dist/tools/__tests__/world.test.d.ts +1 -0
  92. package/dist/tools/__tests__/world.test.js +70 -0
  93. package/dist/tools/ability.d.ts +48 -0
  94. package/dist/tools/ability.js +238 -0
  95. package/dist/tools/audio.d.ts +24 -0
  96. package/dist/tools/audio.js +365 -0
  97. package/dist/tools/character.d.ts +70 -0
  98. package/dist/tools/character.js +309 -0
  99. package/dist/tools/combat.d.ts +13 -0
  100. package/dist/tools/combat.js +195 -0
  101. package/dist/tools/constraint.d.ts +132 -0
  102. package/dist/tools/constraint.js +269 -0
  103. package/dist/tools/dice.d.ts +23 -0
  104. package/dist/tools/dice.js +111 -0
  105. package/dist/tools/display.d.ts +120 -0
  106. package/dist/tools/display.js +528 -0
  107. package/dist/tools/faction.d.ts +61 -0
  108. package/dist/tools/faction.js +269 -0
  109. package/dist/tools/game.d.ts +96 -0
  110. package/dist/tools/game.js +526 -0
  111. package/dist/tools/image-prompt.d.ts +49 -0
  112. package/dist/tools/image-prompt.js +479 -0
  113. package/dist/tools/images.d.ts +47 -0
  114. package/dist/tools/images.js +449 -0
  115. package/dist/tools/inventory.d.ts +20 -0
  116. package/dist/tools/inventory.js +145 -0
  117. package/dist/tools/narrative.d.ts +58 -0
  118. package/dist/tools/narrative.js +237 -0
  119. package/dist/tools/notes.d.ts +41 -0
  120. package/dist/tools/notes.js +220 -0
  121. package/dist/tools/pause.d.ts +110 -0
  122. package/dist/tools/pause.js +1254 -0
  123. package/dist/tools/quest.d.ts +34 -0
  124. package/dist/tools/quest.js +164 -0
  125. package/dist/tools/relationship.d.ts +74 -0
  126. package/dist/tools/relationship.js +324 -0
  127. package/dist/tools/resource.d.ts +93 -0
  128. package/dist/tools/resource.js +374 -0
  129. package/dist/tools/rules.d.ts +4 -0
  130. package/dist/tools/rules.js +30 -0
  131. package/dist/tools/secrets.d.ts +49 -0
  132. package/dist/tools/secrets.js +195 -0
  133. package/dist/tools/status.d.ts +36 -0
  134. package/dist/tools/status.js +218 -0
  135. package/dist/tools/tables.d.ts +33 -0
  136. package/dist/tools/tables.js +209 -0
  137. package/dist/tools/tags.d.ts +52 -0
  138. package/dist/tools/tags.js +176 -0
  139. package/dist/tools/time.d.ts +33 -0
  140. package/dist/tools/time.js +276 -0
  141. package/dist/tools/timers.d.ts +41 -0
  142. package/dist/tools/timers.js +215 -0
  143. package/dist/tools/world.d.ts +78 -0
  144. package/dist/tools/world.js +331 -0
  145. package/dist/types/index.d.ts +969 -0
  146. package/dist/types/index.js +1 -0
  147. package/dist/utils/__tests__/json.test.d.ts +1 -0
  148. package/dist/utils/__tests__/json.test.js +55 -0
  149. package/dist/utils/__tests__/validation.test.d.ts +1 -0
  150. package/dist/utils/__tests__/validation.test.js +90 -0
  151. package/dist/utils/errors.d.ts +44 -0
  152. package/dist/utils/errors.js +121 -0
  153. package/dist/utils/json.d.ts +9 -0
  154. package/dist/utils/json.js +23 -0
  155. package/dist/utils/logger.d.ts +7 -0
  156. package/dist/utils/logger.js +50 -0
  157. package/dist/utils/output-schemas.d.ts +594 -0
  158. package/dist/utils/output-schemas.js +331 -0
  159. package/dist/utils/tool-annotations.d.ts +147 -0
  160. package/dist/utils/tool-annotations.js +98 -0
  161. package/dist/utils/validation.d.ts +34 -0
  162. package/dist/utils/validation.js +52 -0
  163. package/dist/utils/verbosity.d.ts +57 -0
  164. package/dist/utils/verbosity.js +67 -0
  165. package/dist/utils/webui.d.ts +20 -0
  166. package/dist/utils/webui.js +35 -0
  167. package/package.json +75 -0
@@ -0,0 +1,528 @@
1
+ import { getDatabase } from "../db/connection.js";
2
+ const defaultConfig = {
3
+ // Colors - neutral paper theme
4
+ bgColor: "#f5f3ef",
5
+ bgSecondary: "#ebe8e2",
6
+ bgElevated: "#ffffff",
7
+ textColor: "#2a2a2a",
8
+ textMuted: "#6b6b6b",
9
+ accentColor: "#4a5568",
10
+ accentHover: "#2d3748",
11
+ borderColor: "#d4d0c8",
12
+ successColor: "#2f855a",
13
+ warningColor: "#b7791f",
14
+ dangerColor: "#c53030",
15
+ // Code block colors
16
+ codeBackground: "#e8e5df",
17
+ codeText: "#4a4a4a",
18
+ // Visual style
19
+ borderRadius: "rounded",
20
+ cardStyle: "clean",
21
+ // Fonts
22
+ fontDisplay: "Libre Baskerville",
23
+ fontBody: "Source Sans Pro",
24
+ fontMono: "IBM Plex Mono",
25
+ // Display options
26
+ showHealthBars: true,
27
+ showConditionTags: true,
28
+ showImages: true,
29
+ // Title
30
+ appTitle: "DMCP Game Viewer",
31
+ };
32
+ // Preset themes for different game genres
33
+ // Each preset provides a complete visual identity appropriate for the genre
34
+ export const themePresets = {
35
+ // Classic medieval fantasy with gold accents and parchment feel
36
+ "high-fantasy": {
37
+ bgColor: "#1a1610",
38
+ bgSecondary: "#252015",
39
+ bgElevated: "#302a1f",
40
+ textColor: "#f5e6c8",
41
+ textMuted: "#a09070",
42
+ accentColor: "#daa520",
43
+ accentHover: "#ffd700",
44
+ borderColor: "#4a4030",
45
+ successColor: "#6b8e23",
46
+ warningColor: "#cd853f",
47
+ dangerColor: "#8b0000",
48
+ codeBackground: "#151208",
49
+ codeText: "#c0b090",
50
+ borderRadius: "rounded",
51
+ cardStyle: "parchment",
52
+ fontDisplay: "Cinzel",
53
+ fontBody: "Lora",
54
+ fontMono: "Fira Code",
55
+ appTitle: "High Fantasy Adventures",
56
+ },
57
+ // Dark, gritty fantasy with purple/blood red tones
58
+ "dark-fantasy": {
59
+ bgColor: "#0d0a10",
60
+ bgSecondary: "#15101a",
61
+ bgElevated: "#201825",
62
+ textColor: "#d0c8d8",
63
+ textMuted: "#706878",
64
+ accentColor: "#9d174d",
65
+ accentHover: "#be185d",
66
+ borderColor: "#3d2848",
67
+ successColor: "#4a5568",
68
+ warningColor: "#92400e",
69
+ dangerColor: "#991b1b",
70
+ codeBackground: "#08050a",
71
+ codeText: "#8b7090",
72
+ borderRadius: "sharp",
73
+ cardStyle: "grungy",
74
+ fontDisplay: "Uncial Antiqua",
75
+ fontBody: "Crimson Text",
76
+ fontMono: "Fira Code",
77
+ appTitle: "Dark Fantasy Campaign",
78
+ },
79
+ // Futuristic sci-fi with cyan/chrome aesthetic
80
+ "sci-fi": {
81
+ bgColor: "#050810",
82
+ bgSecondary: "#0a1020",
83
+ bgElevated: "#101830",
84
+ textColor: "#e0e8f0",
85
+ textMuted: "#6080a0",
86
+ accentColor: "#06b6d4",
87
+ accentHover: "#22d3ee",
88
+ borderColor: "#1e3a5f",
89
+ successColor: "#10b981",
90
+ warningColor: "#f59e0b",
91
+ dangerColor: "#ef4444",
92
+ codeBackground: "#020508",
93
+ codeText: "#4ecdc4",
94
+ borderRadius: "sharp",
95
+ cardStyle: "tech",
96
+ fontDisplay: "Orbitron",
97
+ fontBody: "Exo 2",
98
+ fontMono: "Share Tech Mono",
99
+ appTitle: "Sci-Fi Chronicles",
100
+ },
101
+ // Neon-drenched cyberpunk with pink/yellow highlights
102
+ cyberpunk: {
103
+ bgColor: "#0a0a0f",
104
+ bgSecondary: "#121218",
105
+ bgElevated: "#1a1a24",
106
+ textColor: "#e0e0e8",
107
+ textMuted: "#6b6b80",
108
+ accentColor: "#f72585",
109
+ accentHover: "#ff4d9d",
110
+ borderColor: "#2d2d40",
111
+ successColor: "#39ff14",
112
+ warningColor: "#ffee00",
113
+ dangerColor: "#ff0055",
114
+ codeBackground: "#050508",
115
+ codeText: "#00ffff",
116
+ borderRadius: "sharp",
117
+ cardStyle: "tech",
118
+ fontDisplay: "Orbitron",
119
+ fontBody: "Share Tech",
120
+ fontMono: "Fira Code",
121
+ appTitle: "Cyberpunk Chronicles",
122
+ },
123
+ // Wild west with tan/rust/leather tones
124
+ western: {
125
+ bgColor: "#1a1510",
126
+ bgSecondary: "#252018",
127
+ bgElevated: "#302820",
128
+ textColor: "#e8dcc8",
129
+ textMuted: "#a09080",
130
+ accentColor: "#b45309",
131
+ accentHover: "#d97706",
132
+ borderColor: "#4a3828",
133
+ successColor: "#65a30d",
134
+ warningColor: "#ca8a04",
135
+ dangerColor: "#b91c1c",
136
+ codeBackground: "#100c08",
137
+ codeText: "#9a8060",
138
+ borderRadius: "soft",
139
+ cardStyle: "wooden",
140
+ fontDisplay: "Rye",
141
+ fontBody: "Bitter",
142
+ fontMono: "Courier Prime",
143
+ appTitle: "Wild West Adventures",
144
+ },
145
+ // Black and white noir with red accents
146
+ noir: {
147
+ bgColor: "#0a0a0a",
148
+ bgSecondary: "#141414",
149
+ bgElevated: "#1e1e1e",
150
+ textColor: "#e0e0e0",
151
+ textMuted: "#707070",
152
+ accentColor: "#dc2626",
153
+ accentHover: "#ef4444",
154
+ borderColor: "#333333",
155
+ successColor: "#6b7280",
156
+ warningColor: "#9ca3af",
157
+ dangerColor: "#b91c1c",
158
+ codeBackground: "#050505",
159
+ codeText: "#808080",
160
+ borderRadius: "sharp",
161
+ cardStyle: "clean",
162
+ fontDisplay: "Playfair Display",
163
+ fontBody: "Source Serif Pro",
164
+ fontMono: "IBM Plex Mono",
165
+ appTitle: "Noir Detective Story",
166
+ },
167
+ // Eldritch cosmic horror with deep purple/green void colors
168
+ "cosmic-horror": {
169
+ bgColor: "#08060d",
170
+ bgSecondary: "#100d18",
171
+ bgElevated: "#181324",
172
+ textColor: "#c8c0d8",
173
+ textMuted: "#605870",
174
+ accentColor: "#7c3aed",
175
+ accentHover: "#8b5cf6",
176
+ borderColor: "#2d2840",
177
+ successColor: "#4b5563",
178
+ warningColor: "#78716c",
179
+ dangerColor: "#7f1d1d",
180
+ codeBackground: "#040308",
181
+ codeText: "#6b21a8",
182
+ borderRadius: "soft",
183
+ cardStyle: "grungy",
184
+ fontDisplay: "Special Elite",
185
+ fontBody: "Alegreya",
186
+ fontMono: "Fira Code",
187
+ appTitle: "Cosmic Horror Campaign",
188
+ },
189
+ // Victorian steampunk with brass/copper/sepia tones
190
+ steampunk: {
191
+ bgColor: "#1a1512",
192
+ bgSecondary: "#251f18",
193
+ bgElevated: "#302820",
194
+ textColor: "#e0d0b8",
195
+ textMuted: "#908068",
196
+ accentColor: "#b87333",
197
+ accentHover: "#cd853f",
198
+ borderColor: "#4a3c2c",
199
+ successColor: "#6b8e23",
200
+ warningColor: "#b8860b",
201
+ dangerColor: "#8b4513",
202
+ codeBackground: "#100c08",
203
+ codeText: "#b87333",
204
+ borderRadius: "rounded",
205
+ cardStyle: "metallic",
206
+ fontDisplay: "IM Fell English SC",
207
+ fontBody: "IM Fell English",
208
+ fontMono: "Courier Prime",
209
+ appTitle: "Steampunk Adventures",
210
+ },
211
+ // Post-apocalyptic wasteland with grey/rust/warning colors
212
+ "post-apocalyptic": {
213
+ bgColor: "#12100e",
214
+ bgSecondary: "#1a1815",
215
+ bgElevated: "#24201c",
216
+ textColor: "#c8c0b0",
217
+ textMuted: "#787068",
218
+ accentColor: "#ea580c",
219
+ accentHover: "#f97316",
220
+ borderColor: "#3a3428",
221
+ successColor: "#65a30d",
222
+ warningColor: "#eab308",
223
+ dangerColor: "#dc2626",
224
+ codeBackground: "#0a0908",
225
+ codeText: "#808070",
226
+ borderRadius: "sharp",
227
+ cardStyle: "grungy",
228
+ fontDisplay: "Bangers",
229
+ fontBody: "Oswald",
230
+ fontMono: "Share Tech Mono",
231
+ appTitle: "Wasteland Chronicles",
232
+ },
233
+ // Pirate/nautical theme with ocean blue/gold/wood
234
+ pirate: {
235
+ bgColor: "#0d1418",
236
+ bgSecondary: "#141e24",
237
+ bgElevated: "#1c2830",
238
+ textColor: "#e0dcd0",
239
+ textMuted: "#7890a0",
240
+ accentColor: "#d4a017",
241
+ accentHover: "#f0c020",
242
+ borderColor: "#2a4050",
243
+ successColor: "#22c55e",
244
+ warningColor: "#eab308",
245
+ dangerColor: "#dc2626",
246
+ codeBackground: "#080c10",
247
+ codeText: "#6890a0",
248
+ borderRadius: "rounded",
249
+ cardStyle: "wooden",
250
+ fontDisplay: "Pirata One",
251
+ fontBody: "Merriweather",
252
+ fontMono: "Fira Code",
253
+ appTitle: "Pirate Adventures",
254
+ },
255
+ // Modern/contemporary urban setting
256
+ modern: {
257
+ bgColor: "#0f0f14",
258
+ bgSecondary: "#18181f",
259
+ bgElevated: "#22222c",
260
+ textColor: "#e8e8ec",
261
+ textMuted: "#8888a0",
262
+ accentColor: "#3b82f6",
263
+ accentHover: "#60a5fa",
264
+ borderColor: "#333340",
265
+ successColor: "#22c55e",
266
+ warningColor: "#f59e0b",
267
+ dangerColor: "#ef4444",
268
+ codeBackground: "#08080c",
269
+ codeText: "#9090a8",
270
+ borderRadius: "rounded",
271
+ cardStyle: "clean",
272
+ fontDisplay: "Inter",
273
+ fontBody: "Inter",
274
+ fontMono: "JetBrains Mono",
275
+ appTitle: "Modern Adventures",
276
+ },
277
+ // Superhero comic book style
278
+ superhero: {
279
+ bgColor: "#0a0c15",
280
+ bgSecondary: "#101525",
281
+ bgElevated: "#182035",
282
+ textColor: "#f0f0f8",
283
+ textMuted: "#7080a0",
284
+ accentColor: "#dc2626",
285
+ accentHover: "#ef4444",
286
+ borderColor: "#253050",
287
+ successColor: "#22c55e",
288
+ warningColor: "#fbbf24",
289
+ dangerColor: "#b91c1c",
290
+ codeBackground: "#050810",
291
+ codeText: "#6080c0",
292
+ borderRadius: "rounded",
293
+ cardStyle: "clean",
294
+ fontDisplay: "Bangers",
295
+ fontBody: "Open Sans",
296
+ fontMono: "Fira Code",
297
+ appTitle: "Superhero Adventures",
298
+ },
299
+ };
300
+ /**
301
+ * Get the current display configuration
302
+ */
303
+ export function getDisplayConfig() {
304
+ const db = getDatabase();
305
+ const row = db
306
+ .prepare("SELECT config FROM display_config WHERE id = 1")
307
+ .get();
308
+ if (row) {
309
+ return { ...defaultConfig, ...JSON.parse(row.config) };
310
+ }
311
+ return { ...defaultConfig };
312
+ }
313
+ /**
314
+ * Set the display configuration (full or partial update)
315
+ */
316
+ export function setDisplayConfig(config) {
317
+ const db = getDatabase();
318
+ const current = getDisplayConfig();
319
+ const updated = { ...current, ...config };
320
+ db.prepare(`
321
+ INSERT INTO display_config (id, config, updated_at)
322
+ VALUES (1, ?, datetime('now'))
323
+ ON CONFLICT(id) DO UPDATE SET
324
+ config = excluded.config,
325
+ updated_at = excluded.updated_at
326
+ `).run(JSON.stringify(updated));
327
+ return updated;
328
+ }
329
+ /**
330
+ * Reset display configuration to defaults
331
+ */
332
+ export function resetDisplayConfig() {
333
+ const db = getDatabase();
334
+ db.prepare("DELETE FROM display_config WHERE id = 1").run();
335
+ return { ...defaultConfig };
336
+ }
337
+ /**
338
+ * Apply a preset theme
339
+ */
340
+ export function applyThemePreset(presetName) {
341
+ const preset = themePresets[presetName];
342
+ if (!preset) {
343
+ return null;
344
+ }
345
+ return setDisplayConfig(preset);
346
+ }
347
+ /**
348
+ * List available theme presets
349
+ */
350
+ export function listThemePresets() {
351
+ return Object.entries(themePresets).map(([name, preview]) => ({
352
+ name,
353
+ preview,
354
+ }));
355
+ }
356
+ /**
357
+ * Set specific display options (convenience function)
358
+ */
359
+ export function setDisplayOptions(options) {
360
+ return setDisplayConfig(options);
361
+ }
362
+ /**
363
+ * Set theme colors (convenience function)
364
+ */
365
+ export function setThemeColors(colors) {
366
+ return setDisplayConfig(colors);
367
+ }
368
+ /**
369
+ * Set the app title
370
+ */
371
+ export function setAppTitle(title) {
372
+ return setDisplayConfig({ appTitle: title });
373
+ }
374
+ /**
375
+ * Set fonts (Google Fonts names)
376
+ */
377
+ export function setFonts(fonts) {
378
+ return setDisplayConfig(fonts);
379
+ }
380
+ /**
381
+ * Set visual style options
382
+ */
383
+ export function setVisualStyle(style) {
384
+ return setDisplayConfig(style);
385
+ }
386
+ // ============================================
387
+ // Per-Game Theme Support
388
+ // ============================================
389
+ /**
390
+ * Get display configuration for a specific game
391
+ * Falls back to global config if no game theme exists
392
+ */
393
+ export function getGameDisplayConfig(gameId) {
394
+ const db = getDatabase();
395
+ const row = db
396
+ .prepare("SELECT config FROM game_themes WHERE game_id = ?")
397
+ .get(gameId);
398
+ if (row) {
399
+ return { ...defaultConfig, ...JSON.parse(row.config) };
400
+ }
401
+ // Fall back to global config
402
+ return getDisplayConfig();
403
+ }
404
+ /**
405
+ * Set display configuration for a specific game
406
+ */
407
+ export function setGameDisplayConfig(gameId, config) {
408
+ const db = getDatabase();
409
+ const current = getGameDisplayConfig(gameId);
410
+ const updated = { ...current, ...config };
411
+ db.prepare(`
412
+ INSERT INTO game_themes (game_id, config, updated_at)
413
+ VALUES (?, ?, datetime('now'))
414
+ ON CONFLICT(game_id) DO UPDATE SET
415
+ config = excluded.config,
416
+ updated_at = excluded.updated_at
417
+ `).run(gameId, JSON.stringify(updated));
418
+ return updated;
419
+ }
420
+ /**
421
+ * Apply a preset theme to a specific game
422
+ */
423
+ export function applyGameThemePreset(gameId, presetName) {
424
+ const preset = themePresets[presetName];
425
+ if (!preset) {
426
+ return null;
427
+ }
428
+ return setGameDisplayConfig(gameId, preset);
429
+ }
430
+ /**
431
+ * Reset game theme (removes game-specific config, falls back to global)
432
+ */
433
+ export function resetGameTheme(gameId) {
434
+ const db = getDatabase();
435
+ db.prepare("DELETE FROM game_themes WHERE game_id = ?").run(gameId);
436
+ }
437
+ /**
438
+ * Check if a game has a custom theme
439
+ */
440
+ export function hasGameTheme(gameId) {
441
+ const db = getDatabase();
442
+ const row = db
443
+ .prepare("SELECT 1 FROM game_themes WHERE game_id = ?")
444
+ .get(gameId);
445
+ return !!row;
446
+ }
447
+ /**
448
+ * Infer and apply theme based on game genre/setting
449
+ */
450
+ export function inferAndApplyTheme(gameId, genre, setting) {
451
+ const genreLower = genre.toLowerCase();
452
+ const settingLower = (setting || "").toLowerCase();
453
+ const combined = `${genreLower} ${settingLower}`;
454
+ // Map common genre keywords to presets
455
+ let presetName = null;
456
+ if (combined.includes("cyberpunk") ||
457
+ combined.includes("neon") ||
458
+ combined.includes("hacker")) {
459
+ presetName = "cyberpunk";
460
+ }
461
+ else if (combined.includes("sci-fi") ||
462
+ combined.includes("science fiction") ||
463
+ combined.includes("space") ||
464
+ combined.includes("futuristic")) {
465
+ presetName = "sci-fi";
466
+ }
467
+ else if (combined.includes("western") ||
468
+ combined.includes("wild west") ||
469
+ combined.includes("frontier") ||
470
+ combined.includes("cowboy")) {
471
+ presetName = "western";
472
+ }
473
+ else if (combined.includes("noir") ||
474
+ combined.includes("detective") ||
475
+ combined.includes("mystery") ||
476
+ combined.includes("hardboiled")) {
477
+ presetName = "noir";
478
+ }
479
+ else if (combined.includes("cosmic horror") ||
480
+ combined.includes("lovecraft") ||
481
+ combined.includes("eldritch") ||
482
+ combined.includes("cthulhu")) {
483
+ presetName = "cosmic-horror";
484
+ }
485
+ else if (combined.includes("steampunk") ||
486
+ combined.includes("victorian") ||
487
+ combined.includes("clockwork")) {
488
+ presetName = "steampunk";
489
+ }
490
+ else if (combined.includes("post-apocalyptic") ||
491
+ combined.includes("wasteland") ||
492
+ combined.includes("apocalypse") ||
493
+ combined.includes("survival")) {
494
+ presetName = "post-apocalyptic";
495
+ }
496
+ else if (combined.includes("pirate") ||
497
+ combined.includes("nautical") ||
498
+ combined.includes("sailing") ||
499
+ combined.includes("ocean")) {
500
+ presetName = "pirate";
501
+ }
502
+ else if (combined.includes("superhero") ||
503
+ combined.includes("comic") ||
504
+ combined.includes("hero")) {
505
+ presetName = "superhero";
506
+ }
507
+ else if (combined.includes("modern") ||
508
+ combined.includes("contemporary") ||
509
+ combined.includes("urban")) {
510
+ presetName = "modern";
511
+ }
512
+ else if (combined.includes("dark fantasy") ||
513
+ combined.includes("grimdark") ||
514
+ combined.includes("dark")) {
515
+ presetName = "dark-fantasy";
516
+ }
517
+ else if (combined.includes("fantasy") ||
518
+ combined.includes("medieval") ||
519
+ combined.includes("magic") ||
520
+ combined.includes("dragon")) {
521
+ presetName = "high-fantasy";
522
+ }
523
+ if (presetName) {
524
+ return applyGameThemePreset(gameId, presetName) || getDisplayConfig();
525
+ }
526
+ // No match - use default
527
+ return getDisplayConfig();
528
+ }
@@ -0,0 +1,61 @@
1
+ import type { Faction } from "../types/index.js";
2
+ export declare function createFaction(params: {
3
+ gameId: string;
4
+ name: string;
5
+ description?: string;
6
+ leaderId?: string;
7
+ headquartersId?: string;
8
+ resources?: Record<string, number>;
9
+ goals?: string[];
10
+ traits?: string[];
11
+ status?: "active" | "disbanded" | "hidden";
12
+ }): Faction;
13
+ export declare function getFaction(id: string): Faction | null;
14
+ export declare function updateFaction(id: string, updates: {
15
+ name?: string;
16
+ description?: string;
17
+ leaderId?: string | null;
18
+ headquartersId?: string | null;
19
+ traits?: string[];
20
+ status?: "active" | "disbanded" | "hidden";
21
+ }): Faction | null;
22
+ export declare function deleteFaction(id: string): boolean;
23
+ export declare function listFactions(gameId: string, filter?: {
24
+ status?: "active" | "disbanded" | "hidden";
25
+ }): Faction[];
26
+ export declare function modifyFactionResource(params: {
27
+ factionId: string;
28
+ resource: string;
29
+ delta: number;
30
+ }): Faction | null;
31
+ export declare function setFactionResource(params: {
32
+ factionId: string;
33
+ resource: string;
34
+ value: number;
35
+ }): Faction | null;
36
+ export declare function addFactionGoal(factionId: string, goal: string): Faction | null;
37
+ export declare function completeFactionGoal(factionId: string, goalIndex: number): Faction | null;
38
+ export declare function addFactionTrait(factionId: string, trait: string): Faction | null;
39
+ export declare function removeFactionTrait(factionId: string, trait: string): Faction | null;
40
+ export declare function modifyFactionTraits(factionId: string, params: {
41
+ add?: string[];
42
+ remove?: string[];
43
+ }): {
44
+ faction: Faction;
45
+ added: string[];
46
+ removed: string[];
47
+ } | null;
48
+ export declare function modifyFactionGoals(factionId: string, params: {
49
+ add?: string[];
50
+ complete?: number[];
51
+ }): {
52
+ faction: Faction;
53
+ added: string[];
54
+ completed: string[];
55
+ } | null;
56
+ export declare function updateFactionResource(params: {
57
+ factionId: string;
58
+ resource: string;
59
+ mode: "delta" | "set";
60
+ value: number;
61
+ }): Faction | null;