playlist-data-engine 1.7.2 → 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.
- package/README.md +20 -3
- package/bin/cli.cjs +85 -0
- package/dist/core/parser/TrackExtras.d.ts +150 -1
- package/dist/core/parser/TrackExtras.d.ts.map +1 -1
- package/dist/gateway-CDMPqFEH.js +1320 -0
- package/dist/gateway-DKa45Uz6.cjs +6 -0
- package/dist/gateway.d.ts +3 -1
- package/dist/gateway.d.ts.map +1 -1
- package/dist/gateway.js +1 -1
- package/dist/gateway.mjs +22 -14
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/playlist-data-engine.js +34 -34
- package/dist/playlist-data-engine.mjs +964 -1240
- package/dist/utils/engineDocs.d.ts +33 -0
- package/dist/utils/engineDocs.d.ts.map +1 -0
- package/dist/utils/playlistUtils.d.ts +37 -0
- package/dist/utils/playlistUtils.d.ts.map +1 -1
- package/dist/utils/validators.d.ts +27 -0
- package/dist/utils/validators.d.ts.map +1 -1
- package/docs/DATA_ENGINE_REFERENCE.md +6660 -0
- package/docs/USAGE_IN_OTHER_PROJECTS.md +587 -0
- package/docs/features/AUDIO_ANALYSIS.md +610 -0
- package/docs/features/BEAT_DETECTION.md +5250 -0
- package/docs/features/COMBAT_SYSTEM.md +1632 -0
- package/docs/features/CONTENT_PACKS.md +464 -0
- package/docs/features/CUSTOM_CONTENT.md +603 -0
- package/docs/features/ENEMY_GENERATION.md +1711 -0
- package/docs/features/EQUIPMENT_SYSTEM.md +2279 -0
- package/docs/features/EXTENSIBILITY_GUIDE.md +1106 -0
- package/docs/features/GATEWAY_RESOLUTION.md +725 -0
- package/docs/features/IRL_SENSORS.md +360 -0
- package/docs/features/PLAYLIST_PARSING.md +446 -0
- package/docs/features/PREREQUISITES.md +571 -0
- package/docs/features/ROLLS_AND_SEEDS.md +687 -0
- package/docs/features/XP_AND_STATS.md +1221 -0
- package/llms.txt +33 -0
- package/package.json +9 -2
- package/skills/playlist-data-engine/SKILL.md +69 -0
- package/dist/gateway-DUk4nCao.cjs +0 -1
- package/dist/gateway-DyR4M-uH.js +0 -681
|
@@ -0,0 +1,603 @@
|
|
|
1
|
+
# Custom Content Reference
|
|
2
|
+
|
|
3
|
+
Complete guide to custom races, custom classes, and spawn rate control in the Playlist Data Engine.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [Custom Races](#custom-races)
|
|
10
|
+
- [Race Type](#race-type)
|
|
11
|
+
- [RaceDataEntry](#racedataentry)
|
|
12
|
+
- [Registering Custom Races](#registering-custom-races)
|
|
13
|
+
- [Race Validation](#race-validation)
|
|
14
|
+
- [getRaceData() Helper](#getracedata-helper)
|
|
15
|
+
- [Controlling Race Spawn Rates](#controlling-race-spawn-rates)
|
|
16
|
+
2. [Subrace Support](#subrace-support)
|
|
17
|
+
- [Subrace Property](#subrace-property)
|
|
18
|
+
- [RacialTrait with Subrace](#racialtrait-with-subrace)
|
|
19
|
+
- [Subrace Validation](#subrace-validation)
|
|
20
|
+
- [Subrace Filtering](#subrace-filtering)
|
|
21
|
+
- [Complete Subrace Registration Example](#complete-subrace-registration-example)
|
|
22
|
+
- [Feature with Subrace Prerequisite](#feature-with-subrace-prerequisite)
|
|
23
|
+
3. [Custom Classes](#custom-classes)
|
|
24
|
+
- [Overview](#overview)
|
|
25
|
+
- [Class Type Extensibility](#class-type-extensibility)
|
|
26
|
+
- [ClassDataEntry](#classdataentry)
|
|
27
|
+
- [getClassData() Helper](#getclassdata-helper)
|
|
28
|
+
- [Class-Specific Data Helpers](#class-specific-data-helpers)
|
|
29
|
+
- [Registering Custom Classes](#registering-custom-classes)
|
|
30
|
+
- [Controlling Class Spawn Rates](#controlling-class-spawn-rates)
|
|
31
|
+
- [Overriding Audio Preferences for Default Classes](#overriding-audio-preferences-for-default-classes)
|
|
32
|
+
- [Example: Complete Custom Class (from scratch)](#example-complete-custom-class-from-scratch)
|
|
33
|
+
- [Common Patterns](#common-patterns)
|
|
34
|
+
- [Custom Class Validation](#custom-class-validation)
|
|
35
|
+
- [ClassSuggester Custom Class Support](#classsuggester-custom-class-support)
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Custom Races
|
|
40
|
+
|
|
41
|
+
The engine supports custom races through the ExtensionManager.
|
|
42
|
+
|
|
43
|
+
### Race Type
|
|
44
|
+
|
|
45
|
+
**Location:** [src/core/types/Character.ts](../src/core/types/Character.ts)
|
|
46
|
+
|
|
47
|
+
*Also known as: Playable race, character race*
|
|
48
|
+
|
|
49
|
+
For the complete list of default D&D 5e races and type definitions, see [DATA_ENGINE_REFERENCE.md](../DATA_ENGINE_REFERENCE.md#data-types).
|
|
50
|
+
|
|
51
|
+
**Helper Functions:**
|
|
52
|
+
|
|
53
|
+
| Function | Description |
|
|
54
|
+
|----------|-------------|
|
|
55
|
+
| `asRace(value: string)` | Convert a string to the Race type |
|
|
56
|
+
| `isValidRace(value: string)` | Type guard for valid race names |
|
|
57
|
+
|
|
58
|
+
**Usage:**
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { asRace, isValidRace } from 'playlist-data-engine';
|
|
62
|
+
|
|
63
|
+
const raceName = 'Dragonkin';
|
|
64
|
+
if (isValidRace(raceName)) {
|
|
65
|
+
const race: Race = asRace(raceName);
|
|
66
|
+
// Safe to use
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### RaceDataEntry
|
|
71
|
+
|
|
72
|
+
**Location:** [src/utils/constants.ts](../src/utils/constants.ts)
|
|
73
|
+
|
|
74
|
+
For complete type definitions, see [DATA_ENGINE_REFERENCE.md](../DATA_ENGINE_REFERENCE.md#data-types).
|
|
75
|
+
|
|
76
|
+
| Property | Type | Description |
|
|
77
|
+
|----------|------|-------------|
|
|
78
|
+
| `race` | string | Race identifier |
|
|
79
|
+
| `ability_bonuses` | `Partial<Record<Ability, number>>` | Ability score bonuses |
|
|
80
|
+
| `speed` | number | Base speed in feet |
|
|
81
|
+
| `traits` | string[] | Array of trait IDs for this race |
|
|
82
|
+
| `subraces?` | string[] | Optional available subraces |
|
|
83
|
+
| `icon?` | string | Optional icon URL for small UI display |
|
|
84
|
+
| `image?` | string | Optional image URL for larger display |
|
|
85
|
+
|
|
86
|
+
### Registering Custom Races
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { ExtensionManager } from 'playlist-data-engine';
|
|
90
|
+
|
|
91
|
+
const manager = ExtensionManager.getInstance();
|
|
92
|
+
|
|
93
|
+
// Step 1: Register custom race data
|
|
94
|
+
manager.register('races.data', [{
|
|
95
|
+
race: 'Dragonkin',
|
|
96
|
+
ability_bonuses: { STR: 2, CON: 1, CHA: 1 },
|
|
97
|
+
speed: 30,
|
|
98
|
+
traits: ['Draconic Ancestry', 'Darkvision'],
|
|
99
|
+
subraces: ['Fire Dragonkin', 'Ice Dragonkin', 'Lightning Dragonkin'],
|
|
100
|
+
icon: '/icons/races/dragonkin.png',
|
|
101
|
+
image: '/images/races/dragonkin-full.png'
|
|
102
|
+
}]);
|
|
103
|
+
|
|
104
|
+
// Step 2: Register the race name (enables validation)
|
|
105
|
+
manager.register('races', ['Dragonkin']);
|
|
106
|
+
|
|
107
|
+
// Step 3: Register custom racial traits (optional)
|
|
108
|
+
manager.register('racialTraits', [{
|
|
109
|
+
id: 'dragonkin_draconic_ancestry',
|
|
110
|
+
name: 'Draconic Ancestry',
|
|
111
|
+
race: 'Dragonkin',
|
|
112
|
+
description: 'You have draconic heritage',
|
|
113
|
+
effects: [
|
|
114
|
+
{ type: 'ability_unlock', target: 'damage_resistance', value: 'elemental' }
|
|
115
|
+
],
|
|
116
|
+
source: 'custom'
|
|
117
|
+
}]);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Race Validation
|
|
121
|
+
|
|
122
|
+
The ExtensionManager validates races in this order:
|
|
123
|
+
|
|
124
|
+
1. Check if it's a default race (Human, Elf, etc.)
|
|
125
|
+
2. Check if it's been registered as a custom race name
|
|
126
|
+
3. Check if it has data registered via 'races.data'
|
|
127
|
+
4. If validation is disabled via `{ validate: false }`, allow any race
|
|
128
|
+
|
|
129
|
+
### getRaceData() Helper
|
|
130
|
+
|
|
131
|
+
The `getRaceData()` function retrieves race data from both default and custom races. For complete API reference, see [DATA_ENGINE_REFERENCE.md](../DATA_ENGINE_REFERENCE.md#helper-functions).
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import { getRaceData } from 'playlist-data-engine';
|
|
135
|
+
|
|
136
|
+
const dragonkinData = getRaceData('Dragonkin');
|
|
137
|
+
// Returns: { ability_bonuses: { STR: 2, CON: 1, CHA: 1 }, speed: 30, traits: [...] }
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Controlling Race Spawn Rates
|
|
141
|
+
|
|
142
|
+
Adjust how frequently custom (or default) races appear during character generation:
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
import { ExtensionManager, CharacterGenerator } from 'playlist-data-engine';
|
|
146
|
+
|
|
147
|
+
const manager = ExtensionManager.getInstance();
|
|
148
|
+
|
|
149
|
+
// Register custom races
|
|
150
|
+
manager.register('races', ['Dragonkin', 'Fairy', 'Elemental']);
|
|
151
|
+
|
|
152
|
+
// Set spawn rates (relative weights)
|
|
153
|
+
manager.setWeights('races', {
|
|
154
|
+
'Dragonkin': 0.3, // Rare (30% of default weight)
|
|
155
|
+
'Fairy': 0.5, // Uncommon (50% of default weight)
|
|
156
|
+
'Human': 2.0 // Common (2x default weight)
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Now custom races will be selected during character generation
|
|
160
|
+
const character = CharacterGenerator.generate('my-seed', audioProfile, track);
|
|
161
|
+
// character.race could be 'Dragonkin', 'Fairy', or any default race
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Subrace Support
|
|
167
|
+
|
|
168
|
+
Characters can have subraces, and features/traits can require specific subraces.
|
|
169
|
+
|
|
170
|
+
### Subrace Property
|
|
171
|
+
|
|
172
|
+
**Location:** [src/core/types/Character.ts](../src/core/types/Character.ts)
|
|
173
|
+
|
|
174
|
+
| Property | Type | Description |
|
|
175
|
+
|----------|------|-------------|
|
|
176
|
+
| `subrace?` | string | Optional subrace (e.g., 'High Elf', 'Hill Dwarf', 'Wood Elf') |
|
|
177
|
+
|
|
178
|
+
### RacialTrait with Subrace
|
|
179
|
+
|
|
180
|
+
**Location:** [src/core/features/FeatureTypes.ts](../src/core/features/FeatureTypes.ts)
|
|
181
|
+
|
|
182
|
+
| Property | Type | Description |
|
|
183
|
+
|----------|------|-------------|
|
|
184
|
+
| `id` | string | Unique trait identifier |
|
|
185
|
+
| `name` | string | Trait name |
|
|
186
|
+
| `race` | Race | Parent race |
|
|
187
|
+
| `subrace?` | string | Optional subrace requirement |
|
|
188
|
+
| `prerequisites?` | `FeaturePrerequisite` | Additional requirements |
|
|
189
|
+
| `effects?` | `FeatureEffect[]` | Effects when applied |
|
|
190
|
+
| `source` | `'default' \| 'custom'` | Data source |
|
|
191
|
+
|
|
192
|
+
### Subrace Validation
|
|
193
|
+
|
|
194
|
+
Features with subrace requirements validate that the character has the specified subrace before applying effects.
|
|
195
|
+
|
|
196
|
+
### Subrace Filtering
|
|
197
|
+
|
|
198
|
+
FeatureQuery provides `getRacialTraitsForSubrace()`:
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
const query = FeatureQuery.getInstance();
|
|
202
|
+
|
|
203
|
+
// Get traits specific to High Elf subrace
|
|
204
|
+
const highElfTraits = query.getRacialTraitsForSubrace('Elf', 'High Elf');
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Complete Subrace Registration Example
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
import { ExtensionManager, CharacterGenerator } from 'playlist-data-engine';
|
|
211
|
+
|
|
212
|
+
const manager = ExtensionManager.getInstance();
|
|
213
|
+
|
|
214
|
+
// ===== REGISTER CUSTOM RACE WITH SUBRACES =====
|
|
215
|
+
// Step 1: Register the race name
|
|
216
|
+
manager.register('races', ['Dragonkin'], { validate: true });
|
|
217
|
+
|
|
218
|
+
// Step 2: Register the race data (ability bonuses, speed, traits, subraces)
|
|
219
|
+
manager.register('races.data', [{
|
|
220
|
+
race: 'Dragonkin',
|
|
221
|
+
ability_bonuses: { STR: 2, CON: 1, CHA: 1 },
|
|
222
|
+
speed: 30,
|
|
223
|
+
traits: ['Draconic Ancestry', 'Darkvision'],
|
|
224
|
+
subraces: ['Fire Dragonkin', 'Ice Dragonkin', 'Lightning Dragonkin'],
|
|
225
|
+
icon: '/icons/races/dragonkin.png',
|
|
226
|
+
image: '/images/races/dragonkin-full.png'
|
|
227
|
+
}]);
|
|
228
|
+
|
|
229
|
+
// ===== REGISTER SUBRACE-SPECIFIC TRAITS =====
|
|
230
|
+
// Fire Dragonkin only
|
|
231
|
+
manager.register('racialTraits', [{
|
|
232
|
+
id: 'fire_dragonkin_resistance',
|
|
233
|
+
name: 'Fire Resistance',
|
|
234
|
+
description: 'Resistance to fire damage',
|
|
235
|
+
race: 'Dragonkin',
|
|
236
|
+
subrace: 'Fire Dragonkin', // Only for this subrace
|
|
237
|
+
effects: [
|
|
238
|
+
{ type: 'passive_modifier', target: 'fire_resistance', value: true }
|
|
239
|
+
],
|
|
240
|
+
source: 'custom'
|
|
241
|
+
}]);
|
|
242
|
+
|
|
243
|
+
// Ice Dragonkin only
|
|
244
|
+
manager.register('racialTraits', [{
|
|
245
|
+
id: 'ice_dragonkin_resistance',
|
|
246
|
+
name: 'Cold Resistance',
|
|
247
|
+
description: 'Resistance to cold damage',
|
|
248
|
+
race: 'Dragonkin',
|
|
249
|
+
subrace: 'Ice Dragonkin',
|
|
250
|
+
effects: [
|
|
251
|
+
{ type: 'passive_modifier', target: 'cold_resistance', value: true }
|
|
252
|
+
],
|
|
253
|
+
source: 'custom'
|
|
254
|
+
}]);
|
|
255
|
+
|
|
256
|
+
// ===== GENERATE CHARACTER WITH SUBRACE =====
|
|
257
|
+
const character = CharacterGenerator.generate(seed, audioProfile, track, {
|
|
258
|
+
forceRace: 'Dragonkin',
|
|
259
|
+
subrace: 'Fire Dragonkin'
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Character will have:
|
|
263
|
+
// - Base Dragonkin traits (Draconic Ancestry, Darkvision)
|
|
264
|
+
// - Subrace-specific traits (Fire Resistance)
|
|
265
|
+
// - Correct ability bonuses (STR+2, CON+1, CHA+1)
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Feature with Subrace Prerequisite
|
|
269
|
+
|
|
270
|
+
Traits can require a specific subrace via prerequisites:
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
import { ExtensionManager } from 'playlist-data-engine';
|
|
274
|
+
|
|
275
|
+
const manager = ExtensionManager.getInstance();
|
|
276
|
+
|
|
277
|
+
// Trait that requires a specific subrace
|
|
278
|
+
manager.register('racialTraits', [{
|
|
279
|
+
id: 'inferno_breath',
|
|
280
|
+
name: 'Inferno Breath',
|
|
281
|
+
description: 'Breathe fire like a true red dragon',
|
|
282
|
+
race: 'Dragonkin',
|
|
283
|
+
subrace: 'Fire Dragonkin',
|
|
284
|
+
prerequisites: {
|
|
285
|
+
subrace: 'Fire Dragonkin', // Must be Fire Dragonkin
|
|
286
|
+
level: 5
|
|
287
|
+
},
|
|
288
|
+
effects: [
|
|
289
|
+
{ type: 'active_ability', target: 'fire_breath', value: '6d6' }
|
|
290
|
+
],
|
|
291
|
+
source: 'custom'
|
|
292
|
+
}]);
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## Custom Classes
|
|
298
|
+
|
|
299
|
+
The engine supports template-based custom classes through the ExtensionManager. Custom classes can extend (inherit from) existing D&D 5e base classes or be defined completely from scratch.
|
|
300
|
+
|
|
301
|
+
### Overview
|
|
302
|
+
|
|
303
|
+
The Template Class System enables creating new classes that extend existing D&D 5e base classes without duplicating all properties. For example, a "Necromancer" class can extend "Wizard" and only override the properties that differ.
|
|
304
|
+
|
|
305
|
+
**Key Features:**
|
|
306
|
+
- **Template inheritance**: Custom classes can inherit from base classes via `baseClass` property
|
|
307
|
+
- **Complete customization**: Classes can be defined from scratch without `baseClass`
|
|
308
|
+
- **Skill lists**: Custom skill lists (including custom skills)
|
|
309
|
+
- **Spell casting**: Custom spell lists and slot progressions
|
|
310
|
+
- **Equipment**: Custom starting equipment
|
|
311
|
+
- **Features**: Custom class features with prerequisites
|
|
312
|
+
- **Audio preferences**: Optional audio affinity for class suggestion
|
|
313
|
+
|
|
314
|
+
### Class Type Extensibility
|
|
315
|
+
|
|
316
|
+
**Location:** [src/core/types/Character.ts](../src/core/types/Character.ts)
|
|
317
|
+
|
|
318
|
+
*Also known as: Class name type, branded class type*
|
|
319
|
+
|
|
320
|
+
For the complete list of default D&D 5e classes and type definitions, see [DATA_ENGINE_REFERENCE.md](../DATA_ENGINE_REFERENCE.md#data-types).
|
|
321
|
+
|
|
322
|
+
**Helper Functions:**
|
|
323
|
+
|
|
324
|
+
| Function | Description |
|
|
325
|
+
|----------|-------------|
|
|
326
|
+
| `asClass(value: string)` | Convert a string to the Class type |
|
|
327
|
+
| `isValidClass(value: string)` | Type guard for valid class names |
|
|
328
|
+
|
|
329
|
+
### ClassDataEntry
|
|
330
|
+
|
|
331
|
+
**Location:** [src/utils/constants.ts](../src/utils/constants.ts)
|
|
332
|
+
|
|
333
|
+
For complete type definitions, see [DATA_ENGINE_REFERENCE.md](../DATA_ENGINE_REFERENCE.md#data-types).
|
|
334
|
+
|
|
335
|
+
| Property | Type | Required | Description |
|
|
336
|
+
|----------|------|----------|-------------|
|
|
337
|
+
| `name` | string | Yes | Unique class name |
|
|
338
|
+
| `baseClass?` | Class | No | Base class to inherit from (template system) |
|
|
339
|
+
| `primary_ability` | Ability | Yes* | Primary ability score |
|
|
340
|
+
| `hit_die` | number | Yes* | Hit die size (6, 8, 10, 12) |
|
|
341
|
+
| `saving_throws` | Ability[] | Yes* | Two saving throw abilities |
|
|
342
|
+
| `is_spellcaster` | boolean | Yes* | Can this class cast spells |
|
|
343
|
+
| `skill_count` | number | Yes* | Number of skill proficiencies |
|
|
344
|
+
| `available_skills` | string[] | Yes* | Array of skill IDs |
|
|
345
|
+
| `has_expertise` | boolean | Yes* | Has expertise feature |
|
|
346
|
+
| `expertise_count?` | number | No | Number of expertise choices |
|
|
347
|
+
| `audio_preferences?` | object | No | Audio affinity for class suggestion |
|
|
348
|
+
| `icon?` | string | No | Optional icon URL for small UI display |
|
|
349
|
+
| `image?` | string | No | Optional image URL for larger display |
|
|
350
|
+
|
|
351
|
+
*Required only if `baseClass` is not specified.
|
|
352
|
+
|
|
353
|
+
**Audio Preferences:**
|
|
354
|
+
|
|
355
|
+
| Property | Type | Description |
|
|
356
|
+
|----------|------|-------------|
|
|
357
|
+
| `primary` | `'bass' \| 'treble' \| 'mid' \| 'amplitude' \| 'chaos'` | Primary audio trait |
|
|
358
|
+
| `secondary?` | audio trait | Secondary preference |
|
|
359
|
+
| `tertiary?` | audio trait | Tertiary preference |
|
|
360
|
+
| `bass?`, `treble?`, `mid?`, `amplitude?` | number | Optional weight values |
|
|
361
|
+
|
|
362
|
+
See also: [DATA_ENGINE_REFERENCE.md - Audio Preferences](../DATA_ENGINE_REFERENCE.md#data-types)
|
|
363
|
+
|
|
364
|
+
### getClassData() Helper
|
|
365
|
+
|
|
366
|
+
**Location:** [src/utils/constants.ts](../src/utils/constants.ts)
|
|
367
|
+
|
|
368
|
+
Retrieves class data from default CLASS_DATA or ExtensionManager. For template-based classes, merges base class data with custom data (custom properties override).
|
|
369
|
+
|
|
370
|
+
For complete API reference, see [DATA_ENGINE_REFERENCE.md - Helper Functions](../DATA_ENGINE_REFERENCE.md#helper-functions).
|
|
371
|
+
|
|
372
|
+
**Property Override Behavior:**
|
|
373
|
+
|
|
374
|
+
| Property | Behavior | Example |
|
|
375
|
+
|----------|----------|---------|
|
|
376
|
+
| `primary_ability` | Inherited unless specified | `baseClass: 'Wizard'` → inherits `INT` |
|
|
377
|
+
| `hit_die` | Inherited unless specified | `baseClass: 'Wizard'` → inherits `8` |
|
|
378
|
+
| `saving_throws` | Inherited unless specified | `baseClass: 'Wizard'` → inherits `['INT', 'WIS']` |
|
|
379
|
+
| `is_spellcaster` | Inherited unless specified | `baseClass: 'Wizard'` → inherits `true` |
|
|
380
|
+
| `skill_count` | Inherited unless specified | `baseClass: 'Wizard'` → inherits `2` |
|
|
381
|
+
| `available_skills` | **Replaced** (not merged) | Custom list replaces base entirely |
|
|
382
|
+
| `has_expertise` | Inherited unless specified | `baseClass: 'Wizard'` → inherits `false` |
|
|
383
|
+
| `audio_preferences` | Inherited unless specified | Can override for custom audio affinity |
|
|
384
|
+
|
|
385
|
+
### Class-Specific Data Helpers
|
|
386
|
+
|
|
387
|
+
**Location:** [src/utils/constants.ts](../src/utils/constants.ts)
|
|
388
|
+
|
|
389
|
+
For complete API reference, see [DATA_ENGINE_REFERENCE.md - Helper Functions](../DATA_ENGINE_REFERENCE.md#helper-functions).
|
|
390
|
+
|
|
391
|
+
| Function | Description |
|
|
392
|
+
|----------|-------------|
|
|
393
|
+
| `getClassSpellList(className: string)` | Get spell list for a class |
|
|
394
|
+
| `getSpellSlotsForClass(className: string, level: number)` | Get spell slots at a level |
|
|
395
|
+
| `getClassStartingEquipment(className: string)` | Get starting equipment |
|
|
396
|
+
|
|
397
|
+
### Registering Custom Classes
|
|
398
|
+
|
|
399
|
+
**Via ExtensionManager:**
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
import { ExtensionManager } from 'playlist-data-engine';
|
|
403
|
+
import { asClass } from 'playlist-data-engine';
|
|
404
|
+
|
|
405
|
+
const manager = ExtensionManager.getInstance();
|
|
406
|
+
|
|
407
|
+
// Step 1: Register custom class data
|
|
408
|
+
manager.register('classes.data', [{
|
|
409
|
+
name: 'Necromancer',
|
|
410
|
+
baseClass: 'Wizard', // Inherits from Wizard
|
|
411
|
+
// Only override what's different:
|
|
412
|
+
available_skills: ['arcana', 'medicine', 'religion', 'necromancy'],
|
|
413
|
+
icon: '/icons/classes/necromancer.png',
|
|
414
|
+
image: '/images/classes/necromancer-full.png'
|
|
415
|
+
// All other properties (hit_die, saving_throws, etc.) are inherited from Wizard
|
|
416
|
+
}]);
|
|
417
|
+
|
|
418
|
+
// Step 2: Register the class name for validation
|
|
419
|
+
manager.register('classes', [asClass('Necromancer')]);
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### Controlling Class Spawn Rates
|
|
423
|
+
|
|
424
|
+
Adjust how frequently custom (or default) classes appear during character generation:
|
|
425
|
+
|
|
426
|
+
```typescript
|
|
427
|
+
import { ExtensionManager, CharacterGenerator } from 'playlist-data-engine';
|
|
428
|
+
|
|
429
|
+
const manager = ExtensionManager.getInstance();
|
|
430
|
+
|
|
431
|
+
// Make certain classes more or less common
|
|
432
|
+
manager.setWeights('classes', {
|
|
433
|
+
'Necromancer': 0.5, // Rare (half default weight)
|
|
434
|
+
'Sorcerer': 2.0, // Common (2x default weight)
|
|
435
|
+
'Warlock': 1.5, // Uncommon (1.5x default weight)
|
|
436
|
+
'Paladin': 0.3 // Very rare
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
// Classes will be selected according to their weights during generation
|
|
440
|
+
const character = CharacterGenerator.generate('my-seed', audioProfile, track);
|
|
441
|
+
// character.class will favor Sorcerer and Warlock over Necromancer and Paladin
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### Overriding Audio Preferences for Default Classes:
|
|
445
|
+
|
|
446
|
+
```typescript
|
|
447
|
+
import { ExtensionManager } from 'playlist-data-engine';
|
|
448
|
+
|
|
449
|
+
const manager = ExtensionManager.getInstance();
|
|
450
|
+
|
|
451
|
+
// Override default class audio preferences
|
|
452
|
+
manager.register('classes.data', [{
|
|
453
|
+
name: 'Barbarian', // Override default Barbarian
|
|
454
|
+
audio_preferences: {
|
|
455
|
+
primary: 'treble', // Make Barbarians prefer treble instead of bass
|
|
456
|
+
treble: 1.0
|
|
457
|
+
}
|
|
458
|
+
}]);
|
|
459
|
+
|
|
460
|
+
// Now Barbarians will be suggested for treble-heavy audio instead of bass-heavy
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### Example: Complete Custom Class (from scratch):
|
|
464
|
+
|
|
465
|
+
```typescript
|
|
466
|
+
import { ExtensionManager, asClass } from 'playlist-data-engine';
|
|
467
|
+
|
|
468
|
+
const manager = ExtensionManager.getInstance();
|
|
469
|
+
|
|
470
|
+
// Step 1: Register complete custom class data
|
|
471
|
+
manager.register('classes.data', [{
|
|
472
|
+
name: 'Runecaster',
|
|
473
|
+
// No baseClass - must specify everything
|
|
474
|
+
primary_ability: 'WIS',
|
|
475
|
+
hit_die: 8,
|
|
476
|
+
saving_throws: ['WIS', 'CON'],
|
|
477
|
+
is_spellcaster: true,
|
|
478
|
+
skill_count: 3,
|
|
479
|
+
available_skills: ['arcana', 'nature', 'religion', 'insight', 'medicine'],
|
|
480
|
+
has_expertise: false,
|
|
481
|
+
icon: '/icons/classes/runecaster.png',
|
|
482
|
+
image: '/images/classes/runecaster-full.png'
|
|
483
|
+
}]);
|
|
484
|
+
|
|
485
|
+
// Step 2: Register the class name
|
|
486
|
+
manager.register('classes', [asClass('Runecaster')]);
|
|
487
|
+
|
|
488
|
+
// Step 3: (Optional) Set custom spell list
|
|
489
|
+
manager.register('classSpellLists.Runecaster', [{
|
|
490
|
+
cantrips: ['druidcraft', 'guidance', 'resistance'],
|
|
491
|
+
spells_by_level: {
|
|
492
|
+
1: ['detect magic', 'magic stone', 'faerie fire']
|
|
493
|
+
}
|
|
494
|
+
}]);
|
|
495
|
+
|
|
496
|
+
// Step 4: (Optional) Set custom spell slot progression
|
|
497
|
+
manager.register('classSpellSlots', [{
|
|
498
|
+
class: 'Runecaster',
|
|
499
|
+
slots: {
|
|
500
|
+
1: { 1: 2 },
|
|
501
|
+
2: { 1: 3 },
|
|
502
|
+
3: { 1: 4, 2: 2 }
|
|
503
|
+
// ... define for all levels 1-20
|
|
504
|
+
}
|
|
505
|
+
}]);
|
|
506
|
+
|
|
507
|
+
// Step 5: (Optional) Set custom starting equipment
|
|
508
|
+
manager.register('classStartingEquipment.Runecaster', [{
|
|
509
|
+
weapons: ['Quarterstaff', 'Dagger'],
|
|
510
|
+
armor: [],
|
|
511
|
+
items: ['Component pouch', 'Spellbook']
|
|
512
|
+
}]);
|
|
513
|
+
|
|
514
|
+
// Now generate a Runecaster character!
|
|
515
|
+
const character = CharacterGenerator.generate(
|
|
516
|
+
'my-seed',
|
|
517
|
+
audioProfile,
|
|
518
|
+
track
|
|
519
|
+
);
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
**Note:** For complete ClassDataEntry property definitions and the full list of default D&D 5e classes, see [DATA_ENGINE_REFERENCE.md](../DATA_ENGINE_REFERENCE.md#data-types).
|
|
523
|
+
|
|
524
|
+
### Common Patterns
|
|
525
|
+
|
|
526
|
+
Here are common patterns for creating custom classes using the template system:
|
|
527
|
+
|
|
528
|
+
**Archetype Variant** - Same class, different flavor:
|
|
529
|
+
|
|
530
|
+
```typescript
|
|
531
|
+
{
|
|
532
|
+
name: 'BattleMage',
|
|
533
|
+
baseClass: 'Wizard',
|
|
534
|
+
hit_die: 10, // More durable than standard Wizard
|
|
535
|
+
saving_throws: ['INT', 'CON'], // CON instead of WIS
|
|
536
|
+
available_skills: ['arcana', 'athletics', 'intimidation'],
|
|
537
|
+
icon: '/icons/classes/battlemage.png'
|
|
538
|
+
}
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
**Multiclass-Inspired** - Two classes combined:
|
|
542
|
+
|
|
543
|
+
```typescript
|
|
544
|
+
{
|
|
545
|
+
name: 'Spellsword',
|
|
546
|
+
baseClass: 'Fighter',
|
|
547
|
+
is_spellcaster: true, // Add spellcasting
|
|
548
|
+
primary_ability: 'STR', // Keep Fighter primary
|
|
549
|
+
available_skills: ['athletics', 'acrobatics', 'arcana', 'intimidation'],
|
|
550
|
+
icon: '/icons/classes/spellsword.png'
|
|
551
|
+
}
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
**Specialist** - Narrow focus:
|
|
555
|
+
|
|
556
|
+
```typescript
|
|
557
|
+
{
|
|
558
|
+
name: 'Beastmaster',
|
|
559
|
+
baseClass: 'Ranger',
|
|
560
|
+
skill_count: 3, // Extra skill for animal handling
|
|
561
|
+
available_skills: ['animal_handling', 'nature', 'survival', 'perception'],
|
|
562
|
+
icon: '/icons/classes/beastmaster.png'
|
|
563
|
+
}
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
### Custom Class Validation
|
|
567
|
+
|
|
568
|
+
**Location:** [src/core/extensions/ExtensionManager.ts](../src/core/extensions/ExtensionManager.ts)
|
|
569
|
+
|
|
570
|
+
The ExtensionManager validates custom classes:
|
|
571
|
+
|
|
572
|
+
1. **Class Names**: Must be either a default class or registered via `classes.data`
|
|
573
|
+
2. **Class Data**: Must have `name` (string), `primary_ability` (Ability), `hit_die` (number), `saving_throws` (Ability[]), `is_spellcaster` (boolean), `skill_count` (number), `available_skills` (string[]), `has_expertise` (boolean)
|
|
574
|
+
|
|
575
|
+
```typescript
|
|
576
|
+
// Validation errors for invalid class data
|
|
577
|
+
manager.register('classes', ['InvalidClass']);
|
|
578
|
+
// Throws: "Invalid items for category 'classes':
|
|
579
|
+
// Invalid class (must be one of: Barbarian, Bard, Cleric, Druid, Fighter,
|
|
580
|
+
// Monk, Paladin, Ranger, Rogue, Sorcerer, Warlock, Wizard or a custom
|
|
581
|
+
// class registered via 'classes.data')"
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
### ClassSuggester Custom Class Support
|
|
585
|
+
|
|
586
|
+
**Location:** [src/core/generation/ClassSuggester.ts](../src/core/generation/ClassSuggester.ts)
|
|
587
|
+
|
|
588
|
+
The `ClassSuggester` automatically includes custom classes registered via ExtensionManager when suggesting a class based on audio profile. Custom classes with `audio_preferences` are matched against the audio profile.
|
|
589
|
+
|
|
590
|
+
| Method | Returns | Description |
|
|
591
|
+
|--------|---------|-------------|
|
|
592
|
+
| `suggest(audioProfile: AudioProfile, rng: SeededRNG)` | `string` | Suggest a class based on audio profile |
|
|
593
|
+
|
|
594
|
+
---
|
|
595
|
+
|
|
596
|
+
## See Also
|
|
597
|
+
|
|
598
|
+
- [PREREQUISITES.md](PREREQUISITES.md) - Prerequisites system guide
|
|
599
|
+
- [DATA_ENGINE_REFERENCE.md](../DATA_ENGINE_REFERENCE.md) - Complete API reference
|
|
600
|
+
- [USAGE_IN_OTHER_PROJECTS.md](../USAGE_IN_OTHER_PROJECTS.md) - Usage examples
|
|
601
|
+
- [EXTENSIBILITY_GUIDE.md](EXTENSIBILITY_GUIDE.md) - Custom content registration and spawn rates
|
|
602
|
+
- [EQUIPMENT_SYSTEM.md](EQUIPMENT_SYSTEM.md) - Custom equipment
|
|
603
|
+
- [XP_AND_STATS.md](XP_AND_STATS.md) - Progression and stat strategies
|