eve-fit-engine 0.1.2 → 0.1.4
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/README.md +8 -1
- package/dist/index.cjs +17 -2
- package/dist/index.d.cts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +17 -2
- package/dist/node.cjs +17 -2
- package/dist/node.js +17 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ projected effects, and structure fuel/service stats.
|
|
|
25
25
|
release gate and the engine's correctness ground truth.
|
|
26
26
|
- **Differential harness** — `npm run diff`: generates 4 fits for *every*
|
|
27
27
|
published ship and compares every stat against a headless **pyfa-org/Pyfa**
|
|
28
|
-
oracle.
|
|
28
|
+
oracle. 1662/1676 fits (99.2%) match exactly; the residual is a documented
|
|
29
29
|
set of pyfa float/modelling/per-ship quirks (see
|
|
30
30
|
[`test/diff/known-diffs.mjs`](test/diff/known-diffs.mjs)). Exits 0 on no
|
|
31
31
|
*unexpected* diffs; `--strict` re-lists the accepted set as failures.
|
|
@@ -99,6 +99,13 @@ tracked and ported, then re-checked against **both** the fixture suite
|
|
|
99
99
|
(`npm run test:pyfa`) and the per-ship differential harness (`npm run diff`)
|
|
100
100
|
before release. See [`MAINTENANCE.md`](./MAINTENANCE.md) for the update flows.
|
|
101
101
|
|
|
102
|
+
## Contributing
|
|
103
|
+
|
|
104
|
+
See [`CONTRIBUTING.md`](./CONTRIBUTING.md) — the step-by-step guide to setup,
|
|
105
|
+
every internal command, the two validation suites (`npm run test:pyfa` +
|
|
106
|
+
`npm run diff`), how to add fixtures / port effects, the pyfa-pin recalibration
|
|
107
|
+
flow, and the project's hard rules.
|
|
108
|
+
|
|
102
109
|
## Licence
|
|
103
110
|
|
|
104
111
|
GPL-3.0-or-later. Copyright (C) 2026 Capsuleers.app. This is free software; see
|
package/dist/index.cjs
CHANGED
|
@@ -232,6 +232,8 @@ var ATTR = {
|
|
|
232
232
|
EXPLOSION_RADIUS: 654,
|
|
233
233
|
DRF: 858,
|
|
234
234
|
// damage reduction factor
|
|
235
|
+
EXPLOSION_DELAY: 281,
|
|
236
|
+
// charge attr — "Maximum Flight Time" (ms); range = maxVelocity × flightTime/1000
|
|
235
237
|
// ---- Breacher Pods (DOT charges, e.g. SCARAB Breacher Pod M) ----
|
|
236
238
|
DOT_DURATION: 5735,
|
|
237
239
|
// ms
|
|
@@ -4217,11 +4219,15 @@ function collectDrains(ctx, dataset) {
|
|
|
4217
4219
|
}
|
|
4218
4220
|
continue;
|
|
4219
4221
|
}
|
|
4222
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4220
4223
|
for (const eid of m.effectIDs) {
|
|
4221
4224
|
const effect = dataset.effects.get(eid);
|
|
4222
4225
|
if (!effect) continue;
|
|
4223
4226
|
const entry = drainEntryFromEffect(effect, m);
|
|
4224
4227
|
if (!entry) continue;
|
|
4228
|
+
const sig = `${entry.cycleMs}|${entry.capNeed}|${entry.clipSize}|${entry.reloadMs}|${entry.isInjector ? 1 : 0}|${entry.disableStagger ? 1 : 0}`;
|
|
4229
|
+
if (seen.has(sig)) continue;
|
|
4230
|
+
seen.add(sig);
|
|
4225
4231
|
drains.push(entry);
|
|
4226
4232
|
if (entry.capNeed > 0 && entry.cycleMs > 0) {
|
|
4227
4233
|
totalGrossDrain += entry.capNeed / (entry.cycleMs / 1e3);
|
|
@@ -4651,10 +4657,14 @@ function readRangeInfo(item, effect, kind) {
|
|
|
4651
4657
|
let explosionRadius = 0;
|
|
4652
4658
|
let explosionVelocity = 0;
|
|
4653
4659
|
let drf = 0;
|
|
4660
|
+
let flightRange = 0;
|
|
4654
4661
|
if (kind === "MISSILE" && item.charge) {
|
|
4655
4662
|
explosionRadius = item.charge.getFinal(ATTR.EXPLOSION_RADIUS, 0);
|
|
4656
4663
|
explosionVelocity = item.charge.getFinal(ATTR.EXPLOSION_VELOCITY, 0);
|
|
4657
4664
|
drf = item.charge.getFinal(ATTR.DRF, 0);
|
|
4665
|
+
const velocity = item.charge.getFinal(ATTR.MAX_VELOCITY, 0);
|
|
4666
|
+
const flightMs = item.charge.getFinal(ATTR.EXPLOSION_DELAY, 0);
|
|
4667
|
+
flightRange = velocity * flightMs / 1e3;
|
|
4658
4668
|
}
|
|
4659
4669
|
return {
|
|
4660
4670
|
optimal,
|
|
@@ -4663,7 +4673,8 @@ function readRangeInfo(item, effect, kind) {
|
|
|
4663
4673
|
burstRange,
|
|
4664
4674
|
explosionRadius,
|
|
4665
4675
|
explosionVelocity,
|
|
4666
|
-
drf
|
|
4676
|
+
drf,
|
|
4677
|
+
flightRange
|
|
4667
4678
|
};
|
|
4668
4679
|
}
|
|
4669
4680
|
|
|
@@ -4744,6 +4755,7 @@ function computeOffense(ctx, dataset, fit) {
|
|
|
4744
4755
|
const weaponTracking = turretContribs.length > 0 ? Math.min(...turretContribs.map((c) => c.range.tracking).filter((v) => v > 0)) : void 0;
|
|
4745
4756
|
const explosionVelocity = missileContribs.length > 0 ? missileContribs[0].range.explosionVelocity : void 0;
|
|
4746
4757
|
const explosionRadius = missileContribs.length > 0 ? missileContribs[0].range.explosionRadius : void 0;
|
|
4758
|
+
const missileRange = missileContribs.length > 0 ? Math.max(...missileContribs.map((c) => c.range.flightRange).filter((v) => v > 0)) : void 0;
|
|
4747
4759
|
return {
|
|
4748
4760
|
weaponDps,
|
|
4749
4761
|
weaponSustainedDps,
|
|
@@ -4757,6 +4769,7 @@ function computeOffense(ctx, dataset, fit) {
|
|
|
4757
4769
|
weaponTracking,
|
|
4758
4770
|
explosionVelocity,
|
|
4759
4771
|
explosionRadius,
|
|
4772
|
+
missileRange: missileRange != null && Number.isFinite(missileRange) ? missileRange : void 0,
|
|
4760
4773
|
breakdown
|
|
4761
4774
|
};
|
|
4762
4775
|
}
|
|
@@ -4825,7 +4838,8 @@ function fighterContributionFor(fighter, count, _ctx, dataset) {
|
|
|
4825
4838
|
burstRange: 0,
|
|
4826
4839
|
explosionRadius: fighter.getFinal(2125, 0),
|
|
4827
4840
|
explosionVelocity: fighter.getFinal(2126, 0),
|
|
4828
|
-
drf: fighter.getFinal(2127, 0)
|
|
4841
|
+
drf: fighter.getFinal(2127, 0),
|
|
4842
|
+
flightRange: 0
|
|
4829
4843
|
},
|
|
4830
4844
|
count
|
|
4831
4845
|
};
|
|
@@ -5457,6 +5471,7 @@ function deriveStats(ctx, dataset, damageProfile, fit, projectionReports) {
|
|
|
5457
5471
|
weaponTracking: offense.weaponTracking,
|
|
5458
5472
|
explosionVelocity: offense.explosionVelocity,
|
|
5459
5473
|
explosionRadius: offense.explosionRadius,
|
|
5474
|
+
missileRange: offense.missileRange,
|
|
5460
5475
|
breakdown: offense.breakdown
|
|
5461
5476
|
},
|
|
5462
5477
|
capacitor: {
|
package/dist/index.d.cts
CHANGED
|
@@ -464,6 +464,7 @@ interface DerivedStats {
|
|
|
464
464
|
weaponTracking?: number;
|
|
465
465
|
explosionVelocity?: number;
|
|
466
466
|
explosionRadius?: number;
|
|
467
|
+
missileRange?: number;
|
|
467
468
|
breakdown: WeaponContribution[];
|
|
468
469
|
};
|
|
469
470
|
capacitor: {
|
|
@@ -652,6 +653,7 @@ interface WeaponContribution {
|
|
|
652
653
|
explosionRadius: number;
|
|
653
654
|
explosionVelocity: number;
|
|
654
655
|
drf: number;
|
|
656
|
+
flightRange: number;
|
|
655
657
|
};
|
|
656
658
|
chargeTypeID?: number;
|
|
657
659
|
count: number;
|
|
@@ -1025,6 +1027,7 @@ declare const ATTR: {
|
|
|
1025
1027
|
readonly EXPLOSION_VELOCITY: 653;
|
|
1026
1028
|
readonly EXPLOSION_RADIUS: 654;
|
|
1027
1029
|
readonly DRF: 858;
|
|
1030
|
+
readonly EXPLOSION_DELAY: 281;
|
|
1028
1031
|
readonly DOT_DURATION: 5735;
|
|
1029
1032
|
readonly DOT_MAX_DAMAGE_PER_TICK: 5736;
|
|
1030
1033
|
readonly DOT_MAX_HP_PERCENTAGE_PER_TICK: 5737;
|
|
@@ -1756,6 +1759,9 @@ interface OffenseReport {
|
|
|
1756
1759
|
weaponTracking?: number;
|
|
1757
1760
|
explosionVelocity?: number;
|
|
1758
1761
|
explosionRadius?: number;
|
|
1762
|
+
/** Missile max flight range (m) — modified velocity × flight time. The
|
|
1763
|
+
* missile equivalent of `weaponOptimal`. Undefined for non-missile fits. */
|
|
1764
|
+
missileRange?: number;
|
|
1759
1765
|
breakdown: WeaponContribution[];
|
|
1760
1766
|
}
|
|
1761
1767
|
declare function computeOffense(ctx: FitContext, dataset: FittingDataset, fit: {
|
|
@@ -1891,6 +1897,9 @@ interface WeaponRangeInfo {
|
|
|
1891
1897
|
explosionVelocity: number;
|
|
1892
1898
|
/** Missile-specific: damage reduction factor (DRF). */
|
|
1893
1899
|
drf: number;
|
|
1900
|
+
/** Missile-specific: max flight range in meters — modified charge
|
|
1901
|
+
* velocity × flight time (ship/skill/rig bonuses included). 0 otherwise. */
|
|
1902
|
+
flightRange: number;
|
|
1894
1903
|
}
|
|
1895
1904
|
declare function readRangeInfo(item: ItemState, effect: SdeEffect, kind: WeaponEffectKind): WeaponRangeInfo;
|
|
1896
1905
|
|
package/dist/index.d.ts
CHANGED
|
@@ -464,6 +464,7 @@ interface DerivedStats {
|
|
|
464
464
|
weaponTracking?: number;
|
|
465
465
|
explosionVelocity?: number;
|
|
466
466
|
explosionRadius?: number;
|
|
467
|
+
missileRange?: number;
|
|
467
468
|
breakdown: WeaponContribution[];
|
|
468
469
|
};
|
|
469
470
|
capacitor: {
|
|
@@ -652,6 +653,7 @@ interface WeaponContribution {
|
|
|
652
653
|
explosionRadius: number;
|
|
653
654
|
explosionVelocity: number;
|
|
654
655
|
drf: number;
|
|
656
|
+
flightRange: number;
|
|
655
657
|
};
|
|
656
658
|
chargeTypeID?: number;
|
|
657
659
|
count: number;
|
|
@@ -1025,6 +1027,7 @@ declare const ATTR: {
|
|
|
1025
1027
|
readonly EXPLOSION_VELOCITY: 653;
|
|
1026
1028
|
readonly EXPLOSION_RADIUS: 654;
|
|
1027
1029
|
readonly DRF: 858;
|
|
1030
|
+
readonly EXPLOSION_DELAY: 281;
|
|
1028
1031
|
readonly DOT_DURATION: 5735;
|
|
1029
1032
|
readonly DOT_MAX_DAMAGE_PER_TICK: 5736;
|
|
1030
1033
|
readonly DOT_MAX_HP_PERCENTAGE_PER_TICK: 5737;
|
|
@@ -1756,6 +1759,9 @@ interface OffenseReport {
|
|
|
1756
1759
|
weaponTracking?: number;
|
|
1757
1760
|
explosionVelocity?: number;
|
|
1758
1761
|
explosionRadius?: number;
|
|
1762
|
+
/** Missile max flight range (m) — modified velocity × flight time. The
|
|
1763
|
+
* missile equivalent of `weaponOptimal`. Undefined for non-missile fits. */
|
|
1764
|
+
missileRange?: number;
|
|
1759
1765
|
breakdown: WeaponContribution[];
|
|
1760
1766
|
}
|
|
1761
1767
|
declare function computeOffense(ctx: FitContext, dataset: FittingDataset, fit: {
|
|
@@ -1891,6 +1897,9 @@ interface WeaponRangeInfo {
|
|
|
1891
1897
|
explosionVelocity: number;
|
|
1892
1898
|
/** Missile-specific: damage reduction factor (DRF). */
|
|
1893
1899
|
drf: number;
|
|
1900
|
+
/** Missile-specific: max flight range in meters — modified charge
|
|
1901
|
+
* velocity × flight time (ship/skill/rig bonuses included). 0 otherwise. */
|
|
1902
|
+
flightRange: number;
|
|
1894
1903
|
}
|
|
1895
1904
|
declare function readRangeInfo(item: ItemState, effect: SdeEffect, kind: WeaponEffectKind): WeaponRangeInfo;
|
|
1896
1905
|
|
package/dist/index.js
CHANGED
|
@@ -230,6 +230,8 @@ var ATTR = {
|
|
|
230
230
|
EXPLOSION_RADIUS: 654,
|
|
231
231
|
DRF: 858,
|
|
232
232
|
// damage reduction factor
|
|
233
|
+
EXPLOSION_DELAY: 281,
|
|
234
|
+
// charge attr — "Maximum Flight Time" (ms); range = maxVelocity × flightTime/1000
|
|
233
235
|
// ---- Breacher Pods (DOT charges, e.g. SCARAB Breacher Pod M) ----
|
|
234
236
|
DOT_DURATION: 5735,
|
|
235
237
|
// ms
|
|
@@ -4215,11 +4217,15 @@ function collectDrains(ctx, dataset) {
|
|
|
4215
4217
|
}
|
|
4216
4218
|
continue;
|
|
4217
4219
|
}
|
|
4220
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4218
4221
|
for (const eid of m.effectIDs) {
|
|
4219
4222
|
const effect = dataset.effects.get(eid);
|
|
4220
4223
|
if (!effect) continue;
|
|
4221
4224
|
const entry = drainEntryFromEffect(effect, m);
|
|
4222
4225
|
if (!entry) continue;
|
|
4226
|
+
const sig = `${entry.cycleMs}|${entry.capNeed}|${entry.clipSize}|${entry.reloadMs}|${entry.isInjector ? 1 : 0}|${entry.disableStagger ? 1 : 0}`;
|
|
4227
|
+
if (seen.has(sig)) continue;
|
|
4228
|
+
seen.add(sig);
|
|
4223
4229
|
drains.push(entry);
|
|
4224
4230
|
if (entry.capNeed > 0 && entry.cycleMs > 0) {
|
|
4225
4231
|
totalGrossDrain += entry.capNeed / (entry.cycleMs / 1e3);
|
|
@@ -4649,10 +4655,14 @@ function readRangeInfo(item, effect, kind) {
|
|
|
4649
4655
|
let explosionRadius = 0;
|
|
4650
4656
|
let explosionVelocity = 0;
|
|
4651
4657
|
let drf = 0;
|
|
4658
|
+
let flightRange = 0;
|
|
4652
4659
|
if (kind === "MISSILE" && item.charge) {
|
|
4653
4660
|
explosionRadius = item.charge.getFinal(ATTR.EXPLOSION_RADIUS, 0);
|
|
4654
4661
|
explosionVelocity = item.charge.getFinal(ATTR.EXPLOSION_VELOCITY, 0);
|
|
4655
4662
|
drf = item.charge.getFinal(ATTR.DRF, 0);
|
|
4663
|
+
const velocity = item.charge.getFinal(ATTR.MAX_VELOCITY, 0);
|
|
4664
|
+
const flightMs = item.charge.getFinal(ATTR.EXPLOSION_DELAY, 0);
|
|
4665
|
+
flightRange = velocity * flightMs / 1e3;
|
|
4656
4666
|
}
|
|
4657
4667
|
return {
|
|
4658
4668
|
optimal,
|
|
@@ -4661,7 +4671,8 @@ function readRangeInfo(item, effect, kind) {
|
|
|
4661
4671
|
burstRange,
|
|
4662
4672
|
explosionRadius,
|
|
4663
4673
|
explosionVelocity,
|
|
4664
|
-
drf
|
|
4674
|
+
drf,
|
|
4675
|
+
flightRange
|
|
4665
4676
|
};
|
|
4666
4677
|
}
|
|
4667
4678
|
|
|
@@ -4742,6 +4753,7 @@ function computeOffense(ctx, dataset, fit) {
|
|
|
4742
4753
|
const weaponTracking = turretContribs.length > 0 ? Math.min(...turretContribs.map((c) => c.range.tracking).filter((v) => v > 0)) : void 0;
|
|
4743
4754
|
const explosionVelocity = missileContribs.length > 0 ? missileContribs[0].range.explosionVelocity : void 0;
|
|
4744
4755
|
const explosionRadius = missileContribs.length > 0 ? missileContribs[0].range.explosionRadius : void 0;
|
|
4756
|
+
const missileRange = missileContribs.length > 0 ? Math.max(...missileContribs.map((c) => c.range.flightRange).filter((v) => v > 0)) : void 0;
|
|
4745
4757
|
return {
|
|
4746
4758
|
weaponDps,
|
|
4747
4759
|
weaponSustainedDps,
|
|
@@ -4755,6 +4767,7 @@ function computeOffense(ctx, dataset, fit) {
|
|
|
4755
4767
|
weaponTracking,
|
|
4756
4768
|
explosionVelocity,
|
|
4757
4769
|
explosionRadius,
|
|
4770
|
+
missileRange: missileRange != null && Number.isFinite(missileRange) ? missileRange : void 0,
|
|
4758
4771
|
breakdown
|
|
4759
4772
|
};
|
|
4760
4773
|
}
|
|
@@ -4823,7 +4836,8 @@ function fighterContributionFor(fighter, count, _ctx, dataset) {
|
|
|
4823
4836
|
burstRange: 0,
|
|
4824
4837
|
explosionRadius: fighter.getFinal(2125, 0),
|
|
4825
4838
|
explosionVelocity: fighter.getFinal(2126, 0),
|
|
4826
|
-
drf: fighter.getFinal(2127, 0)
|
|
4839
|
+
drf: fighter.getFinal(2127, 0),
|
|
4840
|
+
flightRange: 0
|
|
4827
4841
|
},
|
|
4828
4842
|
count
|
|
4829
4843
|
};
|
|
@@ -5455,6 +5469,7 @@ function deriveStats(ctx, dataset, damageProfile, fit, projectionReports) {
|
|
|
5455
5469
|
weaponTracking: offense.weaponTracking,
|
|
5456
5470
|
explosionVelocity: offense.explosionVelocity,
|
|
5457
5471
|
explosionRadius: offense.explosionRadius,
|
|
5472
|
+
missileRange: offense.missileRange,
|
|
5458
5473
|
breakdown: offense.breakdown
|
|
5459
5474
|
},
|
|
5460
5475
|
capacitor: {
|
package/dist/node.cjs
CHANGED
|
@@ -260,6 +260,8 @@ var ATTR = {
|
|
|
260
260
|
EXPLOSION_RADIUS: 654,
|
|
261
261
|
DRF: 858,
|
|
262
262
|
// damage reduction factor
|
|
263
|
+
EXPLOSION_DELAY: 281,
|
|
264
|
+
// charge attr — "Maximum Flight Time" (ms); range = maxVelocity × flightTime/1000
|
|
263
265
|
// ---- Breacher Pods (DOT charges, e.g. SCARAB Breacher Pod M) ----
|
|
264
266
|
DOT_DURATION: 5735,
|
|
265
267
|
// ms
|
|
@@ -4245,11 +4247,15 @@ function collectDrains(ctx, dataset) {
|
|
|
4245
4247
|
}
|
|
4246
4248
|
continue;
|
|
4247
4249
|
}
|
|
4250
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4248
4251
|
for (const eid of m.effectIDs) {
|
|
4249
4252
|
const effect = dataset.effects.get(eid);
|
|
4250
4253
|
if (!effect) continue;
|
|
4251
4254
|
const entry = drainEntryFromEffect(effect, m);
|
|
4252
4255
|
if (!entry) continue;
|
|
4256
|
+
const sig = `${entry.cycleMs}|${entry.capNeed}|${entry.clipSize}|${entry.reloadMs}|${entry.isInjector ? 1 : 0}|${entry.disableStagger ? 1 : 0}`;
|
|
4257
|
+
if (seen.has(sig)) continue;
|
|
4258
|
+
seen.add(sig);
|
|
4253
4259
|
drains.push(entry);
|
|
4254
4260
|
if (entry.capNeed > 0 && entry.cycleMs > 0) {
|
|
4255
4261
|
totalGrossDrain += entry.capNeed / (entry.cycleMs / 1e3);
|
|
@@ -4679,10 +4685,14 @@ function readRangeInfo(item, effect, kind) {
|
|
|
4679
4685
|
let explosionRadius = 0;
|
|
4680
4686
|
let explosionVelocity = 0;
|
|
4681
4687
|
let drf = 0;
|
|
4688
|
+
let flightRange = 0;
|
|
4682
4689
|
if (kind === "MISSILE" && item.charge) {
|
|
4683
4690
|
explosionRadius = item.charge.getFinal(ATTR.EXPLOSION_RADIUS, 0);
|
|
4684
4691
|
explosionVelocity = item.charge.getFinal(ATTR.EXPLOSION_VELOCITY, 0);
|
|
4685
4692
|
drf = item.charge.getFinal(ATTR.DRF, 0);
|
|
4693
|
+
const velocity = item.charge.getFinal(ATTR.MAX_VELOCITY, 0);
|
|
4694
|
+
const flightMs = item.charge.getFinal(ATTR.EXPLOSION_DELAY, 0);
|
|
4695
|
+
flightRange = velocity * flightMs / 1e3;
|
|
4686
4696
|
}
|
|
4687
4697
|
return {
|
|
4688
4698
|
optimal,
|
|
@@ -4691,7 +4701,8 @@ function readRangeInfo(item, effect, kind) {
|
|
|
4691
4701
|
burstRange,
|
|
4692
4702
|
explosionRadius,
|
|
4693
4703
|
explosionVelocity,
|
|
4694
|
-
drf
|
|
4704
|
+
drf,
|
|
4705
|
+
flightRange
|
|
4695
4706
|
};
|
|
4696
4707
|
}
|
|
4697
4708
|
|
|
@@ -4772,6 +4783,7 @@ function computeOffense(ctx, dataset, fit) {
|
|
|
4772
4783
|
const weaponTracking = turretContribs.length > 0 ? Math.min(...turretContribs.map((c) => c.range.tracking).filter((v) => v > 0)) : void 0;
|
|
4773
4784
|
const explosionVelocity = missileContribs.length > 0 ? missileContribs[0].range.explosionVelocity : void 0;
|
|
4774
4785
|
const explosionRadius = missileContribs.length > 0 ? missileContribs[0].range.explosionRadius : void 0;
|
|
4786
|
+
const missileRange = missileContribs.length > 0 ? Math.max(...missileContribs.map((c) => c.range.flightRange).filter((v) => v > 0)) : void 0;
|
|
4775
4787
|
return {
|
|
4776
4788
|
weaponDps,
|
|
4777
4789
|
weaponSustainedDps,
|
|
@@ -4785,6 +4797,7 @@ function computeOffense(ctx, dataset, fit) {
|
|
|
4785
4797
|
weaponTracking,
|
|
4786
4798
|
explosionVelocity,
|
|
4787
4799
|
explosionRadius,
|
|
4800
|
+
missileRange: missileRange != null && Number.isFinite(missileRange) ? missileRange : void 0,
|
|
4788
4801
|
breakdown
|
|
4789
4802
|
};
|
|
4790
4803
|
}
|
|
@@ -4853,7 +4866,8 @@ function fighterContributionFor(fighter, count, _ctx, dataset) {
|
|
|
4853
4866
|
burstRange: 0,
|
|
4854
4867
|
explosionRadius: fighter.getFinal(2125, 0),
|
|
4855
4868
|
explosionVelocity: fighter.getFinal(2126, 0),
|
|
4856
|
-
drf: fighter.getFinal(2127, 0)
|
|
4869
|
+
drf: fighter.getFinal(2127, 0),
|
|
4870
|
+
flightRange: 0
|
|
4857
4871
|
},
|
|
4858
4872
|
count
|
|
4859
4873
|
};
|
|
@@ -5485,6 +5499,7 @@ function deriveStats(ctx, dataset, damageProfile, fit, projectionReports) {
|
|
|
5485
5499
|
weaponTracking: offense.weaponTracking,
|
|
5486
5500
|
explosionVelocity: offense.explosionVelocity,
|
|
5487
5501
|
explosionRadius: offense.explosionRadius,
|
|
5502
|
+
missileRange: offense.missileRange,
|
|
5488
5503
|
breakdown: offense.breakdown
|
|
5489
5504
|
},
|
|
5490
5505
|
capacitor: {
|
package/dist/node.js
CHANGED
|
@@ -236,6 +236,8 @@ var ATTR = {
|
|
|
236
236
|
EXPLOSION_RADIUS: 654,
|
|
237
237
|
DRF: 858,
|
|
238
238
|
// damage reduction factor
|
|
239
|
+
EXPLOSION_DELAY: 281,
|
|
240
|
+
// charge attr — "Maximum Flight Time" (ms); range = maxVelocity × flightTime/1000
|
|
239
241
|
// ---- Breacher Pods (DOT charges, e.g. SCARAB Breacher Pod M) ----
|
|
240
242
|
DOT_DURATION: 5735,
|
|
241
243
|
// ms
|
|
@@ -4221,11 +4223,15 @@ function collectDrains(ctx, dataset) {
|
|
|
4221
4223
|
}
|
|
4222
4224
|
continue;
|
|
4223
4225
|
}
|
|
4226
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4224
4227
|
for (const eid of m.effectIDs) {
|
|
4225
4228
|
const effect = dataset.effects.get(eid);
|
|
4226
4229
|
if (!effect) continue;
|
|
4227
4230
|
const entry = drainEntryFromEffect(effect, m);
|
|
4228
4231
|
if (!entry) continue;
|
|
4232
|
+
const sig = `${entry.cycleMs}|${entry.capNeed}|${entry.clipSize}|${entry.reloadMs}|${entry.isInjector ? 1 : 0}|${entry.disableStagger ? 1 : 0}`;
|
|
4233
|
+
if (seen.has(sig)) continue;
|
|
4234
|
+
seen.add(sig);
|
|
4229
4235
|
drains.push(entry);
|
|
4230
4236
|
if (entry.capNeed > 0 && entry.cycleMs > 0) {
|
|
4231
4237
|
totalGrossDrain += entry.capNeed / (entry.cycleMs / 1e3);
|
|
@@ -4655,10 +4661,14 @@ function readRangeInfo(item, effect, kind) {
|
|
|
4655
4661
|
let explosionRadius = 0;
|
|
4656
4662
|
let explosionVelocity = 0;
|
|
4657
4663
|
let drf = 0;
|
|
4664
|
+
let flightRange = 0;
|
|
4658
4665
|
if (kind === "MISSILE" && item.charge) {
|
|
4659
4666
|
explosionRadius = item.charge.getFinal(ATTR.EXPLOSION_RADIUS, 0);
|
|
4660
4667
|
explosionVelocity = item.charge.getFinal(ATTR.EXPLOSION_VELOCITY, 0);
|
|
4661
4668
|
drf = item.charge.getFinal(ATTR.DRF, 0);
|
|
4669
|
+
const velocity = item.charge.getFinal(ATTR.MAX_VELOCITY, 0);
|
|
4670
|
+
const flightMs = item.charge.getFinal(ATTR.EXPLOSION_DELAY, 0);
|
|
4671
|
+
flightRange = velocity * flightMs / 1e3;
|
|
4662
4672
|
}
|
|
4663
4673
|
return {
|
|
4664
4674
|
optimal,
|
|
@@ -4667,7 +4677,8 @@ function readRangeInfo(item, effect, kind) {
|
|
|
4667
4677
|
burstRange,
|
|
4668
4678
|
explosionRadius,
|
|
4669
4679
|
explosionVelocity,
|
|
4670
|
-
drf
|
|
4680
|
+
drf,
|
|
4681
|
+
flightRange
|
|
4671
4682
|
};
|
|
4672
4683
|
}
|
|
4673
4684
|
|
|
@@ -4748,6 +4759,7 @@ function computeOffense(ctx, dataset, fit) {
|
|
|
4748
4759
|
const weaponTracking = turretContribs.length > 0 ? Math.min(...turretContribs.map((c) => c.range.tracking).filter((v) => v > 0)) : void 0;
|
|
4749
4760
|
const explosionVelocity = missileContribs.length > 0 ? missileContribs[0].range.explosionVelocity : void 0;
|
|
4750
4761
|
const explosionRadius = missileContribs.length > 0 ? missileContribs[0].range.explosionRadius : void 0;
|
|
4762
|
+
const missileRange = missileContribs.length > 0 ? Math.max(...missileContribs.map((c) => c.range.flightRange).filter((v) => v > 0)) : void 0;
|
|
4751
4763
|
return {
|
|
4752
4764
|
weaponDps,
|
|
4753
4765
|
weaponSustainedDps,
|
|
@@ -4761,6 +4773,7 @@ function computeOffense(ctx, dataset, fit) {
|
|
|
4761
4773
|
weaponTracking,
|
|
4762
4774
|
explosionVelocity,
|
|
4763
4775
|
explosionRadius,
|
|
4776
|
+
missileRange: missileRange != null && Number.isFinite(missileRange) ? missileRange : void 0,
|
|
4764
4777
|
breakdown
|
|
4765
4778
|
};
|
|
4766
4779
|
}
|
|
@@ -4829,7 +4842,8 @@ function fighterContributionFor(fighter, count, _ctx, dataset) {
|
|
|
4829
4842
|
burstRange: 0,
|
|
4830
4843
|
explosionRadius: fighter.getFinal(2125, 0),
|
|
4831
4844
|
explosionVelocity: fighter.getFinal(2126, 0),
|
|
4832
|
-
drf: fighter.getFinal(2127, 0)
|
|
4845
|
+
drf: fighter.getFinal(2127, 0),
|
|
4846
|
+
flightRange: 0
|
|
4833
4847
|
},
|
|
4834
4848
|
count
|
|
4835
4849
|
};
|
|
@@ -5461,6 +5475,7 @@ function deriveStats(ctx, dataset, damageProfile, fit, projectionReports) {
|
|
|
5461
5475
|
weaponTracking: offense.weaponTracking,
|
|
5462
5476
|
explosionVelocity: offense.explosionVelocity,
|
|
5463
5477
|
explosionRadius: offense.explosionRadius,
|
|
5478
|
+
missileRange: offense.missileRange,
|
|
5464
5479
|
breakdown: offense.breakdown
|
|
5465
5480
|
},
|
|
5466
5481
|
capacitor: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eve-fit-engine",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Pyfa-parity EVE Online ship & structure fitting calculation engine. Inject an SDE dataset + a fit, get the full derived stat block (offense, defense, capacitor, navigation, targeting, fitting, projected, structure).",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"type": "module",
|