Skip to main content
Glama
class-starting-data.ts25 kB
/** * D&D 5e SRD Class Starting Data * * Contains starting equipment, hit dice, spell slots, and starting spells * for each core class. Used by create_character to auto-provision new characters. */ // Type for valid D&D 5e class names export type D5EClass = 'barbarian' | 'bard' | 'cleric' | 'druid' | 'fighter' | 'monk' | 'paladin' | 'ranger' | 'rogue' | 'sorcerer' | 'warlock' | 'wizard'; export interface ClassStartingData { hitDice: string; startingHP: (conMod: number) => number; savingThrows: string[]; armorProficiencies: string[]; weaponProficiencies: string[]; startingEquipment: StartingEquipmentChoice[]; spellcasting?: SpellcastingInfo; // Added for provisioning service startingGold?: number; spellcastingAbility?: 'int' | 'wis' | 'cha'; startingCantrips?: string[]; startingSpells?: string[]; } export interface StartingEquipmentChoice { // Either a fixed item or a choice between options fixed?: string[]; choose?: { count: number; from: string[][]; // Array of item sets to choose from }; } export interface SpellcastingInfo { ability: 'int' | 'wis' | 'cha'; cantripsKnown: number[]; // By level (index 0 = level 1) spellsKnown?: number[]; // By level (for known-casters like Sorcerer) spellsPrepared?: boolean; // Prepared casters calculate from level + ability mod slotsByLevel: Record<number, number[]>; // Character level -> [1st, 2nd, 3rd, 4th, 5th, 6th, 7th, 8th, 9th] startingCantrips: string[]; startingSpells: string[]; } // Default starting items for any class (explorer's pack contents simplified) const EXPLORERS_PACK = [ 'Backpack', 'Bedroll', 'Mess Kit', 'Tinderbox', 'Torches x10', 'Rations x10', 'Waterskin', 'Hempen Rope (50 feet)' ]; const DUNGEONEERS_PACK = [ 'Backpack', 'Crowbar', 'Hammer', 'Pitons x10', 'Torches x10', 'Tinderbox', 'Rations x10', 'Waterskin', 'Hempen Rope (50 feet)' ]; const PRIESTS_PACK = [ 'Backpack', 'Blanket', 'Candles x10', 'Tinderbox', 'Alms Box', 'Incense x2', 'Censer', 'Vestments', 'Rations x2', 'Waterskin' ]; const SCHOLARS_PACK = [ 'Backpack', 'Book of Lore', 'Ink', 'Ink Pen', 'Parchment x10', 'Little Bag of Sand', 'Small Knife' ]; const BURGLAR_PACK = [ 'Backpack', 'Ball Bearings x1000', 'String (10 feet)', 'Bell', 'Candles x5', 'Crowbar', 'Hammer', 'Pitons x10', 'Hooded Lantern', 'Oil x2', 'Rations x5', 'Tinderbox', 'Waterskin', 'Hempen Rope (50 feet)' ]; // Export pack contents for external use export const EquipmentPacks = { explorersPack: EXPLORERS_PACK, dungeoneersPack: DUNGEONEERS_PACK, priestsPack: PRIESTS_PACK, scholarsPack: SCHOLARS_PACK, burglarsPack: BURGLAR_PACK, diplomatsPack: ['Backpack', 'Fine Clothes', 'Perfume', 'Sealing Wax', 'Paper x5'], entertainersPack: ['Backpack', 'Bedroll', 'Costume x2', 'Candles x5', 'Rations x5', 'Waterskin', 'Disguise Kit'] }; // Spell slot progression for full casters (Bard, Cleric, Druid, Sorcerer, Wizard) const FULL_CASTER_SLOTS: Record<number, number[]> = { 1: [2, 0, 0, 0, 0, 0, 0, 0, 0], 2: [3, 0, 0, 0, 0, 0, 0, 0, 0], 3: [4, 2, 0, 0, 0, 0, 0, 0, 0], 4: [4, 3, 0, 0, 0, 0, 0, 0, 0], 5: [4, 3, 2, 0, 0, 0, 0, 0, 0], 6: [4, 3, 3, 0, 0, 0, 0, 0, 0], 7: [4, 3, 3, 1, 0, 0, 0, 0, 0], 8: [4, 3, 3, 2, 0, 0, 0, 0, 0], 9: [4, 3, 3, 3, 1, 0, 0, 0, 0], 10: [4, 3, 3, 3, 2, 0, 0, 0, 0], 11: [4, 3, 3, 3, 2, 1, 0, 0, 0], 12: [4, 3, 3, 3, 2, 1, 0, 0, 0], 13: [4, 3, 3, 3, 2, 1, 1, 0, 0], 14: [4, 3, 3, 3, 2, 1, 1, 0, 0], 15: [4, 3, 3, 3, 2, 1, 1, 1, 0], 16: [4, 3, 3, 3, 2, 1, 1, 1, 0], 17: [4, 3, 3, 3, 2, 1, 1, 1, 1], 18: [4, 3, 3, 3, 3, 1, 1, 1, 1], 19: [4, 3, 3, 3, 3, 2, 1, 1, 1], 20: [4, 3, 3, 3, 3, 2, 2, 1, 1] }; // Half-caster spell slots (Paladin, Ranger) const HALF_CASTER_SLOTS: Record<number, number[]> = { 1: [0, 0, 0, 0, 0, 0, 0, 0, 0], 2: [2, 0, 0, 0, 0, 0, 0, 0, 0], 3: [3, 0, 0, 0, 0, 0, 0, 0, 0], 4: [3, 0, 0, 0, 0, 0, 0, 0, 0], 5: [4, 2, 0, 0, 0, 0, 0, 0, 0], 6: [4, 2, 0, 0, 0, 0, 0, 0, 0], 7: [4, 3, 0, 0, 0, 0, 0, 0, 0], 8: [4, 3, 0, 0, 0, 0, 0, 0, 0], 9: [4, 3, 2, 0, 0, 0, 0, 0, 0], 10: [4, 3, 2, 0, 0, 0, 0, 0, 0], 11: [4, 3, 3, 0, 0, 0, 0, 0, 0], 12: [4, 3, 3, 0, 0, 0, 0, 0, 0], 13: [4, 3, 3, 1, 0, 0, 0, 0, 0], 14: [4, 3, 3, 1, 0, 0, 0, 0, 0], 15: [4, 3, 3, 2, 0, 0, 0, 0, 0], 16: [4, 3, 3, 2, 0, 0, 0, 0, 0], 17: [4, 3, 3, 3, 1, 0, 0, 0, 0], 18: [4, 3, 3, 3, 1, 0, 0, 0, 0], 19: [4, 3, 3, 3, 2, 0, 0, 0, 0], 20: [4, 3, 3, 3, 2, 0, 0, 0, 0] }; export const CLASS_DATA: Record<string, ClassStartingData> = { // ═══════════════════════════════════════════════════════════════════════════ // BARBARIAN // ═══════════════════════════════════════════════════════════════════════════ barbarian: { hitDice: 'd12', startingHP: (conMod) => 12 + conMod, savingThrows: ['strength', 'constitution'], armorProficiencies: ['light', 'medium', 'shields'], weaponProficiencies: ['simple', 'martial'], startingEquipment: [ { choose: { count: 1, from: [['Greataxe'], ['Martial Melee Weapon']] } }, { choose: { count: 1, from: [['Handaxe', 'Handaxe'], ['Simple Weapon']] } }, { fixed: [...EXPLORERS_PACK, 'Javelin', 'Javelin', 'Javelin', 'Javelin'] } ] }, // ═══════════════════════════════════════════════════════════════════════════ // BARD // ═══════════════════════════════════════════════════════════════════════════ bard: { hitDice: 'd8', startingHP: (conMod) => 8 + conMod, savingThrows: ['dexterity', 'charisma'], armorProficiencies: ['light'], weaponProficiencies: ['simple', 'hand crossbows', 'longswords', 'rapiers', 'shortswords'], startingEquipment: [ { choose: { count: 1, from: [['Rapier'], ['Longsword'], ['Simple Weapon']] } }, { choose: { count: 1, from: [[...DUNGEONEERS_PACK], [...EXPLORERS_PACK]] } }, { fixed: ['Leather Armor', 'Dagger', 'Lute'] } ], spellcasting: { ability: 'cha', cantripsKnown: [2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4], spellsKnown: [4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 15, 16, 18, 19, 19, 20, 22, 22, 22], slotsByLevel: FULL_CASTER_SLOTS, startingCantrips: ['Vicious Mockery', 'Light'], startingSpells: ['Healing Word', 'Dissonant Whispers', 'Faerie Fire', 'Thunderwave'] } }, // ═══════════════════════════════════════════════════════════════════════════ // CLERIC // ═══════════════════════════════════════════════════════════════════════════ cleric: { hitDice: 'd8', startingHP: (conMod) => 8 + conMod, savingThrows: ['wisdom', 'charisma'], armorProficiencies: ['light', 'medium', 'shields'], weaponProficiencies: ['simple'], startingEquipment: [ { choose: { count: 1, from: [['Mace'], ['Warhammer']] } }, { choose: { count: 1, from: [['Scale Mail'], ['Leather Armor'], ['Chain Mail']] } }, { choose: { count: 1, from: [['Light Crossbow', 'Crossbow Bolts x20'], ['Simple Weapon']] } }, { choose: { count: 1, from: [[...PRIESTS_PACK], [...EXPLORERS_PACK]] } }, { fixed: ['Shield', 'Holy Symbol'] } ], spellcasting: { ability: 'wis', cantripsKnown: [3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5], spellsPrepared: true, // WIS mod + cleric level slotsByLevel: FULL_CASTER_SLOTS, startingCantrips: ['Sacred Flame', 'Guidance', 'Spare the Dying'], startingSpells: ['Cure Wounds', 'Bless', 'Shield of Faith', 'Guiding Bolt'] } }, // ═══════════════════════════════════════════════════════════════════════════ // DRUID // ═══════════════════════════════════════════════════════════════════════════ druid: { hitDice: 'd8', startingHP: (conMod) => 8 + conMod, savingThrows: ['intelligence', 'wisdom'], armorProficiencies: ['light', 'medium', 'shields'], weaponProficiencies: ['clubs', 'daggers', 'darts', 'javelins', 'maces', 'quarterstaffs', 'scimitars', 'sickles', 'slings', 'spears'], startingEquipment: [ { choose: { count: 1, from: [['Shield'], ['Simple Weapon']] } }, { choose: { count: 1, from: [['Scimitar'], ['Simple Melee Weapon']] } }, { fixed: ['Leather Armor', ...EXPLORERS_PACK, 'Druidic Focus'] } ], spellcasting: { ability: 'wis', cantripsKnown: [2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4], spellsPrepared: true, slotsByLevel: FULL_CASTER_SLOTS, startingCantrips: ['Produce Flame', 'Druidcraft'], startingSpells: ['Entangle', 'Healing Word', 'Faerie Fire', 'Thunderwave'] } }, // ═══════════════════════════════════════════════════════════════════════════ // FIGHTER // ═══════════════════════════════════════════════════════════════════════════ fighter: { hitDice: 'd10', startingHP: (conMod) => 10 + conMod, savingThrows: ['strength', 'constitution'], armorProficiencies: ['light', 'medium', 'heavy', 'shields'], weaponProficiencies: ['simple', 'martial'], startingEquipment: [ { choose: { count: 1, from: [['Chain Mail'], ['Leather Armor', 'Longbow', 'Arrows x20']] } }, { choose: { count: 1, from: [['Martial Weapon', 'Shield'], ['Martial Weapon', 'Martial Weapon']] } }, { choose: { count: 1, from: [['Light Crossbow', 'Crossbow Bolts x20'], ['Handaxe', 'Handaxe']] } }, { choose: { count: 1, from: [[...DUNGEONEERS_PACK], [...EXPLORERS_PACK]] } } ] }, // ═══════════════════════════════════════════════════════════════════════════ // MONK // ═══════════════════════════════════════════════════════════════════════════ monk: { hitDice: 'd8', startingHP: (conMod) => 8 + conMod, savingThrows: ['strength', 'dexterity'], armorProficiencies: [], weaponProficiencies: ['simple', 'shortswords'], startingEquipment: [ { choose: { count: 1, from: [['Shortsword'], ['Simple Weapon']] } }, { choose: { count: 1, from: [[...DUNGEONEERS_PACK], [...EXPLORERS_PACK]] } }, { fixed: ['Dart', 'Dart', 'Dart', 'Dart', 'Dart', 'Dart', 'Dart', 'Dart', 'Dart', 'Dart'] } ] }, // ═══════════════════════════════════════════════════════════════════════════ // PALADIN // ═══════════════════════════════════════════════════════════════════════════ paladin: { hitDice: 'd10', startingHP: (conMod) => 10 + conMod, savingThrows: ['wisdom', 'charisma'], armorProficiencies: ['light', 'medium', 'heavy', 'shields'], weaponProficiencies: ['simple', 'martial'], startingEquipment: [ { choose: { count: 1, from: [['Martial Weapon', 'Shield'], ['Martial Weapon', 'Martial Weapon']] } }, { choose: { count: 1, from: [['Javelin', 'Javelin', 'Javelin', 'Javelin', 'Javelin'], ['Simple Melee Weapon']] } }, { choose: { count: 1, from: [[...PRIESTS_PACK], [...EXPLORERS_PACK]] } }, { fixed: ['Chain Mail', 'Holy Symbol'] } ], spellcasting: { ability: 'cha', cantripsKnown: [], // Paladins don't get cantrips spellsPrepared: true, slotsByLevel: HALF_CASTER_SLOTS, startingCantrips: [], startingSpells: ['Divine Smite', 'Cure Wounds', 'Shield of Faith', 'Thunderous Smite'] } }, // ═══════════════════════════════════════════════════════════════════════════ // RANGER // ═══════════════════════════════════════════════════════════════════════════ ranger: { hitDice: 'd10', startingHP: (conMod) => 10 + conMod, savingThrows: ['strength', 'dexterity'], armorProficiencies: ['light', 'medium', 'shields'], weaponProficiencies: ['simple', 'martial'], startingEquipment: [ { choose: { count: 1, from: [['Scale Mail'], ['Leather Armor']] } }, { choose: { count: 1, from: [['Shortsword', 'Shortsword'], ['Simple Melee Weapon', 'Simple Melee Weapon']] } }, { choose: { count: 1, from: [[...DUNGEONEERS_PACK], [...EXPLORERS_PACK]] } }, { fixed: ['Longbow', 'Arrows x20', 'Quiver'] } ], spellcasting: { ability: 'wis', cantripsKnown: [], // Rangers don't get cantrips (without Druidic Warrior) spellsKnown: [0, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11], slotsByLevel: HALF_CASTER_SLOTS, startingCantrips: [], startingSpells: ["Hunter's Mark", 'Cure Wounds'] } }, // ═══════════════════════════════════════════════════════════════════════════ // ROGUE // ═══════════════════════════════════════════════════════════════════════════ rogue: { hitDice: 'd8', startingHP: (conMod) => 8 + conMod, savingThrows: ['dexterity', 'intelligence'], armorProficiencies: ['light'], weaponProficiencies: ['simple', 'hand crossbows', 'longswords', 'rapiers', 'shortswords'], startingEquipment: [ { choose: { count: 1, from: [['Rapier'], ['Shortsword']] } }, { choose: { count: 1, from: [['Shortbow', 'Quiver', 'Arrows x20'], ['Shortsword']] } }, { choose: { count: 1, from: [[...BURGLAR_PACK], [...DUNGEONEERS_PACK], [...EXPLORERS_PACK]] } }, { fixed: ['Leather Armor', 'Dagger', 'Dagger', "Thieves' Tools"] } ] }, // ═══════════════════════════════════════════════════════════════════════════ // SORCERER // ═══════════════════════════════════════════════════════════════════════════ sorcerer: { hitDice: 'd6', startingHP: (conMod) => 6 + conMod, savingThrows: ['constitution', 'charisma'], armorProficiencies: [], weaponProficiencies: ['daggers', 'darts', 'slings', 'quarterstaffs', 'light crossbows'], startingEquipment: [ { choose: { count: 1, from: [['Light Crossbow', 'Crossbow Bolts x20'], ['Simple Weapon']] } }, { choose: { count: 1, from: [['Component Pouch'], ['Arcane Focus']] } }, { choose: { count: 1, from: [[...DUNGEONEERS_PACK], [...EXPLORERS_PACK]] } }, { fixed: ['Dagger', 'Dagger'] } ], spellcasting: { ability: 'cha', cantripsKnown: [4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6], spellsKnown: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 15], slotsByLevel: FULL_CASTER_SLOTS, startingCantrips: ['Fire Bolt', 'Ray of Frost', 'Prestidigitation', 'Light'], startingSpells: ['Magic Missile', 'Shield'] } }, // ═══════════════════════════════════════════════════════════════════════════ // WARLOCK // ═══════════════════════════════════════════════════════════════════════════ warlock: { hitDice: 'd8', startingHP: (conMod) => 8 + conMod, savingThrows: ['wisdom', 'charisma'], armorProficiencies: ['light'], weaponProficiencies: ['simple'], startingEquipment: [ { choose: { count: 1, from: [['Light Crossbow', 'Crossbow Bolts x20'], ['Simple Weapon']] } }, { choose: { count: 1, from: [['Component Pouch'], ['Arcane Focus']] } }, { choose: { count: 1, from: [[...SCHOLARS_PACK], [...DUNGEONEERS_PACK]] } }, { fixed: ['Leather Armor', 'Simple Weapon', 'Dagger', 'Dagger'] } ], spellcasting: { ability: 'cha', cantripsKnown: [2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4], spellsKnown: [2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15], // Warlock has pact magic, not standard slots - handled separately slotsByLevel: { 1: [1, 0, 0, 0, 0, 0, 0, 0, 0], 2: [2, 0, 0, 0, 0, 0, 0, 0, 0], 3: [0, 2, 0, 0, 0, 0, 0, 0, 0], 4: [0, 2, 0, 0, 0, 0, 0, 0, 0], 5: [0, 0, 2, 0, 0, 0, 0, 0, 0], 6: [0, 0, 2, 0, 0, 0, 0, 0, 0], 7: [0, 0, 0, 2, 0, 0, 0, 0, 0], 8: [0, 0, 0, 2, 0, 0, 0, 0, 0], 9: [0, 0, 0, 0, 2, 0, 0, 0, 0], 10: [0, 0, 0, 0, 2, 0, 0, 0, 0], 11: [0, 0, 0, 0, 3, 0, 0, 0, 0], 12: [0, 0, 0, 0, 3, 0, 0, 0, 0], 13: [0, 0, 0, 0, 3, 0, 0, 0, 0], 14: [0, 0, 0, 0, 3, 0, 0, 0, 0], 15: [0, 0, 0, 0, 3, 0, 0, 0, 0], 16: [0, 0, 0, 0, 3, 0, 0, 0, 0], 17: [0, 0, 0, 0, 4, 0, 0, 0, 0], 18: [0, 0, 0, 0, 4, 0, 0, 0, 0], 19: [0, 0, 0, 0, 4, 0, 0, 0, 0], 20: [0, 0, 0, 0, 4, 0, 0, 0, 0] }, startingCantrips: ['Eldritch Blast', 'Minor Illusion'], startingSpells: ['Hex', 'Armor of Agathys'] } }, // ═══════════════════════════════════════════════════════════════════════════ // WIZARD // ═══════════════════════════════════════════════════════════════════════════ wizard: { hitDice: 'd6', startingHP: (conMod) => 6 + conMod, savingThrows: ['intelligence', 'wisdom'], armorProficiencies: [], weaponProficiencies: ['daggers', 'darts', 'slings', 'quarterstaffs', 'light crossbows'], startingEquipment: [ { choose: { count: 1, from: [['Quarterstaff'], ['Dagger']] } }, { choose: { count: 1, from: [['Component Pouch'], ['Arcane Focus']] } }, { choose: { count: 1, from: [[...SCHOLARS_PACK], [...EXPLORERS_PACK]] } }, { fixed: ['Spellbook'] } ], spellcasting: { ability: 'int', cantripsKnown: [3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5], spellsPrepared: true, // INT mod + wizard level slotsByLevel: FULL_CASTER_SLOTS, startingCantrips: ['Fire Bolt', 'Mage Hand', 'Prestidigitation'], startingSpells: ['Magic Missile', 'Shield', 'Mage Armor', 'Sleep', 'Detect Magic', 'Identify'] } } }; /** * Get starting data for a class (case-insensitive) */ export function getClassStartingData(className: string): ClassStartingData | null { const normalized = className.toLowerCase().trim(); return CLASS_DATA[normalized] || null; } /** * Get all simple starting equipment for a class (resolves choices to first option) * This is used for auto-provisioning when the LLM doesn't specify choices */ export function getDefaultStartingEquipment(className: string): string[] { const classData = getClassStartingData(className); if (!classData) return []; const equipment: string[] = []; for (const choice of classData.startingEquipment) { if (choice.fixed) { equipment.push(...choice.fixed); } else if (choice.choose) { // Default to first option const firstOption = choice.choose.from[0]; if (firstOption) { equipment.push(...firstOption); } } } return equipment; } /** * Get spell slot array for a class at a given level * Returns [level1, level2, ..., level9] slot counts */ export function getSpellSlots(className: string, characterLevel: number): number[] { const classData = getClassStartingData(className); if (!classData?.spellcasting) return [0, 0, 0, 0, 0, 0, 0, 0, 0]; const level = Math.min(Math.max(characterLevel, 1), 20); return classData.spellcasting.slotsByLevel[level] || [0, 0, 0, 0, 0, 0, 0, 0, 0]; } /** * Get starting cantrips for a class */ export function getStartingCantrips(className: string): string[] { const classData = getClassStartingData(className); return classData?.spellcasting?.startingCantrips || []; } /** * Get starting spells for a class */ export function getStartingSpells(className: string): string[] { const classData = getClassStartingData(className); return classData?.spellcasting?.startingSpells || []; } /** * Calculate starting HP for a class given constitution modifier */ export function calculateStartingHP(className: string, conMod: number): number { const classData = getClassStartingData(className); if (!classData) return 10 + conMod; // Fallback return classData.startingHP(conMod); } /** * Check if a class is a spellcaster */ export function isSpellcaster(className: string): boolean { const classData = getClassStartingData(className); return !!classData?.spellcasting; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Mnehmos/rpg-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server