@yipe/dice 0.7.0 → 0.8.0
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/ac.d.ts +1 -0
- package/dist/builder/ac.d.ts.map +1 -1
- package/dist/builder/attack.d.ts +9 -0
- package/dist/builder/attack.d.ts.map +1 -1
- package/dist/builder/index.cjs +85 -2
- package/dist/builder/index.cjs.map +1 -1
- package/dist/builder/index.js +85 -3
- package/dist/builder/index.js.map +1 -1
- package/dist/builder/roll.d.ts +16 -0
- package/dist/builder/roll.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/builder/index.js
CHANGED
|
@@ -3501,6 +3501,17 @@ var RollBuilder = class _RollBuilder {
|
|
|
3501
3501
|
getSubRollConfigs() {
|
|
3502
3502
|
return this.subRollConfigs.map((c) => ({ ...c }));
|
|
3503
3503
|
}
|
|
3504
|
+
/**
|
|
3505
|
+
* A cheap, stable string that FULLY identifies this builder's PMF — used by {@link AttackBuilder.toPMF}
|
|
3506
|
+
* to cache resolved attack PMFs across rebuilds without walking the AST via {@link toExpression}. A plain
|
|
3507
|
+
* roll is fully determined by its {@link RollConfig} array (count/sides/modifier/reroll/explode/minimum/
|
|
3508
|
+
* bestOf/keep/rollType/isSubtraction), so serializing that is sound. Subclasses whose PMF depends on
|
|
3509
|
+
* hidden state NOT captured by `subRollConfigs` (half/scale/max/parsed/pooled/composite transforms) return
|
|
3510
|
+
* `null` to opt OUT of caching — a conservative miss is always safe; a wrong key would corrupt DPR.
|
|
3511
|
+
*/
|
|
3512
|
+
cacheKey() {
|
|
3513
|
+
return JSON.stringify(this.subRollConfigs);
|
|
3514
|
+
}
|
|
3504
3515
|
// for testing
|
|
3505
3516
|
static fromConfig(config) {
|
|
3506
3517
|
return new _RollBuilder([{ ...defaultConfig, ...config }]);
|
|
@@ -4009,6 +4020,9 @@ var HalfRollBuilder = class _HalfRollBuilder extends RollBuilder {
|
|
|
4009
4020
|
hasHiddenState() {
|
|
4010
4021
|
return this.innerRoll.hasHiddenState();
|
|
4011
4022
|
}
|
|
4023
|
+
cacheKey() {
|
|
4024
|
+
return null;
|
|
4025
|
+
}
|
|
4012
4026
|
// No need to override create if we don't expose RollBuilder methods that use it,
|
|
4013
4027
|
// but HalfRollBuilder extends RollBuilder so it does.
|
|
4014
4028
|
// However, HalfRollBuilder seems to just wrap another roll.
|
|
@@ -4053,6 +4067,9 @@ var ScaleRollBuilder = class _ScaleRollBuilder extends RollBuilder {
|
|
|
4053
4067
|
hasHiddenState() {
|
|
4054
4068
|
return this.innerRoll.hasHiddenState();
|
|
4055
4069
|
}
|
|
4070
|
+
cacheKey() {
|
|
4071
|
+
return null;
|
|
4072
|
+
}
|
|
4056
4073
|
get lastConfig() {
|
|
4057
4074
|
return this.innerRoll.lastConfig;
|
|
4058
4075
|
}
|
|
@@ -4097,6 +4114,9 @@ var MaxOfRollBuilder = class _MaxOfRollBuilder extends RollBuilder {
|
|
|
4097
4114
|
hasHiddenState() {
|
|
4098
4115
|
return this.innerRoll.hasHiddenState();
|
|
4099
4116
|
}
|
|
4117
|
+
cacheKey() {
|
|
4118
|
+
return null;
|
|
4119
|
+
}
|
|
4100
4120
|
get lastConfig() {
|
|
4101
4121
|
return this.innerRoll.lastConfig;
|
|
4102
4122
|
}
|
|
@@ -4174,6 +4194,10 @@ var AlwaysHitBuilder = class _AlwaysHitBuilder extends RollBuilder {
|
|
|
4174
4194
|
get critThreshold() {
|
|
4175
4195
|
return this.attackConfig.critThreshold;
|
|
4176
4196
|
}
|
|
4197
|
+
cacheKey() {
|
|
4198
|
+
const base = super.cacheKey();
|
|
4199
|
+
return base === null ? null : `H|${this.attackConfig.critThreshold}|${base}`;
|
|
4200
|
+
}
|
|
4177
4201
|
// TODO - move this to AC Builder… or if we create a DC builder that has critOn, throw an error?
|
|
4178
4202
|
critOn(critThreshold) {
|
|
4179
4203
|
const newConfig = { critThreshold };
|
|
@@ -4224,6 +4248,10 @@ var AlwaysCritBuilder = class _AlwaysCritBuilder extends RollBuilder {
|
|
|
4224
4248
|
get critThreshold() {
|
|
4225
4249
|
return this.attackConfig.critThreshold;
|
|
4226
4250
|
}
|
|
4251
|
+
cacheKey() {
|
|
4252
|
+
const base = super.cacheKey();
|
|
4253
|
+
return base === null ? null : `C|${this.fromAlwaysHit ? 1 : 0}|${this.attackConfig.critThreshold}|${this.attackConfig.ac ?? ""}|${base}`;
|
|
4254
|
+
}
|
|
4227
4255
|
critOn(critThreshold) {
|
|
4228
4256
|
const newConfig = { critThreshold, ac: this.attackConfig.ac };
|
|
4229
4257
|
return new _AlwaysCritBuilder(this, newConfig, this.fromAlwaysHit);
|
|
@@ -4254,6 +4282,9 @@ var ParsedRollBuilder = class _ParsedRollBuilder extends RollBuilder {
|
|
|
4254
4282
|
hasHiddenState() {
|
|
4255
4283
|
return true;
|
|
4256
4284
|
}
|
|
4285
|
+
cacheKey() {
|
|
4286
|
+
return null;
|
|
4287
|
+
}
|
|
4257
4288
|
create(configs) {
|
|
4258
4289
|
return new RollBuilder(configs);
|
|
4259
4290
|
}
|
|
@@ -4289,6 +4320,9 @@ var PooledRollBuilder = class _PooledRollBuilder extends RollBuilder {
|
|
|
4289
4320
|
hasHiddenState() {
|
|
4290
4321
|
return true;
|
|
4291
4322
|
}
|
|
4323
|
+
cacheKey() {
|
|
4324
|
+
return null;
|
|
4325
|
+
}
|
|
4292
4326
|
d(_sides) {
|
|
4293
4327
|
throw new Error("Cannot add dice to a pooled roll. The pool is finalized.");
|
|
4294
4328
|
}
|
|
@@ -4392,6 +4426,9 @@ var CompositeSumRollBuilder = class _CompositeSumRollBuilder extends RollBuilder
|
|
|
4392
4426
|
hasHiddenState() {
|
|
4393
4427
|
return true;
|
|
4394
4428
|
}
|
|
4429
|
+
cacheKey() {
|
|
4430
|
+
return null;
|
|
4431
|
+
}
|
|
4395
4432
|
getSubRollConfigs() {
|
|
4396
4433
|
return [];
|
|
4397
4434
|
}
|
|
@@ -4944,6 +4981,10 @@ function getASTSignature(node) {
|
|
|
4944
4981
|
}
|
|
4945
4982
|
|
|
4946
4983
|
// src/builder/attack.ts
|
|
4984
|
+
var attackPMFCache = new LRUCache(4e3);
|
|
4985
|
+
function clearAttackCache() {
|
|
4986
|
+
attackPMFCache.clear();
|
|
4987
|
+
}
|
|
4947
4988
|
var AttackBuilder = class _AttackBuilder {
|
|
4948
4989
|
constructor(check, hitEffect, critEffect, missEffect) {
|
|
4949
4990
|
this.check = check;
|
|
@@ -5122,9 +5163,46 @@ var AttackBuilder = class _AttackBuilder {
|
|
|
5122
5163
|
weights: { hit: phit, crit: pcrit, miss: pmiss }
|
|
5123
5164
|
};
|
|
5124
5165
|
}
|
|
5125
|
-
|
|
5166
|
+
/**
|
|
5167
|
+
* A cheap, complete key for this attack's resolved PMF, or `null` when it can't be cached soundly (an
|
|
5168
|
+
* effect whose PMF isn't captured by its {@link RollConfig}s — see {@link RollBuilder.cacheKey}). Composed
|
|
5169
|
+
* from the check + hit/crit/miss effect keys + `eps`. `critEffect === null` (noCrit) and `undefined`
|
|
5170
|
+
* (auto-double the hit dice) are distinct crit states, encoded separately.
|
|
5171
|
+
*/
|
|
5172
|
+
cacheKey(eps) {
|
|
5173
|
+
const checkKey = this.check.cacheKey();
|
|
5174
|
+
if (checkKey === null) return null;
|
|
5175
|
+
let hitKey = "";
|
|
5176
|
+
if (this.hitEffect) {
|
|
5177
|
+
const k = this.hitEffect.cacheKey();
|
|
5178
|
+
if (k === null) return null;
|
|
5179
|
+
hitKey = k;
|
|
5180
|
+
}
|
|
5181
|
+
let critKey;
|
|
5182
|
+
if (this.critEffect === null) critKey = "n";
|
|
5183
|
+
else if (this.critEffect === void 0) critKey = "a";
|
|
5184
|
+
else {
|
|
5185
|
+
const k = this.critEffect.cacheKey();
|
|
5186
|
+
if (k === null) return null;
|
|
5187
|
+
critKey = k;
|
|
5188
|
+
}
|
|
5189
|
+
let missKey = "";
|
|
5190
|
+
if (this.missEffect) {
|
|
5191
|
+
const k = this.missEffect.cacheKey();
|
|
5192
|
+
if (k === null) return null;
|
|
5193
|
+
missKey = k;
|
|
5194
|
+
}
|
|
5195
|
+
return `${checkKey}*H${hitKey}*C${critKey}*M${missKey}*e${eps}`;
|
|
5196
|
+
}
|
|
5197
|
+
// By default, create PMF with no pruning. Cached by the cheap config key across identical rebuilds.
|
|
5126
5198
|
toPMF(eps = 0) {
|
|
5127
|
-
|
|
5199
|
+
const key = this.cacheKey(eps);
|
|
5200
|
+
if (key === null) return this.resolve(eps).pmf;
|
|
5201
|
+
const cached = attackPMFCache.get(key);
|
|
5202
|
+
if (cached) return cached;
|
|
5203
|
+
const pmf = this.resolve(eps).pmf;
|
|
5204
|
+
attackPMFCache.set(key, pmf);
|
|
5205
|
+
return pmf;
|
|
5128
5206
|
}
|
|
5129
5207
|
get pmf() {
|
|
5130
5208
|
return this.toPMF();
|
|
@@ -5152,6 +5230,10 @@ var ACBuilder = class _ACBuilder extends RollBuilder {
|
|
|
5152
5230
|
get critThreshold() {
|
|
5153
5231
|
return this.attackConfig.critThreshold;
|
|
5154
5232
|
}
|
|
5233
|
+
cacheKey() {
|
|
5234
|
+
const base = super.cacheKey();
|
|
5235
|
+
return base === null ? null : `A|${this.attackConfig.ac}|${this.attackConfig.critThreshold}|${base}`;
|
|
5236
|
+
}
|
|
5155
5237
|
// TODO - move this to AC Builder… or if we create a DC builder that has critOn, throw an error?
|
|
5156
5238
|
critOn(threshold) {
|
|
5157
5239
|
const newConfig = {
|
|
@@ -5356,6 +5438,6 @@ RollBuilder.prototype.dc = function(saveDC) {
|
|
|
5356
5438
|
return new DCBuilder(this).dc(saveDC);
|
|
5357
5439
|
};
|
|
5358
5440
|
|
|
5359
|
-
export { ACBuilder, AlwaysCritBuilder, AlwaysHitBuilder, AttackBuilder, DCBuilder, HalfRollBuilder, MaxOfRollBuilder, ParsedRollBuilder, PooledRollBuilder, RollBuilder, SaveBuilder, ScaleRollBuilder, builderPMFCache, d, d10, d100, d12, d20, d4, d6, d8, defaultConfig, flat, hd20, roll, sumRolls };
|
|
5441
|
+
export { ACBuilder, AlwaysCritBuilder, AlwaysHitBuilder, AttackBuilder, DCBuilder, HalfRollBuilder, MaxOfRollBuilder, ParsedRollBuilder, PooledRollBuilder, RollBuilder, SaveBuilder, ScaleRollBuilder, builderPMFCache, clearAttackCache, d, d10, d100, d12, d20, d4, d6, d8, defaultConfig, flat, hd20, roll, sumRolls };
|
|
5360
5442
|
//# sourceMappingURL=index.js.map
|
|
5361
5443
|
//# sourceMappingURL=index.js.map
|