@lippelt/srd-dnd-3.5 0.1.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,24 @@
1
+ # Changelog
2
+
3
+ Todas as mudanças notáveis deste pacote são documentadas aqui.
4
+
5
+ O formato segue [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/)
6
+ e o projeto adere ao [Versionamento Semântico](https://semver.org/lang/pt-BR/).
7
+
8
+
9
+ ## [1.0.0] — 2026-07-13
10
+
11
+ ### Mudou
12
+ - **Marco de estabilidade 1.0.0.** O contrato público do pacote é
13
+ promovido a estável — sem mudanças de API em relação à série 0.1.x.
14
+ A partir daqui, quebras de compatibilidade seguem SemVer (major).
15
+ - `peerDependencies`: `@lippelt/srd-core` de `^0.1.0` → `^1.0.0`.
16
+
17
+ ## [0.1.2] — 2026-06-06
18
+ - Release de manutenção (sincronização de versão).
19
+
20
+ ## [0.1.1] — 2026-06-05
21
+ - Docs: README em PT-BR com termos oficiais.
22
+
23
+ ## [0.1.0] — 2026-06-04
24
+ - Primeira publicação no npm.
package/dist/index.cjs CHANGED
@@ -157,6 +157,44 @@ var RULES = {
157
157
  }
158
158
  }
159
159
  };
160
+ var DND35_SKILLS = [
161
+ "appraise",
162
+ "balance",
163
+ "bluff",
164
+ "climb",
165
+ "concentration",
166
+ "craft",
167
+ "decipher-script",
168
+ "diplomacy",
169
+ "disable-device",
170
+ "disguise",
171
+ "escape-artist",
172
+ "forgery",
173
+ "gather-information",
174
+ "handle-animal",
175
+ "heal",
176
+ "hide",
177
+ "intimidate",
178
+ "jump",
179
+ "knowledge",
180
+ "listen",
181
+ "move-silently",
182
+ "open-lock",
183
+ "perform",
184
+ "profession",
185
+ "ride",
186
+ "search",
187
+ "sense-motive",
188
+ "sleight-of-hand",
189
+ "speak-language",
190
+ "spellcraft",
191
+ "spot",
192
+ "survival",
193
+ "swim",
194
+ "tumble",
195
+ "use-magic-device",
196
+ "use-rope"
197
+ ];
160
198
  var dnd35 = {
161
199
  id: "dnd-3.5",
162
200
  name: "Dungeons & Dragons 3.5",
@@ -165,7 +203,11 @@ var dnd35 = {
165
203
  dicePresets: DICE_PRESETS,
166
204
  conditions: CONDITIONS,
167
205
  trackerFields: TRACKER_FIELDS,
168
- rules: RULES
206
+ rules: RULES,
207
+ npc: {
208
+ cantripDamageDice: () => 1,
209
+ skills: DND35_SKILLS
210
+ }
169
211
  };
170
212
  // Annotate the CommonJS export names for ESM import in node:
171
213
  0 && (module.exports = {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Dungeons & Dragons 3.5 (System Reference Document 3.5).\n *\n * Conteúdo de regras deriva do SRD 3.5 da Wizards of the Coast, sob a\n * Open Game License v1.0a:\n * https://www.opengamingfoundation.org/ogl.html\n *\n * Esta implementação é original (não copia código de outras\n * implementações). Termos de game design (saves Fortitude/Reflex/Will,\n * Base Attack Bonus, AC, etc) são parte da Open Game Content do SRD 3.5.\n */\n\nimport type {\n ConditionDef,\n DicePreset,\n RollResult,\n System,\n SystemRules,\n TrackerField,\n} from '@lippelt/srd-core'\n\n// ============================================================================\n// Random helper\n// ============================================================================\n\ntype Roller = (sides: number) => number\n\nlet roller: Roller = (sides: number) => Math.floor(Math.random() * sides) + 1\n\nexport function setRoller(fn: Roller): void {\n roller = fn\n}\n\nexport function resetRoller(): void {\n roller = (sides: number) => Math.floor(Math.random() * sides) + 1\n}\n\nfunction rollDice(sides: number, count = 1): number[] {\n const out: number[] = []\n for (let i = 0; i < count; i++) out.push(roller(sides))\n return out\n}\n\n// ============================================================================\n// Helpers públicos do D&D 3.5\n// ============================================================================\n\n/** Modificador de habilidade: floor((score - 10) / 2). */\nexport function abilityMod(score: number): number {\n return Math.floor((score - 10) / 2)\n}\n\n/** DC de feitiço: 10 + nível do feitiço + modificador da habilidade. */\nexport function spellSaveDC(spellLevel: number, abilityMod: number): number {\n return 10 + spellLevel + abilityMod\n}\n\n// ============================================================================\n// Presets de dados\n// ============================================================================\n\nconst DICE_PRESETS: DicePreset[] = [\n { id: 'd20', label: 'd20', notation: '1d20', category: 'check' },\n { id: 'd4', label: 'd4', notation: '1d4', category: 'damage' },\n { id: 'd6', label: 'd6', notation: '1d6', category: 'damage' },\n { id: 'd8', label: 'd8', notation: '1d8', category: 'damage' },\n { id: 'd10', label: 'd10', notation: '1d10', category: 'damage' },\n { id: 'd12', label: 'd12', notation: '1d12', category: 'damage' },\n { id: 'd100', label: 'd100', notation: '1d100', category: 'special' },\n]\n\n// ============================================================================\n// Conditions — SRD 3.5 (Open Game Content)\n// ============================================================================\n\nconst CONDITIONS: ConditionDef[] = [\n { id: 'blinded', label: 'Blinded', summary: 'Cego: −2 na AC, perde Dex à AC; metade da velocidade.' },\n { id: 'confused', label: 'Confused', summary: 'Confuso: comportamento aleatório por d% (atacar, fugir, divagar).' },\n { id: 'cowering', label: 'Cowering', summary: 'Encolhido de medo: sem ações; perde Dex à AC; atacantes têm +2.' },\n { id: 'dazed', label: 'Dazed', summary: 'Aturdido: nenhuma ação nesta rodada (livre de defesas).' },\n { id: 'dazzled', label: 'Dazzled', summary: 'Cegado por luz: −1 em ataques e Spot/Search.' },\n { id: 'deafened', label: 'Deafened', summary: 'Surdo: −4 em iniciativa; 20% de chance de falhar feitiço verbal.' },\n { id: 'disabled', label: 'Disabled', summary: 'Com HP em 0: 1 ação parcial por turno; ação extenuante perde 1 HP.' },\n { id: 'dying', label: 'Dying', summary: 'Inconsciente entre −1 e −9 HP: estabiliza com 10% por turno; senão perde 1 HP.' },\n { id: 'entangled', label: 'Entangled', summary: 'Enredado: ½ velocidade, −2 em ataques, −4 em Dex efetivo.' },\n { id: 'exhausted', label: 'Exhausted', summary: 'Exausto: −6 em Str/Dex; metade da velocidade. Vira fatigado após 1 hora.' },\n { id: 'fatigued', label: 'Fatigued', summary: 'Fatigado: −2 em Str/Dex; não pode correr ou carga.' },\n { id: 'frightened', label: 'Frightened', summary: 'Amedrontado: foge da fonte; −2 em ataques/saves/checks.' },\n { id: 'grappled', label: 'Grappled', summary: 'Agarrado: sem feitiços com somatic ou material; perde Dex à AC.' },\n { id: 'helpless', label: 'Helpless', summary: 'Indefeso: Dex efetivo 0; melee crit auto; coup de grace possível.' },\n { id: 'nauseated', label: 'Nauseated', summary: 'Nauseado: só move ações; sem ataques, feitiços ou concentração.' },\n { id: 'panicked', label: 'Panicked', summary: 'Em pânico: −2 em saves/skills; deve fugir; sem ataques.' },\n { id: 'paralyzed', label: 'Paralyzed', summary: 'Paralisado: sem ações físicas; Dex/Str efetivos 0; helpless.' },\n { id: 'petrified', label: 'Petrified', summary: 'Petrificado: virou pedra; inconsciente e indefeso.' },\n { id: 'pinned', label: 'Pinned', summary: 'Imobilizado em grapple: perde Dex à AC; sem ações exceto escapar.' },\n { id: 'prone', label: 'Prone', summary: 'Caído: −4 em ataques corpo-a-corpo; +4 AC vs ranged; −4 AC vs melee.' },\n { id: 'shaken', label: 'Shaken', summary: 'Abalado: −2 em ataques, saves, skills.' },\n { id: 'sickened', label: 'Sickened', summary: 'Enjoado: −2 em ataques, saves, skills, damage.' },\n { id: 'stunned', label: 'Stunned', summary: 'Atordoado: sem ações; perde Dex; −2 AC; solta itens.' },\n { id: 'unconscious', label: 'Unconscious', summary: 'Inconsciente: indefeso, normalmente porque HP ≤ −1.' },\n]\n\n// ============================================================================\n// Tracker fields\n// ============================================================================\n\nconst TRACKER_FIELDS: TrackerField[] = [\n { key: 'ac', label: 'CA', kind: 'integer', min: 0, max: 50, default: 10, description: 'Armor Class (Classe de Armadura).' },\n { key: 'fort', label: 'Fort', kind: 'integer', min: -10, max: 30, default: 0, description: 'Fortitude save modifier.' },\n { key: 'ref', label: 'Ref', kind: 'integer', min: -10, max: 30, default: 0, description: 'Reflex save modifier.' },\n { key: 'will', label: 'Will', kind: 'integer', min: -10, max: 30, default: 0, description: 'Will save modifier.' },\n { key: 'bab', label: 'BAB', kind: 'integer', min: 0, max: 25, default: 0, description: 'Base Attack Bonus.' },\n]\n\n// ============================================================================\n// Rules\n// ============================================================================\n\ninterface D20Params {\n modifier?: number\n}\n\nfunction rollD20({ modifier = 0 }: D20Params): RollResult {\n const d20 = rollDice(20)[0]!\n const total = d20 + modifier\n const notes: string[] = []\n if (d20 === 20) notes.push('20 natural')\n if (d20 === 1) notes.push('1 natural')\n const modStr = modifier === 0 ? '' : modifier > 0 ? `+${modifier}` : `${modifier}`\n return { rolls: [d20], modifier, total, notation: `1d20${modStr}`, notes }\n}\n\ninterface AttackParams {\n modifier?: number\n targetAC?: number\n /** Threshold do crítico (default 20). Armas como rapier 18-20, picareta 20/×4 etc. */\n critRange?: number\n}\n\nfunction rollAttack(params: AttackParams): RollResult {\n const r = rollD20(params)\n const d20 = r.rolls[0]!\n const notes = [...(r.notes ?? [])]\n if (params.targetAC !== undefined) {\n const critRange = params.critRange ?? 20\n const natural20 = d20 === 20\n const natural1 = d20 === 1\n const threat = d20 >= critRange && !natural1\n const hit = natural20 || (!natural1 && r.total >= params.targetAC)\n if (natural20) notes.push('acerto crítico (20 natural)')\n else if (threat) notes.push(`ameaça de crítico (${d20} ≥ ${critRange})`)\n else if (natural1) notes.push('erro automático (1 natural)')\n else notes.push(hit ? 'acertou' : 'errou')\n }\n return { ...r, notes }\n}\n\ninterface SaveParams {\n modifier?: number\n dc: number\n}\n\nfunction rollSave({ modifier = 0, dc }: SaveParams): RollResult {\n const r = rollD20({ modifier })\n const notes = [...(r.notes ?? [])]\n // 1 natural sempre falha; 20 natural sempre passa (regra do SRD 3.5).\n const d20 = r.rolls[0]!\n const auto = d20 === 1 ? false : d20 === 20 ? true : null\n const passed = auto !== null ? auto : r.total >= dc\n notes.push(passed ? 'sucesso' : 'falha')\n notes.push(`DC ${dc}`)\n return { ...r, notes }\n}\n\ninterface DamageParams {\n count: number\n sides: number\n modifier?: number\n /** Multiplica os dados de dano em caso de crítico (×2 default, alguns ×3 ou ×4). */\n critMultiplier?: number\n}\n\nfunction rollDamage({ count, sides, modifier = 0, critMultiplier }: DamageParams): RollResult {\n const m = critMultiplier && critMultiplier > 1 ? Math.trunc(critMultiplier) : 1\n const dice = rollDice(sides, count * m)\n const sum = dice.reduce((a, b) => a + b, 0)\n const total = Math.max(0, sum + modifier * m)\n const modStr = modifier === 0 ? '' : modifier > 0 ? `+${modifier}` : `${modifier}`\n const critStr = m > 1 ? ` (crit ×${m})` : ''\n return {\n rolls: dice,\n modifier: modifier * m,\n total,\n notation: `${count * m}d${sides}${modStr}${critStr}`,\n notes: m > 1 ? [`dano crítico — dados e mod ×${m}`] : [],\n }\n}\n\nconst RULES: SystemRules = {\n roll(kind, params) {\n switch (kind) {\n case 'd20':\n case 'check':\n case 'ability':\n case 'skill':\n return rollD20(params as unknown as D20Params)\n case 'attack':\n return rollAttack(params as unknown as AttackParams)\n case 'save':\n return rollSave(params as unknown as SaveParams)\n case 'damage':\n return rollDamage(params as unknown as DamageParams)\n default:\n return null\n }\n },\n}\n\n// ============================================================================\n// Bundle\n// ============================================================================\n\nexport const dnd35: System = {\n id: 'dnd-3.5',\n name: 'Dungeons & Dragons 3.5',\n ruleVersion: 'SRD 3.5',\n attribution:\n 'Contains material from the System Reference Document 3.5 by Wizards of the Coast LLC under the Open Game License v1.0a.',\n dicePresets: DICE_PRESETS,\n conditions: CONDITIONS,\n trackerFields: TRACKER_FIELDS,\n rules: RULES,\n}\n"],"mappings":"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","names":["abilityMod"]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Dungeons & Dragons 3.5 (System Reference Document 3.5).\n *\n * Conteúdo de regras deriva do SRD 3.5 da Wizards of the Coast, sob a\n * Open Game License v1.0a:\n * https://www.opengamingfoundation.org/ogl.html\n *\n * Esta implementação é original (não copia código de outras\n * implementações). Termos de game design (saves Fortitude/Reflex/Will,\n * Base Attack Bonus, AC, etc) são parte da Open Game Content do SRD 3.5.\n */\n\nimport type {\n ConditionDef,\n DicePreset,\n RollResult,\n System,\n SystemRules,\n TrackerField,\n} from '@lippelt/srd-core'\n\n// ============================================================================\n// Random helper\n// ============================================================================\n\ntype Roller = (sides: number) => number\n\nlet roller: Roller = (sides: number) => Math.floor(Math.random() * sides) + 1\n\nexport function setRoller(fn: Roller): void {\n roller = fn\n}\n\nexport function resetRoller(): void {\n roller = (sides: number) => Math.floor(Math.random() * sides) + 1\n}\n\nfunction rollDice(sides: number, count = 1): number[] {\n const out: number[] = []\n for (let i = 0; i < count; i++) out.push(roller(sides))\n return out\n}\n\n// ============================================================================\n// Helpers públicos do D&D 3.5\n// ============================================================================\n\n/** Modificador de habilidade: floor((score - 10) / 2). */\nexport function abilityMod(score: number): number {\n return Math.floor((score - 10) / 2)\n}\n\n/** DC de feitiço: 10 + nível do feitiço + modificador da habilidade. */\nexport function spellSaveDC(spellLevel: number, abilityMod: number): number {\n return 10 + spellLevel + abilityMod\n}\n\n// ============================================================================\n// Presets de dados\n// ============================================================================\n\nconst DICE_PRESETS: DicePreset[] = [\n { id: 'd20', label: 'd20', notation: '1d20', category: 'check' },\n { id: 'd4', label: 'd4', notation: '1d4', category: 'damage' },\n { id: 'd6', label: 'd6', notation: '1d6', category: 'damage' },\n { id: 'd8', label: 'd8', notation: '1d8', category: 'damage' },\n { id: 'd10', label: 'd10', notation: '1d10', category: 'damage' },\n { id: 'd12', label: 'd12', notation: '1d12', category: 'damage' },\n { id: 'd100', label: 'd100', notation: '1d100', category: 'special' },\n]\n\n// ============================================================================\n// Conditions — SRD 3.5 (Open Game Content)\n// ============================================================================\n\nconst CONDITIONS: ConditionDef[] = [\n { id: 'blinded', label: 'Blinded', summary: 'Cego: −2 na AC, perde Dex à AC; metade da velocidade.' },\n { id: 'confused', label: 'Confused', summary: 'Confuso: comportamento aleatório por d% (atacar, fugir, divagar).' },\n { id: 'cowering', label: 'Cowering', summary: 'Encolhido de medo: sem ações; perde Dex à AC; atacantes têm +2.' },\n { id: 'dazed', label: 'Dazed', summary: 'Aturdido: nenhuma ação nesta rodada (livre de defesas).' },\n { id: 'dazzled', label: 'Dazzled', summary: 'Cegado por luz: −1 em ataques e Spot/Search.' },\n { id: 'deafened', label: 'Deafened', summary: 'Surdo: −4 em iniciativa; 20% de chance de falhar feitiço verbal.' },\n { id: 'disabled', label: 'Disabled', summary: 'Com HP em 0: 1 ação parcial por turno; ação extenuante perde 1 HP.' },\n { id: 'dying', label: 'Dying', summary: 'Inconsciente entre −1 e −9 HP: estabiliza com 10% por turno; senão perde 1 HP.' },\n { id: 'entangled', label: 'Entangled', summary: 'Enredado: ½ velocidade, −2 em ataques, −4 em Dex efetivo.' },\n { id: 'exhausted', label: 'Exhausted', summary: 'Exausto: −6 em Str/Dex; metade da velocidade. Vira fatigado após 1 hora.' },\n { id: 'fatigued', label: 'Fatigued', summary: 'Fatigado: −2 em Str/Dex; não pode correr ou carga.' },\n { id: 'frightened', label: 'Frightened', summary: 'Amedrontado: foge da fonte; −2 em ataques/saves/checks.' },\n { id: 'grappled', label: 'Grappled', summary: 'Agarrado: sem feitiços com somatic ou material; perde Dex à AC.' },\n { id: 'helpless', label: 'Helpless', summary: 'Indefeso: Dex efetivo 0; melee crit auto; coup de grace possível.' },\n { id: 'nauseated', label: 'Nauseated', summary: 'Nauseado: só move ações; sem ataques, feitiços ou concentração.' },\n { id: 'panicked', label: 'Panicked', summary: 'Em pânico: −2 em saves/skills; deve fugir; sem ataques.' },\n { id: 'paralyzed', label: 'Paralyzed', summary: 'Paralisado: sem ações físicas; Dex/Str efetivos 0; helpless.' },\n { id: 'petrified', label: 'Petrified', summary: 'Petrificado: virou pedra; inconsciente e indefeso.' },\n { id: 'pinned', label: 'Pinned', summary: 'Imobilizado em grapple: perde Dex à AC; sem ações exceto escapar.' },\n { id: 'prone', label: 'Prone', summary: 'Caído: −4 em ataques corpo-a-corpo; +4 AC vs ranged; −4 AC vs melee.' },\n { id: 'shaken', label: 'Shaken', summary: 'Abalado: −2 em ataques, saves, skills.' },\n { id: 'sickened', label: 'Sickened', summary: 'Enjoado: −2 em ataques, saves, skills, damage.' },\n { id: 'stunned', label: 'Stunned', summary: 'Atordoado: sem ações; perde Dex; −2 AC; solta itens.' },\n { id: 'unconscious', label: 'Unconscious', summary: 'Inconsciente: indefeso, normalmente porque HP ≤ −1.' },\n]\n\n// ============================================================================\n// Tracker fields\n// ============================================================================\n\nconst TRACKER_FIELDS: TrackerField[] = [\n { key: 'ac', label: 'CA', kind: 'integer', min: 0, max: 50, default: 10, description: 'Armor Class (Classe de Armadura).' },\n { key: 'fort', label: 'Fort', kind: 'integer', min: -10, max: 30, default: 0, description: 'Fortitude save modifier.' },\n { key: 'ref', label: 'Ref', kind: 'integer', min: -10, max: 30, default: 0, description: 'Reflex save modifier.' },\n { key: 'will', label: 'Will', kind: 'integer', min: -10, max: 30, default: 0, description: 'Will save modifier.' },\n { key: 'bab', label: 'BAB', kind: 'integer', min: 0, max: 25, default: 0, description: 'Base Attack Bonus.' },\n]\n\n// ============================================================================\n// Rules\n// ============================================================================\n\ninterface D20Params {\n modifier?: number\n}\n\nfunction rollD20({ modifier = 0 }: D20Params): RollResult {\n const d20 = rollDice(20)[0]!\n const total = d20 + modifier\n const notes: string[] = []\n if (d20 === 20) notes.push('20 natural')\n if (d20 === 1) notes.push('1 natural')\n const modStr = modifier === 0 ? '' : modifier > 0 ? `+${modifier}` : `${modifier}`\n return { rolls: [d20], modifier, total, notation: `1d20${modStr}`, notes }\n}\n\ninterface AttackParams {\n modifier?: number\n targetAC?: number\n /** Threshold do crítico (default 20). Armas como rapier 18-20, picareta 20/×4 etc. */\n critRange?: number\n}\n\nfunction rollAttack(params: AttackParams): RollResult {\n const r = rollD20(params)\n const d20 = r.rolls[0]!\n const notes = [...(r.notes ?? [])]\n if (params.targetAC !== undefined) {\n const critRange = params.critRange ?? 20\n const natural20 = d20 === 20\n const natural1 = d20 === 1\n const threat = d20 >= critRange && !natural1\n const hit = natural20 || (!natural1 && r.total >= params.targetAC)\n if (natural20) notes.push('acerto crítico (20 natural)')\n else if (threat) notes.push(`ameaça de crítico (${d20} ≥ ${critRange})`)\n else if (natural1) notes.push('erro automático (1 natural)')\n else notes.push(hit ? 'acertou' : 'errou')\n }\n return { ...r, notes }\n}\n\ninterface SaveParams {\n modifier?: number\n dc: number\n}\n\nfunction rollSave({ modifier = 0, dc }: SaveParams): RollResult {\n const r = rollD20({ modifier })\n const notes = [...(r.notes ?? [])]\n // 1 natural sempre falha; 20 natural sempre passa (regra do SRD 3.5).\n const d20 = r.rolls[0]!\n const auto = d20 === 1 ? false : d20 === 20 ? true : null\n const passed = auto !== null ? auto : r.total >= dc\n notes.push(passed ? 'sucesso' : 'falha')\n notes.push(`DC ${dc}`)\n return { ...r, notes }\n}\n\ninterface DamageParams {\n count: number\n sides: number\n modifier?: number\n /** Multiplica os dados de dano em caso de crítico (×2 default, alguns ×3 ou ×4). */\n critMultiplier?: number\n}\n\nfunction rollDamage({ count, sides, modifier = 0, critMultiplier }: DamageParams): RollResult {\n const m = critMultiplier && critMultiplier > 1 ? Math.trunc(critMultiplier) : 1\n const dice = rollDice(sides, count * m)\n const sum = dice.reduce((a, b) => a + b, 0)\n const total = Math.max(0, sum + modifier * m)\n const modStr = modifier === 0 ? '' : modifier > 0 ? `+${modifier}` : `${modifier}`\n const critStr = m > 1 ? ` (crit ×${m})` : ''\n return {\n rolls: dice,\n modifier: modifier * m,\n total,\n notation: `${count * m}d${sides}${modStr}${critStr}`,\n notes: m > 1 ? [`dano crítico — dados e mod ×${m}`] : [],\n }\n}\n\nconst RULES: SystemRules = {\n roll(kind, params) {\n switch (kind) {\n case 'd20':\n case 'check':\n case 'ability':\n case 'skill':\n return rollD20(params as unknown as D20Params)\n case 'attack':\n return rollAttack(params as unknown as AttackParams)\n case 'save':\n return rollSave(params as unknown as SaveParams)\n case 'damage':\n return rollDamage(params as unknown as DamageParams)\n default:\n return null\n }\n },\n}\n\n// ============================================================================\n// Bundle\n// ============================================================================\n\n/**\n * Hooks pra `@lippelt/srd-npcgen`. 3.5 usa BAB (default do npcgen já trata\n * via D20_MODEL). Cantrips/0-level spells em 3.5 têm dano FIXO (não escalam\n * por nível) — override pra retornar 1 dado sempre.\n *\n * Skill list é a do SRD 3.5; algumas skills (Knowledge, Profession, Craft,\n * Perform) têm múltiplas instâncias na ficha real, mas aqui listamos só os\n * IDs canônicos.\n */\nconst DND35_SKILLS = [\n 'appraise',\n 'balance',\n 'bluff',\n 'climb',\n 'concentration',\n 'craft',\n 'decipher-script',\n 'diplomacy',\n 'disable-device',\n 'disguise',\n 'escape-artist',\n 'forgery',\n 'gather-information',\n 'handle-animal',\n 'heal',\n 'hide',\n 'intimidate',\n 'jump',\n 'knowledge',\n 'listen',\n 'move-silently',\n 'open-lock',\n 'perform',\n 'profession',\n 'ride',\n 'search',\n 'sense-motive',\n 'sleight-of-hand',\n 'speak-language',\n 'spellcraft',\n 'spot',\n 'survival',\n 'swim',\n 'tumble',\n 'use-magic-device',\n 'use-rope',\n] as const\n\nexport const dnd35: System = {\n id: 'dnd-3.5',\n name: 'Dungeons & Dragons 3.5',\n ruleVersion: 'SRD 3.5',\n attribution:\n 'Contains material from the System Reference Document 3.5 by Wizards of the Coast LLC under the Open Game License v1.0a.',\n dicePresets: DICE_PRESETS,\n conditions: CONDITIONS,\n trackerFields: TRACKER_FIELDS,\n rules: RULES,\n npc: {\n cantripDamageDice: () => 1,\n skills: DND35_SKILLS,\n },\n}\n"],"mappings":"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","names":["abilityMod"]}
package/dist/index.js CHANGED
@@ -129,6 +129,44 @@ var RULES = {
129
129
  }
130
130
  }
131
131
  };
132
+ var DND35_SKILLS = [
133
+ "appraise",
134
+ "balance",
135
+ "bluff",
136
+ "climb",
137
+ "concentration",
138
+ "craft",
139
+ "decipher-script",
140
+ "diplomacy",
141
+ "disable-device",
142
+ "disguise",
143
+ "escape-artist",
144
+ "forgery",
145
+ "gather-information",
146
+ "handle-animal",
147
+ "heal",
148
+ "hide",
149
+ "intimidate",
150
+ "jump",
151
+ "knowledge",
152
+ "listen",
153
+ "move-silently",
154
+ "open-lock",
155
+ "perform",
156
+ "profession",
157
+ "ride",
158
+ "search",
159
+ "sense-motive",
160
+ "sleight-of-hand",
161
+ "speak-language",
162
+ "spellcraft",
163
+ "spot",
164
+ "survival",
165
+ "swim",
166
+ "tumble",
167
+ "use-magic-device",
168
+ "use-rope"
169
+ ];
132
170
  var dnd35 = {
133
171
  id: "dnd-3.5",
134
172
  name: "Dungeons & Dragons 3.5",
@@ -137,7 +175,11 @@ var dnd35 = {
137
175
  dicePresets: DICE_PRESETS,
138
176
  conditions: CONDITIONS,
139
177
  trackerFields: TRACKER_FIELDS,
140
- rules: RULES
178
+ rules: RULES,
179
+ npc: {
180
+ cantripDamageDice: () => 1,
181
+ skills: DND35_SKILLS
182
+ }
141
183
  };
142
184
  export {
143
185
  abilityMod,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Dungeons & Dragons 3.5 (System Reference Document 3.5).\n *\n * Conteúdo de regras deriva do SRD 3.5 da Wizards of the Coast, sob a\n * Open Game License v1.0a:\n * https://www.opengamingfoundation.org/ogl.html\n *\n * Esta implementação é original (não copia código de outras\n * implementações). Termos de game design (saves Fortitude/Reflex/Will,\n * Base Attack Bonus, AC, etc) são parte da Open Game Content do SRD 3.5.\n */\n\nimport type {\n ConditionDef,\n DicePreset,\n RollResult,\n System,\n SystemRules,\n TrackerField,\n} from '@lippelt/srd-core'\n\n// ============================================================================\n// Random helper\n// ============================================================================\n\ntype Roller = (sides: number) => number\n\nlet roller: Roller = (sides: number) => Math.floor(Math.random() * sides) + 1\n\nexport function setRoller(fn: Roller): void {\n roller = fn\n}\n\nexport function resetRoller(): void {\n roller = (sides: number) => Math.floor(Math.random() * sides) + 1\n}\n\nfunction rollDice(sides: number, count = 1): number[] {\n const out: number[] = []\n for (let i = 0; i < count; i++) out.push(roller(sides))\n return out\n}\n\n// ============================================================================\n// Helpers públicos do D&D 3.5\n// ============================================================================\n\n/** Modificador de habilidade: floor((score - 10) / 2). */\nexport function abilityMod(score: number): number {\n return Math.floor((score - 10) / 2)\n}\n\n/** DC de feitiço: 10 + nível do feitiço + modificador da habilidade. */\nexport function spellSaveDC(spellLevel: number, abilityMod: number): number {\n return 10 + spellLevel + abilityMod\n}\n\n// ============================================================================\n// Presets de dados\n// ============================================================================\n\nconst DICE_PRESETS: DicePreset[] = [\n { id: 'd20', label: 'd20', notation: '1d20', category: 'check' },\n { id: 'd4', label: 'd4', notation: '1d4', category: 'damage' },\n { id: 'd6', label: 'd6', notation: '1d6', category: 'damage' },\n { id: 'd8', label: 'd8', notation: '1d8', category: 'damage' },\n { id: 'd10', label: 'd10', notation: '1d10', category: 'damage' },\n { id: 'd12', label: 'd12', notation: '1d12', category: 'damage' },\n { id: 'd100', label: 'd100', notation: '1d100', category: 'special' },\n]\n\n// ============================================================================\n// Conditions — SRD 3.5 (Open Game Content)\n// ============================================================================\n\nconst CONDITIONS: ConditionDef[] = [\n { id: 'blinded', label: 'Blinded', summary: 'Cego: −2 na AC, perde Dex à AC; metade da velocidade.' },\n { id: 'confused', label: 'Confused', summary: 'Confuso: comportamento aleatório por d% (atacar, fugir, divagar).' },\n { id: 'cowering', label: 'Cowering', summary: 'Encolhido de medo: sem ações; perde Dex à AC; atacantes têm +2.' },\n { id: 'dazed', label: 'Dazed', summary: 'Aturdido: nenhuma ação nesta rodada (livre de defesas).' },\n { id: 'dazzled', label: 'Dazzled', summary: 'Cegado por luz: −1 em ataques e Spot/Search.' },\n { id: 'deafened', label: 'Deafened', summary: 'Surdo: −4 em iniciativa; 20% de chance de falhar feitiço verbal.' },\n { id: 'disabled', label: 'Disabled', summary: 'Com HP em 0: 1 ação parcial por turno; ação extenuante perde 1 HP.' },\n { id: 'dying', label: 'Dying', summary: 'Inconsciente entre −1 e −9 HP: estabiliza com 10% por turno; senão perde 1 HP.' },\n { id: 'entangled', label: 'Entangled', summary: 'Enredado: ½ velocidade, −2 em ataques, −4 em Dex efetivo.' },\n { id: 'exhausted', label: 'Exhausted', summary: 'Exausto: −6 em Str/Dex; metade da velocidade. Vira fatigado após 1 hora.' },\n { id: 'fatigued', label: 'Fatigued', summary: 'Fatigado: −2 em Str/Dex; não pode correr ou carga.' },\n { id: 'frightened', label: 'Frightened', summary: 'Amedrontado: foge da fonte; −2 em ataques/saves/checks.' },\n { id: 'grappled', label: 'Grappled', summary: 'Agarrado: sem feitiços com somatic ou material; perde Dex à AC.' },\n { id: 'helpless', label: 'Helpless', summary: 'Indefeso: Dex efetivo 0; melee crit auto; coup de grace possível.' },\n { id: 'nauseated', label: 'Nauseated', summary: 'Nauseado: só move ações; sem ataques, feitiços ou concentração.' },\n { id: 'panicked', label: 'Panicked', summary: 'Em pânico: −2 em saves/skills; deve fugir; sem ataques.' },\n { id: 'paralyzed', label: 'Paralyzed', summary: 'Paralisado: sem ações físicas; Dex/Str efetivos 0; helpless.' },\n { id: 'petrified', label: 'Petrified', summary: 'Petrificado: virou pedra; inconsciente e indefeso.' },\n { id: 'pinned', label: 'Pinned', summary: 'Imobilizado em grapple: perde Dex à AC; sem ações exceto escapar.' },\n { id: 'prone', label: 'Prone', summary: 'Caído: −4 em ataques corpo-a-corpo; +4 AC vs ranged; −4 AC vs melee.' },\n { id: 'shaken', label: 'Shaken', summary: 'Abalado: −2 em ataques, saves, skills.' },\n { id: 'sickened', label: 'Sickened', summary: 'Enjoado: −2 em ataques, saves, skills, damage.' },\n { id: 'stunned', label: 'Stunned', summary: 'Atordoado: sem ações; perde Dex; −2 AC; solta itens.' },\n { id: 'unconscious', label: 'Unconscious', summary: 'Inconsciente: indefeso, normalmente porque HP ≤ −1.' },\n]\n\n// ============================================================================\n// Tracker fields\n// ============================================================================\n\nconst TRACKER_FIELDS: TrackerField[] = [\n { key: 'ac', label: 'CA', kind: 'integer', min: 0, max: 50, default: 10, description: 'Armor Class (Classe de Armadura).' },\n { key: 'fort', label: 'Fort', kind: 'integer', min: -10, max: 30, default: 0, description: 'Fortitude save modifier.' },\n { key: 'ref', label: 'Ref', kind: 'integer', min: -10, max: 30, default: 0, description: 'Reflex save modifier.' },\n { key: 'will', label: 'Will', kind: 'integer', min: -10, max: 30, default: 0, description: 'Will save modifier.' },\n { key: 'bab', label: 'BAB', kind: 'integer', min: 0, max: 25, default: 0, description: 'Base Attack Bonus.' },\n]\n\n// ============================================================================\n// Rules\n// ============================================================================\n\ninterface D20Params {\n modifier?: number\n}\n\nfunction rollD20({ modifier = 0 }: D20Params): RollResult {\n const d20 = rollDice(20)[0]!\n const total = d20 + modifier\n const notes: string[] = []\n if (d20 === 20) notes.push('20 natural')\n if (d20 === 1) notes.push('1 natural')\n const modStr = modifier === 0 ? '' : modifier > 0 ? `+${modifier}` : `${modifier}`\n return { rolls: [d20], modifier, total, notation: `1d20${modStr}`, notes }\n}\n\ninterface AttackParams {\n modifier?: number\n targetAC?: number\n /** Threshold do crítico (default 20). Armas como rapier 18-20, picareta 20/×4 etc. */\n critRange?: number\n}\n\nfunction rollAttack(params: AttackParams): RollResult {\n const r = rollD20(params)\n const d20 = r.rolls[0]!\n const notes = [...(r.notes ?? [])]\n if (params.targetAC !== undefined) {\n const critRange = params.critRange ?? 20\n const natural20 = d20 === 20\n const natural1 = d20 === 1\n const threat = d20 >= critRange && !natural1\n const hit = natural20 || (!natural1 && r.total >= params.targetAC)\n if (natural20) notes.push('acerto crítico (20 natural)')\n else if (threat) notes.push(`ameaça de crítico (${d20} ≥ ${critRange})`)\n else if (natural1) notes.push('erro automático (1 natural)')\n else notes.push(hit ? 'acertou' : 'errou')\n }\n return { ...r, notes }\n}\n\ninterface SaveParams {\n modifier?: number\n dc: number\n}\n\nfunction rollSave({ modifier = 0, dc }: SaveParams): RollResult {\n const r = rollD20({ modifier })\n const notes = [...(r.notes ?? [])]\n // 1 natural sempre falha; 20 natural sempre passa (regra do SRD 3.5).\n const d20 = r.rolls[0]!\n const auto = d20 === 1 ? false : d20 === 20 ? true : null\n const passed = auto !== null ? auto : r.total >= dc\n notes.push(passed ? 'sucesso' : 'falha')\n notes.push(`DC ${dc}`)\n return { ...r, notes }\n}\n\ninterface DamageParams {\n count: number\n sides: number\n modifier?: number\n /** Multiplica os dados de dano em caso de crítico (×2 default, alguns ×3 ou ×4). */\n critMultiplier?: number\n}\n\nfunction rollDamage({ count, sides, modifier = 0, critMultiplier }: DamageParams): RollResult {\n const m = critMultiplier && critMultiplier > 1 ? Math.trunc(critMultiplier) : 1\n const dice = rollDice(sides, count * m)\n const sum = dice.reduce((a, b) => a + b, 0)\n const total = Math.max(0, sum + modifier * m)\n const modStr = modifier === 0 ? '' : modifier > 0 ? `+${modifier}` : `${modifier}`\n const critStr = m > 1 ? ` (crit ×${m})` : ''\n return {\n rolls: dice,\n modifier: modifier * m,\n total,\n notation: `${count * m}d${sides}${modStr}${critStr}`,\n notes: m > 1 ? [`dano crítico — dados e mod ×${m}`] : [],\n }\n}\n\nconst RULES: SystemRules = {\n roll(kind, params) {\n switch (kind) {\n case 'd20':\n case 'check':\n case 'ability':\n case 'skill':\n return rollD20(params as unknown as D20Params)\n case 'attack':\n return rollAttack(params as unknown as AttackParams)\n case 'save':\n return rollSave(params as unknown as SaveParams)\n case 'damage':\n return rollDamage(params as unknown as DamageParams)\n default:\n return null\n }\n },\n}\n\n// ============================================================================\n// Bundle\n// ============================================================================\n\nexport const dnd35: System = {\n id: 'dnd-3.5',\n name: 'Dungeons & Dragons 3.5',\n ruleVersion: 'SRD 3.5',\n attribution:\n 'Contains material from the System Reference Document 3.5 by Wizards of the Coast LLC under the Open Game License v1.0a.',\n dicePresets: DICE_PRESETS,\n conditions: CONDITIONS,\n trackerFields: TRACKER_FIELDS,\n rules: RULES,\n}\n"],"mappings":"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","names":["abilityMod"]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Dungeons & Dragons 3.5 (System Reference Document 3.5).\n *\n * Conteúdo de regras deriva do SRD 3.5 da Wizards of the Coast, sob a\n * Open Game License v1.0a:\n * https://www.opengamingfoundation.org/ogl.html\n *\n * Esta implementação é original (não copia código de outras\n * implementações). Termos de game design (saves Fortitude/Reflex/Will,\n * Base Attack Bonus, AC, etc) são parte da Open Game Content do SRD 3.5.\n */\n\nimport type {\n ConditionDef,\n DicePreset,\n RollResult,\n System,\n SystemRules,\n TrackerField,\n} from '@lippelt/srd-core'\n\n// ============================================================================\n// Random helper\n// ============================================================================\n\ntype Roller = (sides: number) => number\n\nlet roller: Roller = (sides: number) => Math.floor(Math.random() * sides) + 1\n\nexport function setRoller(fn: Roller): void {\n roller = fn\n}\n\nexport function resetRoller(): void {\n roller = (sides: number) => Math.floor(Math.random() * sides) + 1\n}\n\nfunction rollDice(sides: number, count = 1): number[] {\n const out: number[] = []\n for (let i = 0; i < count; i++) out.push(roller(sides))\n return out\n}\n\n// ============================================================================\n// Helpers públicos do D&D 3.5\n// ============================================================================\n\n/** Modificador de habilidade: floor((score - 10) / 2). */\nexport function abilityMod(score: number): number {\n return Math.floor((score - 10) / 2)\n}\n\n/** DC de feitiço: 10 + nível do feitiço + modificador da habilidade. */\nexport function spellSaveDC(spellLevel: number, abilityMod: number): number {\n return 10 + spellLevel + abilityMod\n}\n\n// ============================================================================\n// Presets de dados\n// ============================================================================\n\nconst DICE_PRESETS: DicePreset[] = [\n { id: 'd20', label: 'd20', notation: '1d20', category: 'check' },\n { id: 'd4', label: 'd4', notation: '1d4', category: 'damage' },\n { id: 'd6', label: 'd6', notation: '1d6', category: 'damage' },\n { id: 'd8', label: 'd8', notation: '1d8', category: 'damage' },\n { id: 'd10', label: 'd10', notation: '1d10', category: 'damage' },\n { id: 'd12', label: 'd12', notation: '1d12', category: 'damage' },\n { id: 'd100', label: 'd100', notation: '1d100', category: 'special' },\n]\n\n// ============================================================================\n// Conditions — SRD 3.5 (Open Game Content)\n// ============================================================================\n\nconst CONDITIONS: ConditionDef[] = [\n { id: 'blinded', label: 'Blinded', summary: 'Cego: −2 na AC, perde Dex à AC; metade da velocidade.' },\n { id: 'confused', label: 'Confused', summary: 'Confuso: comportamento aleatório por d% (atacar, fugir, divagar).' },\n { id: 'cowering', label: 'Cowering', summary: 'Encolhido de medo: sem ações; perde Dex à AC; atacantes têm +2.' },\n { id: 'dazed', label: 'Dazed', summary: 'Aturdido: nenhuma ação nesta rodada (livre de defesas).' },\n { id: 'dazzled', label: 'Dazzled', summary: 'Cegado por luz: −1 em ataques e Spot/Search.' },\n { id: 'deafened', label: 'Deafened', summary: 'Surdo: −4 em iniciativa; 20% de chance de falhar feitiço verbal.' },\n { id: 'disabled', label: 'Disabled', summary: 'Com HP em 0: 1 ação parcial por turno; ação extenuante perde 1 HP.' },\n { id: 'dying', label: 'Dying', summary: 'Inconsciente entre −1 e −9 HP: estabiliza com 10% por turno; senão perde 1 HP.' },\n { id: 'entangled', label: 'Entangled', summary: 'Enredado: ½ velocidade, −2 em ataques, −4 em Dex efetivo.' },\n { id: 'exhausted', label: 'Exhausted', summary: 'Exausto: −6 em Str/Dex; metade da velocidade. Vira fatigado após 1 hora.' },\n { id: 'fatigued', label: 'Fatigued', summary: 'Fatigado: −2 em Str/Dex; não pode correr ou carga.' },\n { id: 'frightened', label: 'Frightened', summary: 'Amedrontado: foge da fonte; −2 em ataques/saves/checks.' },\n { id: 'grappled', label: 'Grappled', summary: 'Agarrado: sem feitiços com somatic ou material; perde Dex à AC.' },\n { id: 'helpless', label: 'Helpless', summary: 'Indefeso: Dex efetivo 0; melee crit auto; coup de grace possível.' },\n { id: 'nauseated', label: 'Nauseated', summary: 'Nauseado: só move ações; sem ataques, feitiços ou concentração.' },\n { id: 'panicked', label: 'Panicked', summary: 'Em pânico: −2 em saves/skills; deve fugir; sem ataques.' },\n { id: 'paralyzed', label: 'Paralyzed', summary: 'Paralisado: sem ações físicas; Dex/Str efetivos 0; helpless.' },\n { id: 'petrified', label: 'Petrified', summary: 'Petrificado: virou pedra; inconsciente e indefeso.' },\n { id: 'pinned', label: 'Pinned', summary: 'Imobilizado em grapple: perde Dex à AC; sem ações exceto escapar.' },\n { id: 'prone', label: 'Prone', summary: 'Caído: −4 em ataques corpo-a-corpo; +4 AC vs ranged; −4 AC vs melee.' },\n { id: 'shaken', label: 'Shaken', summary: 'Abalado: −2 em ataques, saves, skills.' },\n { id: 'sickened', label: 'Sickened', summary: 'Enjoado: −2 em ataques, saves, skills, damage.' },\n { id: 'stunned', label: 'Stunned', summary: 'Atordoado: sem ações; perde Dex; −2 AC; solta itens.' },\n { id: 'unconscious', label: 'Unconscious', summary: 'Inconsciente: indefeso, normalmente porque HP ≤ −1.' },\n]\n\n// ============================================================================\n// Tracker fields\n// ============================================================================\n\nconst TRACKER_FIELDS: TrackerField[] = [\n { key: 'ac', label: 'CA', kind: 'integer', min: 0, max: 50, default: 10, description: 'Armor Class (Classe de Armadura).' },\n { key: 'fort', label: 'Fort', kind: 'integer', min: -10, max: 30, default: 0, description: 'Fortitude save modifier.' },\n { key: 'ref', label: 'Ref', kind: 'integer', min: -10, max: 30, default: 0, description: 'Reflex save modifier.' },\n { key: 'will', label: 'Will', kind: 'integer', min: -10, max: 30, default: 0, description: 'Will save modifier.' },\n { key: 'bab', label: 'BAB', kind: 'integer', min: 0, max: 25, default: 0, description: 'Base Attack Bonus.' },\n]\n\n// ============================================================================\n// Rules\n// ============================================================================\n\ninterface D20Params {\n modifier?: number\n}\n\nfunction rollD20({ modifier = 0 }: D20Params): RollResult {\n const d20 = rollDice(20)[0]!\n const total = d20 + modifier\n const notes: string[] = []\n if (d20 === 20) notes.push('20 natural')\n if (d20 === 1) notes.push('1 natural')\n const modStr = modifier === 0 ? '' : modifier > 0 ? `+${modifier}` : `${modifier}`\n return { rolls: [d20], modifier, total, notation: `1d20${modStr}`, notes }\n}\n\ninterface AttackParams {\n modifier?: number\n targetAC?: number\n /** Threshold do crítico (default 20). Armas como rapier 18-20, picareta 20/×4 etc. */\n critRange?: number\n}\n\nfunction rollAttack(params: AttackParams): RollResult {\n const r = rollD20(params)\n const d20 = r.rolls[0]!\n const notes = [...(r.notes ?? [])]\n if (params.targetAC !== undefined) {\n const critRange = params.critRange ?? 20\n const natural20 = d20 === 20\n const natural1 = d20 === 1\n const threat = d20 >= critRange && !natural1\n const hit = natural20 || (!natural1 && r.total >= params.targetAC)\n if (natural20) notes.push('acerto crítico (20 natural)')\n else if (threat) notes.push(`ameaça de crítico (${d20} ≥ ${critRange})`)\n else if (natural1) notes.push('erro automático (1 natural)')\n else notes.push(hit ? 'acertou' : 'errou')\n }\n return { ...r, notes }\n}\n\ninterface SaveParams {\n modifier?: number\n dc: number\n}\n\nfunction rollSave({ modifier = 0, dc }: SaveParams): RollResult {\n const r = rollD20({ modifier })\n const notes = [...(r.notes ?? [])]\n // 1 natural sempre falha; 20 natural sempre passa (regra do SRD 3.5).\n const d20 = r.rolls[0]!\n const auto = d20 === 1 ? false : d20 === 20 ? true : null\n const passed = auto !== null ? auto : r.total >= dc\n notes.push(passed ? 'sucesso' : 'falha')\n notes.push(`DC ${dc}`)\n return { ...r, notes }\n}\n\ninterface DamageParams {\n count: number\n sides: number\n modifier?: number\n /** Multiplica os dados de dano em caso de crítico (×2 default, alguns ×3 ou ×4). */\n critMultiplier?: number\n}\n\nfunction rollDamage({ count, sides, modifier = 0, critMultiplier }: DamageParams): RollResult {\n const m = critMultiplier && critMultiplier > 1 ? Math.trunc(critMultiplier) : 1\n const dice = rollDice(sides, count * m)\n const sum = dice.reduce((a, b) => a + b, 0)\n const total = Math.max(0, sum + modifier * m)\n const modStr = modifier === 0 ? '' : modifier > 0 ? `+${modifier}` : `${modifier}`\n const critStr = m > 1 ? ` (crit ×${m})` : ''\n return {\n rolls: dice,\n modifier: modifier * m,\n total,\n notation: `${count * m}d${sides}${modStr}${critStr}`,\n notes: m > 1 ? [`dano crítico — dados e mod ×${m}`] : [],\n }\n}\n\nconst RULES: SystemRules = {\n roll(kind, params) {\n switch (kind) {\n case 'd20':\n case 'check':\n case 'ability':\n case 'skill':\n return rollD20(params as unknown as D20Params)\n case 'attack':\n return rollAttack(params as unknown as AttackParams)\n case 'save':\n return rollSave(params as unknown as SaveParams)\n case 'damage':\n return rollDamage(params as unknown as DamageParams)\n default:\n return null\n }\n },\n}\n\n// ============================================================================\n// Bundle\n// ============================================================================\n\n/**\n * Hooks pra `@lippelt/srd-npcgen`. 3.5 usa BAB (default do npcgen já trata\n * via D20_MODEL). Cantrips/0-level spells em 3.5 têm dano FIXO (não escalam\n * por nível) — override pra retornar 1 dado sempre.\n *\n * Skill list é a do SRD 3.5; algumas skills (Knowledge, Profession, Craft,\n * Perform) têm múltiplas instâncias na ficha real, mas aqui listamos só os\n * IDs canônicos.\n */\nconst DND35_SKILLS = [\n 'appraise',\n 'balance',\n 'bluff',\n 'climb',\n 'concentration',\n 'craft',\n 'decipher-script',\n 'diplomacy',\n 'disable-device',\n 'disguise',\n 'escape-artist',\n 'forgery',\n 'gather-information',\n 'handle-animal',\n 'heal',\n 'hide',\n 'intimidate',\n 'jump',\n 'knowledge',\n 'listen',\n 'move-silently',\n 'open-lock',\n 'perform',\n 'profession',\n 'ride',\n 'search',\n 'sense-motive',\n 'sleight-of-hand',\n 'speak-language',\n 'spellcraft',\n 'spot',\n 'survival',\n 'swim',\n 'tumble',\n 'use-magic-device',\n 'use-rope',\n] as const\n\nexport const dnd35: System = {\n id: 'dnd-3.5',\n name: 'Dungeons & Dragons 3.5',\n ruleVersion: 'SRD 3.5',\n attribution:\n 'Contains material from the System Reference Document 3.5 by Wizards of the Coast LLC under the Open Game License v1.0a.',\n dicePresets: DICE_PRESETS,\n conditions: CONDITIONS,\n trackerFields: TRACKER_FIELDS,\n rules: RULES,\n npc: {\n cantripDamageDice: () => 1,\n skills: DND35_SKILLS,\n },\n}\n"],"mappings":"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","names":["abilityMod"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lippelt/srd-dnd-3.5",
3
- "version": "0.1.1",
3
+ "version": "1.0.0",
4
4
  "description": "D&D 3.5 system module for @lippelt/srd-core — SRD 3.5, OGL 1.0a",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -16,7 +16,8 @@
16
16
  "files": [
17
17
  "dist",
18
18
  "README.md",
19
- "LICENSE"
19
+ "LICENSE",
20
+ "CHANGELOG.md"
20
21
  ],
21
22
  "publishConfig": {
22
23
  "access": "public"
@@ -36,7 +37,7 @@
36
37
  "directory": "packages/dnd-3.5"
37
38
  },
38
39
  "peerDependencies": {
39
- "@lippelt/srd-core": "^0.1.0"
40
+ "@lippelt/srd-core": "^1.0.0"
40
41
  },
41
42
  "devDependencies": {
42
43
  "@lippelt/srd-core": "*"