@yipe/dice 0.2.12 → 0.2.15

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.
@@ -2659,14 +2659,6 @@ var RollBuilder = class _RollBuilder {
2659
2659
  const scaleInt = Math.floor(scale);
2660
2660
  if (scaleInt !== scale) throw new Error("Scale must be an integer");
2661
2661
  if (scaleInt <= 0) throw new Error("Scale must be > 0");
2662
- if (scaleInt > 1) {
2663
- const configs = this.getSubRollConfigs();
2664
- if (configs.length === 1 && configs[0].keep && configs[0].count === 1) {
2665
- const config = configs[0];
2666
- const newConfig = { ...config, count: scaleInt };
2667
- return new _RollBuilder([newConfig]);
2668
- }
2669
- }
2670
2662
  const newConfigs = this.getSubRollConfigs().map((config) => {
2671
2663
  if (!config.sides || config.sides <= 0) return config;
2672
2664
  return { ...config, count: config.count * scaleInt };
@@ -2676,6 +2668,9 @@ var RollBuilder = class _RollBuilder {
2676
2668
  doubleDice() {
2677
2669
  return this.scaleDice(2);
2678
2670
  }
2671
+ alwaysHits() {
2672
+ return new AlwaysHitBuilder(this);
2673
+ }
2679
2674
  copy() {
2680
2675
  return new this.constructor(this.getSubRollConfigs());
2681
2676
  }
@@ -2939,17 +2934,6 @@ var MaxOfRollBuilder = class _MaxOfRollBuilder extends RollBuilder {
2939
2934
  }
2940
2935
  return `max${this.count}(?d?)`;
2941
2936
  }
2942
- findDieInAST(node) {
2943
- if (node.type === "die") return node;
2944
- if (node.child) return this.findDieInAST(node.child);
2945
- if (node.children) {
2946
- for (const child of node.children) {
2947
- const result = this.findDieInAST(child.node || child);
2948
- if (result) return result;
2949
- }
2950
- }
2951
- return null;
2952
- }
2953
2937
  toAST() {
2954
2938
  if (this.diceCount && this.diceSides) {
2955
2939
  const sumChild = {
@@ -2991,6 +2975,80 @@ var MaxOfRollBuilder = class _MaxOfRollBuilder extends RollBuilder {
2991
2975
  return new _MaxOfRollBuilder(this.innerRoll.copy(), this.count);
2992
2976
  }
2993
2977
  };
2978
+ var AlwaysHitBuilder = class _AlwaysHitBuilder extends RollBuilder {
2979
+ constructor(baseRoll, attackConfig) {
2980
+ super(baseRoll.getSubRollConfigs());
2981
+ __publicField(this, "attackConfig");
2982
+ if (attackConfig) {
2983
+ this.attackConfig = { ...attackConfig };
2984
+ } else {
2985
+ this.attackConfig = { critThreshold: 20 };
2986
+ }
2987
+ }
2988
+ onHit(...args) {
2989
+ const damageRoll = RollBuilder.fromArgs(...args);
2990
+ return new AttackBuilder(this, damageRoll);
2991
+ }
2992
+ get critThreshold() {
2993
+ return this.attackConfig.critThreshold;
2994
+ }
2995
+ // TODO - move this to AC Builder… or if we create a DC builder that has critOn, throw an error?
2996
+ critOn(critThreshold) {
2997
+ const newConfig = { critThreshold };
2998
+ return new _AlwaysHitBuilder(this, newConfig);
2999
+ }
3000
+ // Legacy expressions
3001
+ toExpression() {
3002
+ const configs = this.getSubRollConfigs();
3003
+ return new RollBuilder(configs).toExpression();
3004
+ }
3005
+ toPMF() {
3006
+ const rollType = this.rollType;
3007
+ const rerollOne = this.baseReroll > 0;
3008
+ return d20RollPMF(rollType, rerollOne);
3009
+ }
3010
+ copy() {
3011
+ const baseCopy = new RollBuilder(this.getSubRollConfigs());
3012
+ const critThreshold = this.critThreshold;
3013
+ const newConfig = { critThreshold };
3014
+ return new _AlwaysHitBuilder(baseCopy, newConfig);
3015
+ }
3016
+ };
3017
+ var AlwaysCritBuilder = class extends RollBuilder {
3018
+ constructor(baseRoll, attackConfig) {
3019
+ super(baseRoll.getSubRollConfigs());
3020
+ __publicField(this, "attackConfig");
3021
+ if (attackConfig) {
3022
+ this.attackConfig = { ...attackConfig };
3023
+ } else {
3024
+ this.attackConfig = { critThreshold: 20 };
3025
+ }
3026
+ }
3027
+ get critThreshold() {
3028
+ return this.attackConfig.critThreshold;
3029
+ }
3030
+ // TODO - move this to AC Builder… or if we create a DC builder that has critOn, throw an error?
3031
+ critOn(critThreshold) {
3032
+ const newConfig = { critThreshold };
3033
+ return new AlwaysHitBuilder(this, newConfig);
3034
+ }
3035
+ // Legacy expressions
3036
+ toExpression() {
3037
+ const configs = this.getSubRollConfigs();
3038
+ return new RollBuilder(configs).toExpression();
3039
+ }
3040
+ toPMF() {
3041
+ const rollType = this.rollType;
3042
+ const rerollOne = this.baseReroll > 0;
3043
+ return d20RollPMF(rollType, rerollOne);
3044
+ }
3045
+ copy() {
3046
+ const baseCopy = new RollBuilder(this.getSubRollConfigs());
3047
+ const critThreshold = this.critThreshold;
3048
+ const newConfig = { critThreshold };
3049
+ return new AlwaysHitBuilder(baseCopy, newConfig);
3050
+ }
3051
+ };
2994
3052
 
2995
3053
  // src/builder/factory.ts
2996
3054
  var rollFn = (count, sidesOrDie, modifier) => {
@@ -3082,7 +3140,18 @@ function astFromRollConfigs(configs) {
3082
3140
  const baseCount = Math.max(1, Math.floor(Math.abs(count || 1)));
3083
3141
  const trials = Math.max(1, Math.floor(cfg.keep.total));
3084
3142
  const k = Math.max(0, Math.floor(cfg.keep.count));
3085
- if (trials === baseCount) {
3143
+ if (k === 1 && cfg.keep.mode === "highest") {
3144
+ const perTrial = {
3145
+ type: "sum",
3146
+ count: baseCount,
3147
+ child: node
3148
+ };
3149
+ if (trials === 1) {
3150
+ node = perTrial;
3151
+ } else {
3152
+ node = { type: "maxOf", count: trials, child: perTrial };
3153
+ }
3154
+ } else if (trials === baseCount) {
3086
3155
  const base = { type: "sum", count: trials, child: node };
3087
3156
  node = {
3088
3157
  type: "keep",
@@ -3098,8 +3167,6 @@ function astFromRollConfigs(configs) {
3098
3167
  };
3099
3168
  if (trials === 1) {
3100
3169
  node = perTrial;
3101
- } else if (k === 1 && cfg.keep.mode === "highest") {
3102
- node = { type: "maxOf", count: trials, child: perTrial };
3103
3170
  } else {
3104
3171
  const trialPool = {
3105
3172
  type: "sum",
@@ -3534,11 +3601,25 @@ var AttackBuilder = class _AttackBuilder {
3534
3601
  return `${checkPart} * ${effectPart}`;
3535
3602
  }
3536
3603
  resolveProbabilities(check, eps = 0) {
3537
- const ac = check.attackConfig.ac;
3604
+ if (check instanceof AlwaysCritBuilder) {
3605
+ return { pSuccess: 1, pHit: 0, pCrit: 1, pMiss: 0 };
3606
+ }
3538
3607
  const rollType = check.rollType;
3539
3608
  const rerollOne = check.baseReroll > 0;
3540
3609
  const critThreshold = check.critThreshold;
3541
3610
  const d202 = d20RollPMF(rollType, rerollOne);
3611
+ if (check instanceof AlwaysHitBuilder) {
3612
+ let pCrit = 0;
3613
+ for (const [r, rec] of d202) {
3614
+ const pr = typeof rec === "number" ? rec : rec.p;
3615
+ if (pr <= 0) continue;
3616
+ if (r >= critThreshold) pCrit += pr;
3617
+ }
3618
+ const pHit = 1 - pCrit;
3619
+ const pMiss = 0;
3620
+ return { pSuccess: 1, pHit, pCrit, pMiss };
3621
+ }
3622
+ const ac = check.attackConfig.ac;
3542
3623
  const staticMod = this.check.modifier;
3543
3624
  const bonusDicePMFs = this.check.getBonusDicePMFs(this.check, eps);
3544
3625
  const bonusPMF = bonusDicePMFs.length ? PMF.convolveMany(bonusDicePMFs, eps) : PMF.delta(0, eps);
@@ -3824,6 +3905,8 @@ RollBuilder.prototype.dc = function(saveDC) {
3824
3905
  };
3825
3906
 
3826
3907
  exports.ACBuilder = ACBuilder;
3908
+ exports.AlwaysCritBuilder = AlwaysCritBuilder;
3909
+ exports.AlwaysHitBuilder = AlwaysHitBuilder;
3827
3910
  exports.DCBuilder = DCBuilder;
3828
3911
  exports.HalfRollBuilder = HalfRollBuilder;
3829
3912
  exports.MaxOfRollBuilder = MaxOfRollBuilder;