@yipe/dice 0.2.20 → 0.2.21

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.
@@ -112,7 +112,9 @@ declare const defaultConfig: RollConfig;
112
112
  declare class RollBuilder {
113
113
  protected readonly subRollConfigs: readonly RollConfig[];
114
114
  constructor(countOrConfigs?: number | readonly RollConfig[]);
115
+ protected create(configs: readonly RollConfig[]): RollBuilder;
115
116
  protected get lastConfig(): RollConfig;
117
+ hasHiddenState(): boolean;
116
118
  getSubRollConfigs(): readonly RollConfig[];
117
119
  static fromConfig(config: Partial<RollConfig>): RollBuilder;
118
120
  static fromConfigs(configs: Partial<RollConfig>[]): RollBuilder;
@@ -131,6 +133,8 @@ declare class RollBuilder {
131
133
  bestOf(count: number | undefined): RollBuilder;
132
134
  keepHighest(total: number, count: number): RollBuilder;
133
135
  keepLowest(total: number, count: number): RollBuilder;
136
+ keepHighestAll(total: number, count: number): PooledRollBuilder;
137
+ keepLowestAll(total: number, count: number): PooledRollBuilder;
134
138
  withAdvantage(): RollBuilder;
135
139
  withDisadvantage(): RollBuilder;
136
140
  add(anotherRoll: RollBuilder | undefined): RollBuilder;
@@ -168,6 +172,7 @@ declare class RollBuilder {
168
172
  declare class HalfRollBuilder extends RollBuilder {
169
173
  private readonly innerRoll;
170
174
  constructor(innerRoll: RollBuilder);
175
+ hasHiddenState(): boolean;
171
176
  get lastConfig(): any;
172
177
  getSubRollConfigs(): readonly RollConfig[];
173
178
  toExpression(): string;
@@ -181,6 +186,7 @@ declare class MaxOfRollBuilder extends RollBuilder {
181
186
  private readonly diceCount?;
182
187
  private readonly diceSides?;
183
188
  constructor(innerRoll: RollBuilder, count: number, diceCount?: number, diceSides?: number);
189
+ hasHiddenState(): boolean;
184
190
  get lastConfig(): any;
185
191
  getSubRollConfigs(): readonly RollConfig[];
186
192
  toExpression(): string;
@@ -191,6 +197,7 @@ declare class MaxOfRollBuilder extends RollBuilder {
191
197
  declare class AlwaysHitBuilder extends RollBuilder {
192
198
  readonly attackConfig: CritConfig;
193
199
  constructor(baseRoll: RollBuilder, attackConfig?: CritConfig);
200
+ protected create(configs: readonly RollConfig[]): RollBuilder;
194
201
  onHit(val: number): AttackBuilder;
195
202
  onHit(val: string): AttackBuilder;
196
203
  onHit(val: RollBuilder): AttackBuilder;
@@ -213,6 +220,7 @@ declare class AlwaysCritBuilder extends RollBuilder {
213
220
  constructor(baseRoll: RollBuilder, attackConfig?: CritConfig & {
214
221
  ac?: number;
215
222
  }, fromAlwaysHit?: boolean);
223
+ protected create(configs: readonly RollConfig[]): RollBuilder;
216
224
  onHit(val: number): AttackBuilder;
217
225
  onHit(val: string): AttackBuilder;
218
226
  onHit(val: RollBuilder): AttackBuilder;
@@ -226,22 +234,40 @@ declare class AlwaysCritBuilder extends RollBuilder {
226
234
  toPMF(): PMF;
227
235
  copy(): AlwaysCritBuilder;
228
236
  }
229
- /**
230
- * A RollBuilder that wraps a pre-computed PMF from a parsed string expression.
231
- * This is used to support string-based damage expressions in onHit/onCrit/onMiss.
232
- * The builder is not fully composable (e.g., you can't call .plus() on it),
233
- * but it can be used anywhere a RollBuilder is expected for terminal damage.
234
- */
235
237
  declare class ParsedRollBuilder extends RollBuilder {
236
238
  private readonly cachedPMF;
237
239
  private readonly originalExpression;
238
240
  constructor(expression: string);
239
- toPMF(eps?: number): PMF;
241
+ hasHiddenState(): boolean;
242
+ protected create(configs: readonly RollConfig[]): RollBuilder;
243
+ toPMF(_eps?: number): PMF;
240
244
  toExpression(): string;
241
245
  toAST(): ExpressionNode;
242
246
  copy(): ParsedRollBuilder;
243
247
  doubleDice(): ParsedRollBuilder;
244
248
  }
249
+ declare class PooledRollBuilder extends RollBuilder {
250
+ private readonly baseAST;
251
+ private readonly baseExpression;
252
+ constructor(baseAST: ExpressionNode, baseExpression: string, configs?: readonly RollConfig[]);
253
+ protected create(configs: readonly RollConfig[]): PooledRollBuilder;
254
+ hasHiddenState(): boolean;
255
+ d(_sides: number | undefined): RollBuilder;
256
+ reroll(_value: number): RollBuilder;
257
+ explode(_count?: number | undefined): RollBuilder;
258
+ minimum(_val: number | undefined): RollBuilder;
259
+ bestOf(_count: number | undefined): RollBuilder;
260
+ keepHighest(_total: number, _count: number): RollBuilder;
261
+ keepLowest(_total: number, _count: number): RollBuilder;
262
+ withAdvantage(): RollBuilder;
263
+ withDisadvantage(): RollBuilder;
264
+ withElvenAccuracy(): RollBuilder;
265
+ toAST(): ExpressionNode;
266
+ toExpression(): string;
267
+ copy(): PooledRollBuilder;
268
+ scaleDice(scale: number): RollBuilder;
269
+ times(count: number): PooledRollBuilder;
270
+ }
245
271
 
246
272
  type ActionEffect = RollBuilder;
247
273
  declare class AttackBuilder implements CheckBuilder {
@@ -359,4 +385,4 @@ declare const flat: (n: number) => RollBuilder;
359
385
  declare const roll: RollFactory;
360
386
  declare const builderPMFCache: LRUCache<string, PMF>;
361
387
 
362
- export { ACBuilder, AlwaysCritBuilder, AlwaysHitBuilder, type AttackConfig, type AttackResolution, type CheckBuilder, DCBuilder, HalfRollBuilder, type KeepMode, MaxOfRollBuilder, ParsedRollBuilder, type Resolution, RollBuilder, type RollConfig, type RollFactory, type RollType, SaveBuilder, type SaveOutcome, type SaveResolution, builderPMFCache, d, d10, d100, d12, d20, d4, d6, d8, defaultConfig, flat, hd20, roll };
388
+ export { ACBuilder, AlwaysCritBuilder, AlwaysHitBuilder, type AttackConfig, type AttackResolution, type CheckBuilder, DCBuilder, HalfRollBuilder, type KeepMode, MaxOfRollBuilder, ParsedRollBuilder, PooledRollBuilder, type Resolution, RollBuilder, type RollConfig, type RollFactory, type RollType, SaveBuilder, type SaveOutcome, type SaveResolution, builderPMFCache, d, d10, d100, d12, d20, d4, d6, d8, defaultConfig, flat, hd20, roll };
@@ -112,7 +112,9 @@ declare const defaultConfig: RollConfig;
112
112
  declare class RollBuilder {
113
113
  protected readonly subRollConfigs: readonly RollConfig[];
114
114
  constructor(countOrConfigs?: number | readonly RollConfig[]);
115
+ protected create(configs: readonly RollConfig[]): RollBuilder;
115
116
  protected get lastConfig(): RollConfig;
117
+ hasHiddenState(): boolean;
116
118
  getSubRollConfigs(): readonly RollConfig[];
117
119
  static fromConfig(config: Partial<RollConfig>): RollBuilder;
118
120
  static fromConfigs(configs: Partial<RollConfig>[]): RollBuilder;
@@ -131,6 +133,8 @@ declare class RollBuilder {
131
133
  bestOf(count: number | undefined): RollBuilder;
132
134
  keepHighest(total: number, count: number): RollBuilder;
133
135
  keepLowest(total: number, count: number): RollBuilder;
136
+ keepHighestAll(total: number, count: number): PooledRollBuilder;
137
+ keepLowestAll(total: number, count: number): PooledRollBuilder;
134
138
  withAdvantage(): RollBuilder;
135
139
  withDisadvantage(): RollBuilder;
136
140
  add(anotherRoll: RollBuilder | undefined): RollBuilder;
@@ -168,6 +172,7 @@ declare class RollBuilder {
168
172
  declare class HalfRollBuilder extends RollBuilder {
169
173
  private readonly innerRoll;
170
174
  constructor(innerRoll: RollBuilder);
175
+ hasHiddenState(): boolean;
171
176
  get lastConfig(): any;
172
177
  getSubRollConfigs(): readonly RollConfig[];
173
178
  toExpression(): string;
@@ -181,6 +186,7 @@ declare class MaxOfRollBuilder extends RollBuilder {
181
186
  private readonly diceCount?;
182
187
  private readonly diceSides?;
183
188
  constructor(innerRoll: RollBuilder, count: number, diceCount?: number, diceSides?: number);
189
+ hasHiddenState(): boolean;
184
190
  get lastConfig(): any;
185
191
  getSubRollConfigs(): readonly RollConfig[];
186
192
  toExpression(): string;
@@ -191,6 +197,7 @@ declare class MaxOfRollBuilder extends RollBuilder {
191
197
  declare class AlwaysHitBuilder extends RollBuilder {
192
198
  readonly attackConfig: CritConfig;
193
199
  constructor(baseRoll: RollBuilder, attackConfig?: CritConfig);
200
+ protected create(configs: readonly RollConfig[]): RollBuilder;
194
201
  onHit(val: number): AttackBuilder;
195
202
  onHit(val: string): AttackBuilder;
196
203
  onHit(val: RollBuilder): AttackBuilder;
@@ -213,6 +220,7 @@ declare class AlwaysCritBuilder extends RollBuilder {
213
220
  constructor(baseRoll: RollBuilder, attackConfig?: CritConfig & {
214
221
  ac?: number;
215
222
  }, fromAlwaysHit?: boolean);
223
+ protected create(configs: readonly RollConfig[]): RollBuilder;
216
224
  onHit(val: number): AttackBuilder;
217
225
  onHit(val: string): AttackBuilder;
218
226
  onHit(val: RollBuilder): AttackBuilder;
@@ -226,22 +234,40 @@ declare class AlwaysCritBuilder extends RollBuilder {
226
234
  toPMF(): PMF;
227
235
  copy(): AlwaysCritBuilder;
228
236
  }
229
- /**
230
- * A RollBuilder that wraps a pre-computed PMF from a parsed string expression.
231
- * This is used to support string-based damage expressions in onHit/onCrit/onMiss.
232
- * The builder is not fully composable (e.g., you can't call .plus() on it),
233
- * but it can be used anywhere a RollBuilder is expected for terminal damage.
234
- */
235
237
  declare class ParsedRollBuilder extends RollBuilder {
236
238
  private readonly cachedPMF;
237
239
  private readonly originalExpression;
238
240
  constructor(expression: string);
239
- toPMF(eps?: number): PMF;
241
+ hasHiddenState(): boolean;
242
+ protected create(configs: readonly RollConfig[]): RollBuilder;
243
+ toPMF(_eps?: number): PMF;
240
244
  toExpression(): string;
241
245
  toAST(): ExpressionNode;
242
246
  copy(): ParsedRollBuilder;
243
247
  doubleDice(): ParsedRollBuilder;
244
248
  }
249
+ declare class PooledRollBuilder extends RollBuilder {
250
+ private readonly baseAST;
251
+ private readonly baseExpression;
252
+ constructor(baseAST: ExpressionNode, baseExpression: string, configs?: readonly RollConfig[]);
253
+ protected create(configs: readonly RollConfig[]): PooledRollBuilder;
254
+ hasHiddenState(): boolean;
255
+ d(_sides: number | undefined): RollBuilder;
256
+ reroll(_value: number): RollBuilder;
257
+ explode(_count?: number | undefined): RollBuilder;
258
+ minimum(_val: number | undefined): RollBuilder;
259
+ bestOf(_count: number | undefined): RollBuilder;
260
+ keepHighest(_total: number, _count: number): RollBuilder;
261
+ keepLowest(_total: number, _count: number): RollBuilder;
262
+ withAdvantage(): RollBuilder;
263
+ withDisadvantage(): RollBuilder;
264
+ withElvenAccuracy(): RollBuilder;
265
+ toAST(): ExpressionNode;
266
+ toExpression(): string;
267
+ copy(): PooledRollBuilder;
268
+ scaleDice(scale: number): RollBuilder;
269
+ times(count: number): PooledRollBuilder;
270
+ }
245
271
 
246
272
  type ActionEffect = RollBuilder;
247
273
  declare class AttackBuilder implements CheckBuilder {
@@ -359,4 +385,4 @@ declare const flat: (n: number) => RollBuilder;
359
385
  declare const roll: RollFactory;
360
386
  declare const builderPMFCache: LRUCache<string, PMF>;
361
387
 
362
- export { ACBuilder, AlwaysCritBuilder, AlwaysHitBuilder, type AttackConfig, type AttackResolution, type CheckBuilder, DCBuilder, HalfRollBuilder, type KeepMode, MaxOfRollBuilder, ParsedRollBuilder, type Resolution, RollBuilder, type RollConfig, type RollFactory, type RollType, SaveBuilder, type SaveOutcome, type SaveResolution, builderPMFCache, d, d10, d100, d12, d20, d4, d6, d8, defaultConfig, flat, hd20, roll };
388
+ export { ACBuilder, AlwaysCritBuilder, AlwaysHitBuilder, type AttackConfig, type AttackResolution, type CheckBuilder, DCBuilder, HalfRollBuilder, type KeepMode, MaxOfRollBuilder, ParsedRollBuilder, PooledRollBuilder, type Resolution, RollBuilder, type RollConfig, type RollFactory, type RollType, SaveBuilder, type SaveOutcome, type SaveResolution, builderPMFCache, d, d10, d100, d12, d20, d4, d6, d8, defaultConfig, flat, hd20, roll };
@@ -3338,9 +3338,15 @@ var RollBuilder = class _RollBuilder {
3338
3338
  this.subRollConfigs = countOrConfigs.map((c) => ({ ...c }));
3339
3339
  }
3340
3340
  }
3341
+ create(configs) {
3342
+ return new _RollBuilder(configs);
3343
+ }
3341
3344
  get lastConfig() {
3342
3345
  return this.subRollConfigs[this.subRollConfigs.length - 1];
3343
3346
  }
3347
+ hasHiddenState() {
3348
+ return false;
3349
+ }
3344
3350
  getSubRollConfigs() {
3345
3351
  return this.subRollConfigs.map((c) => ({ ...c }));
3346
3352
  }
@@ -3374,6 +3380,11 @@ var RollBuilder = class _RollBuilder {
3374
3380
  }
3375
3381
  if (isNaN(count)) throw new Error("Invalid NaN value for count argument");
3376
3382
  if (sidesOrDie instanceof _RollBuilder) {
3383
+ if (sidesOrDie.hasHiddenState()) {
3384
+ throw new Error(
3385
+ "Cannot use a roll with hidden state (like a pooled roll) as a die type."
3386
+ );
3387
+ }
3377
3388
  const subRollConfigs = sidesOrDie.getSubRollConfigs();
3378
3389
  if (subRollConfigs.length === 0) {
3379
3390
  const result = new _RollBuilder(0);
@@ -3414,12 +3425,17 @@ var RollBuilder = class _RollBuilder {
3414
3425
  if (sides === 0) return this;
3415
3426
  const newConfigs = this.getSubRollConfigs();
3416
3427
  newConfigs[newConfigs.length - 1].sides = sides;
3417
- return new this.constructor(newConfigs);
3428
+ return this.create(newConfigs);
3418
3429
  }
3419
3430
  plus(modOrRoll, die) {
3420
3431
  if (typeof modOrRoll === "number" && isNaN(modOrRoll))
3421
3432
  throw new Error("Invalid NaN value for modOrRoll");
3422
3433
  if (die instanceof _RollBuilder && typeof modOrRoll === "number") {
3434
+ if (die.hasHiddenState()) {
3435
+ throw new Error(
3436
+ "Cannot use a roll with hidden state (like a pooled roll) as a die type."
3437
+ );
3438
+ }
3423
3439
  const count = modOrRoll;
3424
3440
  const subRollConfigs = die.getSubRollConfigs();
3425
3441
  if (subRollConfigs.length === 0) return this;
@@ -3444,7 +3460,7 @@ var RollBuilder = class _RollBuilder {
3444
3460
  if (modOrRoll === 0) return this;
3445
3461
  const newConfigs = this.getSubRollConfigs();
3446
3462
  newConfigs[newConfigs.length - 1].modifier += modOrRoll;
3447
- return new this.constructor(newConfigs);
3463
+ return this.create(newConfigs);
3448
3464
  }
3449
3465
  return this.add(modOrRoll);
3450
3466
  }
@@ -3462,7 +3478,7 @@ var RollBuilder = class _RollBuilder {
3462
3478
  if (value === this.lastConfig.reroll) return this;
3463
3479
  const newConfigs = this.getSubRollConfigs();
3464
3480
  newConfigs[newConfigs.length - 1].reroll = value;
3465
- return new this.constructor(newConfigs);
3481
+ return this.create(newConfigs);
3466
3482
  }
3467
3483
  /** Set finite explode count for max-face explosions (Infinity allowed). */
3468
3484
  explode(count = Infinity) {
@@ -3473,7 +3489,7 @@ var RollBuilder = class _RollBuilder {
3473
3489
  if (count < 0) throw new Error("Explode count must be >= 0");
3474
3490
  const newConfigs = this.getSubRollConfigs();
3475
3491
  newConfigs[newConfigs.length - 1].explode = count;
3476
- return new this.constructor(newConfigs);
3492
+ return this.create(newConfigs);
3477
3493
  }
3478
3494
  /** Apply per-die minimum value (min > 0). */
3479
3495
  minimum(val) {
@@ -3484,7 +3500,7 @@ var RollBuilder = class _RollBuilder {
3484
3500
  if (val < 0) throw new Error("Minimum value must be >= 0");
3485
3501
  const newConfigs = this.getSubRollConfigs();
3486
3502
  newConfigs[newConfigs.length - 1].minimum = val + 1;
3487
- return new this.constructor(newConfigs);
3503
+ return this.create(newConfigs);
3488
3504
  }
3489
3505
  bestOf(count) {
3490
3506
  if (count !== void 0 && isNaN(count))
@@ -3493,40 +3509,83 @@ var RollBuilder = class _RollBuilder {
3493
3509
  if (count <= 0) throw new Error("Best of count must be > 0");
3494
3510
  const newConfigs = this.getSubRollConfigs();
3495
3511
  newConfigs[newConfigs.length - 1].bestOf = count;
3496
- return new this.constructor(newConfigs);
3512
+ return this.create(newConfigs);
3497
3513
  }
3498
3514
  keepHighest(total, count) {
3499
3515
  if (isNaN(total) || isNaN(count))
3500
3516
  throw new Error("Invalid NaN value for keepHighest");
3501
3517
  const newConfigs = this.getSubRollConfigs();
3502
3518
  newConfigs[newConfigs.length - 1].keep = { total, count, mode: "highest" };
3503
- return new this.constructor(newConfigs);
3519
+ return this.create(newConfigs);
3504
3520
  }
3505
3521
  keepLowest(total, count) {
3506
3522
  if (isNaN(total) || isNaN(count))
3507
3523
  throw new Error("Invalid NaN value for keepLowest");
3508
3524
  const newConfigs = this.getSubRollConfigs();
3509
3525
  newConfigs[newConfigs.length - 1].keep = { total, count, mode: "lowest" };
3510
- return new this.constructor(newConfigs);
3526
+ return this.create(newConfigs);
3527
+ }
3528
+ keepHighestAll(total, count) {
3529
+ if (isNaN(total) || isNaN(count))
3530
+ throw new Error("Invalid NaN value for keepHighestAll");
3531
+ const currentAST = this.toAST();
3532
+ const trialPool = {
3533
+ type: "sum",
3534
+ count: total,
3535
+ child: currentAST
3536
+ };
3537
+ const keepNode = {
3538
+ type: "keep",
3539
+ mode: "highest",
3540
+ count,
3541
+ child: trialPool
3542
+ };
3543
+ const currentExpr = this.toExpression();
3544
+ const expression = `${total}kh${count}(${currentExpr})`;
3545
+ return new PooledRollBuilder(keepNode, expression);
3546
+ }
3547
+ keepLowestAll(total, count) {
3548
+ if (isNaN(total) || isNaN(count))
3549
+ throw new Error("Invalid NaN value for keepLowestAll");
3550
+ const currentAST = this.toAST();
3551
+ const trialPool = {
3552
+ type: "sum",
3553
+ count: total,
3554
+ child: currentAST
3555
+ };
3556
+ const keepNode = {
3557
+ type: "keep",
3558
+ mode: "lowest",
3559
+ count,
3560
+ child: trialPool
3561
+ };
3562
+ const currentExpr = this.toExpression();
3563
+ const expression = `${total}kl${count}(${currentExpr})`;
3564
+ return new PooledRollBuilder(keepNode, expression);
3511
3565
  }
3512
3566
  withAdvantage() {
3513
3567
  const newConfigs = this.getSubRollConfigs();
3514
3568
  newConfigs[newConfigs.length - 1].rollType = "advantage";
3515
- return new this.constructor(newConfigs);
3569
+ return this.create(newConfigs);
3516
3570
  }
3517
3571
  withDisadvantage() {
3518
3572
  const configs = this.getSubRollConfigs();
3519
3573
  configs[configs.length - 1].rollType = "disadvantage";
3520
- return new this.constructor(configs);
3574
+ return this.create(configs);
3521
3575
  }
3522
3576
  add(anotherRoll) {
3523
3577
  if (anotherRoll === void 0) return this;
3578
+ if (anotherRoll.hasHiddenState()) {
3579
+ throw new Error(
3580
+ "Cannot add a roll with hidden state (like a pooled roll) to a standard roll. Try adding the standard roll to the pooled roll instead: pool.plus(roll)."
3581
+ );
3582
+ }
3524
3583
  const configs = [...this.subRollConfigs, ...anotherRoll.subRollConfigs];
3525
- return new this.constructor(configs);
3584
+ return this.create(configs);
3526
3585
  }
3527
3586
  withBonus(anotherRoll) {
3528
3587
  const configs = [...this.subRollConfigs, ...anotherRoll.subRollConfigs];
3529
- return new this.constructor(configs);
3588
+ return this.create(configs);
3530
3589
  }
3531
3590
  addRoll(count = 1) {
3532
3591
  if (isNaN(count)) throw new Error("Invalid NaN value for count");
@@ -3538,7 +3597,7 @@ var RollBuilder = class _RollBuilder {
3538
3597
  isSubtraction: count < 0
3539
3598
  }
3540
3599
  ];
3541
- return new this.constructor(configs);
3600
+ return this.create(configs);
3542
3601
  }
3543
3602
  scaleDice(scale) {
3544
3603
  const scaleInt = Math.floor(scale);
@@ -3548,7 +3607,7 @@ var RollBuilder = class _RollBuilder {
3548
3607
  if (!config.sides || config.sides <= 0) return config;
3549
3608
  return { ...config, count: config.count * scaleInt };
3550
3609
  });
3551
- return new this.constructor(newConfigs);
3610
+ return this.create(newConfigs);
3552
3611
  }
3553
3612
  doubleDice() {
3554
3613
  return this.scaleDice(2);
@@ -3560,12 +3619,12 @@ var RollBuilder = class _RollBuilder {
3560
3619
  return new AlwaysCritBuilder(this);
3561
3620
  }
3562
3621
  copy() {
3563
- return new this.constructor(this.getSubRollConfigs());
3622
+ return this.create(this.getSubRollConfigs());
3564
3623
  }
3565
3624
  withElvenAccuracy() {
3566
3625
  const newConfigs = this.getSubRollConfigs();
3567
3626
  newConfigs[newConfigs.length - 1].rollType = "elven accuracy";
3568
- return new this.constructor(newConfigs);
3627
+ return this.create(newConfigs);
3569
3628
  }
3570
3629
  toExpression() {
3571
3630
  const originalDiceConfigs = this.subRollConfigs.filter(
@@ -3779,6 +3838,19 @@ var HalfRollBuilder = class _HalfRollBuilder extends RollBuilder {
3779
3838
  super(0);
3780
3839
  this.innerRoll = innerRoll;
3781
3840
  }
3841
+ hasHiddenState() {
3842
+ return this.innerRoll.hasHiddenState();
3843
+ }
3844
+ // No need to override create if we don't expose RollBuilder methods that use it,
3845
+ // but HalfRollBuilder extends RollBuilder so it does.
3846
+ // However, HalfRollBuilder seems to just wrap another roll.
3847
+ // If we call .plus() on HalfRollBuilder, it returns a HalfRollBuilder?
3848
+ // No, RollBuilder.plus returns RollBuilder.
3849
+ // The inheritance here is a bit tricky.
3850
+ // Existing code for HalfRollBuilder doesn't seem to implement plus/etc.
3851
+ // So .plus() on a HalfRollBuilder would return a RollBuilder (base class).
3852
+ // Which is fine.
3853
+ // The only issue is if we want it to return HalfRollBuilder, but it doesn't seem designed for that.
3782
3854
  get lastConfig() {
3783
3855
  return this.innerRoll.lastConfig;
3784
3856
  }
@@ -3810,6 +3882,9 @@ var MaxOfRollBuilder = class _MaxOfRollBuilder extends RollBuilder {
3810
3882
  this.diceCount = diceCount;
3811
3883
  this.diceSides = diceSides;
3812
3884
  }
3885
+ hasHiddenState() {
3886
+ return this.innerRoll.hasHiddenState();
3887
+ }
3813
3888
  get lastConfig() {
3814
3889
  return this.innerRoll.lastConfig;
3815
3890
  }
@@ -3865,6 +3940,11 @@ var MaxOfRollBuilder = class _MaxOfRollBuilder extends RollBuilder {
3865
3940
  };
3866
3941
  var AlwaysHitBuilder = class _AlwaysHitBuilder extends RollBuilder {
3867
3942
  constructor(baseRoll, attackConfig) {
3943
+ if (baseRoll.hasHiddenState()) {
3944
+ throw new Error(
3945
+ "Cannot create AlwaysHitBuilder from a roll with hidden state."
3946
+ );
3947
+ }
3868
3948
  super(baseRoll.getSubRollConfigs());
3869
3949
  __publicField(this, "attackConfig");
3870
3950
  if (attackConfig) {
@@ -3873,6 +3953,9 @@ var AlwaysHitBuilder = class _AlwaysHitBuilder extends RollBuilder {
3873
3953
  this.attackConfig = { critThreshold: 20 };
3874
3954
  }
3875
3955
  }
3956
+ create(configs) {
3957
+ return new RollBuilder(configs);
3958
+ }
3876
3959
  onHit(...args) {
3877
3960
  const damageRoll = RollBuilder.fromArgs(...args);
3878
3961
  return new AttackBuilder(this, damageRoll);
@@ -3907,6 +3990,11 @@ var AlwaysHitBuilder = class _AlwaysHitBuilder extends RollBuilder {
3907
3990
  };
3908
3991
  var AlwaysCritBuilder = class _AlwaysCritBuilder extends RollBuilder {
3909
3992
  constructor(baseRoll, attackConfig, fromAlwaysHit = false) {
3993
+ if (baseRoll.hasHiddenState()) {
3994
+ throw new Error(
3995
+ "Cannot create AlwaysCritBuilder from a roll with hidden state."
3996
+ );
3997
+ }
3910
3998
  super(baseRoll.getSubRollConfigs());
3911
3999
  __publicField(this, "attackConfig");
3912
4000
  __publicField(this, "fromAlwaysHit");
@@ -3917,6 +4005,9 @@ var AlwaysCritBuilder = class _AlwaysCritBuilder extends RollBuilder {
3917
4005
  }
3918
4006
  this.fromAlwaysHit = fromAlwaysHit || baseRoll instanceof AlwaysHitBuilder;
3919
4007
  }
4008
+ create(configs) {
4009
+ return new RollBuilder(configs);
4010
+ }
3920
4011
  onHit(...args) {
3921
4012
  const damageRoll = RollBuilder.fromArgs(...args);
3922
4013
  return new AttackBuilder(this, damageRoll);
@@ -3953,7 +4044,13 @@ var ParsedRollBuilder = class _ParsedRollBuilder extends RollBuilder {
3953
4044
  this.originalExpression = expression;
3954
4045
  this.cachedPMF = parse(expression, 0);
3955
4046
  }
3956
- toPMF(eps = 0) {
4047
+ hasHiddenState() {
4048
+ return true;
4049
+ }
4050
+ create(configs) {
4051
+ return new RollBuilder(configs);
4052
+ }
4053
+ toPMF(_eps = 0) {
3957
4054
  return this.cachedPMF;
3958
4055
  }
3959
4056
  toExpression() {
@@ -3973,10 +4070,122 @@ var ParsedRollBuilder = class _ParsedRollBuilder extends RollBuilder {
3973
4070
  );
3974
4071
  }
3975
4072
  };
4073
+ var PooledRollBuilder = class _PooledRollBuilder extends RollBuilder {
4074
+ constructor(baseAST, baseExpression, configs = []) {
4075
+ super(configs.length > 0 ? configs : 0);
4076
+ this.baseAST = baseAST;
4077
+ this.baseExpression = baseExpression;
4078
+ }
4079
+ create(configs) {
4080
+ return new _PooledRollBuilder(this.baseAST, this.baseExpression, configs);
4081
+ }
4082
+ hasHiddenState() {
4083
+ return true;
4084
+ }
4085
+ d(_sides) {
4086
+ throw new Error("Cannot add dice to a pooled roll. The pool is finalized.");
4087
+ }
4088
+ reroll(_value) {
4089
+ throw new Error("Cannot set reroll on a pooled roll.");
4090
+ }
4091
+ explode(_count = Infinity) {
4092
+ throw new Error("Cannot set explode on a pooled roll.");
4093
+ }
4094
+ minimum(_val) {
4095
+ throw new Error("Cannot set minimum on a pooled roll.");
4096
+ }
4097
+ bestOf(_count) {
4098
+ throw new Error("Cannot set bestOf on a pooled roll.");
4099
+ }
4100
+ keepHighest(_total, _count) {
4101
+ throw new Error(
4102
+ "Cannot use keepHighest on a pooled roll. Use keepHighestAll again if you want nested pooling."
4103
+ );
4104
+ }
4105
+ keepLowest(_total, _count) {
4106
+ throw new Error(
4107
+ "Cannot use keepLowest on a pooled roll. Use keepLowestAll again if you want nested pooling."
4108
+ );
4109
+ }
4110
+ withAdvantage() {
4111
+ throw new Error("Cannot set advantage on a pooled roll.");
4112
+ }
4113
+ withDisadvantage() {
4114
+ throw new Error("Cannot set disadvantage on a pooled roll.");
4115
+ }
4116
+ withElvenAccuracy() {
4117
+ throw new Error("Cannot set elven accuracy on a pooled roll.");
4118
+ }
4119
+ toAST() {
4120
+ const configsAST = super.toAST();
4121
+ const isZero = configsAST.type === "constant" && configsAST.value === 0;
4122
+ if (isZero) {
4123
+ return this.baseAST;
4124
+ }
4125
+ const children = [
4126
+ { node: this.baseAST, sign: 1 },
4127
+ { node: configsAST, sign: 1 }
4128
+ ];
4129
+ return { type: "add", children };
4130
+ }
4131
+ toExpression() {
4132
+ const configsExpression = super.toExpression();
4133
+ if (configsExpression === "0") {
4134
+ return this.baseExpression;
4135
+ }
4136
+ if (configsExpression.startsWith("-")) {
4137
+ return `${this.baseExpression} - ${configsExpression.substring(1)}`;
4138
+ }
4139
+ return `${this.baseExpression} + ${configsExpression}`;
4140
+ }
4141
+ copy() {
4142
+ return new _PooledRollBuilder(
4143
+ this.baseAST,
4144
+ this.baseExpression,
4145
+ this.getSubRollConfigs()
4146
+ );
4147
+ }
4148
+ scaleDice(scale) {
4149
+ const scaleInt = Math.floor(scale);
4150
+ if (scaleInt !== scale) throw new Error("Scale must be an integer");
4151
+ if (scaleInt <= 0) throw new Error("Scale must be > 0");
4152
+ const newBaseAST = {
4153
+ type: "sum",
4154
+ count: scaleInt,
4155
+ child: this.baseAST
4156
+ };
4157
+ const newBaseExpr = scaleInt === 1 ? this.baseExpression : `${scaleInt}(${this.baseExpression})`;
4158
+ return new _PooledRollBuilder(
4159
+ newBaseAST,
4160
+ newBaseExpr,
4161
+ this.getSubRollConfigs()
4162
+ );
4163
+ }
4164
+ times(count) {
4165
+ if (isNaN(count)) throw new Error("Invalid NaN value for times");
4166
+ if (Math.floor(count) !== count)
4167
+ throw new Error("times() requires an integer");
4168
+ if (count < 0) throw new Error("times() requires a non-negative integer");
4169
+ const currentAST = this.toAST();
4170
+ const currentExpr = this.toExpression();
4171
+ const sumNode = {
4172
+ type: "sum",
4173
+ count,
4174
+ child: currentAST
4175
+ };
4176
+ const newExpr = count === 1 ? currentExpr : `${count}(${currentExpr})`;
4177
+ return new _PooledRollBuilder(sumNode, newExpr);
4178
+ }
4179
+ };
3976
4180
 
3977
4181
  // src/builder/factory.ts
3978
4182
  var rollFn = (count, sidesOrDie, modifier) => {
3979
4183
  if (sidesOrDie instanceof RollBuilder) {
4184
+ if (sidesOrDie.hasHiddenState()) {
4185
+ throw new Error(
4186
+ "Cannot use a roll with hidden state (like a pooled roll) as a die type."
4187
+ );
4188
+ }
3980
4189
  const subRollConfigs = sidesOrDie.getSubRollConfigs();
3981
4190
  if (subRollConfigs.length === 0) return new RollBuilder(0).plus(modifier);
3982
4191
  const absCount = Math.abs(count);
@@ -4878,6 +5087,6 @@ RollBuilder.prototype.dc = function(saveDC) {
4878
5087
  return new DCBuilder(this).dc(saveDC);
4879
5088
  };
4880
5089
 
4881
- export { ACBuilder, AlwaysCritBuilder, AlwaysHitBuilder, DCBuilder, HalfRollBuilder, MaxOfRollBuilder, ParsedRollBuilder, RollBuilder, SaveBuilder, builderPMFCache, d, d10, d100, d12, d20, d4, d6, d8, defaultConfig, flat, hd20, roll };
5090
+ export { ACBuilder, AlwaysCritBuilder, AlwaysHitBuilder, DCBuilder, HalfRollBuilder, MaxOfRollBuilder, ParsedRollBuilder, PooledRollBuilder, RollBuilder, SaveBuilder, builderPMFCache, d, d10, d100, d12, d20, d4, d6, d8, defaultConfig, flat, hd20, roll };
4882
5091
  //# sourceMappingURL=index.js.map
4883
5092
  //# sourceMappingURL=index.js.map