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