eve-fit-engine 0.1.35 → 0.2.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.
Files changed (33) hide show
  1. package/data/manifest.json +1 -1
  2. package/data/v0c61c617c9b9/attributes.json +1 -0
  3. package/dist/index.cjs +816 -548
  4. package/dist/index.d.cts +104 -4
  5. package/dist/index.d.ts +104 -4
  6. package/dist/index.js +815 -549
  7. package/dist/node.cjs +816 -548
  8. package/dist/node.d.cts +1 -1
  9. package/dist/node.d.ts +1 -1
  10. package/dist/node.js +815 -549
  11. package/package.json +3 -1
  12. package/data/v796a8249240a/attributes.json +0 -1
  13. /package/data/{v796a8249240a → v0c61c617c9b9}/categories.json +0 -0
  14. /package/data/{v796a8249240a → v0c61c617c9b9}/clone-grades.json +0 -0
  15. /package/data/{v796a8249240a → v0c61c617c9b9}/dbuff-collections.json +0 -0
  16. /package/data/{v796a8249240a → v0c61c617c9b9}/dynamic-attributes.json +0 -0
  17. /package/data/{v796a8249240a → v0c61c617c9b9}/effects.json +0 -0
  18. /package/data/{v796a8249240a → v0c61c617c9b9}/groups.json +0 -0
  19. /package/data/{v796a8249240a → v0c61c617c9b9}/market-groups.json +0 -0
  20. /package/data/{v796a8249240a → v0c61c617c9b9}/meta-groups.json +0 -0
  21. /package/data/{v796a8249240a → v0c61c617c9b9}/types/charges.json +0 -0
  22. /package/data/{v796a8249240a → v0c61c617c9b9}/types/drones.json +0 -0
  23. /package/data/{v796a8249240a → v0c61c617c9b9}/types/fighters.json +0 -0
  24. /package/data/{v796a8249240a → v0c61c617c9b9}/types/implants.json +0 -0
  25. /package/data/{v796a8249240a → v0c61c617c9b9}/types/modules.json +0 -0
  26. /package/data/{v796a8249240a → v0c61c617c9b9}/types/mutaplasmids.json +0 -0
  27. /package/data/{v796a8249240a → v0c61c617c9b9}/types/ships.json +0 -0
  28. /package/data/{v796a8249240a → v0c61c617c9b9}/types/skills.json +0 -0
  29. /package/data/{v796a8249240a → v0c61c617c9b9}/types/structure-modules.json +0 -0
  30. /package/data/{v796a8249240a → v0c61c617c9b9}/types/structures.json +0 -0
  31. /package/data/{v796a8249240a → v0c61c617c9b9}/types/subsystems.json +0 -0
  32. /package/data/{v796a8249240a → v0c61c617c9b9}/types/system-effects.json +0 -0
  33. /package/data/{v796a8249240a → v0c61c617c9b9}/units.json +0 -0
package/dist/index.d.cts CHANGED
@@ -20,6 +20,11 @@ interface SdeAttribute {
20
20
  stackable: boolean;
21
21
  attributeCategoryID?: number;
22
22
  dataType?: number;
23
+ /** Attribute whose value CAPS this one (EVE `maxAttributeID`). Read off the
24
+ * same item, so a hull can raise its own ceiling. */
25
+ maxAttributeID?: number;
26
+ /** Attribute whose value FLOORS this one (EVE `minAttributeID`). */
27
+ minAttributeID?: number;
23
28
  }
24
29
  interface SdeUnit {
25
30
  id: number;
@@ -506,7 +511,9 @@ interface DerivedStats {
506
511
  signatureRadius: number;
507
512
  scanResolution: number;
508
513
  sensorStrength: number;
509
- sensorType: 'radar' | 'ladar' | 'magnetometric' | 'gravimetric' | 'unknown';
514
+ /** Strongest sensor's type; `multispectral` when two or more tie
515
+ * (pyfa's `scanType`). */
516
+ sensorType: 'radar' | 'ladar' | 'magnetometric' | 'gravimetric' | 'multispectral' | 'unknown';
510
517
  };
511
518
  drones: {
512
519
  bayUsed: number;
@@ -1080,6 +1087,27 @@ interface LegacyEffectEntry {
1080
1087
  handler: string;
1081
1088
  }
1082
1089
  declare const LEGACY_EFFECT_IDS: ReadonlyArray<LegacyEffectEntry>;
1090
+ /**
1091
+ * Booster SIDE EFFECTS — the drawbacks a combat booster carries alongside its
1092
+ * bonus (Blue Pill's shield-capacity and capacitor penalties, Crash's armour
1093
+ * and velocity penalties, …).
1094
+ *
1095
+ * They are OPT-IN: in the game the pilot rolls for them, and pyfa models that
1096
+ * with `BoosterSideEffect.active = False` by default, applying an effect only
1097
+ * when the user switches it on (`eos/saveddata/booster.py` — a booster applies
1098
+ * effects that are `passive` OR `boosterSideEffect`, and the latter only when
1099
+ * active). A fit that applies them unconditionally under-reports the ship: a
1100
+ * Strong Blue Pill alone took a Rifter from 562 to 436 shield HP (-22%) and
1101
+ * 312 to 242 GJ of capacitor, on a fit the pilot never asked to be penalised.
1102
+ *
1103
+ * The classification is NOT in the SDE — the effect names are inconsistent
1104
+ * (`boosterShieldCapacityPenalty` is one, but so is
1105
+ * `boosterShieldBoostAmountPenaltyShieldSkills`, while the similarly-named
1106
+ * `shieldBoostAmplifierPassiveBooster` is the BONUS). pyfa hardcodes it as
1107
+ * `type = 'boosterSideEffect'` on twelve handlers in `eos/effects.py`, and this
1108
+ * set is that list. `npm run drift` is what catches pyfa adding a thirteenth.
1109
+ */
1110
+ declare const BOOSTER_SIDE_EFFECT_IDS: ReadonlySet<number>;
1083
1111
  declare const OUT_OF_SCOPE_EFFECT_IDS: ReadonlyArray<LegacyEffectEntry>;
1084
1112
  /** Verify every effect ID in LEGACY_EFFECT_IDS is still present in the
1085
1113
  * loaded dataset. Returns an array of complaints (empty when clean) so
@@ -1187,6 +1215,9 @@ interface ItemStateInit {
1187
1215
  charge?: ItemState;
1188
1216
  /** Per-instance attribute overrides (mutaplasmid). */
1189
1217
  attributeOverrides?: Record<number, number>;
1218
+ /** Booster side effects the pilot has switched ON (effect IDs). Every
1219
+ * other side effect this booster declares stays dormant. */
1220
+ activeSideEffects?: Iterable<number>;
1190
1221
  }
1191
1222
  declare class ItemState {
1192
1223
  readonly kind: ItemKind;
@@ -1199,6 +1230,16 @@ declare class ItemState {
1199
1230
  charge: ItemState | null;
1200
1231
  /** Effect IDs the item carries, populated lazily on first access. */
1201
1232
  readonly effectIDs: ReadonlySet<number>;
1233
+ /** Booster side effects switched on for this instance — see
1234
+ * BOOSTER_SIDE_EFFECT_IDS. Empty for every other item kind. */
1235
+ readonly activeSideEffects: ReadonlySet<number>;
1236
+ /**
1237
+ * Attribute schema, needed to honour the SDE's attribute CAPS
1238
+ * (`maxAttributeID` / `minAttributeID`). Assigned by `computeFit` once for
1239
+ * every item in the fit; when absent, `getFinal` simply doesn't clamp — the
1240
+ * behaviour before caps existed.
1241
+ */
1242
+ attrSchema: ReadonlyMap<number, SdeAttribute> | null;
1202
1243
  readonly attrs: Map<number, ModifiedAttribute>;
1203
1244
  constructor(init: ItemStateInit);
1204
1245
  /**
@@ -1216,6 +1257,28 @@ declare class ItemState {
1216
1257
  hasAttr(id: number): boolean;
1217
1258
  /** Compute the current modified value (cached until next addAffliction). */
1218
1259
  getFinal(id: number, fallback?: number): number;
1260
+ /**
1261
+ * Clamp a computed value to the ceiling / floor the SDE declares for that
1262
+ * attribute, mirroring pyfa's `__calculateValue` (which applies the same
1263
+ * bounds to forced values AND to normally-calculated ones).
1264
+ *
1265
+ * An attribute may name another attribute as its bound — resonances are
1266
+ * capped by `<layer>MaxDamageResonance` (1.0), `maxVelocity` by
1267
+ * `speedLimit`, the `*Bonus` attributes floored by `ConstantMinusNinetyNine`
1268
+ * — and the bound is read off THIS item, so a hull that raises its own
1269
+ * ceiling is honoured.
1270
+ *
1271
+ * Without it a Polarized weapon, which force-assigns every resonance to
1272
+ * `resistanceKiller` = 100, read as -9900% resistance instead of the 0% the
1273
+ * game shows: the cap is what turns "assign 100" into "resistances removed".
1274
+ * Measured on real published fits — five hulls, forty stats.
1275
+ */
1276
+ private clampToCaps;
1277
+ /** Value of a bounding attribute on this item, falling back to the SDE
1278
+ * default when the item doesn't carry it (pyfa's `getOriginal(key,
1279
+ * default)`). Bounds never declare bounds of their own, so this recurses
1280
+ * exactly one level. */
1281
+ private capValue;
1219
1282
  /** `defaultBase` is the seed base value when the target attribute isn't
1220
1283
  * in the item's typeDogma — needed for SDE attrs whose `defaultValue`
1221
1284
  * is non-zero (e.g. `missileDamageMultiplier` defaults to 1; without
@@ -1364,7 +1427,7 @@ declare class FitContext {
1364
1427
  * - EffectStopper: handled outside this resolver (stops other effects
1365
1428
  * rather than applying a modifier).
1366
1429
  */
1367
- targetsForModifier(modifier: SdeModifierInfo, source: ItemState): ItemState[];
1430
+ targetsForModifier(modifier: SdeModifierInfo, source: ItemState, effectID?: number): ItemState[];
1368
1431
  /**
1369
1432
  * Items that count as "located in" a given root. The exact set depends
1370
1433
  * on the root kind:
@@ -1414,7 +1477,23 @@ declare function moduleAcceptsCharge(module: ItemState, charge: ItemState): bool
1414
1477
  * state contribute). EffectStopper modifiers are NOT applied here — see
1415
1478
  * `collectEffectStoppers()` for that.
1416
1479
  */
1417
- declare function applySourceItem(source: ItemState, ctx: FitContext, dataset: FittingDataset): void;
1480
+ /**
1481
+ * Which half of a two-pass application this call is doing.
1482
+ *
1483
+ * `all` (default) is the single-pass behaviour every phase but one uses.
1484
+ *
1485
+ * The exception is the character side. An implant SET is a mutual bonus: every
1486
+ * member carries a `setBonus*` effect that PreMuls the OTHER members' bonus
1487
+ * attribute by its own attr 802, itself included. Applied in one pass the
1488
+ * result depends on the order the implants happen to sit in — a Low-grade Snake
1489
+ * set read 468.9 m/s listed alpha-first and 483.2 m/s listed omega-first, where
1490
+ * pyfa (whose attributes resolve lazily, so order cannot matter) says 503.96.
1491
+ * So the char side runs `peer` first — every modifier that lands on another
1492
+ * implant or booster — and only then `outgoing`, by which point each implant's
1493
+ * bonus attribute already carries the whole set multiplier.
1494
+ */
1495
+ type ApplyPass = 'all' | 'peer' | 'outgoing';
1496
+ declare function applySourceItem(source: ItemState, ctx: FitContext, dataset: FittingDataset, pass?: ApplyPass): void;
1418
1497
  /**
1419
1498
  * Apply a single modifier descriptor. Pulled out for testability and so the
1420
1499
  * top-level engine can call it directly when applying skill effects through
@@ -2202,5 +2281,26 @@ declare function freeHardpointsFor(mod: SdeType, ship: SdeType, fittedHiModules:
2202
2281
  }>, dataset: {
2203
2282
  getType(id: number): SdeType | undefined;
2204
2283
  }): number;
2284
+ /**
2285
+ * The mode a Tactical Destroyer falls back to when the fit names none.
2286
+ *
2287
+ * In game a T3D is ALWAYS in one of its three modes — there is no "no mode"
2288
+ * state — and pyfa encodes that by forcing one: `Ship.validateModeItem(None)`
2289
+ * returns `Mode(modeItems[0])` whenever the hull has modes at all, so every
2290
+ * pyfa T3D fit carries the Defense Mode unless told otherwise.
2291
+ *
2292
+ * We used to leave `mode` unset when a fit didn't name one, which is a state
2293
+ * the game cannot be in, and it showed: a Svipul imported from EFT differed
2294
+ * from pyfa on ELEVEN stats at once (resists, sig, scan resolution, EHP),
2295
+ * because the mode's bonuses were simply absent. Every published Confessor,
2296
+ * Jackdaw, Hecate and Svipul fit was affected.
2297
+ *
2298
+ * Mode items are matched the way pyfa matches them — by NAME prefix within the
2299
+ * "Ship Modifiers" group, since the race field is unreliable — and the default
2300
+ * is the lowest typeID, which is the Defense Mode on every current hull.
2301
+ *
2302
+ * Returns undefined for any hull that has no modes.
2303
+ */
2304
+ declare function defaultModeTypeIDFor(ship: SdeType, dataset: FittingDataset): number | undefined;
2205
2305
 
2206
- export { ACTIVATION_EFFECT_ID, ATTR, type BundleManifest, CATEGORY, type CapacitorReport, type ComputeFitOptions, type ComputedAttribute, type ComputedFit, DAMAGE_PROFILE_PRESETS, type DamageProfile, type DefenseLayerKind, type DerivedStats, type DroneComputed, type EftParseResult, type EwarKind, type FighterComputed, type Fit, type FitBooster, type FitCargo, FitContext, type FitDrone, type FitFighter, type FitImplant, type FitModule, type FitSubsystem, type FitVisibility, type FitWarning, type FittingDataset, type ItemKind, ItemState, LEGACY_EFFECT_IDS, LEGACY_HANDLED_EFFECT_IDS, type LayerEhp, type LegacyEffectEntry, type MarketGroupPlacement, ModifiedAttribute, type ModifierAffliction, type ModifierOperation, type ModuleAttrSnapshot, type ModuleComputed, type ModuleState, type MutatorData, OPERATION_BY_SDE_CODE, OUT_OF_SCOPE_EFFECT_IDS, type OffenseReport, type ProjectedEffectReport, type ProjectedSource, REPAIR_EFFECT_AMOUNT_ATTR, REQUIRED_SKILL_PAIRS, SLOT_EFFECT_ID, SLOT_EFFECT_TO_SLOT_TYPE, STACKING_PENALTY_K, type SdeAttribute, type SdeCategory, type SdeCloneGrade, type SdeDbuffCollection, type SdeDynamicAttribute, type SdeEffect, type SdeGroup, type SdeMarketGroup, type SdeMetaGroup, type SdeModifierInfo, type SdeType, type SdeUnit, type SkillCheckResult, type SkillProfile, type SkillRequirement, type SlotType, type StructureMeta, type StructureServiceModule, TARGET_PROFILE_PRESETS, type TankRates, type TargetProfile, type TypeBucket, WEAPON_EFFECT_KIND, type WeaponContribution, type WeaponEffectKind, type WeaponKind, applyOneModifier, applySkills, applySourceItem, buildNameIndex, canFitModuleOnShip, chargeGroupsForModule, checkSkills, classifyEwar, classifyWeapon, combineJamChances, combineMultiplicative, combinePenalized, combineUnstacked, computeCapacitor, computeFit, computeLayerEhp, computeOffense, computeStructureMeta, computeT3CVariantCode, computeTank, computeTotalEhp, defaultStateForModule, disintegratorCyclesToFullSpool, disintegratorSpoolBonus, ecmJamChance, effectiveDps, ehpUnderProfile, formatDna, formatEft, formatMultibuy, formatTypeIds, freeFitGroupSlotsFor, freeHardpointsFor, isActivatableModule, isMissileLauncher, isSmartBomb, isTurretWeapon, marketGroupPlacement, maxGroupFittedFor, maxTypeFittedFor, moduleAcceptsAnyCharge, moduleAcceptsCharge, moduleAcceptsChargeType, parseEft, peakRecharge, readCycleInfo, readDamageComponents, readRangeInfo, rechargeRateAt, shipGroupRestrictions, shipTypeRestrictions, typeFitsSlotType, verifyLegacyEffectIds };
2306
+ export { ACTIVATION_EFFECT_ID, ATTR, BOOSTER_SIDE_EFFECT_IDS, type BundleManifest, CATEGORY, type CapacitorReport, type ComputeFitOptions, type ComputedAttribute, type ComputedFit, DAMAGE_PROFILE_PRESETS, type DamageProfile, type DefenseLayerKind, type DerivedStats, type DroneComputed, type EftParseResult, type EwarKind, type FighterComputed, type Fit, type FitBooster, type FitCargo, FitContext, type FitDrone, type FitFighter, type FitImplant, type FitModule, type FitSubsystem, type FitVisibility, type FitWarning, type FittingDataset, type ItemKind, ItemState, LEGACY_EFFECT_IDS, LEGACY_HANDLED_EFFECT_IDS, type LayerEhp, type LegacyEffectEntry, type MarketGroupPlacement, ModifiedAttribute, type ModifierAffliction, type ModifierOperation, type ModuleAttrSnapshot, type ModuleComputed, type ModuleState, type MutatorData, OPERATION_BY_SDE_CODE, OUT_OF_SCOPE_EFFECT_IDS, type OffenseReport, type ProjectedEffectReport, type ProjectedSource, REPAIR_EFFECT_AMOUNT_ATTR, REQUIRED_SKILL_PAIRS, SLOT_EFFECT_ID, SLOT_EFFECT_TO_SLOT_TYPE, STACKING_PENALTY_K, type SdeAttribute, type SdeCategory, type SdeCloneGrade, type SdeDbuffCollection, type SdeDynamicAttribute, type SdeEffect, type SdeGroup, type SdeMarketGroup, type SdeMetaGroup, type SdeModifierInfo, type SdeType, type SdeUnit, type SkillCheckResult, type SkillProfile, type SkillRequirement, type SlotType, type StructureMeta, type StructureServiceModule, TARGET_PROFILE_PRESETS, type TankRates, type TargetProfile, type TypeBucket, WEAPON_EFFECT_KIND, type WeaponContribution, type WeaponEffectKind, type WeaponKind, applyOneModifier, applySkills, applySourceItem, buildNameIndex, canFitModuleOnShip, chargeGroupsForModule, checkSkills, classifyEwar, classifyWeapon, combineJamChances, combineMultiplicative, combinePenalized, combineUnstacked, computeCapacitor, computeFit, computeLayerEhp, computeOffense, computeStructureMeta, computeT3CVariantCode, computeTank, computeTotalEhp, defaultModeTypeIDFor, defaultStateForModule, disintegratorCyclesToFullSpool, disintegratorSpoolBonus, ecmJamChance, effectiveDps, ehpUnderProfile, formatDna, formatEft, formatMultibuy, formatTypeIds, freeFitGroupSlotsFor, freeHardpointsFor, isActivatableModule, isMissileLauncher, isSmartBomb, isTurretWeapon, marketGroupPlacement, maxGroupFittedFor, maxTypeFittedFor, moduleAcceptsAnyCharge, moduleAcceptsCharge, moduleAcceptsChargeType, parseEft, peakRecharge, readCycleInfo, readDamageComponents, readRangeInfo, rechargeRateAt, shipGroupRestrictions, shipTypeRestrictions, typeFitsSlotType, verifyLegacyEffectIds };
package/dist/index.d.ts CHANGED
@@ -20,6 +20,11 @@ interface SdeAttribute {
20
20
  stackable: boolean;
21
21
  attributeCategoryID?: number;
22
22
  dataType?: number;
23
+ /** Attribute whose value CAPS this one (EVE `maxAttributeID`). Read off the
24
+ * same item, so a hull can raise its own ceiling. */
25
+ maxAttributeID?: number;
26
+ /** Attribute whose value FLOORS this one (EVE `minAttributeID`). */
27
+ minAttributeID?: number;
23
28
  }
24
29
  interface SdeUnit {
25
30
  id: number;
@@ -506,7 +511,9 @@ interface DerivedStats {
506
511
  signatureRadius: number;
507
512
  scanResolution: number;
508
513
  sensorStrength: number;
509
- sensorType: 'radar' | 'ladar' | 'magnetometric' | 'gravimetric' | 'unknown';
514
+ /** Strongest sensor's type; `multispectral` when two or more tie
515
+ * (pyfa's `scanType`). */
516
+ sensorType: 'radar' | 'ladar' | 'magnetometric' | 'gravimetric' | 'multispectral' | 'unknown';
510
517
  };
511
518
  drones: {
512
519
  bayUsed: number;
@@ -1080,6 +1087,27 @@ interface LegacyEffectEntry {
1080
1087
  handler: string;
1081
1088
  }
1082
1089
  declare const LEGACY_EFFECT_IDS: ReadonlyArray<LegacyEffectEntry>;
1090
+ /**
1091
+ * Booster SIDE EFFECTS — the drawbacks a combat booster carries alongside its
1092
+ * bonus (Blue Pill's shield-capacity and capacitor penalties, Crash's armour
1093
+ * and velocity penalties, …).
1094
+ *
1095
+ * They are OPT-IN: in the game the pilot rolls for them, and pyfa models that
1096
+ * with `BoosterSideEffect.active = False` by default, applying an effect only
1097
+ * when the user switches it on (`eos/saveddata/booster.py` — a booster applies
1098
+ * effects that are `passive` OR `boosterSideEffect`, and the latter only when
1099
+ * active). A fit that applies them unconditionally under-reports the ship: a
1100
+ * Strong Blue Pill alone took a Rifter from 562 to 436 shield HP (-22%) and
1101
+ * 312 to 242 GJ of capacitor, on a fit the pilot never asked to be penalised.
1102
+ *
1103
+ * The classification is NOT in the SDE — the effect names are inconsistent
1104
+ * (`boosterShieldCapacityPenalty` is one, but so is
1105
+ * `boosterShieldBoostAmountPenaltyShieldSkills`, while the similarly-named
1106
+ * `shieldBoostAmplifierPassiveBooster` is the BONUS). pyfa hardcodes it as
1107
+ * `type = 'boosterSideEffect'` on twelve handlers in `eos/effects.py`, and this
1108
+ * set is that list. `npm run drift` is what catches pyfa adding a thirteenth.
1109
+ */
1110
+ declare const BOOSTER_SIDE_EFFECT_IDS: ReadonlySet<number>;
1083
1111
  declare const OUT_OF_SCOPE_EFFECT_IDS: ReadonlyArray<LegacyEffectEntry>;
1084
1112
  /** Verify every effect ID in LEGACY_EFFECT_IDS is still present in the
1085
1113
  * loaded dataset. Returns an array of complaints (empty when clean) so
@@ -1187,6 +1215,9 @@ interface ItemStateInit {
1187
1215
  charge?: ItemState;
1188
1216
  /** Per-instance attribute overrides (mutaplasmid). */
1189
1217
  attributeOverrides?: Record<number, number>;
1218
+ /** Booster side effects the pilot has switched ON (effect IDs). Every
1219
+ * other side effect this booster declares stays dormant. */
1220
+ activeSideEffects?: Iterable<number>;
1190
1221
  }
1191
1222
  declare class ItemState {
1192
1223
  readonly kind: ItemKind;
@@ -1199,6 +1230,16 @@ declare class ItemState {
1199
1230
  charge: ItemState | null;
1200
1231
  /** Effect IDs the item carries, populated lazily on first access. */
1201
1232
  readonly effectIDs: ReadonlySet<number>;
1233
+ /** Booster side effects switched on for this instance — see
1234
+ * BOOSTER_SIDE_EFFECT_IDS. Empty for every other item kind. */
1235
+ readonly activeSideEffects: ReadonlySet<number>;
1236
+ /**
1237
+ * Attribute schema, needed to honour the SDE's attribute CAPS
1238
+ * (`maxAttributeID` / `minAttributeID`). Assigned by `computeFit` once for
1239
+ * every item in the fit; when absent, `getFinal` simply doesn't clamp — the
1240
+ * behaviour before caps existed.
1241
+ */
1242
+ attrSchema: ReadonlyMap<number, SdeAttribute> | null;
1202
1243
  readonly attrs: Map<number, ModifiedAttribute>;
1203
1244
  constructor(init: ItemStateInit);
1204
1245
  /**
@@ -1216,6 +1257,28 @@ declare class ItemState {
1216
1257
  hasAttr(id: number): boolean;
1217
1258
  /** Compute the current modified value (cached until next addAffliction). */
1218
1259
  getFinal(id: number, fallback?: number): number;
1260
+ /**
1261
+ * Clamp a computed value to the ceiling / floor the SDE declares for that
1262
+ * attribute, mirroring pyfa's `__calculateValue` (which applies the same
1263
+ * bounds to forced values AND to normally-calculated ones).
1264
+ *
1265
+ * An attribute may name another attribute as its bound — resonances are
1266
+ * capped by `<layer>MaxDamageResonance` (1.0), `maxVelocity` by
1267
+ * `speedLimit`, the `*Bonus` attributes floored by `ConstantMinusNinetyNine`
1268
+ * — and the bound is read off THIS item, so a hull that raises its own
1269
+ * ceiling is honoured.
1270
+ *
1271
+ * Without it a Polarized weapon, which force-assigns every resonance to
1272
+ * `resistanceKiller` = 100, read as -9900% resistance instead of the 0% the
1273
+ * game shows: the cap is what turns "assign 100" into "resistances removed".
1274
+ * Measured on real published fits — five hulls, forty stats.
1275
+ */
1276
+ private clampToCaps;
1277
+ /** Value of a bounding attribute on this item, falling back to the SDE
1278
+ * default when the item doesn't carry it (pyfa's `getOriginal(key,
1279
+ * default)`). Bounds never declare bounds of their own, so this recurses
1280
+ * exactly one level. */
1281
+ private capValue;
1219
1282
  /** `defaultBase` is the seed base value when the target attribute isn't
1220
1283
  * in the item's typeDogma — needed for SDE attrs whose `defaultValue`
1221
1284
  * is non-zero (e.g. `missileDamageMultiplier` defaults to 1; without
@@ -1364,7 +1427,7 @@ declare class FitContext {
1364
1427
  * - EffectStopper: handled outside this resolver (stops other effects
1365
1428
  * rather than applying a modifier).
1366
1429
  */
1367
- targetsForModifier(modifier: SdeModifierInfo, source: ItemState): ItemState[];
1430
+ targetsForModifier(modifier: SdeModifierInfo, source: ItemState, effectID?: number): ItemState[];
1368
1431
  /**
1369
1432
  * Items that count as "located in" a given root. The exact set depends
1370
1433
  * on the root kind:
@@ -1414,7 +1477,23 @@ declare function moduleAcceptsCharge(module: ItemState, charge: ItemState): bool
1414
1477
  * state contribute). EffectStopper modifiers are NOT applied here — see
1415
1478
  * `collectEffectStoppers()` for that.
1416
1479
  */
1417
- declare function applySourceItem(source: ItemState, ctx: FitContext, dataset: FittingDataset): void;
1480
+ /**
1481
+ * Which half of a two-pass application this call is doing.
1482
+ *
1483
+ * `all` (default) is the single-pass behaviour every phase but one uses.
1484
+ *
1485
+ * The exception is the character side. An implant SET is a mutual bonus: every
1486
+ * member carries a `setBonus*` effect that PreMuls the OTHER members' bonus
1487
+ * attribute by its own attr 802, itself included. Applied in one pass the
1488
+ * result depends on the order the implants happen to sit in — a Low-grade Snake
1489
+ * set read 468.9 m/s listed alpha-first and 483.2 m/s listed omega-first, where
1490
+ * pyfa (whose attributes resolve lazily, so order cannot matter) says 503.96.
1491
+ * So the char side runs `peer` first — every modifier that lands on another
1492
+ * implant or booster — and only then `outgoing`, by which point each implant's
1493
+ * bonus attribute already carries the whole set multiplier.
1494
+ */
1495
+ type ApplyPass = 'all' | 'peer' | 'outgoing';
1496
+ declare function applySourceItem(source: ItemState, ctx: FitContext, dataset: FittingDataset, pass?: ApplyPass): void;
1418
1497
  /**
1419
1498
  * Apply a single modifier descriptor. Pulled out for testability and so the
1420
1499
  * top-level engine can call it directly when applying skill effects through
@@ -2202,5 +2281,26 @@ declare function freeHardpointsFor(mod: SdeType, ship: SdeType, fittedHiModules:
2202
2281
  }>, dataset: {
2203
2282
  getType(id: number): SdeType | undefined;
2204
2283
  }): number;
2284
+ /**
2285
+ * The mode a Tactical Destroyer falls back to when the fit names none.
2286
+ *
2287
+ * In game a T3D is ALWAYS in one of its three modes — there is no "no mode"
2288
+ * state — and pyfa encodes that by forcing one: `Ship.validateModeItem(None)`
2289
+ * returns `Mode(modeItems[0])` whenever the hull has modes at all, so every
2290
+ * pyfa T3D fit carries the Defense Mode unless told otherwise.
2291
+ *
2292
+ * We used to leave `mode` unset when a fit didn't name one, which is a state
2293
+ * the game cannot be in, and it showed: a Svipul imported from EFT differed
2294
+ * from pyfa on ELEVEN stats at once (resists, sig, scan resolution, EHP),
2295
+ * because the mode's bonuses were simply absent. Every published Confessor,
2296
+ * Jackdaw, Hecate and Svipul fit was affected.
2297
+ *
2298
+ * Mode items are matched the way pyfa matches them — by NAME prefix within the
2299
+ * "Ship Modifiers" group, since the race field is unreliable — and the default
2300
+ * is the lowest typeID, which is the Defense Mode on every current hull.
2301
+ *
2302
+ * Returns undefined for any hull that has no modes.
2303
+ */
2304
+ declare function defaultModeTypeIDFor(ship: SdeType, dataset: FittingDataset): number | undefined;
2205
2305
 
2206
- export { ACTIVATION_EFFECT_ID, ATTR, type BundleManifest, CATEGORY, type CapacitorReport, type ComputeFitOptions, type ComputedAttribute, type ComputedFit, DAMAGE_PROFILE_PRESETS, type DamageProfile, type DefenseLayerKind, type DerivedStats, type DroneComputed, type EftParseResult, type EwarKind, type FighterComputed, type Fit, type FitBooster, type FitCargo, FitContext, type FitDrone, type FitFighter, type FitImplant, type FitModule, type FitSubsystem, type FitVisibility, type FitWarning, type FittingDataset, type ItemKind, ItemState, LEGACY_EFFECT_IDS, LEGACY_HANDLED_EFFECT_IDS, type LayerEhp, type LegacyEffectEntry, type MarketGroupPlacement, ModifiedAttribute, type ModifierAffliction, type ModifierOperation, type ModuleAttrSnapshot, type ModuleComputed, type ModuleState, type MutatorData, OPERATION_BY_SDE_CODE, OUT_OF_SCOPE_EFFECT_IDS, type OffenseReport, type ProjectedEffectReport, type ProjectedSource, REPAIR_EFFECT_AMOUNT_ATTR, REQUIRED_SKILL_PAIRS, SLOT_EFFECT_ID, SLOT_EFFECT_TO_SLOT_TYPE, STACKING_PENALTY_K, type SdeAttribute, type SdeCategory, type SdeCloneGrade, type SdeDbuffCollection, type SdeDynamicAttribute, type SdeEffect, type SdeGroup, type SdeMarketGroup, type SdeMetaGroup, type SdeModifierInfo, type SdeType, type SdeUnit, type SkillCheckResult, type SkillProfile, type SkillRequirement, type SlotType, type StructureMeta, type StructureServiceModule, TARGET_PROFILE_PRESETS, type TankRates, type TargetProfile, type TypeBucket, WEAPON_EFFECT_KIND, type WeaponContribution, type WeaponEffectKind, type WeaponKind, applyOneModifier, applySkills, applySourceItem, buildNameIndex, canFitModuleOnShip, chargeGroupsForModule, checkSkills, classifyEwar, classifyWeapon, combineJamChances, combineMultiplicative, combinePenalized, combineUnstacked, computeCapacitor, computeFit, computeLayerEhp, computeOffense, computeStructureMeta, computeT3CVariantCode, computeTank, computeTotalEhp, defaultStateForModule, disintegratorCyclesToFullSpool, disintegratorSpoolBonus, ecmJamChance, effectiveDps, ehpUnderProfile, formatDna, formatEft, formatMultibuy, formatTypeIds, freeFitGroupSlotsFor, freeHardpointsFor, isActivatableModule, isMissileLauncher, isSmartBomb, isTurretWeapon, marketGroupPlacement, maxGroupFittedFor, maxTypeFittedFor, moduleAcceptsAnyCharge, moduleAcceptsCharge, moduleAcceptsChargeType, parseEft, peakRecharge, readCycleInfo, readDamageComponents, readRangeInfo, rechargeRateAt, shipGroupRestrictions, shipTypeRestrictions, typeFitsSlotType, verifyLegacyEffectIds };
2306
+ export { ACTIVATION_EFFECT_ID, ATTR, BOOSTER_SIDE_EFFECT_IDS, type BundleManifest, CATEGORY, type CapacitorReport, type ComputeFitOptions, type ComputedAttribute, type ComputedFit, DAMAGE_PROFILE_PRESETS, type DamageProfile, type DefenseLayerKind, type DerivedStats, type DroneComputed, type EftParseResult, type EwarKind, type FighterComputed, type Fit, type FitBooster, type FitCargo, FitContext, type FitDrone, type FitFighter, type FitImplant, type FitModule, type FitSubsystem, type FitVisibility, type FitWarning, type FittingDataset, type ItemKind, ItemState, LEGACY_EFFECT_IDS, LEGACY_HANDLED_EFFECT_IDS, type LayerEhp, type LegacyEffectEntry, type MarketGroupPlacement, ModifiedAttribute, type ModifierAffliction, type ModifierOperation, type ModuleAttrSnapshot, type ModuleComputed, type ModuleState, type MutatorData, OPERATION_BY_SDE_CODE, OUT_OF_SCOPE_EFFECT_IDS, type OffenseReport, type ProjectedEffectReport, type ProjectedSource, REPAIR_EFFECT_AMOUNT_ATTR, REQUIRED_SKILL_PAIRS, SLOT_EFFECT_ID, SLOT_EFFECT_TO_SLOT_TYPE, STACKING_PENALTY_K, type SdeAttribute, type SdeCategory, type SdeCloneGrade, type SdeDbuffCollection, type SdeDynamicAttribute, type SdeEffect, type SdeGroup, type SdeMarketGroup, type SdeMetaGroup, type SdeModifierInfo, type SdeType, type SdeUnit, type SkillCheckResult, type SkillProfile, type SkillRequirement, type SlotType, type StructureMeta, type StructureServiceModule, TARGET_PROFILE_PRESETS, type TankRates, type TargetProfile, type TypeBucket, WEAPON_EFFECT_KIND, type WeaponContribution, type WeaponEffectKind, type WeaponKind, applyOneModifier, applySkills, applySourceItem, buildNameIndex, canFitModuleOnShip, chargeGroupsForModule, checkSkills, classifyEwar, classifyWeapon, combineJamChances, combineMultiplicative, combinePenalized, combineUnstacked, computeCapacitor, computeFit, computeLayerEhp, computeOffense, computeStructureMeta, computeT3CVariantCode, computeTank, computeTotalEhp, defaultModeTypeIDFor, defaultStateForModule, disintegratorCyclesToFullSpool, disintegratorSpoolBonus, ecmJamChance, effectiveDps, ehpUnderProfile, formatDna, formatEft, formatMultibuy, formatTypeIds, freeFitGroupSlotsFor, freeHardpointsFor, isActivatableModule, isMissileLauncher, isSmartBomb, isTurretWeapon, marketGroupPlacement, maxGroupFittedFor, maxTypeFittedFor, moduleAcceptsAnyCharge, moduleAcceptsCharge, moduleAcceptsChargeType, parseEft, peakRecharge, readCycleInfo, readDamageComponents, readRangeInfo, rechargeRateAt, shipGroupRestrictions, shipTypeRestrictions, typeFitsSlotType, verifyLegacyEffectIds };