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 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. 1646/1676 fits (98.2%) match exactly; the residual is a documented
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.2",
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",