@swrpg-online/dice 1.1.0 → 1.2.1

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 CHANGED
@@ -5,27 +5,73 @@
5
5
  [![codecov](https://codecov.io/gh/swrpg-online/dice/graph/badge.svg?token=BQIFNBWKI8)](https://codecov.io/gh/swrpg-online/dice)
6
6
  [![semantic-release: angular](https://img.shields.io/badge/semantic--release-angular-e10079?logo=semantic-release)](https://github.com/semantic-release/semantic-release)
7
7
 
8
- A TypeScript library that creates dice rolls using the [narrative dice system](https://star-wars-rpg-ffg.fandom.com/wiki/Narrative_Dice) for the Star Wars Role-Playing Game by [Fantasy Flight Games](https://www.fantasyflightgames.com/en/starwarsrpg/) and [Edge Studio](https://www.edge-studio.net/categories-games/starwarsrpg/).
8
+ A TypeScript library that creates dice rolls using the [narrative dice system](https://star-wars-rpg-ffg.fandom.com/wiki/Narrative_Dice) for the Star Wars Roleplaying Game by [Fantasy Flight Games](https://www.fantasyflightgames.com/en/starwarsrpg/) and [Edge Studio](https://www.edge-studio.net/categories-games/starwarsrpg/).
9
+
10
+ ## Features
11
+
12
+ - Complete narrative dice system implementation
13
+ - Detailed roll breakdown for each die
14
+ - Action hints to suggest possible uses for advantages, triumphs, etc.
15
+ - Roll results include:
16
+ - Successes / Failures
17
+ - Advantages / Threats
18
+ - Triumphs / Despairs
19
+ - Light / Dark Side Points (Force dice)
20
+ - Comprehensive Test Coverage
21
+ - The safety of TypeScript
22
+ - CLI Support
9
23
 
10
24
  ## Installation
11
25
 
26
+ ### As a CLI Tool
27
+
28
+ To use the dice roller from the command line:
29
+
30
+ ```bash
31
+ npm i -g @swrpg-online/dice
32
+ ```
33
+
34
+ ### As a project dependency
35
+
12
36
  ```bash
13
37
  npm i @swrpg-online/dice
14
38
  ```
15
39
 
16
- or optionally to use as a CLI command you can install globally with `npm i -g @swrpg-online/dice`
40
+ ## CLI Usage
17
41
 
18
- ## Features
42
+ ```
43
+ swrpg-dice <dice-notation> [options]
44
+ ```
19
45
 
20
- - Complete narrative dice system implementation
21
- - Detailed roll breakdown for each die
22
- - Comprehensive test coverage
23
- - TypeScript type safety
24
- - roll from a CLI
46
+ Example:
25
47
 
26
- ## Usage
48
+ ```
49
+ swrpg-dice 2y 1g 1p 1b 1sb --hints
50
+ ```
51
+
52
+ Output:
27
53
 
28
- via code:
54
+ ```
55
+ 1 Success(es), 4 Advantage(s), 1 Threat(s)
56
+
57
+ Possible actions:
58
+ • 1 Advantage or 1 Triumph - Recover one strain (may be applied more than once).
59
+ • 1 Advantage or 1 Triumph - Add a boost die to the next allied active character's check.
60
+ • 1 Advantage or 1 Triumph - Notice a single important point in the ongoing conflict, such as the location of a blast door's control panel or
61
+ ...
62
+ ```
63
+
64
+ Dice Options:
65
+
66
+ - y/pro = Yellow / Proficiency
67
+ - g/a = Green / Ability
68
+ - b/boo = Blue / Boost
69
+ - r/c = Red / Challenge
70
+ - p/diff = Purple / Difficulty
71
+ - blk/k/sb/s = Black / Setback
72
+ - w/f = White / Force
73
+
74
+ ## Programmatic Usage
29
75
 
30
76
  ```typescript
31
77
  import { roll, DicePool } from '@swrpg-online/dice';
@@ -39,9 +85,8 @@ const pool: DicePool = {
39
85
 
40
86
  const result = roll(pool);
41
87
 
42
- // Access detailed results
43
- console.log(result.results); // Array of individual die results
44
- console.log(result.summary); // Summary of total successes, advantages, etc.
88
+ console.log(result.results);
89
+ console.log(result.summary);
45
90
 
46
91
  => {
47
92
  "results": [
@@ -57,18 +102,6 @@ console.log(result.summary); // Summary of total successes, advantages, etc.
57
102
  "despair": 0
58
103
  }
59
104
  },
60
- {
61
- "type": "ability",
62
- "roll": 3,
63
- "result": {
64
- "successes": 1,
65
- "failures": 0,
66
- "advantages": 0,
67
- "threats": 0,
68
- "triumphs": 0,
69
- "despair": 0
70
- }
71
- },
72
105
  {
73
106
  "type": "proficiency",
74
107
  "roll": 10,
@@ -81,30 +114,7 @@ console.log(result.summary); // Summary of total successes, advantages, etc.
81
114
  "despair": 0
82
115
  }
83
116
  },
84
- {
85
- "type": "difficulty",
86
- "roll": 2,
87
- "result": {
88
- "successes": 0,
89
- "failures": 1,
90
- "advantages": 0,
91
- "threats": 0,
92
- "triumphs": 0,
93
- "despair": 0
94
- }
95
- },
96
- {
97
- "type": "challenge",
98
- "roll": 11,
99
- "result": {
100
- "successes": 0,
101
- "failures": 0,
102
- "advantages": 0,
103
- "threats": 2,
104
- "triumphs": 0,
105
- "despair": 0
106
- }
107
- }
117
+ ...
108
118
  ],
109
119
  "summary": {
110
120
  "successes": 0,
@@ -117,21 +127,9 @@ console.log(result.summary); // Summary of total successes, advantages, etc.
117
127
  }
118
128
  ```
119
129
 
120
- Each roll result includes:
121
-
122
- - Detailed breakdown of each die roll
123
- - Die type identification
124
- - Individual results per die
125
- - Overall summary of the roll
126
-
127
- # Roadmap or Under Review
130
+ # License
128
131
 
129
- - implement the Force die
130
- - implement ability to add success, failure, and so on to dice pools
131
- - ship combat?
132
- - crits?
133
- - polyhedral dice for convenience?
134
- - anything else?
132
+ This project is licensed under the MIT License.
135
133
 
136
134
  # Contribution
137
135
 
package/dist/cli.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { DicePool } from "./types";
2
+ import { DicePool, RollResult } from "./types";
3
3
  export declare function parseDiceNotation(input: string): DicePool;
4
- export declare function formatResult(result: any): string;
4
+ export declare const formatResult: (result: RollResult) => string;
5
5
  export declare const main: () => void;
package/dist/cli.js CHANGED
@@ -1,9 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.main = void 0;
4
+ exports.main = exports.formatResult = void 0;
5
5
  exports.parseDiceNotation = parseDiceNotation;
6
- exports.formatResult = formatResult;
7
6
  const dice_1 = require("./dice");
8
7
  // import * as path from 'path';
9
8
  function parseDiceNotation(input) {
@@ -16,25 +15,6 @@ function parseDiceNotation(input) {
16
15
  challengeDice: 0,
17
16
  forceDice: 0,
18
17
  };
19
- // function getImagePath(type: string): string {
20
- // const basePath = path.join(__dirname, 'images');
21
- // switch (type) {
22
- // case 'successes':
23
- // return path.join(basePath, 'success.svg'); // Adjust path and extension as needed
24
- // case 'failures':
25
- // return path.join(basePath, 'failure.svg');
26
- // case 'advantages':
27
- // return path.join(basePath, 'advantage.svg');
28
- // case 'threats':
29
- // return path.join(basePath, 'threat.svg');
30
- // case 'triumphs':
31
- // return path.join(basePath, 'triumph.svg');
32
- // case 'despair':
33
- // return path.join(basePath, 'despair.svg');
34
- // default:
35
- // return '';
36
- // }
37
- // }
38
18
  const parts = input.toLowerCase().trim().split(" ");
39
19
  for (const part of parts) {
40
20
  const count = parseInt(part);
@@ -99,30 +79,38 @@ function parseDiceNotation(input) {
99
79
  }
100
80
  return pool;
101
81
  }
102
- function formatResult(result) {
103
- const parts = [];
82
+ const formatResult = (result) => {
83
+ const effects = [];
104
84
  if (result.summary.successes > 0)
105
- parts.push(`${result.summary.successes} Success(es)`);
85
+ effects.push(`${result.summary.successes} Success(es)`);
106
86
  if (result.summary.failures > 0)
107
- parts.push(`${result.summary.failures} Failure(s)`);
87
+ effects.push(`${result.summary.failures} Failure(s)`);
108
88
  if (result.summary.advantages > 0)
109
- parts.push(`${result.summary.advantages} Advantage(s)`);
89
+ effects.push(`${result.summary.advantages} Advantage(s)`);
110
90
  if (result.summary.threats > 0)
111
- parts.push(`${result.summary.threats} Threat(s)`);
91
+ effects.push(`${result.summary.threats} Threat(s)`);
112
92
  if (result.summary.triumphs > 0)
113
- parts.push(`${result.summary.triumphs} Triumph(s)`);
93
+ effects.push(`${result.summary.triumphs} Triumph(s)`);
114
94
  if (result.summary.despair > 0)
115
- parts.push(`${result.summary.despair} Despair(s)`);
116
- if (result.summary.lightSide > 0)
117
- parts.push(`${result.summary.lightSide} Light Side(s)`);
118
- if (result.summary.darkSide > 0)
119
- parts.push(`${result.summary.darkSide} Dark Side(s)`);
120
- return parts.join(", ") || "No effects";
121
- }
95
+ effects.push(`${result.summary.despair} Despair(s)`);
96
+ const resultText = effects.length > 0 ? effects.join(", ") : "No effects";
97
+ if (result.summary.hints && result.summary.hints.length > 0) {
98
+ return `${resultText}\n\nPossible actions:\n${result.summary.hints.map((hint) => " • " + hint).join("\n")}`;
99
+ }
100
+ return resultText;
101
+ };
102
+ exports.formatResult = formatResult;
122
103
  const main = () => {
123
- const input = process.argv.slice(2).join(" ");
124
- if (!input) {
125
- console.log(`Usage: > swrpg-dice 2y 1g 2p 1r
104
+ const args = process.argv.slice(2);
105
+ const hintsIndex = args.indexOf("--hints");
106
+ const showHints = hintsIndex !== -1;
107
+ const diceNotation = hintsIndex !== -1
108
+ ? args.filter((_, index) => index !== hintsIndex).join(" ")
109
+ : args.join(" ");
110
+ if (!diceNotation.trim()) {
111
+ console.log(`Usage: swrpg-dice <dice-notation> <dice-options>
112
+ Example: swrpg-dice 2y 1g 1p 1b 1sb --hints
113
+ Dice Options:
126
114
  - y/pro = Yellow / Proficiency
127
115
  - g/a = Green / Ability
128
116
  - b/boo = Blue / Boost
@@ -130,12 +118,13 @@ const main = () => {
130
118
  - p/diff = Purple / Difficulty
131
119
  - blk/k/sb/s = Black / Setback
132
120
  - w/f = White/Force
133
- `);
121
+ Options:
122
+ --hints Show possible actions based on roll results`);
134
123
  process.exit(1);
135
124
  }
136
- const pool = parseDiceNotation(input);
137
- const result = (0, dice_1.roll)(pool);
138
- console.log(formatResult(result));
125
+ const pool = parseDiceNotation(diceNotation);
126
+ const result = (0, dice_1.roll)(pool, { hints: showHints });
127
+ console.log((0, exports.formatResult)(result));
139
128
  };
140
129
  exports.main = main;
141
130
  if (require.main === module) {
package/dist/dice.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { DicePool, RollResult } from "./types";
2
- export declare const roll: (pool: DicePool) => RollResult;
1
+ import { DicePool, RollResult, RollOptions } from "./types";
2
+ export declare const roll: (pool: DicePool, options?: RollOptions) => RollResult;
package/dist/dice.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.roll = void 0;
4
+ const hints_1 = require("./hints");
4
5
  const rollDie = (sides) => Math.floor(Math.random() * sides) + 1;
5
6
  const boostDieResult = (roll) => {
6
7
  switch (roll) {
@@ -483,7 +484,7 @@ const forceDieResult = (roll) => {
483
484
  };
484
485
  }
485
486
  };
486
- const sumResults = (results) => {
487
+ const sumResults = (results, options) => {
487
488
  const sums = results.reduce((acc, curr) => ({
488
489
  successes: acc.successes + curr.successes,
489
490
  failures: acc.failures + curr.failures,
@@ -503,7 +504,6 @@ const sumResults = (results) => {
503
504
  lightSide: 0,
504
505
  darkSide: 0,
505
506
  });
506
- // Calculate net successes/failures
507
507
  let netSuccesses = 0;
508
508
  let netFailures = 0;
509
509
  if (sums.successes === sums.failures) {
@@ -516,7 +516,7 @@ const sumResults = (results) => {
516
516
  else {
517
517
  netFailures = sums.failures - sums.successes;
518
518
  }
519
- return {
519
+ const result = {
520
520
  successes: netSuccesses,
521
521
  failures: netFailures,
522
522
  advantages: sums.advantages,
@@ -526,10 +526,10 @@ const sumResults = (results) => {
526
526
  lightSide: sums.lightSide,
527
527
  darkSide: sums.darkSide,
528
528
  };
529
+ return result;
529
530
  };
530
- const roll = (pool) => {
531
+ const roll = (pool, options) => {
531
532
  var _a, _b, _c, _d, _e, _f, _g;
532
- // Initialize all dice counts to 0 if undefined
533
533
  const boostCount = (_a = pool.boostDice) !== null && _a !== void 0 ? _a : 0;
534
534
  const abilityCount = (_b = pool.abilityDice) !== null && _b !== void 0 ? _b : 0;
535
535
  const proficiencyCount = (_c = pool.proficiencyDice) !== null && _c !== void 0 ? _c : 0;
@@ -611,9 +611,23 @@ const roll = (pool) => {
611
611
  result: forceDieResult(roll),
612
612
  });
613
613
  }
614
+ const summary = sumResults(detailedResults.map((r) => r.result));
615
+ if (options === null || options === void 0 ? void 0 : options.hints) {
616
+ const applicableHints = hints_1.hints.filter((hint) => {
617
+ const { cost } = hint;
618
+ return Object.entries(cost).some(([symbol, required]) => {
619
+ const summaryKey = (symbol.toLowerCase() + "s");
620
+ const value = summary[summaryKey];
621
+ if (typeof value !== "number")
622
+ return false;
623
+ return required <= value;
624
+ });
625
+ });
626
+ summary.hints = applicableHints.map((hint) => `${(0, hints_1.hintCostDisplayText)(hint)} - ${hint.description}`);
627
+ }
614
628
  return {
615
629
  results: detailedResults,
616
- summary: sumResults(detailedResults.map((r) => r.result)),
630
+ summary: summary,
617
631
  };
618
632
  };
619
633
  exports.roll = roll;
@@ -0,0 +1,11 @@
1
+ import { type Symbol } from "./types";
2
+ export type CostType = {
3
+ [key in Symbol]?: number;
4
+ };
5
+ type Hint = {
6
+ description: string;
7
+ cost: CostType;
8
+ };
9
+ export declare const hints: Hint[];
10
+ export declare function hintCostDisplayText(hint: Hint): string;
11
+ export {};
package/dist/hints.js ADDED
@@ -0,0 +1,246 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hints = void 0;
4
+ exports.hintCostDisplayText = hintCostDisplayText;
5
+ const types_1 = require("./types");
6
+ // 1 advantage or 1 triumph
7
+ const recoverOneStrain = "Recover one strain (may be applied more than once).";
8
+ const addBoostDieToActiveAlly = "Add a boost die to the next allied active character's check.";
9
+ const noticeImportantPoint = "Notice a single important point in the ongoing conflict, such as the location of a blast door's control panel or a weak point on an attack speeder.";
10
+ const inflictCriticalInjury = "Inflict a Critical Injury with a successful attack that deals damage past soak (Advantage cost may vary).";
11
+ const activateWeaponQuality = "Activate a weapon quality (Advantage cost may vary).";
12
+ // 2 advantage or 1 triumph
13
+ const performManeuver = "Perform an immediate free maneuver that does not exceed the two maneuver per turn limit.";
14
+ const addSetbackDie = "Add a setback die to the targeted character's next check.";
15
+ const addBoostDieToAnyAlly = "Add a boost die to any allied character's next check, including that of the active character.";
16
+ // 3 advantage or 1 triumph
17
+ const negateEnemy = "Negate the targeted enemy's defensive bonuses (such as the defense gained from cover, equipment, or performing the Guarded Stance maneuver) util the end of the current round.";
18
+ const ignoreEnvironment = "Ignore penalizing environmental effects such as inclement weather, zero gravity, or similar circumstances until the end of the active character's next turn.";
19
+ const disableOpponent = "When dealing damage to a target, have the attack disable the opponent or one piece of gear rather than dealing wounds or strain. This could include hobbling them temporarily with a shot to the leg, or disabling their comlink. This should be agreed upon by the player and the GM, and the effects are up to the GM (although Table 6-10: Critical Injury Result is a god resource to consult for possible effects). The effects should be temporary and not too excessive.";
20
+ const gainDefense = "Gain + 1 melee or ranged defense until the end of the active character's next turn.";
21
+ const dropWeapon = "Force the target to drop a melee or ranged weapon they are wielding.";
22
+ // 1 triumph
23
+ const upgradeDifficultyTargetedCharacter = "Upgrade the difficulty of the targeted character's next check.";
24
+ const doSomethingVital = "Do something vital, such as shooting the controls to the nearby blast doors to seal them shut.";
25
+ const upgradeAnyAllyCheck = "Upgrade any allied character's next check, including that of the current active character.";
26
+ // 2 triumph
27
+ const destroyEquipment = "When dealing damage to a target, have the attack destroy a piece of equipment the target is using, such as blowing up his blaster or destroying a personal shield generator.";
28
+ // 1 threat or 1 despair
29
+ const sufferStrain = "The active character suffers 1 strain.";
30
+ const loseManeuverBenefit = "The active character loses the benefits of a prior maneuver (such as from taking cover or assuming a Guarded Stance) until they perform the maneuver again.";
31
+ // 2 threat or 1 despair
32
+ const freeManeuver = "An opponent may immediately perform one free maneuver in response to the active character's check.";
33
+ const addBoostDieToTargetedCharacter = "Add a boost die to the targeted character's next check.";
34
+ const sufferSetback = "The active character or an allied character suffers a setback die on their next action.";
35
+ // 3 threat or 1 despair
36
+ const fallProne = "The active character falls prone.";
37
+ const gainSignificantAdvantage = "The active character grants the enemy a significant advantage in the ongoing encounter, such as accidentally blasting the controls to a bridge the active character was planning to use for their escape.";
38
+ // 1 despair
39
+ const outOfAmmo = "The character's ranged weapon imediately runs out of ammunition and may not be used for the remainder of the encounter.";
40
+ const upgradeDifficultyAlliedCharacter = "Upgrade the difficulty of an allied character's next check, including that of the current active character.";
41
+ const damagedItem = "The tool or melee weapon the character is using becomes damaged.";
42
+ exports.hints = [
43
+ // 1 advantage or 1 triumph
44
+ {
45
+ description: recoverOneStrain,
46
+ cost: {
47
+ [types_1.SYMBOLS.ADVANTAGE]: 1,
48
+ [types_1.SYMBOLS.TRIUMPH]: 1,
49
+ },
50
+ },
51
+ {
52
+ description: addBoostDieToActiveAlly,
53
+ cost: {
54
+ [types_1.SYMBOLS.ADVANTAGE]: 1,
55
+ [types_1.SYMBOLS.TRIUMPH]: 1,
56
+ },
57
+ },
58
+ {
59
+ description: noticeImportantPoint,
60
+ cost: {
61
+ [types_1.SYMBOLS.ADVANTAGE]: 1,
62
+ [types_1.SYMBOLS.TRIUMPH]: 1,
63
+ },
64
+ },
65
+ {
66
+ description: inflictCriticalInjury,
67
+ cost: {
68
+ [types_1.SYMBOLS.ADVANTAGE]: 1,
69
+ [types_1.SYMBOLS.TRIUMPH]: 1,
70
+ },
71
+ },
72
+ {
73
+ description: activateWeaponQuality,
74
+ cost: {
75
+ [types_1.SYMBOLS.ADVANTAGE]: 1,
76
+ [types_1.SYMBOLS.TRIUMPH]: 1,
77
+ },
78
+ },
79
+ // 2 advantage or 1 triumph
80
+ {
81
+ description: performManeuver,
82
+ cost: {
83
+ [types_1.SYMBOLS.ADVANTAGE]: 2,
84
+ [types_1.SYMBOLS.TRIUMPH]: 1,
85
+ },
86
+ },
87
+ {
88
+ description: addSetbackDie,
89
+ cost: {
90
+ [types_1.SYMBOLS.ADVANTAGE]: 2,
91
+ [types_1.SYMBOLS.TRIUMPH]: 1,
92
+ },
93
+ },
94
+ {
95
+ description: addBoostDieToAnyAlly,
96
+ cost: {
97
+ [types_1.SYMBOLS.ADVANTAGE]: 2,
98
+ [types_1.SYMBOLS.TRIUMPH]: 1,
99
+ },
100
+ },
101
+ // 3 advantage or 1 triumph
102
+ {
103
+ description: negateEnemy,
104
+ cost: {
105
+ [types_1.SYMBOLS.ADVANTAGE]: 3,
106
+ [types_1.SYMBOLS.TRIUMPH]: 1,
107
+ },
108
+ },
109
+ {
110
+ description: ignoreEnvironment,
111
+ cost: {
112
+ [types_1.SYMBOLS.ADVANTAGE]: 3,
113
+ [types_1.SYMBOLS.TRIUMPH]: 1,
114
+ },
115
+ },
116
+ {
117
+ description: disableOpponent,
118
+ cost: {
119
+ [types_1.SYMBOLS.ADVANTAGE]: 3,
120
+ [types_1.SYMBOLS.TRIUMPH]: 1,
121
+ },
122
+ },
123
+ {
124
+ description: gainDefense,
125
+ cost: {
126
+ [types_1.SYMBOLS.ADVANTAGE]: 3,
127
+ [types_1.SYMBOLS.TRIUMPH]: 1,
128
+ },
129
+ },
130
+ {
131
+ description: dropWeapon,
132
+ cost: {
133
+ [types_1.SYMBOLS.ADVANTAGE]: 3,
134
+ [types_1.SYMBOLS.TRIUMPH]: 1,
135
+ },
136
+ },
137
+ // 1 triumph
138
+ {
139
+ description: upgradeDifficultyTargetedCharacter,
140
+ cost: {
141
+ [types_1.SYMBOLS.TRIUMPH]: 1,
142
+ },
143
+ },
144
+ {
145
+ description: doSomethingVital,
146
+ cost: {
147
+ [types_1.SYMBOLS.TRIUMPH]: 1,
148
+ },
149
+ },
150
+ {
151
+ description: upgradeAnyAllyCheck,
152
+ cost: {
153
+ [types_1.SYMBOLS.TRIUMPH]: 1,
154
+ },
155
+ },
156
+ // 2 triumph
157
+ {
158
+ description: destroyEquipment,
159
+ cost: {
160
+ [types_1.SYMBOLS.TRIUMPH]: 2,
161
+ },
162
+ },
163
+ // 1 threat or 1 despair
164
+ {
165
+ description: sufferStrain,
166
+ cost: {
167
+ [types_1.SYMBOLS.THREAT]: 1,
168
+ [types_1.SYMBOLS.DESPAIR]: 1,
169
+ },
170
+ },
171
+ {
172
+ description: loseManeuverBenefit,
173
+ cost: {
174
+ [types_1.SYMBOLS.THREAT]: 1,
175
+ [types_1.SYMBOLS.DESPAIR]: 1,
176
+ },
177
+ },
178
+ // 2 threat or 1 despair
179
+ {
180
+ description: freeManeuver,
181
+ cost: {
182
+ [types_1.SYMBOLS.THREAT]: 2,
183
+ [types_1.SYMBOLS.DESPAIR]: 1,
184
+ },
185
+ },
186
+ {
187
+ description: addBoostDieToTargetedCharacter,
188
+ cost: {
189
+ [types_1.SYMBOLS.THREAT]: 1,
190
+ [types_1.SYMBOLS.DESPAIR]: 1,
191
+ },
192
+ },
193
+ {
194
+ description: sufferSetback,
195
+ cost: {
196
+ [types_1.SYMBOLS.THREAT]: 2,
197
+ [types_1.SYMBOLS.DESPAIR]: 1,
198
+ },
199
+ },
200
+ // 3 threat or 1 despair
201
+ {
202
+ description: fallProne,
203
+ cost: {
204
+ [types_1.SYMBOLS.THREAT]: 3,
205
+ [types_1.SYMBOLS.DESPAIR]: 1,
206
+ },
207
+ },
208
+ {
209
+ description: gainSignificantAdvantage,
210
+ cost: {
211
+ [types_1.SYMBOLS.THREAT]: 3,
212
+ [types_1.SYMBOLS.DESPAIR]: 1,
213
+ },
214
+ },
215
+ // 1 despair
216
+ {
217
+ description: outOfAmmo,
218
+ cost: {
219
+ [types_1.SYMBOLS.DESPAIR]: 1,
220
+ },
221
+ },
222
+ {
223
+ description: upgradeDifficultyAlliedCharacter,
224
+ cost: {
225
+ [types_1.SYMBOLS.DESPAIR]: 1,
226
+ },
227
+ },
228
+ {
229
+ description: damagedItem,
230
+ cost: {
231
+ [types_1.SYMBOLS.DESPAIR]: 1,
232
+ },
233
+ },
234
+ ];
235
+ function hintCostDisplayText(hint) {
236
+ if (!hint.cost || Object.keys(hint.cost).length === 0) {
237
+ return "No cost";
238
+ }
239
+ const parts = Object.entries(hint.cost)
240
+ .filter(([_, count]) => count && count > 0)
241
+ .map(([symbol, count]) => {
242
+ const symbolName = symbol.charAt(0) + symbol.toLowerCase().slice(1);
243
+ return `${count} ${symbolName}${count > 1 ? "(s)" : ""}`;
244
+ });
245
+ return parts.length > 0 ? parts.join(" or ") : "No cost";
246
+ }
package/dist/types.d.ts CHANGED
@@ -16,6 +16,7 @@ export type DiceResult = {
16
16
  despair: number;
17
17
  lightSide: number;
18
18
  darkSide: number;
19
+ hints?: string[];
19
20
  };
20
21
  export type DieType = "boost" | "ability" | "proficiency" | "setback" | "difficulty" | "challenge" | "force";
21
22
  export type DetailedDieResult = {
@@ -27,3 +28,17 @@ export type RollResult = {
27
28
  results: DetailedDieResult[];
28
29
  summary: DiceResult;
29
30
  };
31
+ export declare const SYMBOLS: {
32
+ readonly SUCCESS: "SUCCESS";
33
+ readonly FAILURE: "FAILURE";
34
+ readonly ADVANTAGE: "ADVANTAGE";
35
+ readonly THREAT: "THREAT";
36
+ readonly TRIUMPH: "TRIUMPH";
37
+ readonly DESPAIR: "DESPAIR";
38
+ readonly LIGHT: "LIGHT";
39
+ readonly DARK: "DARK";
40
+ };
41
+ export type Symbol = keyof typeof SYMBOLS;
42
+ export type RollOptions = {
43
+ hints?: boolean;
44
+ };
package/dist/types.js CHANGED
@@ -1,2 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SYMBOLS = void 0;
4
+ exports.SYMBOLS = {
5
+ SUCCESS: "SUCCESS",
6
+ FAILURE: "FAILURE",
7
+ ADVANTAGE: "ADVANTAGE",
8
+ THREAT: "THREAT",
9
+ TRIUMPH: "TRIUMPH",
10
+ DESPAIR: "DESPAIR",
11
+ LIGHT: "LIGHT",
12
+ DARK: "DARK",
13
+ };
package/package.json CHANGED
@@ -1,7 +1,34 @@
1
1
  {
2
2
  "name": "@swrpg-online/dice",
3
- "version": "1.1.0",
4
- "description": "A TypeScript library for simulating Star Wars RPG narrative dice rolls.",
3
+ "version": "1.2.1",
4
+ "description": "A TypeScript library that creates dice rolls using the narrative dice system for the Star Wars Roleplaying Game by Fantasy Flight Games and Edge Studio.",
5
+ "keywords": [
6
+ "swrpg",
7
+ "dice",
8
+ "rpg",
9
+ "star wars",
10
+ "star-wars",
11
+ "rpg",
12
+ "dice",
13
+ "edge-studio",
14
+ "narrative-dice",
15
+ "typescript",
16
+ "cli",
17
+ "swrpg",
18
+ "genesys",
19
+ "fantasy-flight-games",
20
+ "ffg",
21
+ "roleplaying",
22
+ "tabletop",
23
+ "dice-roller"
24
+ ],
25
+ "homepage": "https://github.com/swrpg-online/dice",
26
+ "bugs": {
27
+ "url": "https://github.com/swrpg-online/dice/issues"
28
+ },
29
+ "license": "MIT",
30
+ "author": "@swrpg-online",
31
+ "contributors": [],
5
32
  "main": "dist/index.js",
6
33
  "bin": {
7
34
  "swrpg-dice": "dist/cli.js"
@@ -22,9 +49,6 @@
22
49
  "build": "tsc && ls -l",
23
50
  "prepare": "npm run build && husky install"
24
51
  },
25
- "keywords": [],
26
- "author": "@swrpg-online",
27
- "license": "MIT",
28
52
  "dependencies": {
29
53
  "@types/jest": "^29.5.14",
30
54
  "@types/node": "^22.10.0",
@@ -46,6 +70,9 @@
46
70
  "type": "git",
47
71
  "url": "https://github.com/swrpg-online/dice"
48
72
  },
73
+ "engines": {
74
+ "node": ">=21"
75
+ },
49
76
  "lint-staged": {
50
77
  "**/*": "prettier --write --ignore-unknown"
51
78
  }