@yipe/dice 0.2.13 → 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.
- package/dist/builder/index.cjs +143 -27
- package/dist/builder/index.cjs.map +1 -1
- package/dist/builder/index.d.cts +44 -6
- package/dist/builder/index.d.ts +44 -6
- package/dist/builder/index.js +142 -28
- package/dist/builder/index.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/dist/{pmf-D1p2Re-J.d.cts → pmf-CYaEbY7m.d.cts} +4 -1
- package/dist/{pmf-D1p2Re-J.d.ts → pmf-CYaEbY7m.d.ts} +4 -1
- package/package.json +1 -1
package/dist/builder/index.cjs
CHANGED
|
@@ -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,12 @@ var RollBuilder = class _RollBuilder {
|
|
|
2676
2668
|
doubleDice() {
|
|
2677
2669
|
return this.scaleDice(2);
|
|
2678
2670
|
}
|
|
2671
|
+
alwaysHits() {
|
|
2672
|
+
return new AlwaysHitBuilder(this);
|
|
2673
|
+
}
|
|
2674
|
+
alwaysCrits() {
|
|
2675
|
+
return new AlwaysCritBuilder(this);
|
|
2676
|
+
}
|
|
2679
2677
|
copy() {
|
|
2680
2678
|
return new this.constructor(this.getSubRollConfigs());
|
|
2681
2679
|
}
|
|
@@ -2939,17 +2937,6 @@ var MaxOfRollBuilder = class _MaxOfRollBuilder extends RollBuilder {
|
|
|
2939
2937
|
}
|
|
2940
2938
|
return `max${this.count}(?d?)`;
|
|
2941
2939
|
}
|
|
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
2940
|
toAST() {
|
|
2954
2941
|
if (this.diceCount && this.diceSides) {
|
|
2955
2942
|
const sumChild = {
|
|
@@ -2978,7 +2965,7 @@ var MaxOfRollBuilder = class _MaxOfRollBuilder extends RollBuilder {
|
|
|
2978
2965
|
child: sumChild
|
|
2979
2966
|
};
|
|
2980
2967
|
}
|
|
2981
|
-
} catch
|
|
2968
|
+
} catch {
|
|
2982
2969
|
}
|
|
2983
2970
|
throw new Error(
|
|
2984
2971
|
`MaxOfRollBuilder.toAST(): Unsupported innerRoll configuration`
|
|
@@ -2991,6 +2978,88 @@ var MaxOfRollBuilder = class _MaxOfRollBuilder extends RollBuilder {
|
|
|
2991
2978
|
return new _MaxOfRollBuilder(this.innerRoll.copy(), this.count);
|
|
2992
2979
|
}
|
|
2993
2980
|
};
|
|
2981
|
+
var AlwaysHitBuilder = class _AlwaysHitBuilder extends RollBuilder {
|
|
2982
|
+
constructor(baseRoll, attackConfig) {
|
|
2983
|
+
super(baseRoll.getSubRollConfigs());
|
|
2984
|
+
__publicField(this, "attackConfig");
|
|
2985
|
+
if (attackConfig) {
|
|
2986
|
+
this.attackConfig = { ...attackConfig };
|
|
2987
|
+
} else {
|
|
2988
|
+
this.attackConfig = { critThreshold: 20 };
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
onHit(...args) {
|
|
2992
|
+
const damageRoll = RollBuilder.fromArgs(...args);
|
|
2993
|
+
return new AttackBuilder(this, damageRoll);
|
|
2994
|
+
}
|
|
2995
|
+
get critThreshold() {
|
|
2996
|
+
return this.attackConfig.critThreshold;
|
|
2997
|
+
}
|
|
2998
|
+
// TODO - move this to AC Builder… or if we create a DC builder that has critOn, throw an error?
|
|
2999
|
+
critOn(critThreshold) {
|
|
3000
|
+
const newConfig = { critThreshold };
|
|
3001
|
+
return new _AlwaysHitBuilder(this, newConfig);
|
|
3002
|
+
}
|
|
3003
|
+
alwaysCrits() {
|
|
3004
|
+
return new AlwaysCritBuilder(this, void 0, true);
|
|
3005
|
+
}
|
|
3006
|
+
// Legacy expressions
|
|
3007
|
+
toExpression() {
|
|
3008
|
+
const configs = this.getSubRollConfigs();
|
|
3009
|
+
return new RollBuilder(configs).toExpression();
|
|
3010
|
+
}
|
|
3011
|
+
toPMF() {
|
|
3012
|
+
const rollType = this.rollType;
|
|
3013
|
+
const rerollOne = this.baseReroll > 0;
|
|
3014
|
+
return d20RollPMF(rollType, rerollOne);
|
|
3015
|
+
}
|
|
3016
|
+
copy() {
|
|
3017
|
+
const baseCopy = new RollBuilder(this.getSubRollConfigs());
|
|
3018
|
+
const critThreshold = this.critThreshold;
|
|
3019
|
+
const newConfig = { critThreshold };
|
|
3020
|
+
return new _AlwaysHitBuilder(baseCopy, newConfig);
|
|
3021
|
+
}
|
|
3022
|
+
};
|
|
3023
|
+
var AlwaysCritBuilder = class _AlwaysCritBuilder extends RollBuilder {
|
|
3024
|
+
constructor(baseRoll, attackConfig, fromAlwaysHit = false) {
|
|
3025
|
+
super(baseRoll.getSubRollConfigs());
|
|
3026
|
+
__publicField(this, "attackConfig");
|
|
3027
|
+
__publicField(this, "fromAlwaysHit");
|
|
3028
|
+
if (attackConfig) {
|
|
3029
|
+
this.attackConfig = { ...attackConfig };
|
|
3030
|
+
} else {
|
|
3031
|
+
this.attackConfig = { critThreshold: 20 };
|
|
3032
|
+
}
|
|
3033
|
+
this.fromAlwaysHit = fromAlwaysHit || baseRoll instanceof AlwaysHitBuilder;
|
|
3034
|
+
}
|
|
3035
|
+
onHit(...args) {
|
|
3036
|
+
const damageRoll = RollBuilder.fromArgs(...args);
|
|
3037
|
+
return new AttackBuilder(this, damageRoll);
|
|
3038
|
+
}
|
|
3039
|
+
get critThreshold() {
|
|
3040
|
+
return this.attackConfig.critThreshold;
|
|
3041
|
+
}
|
|
3042
|
+
critOn(critThreshold) {
|
|
3043
|
+
const newConfig = { critThreshold, ac: this.attackConfig.ac };
|
|
3044
|
+
return new _AlwaysCritBuilder(this, newConfig, this.fromAlwaysHit);
|
|
3045
|
+
}
|
|
3046
|
+
// Legacy expressions
|
|
3047
|
+
toExpression() {
|
|
3048
|
+
const configs = this.getSubRollConfigs();
|
|
3049
|
+
return new RollBuilder(configs).toExpression();
|
|
3050
|
+
}
|
|
3051
|
+
toPMF() {
|
|
3052
|
+
const rollType = this.rollType;
|
|
3053
|
+
const rerollOne = this.baseReroll > 0;
|
|
3054
|
+
return d20RollPMF(rollType, rerollOne);
|
|
3055
|
+
}
|
|
3056
|
+
copy() {
|
|
3057
|
+
const baseCopy = new RollBuilder(this.getSubRollConfigs());
|
|
3058
|
+
const critThreshold = this.critThreshold;
|
|
3059
|
+
const newConfig = { critThreshold, ac: this.attackConfig.ac };
|
|
3060
|
+
return new _AlwaysCritBuilder(baseCopy, newConfig, this.fromAlwaysHit);
|
|
3061
|
+
}
|
|
3062
|
+
};
|
|
2994
3063
|
|
|
2995
3064
|
// src/builder/factory.ts
|
|
2996
3065
|
var rollFn = (count, sidesOrDie, modifier) => {
|
|
@@ -3524,9 +3593,9 @@ var AttackBuilder = class _AttackBuilder {
|
|
|
3524
3593
|
crit = this.hitEffect?.copy().doubleDice() ?? RollBuilder.fromArgs(0);
|
|
3525
3594
|
}
|
|
3526
3595
|
const critThreshold = this.check.critThreshold;
|
|
3527
|
-
if (critThreshold <
|
|
3596
|
+
if (critThreshold < 1 || critThreshold > 20) {
|
|
3528
3597
|
throw new Error(
|
|
3529
|
-
`Invalid crit threshold: ${critThreshold}. Must be between
|
|
3598
|
+
`Invalid crit threshold: ${critThreshold}. Must be between 1 and 20.`
|
|
3530
3599
|
);
|
|
3531
3600
|
}
|
|
3532
3601
|
if (critThreshold === 20) {
|
|
@@ -3543,11 +3612,46 @@ var AttackBuilder = class _AttackBuilder {
|
|
|
3543
3612
|
return `${checkPart} * ${effectPart}`;
|
|
3544
3613
|
}
|
|
3545
3614
|
resolveProbabilities(check, eps = 0) {
|
|
3546
|
-
const ac = check.attackConfig.ac;
|
|
3547
3615
|
const rollType = check.rollType;
|
|
3548
3616
|
const rerollOne = check.baseReroll > 0;
|
|
3549
3617
|
const critThreshold = check.critThreshold;
|
|
3550
3618
|
const d202 = d20RollPMF(rollType, rerollOne);
|
|
3619
|
+
if (check instanceof AlwaysCritBuilder) {
|
|
3620
|
+
if (check.fromAlwaysHit) {
|
|
3621
|
+
return { pSuccess: 1, pHit: 0, pCrit: 1, pMiss: 0 };
|
|
3622
|
+
}
|
|
3623
|
+
const ac2 = check.attackConfig.ac ?? 0;
|
|
3624
|
+
const staticMod2 = this.check.modifier;
|
|
3625
|
+
const bonusDicePMFs2 = this.check.getBonusDicePMFs(this.check, eps);
|
|
3626
|
+
const bonusPMF2 = bonusDicePMFs2.length ? PMF.convolveMany(bonusDicePMFs2, eps) : PMF.delta(0, eps);
|
|
3627
|
+
let pcrit2 = 0;
|
|
3628
|
+
let pmiss2 = 0;
|
|
3629
|
+
for (const [r, rec] of d202) {
|
|
3630
|
+
const pr = typeof rec === "number" ? rec : rec.p;
|
|
3631
|
+
if (pr <= 0) continue;
|
|
3632
|
+
if (r === 1) {
|
|
3633
|
+
pmiss2 += pr;
|
|
3634
|
+
continue;
|
|
3635
|
+
}
|
|
3636
|
+
const need = ac2 - staticMod2 - r;
|
|
3637
|
+
const pBonusHit = bonusPMF2.tailProbGE(need);
|
|
3638
|
+
pcrit2 += pr * pBonusHit;
|
|
3639
|
+
pmiss2 += pr * (1 - pBonusHit);
|
|
3640
|
+
}
|
|
3641
|
+
return { pSuccess: pcrit2, pHit: 0, pCrit: pcrit2, pMiss: pmiss2 };
|
|
3642
|
+
}
|
|
3643
|
+
if (check instanceof AlwaysHitBuilder) {
|
|
3644
|
+
let pCrit = 0;
|
|
3645
|
+
for (const [r, rec] of d202) {
|
|
3646
|
+
const pr = typeof rec === "number" ? rec : rec.p;
|
|
3647
|
+
if (pr <= 0) continue;
|
|
3648
|
+
if (r >= critThreshold) pCrit += pr;
|
|
3649
|
+
}
|
|
3650
|
+
const pHit = 1 - pCrit;
|
|
3651
|
+
const pMiss = 0;
|
|
3652
|
+
return { pSuccess: 1, pHit, pCrit, pMiss };
|
|
3653
|
+
}
|
|
3654
|
+
const ac = check.attackConfig.ac;
|
|
3551
3655
|
const staticMod = this.check.modifier;
|
|
3552
3656
|
const bonusDicePMFs = this.check.getBonusDicePMFs(this.check, eps);
|
|
3553
3657
|
const bonusPMF = bonusDicePMFs.length ? PMF.convolveMany(bonusDicePMFs, eps) : PMF.delta(0, eps);
|
|
@@ -3557,14 +3661,14 @@ var AttackBuilder = class _AttackBuilder {
|
|
|
3557
3661
|
for (const [r, rec] of d202) {
|
|
3558
3662
|
const pr = typeof rec === "number" ? rec : rec.p;
|
|
3559
3663
|
if (pr <= 0) continue;
|
|
3560
|
-
if (r >= critThreshold) {
|
|
3561
|
-
pcrit += pr;
|
|
3562
|
-
continue;
|
|
3563
|
-
}
|
|
3564
3664
|
if (r === 1) {
|
|
3565
3665
|
pmiss += pr;
|
|
3566
3666
|
continue;
|
|
3567
3667
|
}
|
|
3668
|
+
if (r >= critThreshold) {
|
|
3669
|
+
pcrit += pr;
|
|
3670
|
+
continue;
|
|
3671
|
+
}
|
|
3568
3672
|
const need = ac - staticMod - r;
|
|
3569
3673
|
const pBonusHit = bonusPMF.tailProbGE(need);
|
|
3570
3674
|
phit += pr * pBonusHit;
|
|
@@ -3645,6 +3749,16 @@ var ACBuilder = class _ACBuilder extends RollBuilder {
|
|
|
3645
3749
|
};
|
|
3646
3750
|
return new _ACBuilder(this, this.attackConfig.ac, newConfig);
|
|
3647
3751
|
}
|
|
3752
|
+
alwaysCrits() {
|
|
3753
|
+
return new AlwaysCritBuilder(
|
|
3754
|
+
this,
|
|
3755
|
+
{
|
|
3756
|
+
critThreshold: this.attackConfig.critThreshold,
|
|
3757
|
+
ac: this.attackConfig.ac
|
|
3758
|
+
},
|
|
3759
|
+
false
|
|
3760
|
+
);
|
|
3761
|
+
}
|
|
3648
3762
|
// Legacy expressions
|
|
3649
3763
|
toExpression() {
|
|
3650
3764
|
const configs = this.getSubRollConfigs();
|
|
@@ -3833,6 +3947,8 @@ RollBuilder.prototype.dc = function(saveDC) {
|
|
|
3833
3947
|
};
|
|
3834
3948
|
|
|
3835
3949
|
exports.ACBuilder = ACBuilder;
|
|
3950
|
+
exports.AlwaysCritBuilder = AlwaysCritBuilder;
|
|
3951
|
+
exports.AlwaysHitBuilder = AlwaysHitBuilder;
|
|
3836
3952
|
exports.DCBuilder = DCBuilder;
|
|
3837
3953
|
exports.HalfRollBuilder = HalfRollBuilder;
|
|
3838
3954
|
exports.MaxOfRollBuilder = MaxOfRollBuilder;
|