@yipe/dice 0.2.20 → 0.2.22

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.
@@ -2422,7 +2422,9 @@ var Dice = class _Dice {
2422
2422
  if (key === "hit") {
2423
2423
  this.ensureHitDistribution();
2424
2424
  }
2425
- return this.outcomeData[key];
2425
+ const distribution = this.outcomeData[key];
2426
+ if (distribution === void 0) return void 0;
2427
+ return { ...distribution };
2426
2428
  }
2427
2429
  getFullOutcomeDistribution() {
2428
2430
  return { ...this.outcomeData };
@@ -3340,9 +3342,15 @@ var RollBuilder = class _RollBuilder {
3340
3342
  this.subRollConfigs = countOrConfigs.map((c) => ({ ...c }));
3341
3343
  }
3342
3344
  }
3345
+ create(configs) {
3346
+ return new _RollBuilder(configs);
3347
+ }
3343
3348
  get lastConfig() {
3344
3349
  return this.subRollConfigs[this.subRollConfigs.length - 1];
3345
3350
  }
3351
+ hasHiddenState() {
3352
+ return false;
3353
+ }
3346
3354
  getSubRollConfigs() {
3347
3355
  return this.subRollConfigs.map((c) => ({ ...c }));
3348
3356
  }
@@ -3376,6 +3384,11 @@ var RollBuilder = class _RollBuilder {
3376
3384
  }
3377
3385
  if (isNaN(count)) throw new Error("Invalid NaN value for count argument");
3378
3386
  if (sidesOrDie instanceof _RollBuilder) {
3387
+ if (sidesOrDie.hasHiddenState()) {
3388
+ throw new Error(
3389
+ "Cannot use a roll with hidden state (like a pooled roll) as a die type."
3390
+ );
3391
+ }
3379
3392
  const subRollConfigs = sidesOrDie.getSubRollConfigs();
3380
3393
  if (subRollConfigs.length === 0) {
3381
3394
  const result = new _RollBuilder(0);
@@ -3416,12 +3429,17 @@ var RollBuilder = class _RollBuilder {
3416
3429
  if (sides === 0) return this;
3417
3430
  const newConfigs = this.getSubRollConfigs();
3418
3431
  newConfigs[newConfigs.length - 1].sides = sides;
3419
- return new this.constructor(newConfigs);
3432
+ return this.create(newConfigs);
3420
3433
  }
3421
3434
  plus(modOrRoll, die) {
3422
3435
  if (typeof modOrRoll === "number" && isNaN(modOrRoll))
3423
3436
  throw new Error("Invalid NaN value for modOrRoll");
3424
3437
  if (die instanceof _RollBuilder && typeof modOrRoll === "number") {
3438
+ if (die.hasHiddenState()) {
3439
+ throw new Error(
3440
+ "Cannot use a roll with hidden state (like a pooled roll) as a die type."
3441
+ );
3442
+ }
3425
3443
  const count = modOrRoll;
3426
3444
  const subRollConfigs = die.getSubRollConfigs();
3427
3445
  if (subRollConfigs.length === 0) return this;
@@ -3446,7 +3464,7 @@ var RollBuilder = class _RollBuilder {
3446
3464
  if (modOrRoll === 0) return this;
3447
3465
  const newConfigs = this.getSubRollConfigs();
3448
3466
  newConfigs[newConfigs.length - 1].modifier += modOrRoll;
3449
- return new this.constructor(newConfigs);
3467
+ return this.create(newConfigs);
3450
3468
  }
3451
3469
  return this.add(modOrRoll);
3452
3470
  }
@@ -3464,7 +3482,7 @@ var RollBuilder = class _RollBuilder {
3464
3482
  if (value === this.lastConfig.reroll) return this;
3465
3483
  const newConfigs = this.getSubRollConfigs();
3466
3484
  newConfigs[newConfigs.length - 1].reroll = value;
3467
- return new this.constructor(newConfigs);
3485
+ return this.create(newConfigs);
3468
3486
  }
3469
3487
  /** Set finite explode count for max-face explosions (Infinity allowed). */
3470
3488
  explode(count = Infinity) {
@@ -3475,7 +3493,7 @@ var RollBuilder = class _RollBuilder {
3475
3493
  if (count < 0) throw new Error("Explode count must be >= 0");
3476
3494
  const newConfigs = this.getSubRollConfigs();
3477
3495
  newConfigs[newConfigs.length - 1].explode = count;
3478
- return new this.constructor(newConfigs);
3496
+ return this.create(newConfigs);
3479
3497
  }
3480
3498
  /** Apply per-die minimum value (min > 0). */
3481
3499
  minimum(val) {
@@ -3486,7 +3504,7 @@ var RollBuilder = class _RollBuilder {
3486
3504
  if (val < 0) throw new Error("Minimum value must be >= 0");
3487
3505
  const newConfigs = this.getSubRollConfigs();
3488
3506
  newConfigs[newConfigs.length - 1].minimum = val + 1;
3489
- return new this.constructor(newConfigs);
3507
+ return this.create(newConfigs);
3490
3508
  }
3491
3509
  bestOf(count) {
3492
3510
  if (count !== void 0 && isNaN(count))
@@ -3495,40 +3513,83 @@ var RollBuilder = class _RollBuilder {
3495
3513
  if (count <= 0) throw new Error("Best of count must be > 0");
3496
3514
  const newConfigs = this.getSubRollConfigs();
3497
3515
  newConfigs[newConfigs.length - 1].bestOf = count;
3498
- return new this.constructor(newConfigs);
3516
+ return this.create(newConfigs);
3499
3517
  }
3500
3518
  keepHighest(total, count) {
3501
3519
  if (isNaN(total) || isNaN(count))
3502
3520
  throw new Error("Invalid NaN value for keepHighest");
3503
3521
  const newConfigs = this.getSubRollConfigs();
3504
3522
  newConfigs[newConfigs.length - 1].keep = { total, count, mode: "highest" };
3505
- return new this.constructor(newConfigs);
3523
+ return this.create(newConfigs);
3506
3524
  }
3507
3525
  keepLowest(total, count) {
3508
3526
  if (isNaN(total) || isNaN(count))
3509
3527
  throw new Error("Invalid NaN value for keepLowest");
3510
3528
  const newConfigs = this.getSubRollConfigs();
3511
3529
  newConfigs[newConfigs.length - 1].keep = { total, count, mode: "lowest" };
3512
- return new this.constructor(newConfigs);
3530
+ return this.create(newConfigs);
3531
+ }
3532
+ keepHighestAll(total, count) {
3533
+ if (isNaN(total) || isNaN(count))
3534
+ throw new Error("Invalid NaN value for keepHighestAll");
3535
+ const currentAST = this.toAST();
3536
+ const trialPool = {
3537
+ type: "sum",
3538
+ count: total,
3539
+ child: currentAST
3540
+ };
3541
+ const keepNode = {
3542
+ type: "keep",
3543
+ mode: "highest",
3544
+ count,
3545
+ child: trialPool
3546
+ };
3547
+ const currentExpr = this.toExpression();
3548
+ const expression = `${total}kh${count}(${currentExpr})`;
3549
+ return new PooledRollBuilder(keepNode, expression);
3550
+ }
3551
+ keepLowestAll(total, count) {
3552
+ if (isNaN(total) || isNaN(count))
3553
+ throw new Error("Invalid NaN value for keepLowestAll");
3554
+ const currentAST = this.toAST();
3555
+ const trialPool = {
3556
+ type: "sum",
3557
+ count: total,
3558
+ child: currentAST
3559
+ };
3560
+ const keepNode = {
3561
+ type: "keep",
3562
+ mode: "lowest",
3563
+ count,
3564
+ child: trialPool
3565
+ };
3566
+ const currentExpr = this.toExpression();
3567
+ const expression = `${total}kl${count}(${currentExpr})`;
3568
+ return new PooledRollBuilder(keepNode, expression);
3513
3569
  }
3514
3570
  withAdvantage() {
3515
3571
  const newConfigs = this.getSubRollConfigs();
3516
3572
  newConfigs[newConfigs.length - 1].rollType = "advantage";
3517
- return new this.constructor(newConfigs);
3573
+ return this.create(newConfigs);
3518
3574
  }
3519
3575
  withDisadvantage() {
3520
3576
  const configs = this.getSubRollConfigs();
3521
3577
  configs[configs.length - 1].rollType = "disadvantage";
3522
- return new this.constructor(configs);
3578
+ return this.create(configs);
3523
3579
  }
3524
3580
  add(anotherRoll) {
3525
3581
  if (anotherRoll === void 0) return this;
3582
+ if (anotherRoll.hasHiddenState()) {
3583
+ throw new Error(
3584
+ "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)."
3585
+ );
3586
+ }
3526
3587
  const configs = [...this.subRollConfigs, ...anotherRoll.subRollConfigs];
3527
- return new this.constructor(configs);
3588
+ return this.create(configs);
3528
3589
  }
3529
3590
  withBonus(anotherRoll) {
3530
3591
  const configs = [...this.subRollConfigs, ...anotherRoll.subRollConfigs];
3531
- return new this.constructor(configs);
3592
+ return this.create(configs);
3532
3593
  }
3533
3594
  addRoll(count = 1) {
3534
3595
  if (isNaN(count)) throw new Error("Invalid NaN value for count");
@@ -3540,7 +3601,7 @@ var RollBuilder = class _RollBuilder {
3540
3601
  isSubtraction: count < 0
3541
3602
  }
3542
3603
  ];
3543
- return new this.constructor(configs);
3604
+ return this.create(configs);
3544
3605
  }
3545
3606
  scaleDice(scale) {
3546
3607
  const scaleInt = Math.floor(scale);
@@ -3550,7 +3611,7 @@ var RollBuilder = class _RollBuilder {
3550
3611
  if (!config.sides || config.sides <= 0) return config;
3551
3612
  return { ...config, count: config.count * scaleInt };
3552
3613
  });
3553
- return new this.constructor(newConfigs);
3614
+ return this.create(newConfigs);
3554
3615
  }
3555
3616
  doubleDice() {
3556
3617
  return this.scaleDice(2);
@@ -3562,12 +3623,12 @@ var RollBuilder = class _RollBuilder {
3562
3623
  return new AlwaysCritBuilder(this);
3563
3624
  }
3564
3625
  copy() {
3565
- return new this.constructor(this.getSubRollConfigs());
3626
+ return this.create(this.getSubRollConfigs());
3566
3627
  }
3567
3628
  withElvenAccuracy() {
3568
3629
  const newConfigs = this.getSubRollConfigs();
3569
3630
  newConfigs[newConfigs.length - 1].rollType = "elven accuracy";
3570
- return new this.constructor(newConfigs);
3631
+ return this.create(newConfigs);
3571
3632
  }
3572
3633
  toExpression() {
3573
3634
  const originalDiceConfigs = this.subRollConfigs.filter(
@@ -3775,12 +3836,33 @@ var RollBuilder = class _RollBuilder {
3775
3836
  maxOf(count) {
3776
3837
  return new MaxOfRollBuilder(this, count);
3777
3838
  }
3839
+ // These methods are implemented via prototype augmentation in ac.ts and dc.ts
3840
+ // They are declared here to provide proper TypeScript types
3841
+ ac(targetAC) {
3842
+ throw new Error("ac() should be implemented via prototype augmentation");
3843
+ }
3844
+ dc(saveDC) {
3845
+ throw new Error("dc() should be implemented via prototype augmentation");
3846
+ }
3778
3847
  };
3779
3848
  var HalfRollBuilder = class _HalfRollBuilder extends RollBuilder {
3780
3849
  constructor(innerRoll) {
3781
3850
  super(0);
3782
3851
  this.innerRoll = innerRoll;
3783
3852
  }
3853
+ hasHiddenState() {
3854
+ return this.innerRoll.hasHiddenState();
3855
+ }
3856
+ // No need to override create if we don't expose RollBuilder methods that use it,
3857
+ // but HalfRollBuilder extends RollBuilder so it does.
3858
+ // However, HalfRollBuilder seems to just wrap another roll.
3859
+ // If we call .plus() on HalfRollBuilder, it returns a HalfRollBuilder?
3860
+ // No, RollBuilder.plus returns RollBuilder.
3861
+ // The inheritance here is a bit tricky.
3862
+ // Existing code for HalfRollBuilder doesn't seem to implement plus/etc.
3863
+ // So .plus() on a HalfRollBuilder would return a RollBuilder (base class).
3864
+ // Which is fine.
3865
+ // The only issue is if we want it to return HalfRollBuilder, but it doesn't seem designed for that.
3784
3866
  get lastConfig() {
3785
3867
  return this.innerRoll.lastConfig;
3786
3868
  }
@@ -3812,6 +3894,9 @@ var MaxOfRollBuilder = class _MaxOfRollBuilder extends RollBuilder {
3812
3894
  this.diceCount = diceCount;
3813
3895
  this.diceSides = diceSides;
3814
3896
  }
3897
+ hasHiddenState() {
3898
+ return this.innerRoll.hasHiddenState();
3899
+ }
3815
3900
  get lastConfig() {
3816
3901
  return this.innerRoll.lastConfig;
3817
3902
  }
@@ -3867,6 +3952,11 @@ var MaxOfRollBuilder = class _MaxOfRollBuilder extends RollBuilder {
3867
3952
  };
3868
3953
  var AlwaysHitBuilder = class _AlwaysHitBuilder extends RollBuilder {
3869
3954
  constructor(baseRoll, attackConfig) {
3955
+ if (baseRoll.hasHiddenState()) {
3956
+ throw new Error(
3957
+ "Cannot create AlwaysHitBuilder from a roll with hidden state."
3958
+ );
3959
+ }
3870
3960
  super(baseRoll.getSubRollConfigs());
3871
3961
  __publicField(this, "attackConfig");
3872
3962
  if (attackConfig) {
@@ -3875,6 +3965,9 @@ var AlwaysHitBuilder = class _AlwaysHitBuilder extends RollBuilder {
3875
3965
  this.attackConfig = { critThreshold: 20 };
3876
3966
  }
3877
3967
  }
3968
+ create(configs) {
3969
+ return new RollBuilder(configs);
3970
+ }
3878
3971
  onHit(...args) {
3879
3972
  const damageRoll = RollBuilder.fromArgs(...args);
3880
3973
  return new AttackBuilder(this, damageRoll);
@@ -3909,6 +4002,11 @@ var AlwaysHitBuilder = class _AlwaysHitBuilder extends RollBuilder {
3909
4002
  };
3910
4003
  var AlwaysCritBuilder = class _AlwaysCritBuilder extends RollBuilder {
3911
4004
  constructor(baseRoll, attackConfig, fromAlwaysHit = false) {
4005
+ if (baseRoll.hasHiddenState()) {
4006
+ throw new Error(
4007
+ "Cannot create AlwaysCritBuilder from a roll with hidden state."
4008
+ );
4009
+ }
3912
4010
  super(baseRoll.getSubRollConfigs());
3913
4011
  __publicField(this, "attackConfig");
3914
4012
  __publicField(this, "fromAlwaysHit");
@@ -3919,6 +4017,9 @@ var AlwaysCritBuilder = class _AlwaysCritBuilder extends RollBuilder {
3919
4017
  }
3920
4018
  this.fromAlwaysHit = fromAlwaysHit || baseRoll instanceof AlwaysHitBuilder;
3921
4019
  }
4020
+ create(configs) {
4021
+ return new RollBuilder(configs);
4022
+ }
3922
4023
  onHit(...args) {
3923
4024
  const damageRoll = RollBuilder.fromArgs(...args);
3924
4025
  return new AttackBuilder(this, damageRoll);
@@ -3955,7 +4056,13 @@ var ParsedRollBuilder = class _ParsedRollBuilder extends RollBuilder {
3955
4056
  this.originalExpression = expression;
3956
4057
  this.cachedPMF = parse(expression, 0);
3957
4058
  }
3958
- toPMF(eps = 0) {
4059
+ hasHiddenState() {
4060
+ return true;
4061
+ }
4062
+ create(configs) {
4063
+ return new RollBuilder(configs);
4064
+ }
4065
+ toPMF(_eps = 0) {
3959
4066
  return this.cachedPMF;
3960
4067
  }
3961
4068
  toExpression() {
@@ -3975,10 +4082,122 @@ var ParsedRollBuilder = class _ParsedRollBuilder extends RollBuilder {
3975
4082
  );
3976
4083
  }
3977
4084
  };
4085
+ var PooledRollBuilder = class _PooledRollBuilder extends RollBuilder {
4086
+ constructor(baseAST, baseExpression, configs = []) {
4087
+ super(configs.length > 0 ? configs : 0);
4088
+ this.baseAST = baseAST;
4089
+ this.baseExpression = baseExpression;
4090
+ }
4091
+ create(configs) {
4092
+ return new _PooledRollBuilder(this.baseAST, this.baseExpression, configs);
4093
+ }
4094
+ hasHiddenState() {
4095
+ return true;
4096
+ }
4097
+ d(_sides) {
4098
+ throw new Error("Cannot add dice to a pooled roll. The pool is finalized.");
4099
+ }
4100
+ reroll(_value) {
4101
+ throw new Error("Cannot set reroll on a pooled roll.");
4102
+ }
4103
+ explode(_count = Infinity) {
4104
+ throw new Error("Cannot set explode on a pooled roll.");
4105
+ }
4106
+ minimum(_val) {
4107
+ throw new Error("Cannot set minimum on a pooled roll.");
4108
+ }
4109
+ bestOf(_count) {
4110
+ throw new Error("Cannot set bestOf on a pooled roll.");
4111
+ }
4112
+ keepHighest(_total, _count) {
4113
+ throw new Error(
4114
+ "Cannot use keepHighest on a pooled roll. Use keepHighestAll again if you want nested pooling."
4115
+ );
4116
+ }
4117
+ keepLowest(_total, _count) {
4118
+ throw new Error(
4119
+ "Cannot use keepLowest on a pooled roll. Use keepLowestAll again if you want nested pooling."
4120
+ );
4121
+ }
4122
+ withAdvantage() {
4123
+ throw new Error("Cannot set advantage on a pooled roll.");
4124
+ }
4125
+ withDisadvantage() {
4126
+ throw new Error("Cannot set disadvantage on a pooled roll.");
4127
+ }
4128
+ withElvenAccuracy() {
4129
+ throw new Error("Cannot set elven accuracy on a pooled roll.");
4130
+ }
4131
+ toAST() {
4132
+ const configsAST = super.toAST();
4133
+ const isZero = configsAST.type === "constant" && configsAST.value === 0;
4134
+ if (isZero) {
4135
+ return this.baseAST;
4136
+ }
4137
+ const children = [
4138
+ { node: this.baseAST, sign: 1 },
4139
+ { node: configsAST, sign: 1 }
4140
+ ];
4141
+ return { type: "add", children };
4142
+ }
4143
+ toExpression() {
4144
+ const configsExpression = super.toExpression();
4145
+ if (configsExpression === "0") {
4146
+ return this.baseExpression;
4147
+ }
4148
+ if (configsExpression.startsWith("-")) {
4149
+ return `${this.baseExpression} - ${configsExpression.substring(1)}`;
4150
+ }
4151
+ return `${this.baseExpression} + ${configsExpression}`;
4152
+ }
4153
+ copy() {
4154
+ return new _PooledRollBuilder(
4155
+ this.baseAST,
4156
+ this.baseExpression,
4157
+ this.getSubRollConfigs()
4158
+ );
4159
+ }
4160
+ scaleDice(scale) {
4161
+ const scaleInt = Math.floor(scale);
4162
+ if (scaleInt !== scale) throw new Error("Scale must be an integer");
4163
+ if (scaleInt <= 0) throw new Error("Scale must be > 0");
4164
+ const newBaseAST = {
4165
+ type: "sum",
4166
+ count: scaleInt,
4167
+ child: this.baseAST
4168
+ };
4169
+ const newBaseExpr = scaleInt === 1 ? this.baseExpression : `${scaleInt}(${this.baseExpression})`;
4170
+ return new _PooledRollBuilder(
4171
+ newBaseAST,
4172
+ newBaseExpr,
4173
+ this.getSubRollConfigs()
4174
+ );
4175
+ }
4176
+ times(count) {
4177
+ if (isNaN(count)) throw new Error("Invalid NaN value for times");
4178
+ if (Math.floor(count) !== count)
4179
+ throw new Error("times() requires an integer");
4180
+ if (count < 0) throw new Error("times() requires a non-negative integer");
4181
+ const currentAST = this.toAST();
4182
+ const currentExpr = this.toExpression();
4183
+ const sumNode = {
4184
+ type: "sum",
4185
+ count,
4186
+ child: currentAST
4187
+ };
4188
+ const newExpr = count === 1 ? currentExpr : `${count}(${currentExpr})`;
4189
+ return new _PooledRollBuilder(sumNode, newExpr);
4190
+ }
4191
+ };
3978
4192
 
3979
4193
  // src/builder/factory.ts
3980
4194
  var rollFn = (count, sidesOrDie, modifier) => {
3981
4195
  if (sidesOrDie instanceof RollBuilder) {
4196
+ if (sidesOrDie.hasHiddenState()) {
4197
+ throw new Error(
4198
+ "Cannot use a roll with hidden state (like a pooled roll) as a die type."
4199
+ );
4200
+ }
3982
4201
  const subRollConfigs = sidesOrDie.getSubRollConfigs();
3983
4202
  if (subRollConfigs.length === 0) return new RollBuilder(0).plus(modifier);
3984
4203
  const absCount = Math.abs(count);
@@ -4001,7 +4220,12 @@ var rollFn = (count, sidesOrDie, modifier) => {
4001
4220
  return builder.plus(modifier);
4002
4221
  }
4003
4222
  };
4004
- rollFn.d = (sides) => new RollBuilder(1).d(sides);
4223
+ rollFn.d = (sides) => {
4224
+ if (typeof sides === "string") {
4225
+ return RollBuilder.fromArgs(sides);
4226
+ }
4227
+ return new RollBuilder(1).d(sides);
4228
+ };
4005
4229
  rollFn.hd20 = () => new RollBuilder(1).d20().reroll(1);
4006
4230
  rollFn.d4 = () => new RollBuilder(1).d4();
4007
4231
  rollFn.d6 = () => new RollBuilder(1).d6();
@@ -4012,6 +4236,9 @@ rollFn.d20 = () => new RollBuilder(1).d20();
4012
4236
  rollFn.d100 = () => new RollBuilder(1).d100();
4013
4237
  rollFn.flat = (n) => new RollBuilder(0).plus(n);
4014
4238
  function d(sides) {
4239
+ if (typeof sides === "string") {
4240
+ return RollBuilder.fromArgs(sides);
4241
+ }
4015
4242
  return new RollBuilder(1).d(sides);
4016
4243
  }
4017
4244
  var d4 = new RollBuilder(1).d4();
@@ -4887,6 +5114,7 @@ exports.DCBuilder = DCBuilder;
4887
5114
  exports.HalfRollBuilder = HalfRollBuilder;
4888
5115
  exports.MaxOfRollBuilder = MaxOfRollBuilder;
4889
5116
  exports.ParsedRollBuilder = ParsedRollBuilder;
5117
+ exports.PooledRollBuilder = PooledRollBuilder;
4890
5118
  exports.RollBuilder = RollBuilder;
4891
5119
  exports.SaveBuilder = SaveBuilder;
4892
5120
  exports.builderPMFCache = builderPMFCache;