@yipe/dice 0.2.15 → 0.2.16

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.
@@ -139,6 +139,7 @@ declare class RollBuilder {
139
139
  scaleDice(scale: number): RollBuilder;
140
140
  doubleDice(): RollBuilder;
141
141
  alwaysHits(): AlwaysHitBuilder;
142
+ alwaysCrits(): AlwaysCritBuilder;
142
143
  copy(): RollBuilder;
143
144
  d4: () => RollBuilder;
144
145
  d6: () => RollBuilder;
@@ -198,18 +199,30 @@ declare class AlwaysHitBuilder extends RollBuilder {
198
199
  onHit(count: number, sides: number, modifier: number): AttackBuilder;
199
200
  get critThreshold(): number;
200
201
  critOn(critThreshold: number): AlwaysHitBuilder;
202
+ alwaysCrits(): AlwaysCritBuilder;
201
203
  toExpression(): string;
202
204
  toPMF(): PMF;
203
205
  copy(): AlwaysHitBuilder;
204
206
  }
205
207
  declare class AlwaysCritBuilder extends RollBuilder {
206
- readonly attackConfig: CritConfig;
207
- constructor(baseRoll: RollBuilder, attackConfig?: CritConfig);
208
+ readonly attackConfig: CritConfig & {
209
+ ac?: number;
210
+ };
211
+ readonly fromAlwaysHit: boolean;
212
+ constructor(baseRoll: RollBuilder, attackConfig?: CritConfig & {
213
+ ac?: number;
214
+ }, fromAlwaysHit?: boolean);
215
+ onHit(val: number): AttackBuilder;
216
+ onHit(val: RollBuilder): AttackBuilder;
217
+ onHit(count: number, die: RollBuilder): AttackBuilder;
218
+ onHit(count: number, sides: number): AttackBuilder;
219
+ onHit(count: number, die: RollBuilder, modifier: number): AttackBuilder;
220
+ onHit(count: number, sides: number, modifier: number): AttackBuilder;
208
221
  get critThreshold(): number;
209
- critOn(critThreshold: number): AlwaysHitBuilder;
222
+ critOn(critThreshold: number): AlwaysCritBuilder;
210
223
  toExpression(): string;
211
224
  toPMF(): PMF;
212
- copy(): AlwaysHitBuilder;
225
+ copy(): AlwaysCritBuilder;
213
226
  }
214
227
 
215
228
  type ActionEffect = RollBuilder;
@@ -260,6 +273,7 @@ declare class ACBuilder extends RollBuilder {
260
273
  onHit(count: number, sides: number, modifier: number): AttackBuilder;
261
274
  get critThreshold(): number;
262
275
  critOn(threshold: number): ACBuilder;
276
+ alwaysCrits(): AlwaysCritBuilder;
263
277
  toExpression(): string;
264
278
  toPMF(eps?: number): PMF;
265
279
  copy(): ACBuilder;
@@ -139,6 +139,7 @@ declare class RollBuilder {
139
139
  scaleDice(scale: number): RollBuilder;
140
140
  doubleDice(): RollBuilder;
141
141
  alwaysHits(): AlwaysHitBuilder;
142
+ alwaysCrits(): AlwaysCritBuilder;
142
143
  copy(): RollBuilder;
143
144
  d4: () => RollBuilder;
144
145
  d6: () => RollBuilder;
@@ -198,18 +199,30 @@ declare class AlwaysHitBuilder extends RollBuilder {
198
199
  onHit(count: number, sides: number, modifier: number): AttackBuilder;
199
200
  get critThreshold(): number;
200
201
  critOn(critThreshold: number): AlwaysHitBuilder;
202
+ alwaysCrits(): AlwaysCritBuilder;
201
203
  toExpression(): string;
202
204
  toPMF(): PMF;
203
205
  copy(): AlwaysHitBuilder;
204
206
  }
205
207
  declare class AlwaysCritBuilder extends RollBuilder {
206
- readonly attackConfig: CritConfig;
207
- constructor(baseRoll: RollBuilder, attackConfig?: CritConfig);
208
+ readonly attackConfig: CritConfig & {
209
+ ac?: number;
210
+ };
211
+ readonly fromAlwaysHit: boolean;
212
+ constructor(baseRoll: RollBuilder, attackConfig?: CritConfig & {
213
+ ac?: number;
214
+ }, fromAlwaysHit?: boolean);
215
+ onHit(val: number): AttackBuilder;
216
+ onHit(val: RollBuilder): AttackBuilder;
217
+ onHit(count: number, die: RollBuilder): AttackBuilder;
218
+ onHit(count: number, sides: number): AttackBuilder;
219
+ onHit(count: number, die: RollBuilder, modifier: number): AttackBuilder;
220
+ onHit(count: number, sides: number, modifier: number): AttackBuilder;
208
221
  get critThreshold(): number;
209
- critOn(critThreshold: number): AlwaysHitBuilder;
222
+ critOn(critThreshold: number): AlwaysCritBuilder;
210
223
  toExpression(): string;
211
224
  toPMF(): PMF;
212
- copy(): AlwaysHitBuilder;
225
+ copy(): AlwaysCritBuilder;
213
226
  }
214
227
 
215
228
  type ActionEffect = RollBuilder;
@@ -260,6 +273,7 @@ declare class ACBuilder extends RollBuilder {
260
273
  onHit(count: number, sides: number, modifier: number): AttackBuilder;
261
274
  get critThreshold(): number;
262
275
  critOn(threshold: number): ACBuilder;
276
+ alwaysCrits(): AlwaysCritBuilder;
263
277
  toExpression(): string;
264
278
  toPMF(eps?: number): PMF;
265
279
  copy(): ACBuilder;
@@ -2669,6 +2669,9 @@ var RollBuilder = class _RollBuilder {
2669
2669
  alwaysHits() {
2670
2670
  return new AlwaysHitBuilder(this);
2671
2671
  }
2672
+ alwaysCrits() {
2673
+ return new AlwaysCritBuilder(this);
2674
+ }
2672
2675
  copy() {
2673
2676
  return new this.constructor(this.getSubRollConfigs());
2674
2677
  }
@@ -2960,7 +2963,7 @@ var MaxOfRollBuilder = class _MaxOfRollBuilder extends RollBuilder {
2960
2963
  child: sumChild
2961
2964
  };
2962
2965
  }
2963
- } catch (e) {
2966
+ } catch {
2964
2967
  }
2965
2968
  throw new Error(
2966
2969
  `MaxOfRollBuilder.toAST(): Unsupported innerRoll configuration`
@@ -2995,6 +2998,9 @@ var AlwaysHitBuilder = class _AlwaysHitBuilder extends RollBuilder {
2995
2998
  const newConfig = { critThreshold };
2996
2999
  return new _AlwaysHitBuilder(this, newConfig);
2997
3000
  }
3001
+ alwaysCrits() {
3002
+ return new AlwaysCritBuilder(this, void 0, true);
3003
+ }
2998
3004
  // Legacy expressions
2999
3005
  toExpression() {
3000
3006
  const configs = this.getSubRollConfigs();
@@ -3012,23 +3018,28 @@ var AlwaysHitBuilder = class _AlwaysHitBuilder extends RollBuilder {
3012
3018
  return new _AlwaysHitBuilder(baseCopy, newConfig);
3013
3019
  }
3014
3020
  };
3015
- var AlwaysCritBuilder = class extends RollBuilder {
3016
- constructor(baseRoll, attackConfig) {
3021
+ var AlwaysCritBuilder = class _AlwaysCritBuilder extends RollBuilder {
3022
+ constructor(baseRoll, attackConfig, fromAlwaysHit = false) {
3017
3023
  super(baseRoll.getSubRollConfigs());
3018
3024
  __publicField(this, "attackConfig");
3025
+ __publicField(this, "fromAlwaysHit");
3019
3026
  if (attackConfig) {
3020
3027
  this.attackConfig = { ...attackConfig };
3021
3028
  } else {
3022
3029
  this.attackConfig = { critThreshold: 20 };
3023
3030
  }
3031
+ this.fromAlwaysHit = fromAlwaysHit || baseRoll instanceof AlwaysHitBuilder;
3032
+ }
3033
+ onHit(...args) {
3034
+ const damageRoll = RollBuilder.fromArgs(...args);
3035
+ return new AttackBuilder(this, damageRoll);
3024
3036
  }
3025
3037
  get critThreshold() {
3026
3038
  return this.attackConfig.critThreshold;
3027
3039
  }
3028
- // TODO - move this to AC Builder… or if we create a DC builder that has critOn, throw an error?
3029
3040
  critOn(critThreshold) {
3030
- const newConfig = { critThreshold };
3031
- return new AlwaysHitBuilder(this, newConfig);
3041
+ const newConfig = { critThreshold, ac: this.attackConfig.ac };
3042
+ return new _AlwaysCritBuilder(this, newConfig, this.fromAlwaysHit);
3032
3043
  }
3033
3044
  // Legacy expressions
3034
3045
  toExpression() {
@@ -3043,8 +3054,8 @@ var AlwaysCritBuilder = class extends RollBuilder {
3043
3054
  copy() {
3044
3055
  const baseCopy = new RollBuilder(this.getSubRollConfigs());
3045
3056
  const critThreshold = this.critThreshold;
3046
- const newConfig = { critThreshold };
3047
- return new AlwaysHitBuilder(baseCopy, newConfig);
3057
+ const newConfig = { critThreshold, ac: this.attackConfig.ac };
3058
+ return new _AlwaysCritBuilder(baseCopy, newConfig, this.fromAlwaysHit);
3048
3059
  }
3049
3060
  };
3050
3061
 
@@ -3580,9 +3591,9 @@ var AttackBuilder = class _AttackBuilder {
3580
3591
  crit = this.hitEffect?.copy().doubleDice() ?? RollBuilder.fromArgs(0);
3581
3592
  }
3582
3593
  const critThreshold = this.check.critThreshold;
3583
- if (critThreshold < 15 || critThreshold > 20) {
3594
+ if (critThreshold < 1 || critThreshold > 20) {
3584
3595
  throw new Error(
3585
- `Invalid crit threshold: ${critThreshold}. Must be between 15 and 20.`
3596
+ `Invalid crit threshold: ${critThreshold}. Must be between 1 and 20.`
3586
3597
  );
3587
3598
  }
3588
3599
  if (critThreshold === 20) {
@@ -3599,13 +3610,34 @@ var AttackBuilder = class _AttackBuilder {
3599
3610
  return `${checkPart} * ${effectPart}`;
3600
3611
  }
3601
3612
  resolveProbabilities(check, eps = 0) {
3602
- if (check instanceof AlwaysCritBuilder) {
3603
- return { pSuccess: 1, pHit: 0, pCrit: 1, pMiss: 0 };
3604
- }
3605
3613
  const rollType = check.rollType;
3606
3614
  const rerollOne = check.baseReroll > 0;
3607
3615
  const critThreshold = check.critThreshold;
3608
3616
  const d202 = d20RollPMF(rollType, rerollOne);
3617
+ if (check instanceof AlwaysCritBuilder) {
3618
+ if (check.fromAlwaysHit) {
3619
+ return { pSuccess: 1, pHit: 0, pCrit: 1, pMiss: 0 };
3620
+ }
3621
+ const ac2 = check.attackConfig.ac ?? 0;
3622
+ const staticMod2 = this.check.modifier;
3623
+ const bonusDicePMFs2 = this.check.getBonusDicePMFs(this.check, eps);
3624
+ const bonusPMF2 = bonusDicePMFs2.length ? PMF.convolveMany(bonusDicePMFs2, eps) : PMF.delta(0, eps);
3625
+ let pcrit2 = 0;
3626
+ let pmiss2 = 0;
3627
+ for (const [r, rec] of d202) {
3628
+ const pr = typeof rec === "number" ? rec : rec.p;
3629
+ if (pr <= 0) continue;
3630
+ if (r === 1) {
3631
+ pmiss2 += pr;
3632
+ continue;
3633
+ }
3634
+ const need = ac2 - staticMod2 - r;
3635
+ const pBonusHit = bonusPMF2.tailProbGE(need);
3636
+ pcrit2 += pr * pBonusHit;
3637
+ pmiss2 += pr * (1 - pBonusHit);
3638
+ }
3639
+ return { pSuccess: pcrit2, pHit: 0, pCrit: pcrit2, pMiss: pmiss2 };
3640
+ }
3609
3641
  if (check instanceof AlwaysHitBuilder) {
3610
3642
  let pCrit = 0;
3611
3643
  for (const [r, rec] of d202) {
@@ -3627,14 +3659,14 @@ var AttackBuilder = class _AttackBuilder {
3627
3659
  for (const [r, rec] of d202) {
3628
3660
  const pr = typeof rec === "number" ? rec : rec.p;
3629
3661
  if (pr <= 0) continue;
3630
- if (r >= critThreshold) {
3631
- pcrit += pr;
3632
- continue;
3633
- }
3634
3662
  if (r === 1) {
3635
3663
  pmiss += pr;
3636
3664
  continue;
3637
3665
  }
3666
+ if (r >= critThreshold) {
3667
+ pcrit += pr;
3668
+ continue;
3669
+ }
3638
3670
  const need = ac - staticMod - r;
3639
3671
  const pBonusHit = bonusPMF.tailProbGE(need);
3640
3672
  phit += pr * pBonusHit;
@@ -3715,6 +3747,16 @@ var ACBuilder = class _ACBuilder extends RollBuilder {
3715
3747
  };
3716
3748
  return new _ACBuilder(this, this.attackConfig.ac, newConfig);
3717
3749
  }
3750
+ alwaysCrits() {
3751
+ return new AlwaysCritBuilder(
3752
+ this,
3753
+ {
3754
+ critThreshold: this.attackConfig.critThreshold,
3755
+ ac: this.attackConfig.ac
3756
+ },
3757
+ false
3758
+ );
3759
+ }
3718
3760
  // Legacy expressions
3719
3761
  toExpression() {
3720
3762
  const configs = this.getSubRollConfigs();