verseluft-dnd-library 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +35 -0
- package/docs/README.md +10 -0
- package/docs/ai/README.md +25 -0
- package/docs/ai/api/index.md +13 -0
- package/docs/ai/api/tools/dice.md +52 -0
- package/docs/ai/changelog.md +13 -0
- package/docs/ai/overview.md +23 -0
- package/docs/human/README.md +14 -0
- package/docs/human/api/index.md +13 -0
- package/docs/human/api/tools/dice.md +95 -0
- package/docs/human/changelog.md +13 -0
- package/docs/human/overview.md +25 -0
- package/package.json +35 -0
- package/src/content/starterContent.js +28 -0
- package/src/core/createDnDLibrary.js +17 -0
- package/src/index.js +7 -0
- package/src/modules/classes.js +20 -0
- package/src/modules/items.js +22 -0
- package/src/modules/monsters.js +24 -0
- package/src/modules/spells.js +22 -0
- package/src/tools/dice.js +35 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Verseluft
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Verseluft DnD Library
|
|
2
|
+
|
|
3
|
+
A small JavaScript library for DnD-style apps and websites.
|
|
4
|
+
|
|
5
|
+
## What is inside
|
|
6
|
+
|
|
7
|
+
- `core` for shared library setup
|
|
8
|
+
- `modules` for game content such as spells, monsters, items, and classes
|
|
9
|
+
- `tools` for reusable gameplay helpers such as dice rolling
|
|
10
|
+
- `content` for starter data that other apps can extend
|
|
11
|
+
|
|
12
|
+
## Documentation
|
|
13
|
+
|
|
14
|
+
- [Human docs](./docs/human/README.md)
|
|
15
|
+
- [AI docs](./docs/ai/README.md)
|
|
16
|
+
- [Dice tool](./docs/human/api/tools/dice.md)
|
|
17
|
+
- The published npm package includes both doc sets
|
|
18
|
+
- Build the standalone wiki with `npm run wiki:build`
|
|
19
|
+
|
|
20
|
+
## Example
|
|
21
|
+
|
|
22
|
+
```js
|
|
23
|
+
import { createDnDLibrary, rollDice } from "./src/index.js";
|
|
24
|
+
|
|
25
|
+
const lib = createDnDLibrary({
|
|
26
|
+
name: "My Campaign Toolkit"
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
console.log(lib.info());
|
|
30
|
+
console.log(rollDice("2d6+3"));
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Next steps
|
|
34
|
+
|
|
35
|
+
This scaffold is ready to grow into a real package with richer rules, more content packs, and optional UI helpers.
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Documentation Root
|
|
2
|
+
|
|
3
|
+
Choose the version that fits your needs:
|
|
4
|
+
|
|
5
|
+
- [Human docs](./human/README.md)
|
|
6
|
+
- [AI docs](./ai/README.md)
|
|
7
|
+
|
|
8
|
+
The published npm package includes both doc sets.
|
|
9
|
+
The standalone wiki is generated from the human docs only.
|
|
10
|
+
Run `npm run wiki:build` to create it in `wiki-site/`.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# AI Documentation
|
|
2
|
+
|
|
3
|
+
This folder is the AI-facing version of the docs.
|
|
4
|
+
|
|
5
|
+
Use it when you need:
|
|
6
|
+
|
|
7
|
+
- Precise edit boundaries
|
|
8
|
+
- Implementation notes
|
|
9
|
+
- Safer file-by-file updates
|
|
10
|
+
|
|
11
|
+
Start here:
|
|
12
|
+
|
|
13
|
+
- [Overview](./overview.md)
|
|
14
|
+
- [Changelog](./changelog.md)
|
|
15
|
+
- [API Index](./api/index.md)
|
|
16
|
+
|
|
17
|
+
Scope:
|
|
18
|
+
|
|
19
|
+
- Dice tool only
|
|
20
|
+
|
|
21
|
+
Rules:
|
|
22
|
+
|
|
23
|
+
- Update one tool file at a time
|
|
24
|
+
- Do not touch unrelated docs
|
|
25
|
+
- If the API changes, update the matching human docs too
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# AI API Index
|
|
2
|
+
|
|
3
|
+
Only the dice tool is in scope for now.
|
|
4
|
+
|
|
5
|
+
## Tools
|
|
6
|
+
|
|
7
|
+
- [Dice tool](./tools/dice.md)
|
|
8
|
+
|
|
9
|
+
## Agent rules
|
|
10
|
+
|
|
11
|
+
- If you are editing dice behavior, edit only the dice doc first
|
|
12
|
+
- If the public export changes, update this index and the changelog
|
|
13
|
+
- Do not expand scope to new tools unless the task explicitly asks for it
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# AI Dice Tool Reference
|
|
2
|
+
|
|
3
|
+
This file is the AI-specific reference for the dice tool.
|
|
4
|
+
|
|
5
|
+
## Exported functions
|
|
6
|
+
|
|
7
|
+
- `parseDiceExpression(expression)`
|
|
8
|
+
- `rollDice(expression, randomFn?)`
|
|
9
|
+
- `averageRoll(expression)`
|
|
10
|
+
|
|
11
|
+
## Source file
|
|
12
|
+
|
|
13
|
+
- [src/tools/dice.js](/D:/Projects/Verseluft-Core/src/tools/dice.js)
|
|
14
|
+
|
|
15
|
+
## Behavior summary
|
|
16
|
+
|
|
17
|
+
### `parseDiceExpression(expression)`
|
|
18
|
+
- Accepts strings that match `NdM` or `NdM+K` / `NdM-K`
|
|
19
|
+
- If `N` is omitted, it defaults to `1`
|
|
20
|
+
- Returns `{ count, sides, modifier }`
|
|
21
|
+
- Throws `Error` for invalid expressions
|
|
22
|
+
|
|
23
|
+
### `rollDice(expression, randomFn?)`
|
|
24
|
+
- Parses the expression with `parseDiceExpression`
|
|
25
|
+
- Rolls `count` times using `randomFn`, defaulting to `Math.random`
|
|
26
|
+
- Returns `{ expression, rolls, modifier, total }`
|
|
27
|
+
- `total` includes the modifier
|
|
28
|
+
|
|
29
|
+
### `averageRoll(expression)`
|
|
30
|
+
- Returns the mathematical average of the expression
|
|
31
|
+
- Uses the formula `count * ((sides + 1) / 2) + modifier`
|
|
32
|
+
|
|
33
|
+
## Examples
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
import { parseDiceExpression, rollDice, averageRoll } from "verseluft-dnd-library";
|
|
37
|
+
|
|
38
|
+
parseDiceExpression("2d6+3");
|
|
39
|
+
rollDice("2d6+3");
|
|
40
|
+
averageRoll("2d6+3");
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Test guidance
|
|
44
|
+
|
|
45
|
+
- For deterministic tests, pass a custom `randomFn`
|
|
46
|
+
- Do not compare against random outputs without controlling the source
|
|
47
|
+
|
|
48
|
+
## Edit boundaries
|
|
49
|
+
|
|
50
|
+
- If dice logic changes, update this file and the human dice doc together
|
|
51
|
+
- Do not modify unrelated docs when editing the dice tool
|
|
52
|
+
- If the export list changes, update `src/index.js` and both API index files
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# AI Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
- Initial scaffold
|
|
6
|
+
- Dice parsing and rolling utilities added
|
|
7
|
+
- Starter content and tests added
|
|
8
|
+
- Standalone wiki generator added
|
|
9
|
+
|
|
10
|
+
AI editing notes:
|
|
11
|
+
|
|
12
|
+
- Keep changelog entries short and factual
|
|
13
|
+
- When behavior changes, mention the exact file and export affected
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# AI Overview
|
|
2
|
+
|
|
3
|
+
Project: Verseluft DnD Library
|
|
4
|
+
|
|
5
|
+
Purpose:
|
|
6
|
+
|
|
7
|
+
- Small JavaScript toolkit for DnD-style apps and websites
|
|
8
|
+
- Current scope is only the dice tool
|
|
9
|
+
|
|
10
|
+
Agent guidance:
|
|
11
|
+
|
|
12
|
+
- Use the matching AI tool doc for implementation details
|
|
13
|
+
- Do not infer behavior from unrelated files
|
|
14
|
+
- Keep edits narrow and file-specific
|
|
15
|
+
|
|
16
|
+
Documentation map:
|
|
17
|
+
|
|
18
|
+
- `docs/ai/overview.md`
|
|
19
|
+
- `docs/ai/changelog.md`
|
|
20
|
+
- `docs/ai/api/index.md`
|
|
21
|
+
- `docs/ai/api/tools/dice.md`
|
|
22
|
+
|
|
23
|
+
Do not add new tool docs without explicit scope changes.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Documentation
|
|
2
|
+
|
|
3
|
+
Start here:
|
|
4
|
+
|
|
5
|
+
- [Overview](./overview.md)
|
|
6
|
+
- [Changelog](./changelog.md)
|
|
7
|
+
- [API Index](./api/index.md)
|
|
8
|
+
|
|
9
|
+
Current scope:
|
|
10
|
+
|
|
11
|
+
- Dice tool only
|
|
12
|
+
|
|
13
|
+
To generate the standalone wiki site, run `npm run wiki:build`.
|
|
14
|
+
The built HTML files will appear in `wiki-site/`.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# API Index
|
|
2
|
+
|
|
3
|
+
This page is the central map for the library documentation.
|
|
4
|
+
|
|
5
|
+
## Tools
|
|
6
|
+
|
|
7
|
+
- [Dice tool](./tools/dice.md)
|
|
8
|
+
|
|
9
|
+
## Notes for editors
|
|
10
|
+
|
|
11
|
+
- Update only the matching tool file when changing one tool
|
|
12
|
+
- Keep cross-tool changes rare and explicit
|
|
13
|
+
- Add new tools as new Markdown files instead of mixing them together
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Dice Tool
|
|
2
|
+
|
|
3
|
+
The dice tool parses and rolls dice expressions for tabletop and game-style applications.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
Use this tool to:
|
|
8
|
+
|
|
9
|
+
- Parse expressions like `2d6+3`
|
|
10
|
+
- Roll dice with a deterministic or random source
|
|
11
|
+
- Estimate the average result of a dice expression
|
|
12
|
+
|
|
13
|
+
## Exports
|
|
14
|
+
|
|
15
|
+
- `parseDiceExpression(expression)`
|
|
16
|
+
- `rollDice(expression, randomFn?)`
|
|
17
|
+
- `averageRoll(expression)`
|
|
18
|
+
|
|
19
|
+
## `parseDiceExpression(expression)`
|
|
20
|
+
|
|
21
|
+
Parses a dice expression and returns a structured object.
|
|
22
|
+
|
|
23
|
+
### Input
|
|
24
|
+
|
|
25
|
+
- `expression`: a string such as `d20`, `2d6`, or `4d8+2`
|
|
26
|
+
|
|
27
|
+
### Output
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
{
|
|
31
|
+
count: number,
|
|
32
|
+
sides: number,
|
|
33
|
+
modifier: number
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Example
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
import { parseDiceExpression } from "verseluft-dnd-library";
|
|
41
|
+
|
|
42
|
+
const dice = parseDiceExpression("2d6+3");
|
|
43
|
+
// { count: 2, sides: 6, modifier: 3 }
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## `rollDice(expression, randomFn?)`
|
|
47
|
+
|
|
48
|
+
Rolls dice and returns the individual rolls plus the total.
|
|
49
|
+
|
|
50
|
+
### Input
|
|
51
|
+
|
|
52
|
+
- `expression`: a dice expression string
|
|
53
|
+
- `randomFn`: optional random function, defaults to `Math.random`
|
|
54
|
+
|
|
55
|
+
### Output
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
{
|
|
59
|
+
expression: string,
|
|
60
|
+
rolls: number[],
|
|
61
|
+
modifier: number,
|
|
62
|
+
total: number
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Example
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
import { rollDice } from "verseluft-dnd-library";
|
|
70
|
+
|
|
71
|
+
const result = rollDice("2d6+3");
|
|
72
|
+
// { expression: "2d6+3", rolls: [4, 2], modifier: 3, total: 9 }
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Testing tip
|
|
76
|
+
|
|
77
|
+
Pass a custom `randomFn` when you want repeatable tests.
|
|
78
|
+
|
|
79
|
+
## `averageRoll(expression)`
|
|
80
|
+
|
|
81
|
+
Returns the mathematical average for the expression.
|
|
82
|
+
|
|
83
|
+
### Example
|
|
84
|
+
|
|
85
|
+
```js
|
|
86
|
+
import { averageRoll } from "verseluft-dnd-library";
|
|
87
|
+
|
|
88
|
+
averageRoll("2d6+3");
|
|
89
|
+
// 10
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Editing rule
|
|
93
|
+
|
|
94
|
+
If you change the dice tool behavior, update only this file first.
|
|
95
|
+
If the public API changes, then update the central index and changelog too.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
- Initial library scaffold
|
|
6
|
+
- Added dice parsing and rolling helpers
|
|
7
|
+
- Added starter content modules
|
|
8
|
+
- Added package tests and a browser playground
|
|
9
|
+
|
|
10
|
+
## Documentation notes
|
|
11
|
+
|
|
12
|
+
- The docs are organized by topic so AI edits can stay isolated
|
|
13
|
+
- Dice is the only documented tool in the first pass
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Verseluft DnD Library
|
|
2
|
+
|
|
3
|
+
Verseluft DnD Library is a small JavaScript toolkit for DnD-style apps and websites.
|
|
4
|
+
|
|
5
|
+
Right now, this documentation set focuses on a single tool:
|
|
6
|
+
|
|
7
|
+
- Dice tools
|
|
8
|
+
|
|
9
|
+
## What this docs set is for
|
|
10
|
+
|
|
11
|
+
- Human readers who want clear usage guidance
|
|
12
|
+
- AI readers who need a stable, file-based reference
|
|
13
|
+
- Safe editing, where each tool has its own isolated page
|
|
14
|
+
|
|
15
|
+
## Documentation layout
|
|
16
|
+
|
|
17
|
+
- `docs/human/overview.md` for the big picture
|
|
18
|
+
- `docs/human/changelog.md` for version notes
|
|
19
|
+
- `docs/human/api/index.md` for the API map
|
|
20
|
+
- `docs/human/api/tools/dice.md` for the dice tool
|
|
21
|
+
|
|
22
|
+
## Current scope
|
|
23
|
+
|
|
24
|
+
This docs set intentionally covers only the dice tool for now.
|
|
25
|
+
Other tools and modules can be added later as separate files.
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "verseluft-dnd-library",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A small DnD utility library with reusable content modules and dice tools for apps and websites.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"dnd",
|
|
8
|
+
"rpg",
|
|
9
|
+
"library",
|
|
10
|
+
"javascript",
|
|
11
|
+
"browser"
|
|
12
|
+
],
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "./src/index.js",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": "./src/index.js",
|
|
17
|
+
"./modules/*": "./src/modules/*.js",
|
|
18
|
+
"./tools/*": "./src/tools/*.js",
|
|
19
|
+
"./content/*": "./src/content/*.js"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"src",
|
|
23
|
+
"docs",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=20"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"test": "node --test test/library.test.js",
|
|
32
|
+
"test:watch": "node --test --watch test/library.test.js",
|
|
33
|
+
"wiki:build": "node scripts/build-wiki.mjs"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export const starterContent = {
|
|
2
|
+
monsters: [
|
|
3
|
+
{
|
|
4
|
+
id: "goblin",
|
|
5
|
+
name: "Goblin",
|
|
6
|
+
challengeRating: 0.25,
|
|
7
|
+
},
|
|
8
|
+
],
|
|
9
|
+
spells: [
|
|
10
|
+
{
|
|
11
|
+
id: "magic-missile",
|
|
12
|
+
name: "Magic Missile",
|
|
13
|
+
level: 1,
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
items: [
|
|
17
|
+
{
|
|
18
|
+
id: "healing-potion",
|
|
19
|
+
name: "Healing Potion",
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
classes: [
|
|
23
|
+
{
|
|
24
|
+
id: "fighter",
|
|
25
|
+
name: "Fighter",
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { starterContent } from "../content/starterContent.js";
|
|
2
|
+
|
|
3
|
+
export function createDnDLibrary(options = {}) {
|
|
4
|
+
const libraryName = options.name ?? "DnD Library";
|
|
5
|
+
const content = options.content ?? starterContent;
|
|
6
|
+
|
|
7
|
+
return {
|
|
8
|
+
name: libraryName,
|
|
9
|
+
content,
|
|
10
|
+
info() {
|
|
11
|
+
return {
|
|
12
|
+
name: libraryName,
|
|
13
|
+
modules: Object.keys(content),
|
|
14
|
+
};
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { createDnDLibrary } from "./core/createDnDLibrary.js";
|
|
2
|
+
export { rollDice, parseDiceExpression, averageRoll } from "./tools/dice.js";
|
|
3
|
+
export { getMonsterById, listMonsters } from "./modules/monsters.js";
|
|
4
|
+
export { getSpellById, listSpells } from "./modules/spells.js";
|
|
5
|
+
export { getItemById, listItems } from "./modules/items.js";
|
|
6
|
+
export { getClassById, listClasses } from "./modules/classes.js";
|
|
7
|
+
export { starterContent } from "./content/starterContent.js";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const classes = [
|
|
2
|
+
{
|
|
3
|
+
id: "fighter",
|
|
4
|
+
name: "Fighter",
|
|
5
|
+
primaryAbility: "strength",
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
id: "wizard",
|
|
9
|
+
name: "Wizard",
|
|
10
|
+
primaryAbility: "intelligence",
|
|
11
|
+
},
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
export function listClasses() {
|
|
15
|
+
return [...classes];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getClassById(id) {
|
|
19
|
+
return classes.find((characterClass) => characterClass.id === id) ?? null;
|
|
20
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const items = [
|
|
2
|
+
{
|
|
3
|
+
id: "healing-potion",
|
|
4
|
+
name: "Healing Potion",
|
|
5
|
+
rarity: "common",
|
|
6
|
+
kind: "consumable",
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
id: "longsword",
|
|
10
|
+
name: "Longsword",
|
|
11
|
+
rarity: "common",
|
|
12
|
+
kind: "weapon",
|
|
13
|
+
},
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
export function listItems() {
|
|
17
|
+
return [...items];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getItemById(id) {
|
|
21
|
+
return items.find((item) => item.id === id) ?? null;
|
|
22
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const monsters = [
|
|
2
|
+
{
|
|
3
|
+
id: "goblin",
|
|
4
|
+
name: "Goblin",
|
|
5
|
+
type: "humanoid",
|
|
6
|
+
challengeRating: 0.25,
|
|
7
|
+
tags: ["small", "sneaky", "cave"],
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
id: "young-dragon",
|
|
11
|
+
name: "Young Dragon",
|
|
12
|
+
type: "dragon",
|
|
13
|
+
challengeRating: 6,
|
|
14
|
+
tags: ["flying", "boss", "fire"],
|
|
15
|
+
},
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export function listMonsters() {
|
|
19
|
+
return [...monsters];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getMonsterById(id) {
|
|
23
|
+
return monsters.find((monster) => monster.id === id) ?? null;
|
|
24
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const spells = [
|
|
2
|
+
{
|
|
3
|
+
id: "magic-missile",
|
|
4
|
+
name: "Magic Missile",
|
|
5
|
+
level: 1,
|
|
6
|
+
school: "evocation",
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
id: "fireball",
|
|
10
|
+
name: "Fireball",
|
|
11
|
+
level: 3,
|
|
12
|
+
school: "evocation",
|
|
13
|
+
},
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
export function listSpells() {
|
|
17
|
+
return [...spells];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getSpellById(id) {
|
|
21
|
+
return spells.find((spell) => spell.id === id) ?? null;
|
|
22
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const DICE_PATTERN = /^(\d*)d(\d+)([+-]\d+)?$/i;
|
|
2
|
+
|
|
3
|
+
export function parseDiceExpression(expression) {
|
|
4
|
+
const cleaned = String(expression).trim();
|
|
5
|
+
const match = cleaned.match(DICE_PATTERN);
|
|
6
|
+
|
|
7
|
+
if (!match) {
|
|
8
|
+
throw new Error(`Invalid dice expression: ${expression}`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const count = match[1] ? Number(match[1]) : 1;
|
|
12
|
+
const sides = Number(match[2]);
|
|
13
|
+
const modifier = match[3] ? Number(match[3]) : 0;
|
|
14
|
+
|
|
15
|
+
return { count, sides, modifier };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function rollDice(expression, randomFn = Math.random) {
|
|
19
|
+
const { count, sides, modifier } = parseDiceExpression(expression);
|
|
20
|
+
let total = modifier;
|
|
21
|
+
const rolls = [];
|
|
22
|
+
|
|
23
|
+
for (let index = 0; index < count; index += 1) {
|
|
24
|
+
const roll = Math.floor(randomFn() * sides) + 1;
|
|
25
|
+
rolls.push(roll);
|
|
26
|
+
total += roll;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return { expression, rolls, modifier, total };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function averageRoll(expression) {
|
|
33
|
+
const { count, sides, modifier } = parseDiceExpression(expression);
|
|
34
|
+
return count * ((sides + 1) / 2) + modifier;
|
|
35
|
+
}
|