playlist-data-engine 1.7.3 → 1.8.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 (35) hide show
  1. package/README.md +14 -0
  2. package/bin/cli.cjs +85 -0
  3. package/dist/gateway-CDMPqFEH.js +1320 -0
  4. package/dist/gateway-DKa45Uz6.cjs +6 -0
  5. package/dist/gateway.d.ts +1 -0
  6. package/dist/gateway.d.ts.map +1 -1
  7. package/dist/gateway.js +1 -1
  8. package/dist/gateway.mjs +22 -19
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/playlist-data-engine.js +4 -4
  12. package/dist/playlist-data-engine.mjs +30 -27
  13. package/dist/utils/engineDocs.d.ts +33 -0
  14. package/dist/utils/engineDocs.d.ts.map +1 -0
  15. package/docs/DATA_ENGINE_REFERENCE.md +6660 -0
  16. package/docs/USAGE_IN_OTHER_PROJECTS.md +587 -0
  17. package/docs/features/AUDIO_ANALYSIS.md +610 -0
  18. package/docs/features/BEAT_DETECTION.md +5250 -0
  19. package/docs/features/COMBAT_SYSTEM.md +1632 -0
  20. package/docs/features/CONTENT_PACKS.md +464 -0
  21. package/docs/features/CUSTOM_CONTENT.md +603 -0
  22. package/docs/features/ENEMY_GENERATION.md +1711 -0
  23. package/docs/features/EQUIPMENT_SYSTEM.md +2279 -0
  24. package/docs/features/EXTENSIBILITY_GUIDE.md +1106 -0
  25. package/docs/features/GATEWAY_RESOLUTION.md +725 -0
  26. package/docs/features/IRL_SENSORS.md +360 -0
  27. package/docs/features/PLAYLIST_PARSING.md +446 -0
  28. package/docs/features/PREREQUISITES.md +571 -0
  29. package/docs/features/ROLLS_AND_SEEDS.md +687 -0
  30. package/docs/features/XP_AND_STATS.md +1221 -0
  31. package/llms.txt +33 -0
  32. package/package.json +9 -2
  33. package/skills/playlist-data-engine/SKILL.md +69 -0
  34. package/dist/gateway-C_p9Ku3O.js +0 -1211
  35. package/dist/gateway-Ceg-5xug.cjs +0 -1
@@ -0,0 +1,1106 @@
1
+ # Playlist Data Engine - Extensibility Guide
2
+
3
+ This guide explains how to extend the Playlist Data Engine with custom content. The extensibility system allows you to add custom spells, equipment, races, classes, and appearance options at runtime, with full control over spawn rates and validation.
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ 1. [Overview](#overview)
10
+ 2. [ExtensionManager API](#extensionmanager-api)
11
+ 3. [Helper Functions](#helper-functions)
12
+ 4. [Spawn Rate System](#spawn-rate-system)
13
+ 5. [Category-Specific Examples](#category-specific-examples)
14
+ 6. [Batch Image Methods](#batch-image-methods)
15
+ 7. [Content Packs](#content-packs)
16
+ 8. [Best Practices](#best-practices)
17
+ 9. [Validation](#validation)
18
+ 10. [Troubleshooting](#troubleshooting)
19
+ 11. [Reference](#reference)
20
+ 12. [Support](#support)
21
+
22
+ ---
23
+
24
+ ## Overview
25
+
26
+ The extensibility system allows you to:
27
+
28
+ - **Add custom content** to any procedural generation category
29
+ - **Control spawn rates** with relative or absolute weighting
30
+ - **Validate content** automatically with clear error messages
31
+ - **Create content packs** that can be loaded at runtime
32
+
33
+ **Location:** [src/core/extensions/ExtensionManager.ts](../src/core/extensions/ExtensionManager.ts)
34
+
35
+ ### Supported Categories
36
+
37
+ | Category | Description | Example |
38
+ |----------|-------------|---------|
39
+ | `equipment` | Weapons, armor, items | Custom weapons, magic items |
40
+ | `equipment.templates` | Complete equipment templates | Pre-built items with properties |
41
+ | `appearance.bodyTypes` | Character body shapes | 'giant', 'diminutive', etc. |
42
+ | `appearance.skinTones` | Skin color options | Hex colors |
43
+ | `appearance.hairColors` | Hair color options | Hex colors |
44
+ | `appearance.hairStyles` | Hair style options | 'braided', 'mohawk', etc. |
45
+ | `appearance.eyeColors` | Eye color options | Hex colors |
46
+ | `appearance.facialFeatures` | Facial features | 'scar', 'tattoo', etc. |
47
+ | `spells` | Arcane and divine magic | Custom spells |
48
+ | `spells.{className}` | Class-specific spells | 'spells.Wizard' |
49
+ | `races` | Race names | Custom races |
50
+ | `races.data` | Race data | Ability bonuses, speed, traits, subraces |
51
+ | `classes` | Class names | Custom classes |
52
+ | `classes.data` | Class data | Hit die, saves, skills, spellcasting |
53
+ | `classFeatures` | All class features | Custom rage, metamagic, etc. |
54
+ | `classFeatures.{className}` | Class-specific features | 'classFeatures.Barbarian' |
55
+ | `racialTraits` | All racial traits | Custom darkvision, stonecunning |
56
+ | `racialTraits.{raceName}` | Race-specific traits | 'racialTraits.Elf' |
57
+ | `skills` | All skills (default + custom) | Custom survival, knowledge |
58
+ | `skills.{ability}` | Ability-specific skills | 'skills.STR', 'skills.DEX' |
59
+ | `skillLists` | All skill lists | Per-class skill selections |
60
+ | `skillLists.{className}` | Class-specific skill lists | 'skillLists.Barbarian' |
61
+ | `classSpellLists` | All class spell lists | Class-specific spell selections |
62
+ | `classSpellLists.{className}` | Class-specific spell list | Custom spell lists |
63
+ | `classSpellSlots` | Spell slot progressions | Custom slot progressions by level |
64
+ | `classStartingEquipment` | All class starting equipment | Default gear per class |
65
+ | `classStartingEquipment.{className}` | Class-specific equipment | Custom starting equipment |
66
+
67
+ ---
68
+
69
+ ## ExtensionManager API
70
+
71
+ The `ExtensionManager` is a singleton that manages all custom content.
72
+
73
+ ```typescript
74
+ import { ExtensionManager } from 'playlist-data-engine';
75
+
76
+ const manager = ExtensionManager.getInstance();
77
+ ```
78
+
79
+ **Location:** [src/core/extensions/ExtensionManager.ts](../src/core/extensions/ExtensionManager.ts)
80
+
81
+ ### Core Methods
82
+
83
+ | Method | Parameters | Return | Description |
84
+ |--------|------------|--------|-------------|
85
+ | `register()` | `category`, `items`, `options?` | `void` | Register custom content for a category |
86
+ | `registerMultiple()` | `registrations[]` | `void` | Register multiple categories at once |
87
+ | `get()` | `category` | `any[]` | Get all items (defaults + custom) |
88
+ | `getDefaults()` | `category` | `any[]` | Get default items only |
89
+ | `getCustom()` | `category` | `any[]` | Get custom items only |
90
+ | `setWeights()` | `category`, `weights` | `void` | Set spawn weights for items |
91
+ | `getWeights()` | `category` | `Record` | Get current weights (defaults + custom) |
92
+ | `getDefaultWeights()` | `category` | `Record` | Get default weights (all 1.0) |
93
+ | `setMode()` | `category`, `mode` | `void` | Change spawn mode after registration |
94
+ | `getMode()` | `category` | `SpawnMode \| undefined` | Get current spawn mode |
95
+ | `hasCustomData()` | `category` | `boolean` | Check if category has custom data |
96
+ | `validate()` | `category`, `items` | `ValidationResult` | Validate items without registering |
97
+ | `reset()` | `category` | `void` | Reset category to defaults |
98
+ | `resetAll()` | | `void` | Reset all categories to defaults |
99
+ | `getInfo()` | `category?` | `Record` | Get info about registered extensions |
100
+ | `getCurrentOptions()` | `category` | `ExtensionOptions \| undefined` | Get current registration options |
101
+ | `exportCustomData()` | | `Record` | Export all custom data |
102
+ | `exportCustomDataForCategory()` | `category` | `any[]` | Export custom data for single category |
103
+ | `getRegisteredCategories()` | | `ExtensionCategory[]` | Get all categories with defaults |
104
+ | `batchAddIcons()` | `category`, `iconMap`, `identifierKey?` | `number` | Add icons to items by name/ID. Returns count updated. |
105
+ | `batchAddImages()` | `category`, `imageMap`, `identifierKey?` | `number` | Add images to items by name/ID. Returns count updated. |
106
+ | `batchUpdateImages()` | `category`, `predicate`, `updates` | `number` | Update icon/image on items matching predicate. Returns count. |
107
+ | `batchByCategory()` | `category`, `property`, `valueMap` | `number` | Add icons/images by property value (e.g., school). Returns count. |
108
+ | `getImageOverrides()` | | `Map<Category, ImageOverride[]>` | Get all image overrides for all categories. |
109
+ | `getImageOverridesForCategory()` | `category` | `ImageOverride[]` | Get image overrides for a specific category. |
110
+ | `restoreImageOverrides()` | `category`, `overrides` | `void` | Restore saved image overrides (for persistence). |
111
+ | `clearImageOverrides()` | `category` | `void` | Clear all image overrides for a category. |
112
+ | `clearAllImageOverrides()` | | `void` | Clear all image overrides for all categories. |
113
+
114
+ ### Registration Options
115
+
116
+ | Option | Type | Default | Description |
117
+ |--------|------|---------|-------------|
118
+ | `mode` | `'relative' \| 'absolute' \| 'default' \| 'replace'` | `'relative'` | Spawn mode for this extension |
119
+ | `weights` | `Record<string, number>` | `{}` | Custom spawn weights for items |
120
+ | `validate` | `boolean` | `true` | Whether to validate items before registering |
121
+
122
+ **Note:** Setting `mode` or `weights` during registration affects the entire category, not just the items being registered. This is a convenience equivalent to calling `setMode()` or `setWeights()` separately.
123
+
124
+ ### Spawn Modes
125
+
126
+ | Mode | Behavior | Use Case |
127
+ |------|----------|----------|
128
+ | `relative` | Custom items added to default pool with custom weights | Add custom items to existing pool |
129
+ | `absolute` | Only custom items can spawn (defaults excluded) | Themed content packs, complete replacement |
130
+ | `default` | All items have equal weight (1.0) | Disable custom spawn weights |
131
+ | `replace` | Clear previous custom data before registering | Hot-reload content packs during development |
132
+
133
+ ### Weight Values
134
+
135
+ | Value | Effect |
136
+ |-------|--------|
137
+ | `0` | Never spawns |
138
+ | `0.5` | Half as common as default |
139
+ | `1.0` | Default spawn rate |
140
+ | `2.0` | Twice as common as default |
141
+ | `10.0` | Very common |
142
+
143
+ ### Usage Example
144
+
145
+ ```typescript
146
+ // Register custom equipment with icon/image
147
+ manager.register('equipment', [
148
+ {
149
+ name: 'Dragon Sword',
150
+ type: 'weapon',
151
+ rarity: 'legendary',
152
+ weight: 5,
153
+ icon: '/icons/weapons/dragon-sword.png',
154
+ image: '/images/equipment/dragon-sword.png'
155
+ }
156
+ ], { mode: 'relative', weights: { 'Dragon Sword': 0.5 } });
157
+
158
+ // Adjust weights
159
+ manager.setWeights('equipment', {
160
+ 'Longsword': 2,
161
+ 'Dagger': 0.5,
162
+ 'Excalibur': 0.1
163
+ });
164
+ const weights = manager.getWeights('equipment');
165
+
166
+ // Inspect registered data
167
+ if (manager.hasCustomData('equipment')) {
168
+ const info = manager.getInfo('equipment');
169
+ const customItems = manager.getCustom('equipment');
170
+ }
171
+ ```
172
+
173
+ ---
174
+
175
+ ## Helper Functions
176
+
177
+ The engine provides several helper functions for querying custom content. These complement `ExtensionManager` by providing read access to registered data.
178
+
179
+ ### Quick Reference
180
+
181
+ | Function | Parameters | Returns | Description |
182
+ |----------|------------|---------|-------------|
183
+ | `getClassData` | `className: string` | `ClassDataEntry \| undefined` | Class data with hit die, abilities, features |
184
+ | `getRaceData` | `raceName: string` | `RaceDataEntry \| undefined` | Race data with speed, ability bonuses, traits |
185
+ | `getClassSpellList` | `className: string` | Spell list object \| undefined | `{ cantrips: string[], spells_by_level: Record<number, string[]> }` |
186
+ | `getSpellSlotsForClass` | `className, characterLevel` | Slot object \| undefined | `{ [level: number]: slots }` |
187
+ | `getClassStartingEquipment` | `className: string` | Equipment object \| undefined | `{ weapons: [...], armor: [...], items: [...] }` |
188
+
189
+ ### Usage Examples
190
+
191
+ ```typescript
192
+ import {
193
+ getClassData,
194
+ getRaceData,
195
+ getClassSpellList,
196
+ getSpellSlotsForClass,
197
+ getClassStartingEquipment
198
+ } from 'playlist-data-engine';
199
+
200
+ // Class data (default or custom)
201
+ const wizardData = getClassData('Wizard');
202
+ console.log(wizardData.hit_die); // 6
203
+
204
+ const necromancerData = getClassData('Necromancer');
205
+ if (necromancerData) {
206
+ console.log(necromancerData.baseClass); // 'Wizard'
207
+ console.log(necromancerData.primary_ability); // 'INT'
208
+ }
209
+
210
+ // Race data (default or custom)
211
+ const elfData = getRaceData('Elf');
212
+ console.log(elfData.speed); // 30
213
+
214
+ const dragonkinData = getRaceData('Dragonkin');
215
+ if (dragonkinData) {
216
+ console.log(dragonkinData.ability_bonuses);
217
+ // { STR: 2, CON: 1, CHA: 1 }
218
+ }
219
+
220
+ // Additional class queries
221
+ const spellList = getClassSpellList('Necromancer');
222
+ const slots = getSpellSlotsForClass('Necromancer', 5);
223
+ const equipment = getClassStartingEquipment('Necromancer');
224
+ ```
225
+
226
+ ---
227
+
228
+ ## Spawn Rate System
229
+
230
+ The spawn rate system controls how custom content is mixed with default content during procedural generation. See the [Spawn Modes table](#extensionmanager-api) for mode descriptions.
231
+
232
+ ### Advanced Weight Configuration
233
+
234
+ You can use hierarchical weight configuration for fine-grained control:
235
+
236
+ ```typescript
237
+ const manager = ExtensionManager.getInstance();
238
+
239
+ // Hierarchical weight system
240
+ // Category defaults with individual overrides
241
+
242
+ // Set default for all skills
243
+ manager.setWeights('skills', {
244
+ default: 1.0 // All skills have equal weight by default
245
+ });
246
+
247
+ // Override specific skills
248
+ manager.setWeights('skills', {
249
+ 'athletics': 2.0, // Override: athletics is now 2x
250
+ 'acrobatics': 0.5, // Override: acrobatics is now 0.5x
251
+ // All other skills remain at 1.0 (the default)
252
+ });
253
+
254
+ // Per-class skill spawn rates
255
+ manager.setWeights('skillLists.Barbarian', {
256
+ 'athletics': 2.0, // Barbarians favor athletics
257
+ 'survival': 1.5, // And survival
258
+ 'arcana': 0.2 // But rarely get arcana
259
+ });
260
+
261
+ manager.setWeights('skillLists.Wizard', {
262
+ 'arcana': 2.0, // Wizards favor arcana
263
+ 'history': 1.5, // And history
264
+ 'athletics': 0.2 // But rarely get athletics
265
+ });
266
+
267
+ // Zero weight = never spawn
268
+ manager.setWeights('classFeatures.Barbarian', {
269
+ 'useless_feature': 0.0 // This feature will never spawn
270
+ });
271
+
272
+ // Reset to defaults
273
+ manager.reset('classFeatures.Barbarian');
274
+ // Now all Barbarian features are back to equal probability
275
+
276
+ // Reset all categories
277
+ manager.resetAll();
278
+ ```
279
+
280
+ ---
281
+
282
+ ## Category-Specific Examples
283
+
284
+ ### Equipment
285
+
286
+ Register custom equipment through ExtensionManager or via the `CharacterGenerator.generate()` convenience parameter. For complete examples including registration, spawn rates, and the CharacterGenerator convenience method, see [Custom Equipment](EQUIPMENT_SYSTEM.md#custom-equipment).
287
+
288
+ ### Spells
289
+
290
+ Register custom spells through ExtensionManager:
291
+
292
+ ```typescript
293
+ import { ExtensionManager } from 'playlist-data-engine';
294
+
295
+ const manager = ExtensionManager.getInstance();
296
+
297
+ const customSpells = [
298
+ {
299
+ name: 'Phoenix Fire',
300
+ level: 5,
301
+ school: 'Evocation',
302
+ casting_time: '1 action',
303
+ range: '60 feet',
304
+ duration: 'Instantaneous',
305
+ components: ['V', 'S'],
306
+ description: 'A burst of flame engulfs the target...',
307
+ icon: '/icons/spells/phoenix-fire.png',
308
+ image: '/images/spells/phoenix-fire.png'
309
+ },
310
+ {
311
+ name: 'Mind Shield',
312
+ level: 2,
313
+ school: 'Abjuration',
314
+ casting_time: '1 reaction',
315
+ range: 'Self',
316
+ duration: '1 minute',
317
+ components: ['S'],
318
+ description: 'You gain resistance to psychic damage...',
319
+ icon: '/icons/spells/mind-shield.png',
320
+ image: '/images/spells/mind-shield.png'
321
+ }
322
+ ];
323
+
324
+ // Register custom spells
325
+ manager.register('spells', customSpells);
326
+ ```
327
+
328
+
329
+ #### Spell Query
330
+
331
+ Query spells and check prerequisites using SpellQuery:
332
+
333
+ ```typescript
334
+ import { SpellQuery } from 'playlist-data-engine';
335
+
336
+ const spellQuery = SpellQuery.getInstance();
337
+
338
+ // Query spells by level, school, or class
339
+ const fifthLevelSpells = spellQuery.getSpellsByLevel(5);
340
+ const evocationSpells = spellQuery.getSpellsBySchool('Evocation');
341
+ const sorcererSpells = spellQuery.getSpellsForClass('Sorcerer');
342
+
343
+ // Get spells available to a character (prerequisites met)
344
+ const availableSpells = spellQuery.getAvailableSpells(character);
345
+ console.log(`Available spells: ${availableSpells.map(s => s.name).join(', ')}`);
346
+
347
+ // Get a specific spell
348
+ const phoenixFire = spellQuery.getSpell('phoenix_fire');
349
+
350
+ // Validate spell prerequisites
351
+ if (phoenixFire) {
352
+ const validation = spellQuery.validatePrerequisites(phoenixFire, character);
353
+ if (!validation.valid) {
354
+ console.log(`Prerequisites not met: ${validation.errors.join(', ')}`);
355
+ }
356
+ }
357
+
358
+ // Query statistics
359
+ const stats = spellQuery.getQueryStats();
360
+ console.log(`Total spells: ${stats.totalSpells} (${stats.customSpells} custom)`);
361
+ ```
362
+
363
+
364
+ #### Spell Prerequisites
365
+
366
+ Spells can have prerequisites that must be met before a spellcaster can learn them (features, abilities, spells, skills, level, or class). For full examples and usage, see [Spells with Prerequisites](PREREQUISITES.md#spells-with-prerequisites).
367
+
368
+
369
+ ### Races / Subraces
370
+
371
+ Add custom races and subraces, and control their spawn rates. For complete examples including type augmentation, race data registration, validation, subrace support, and spawn rate control, see [Custom Races](CUSTOM_CONTENT.md#custom-races).
372
+
373
+ ### Classes
374
+
375
+ Adjust spawn rates for existing classes or create entirely new custom classes. For complete examples including template inheritance, audio preferences, spawn rate control, and full class registration, see [Custom Classes](CUSTOM_CONTENT.md#custom-classes).
376
+
377
+
378
+ ### Class Features
379
+
380
+ Register custom class features through ExtensionManager:
381
+
382
+ ```typescript
383
+ import { ExtensionManager } from 'playlist-data-engine';
384
+
385
+ const manager = ExtensionManager.getInstance();
386
+
387
+ // Register custom class features (recommended approach)
388
+ manager.register('classFeatures', [
389
+ {
390
+ id: 'dragon_fury',
391
+ name: 'Dragon Fury',
392
+ description: 'Channel your draconic heritage to unleash devastating attacks',
393
+ type: 'active',
394
+ class: 'Barbarian',
395
+ level: 3,
396
+ prerequisites: {
397
+ level: 3
398
+ },
399
+ effects: [
400
+ {
401
+ type: 'passive_modifier',
402
+ target: 'damage',
403
+ value: 2,
404
+ condition: 'while raging'
405
+ }
406
+ ],
407
+ source: 'custom',
408
+ icon: '/icons/features/dragon-fury.png',
409
+ image: '/images/features/dragon-fury.png'
410
+ },
411
+ {
412
+ id: 'arcane_shield',
413
+ name: 'Arcane Shield',
414
+ description: 'Create a protective barrier of magical energy',
415
+ type: 'active',
416
+ class: 'Wizard',
417
+ level: 2,
418
+ prerequisites: {
419
+ level: 2,
420
+ abilities: { INT: 14 }
421
+ },
422
+ effects: [
423
+ {
424
+ type: 'ability_unlock',
425
+ target: 'mage_armor',
426
+ value: true
427
+ }
428
+ ],
429
+ source: 'custom',
430
+ icon: '/icons/features/arcane-shield.png',
431
+ image: '/images/features/arcane-shield.png'
432
+ }
433
+ ]);
434
+
435
+ // Set spawn rates for features
436
+ manager.setWeights('classFeatures.Barbarian', {
437
+ 'dragon_fury': 0.5, // Half as likely to spawn
438
+ 'rage': 1.0 // Default spawn rate
439
+ });
440
+ ```
441
+
442
+ **Feature Effect Types:**
443
+
444
+ | Type | Description | Example |
445
+ |------|-------------|---------|
446
+ | `stat_bonus` | Add to an ability score | +1 STR at level 4 |
447
+ | `skill_proficiency` | Grant proficiency or expertise | Expertise in Perception |
448
+ | `ability_unlock` | Unlock new abilities | Darkvision, flight |
449
+ | `passive_modifier` | Constant bonus to rolls | +2 damage while raging |
450
+ | `resource_grant` | Grant resource pools | Rage counts, ki points |
451
+ | `spell_slot_bonus` | Additional spell slots | +1 level 1 slot |
452
+
453
+ **Feature Prerequisites:**
454
+
455
+ Features can require levels, abilities, skills, spells, features, class, race, subrace, or custom conditions. For complete examples including skill prerequisites, spell prerequisites, racial traits with prerequisites, and validation, see [Feature Prerequisites](PREREQUISITES.md#feature-prerequisites).
456
+
457
+ ### Racial Traits
458
+
459
+ Register custom racial traits through ExtensionManager:
460
+
461
+ ```typescript
462
+ import { ExtensionManager } from 'playlist-data-engine';
463
+
464
+ const manager = ExtensionManager.getInstance();
465
+
466
+ // Register custom racial traits (recommended approach)
467
+ manager.register('racialTraits', [
468
+ {
469
+ id: 'dragon_born_fire_resistance',
470
+ name: 'Fire Resistance',
471
+ description: 'You have resistance to fire damage',
472
+ race: 'Dragonborn',
473
+ effects: [
474
+ {
475
+ type: 'ability_unlock',
476
+ target: 'damage_resistance',
477
+ value: 'fire'
478
+ }
479
+ ],
480
+ source: 'default',
481
+ icon: '/icons/traits/fire-resistance.png',
482
+ image: '/images/traits/fire-resistance.png'
483
+ },
484
+ {
485
+ id: 'fairy_flight',
486
+ name: 'Fey Wings',
487
+ description: 'You can fly using your magical wings',
488
+ race: 'Fairy',
489
+ prerequisites: {
490
+ level: 5
491
+ },
492
+ effects: [
493
+ {
494
+ type: 'ability_unlock',
495
+ target: 'flight',
496
+ value: true,
497
+ condition: 'level 5+'
498
+ }
499
+ ],
500
+ source: 'custom',
501
+ icon: '/icons/traits/fey-wings.png',
502
+ image: '/images/traits/fey-wings.png'
503
+ },
504
+ {
505
+ id: 'elemental_affinity',
506
+ name: 'Elemental Affinity',
507
+ description: 'You are attuned to a specific element',
508
+ race: 'Genasi',
509
+ effects: [
510
+ {
511
+ type: 'ability_unlock',
512
+ target: 'elemental_magic',
513
+ value: true
514
+ }
515
+ ],
516
+ source: 'custom',
517
+ icon: '/icons/traits/elemental-affinity.png',
518
+ image: '/images/traits/elemental-affinity.png'
519
+ }
520
+ ]);
521
+ ```
522
+
523
+
524
+ **Get traits for a race:**
525
+
526
+ ```typescript
527
+ const query = FeatureQuery.getInstance();
528
+
529
+ // Get all traits for a race
530
+ const dragonbornTraits = query.getRacialTraits('Dragonborn');
531
+
532
+ // Get traits for a subrace
533
+ const hillDwarfTraits = query.getRacialTraitsForSubrace('Dwarf', 'Hill Dwarf');
534
+
535
+ // Get a specific trait
536
+ const fireResistance = query.getRacialTraitById('dragon_born_fire_resistance');
537
+
538
+ const featureStats = query.getQueryStats();
539
+ console.log(`Features: ${featureStats.totalFeatures} (${featureStats.customFeatures} custom)`);
540
+ ```
541
+
542
+ ### Skills
543
+
544
+ Register custom skills through ExtensionManager:
545
+
546
+ ```typescript
547
+ import { ExtensionManager } from 'playlist-data-engine';
548
+
549
+ const manager = ExtensionManager.getInstance();
550
+
551
+ // Register custom skills (recommended approach)
552
+ manager.register('skills', [
553
+ {
554
+ id: 'survival_cold',
555
+ name: 'Survival (Cold Environments)',
556
+ description: 'Expertise in surviving freezing conditions',
557
+ ability: 'WIS',
558
+ armorPenalty: false,
559
+ categories: ['exploration', 'environmental'],
560
+ source: 'custom',
561
+ icon: '/icons/skills/survival-cold.png',
562
+ image: '/images/skills/survival-cold.png'
563
+ },
564
+ {
565
+ id: 'arcana_crystal',
566
+ name: 'Arcana (Crystals)',
567
+ description: 'Knowledge of magical crystals and their uses',
568
+ ability: 'INT',
569
+ armorPenalty: false,
570
+ categories: ['knowledge', 'magical'],
571
+ source: 'custom',
572
+ icon: '/icons/skills/arcana-crystal.png',
573
+ image: '/images/skills/arcana-crystal.png'
574
+ },
575
+ {
576
+ id: 'intimidation_war',
577
+ name: 'Intimidation (War Cry)',
578
+ description: 'Terrifying shouts on the battlefield',
579
+ ability: 'CHA',
580
+ armorPenalty: false,
581
+ categories: ['combat', 'social'],
582
+ source: 'custom',
583
+ icon: '/icons/skills/intimidation-war.png',
584
+ image: '/images/skills/intimidation-war.png'
585
+ }
586
+ ]);
587
+
588
+ // Set spawn rates for skills
589
+ manager.setWeights('skills', {
590
+ 'survival_cold': 0.5, // Half as likely
591
+ 'athletics': 2.0, // Twice as likely
592
+ 'intimidation_war': 1.0 // Default rate
593
+ });
594
+ ```
595
+
596
+ **Register ability-specific skills:**
597
+
598
+ ```typescript
599
+ const manager = ExtensionManager.getInstance();
600
+
601
+ // Register skills for specific abilities
602
+ manager.register('skills.STR', [
603
+ {
604
+ id: 'climbing',
605
+ name: 'Climbing',
606
+ ability: 'STR',
607
+ armorPenalty: true,
608
+ categories: ['athletic'],
609
+ source: 'custom'
610
+ }
611
+ ]);
612
+
613
+ manager.register('skills.DEX', [
614
+ {
615
+ id: 'balancing',
616
+ name: 'Balancing',
617
+ ability: 'DEX',
618
+ armorPenalty: true,
619
+ categories: ['athletic'],
620
+ source: 'custom'
621
+ }
622
+ ]);
623
+ ```
624
+
625
+ **Query skills:**
626
+
627
+ ```typescript
628
+ const query = SkillQuery.getInstance();
629
+
630
+ // Get skill by ID
631
+ const survival = query.getSkill('survival_cold');
632
+
633
+ // Get all skills for an ability
634
+ const strSkills = query.getSkillsByAbility('STR');
635
+
636
+ // Get skills by category
637
+ const explorationSkills = query.getSkillsByCategory('exploration');
638
+
639
+ // Get custom skills only
640
+ const customSkills = query.getSkillsBySource('custom');
641
+
642
+ // Check if skill exists
643
+ const isValid = query.isValidSkill('survival_cold'); // true
644
+
645
+ // Get registry statistics
646
+ const skillStats = query.getQueryStats();
647
+ console.log(`Skills: ${skillStats.totalSkills} (${skillStats.customSkills} custom)`);
648
+ ```
649
+
650
+ #### Skills with Prerequisites
651
+
652
+ Skills can have prerequisites that must be met before a character can gain proficiency in them. This allows for advanced skills that require base skills, specific features, spells, ability scores, level, class, or race. For complete examples including skill chains, ability prerequisites, spell prerequisites, race prerequisites, and validation, see [Skill Prerequisites](PREREQUISITES.md#skill-prerequisites).
653
+
654
+ ### Skill Lists
655
+
656
+ Define custom skill lists for classes:
657
+
658
+ ```typescript
659
+ const manager = ExtensionManager.getInstance();
660
+
661
+ // Register custom skill list for a class
662
+ manager.register('skillLists', [
663
+ {
664
+ class: 'Barbarian',
665
+ skillCount: 2,
666
+ availableSkills: [
667
+ 'athletics',
668
+ 'survival',
669
+ 'survival_cold', // Custom skill
670
+ 'intimidation',
671
+ 'intimidation_war', // Custom skill
672
+ 'nature',
673
+ 'perception'
674
+ ],
675
+ selectionWeights: {
676
+ weights: {
677
+ 'athletics': 2.0,
678
+ 'survival_cold': 0.5
679
+ },
680
+ mode: 'relative'
681
+ },
682
+ hasExpertise: false,
683
+ expertiseCount: 0
684
+ },
685
+ {
686
+ class: 'Necromancer', // Custom class
687
+ skillCount: 3,
688
+ availableSkills: [
689
+ 'arcana',
690
+ 'arcana_crystal', // Custom skill
691
+ 'history',
692
+ 'religion',
693
+ 'medicine',
694
+ 'investigation'
695
+ ],
696
+ hasExpertise: true,
697
+ expertiseCount: 1
698
+ }
699
+ ]);
700
+ ```
701
+
702
+
703
+ ### Appearance
704
+
705
+ Customize physical appearance options using ExtensionManager. **Tip:** Set mode to `'absolute'` to use only your custom options:
706
+
707
+ ```typescript
708
+ const manager = ExtensionManager.getInstance();
709
+
710
+ // Register custom appearance options
711
+ manager.register('appearance.bodyTypes', ['giant', 'diminutive', 'elongated']);
712
+ manager.register('appearance.hairStyles', ['mohawk', 'braided', 'pompadour', 'mullet']);
713
+ manager.register('appearance.facialFeatures', ['crystal tattoo', 'runes on cheek', 'glowing eyes', 'fangs']);
714
+
715
+ // Register custom colors (hex format)
716
+ manager.register('appearance.skinTones', ['#8B7355', '#F5DEB3', '#FFE4C4']);
717
+ manager.register('appearance.hairColors', ['#FF69B4', '#00CED1', '#9400D3']);
718
+ manager.register('appearance.eyeColors', ['#FF0000', '#800080', '#C0C0C0']);
719
+
720
+ // Use only custom options (exclude defaults)
721
+ manager.setMode('appearance.bodyTypes', 'absolute');
722
+ manager.setMode('appearance.hairStyles', 'absolute');
723
+ manager.setMode('appearance.facialFeatures', 'absolute');
724
+ manager.setMode('appearance.skinTones', 'absolute');
725
+ manager.setMode('appearance.hairColors', 'absolute');
726
+ manager.setMode('appearance.eyeColors', 'absolute');
727
+
728
+ // Optional: Weight specific options
729
+ manager.setWeights('appearance.bodyTypes', {
730
+ 'giant': 0.2, // Very rare
731
+ 'diminutive': 0.3, // Rare
732
+ 'athletic': 1.5 // Common
733
+ });
734
+ ```
735
+
736
+ **All appearance properties:** `bodyTypes`, `hairStyles`, `facialFeatures`, `skinTones`, `hairColors`, `eyeColors`
737
+
738
+ ### Batch Image Methods
739
+
740
+ Use batch methods to add icons and images to multiple items at once. All methods validate URLs before applying changes.
741
+
742
+ **Supported categories:** `spells`, `skills`, `classFeatures`, `racialTraits`, `equipment`, `races.data`, `classes.data`
743
+
744
+ **Valid URL prefixes:** `http://`, `https://`, `/`, `assets/`
745
+
746
+ ```typescript
747
+ import { ExtensionManager } from 'playlist-data-engine';
748
+
749
+ const manager = ExtensionManager.getInstance();
750
+
751
+ // --- Add icons to specific items by name ---
752
+ manager.batchAddIcons('spells', {
753
+ 'Fireball': '/assets/spells/fireball.png',
754
+ 'Magic Missile': '/assets/spells/magic-missile.png'
755
+ });
756
+
757
+ manager.batchAddIcons('equipment', {
758
+ 'Longsword': '/assets/equipment/longsword.png'
759
+ });
760
+
761
+ // --- Add images to specific items ---
762
+ manager.batchAddImages('spells', {
763
+ 'Fireball': '/assets/spells/fireball-full.png'
764
+ });
765
+
766
+ // --- Update by predicate (matches items and applies updates) ---
767
+ // Add same icon to all cantrips
768
+ manager.batchUpdateImages('spells',
769
+ spell => spell.level === 0,
770
+ { icon: '/assets/spells/cantrip-icon.png' }
771
+ );
772
+
773
+ // Add images to all rare equipment
774
+ manager.batchUpdateImages('equipment',
775
+ item => item.rarity === 'rare',
776
+ { icon: '/assets/icons/rare.png', image: '/assets/images/rare-bg.png' }
777
+ );
778
+
779
+ // --- Update by category property ---
780
+ // Add icons by spell school
781
+ manager.batchByCategory('spells', 'school', {
782
+ 'Evocation': '/assets/icons/fire.png',
783
+ 'Necromancy': '/assets/icons/skull.png',
784
+ 'Abjuration': '/assets/icons/shield.png'
785
+ });
786
+
787
+ // Add icons by equipment rarity
788
+ manager.batchByCategory('equipment', 'rarity', {
789
+ 'legendary': '/assets/icons/star-gold.png',
790
+ 'very_rare': '/assets/icons/star-purple.png',
791
+ 'rare': '/assets/icons/star-blue.png'
792
+ });
793
+
794
+ // Add both icon and image by rarity
795
+ manager.batchByCategory('equipment', 'rarity', {
796
+ 'legendary': {
797
+ icon: '/assets/icons/legendary.png',
798
+ image: '/assets/images/legendary-bg.png'
799
+ }
800
+ });
801
+ ```
802
+
803
+ **Error handling:**
804
+ ```typescript
805
+ try {
806
+ manager.batchAddIcons('spells', { 'Fireball': 'ftp://invalid.com/icon.png' });
807
+ } catch (error) {
808
+ console.error('Invalid URL format:', error.message);
809
+ // URLs must start with http://, https://, /, or assets/
810
+ }
811
+ ```
812
+
813
+ ---
814
+
815
+ ### Image Overrides (Patch System)
816
+
817
+ Batch image methods use a **patch-based storage system** that stores only icon/image changes, not complete item copies. This prevents duplicates when applying images to default items.
818
+
819
+ **How it works:**
820
+ 1. `batchUpdateImages()` and `batchByCategory()` store **patches** (identifier → {icon, image}) in a separate `imageOverrides` map
821
+ 2. When `get()` retrieves items, patches are **applied on top** of the item data
822
+ 3. This means default items remain in the default pool but appear with your custom images
823
+
824
+ **Benefits:**
825
+ - No duplicate items - you always have the same number of items
826
+ - Images persist across sessions via localStorage integration
827
+ - Works correctly in all view modes (default, relative, replace, absolute)
828
+
829
+ **API Methods:**
830
+
831
+ | Method | Parameters | Return | Description |
832
+ |--------|------------|--------|-------------|
833
+ | `getImageOverrides()` | | `Map<Category, ImageOverride[]>` | Get all image overrides for all categories |
834
+ | `getImageOverridesForCategory(category)` | `category` | `ImageOverride[]` | Get overrides for a specific category |
835
+ | `restoreImageOverrides(category, overrides)` | `category`, `overrides` | `void` | Restore saved overrides (for persistence) |
836
+ | `clearImageOverrides(category)` | `category` | `void` | Clear all overrides for a category |
837
+ | `clearAllImageOverrides()` | | `void` | Clear all overrides for all categories |
838
+
839
+ **ImageOverride interface:**
840
+ ```typescript
841
+ interface ImageOverride {
842
+ identifier: string; // Item id (for spells) or name (for others)
843
+ icon?: string; // Icon URL
844
+ image?: string; // Image URL
845
+ appliedAt: number; // Timestamp when applied
846
+ }
847
+ ```
848
+
849
+ **Example - Persistence integration:**
850
+ ```typescript
851
+ import { ExtensionManager, type ImageOverride } from 'playlist-data-engine';
852
+
853
+ const manager = ExtensionManager.getInstance();
854
+
855
+ // After batch applying images, save overrides to localStorage
856
+ const overrides = manager.getImageOverridesForCategory('spells');
857
+ localStorage.setItem('spell_image_overrides', JSON.stringify(overrides));
858
+
859
+ // On app startup, restore overrides
860
+ const savedOverrides = JSON.parse(localStorage.getItem('spell_image_overrides') || '[]');
861
+ manager.restoreImageOverrides('spells', savedOverrides);
862
+
863
+ // Clear overrides to reset to defaults
864
+ manager.clearImageOverrides('spells');
865
+ ```
866
+
867
+ ---
868
+
869
+ ## Content Packs
870
+
871
+ Content packs are reusable collections of custom content for multiple categories that can be saved to files and loaded at runtime. For complete examples including basic packs, themed packs, expansion packs with custom features and skills, prerequisite-based content, and saving/loading functionality, see [Content Packs](CONTENT_PACKS.md).
872
+
873
+ ---
874
+
875
+ ## Best Practices
876
+
877
+ ### 1. Use Descriptive Names
878
+
879
+ ```typescript
880
+ // Good
881
+ { name: 'Sword of the Dawn', type: 'weapon', rarity: 'rare', weight: 3 }
882
+
883
+ // Bad
884
+ { name: 'sword1', type: 'weapon', rarity: 'rare', weight: 3 }
885
+ ```
886
+
887
+ ### 2. Set Appropriate Spawn Rates
888
+
889
+ ```typescript
890
+ // Good balance
891
+ manager.setWeights('equipment', {
892
+ 'Common Sword': 1.0, // Default
893
+ 'Rare Sword': 0.5, // Half as common
894
+ 'Legendary Sword': 0.1 // Very rare
895
+ });
896
+
897
+ // Bad - everything is legendary
898
+ manager.setWeights('equipment', {
899
+ 'Legendary Sword': 1.0,
900
+ 'Legendary Armor': 1.0
901
+ });
902
+ ```
903
+
904
+ ### 3. Use Themed Content Packs
905
+
906
+ ```typescript
907
+ // Good - organized by theme
908
+ loadDarkFantasyPack();
909
+ loadHighFantasyPack();
910
+ loadSciFiPack();
911
+
912
+ // Bad - random mix of content
913
+ register('equipment', [...darkFantasyItems, ...highFantasyItems, ...sciFiItems]);
914
+ ```
915
+
916
+ ### 4. Reset When Needed
917
+
918
+ ```typescript
919
+ // Reset before loading new content
920
+ const manager = ExtensionManager.getInstance();
921
+ manager.resetAll();
922
+
923
+ // Load fresh content
924
+ loadMyContentPack();
925
+ ```
926
+
927
+ ### 5. Handle Validation Errors
928
+
929
+ ```typescript
930
+ try {
931
+ manager.register('equipment', customItems);
932
+ } catch (error) {
933
+ console.error('Failed to register equipment:', error.message);
934
+ // Handle error gracefully
935
+ }
936
+ ```
937
+
938
+ ### 6. Use Absolute Mode for Themed Content
939
+
940
+ ```typescript
941
+ // Good - absolute mode for themed content
942
+ manager.register('equipment', darkFantasyItems, { mode: 'absolute' });
943
+
944
+ // Bad - relative mode for themed content (default items will also spawn)
945
+ manager.register('equipment', darkFantasyItems, { mode: 'relative' });
946
+ ```
947
+
948
+ ### 7. Document Your Content Packs
949
+
950
+ ```typescript
951
+ /**
952
+ * Dark Fantasy Content Pack
953
+ *
954
+ * Adds dark fantasy themed equipment, spells, and appearance options.
955
+ *
956
+ * Equipment: Soul Reaper (legendary), Shadow Cloak (very rare), etc.
957
+ * Spells: Soul Drain, Shadow Step, Death Coil
958
+ * Appearance: Dark skin tones, undead features
959
+ *
960
+ * @author Your Name
961
+ * @version 1.0.0
962
+ */
963
+ export function loadDarkFantasyPack() {
964
+ // ...
965
+ }
966
+ ```
967
+
968
+ ---
969
+
970
+ ## Validation
971
+
972
+ The extensibility system includes automatic validation. Invalid content is rejected with clear error messages.
973
+
974
+ **For complete type definitions, see [Reference](#reference).**
975
+
976
+ ### Key Validation Rules
977
+
978
+ | Category | Required Fields | Validation Rules |
979
+ |----------|-----------------|------------------|
980
+ | **Equipment** | `name`, `type`, `rarity`, `weight` | Type: `weapon`/`armor`/`item`; valid rarity; weight ≥ 0 |
981
+ | **Spells** | `name`, `level`, `school` | Level 0-9; valid school (see `SpellSchool` type) |
982
+ | **Races/Classes** | String values | Must be valid name (default or registered custom) |
983
+ | **Appearance** | String values | Must be strings (not objects) |
984
+ | **Features** | `id`, `name`, `description`, `type`, `class`, `level`, `source` | ID: `lowercase_with_underscores`; must be unique |
985
+ | **Skills** | `id`, `name`, `ability`, `source` | ID: `lowercase_with_underscores`; valid ability |
986
+ | **Skill Lists** | `class`, `skillCount`, `availableSkills` | skillCount ≥ 0; skill IDs must exist |
987
+
988
+ ### ID Format
989
+
990
+ All custom content IDs must use `lowercase_with_underscores` format.
991
+
992
+ | Valid | Invalid |
993
+ |-------|---------|
994
+ | `frost_rage` | `FrostRage` (use lowercase) |
995
+ | `necromancer_raise_dead` | `frost-rage` (use underscores) |
996
+ | `dragon_smithing` | `frost.rage` (use underscores) |
997
+
998
+ ### Notes
999
+
1000
+ - **Duplicate IDs:** Automatically detected. Use `getCustom(category)` to check existing IDs before registering.
1001
+ - **Disable validation:** Use `{ validate: false }` to bypass (advanced use only—may cause runtime errors).
1002
+
1003
+ ### Image/Icon URL Validation
1004
+
1005
+ Use `ImageValidator` to validate `icon` and `image` URL fields for custom content. Valid URL prefixes: `http://`, `https://`, `/`, `assets/`
1006
+
1007
+ ```typescript
1008
+ import { validateImageFields } from 'playlist-data-engine';
1009
+
1010
+ const errors = validateImageFields({ icon: '/assets/icon.png', image: 'https://example.com/image.png' });
1011
+ if (errors.length > 0) {
1012
+ console.error('Invalid image URLs:', errors);
1013
+ }
1014
+ ```
1015
+
1016
+ ---
1017
+
1018
+ ## Troubleshooting
1019
+
1020
+ | Problem | Solution |
1021
+ |---------|----------|
1022
+ | **Content not appearing** | 1. Register before `CharacterGenerator.generate()` 2. Check console for validation errors 3. Verify weights ≠ 0 4. Use correct category name (see [Supported Categories](#supported-categories)) |
1023
+ | **Validation errors** | Error messages include category, item index, and specific issue. Use `validate: false` to bypass (not recommended). |
1024
+ | **Content not persisting** | System is **runtime only**. Re-register on app startup. See [Export/Import System](#exportimport-system) for persistence patterns. |
1025
+ | **Spawn rates not working** | Check spawn mode. See [Spawn Modes](#extensionmanager-api). Use `getMode()` to verify current mode. |
1026
+ | **Duplicate ID errors** | Custom IDs must be unique. Use `getCustom(category)` to check existing IDs. |
1027
+
1028
+ ### Debugging
1029
+
1030
+ ```typescript
1031
+ const manager = ExtensionManager.getInstance();
1032
+
1033
+ // Check registered content
1034
+ manager.getInfo('equipment'); // { hasCustomData, customCount, totalCount, mode, ... }
1035
+
1036
+ // Verify weights
1037
+ manager.getWeights('equipment'); // Current weights
1038
+
1039
+ // Check current mode
1040
+ manager.getMode('equipment'); // 'relative' | 'absolute' | 'default' | undefined
1041
+
1042
+ // Validate without registering
1043
+ manager.validate('equipment', items); // { valid: boolean, errors: string[] }
1044
+ ```
1045
+
1046
+ ---
1047
+
1048
+ ## Reference
1049
+
1050
+ ### Type Definitions
1051
+
1052
+ **Location:** [src/core/extensions/ExtensionManager.ts](../src/core/extensions/ExtensionManager.ts)
1053
+
1054
+ **Import types from the package:**
1055
+
1056
+ ```typescript
1057
+ import type {
1058
+ ClassFeature,
1059
+ RacialTrait,
1060
+ CustomSkill,
1061
+ ExtensionOptions,
1062
+ ExtensionCategory,
1063
+ ImageOverride,
1064
+ SpellSchool
1065
+ } from 'playlist-data-engine';
1066
+ ```
1067
+
1068
+ | Type | Source | Description |
1069
+ |------|--------|-------------|
1070
+ | `ExtensionOptions` | [src/core/extensions/ExtensionManager.ts](../src/core/extensions/ExtensionManager.ts) | Registration options: mode, weights, validate |
1071
+ | `ClassFeature` | [src/core/features/FeatureTypes.ts](../src/core/features/FeatureTypes.ts) | Class features with prerequisites and effects |
1072
+ | `RacialTrait` | [src/core/features/FeatureTypes.ts](../src/core/features/FeatureTypes.ts) | Racial traits with prerequisites and effects |
1073
+ | `CustomSkill` | [src/core/skills/SkillTypes.ts](../src/core/skills/SkillTypes.ts) | Skills with prerequisites, categories, armor penalty |
1074
+ | `SkillListDefinition` | [src/core/skills/SkillTypes.ts](../src/core/skills/SkillTypes.ts) | Skill lists for class character generation |
1075
+ | `SpellSchool` | [src/core/spells/SpellTypes.ts](../src/core/spells/SpellTypes.ts) | D&D 5e schools of magic: Abjuration, Conjuration, Divination, Enchantment, Evocation, Illusion, Necromancy, Transmutation |
1076
+ | `ExtensionCategory` | [src/core/extensions/ExtensionManager.ts](../src/core/extensions/ExtensionManager.ts) | All extensible category names |
1077
+ | `ImageOverride` | [src/core/extensions/ExtensionManager.ts](../src/core/extensions/ExtensionManager.ts) | Image patch: identifier, icon, image, appliedAt |
1078
+
1079
+ ### Character Generator Extensions
1080
+
1081
+ The `CharacterGenerator.generate()` `extensions` option supports a limited set of categories.
1082
+
1083
+ | Property | Type | Description |
1084
+ |----------|------|-------------|
1085
+ | `spells` | `SpellExtension[]` | Custom spells to add |
1086
+ | `equipment` | `EquipmentExtension[]` | Custom equipment to add |
1087
+ | `races` | `string[]` | Custom race names (uses Race enum) |
1088
+ | `classes` | `string[]` | Custom class names (uses Class enum) |
1089
+ | `appearance` | `AppearanceExtension` | Custom appearance options |
1090
+
1091
+ **Important:** For `classFeatures`, `racialTraits`, `skills`, and `skillLists`, use `ExtensionManager.register()` directly instead of the `extensions` option.
1092
+
1093
+ For the complete list of supported categories, see [Supported Categories](#supported-categories) in the Overview.
1094
+
1095
+ ---
1096
+
1097
+ ## Support
1098
+
1099
+ For more information, see:
1100
+ - [DATA_ENGINE_REFERENCE.md](../DATA_ENGINE_REFERENCE.md) - Complete API reference
1101
+ - [USAGE_IN_OTHER_PROJECTS.md](../USAGE_IN_OTHER_PROJECTS.md) - Usage examples
1102
+ - [CONTENT_PACKS.md](CONTENT_PACKS.md) - Content pack creation and examples
1103
+ - [EQUIPMENT_SYSTEM.md](EQUIPMENT_SYSTEM.md) - Equipment properties and enchanting
1104
+ - [XP_AND_STATS.md](XP_AND_STATS.md) - Progression and stat strategies
1105
+ - [PREREQUISITES.md](PREREQUISITES.md) - Feature and skill requirements
1106
+ - [tests/integration/customGeneration.integration.test.ts](../tests/integration/customGeneration.integration.test.ts) - Integration test examples