@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.
@@ -3340,9 +3340,15 @@ var RollBuilder = class _RollBuilder {
3340
3340
  this.subRollConfigs = countOrConfigs.map((c) => ({ ...c }));
3341
3341
  }
3342
3342
  }
3343
+ create(configs) {
3344
+ return new _RollBuilder(configs);
3345
+ }
3343
3346
  get lastConfig() {
3344
3347
  return this.subRollConfigs[this.subRollConfigs.length - 1];
3345
3348
  }
3349
+ hasHiddenState() {
3350
+ return false;
3351
+ }
3346
3352
  getSubRollConfigs() {
3347
3353
  return this.subRollConfigs.map((c) => ({ ...c }));
3348
3354
  }
@@ -3376,6 +3382,11 @@ var RollBuilder = class _RollBuilder {
3376
3382
  }
3377
3383
  if (isNaN(count)) throw new Error("Invalid NaN value for count argument");
3378
3384
  if (sidesOrDie instanceof _RollBuilder) {
3385
+ if (sidesOrDie.hasHiddenState()) {
3386
+ throw new Error(
3387
+ "Cannot use a roll with hidden state (like a pooled roll) as a die type."
3388
+ );
3389
+ }
3379
3390
  const subRollConfigs = sidesOrDie.getSubRollConfigs();
3380
3391
  if (subRollConfigs.length === 0) {
3381
3392
  const result = new _RollBuilder(0);
@@ -3416,12 +3427,17 @@ var RollBuilder = class _RollBuilder {
3416
3427
  if (sides === 0) return this;
3417
3428
  const newConfigs = this.getSubRollConfigs();
3418
3429
  newConfigs[newConfigs.length - 1].sides = sides;
3419
- return new this.constructor(newConfigs);
3430
+ return this.create(newConfigs);
3420
3431
  }
3421
3432
  plus(modOrRoll, die) {
3422
3433
  if (typeof modOrRoll === "number" && isNaN(modOrRoll))
3423
3434
  throw new Error("Invalid NaN value for modOrRoll");
3424
3435
  if (die instanceof _RollBuilder && typeof modOrRoll === "number") {
3436
+ if (die.hasHiddenState()) {
3437
+ throw new Error(
3438
+ "Cannot use a roll with hidden state (like a pooled roll) as a die type."
3439
+ );
3440
+ }
3425
3441
  const count = modOrRoll;
3426
3442
  const subRollConfigs = die.getSubRollConfigs();
3427
3443
  if (subRollConfigs.length === 0) return this;
@@ -3446,7 +3462,7 @@ var RollBuilder = class _RollBuilder {
3446
3462
  if (modOrRoll === 0) return this;
3447
3463
  const newConfigs = this.getSubRollConfigs();
3448
3464
  newConfigs[newConfigs.length - 1].modifier += modOrRoll;
3449
- return new this.constructor(newConfigs);
3465
+ return this.create(newConfigs);
3450
3466
  }
3451
3467
  return this.add(modOrRoll);
3452
3468
  }
@@ -3464,7 +3480,7 @@ var RollBuilder = class _RollBuilder {
3464
3480
  if (value === this.lastConfig.reroll) return this;
3465
3481
  const newConfigs = this.getSubRollConfigs();
3466
3482
  newConfigs[newConfigs.length - 1].reroll = value;
3467
- return new this.constructor(newConfigs);
3483
+ return this.create(newConfigs);
3468
3484
  }
3469
3485
  /** Set finite explode count for max-face explosions (Infinity allowed). */
3470
3486
  explode(count = Infinity) {
@@ -3475,7 +3491,7 @@ var RollBuilder = class _RollBuilder {
3475
3491
  if (count < 0) throw new Error("Explode count must be >= 0");
3476
3492
  const newConfigs = this.getSubRollConfigs();
3477
3493
  newConfigs[newConfigs.length - 1].explode = count;
3478
- return new this.constructor(newConfigs);
3494
+ return this.create(newConfigs);
3479
3495
  }
3480
3496
  /** Apply per-die minimum value (min > 0). */
3481
3497
  minimum(val) {
@@ -3486,7 +3502,7 @@ var RollBuilder = class _RollBuilder {
3486
3502
  if (val < 0) throw new Error("Minimum value must be >= 0");
3487
3503
  const newConfigs = this.getSubRollConfigs();
3488
3504
  newConfigs[newConfigs.length - 1].minimum = val + 1;
3489
- return new this.constructor(newConfigs);
3505
+ return this.create(newConfigs);
3490
3506
  }
3491
3507
  bestOf(count) {
3492
3508
  if (count !== void 0 && isNaN(count))
@@ -3495,40 +3511,83 @@ var RollBuilder = class _RollBuilder {
3495
3511
  if (count <= 0) throw new Error("Best of count must be > 0");
3496
3512
  const newConfigs = this.getSubRollConfigs();
3497
3513
  newConfigs[newConfigs.length - 1].bestOf = count;
3498
- return new this.constructor(newConfigs);
3514
+ return this.create(newConfigs);
3499
3515
  }
3500
3516
  keepHighest(total, count) {
3501
3517
  if (isNaN(total) || isNaN(count))
3502
3518
  throw new Error("Invalid NaN value for keepHighest");
3503
3519
  const newConfigs = this.getSubRollConfigs();
3504
3520
  newConfigs[newConfigs.length - 1].keep = { total, count, mode: "highest" };
3505
- return new this.constructor(newConfigs);
3521
+ return this.create(newConfigs);
3506
3522
  }
3507
3523
  keepLowest(total, count) {
3508
3524
  if (isNaN(total) || isNaN(count))
3509
3525
  throw new Error("Invalid NaN value for keepLowest");
3510
3526
  const newConfigs = this.getSubRollConfigs();
3511
3527
  newConfigs[newConfigs.length - 1].keep = { total, count, mode: "lowest" };
3512
- return new this.constructor(newConfigs);
3528
+ return this.create(newConfigs);
3529
+ }
3530
+ keepHighestAll(total, count) {
3531
+ if (isNaN(total) || isNaN(count))
3532
+ throw new Error("Invalid NaN value for keepHighestAll");
3533
+ const currentAST = this.toAST();
3534
+ const trialPool = {
3535
+ type: "sum",
3536
+ count: total,
3537
+ child: currentAST
3538
+ };
3539
+ const keepNode = {
3540
+ type: "keep",
3541
+ mode: "highest",
3542
+ count,
3543
+ child: trialPool
3544
+ };
3545
+ const currentExpr = this.toExpression();
3546
+ const expression = `${total}kh${count}(${currentExpr})`;
3547
+ return new PooledRollBuilder(keepNode, expression);
3548
+ }
3549
+ keepLowestAll(total, count) {
3550
+ if (isNaN(total) || isNaN(count))
3551
+ throw new Error("Invalid NaN value for keepLowestAll");
3552
+ const currentAST = this.toAST();
3553
+ const trialPool = {
3554
+ type: "sum",
3555
+ count: total,
3556
+ child: currentAST
3557
+ };
3558
+ const keepNode = {
3559
+ type: "keep",
3560
+ mode: "lowest",
3561
+ count,
3562
+ child: trialPool
3563
+ };
3564
+ const currentExpr = this.toExpression();
3565
+ const expression = `${total}kl${count}(${currentExpr})`;
3566
+ return new PooledRollBuilder(keepNode, expression);
3513
3567
  }
3514
3568
  withAdvantage() {
3515
3569
  const newConfigs = this.getSubRollConfigs();
3516
3570
  newConfigs[newConfigs.length - 1].rollType = "advantage";
3517
- return new this.constructor(newConfigs);
3571
+ return this.create(newConfigs);
3518
3572
  }
3519
3573
  withDisadvantage() {
3520
3574
  const configs = this.getSubRollConfigs();
3521
3575
  configs[configs.length - 1].rollType = "disadvantage";
3522
- return new this.constructor(configs);
3576
+ return this.create(configs);
3523
3577
  }
3524
3578
  add(anotherRoll) {
3525
3579
  if (anotherRoll === void 0) return this;
3580
+ if (anotherRoll.hasHiddenState()) {
3581
+ throw new Error(
3582
+ "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)."
3583
+ );
3584
+ }
3526
3585
  const configs = [...this.subRollConfigs, ...anotherRoll.subRollConfigs];
3527
- return new this.constructor(configs);
3586
+ return this.create(configs);
3528
3587
  }
3529
3588
  withBonus(anotherRoll) {
3530
3589
  const configs = [...this.subRollConfigs, ...anotherRoll.subRollConfigs];
3531
- return new this.constructor(configs);
3590
+ return this.create(configs);
3532
3591
  }
3533
3592
  addRoll(count = 1) {
3534
3593
  if (isNaN(count)) throw new Error("Invalid NaN value for count");
@@ -3540,7 +3599,7 @@ var RollBuilder = class _RollBuilder {
3540
3599
  isSubtraction: count < 0
3541
3600
  }
3542
3601
  ];
3543
- return new this.constructor(configs);
3602
+ return this.create(configs);
3544
3603
  }
3545
3604
  scaleDice(scale) {
3546
3605
  const scaleInt = Math.floor(scale);
@@ -3550,7 +3609,7 @@ var RollBuilder = class _RollBuilder {
3550
3609
  if (!config.sides || config.sides <= 0) return config;
3551
3610
  return { ...config, count: config.count * scaleInt };
3552
3611
  });
3553
- return new this.constructor(newConfigs);
3612
+ return this.create(newConfigs);
3554
3613
  }
3555
3614
  doubleDice() {
3556
3615
  return this.scaleDice(2);
@@ -3562,12 +3621,12 @@ var RollBuilder = class _RollBuilder {
3562
3621
  return new AlwaysCritBuilder(this);
3563
3622
  }
3564
3623
  copy() {
3565
- return new this.constructor(this.getSubRollConfigs());
3624
+ return this.create(this.getSubRollConfigs());
3566
3625
  }
3567
3626
  withElvenAccuracy() {
3568
3627
  const newConfigs = this.getSubRollConfigs();
3569
3628
  newConfigs[newConfigs.length - 1].rollType = "elven accuracy";
3570
- return new this.constructor(newConfigs);
3629
+ return this.create(newConfigs);
3571
3630
  }
3572
3631
  toExpression() {
3573
3632
  const originalDiceConfigs = this.subRollConfigs.filter(
@@ -3781,6 +3840,19 @@ var HalfRollBuilder = class _HalfRollBuilder extends RollBuilder {
3781
3840
  super(0);
3782
3841
  this.innerRoll = innerRoll;
3783
3842
  }
3843
+ hasHiddenState() {
3844
+ return this.innerRoll.hasHiddenState();
3845
+ }
3846
+ // No need to override create if we don't expose RollBuilder methods that use it,
3847
+ // but HalfRollBuilder extends RollBuilder so it does.
3848
+ // However, HalfRollBuilder seems to just wrap another roll.
3849
+ // If we call .plus() on HalfRollBuilder, it returns a HalfRollBuilder?
3850
+ // No, RollBuilder.plus returns RollBuilder.
3851
+ // The inheritance here is a bit tricky.
3852
+ // Existing code for HalfRollBuilder doesn't seem to implement plus/etc.
3853
+ // So .plus() on a HalfRollBuilder would return a RollBuilder (base class).
3854
+ // Which is fine.
3855
+ // The only issue is if we want it to return HalfRollBuilder, but it doesn't seem designed for that.
3784
3856
  get lastConfig() {
3785
3857
  return this.innerRoll.lastConfig;
3786
3858
  }
@@ -3812,6 +3884,9 @@ var MaxOfRollBuilder = class _MaxOfRollBuilder extends RollBuilder {
3812
3884
  this.diceCount = diceCount;
3813
3885
  this.diceSides = diceSides;
3814
3886
  }
3887
+ hasHiddenState() {
3888
+ return this.innerRoll.hasHiddenState();
3889
+ }
3815
3890
  get lastConfig() {
3816
3891
  return this.innerRoll.lastConfig;
3817
3892
  }
@@ -3867,6 +3942,11 @@ var MaxOfRollBuilder = class _MaxOfRollBuilder extends RollBuilder {
3867
3942
  };
3868
3943
  var AlwaysHitBuilder = class _AlwaysHitBuilder extends RollBuilder {
3869
3944
  constructor(baseRoll, attackConfig) {
3945
+ if (baseRoll.hasHiddenState()) {
3946
+ throw new Error(
3947
+ "Cannot create AlwaysHitBuilder from a roll with hidden state."
3948
+ );
3949
+ }
3870
3950
  super(baseRoll.getSubRollConfigs());
3871
3951
  __publicField(this, "attackConfig");
3872
3952
  if (attackConfig) {
@@ -3875,6 +3955,9 @@ var AlwaysHitBuilder = class _AlwaysHitBuilder extends RollBuilder {
3875
3955
  this.attackConfig = { critThreshold: 20 };
3876
3956
  }
3877
3957
  }
3958
+ create(configs) {
3959
+ return new RollBuilder(configs);
3960
+ }
3878
3961
  onHit(...args) {
3879
3962
  const damageRoll = RollBuilder.fromArgs(...args);
3880
3963
  return new AttackBuilder(this, damageRoll);
@@ -3909,6 +3992,11 @@ var AlwaysHitBuilder = class _AlwaysHitBuilder extends RollBuilder {
3909
3992
  };
3910
3993
  var AlwaysCritBuilder = class _AlwaysCritBuilder extends RollBuilder {
3911
3994
  constructor(baseRoll, attackConfig, fromAlwaysHit = false) {
3995
+ if (baseRoll.hasHiddenState()) {
3996
+ throw new Error(
3997
+ "Cannot create AlwaysCritBuilder from a roll with hidden state."
3998
+ );
3999
+ }
3912
4000
  super(baseRoll.getSubRollConfigs());
3913
4001
  __publicField(this, "attackConfig");
3914
4002
  __publicField(this, "fromAlwaysHit");
@@ -3919,6 +4007,9 @@ var AlwaysCritBuilder = class _AlwaysCritBuilder extends RollBuilder {
3919
4007
  }
3920
4008
  this.fromAlwaysHit = fromAlwaysHit || baseRoll instanceof AlwaysHitBuilder;
3921
4009
  }
4010
+ create(configs) {
4011
+ return new RollBuilder(configs);
4012
+ }
3922
4013
  onHit(...args) {
3923
4014
  const damageRoll = RollBuilder.fromArgs(...args);
3924
4015
  return new AttackBuilder(this, damageRoll);
@@ -3955,7 +4046,13 @@ var ParsedRollBuilder = class _ParsedRollBuilder extends RollBuilder {
3955
4046
  this.originalExpression = expression;
3956
4047
  this.cachedPMF = parse(expression, 0);
3957
4048
  }
3958
- toPMF(eps = 0) {
4049
+ hasHiddenState() {
4050
+ return true;
4051
+ }
4052
+ create(configs) {
4053
+ return new RollBuilder(configs);
4054
+ }
4055
+ toPMF(_eps = 0) {
3959
4056
  return this.cachedPMF;
3960
4057
  }
3961
4058
  toExpression() {
@@ -3975,10 +4072,122 @@ var ParsedRollBuilder = class _ParsedRollBuilder extends RollBuilder {
3975
4072
  );
3976
4073
  }
3977
4074
  };
4075
+ var PooledRollBuilder = class _PooledRollBuilder extends RollBuilder {
4076
+ constructor(baseAST, baseExpression, configs = []) {
4077
+ super(configs.length > 0 ? configs : 0);
4078
+ this.baseAST = baseAST;
4079
+ this.baseExpression = baseExpression;
4080
+ }
4081
+ create(configs) {
4082
+ return new _PooledRollBuilder(this.baseAST, this.baseExpression, configs);
4083
+ }
4084
+ hasHiddenState() {
4085
+ return true;
4086
+ }
4087
+ d(_sides) {
4088
+ throw new Error("Cannot add dice to a pooled roll. The pool is finalized.");
4089
+ }
4090
+ reroll(_value) {
4091
+ throw new Error("Cannot set reroll on a pooled roll.");
4092
+ }
4093
+ explode(_count = Infinity) {
4094
+ throw new Error("Cannot set explode on a pooled roll.");
4095
+ }
4096
+ minimum(_val) {
4097
+ throw new Error("Cannot set minimum on a pooled roll.");
4098
+ }
4099
+ bestOf(_count) {
4100
+ throw new Error("Cannot set bestOf on a pooled roll.");
4101
+ }
4102
+ keepHighest(_total, _count) {
4103
+ throw new Error(
4104
+ "Cannot use keepHighest on a pooled roll. Use keepHighestAll again if you want nested pooling."
4105
+ );
4106
+ }
4107
+ keepLowest(_total, _count) {
4108
+ throw new Error(
4109
+ "Cannot use keepLowest on a pooled roll. Use keepLowestAll again if you want nested pooling."
4110
+ );
4111
+ }
4112
+ withAdvantage() {
4113
+ throw new Error("Cannot set advantage on a pooled roll.");
4114
+ }
4115
+ withDisadvantage() {
4116
+ throw new Error("Cannot set disadvantage on a pooled roll.");
4117
+ }
4118
+ withElvenAccuracy() {
4119
+ throw new Error("Cannot set elven accuracy on a pooled roll.");
4120
+ }
4121
+ toAST() {
4122
+ const configsAST = super.toAST();
4123
+ const isZero = configsAST.type === "constant" && configsAST.value === 0;
4124
+ if (isZero) {
4125
+ return this.baseAST;
4126
+ }
4127
+ const children = [
4128
+ { node: this.baseAST, sign: 1 },
4129
+ { node: configsAST, sign: 1 }
4130
+ ];
4131
+ return { type: "add", children };
4132
+ }
4133
+ toExpression() {
4134
+ const configsExpression = super.toExpression();
4135
+ if (configsExpression === "0") {
4136
+ return this.baseExpression;
4137
+ }
4138
+ if (configsExpression.startsWith("-")) {
4139
+ return `${this.baseExpression} - ${configsExpression.substring(1)}`;
4140
+ }
4141
+ return `${this.baseExpression} + ${configsExpression}`;
4142
+ }
4143
+ copy() {
4144
+ return new _PooledRollBuilder(
4145
+ this.baseAST,
4146
+ this.baseExpression,
4147
+ this.getSubRollConfigs()
4148
+ );
4149
+ }
4150
+ scaleDice(scale) {
4151
+ const scaleInt = Math.floor(scale);
4152
+ if (scaleInt !== scale) throw new Error("Scale must be an integer");
4153
+ if (scaleInt <= 0) throw new Error("Scale must be > 0");
4154
+ const newBaseAST = {
4155
+ type: "sum",
4156
+ count: scaleInt,
4157
+ child: this.baseAST
4158
+ };
4159
+ const newBaseExpr = scaleInt === 1 ? this.baseExpression : `${scaleInt}(${this.baseExpression})`;
4160
+ return new _PooledRollBuilder(
4161
+ newBaseAST,
4162
+ newBaseExpr,
4163
+ this.getSubRollConfigs()
4164
+ );
4165
+ }
4166
+ times(count) {
4167
+ if (isNaN(count)) throw new Error("Invalid NaN value for times");
4168
+ if (Math.floor(count) !== count)
4169
+ throw new Error("times() requires an integer");
4170
+ if (count < 0) throw new Error("times() requires a non-negative integer");
4171
+ const currentAST = this.toAST();
4172
+ const currentExpr = this.toExpression();
4173
+ const sumNode = {
4174
+ type: "sum",
4175
+ count,
4176
+ child: currentAST
4177
+ };
4178
+ const newExpr = count === 1 ? currentExpr : `${count}(${currentExpr})`;
4179
+ return new _PooledRollBuilder(sumNode, newExpr);
4180
+ }
4181
+ };
3978
4182
 
3979
4183
  // src/builder/factory.ts
3980
4184
  var rollFn = (count, sidesOrDie, modifier) => {
3981
4185
  if (sidesOrDie instanceof RollBuilder) {
4186
+ if (sidesOrDie.hasHiddenState()) {
4187
+ throw new Error(
4188
+ "Cannot use a roll with hidden state (like a pooled roll) as a die type."
4189
+ );
4190
+ }
3982
4191
  const subRollConfigs = sidesOrDie.getSubRollConfigs();
3983
4192
  if (subRollConfigs.length === 0) return new RollBuilder(0).plus(modifier);
3984
4193
  const absCount = Math.abs(count);
@@ -4887,6 +5096,7 @@ exports.DCBuilder = DCBuilder;
4887
5096
  exports.HalfRollBuilder = HalfRollBuilder;
4888
5097
  exports.MaxOfRollBuilder = MaxOfRollBuilder;
4889
5098
  exports.ParsedRollBuilder = ParsedRollBuilder;
5099
+ exports.PooledRollBuilder = PooledRollBuilder;
4890
5100
  exports.RollBuilder = RollBuilder;
4891
5101
  exports.SaveBuilder = SaveBuilder;
4892
5102
  exports.builderPMFCache = builderPMFCache;