@yipe/dice 0.4.0 → 0.5.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.
@@ -98,7 +98,7 @@ declare class DCBuilder extends RollBuilder {
98
98
  toPMF(eps?: number): PMF;
99
99
  }
100
100
 
101
- type ExpressionNode = DieNode | ConstantNode | SumNode | AddNode | KeepNode | D20RollNode | HalfNode | MaxOfNode;
101
+ type ExpressionNode = DieNode | ConstantNode | SumNode | AddNode | KeepNode | D20RollNode | HalfNode | MaxOfNode | ScaleNode;
102
102
  type DieNode = {
103
103
  type: "die";
104
104
  sides: number;
@@ -142,6 +142,22 @@ type MaxOfNode = {
142
142
  count: number;
143
143
  child: ExpressionNode;
144
144
  };
145
+ /**
146
+ * Scale the child's result by `numerator / denominator`, then round.
147
+ *
148
+ * Unlike {@link HalfNode} (a fixed `// 2` with floor), this is a general, composable
149
+ * multiplier/divider — the building block for damage-type resistance (`1/2`, floor),
150
+ * vulnerability (`2/1`), and similar per-source transforms. It renders as
151
+ * `N * (child)` when the denominator is 1, `(child) // D` when the numerator is 1,
152
+ * and `(child) * N // D` otherwise.
153
+ */
154
+ type ScaleNode = {
155
+ type: "scale";
156
+ numerator: number;
157
+ denominator: number;
158
+ rounding: "floor" | "round" | "ceil";
159
+ child: ExpressionNode;
160
+ };
145
161
 
146
162
  declare const defaultConfig: RollConfig;
147
163
  declare class RollBuilder {
@@ -202,6 +218,13 @@ declare class RollBuilder {
202
218
  get rollType(): RollType;
203
219
  get baseReroll(): number;
204
220
  half(): HalfRollBuilder;
221
+ /**
222
+ * Scale this roll's result by `numerator / denominator`, rounding each outcome.
223
+ * A general, composable form of {@link half} — used to model damage-type resistance
224
+ * (`scaleResult(1, 2)` → `(expr) // 2`) and vulnerability (`scaleResult(2)` → `2 * (expr)`).
225
+ * Compose several of these (and plain rolls) into one payload with {@link sumRolls}.
226
+ */
227
+ scaleResult(numerator: number, denominator?: number, rounding?: "floor" | "round" | "ceil"): ScaleRollBuilder;
205
228
  maxOf(count: number): MaxOfRollBuilder;
206
229
  ac(_targetAC: number): ACBuilder;
207
230
  dc(_saveDC: number): DCBuilder;
@@ -217,6 +240,25 @@ declare class HalfRollBuilder extends RollBuilder {
217
240
  toPMF(eps?: number): PMF;
218
241
  copy(): HalfRollBuilder;
219
242
  }
243
+ /**
244
+ * A roll whose result is scaled by `numerator / denominator` and rounded — the composable
245
+ * generalization of {@link HalfRollBuilder}. Renders as `N * (inner)`, `(inner) // D`, or
246
+ * `(inner) * N // D`. Terminal (like `half`): use {@link sumRolls} to combine with other rolls.
247
+ */
248
+ declare class ScaleRollBuilder extends RollBuilder {
249
+ private readonly innerRoll;
250
+ private readonly numerator;
251
+ private readonly denominator;
252
+ private readonly rounding;
253
+ constructor(innerRoll: RollBuilder, numerator: number, denominator?: number, rounding?: "floor" | "round" | "ceil");
254
+ hasHiddenState(): boolean;
255
+ get lastConfig(): RollConfig;
256
+ getSubRollConfigs(): readonly RollConfig[];
257
+ toExpression(): string;
258
+ toAST(): ExpressionNode;
259
+ toPMF(eps?: number): PMF;
260
+ copy(): ScaleRollBuilder;
261
+ }
220
262
  declare class MaxOfRollBuilder extends RollBuilder {
221
263
  private readonly innerRoll;
222
264
  private readonly count;
@@ -305,6 +347,14 @@ declare class PooledRollBuilder extends RollBuilder {
305
347
  scaleDice(scale: number): RollBuilder;
306
348
  times(count: number): PooledRollBuilder;
307
349
  }
350
+ /**
351
+ * Combine several rolls into one additive payload whose PMF is their convolution and whose
352
+ * expression is them joined with ` + `. Unlike `a.plus(b)`, this preserves parts that carry
353
+ * hidden state (e.g. `roll.scaleResult(1, 2)` / `roll.half()`), so per-damage-type resistance
354
+ * and vulnerability survive into both the distribution and the rendered expression.
355
+ * Empty parts collapse to `0`; a single part is returned unwrapped.
356
+ */
357
+ declare function sumRolls(parts: readonly RollBuilder[]): RollBuilder;
308
358
 
309
359
  type ActionEffect = RollBuilder;
310
360
  declare class AttackBuilder implements CheckBuilder {
@@ -376,4 +426,4 @@ declare const flat: (n: number) => RollBuilder;
376
426
  declare const roll: RollFactory;
377
427
  declare const builderPMFCache: LRUCache<string, PMF>;
378
428
 
379
- export { ACBuilder, AlwaysCritBuilder, AlwaysHitBuilder, AttackBuilder, type AttackConfig, type AttackResolution, type CheckBuilder, DCBuilder, HalfRollBuilder, type KeepMode, MaxOfRollBuilder, ParsedRollBuilder, PooledRollBuilder, type Resolution, RollBuilder, type RollConfig, type RollFactory, RollType, SaveBuilder, type SaveOutcome, type SaveResolution, builderPMFCache, d, d10, d100, d12, d20, d4, d6, d8, defaultConfig, flat, hd20, roll };
429
+ export { ACBuilder, AlwaysCritBuilder, AlwaysHitBuilder, AttackBuilder, type AttackConfig, type AttackResolution, type CheckBuilder, DCBuilder, HalfRollBuilder, type KeepMode, MaxOfRollBuilder, ParsedRollBuilder, PooledRollBuilder, type Resolution, RollBuilder, type RollConfig, type RollFactory, RollType, SaveBuilder, type SaveOutcome, type SaveResolution, ScaleRollBuilder, builderPMFCache, d, d10, d100, d12, d20, d4, d6, d8, defaultConfig, flat, hd20, roll, sumRolls };
@@ -98,7 +98,7 @@ declare class DCBuilder extends RollBuilder {
98
98
  toPMF(eps?: number): PMF;
99
99
  }
100
100
 
101
- type ExpressionNode = DieNode | ConstantNode | SumNode | AddNode | KeepNode | D20RollNode | HalfNode | MaxOfNode;
101
+ type ExpressionNode = DieNode | ConstantNode | SumNode | AddNode | KeepNode | D20RollNode | HalfNode | MaxOfNode | ScaleNode;
102
102
  type DieNode = {
103
103
  type: "die";
104
104
  sides: number;
@@ -142,6 +142,22 @@ type MaxOfNode = {
142
142
  count: number;
143
143
  child: ExpressionNode;
144
144
  };
145
+ /**
146
+ * Scale the child's result by `numerator / denominator`, then round.
147
+ *
148
+ * Unlike {@link HalfNode} (a fixed `// 2` with floor), this is a general, composable
149
+ * multiplier/divider — the building block for damage-type resistance (`1/2`, floor),
150
+ * vulnerability (`2/1`), and similar per-source transforms. It renders as
151
+ * `N * (child)` when the denominator is 1, `(child) // D` when the numerator is 1,
152
+ * and `(child) * N // D` otherwise.
153
+ */
154
+ type ScaleNode = {
155
+ type: "scale";
156
+ numerator: number;
157
+ denominator: number;
158
+ rounding: "floor" | "round" | "ceil";
159
+ child: ExpressionNode;
160
+ };
145
161
 
146
162
  declare const defaultConfig: RollConfig;
147
163
  declare class RollBuilder {
@@ -202,6 +218,13 @@ declare class RollBuilder {
202
218
  get rollType(): RollType;
203
219
  get baseReroll(): number;
204
220
  half(): HalfRollBuilder;
221
+ /**
222
+ * Scale this roll's result by `numerator / denominator`, rounding each outcome.
223
+ * A general, composable form of {@link half} — used to model damage-type resistance
224
+ * (`scaleResult(1, 2)` → `(expr) // 2`) and vulnerability (`scaleResult(2)` → `2 * (expr)`).
225
+ * Compose several of these (and plain rolls) into one payload with {@link sumRolls}.
226
+ */
227
+ scaleResult(numerator: number, denominator?: number, rounding?: "floor" | "round" | "ceil"): ScaleRollBuilder;
205
228
  maxOf(count: number): MaxOfRollBuilder;
206
229
  ac(_targetAC: number): ACBuilder;
207
230
  dc(_saveDC: number): DCBuilder;
@@ -217,6 +240,25 @@ declare class HalfRollBuilder extends RollBuilder {
217
240
  toPMF(eps?: number): PMF;
218
241
  copy(): HalfRollBuilder;
219
242
  }
243
+ /**
244
+ * A roll whose result is scaled by `numerator / denominator` and rounded — the composable
245
+ * generalization of {@link HalfRollBuilder}. Renders as `N * (inner)`, `(inner) // D`, or
246
+ * `(inner) * N // D`. Terminal (like `half`): use {@link sumRolls} to combine with other rolls.
247
+ */
248
+ declare class ScaleRollBuilder extends RollBuilder {
249
+ private readonly innerRoll;
250
+ private readonly numerator;
251
+ private readonly denominator;
252
+ private readonly rounding;
253
+ constructor(innerRoll: RollBuilder, numerator: number, denominator?: number, rounding?: "floor" | "round" | "ceil");
254
+ hasHiddenState(): boolean;
255
+ get lastConfig(): RollConfig;
256
+ getSubRollConfigs(): readonly RollConfig[];
257
+ toExpression(): string;
258
+ toAST(): ExpressionNode;
259
+ toPMF(eps?: number): PMF;
260
+ copy(): ScaleRollBuilder;
261
+ }
220
262
  declare class MaxOfRollBuilder extends RollBuilder {
221
263
  private readonly innerRoll;
222
264
  private readonly count;
@@ -305,6 +347,14 @@ declare class PooledRollBuilder extends RollBuilder {
305
347
  scaleDice(scale: number): RollBuilder;
306
348
  times(count: number): PooledRollBuilder;
307
349
  }
350
+ /**
351
+ * Combine several rolls into one additive payload whose PMF is their convolution and whose
352
+ * expression is them joined with ` + `. Unlike `a.plus(b)`, this preserves parts that carry
353
+ * hidden state (e.g. `roll.scaleResult(1, 2)` / `roll.half()`), so per-damage-type resistance
354
+ * and vulnerability survive into both the distribution and the rendered expression.
355
+ * Empty parts collapse to `0`; a single part is returned unwrapped.
356
+ */
357
+ declare function sumRolls(parts: readonly RollBuilder[]): RollBuilder;
308
358
 
309
359
  type ActionEffect = RollBuilder;
310
360
  declare class AttackBuilder implements CheckBuilder {
@@ -376,4 +426,4 @@ declare const flat: (n: number) => RollBuilder;
376
426
  declare const roll: RollFactory;
377
427
  declare const builderPMFCache: LRUCache<string, PMF>;
378
428
 
379
- export { ACBuilder, AlwaysCritBuilder, AlwaysHitBuilder, AttackBuilder, type AttackConfig, type AttackResolution, type CheckBuilder, DCBuilder, HalfRollBuilder, type KeepMode, MaxOfRollBuilder, ParsedRollBuilder, PooledRollBuilder, type Resolution, RollBuilder, type RollConfig, type RollFactory, RollType, SaveBuilder, type SaveOutcome, type SaveResolution, builderPMFCache, d, d10, d100, d12, d20, d4, d6, d8, defaultConfig, flat, hd20, roll };
429
+ export { ACBuilder, AlwaysCritBuilder, AlwaysHitBuilder, AttackBuilder, type AttackConfig, type AttackResolution, type CheckBuilder, DCBuilder, HalfRollBuilder, type KeepMode, MaxOfRollBuilder, ParsedRollBuilder, PooledRollBuilder, type Resolution, RollBuilder, type RollConfig, type RollFactory, RollType, SaveBuilder, type SaveOutcome, type SaveResolution, ScaleRollBuilder, builderPMFCache, d, d10, d100, d12, d20, d4, d6, d8, defaultConfig, flat, hd20, roll, sumRolls };
@@ -4092,6 +4092,15 @@ var RollBuilder = class _RollBuilder {
4092
4092
  half() {
4093
4093
  return new HalfRollBuilder(this);
4094
4094
  }
4095
+ /**
4096
+ * Scale this roll's result by `numerator / denominator`, rounding each outcome.
4097
+ * A general, composable form of {@link half} — used to model damage-type resistance
4098
+ * (`scaleResult(1, 2)` → `(expr) // 2`) and vulnerability (`scaleResult(2)` → `2 * (expr)`).
4099
+ * Compose several of these (and plain rolls) into one payload with {@link sumRolls}.
4100
+ */
4101
+ scaleResult(numerator, denominator = 1, rounding = "floor") {
4102
+ return new ScaleRollBuilder(this, numerator, denominator, rounding);
4103
+ }
4095
4104
  // Create a "max of N rolls" version of this roll for crit damage with keep operations
4096
4105
  maxOf(count) {
4097
4106
  return new MaxOfRollBuilder(this, count);
@@ -4146,6 +4155,50 @@ var HalfRollBuilder = class _HalfRollBuilder extends RollBuilder {
4146
4155
  return new _HalfRollBuilder(this.innerRoll.copy());
4147
4156
  }
4148
4157
  };
4158
+ var ScaleRollBuilder = class _ScaleRollBuilder extends RollBuilder {
4159
+ constructor(innerRoll, numerator, denominator = 1, rounding = "floor") {
4160
+ super(0);
4161
+ this.innerRoll = innerRoll;
4162
+ this.numerator = numerator;
4163
+ this.denominator = denominator;
4164
+ this.rounding = rounding;
4165
+ }
4166
+ hasHiddenState() {
4167
+ return this.innerRoll.hasHiddenState();
4168
+ }
4169
+ get lastConfig() {
4170
+ return this.innerRoll.lastConfig;
4171
+ }
4172
+ getSubRollConfigs() {
4173
+ return this.innerRoll.getSubRollConfigs();
4174
+ }
4175
+ toExpression() {
4176
+ const inner = this.innerRoll.toExpression();
4177
+ if (this.denominator === 1) return `${this.numerator} * (${inner})`;
4178
+ if (this.numerator === 1) return `(${inner}) // ${this.denominator}`;
4179
+ return `(${inner}) * ${this.numerator} // ${this.denominator}`;
4180
+ }
4181
+ toAST() {
4182
+ return {
4183
+ type: "scale",
4184
+ numerator: this.numerator,
4185
+ denominator: this.denominator,
4186
+ rounding: this.rounding,
4187
+ child: this.innerRoll.toAST()
4188
+ };
4189
+ }
4190
+ toPMF(eps = 0) {
4191
+ return pmfFromRollBuilder(this, eps);
4192
+ }
4193
+ copy() {
4194
+ return new _ScaleRollBuilder(
4195
+ this.innerRoll.copy(),
4196
+ this.numerator,
4197
+ this.denominator,
4198
+ this.rounding
4199
+ );
4200
+ }
4201
+ };
4149
4202
  var MaxOfRollBuilder = class _MaxOfRollBuilder extends RollBuilder {
4150
4203
  constructor(innerRoll, count, diceCount, diceSides) {
4151
4204
  super(0);
@@ -4444,6 +4497,49 @@ var PooledRollBuilder = class _PooledRollBuilder extends RollBuilder {
4444
4497
  return new _PooledRollBuilder(sumNode, newExpr);
4445
4498
  }
4446
4499
  };
4500
+ var CompositeSumRollBuilder = class _CompositeSumRollBuilder extends RollBuilder {
4501
+ constructor(parts) {
4502
+ super(0);
4503
+ this.parts = parts;
4504
+ }
4505
+ hasHiddenState() {
4506
+ return true;
4507
+ }
4508
+ getSubRollConfigs() {
4509
+ return [];
4510
+ }
4511
+ toAST() {
4512
+ return {
4513
+ type: "add",
4514
+ children: this.parts.map((p) => ({
4515
+ node: p.toAST(),
4516
+ sign: 1
4517
+ }))
4518
+ };
4519
+ }
4520
+ toExpression() {
4521
+ const exprs = this.parts.map((p) => p.toExpression()).filter((e) => e && e !== "0");
4522
+ if (exprs.length === 0) return "0";
4523
+ let result = exprs[0];
4524
+ for (let i = 1; i < exprs.length; i++) {
4525
+ const e = exprs[i];
4526
+ result += e.startsWith("-") ? ` - ${e.substring(1)}` : ` + ${e}`;
4527
+ }
4528
+ return result.replace(/\+ -/g, "-");
4529
+ }
4530
+ toPMF(eps = 0) {
4531
+ return pmfFromRollBuilder(this, eps);
4532
+ }
4533
+ copy() {
4534
+ return new _CompositeSumRollBuilder(this.parts.map((p) => p.copy()));
4535
+ }
4536
+ };
4537
+ function sumRolls(parts) {
4538
+ const meaningful = parts.filter((p) => p !== void 0);
4539
+ if (meaningful.length === 0) return new RollBuilder(0);
4540
+ if (meaningful.length === 1) return meaningful[0];
4541
+ return new CompositeSumRollBuilder(meaningful);
4542
+ }
4447
4543
 
4448
4544
  // src/builder/factory.ts
4449
4545
  var rollFn = (count, sidesOrDie, modifier) => {
@@ -4674,6 +4770,11 @@ function resolve(node, eps = defaultEps) {
4674
4770
  if (count === 1) return childPMF;
4675
4771
  return computeMaxOfPMF(childPMF, count, eps);
4676
4772
  }
4773
+ case "scale": {
4774
+ const childPMF = resolve(node.child, eps);
4775
+ const denom = node.denominator === 0 ? 1 : node.denominator;
4776
+ return childPMF.scaleDamage(node.numerator / denom, node.rounding);
4777
+ }
4677
4778
  }
4678
4779
  })();
4679
4780
  builderPMFCache.set(cacheKey, result);
@@ -4745,6 +4846,7 @@ function findDie(node) {
4745
4846
  case "d20Roll":
4746
4847
  case "half":
4747
4848
  case "maxOf":
4849
+ case "scale":
4748
4850
  return findDie(node.child);
4749
4851
  case "keep":
4750
4852
  return findDie(node.child.child);
@@ -4929,6 +5031,8 @@ function getASTSignature(node) {
4929
5031
  return `half{ch:${getASTSignature(node.child)}}`;
4930
5032
  case "maxOf":
4931
5033
  return `maxOf{c:${node.count},ch:${getASTSignature(node.child)}}`;
5034
+ case "scale":
5035
+ return `scale{n:${node.numerator},d:${node.denominator},r:${node.rounding},ch:${getASTSignature(node.child)}}`;
4932
5036
  case "add": {
4933
5037
  let constantValue = 0;
4934
5038
  const otherChildrenSigs = [];
@@ -5365,6 +5469,6 @@ RollBuilder.prototype.dc = function(saveDC) {
5365
5469
  return new DCBuilder(this).dc(saveDC);
5366
5470
  };
5367
5471
 
5368
- export { ACBuilder, AlwaysCritBuilder, AlwaysHitBuilder, AttackBuilder, DCBuilder, HalfRollBuilder, MaxOfRollBuilder, ParsedRollBuilder, PooledRollBuilder, RollBuilder, SaveBuilder, builderPMFCache, d, d10, d100, d12, d20, d4, d6, d8, defaultConfig, flat, hd20, roll };
5472
+ export { ACBuilder, AlwaysCritBuilder, AlwaysHitBuilder, AttackBuilder, DCBuilder, HalfRollBuilder, MaxOfRollBuilder, ParsedRollBuilder, PooledRollBuilder, RollBuilder, SaveBuilder, ScaleRollBuilder, builderPMFCache, d, d10, d100, d12, d20, d4, d6, d8, defaultConfig, flat, hd20, roll, sumRolls };
5369
5473
  //# sourceMappingURL=index.js.map
5370
5474
  //# sourceMappingURL=index.js.map