-
Notifications
You must be signed in to change notification settings - Fork 0
Document.Engineering
@elite-dangerous-almanac/core / Engineering
The engineering API answers two questions: what can I put on this module, and what does it do to the stats. The menu is also the validation gate, so an editor and its menu cannot give different answers.
Availability is a property of the module, not of the blueprint. A Pulse Laser accepts
the Efficient blueprint and a Rail Gun does not, and two modules whose blueprints overlap
may still offer different experimental effects. So ships/engineering-options groups the
modules and each group lists what it offers.
import {
getBlueprintsForModule,
getExperimentalsForModule,
} from '@elite-dangerous-almanac/core/ships/engineering-options';
getBlueprintsForModule('Hpt_PulseLaser_Fixed_Small'); // the recipes this weapon takes
getExperimentalsForModule('Hpt_PulseLaser_Fixed_Small'); // the effects it takesapplyBlueprint reads that same catalogue and refuses a recipe it does not list for that module. That is the point: "what can I put on this?" and "may I put this on it?" are answered from one source, so a menu that offers a recipe the editor would reject is not a state this library can reach.
ShipLoadout includes the menu catalogue whether or not a consumer calls these methods.
Using one catalogue keeps the editor and menu consistent.
Not every module engineers. The catalogue groups 1028 of the 1199 modules; the rest take nothing — whole families like fuel tanks, passenger cabins and limpet controllers, plus individual modules upstream denies every recipe. A module with no group is not a gap in the data.
Inferring and comparing families from module and blueprint symbols refuses recipes on 52
modules and disagrees with 76 entries in a 1902-entry build corpus. The failures come from
the inference rather than the data — the Hatch Breaker
Limpet Controller's symbol is Int_DroneControl_ResourceSiphon, which no "hatchbreaker"
prefix rule matches, and the Caustic Sink Launcher's symbol says causticsink where its
group is the heat sink launchers'. A per-module menu has nothing to infer, and two
hand-maintained answers to one question can drift. The API therefore uses only the
per-module menu.
A blueprint grade bounds each modifier, and the engineering quality roll picks a point
in that range: v = min + (max − min) × quality. An experimental effect adds a fixed
contribution on top. computeModifiers folds every
contribution onto the module's base stats and hands back journal-style modifiers.
import { computeModifiers } from '@elite-dangerous-almanac/core/ships/engineering';
import { getBlueprintGrade } from '@elite-dangerous-almanac/core/ships/blueprints';
import { getExperimentalEffect } from '@elite-dangerous-almanac/core/ships/experimental-effects';
const mods = computeModifiers(
{ FSDOptimalMass: 4670 },
getBlueprintGrade('FSD_LongRange', 5)!,
1, // quality: 0 is the worst roll, 1 the best
getExperimentalEffect('special_fsd_heavy')!,
);
// -> [{ Label: 'FSDOptimalMass', Value: 7528.04, OriginalValue: 4670 }]Each contribution names a journal Label and an apply method — multiplicative (the
percentages compound), additive (flat reinforcement) or overwrite (the value replaces
the base). Two behaviors apply on top of those methods:
- Percentages of a multiplier. Hull boost, shield boost and the four resistances compound on their multiplier rather than on the stat, whichever method the recipe names. Build metrics works through what that does to a figure.
-
A capability rather than a number. Anti-Guardian Zone Resistance produces
{ Label: 'GuardianModuleResistance', ValueStr: 'Active' }; its displayed+100%is not folded as a value, and effective module stats expose the granted boolean instead.
On a build, all of this is one call, and it recomputes the derived stats — a weapon's rate of fire, for instance — that follow from what the recipe changed:
import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
const build = ShipLoadout.empty('Anaconda');
build.availableBlueprints('FrameShiftDrive'); // what this mount can take
build.availableExperimentalEffects('FrameShiftDrive');The gate makes exactly three allowances beyond the menu. They are applied in a fixed order — journal spelling, then the pre-engineered route, then the generic spelling — and only the first can change which recipe an accepted id names.
The journal spelling of a menu entry. Where the game writes one BlueprintName for two
different recipes, the module's own group carries the map from that id to the entry it
names. Only the three utility-scanner groups need one. This is pinned data rather than
inference, because unlike the generic spellings the two ids do not describe the same
modification.
The generic spelling. Where a modification applies to several families, the game writes
a family-specific id and the catalogue lists that one — but a build authored elsewhere may
carry the generic Misc_* id. Both are accepted, because both name the same recipe: their
grades touch the same labels by the same methods.
The alias is directional, and that is what keeps it safe. A generic id stands in for a
family's id, never for another generic one. Misc_ChaffCapacity and Misc_HeatSinkCapacity
are both "Ammo capacity" over the same three labels, but they roll different amounts of
different ammunition, so neither may substitute for the other.
The pre-engineered route. Most Operations keys belong to a module bought already
engineered, so no menu lists one and the menu check alone would refuse all of them
everywhere. ships/pre-engineered names which module each arrives on, and the gate accepts
such a recipe on the non-final module sold carrying it and nowhere else — RailGun_LongShot
resolves on the medium rail gun, not on the small one.
This route covers the climb, not the purchase. A Mercenary module arrives at grade 1 and
its recipe publishes grades 2–5, the grades an engineer can still add; the grade it was sold
at cannot be reproduced through this route, and a variant marked engineeringLocked never
takes it at all.
Use ships/pre-engineered to find a variant and ships/pre-engineered-stats to resolve
its fitted stats.
import { getPreEngineeredVariants } from '@elite-dangerous-almanac/core/ships/pre-engineered';
import { getPreEngineeredStats } from '@elite-dangerous-almanac/core/ships/pre-engineered-stats';
const variants = getPreEngineeredVariants('Hpt_BasicMissileRack_Fixed_Medium');
const resolved = variants[0] ? getPreEngineeredStats(variants[0]) : null;
resolved?.symbol;The API documentation owns the record and resolution semantics: see PreEngineeredVariant, getPreEngineeredVariants, and getPreEngineeredStats.
Costs are a separate catalogue from mechanics, on their own subpaths, so a build
editor that never prices anything does not bundle the shopping lists. ships/blueprints
answers what a recipe does; ships/blueprint-costs answers what it takes.
import { getBlueprint } from '@elite-dangerous-almanac/core/ships/blueprints';
import { getBlueprintCost } from '@elite-dangerous-almanac/core/ships/blueprint-costs';
import { rollsForGrade } from '@elite-dangerous-almanac/core/ships/engineering';
getBlueprint('FSD_LongRange'); // the mechanics: every grade's features
getBlueprintCost('FSD_LongRange', 5); // materials for the whole climb, grades 1–5
getBlueprintCost('FSD_LongRange', 5, 4); // grade 5 alone, from a grade-4 module
rollsForGrade(5); // rolls to fill the grade's progress barA grade costs its recipe once per roll and grade g takes g rolls, so the climb is
weighted rather than a plain sum — which is what getBlueprintCost folds in for you.
An experimental effect is a separate single application, on its own subpath again, and
sumMaterials folds the two bills into one:
import { getBlueprintCost } from '@elite-dangerous-almanac/core/ships/blueprint-costs';
import { getExperimentalEffectCost } from '@elite-dangerous-almanac/core/ships/experimental-effect-costs';
import { sumMaterials } from '@elite-dangerous-almanac/core/ships/engineering';
const grand = sumMaterials(
getBlueprintCost('Weapon_LongRange', 5) ?? [],
getExperimentalEffectCost('special_focused') ?? [],
);
grand.length;Pricing the remaining upgrade on a module bought pre-engineered is the same call with the grade it arrived at as the third argument. On a reward variant it is not: those carry hand-set overrides no blueprint grants, so the ordinary material recipe prices ordinary engineering rather than the reward.
- Build metrics — what these modifiers do to power, shields, armour, weapons and range.
- Building an outfitting screen
- Reading a player journal
- Complete API reference
Guides
astro
Classes (1)
ProceduralSystem
Properties
Accessors
Methods
Interfaces (16)
Type Aliases (3)
Variables (11)
Functions (37)
- absoluteBoxelToBoxelCode
- boxelCodeToAbsoluteBoxel
- boxelCodeToLetters
- boxelEdgeLy
- boxelInternalSize
- canonicalizeSectorName
- canonicalizeSystemName
- decodeModSystemAddress
- decodeSystemAddress
- encodeModSystemAddress
- encodeSystemAddress
- findHandAuthoredRegionAt
- formatSystemName
- getCodexRegion
- getCodexRegionByName
- getHandAuthoredRegionOrigin
- getNebulaByName
- isPermitLockedRegionName
- isPermitLockedSystemName
- isProceduralSystemName
- lettersToBoxelCode
- massCodeToSizeClass
- nearestNebulae
- nebulaeWithin
- parseSystemName
- permitLockedRegionForSystemName
- permitLockedSystemForAddress
- permitLockedSystemForName
- permitLockForSystemName
- resolveNamingRegionOrigin
- sectorGridPositionFromGalacticPosition
- sectorGridPositionFromName
- sectorNameFromGalacticPosition
- sectorNameFromGridPosition
- sizeClassToMassCode
- toSystemAddress
- tryToSystemAddress
commodities
Interfaces (1)
Type Aliases (1)
Variables (3)
Functions (3)
equipment
Interfaces (6)
Type Aliases (8)
Variables (3)
Functions (10)
Subpath modules (2)
i18n
Interfaces (1)
Type Aliases (1)
Functions (17)
- getBlueprintName
- getCalculationIssueMessage
- getEngineeringGroupName
- getExperimentalEffectDescription
- getExperimentalEffectName
- getLoadoutEditErrorMessage
- getLoadoutIssueMessage
- getLoadoutSlotName
- getMaterialName
- getMicroResourceName
- getModuleName
- getOutfittingFamilyName
- getPreEngineeredVariantName
- getShipManufacturer
- getShipName
- getSlefDiagnosticMessage
- getSlotRestrictionLabel
materials
Enumerations (2)
Interfaces (2)
Type Aliases (2)
Variables (9)
ships
Classes (3)
BuildMetrics
Methods
- armourMetrics()
- buildCost()
- buildMass()
- cellBanks()
- distributorMetrics()
- distributorMetricsResult()
- frameShiftDrive()
- frameShiftDriveMassFactor()
- fuelPerJump()
- heatMetrics()
- heatMetricsResult()
- jumpRange()
- jumpRangeSummary()
- ladenJumpRange()
- loadout()
- maxJumpRange()
- mobilityCapacitorMetrics()
- mobilityCapacitorMetricsResult()
- mobilityMetrics()
- mobilityMetricsResult()
- powerBudget()
- shieldCapacitorMetrics()
- shieldCapacitorMetricsResult()
- shieldMetrics()
- shieldMetricsResult()
- shieldRecovery()
- shieldRecoveryResult()
- standardLoad()
- standardLoadResult()
- thrusters()
- totalRange()
- weaponMetrics()
- weaponsCapacitorMetrics()
- of()
LoadoutEditError
Constructors
Properties
Methods
ShipLoadout
Accessors
- cargoCapacity
- fuelCapacity
- hullValue
- importOutcomes
- modulesValue
- rebuy
- shipIdent
- shipName
- shipSymbol
- sourcePurchase
- unladenMass
Methods
- applyBlueprint()
- availableBlueprints()
- availableExperimentalEffects()
- clearEngineering()
- completeEngineeringGrade()
- fittedModuleAt()
- fittedModules()
- modulesForSlot()
- removeModule()
- repairFixedMount()
- setExperimentalEffect()
- setModule()
- setModuleEnabled()
- setModulePriority()
- setPreEngineeredVariant()
- slots()
- toLoadoutEvent()
- toSlef()
- toSlefString()
- validation()
- default()
- empty()
- fromLoadout()
- fromSlef()
Interfaces (125)
- AmmunitionCapacity
- AmmunitionStats
- ApplyBlueprintOptions
- ArmourInput
- ArmourMetrics
- AvailableBlueprint
- Blueprint
- BlueprintFeature
- BlueprintGrade
- BlueprintModuleEngineering
- BuildCost
- BuildCredits
- BuildMass
- BuildSlotBase
- BuildWeaponMetrics
- BulkheadParams
- CalculationIssue
- CellBankInput
- CellBankMetrics
- CellBankSummary
- CoreBuildSlot
- CoreSlots
- DamageComponents
- DamageDistribution
- DamageResistanceParams
- DamageSplit
- DamageTypeValues
- DistributorCapacitorMetrics
- DistributorInput
- DistributorMetrics
- DistributorOptions
- DistributorPips
- EngineeringMaterial
- EngineeringModifier
- EngineeringNormalizationUnchanged
- EngineeringNormalizationUnsupported
- EngineeringNormalized
- EngineeringOptionGroup
- ExperimentalContribution
- ExperimentalEffect
- ExperimentalEffectUnchanged
- ExperimentalEffectUnsupported
- ExperimentalEffectUpdated
- FittedModule
- FittedWeaponMetrics
- FrameShiftDriveJumpStats
- FrameShiftDriveParams
- FuelCapacity
- GunsightPoint
- HardpointBuildSlot
- HardpointSlotSpec
- HeatInput
- HeatMetrics
- HeatState
- HeatWeapon
- HullReinforcementParams
- JumpOptions
- JumpRangeSummary
- LoadoutCalculationModule
- LoadoutEvent
- LoadoutExportOptions
- LoadoutIssue
- LoadoutMass
- LoadoutModule
- LoadoutValidation
- LoadoutValidationInput
- MassCurveStats
- MobilityCapacitorInput
- MobilityCapacitorMetrics
- MobilityCapacitorOptions
- MobilityInput
- MobilityMetrics
- ModuleLimitEntry
- ModuleLimitIncrease
- ModuleLimitUsage
- ModuleReinforcementParams
- OptionalBuildSlot
- OptionalSlotSpec
- OutfittingModule
- OutfittingModuleIdentity
- OutfittingModuleStats
- ParsedSlot
- PowerBand
- PowerBudget
- PowerConsumer
- PowerConsumerResult
- PowerDistributorStats
- PowerGenerationStats
- PreEngineeredModifier
- PreEngineeredVariant
- ProjectileRangeBoundaries
- ShieldBoosterParams
- ShieldCapacitorInput
- ShieldCapacitorMetrics
- ShieldCapacitorOptions
- ShieldGeneratorParams
- ShieldInput
- ShieldMetrics
- ShieldRecovery
- ShieldRecoveryInput
- ShieldRecoveryOptions
- ShieldRegenerationStats
- Ship
- ShipSlots
- SimpleBuildSlot
- SlefDiagnostic
- SlefEntry
- SlefExportOptions
- SlefHeader
- SlefInspection
- SlefStringifyOptions
- SourceModuleValue
- SourcePurchaseRecord
- StandardLoadInputs
- ThrusterCurveParams
- ThrusterParams
- TotalRangeDetails
- ValidationModule
- WeaponDamageStats
- WeaponMetrics
- WeaponsCapacitorInput
- WeaponsCapacitorMetrics
- WeaponsOptions
- WeaponStats
- WeaponTotals
Type Aliases (44)
- BlueprintGrades
- BuildSlot
- CalculationIssueReason
- CalculationResult
- CoreSlotType
- DamageResistances
- DamageType
- EngineeringGroupId
- EngineeringNormalizationCode
- EngineeringNormalizationResult
- ExperimentalEffectMutationCode
- ExperimentalEffectMutationResult
- FixedMountRepairResult
- GunsightOffset
- HardpointRestriction
- ImmovableReason
- LoadoutEditErrorCode
- LoadoutImportOutcome
- LoadoutIssueCode
- LoadoutIssueParam
- LoadoutIssueParams
- LoadoutSlot
- ModifierMethod
- ModuleCategory
- ModuleEngineering
- ModuleExclusionGroup
- ModuleFitConstraint
- ModuleGuidance
- ModuleLimitGroup
- ModuleMount
- ModuleRating
- ModuleSlot
- OptionalRestriction
- OutfittingFamilyId
- PreEngineeredAcquisition
- ShipGunsight
- ShipGunsightCatalogue
- Slef
- SlefConstraint
- SlefDiagnosticCode
- SlotKind
- SlotRestriction
- StandardLoad
- ThrusterLoad
Variables (10)
Functions (85)
- ammunitionCapacity
- armourMetrics
- armourPiercingFactor
- calculateCargoCapacity
- calculateFuelCapacity
- calculateModuleLimits
- calculateUnladenMass
- cellBankSummary
- combinedRateOfFire
- computeModifiers
- damageFalloff
- damagePerSecond
- distributorMetrics
- effectiveHitPoints
- effectiveWeaponThermalLoad
- energyPerSecond
- enumerateSlots
- equilibriumHeatLevel
- frameShiftDriveMassFactor
- fuelPerJump
- getBlueprint
- getBlueprintGrade
- getBlueprintsForModule
- getBulkheadsForShip
- getEngineeringGroup
- getExperimentalEffect
- getExperimentalsForBlueprint
- getExperimentalsForModule
- getLoadoutModifier
- getModuleBySymbol
- getModulesByName
- getPreEngineeredJournalModifiers
- getPreEngineeredModifiers
- getPreEngineeredStats
- getPreEngineeredVariants
- getShipByName
- getShipBySymbol
- getShipGunsight
- getShipSlots
- getSourceModuleValue
- hasFrameShiftDriveJumpStats
- hasMassCurveStats
- hasPowerDistributorStats
- hasPowerGenerationStats
- hasShieldRegenerationStats
- hasWeaponDamageStats
- heatLevelAtTime
- heatMetrics
- heatPerSecond
- identifyPreEngineeredVariant
- inspectSlef
- isPreEngineered
- mapDamageTypes
- mobilityCapacitorMetrics
- mobilityMetrics
- parseSlef
- parseSlotName
- powerBudget
- projectGunsight
- resolveBlueprintForModule
- secondsToHeatLevel
- shieldCapacitorMetrics
- shieldMassCurveMultiplier
- shieldMetrics
- shieldRecovery
- shieldStrength
- singleJumpRange
- sourcePurchaseFromLoadout
- splitDamage
- stackArmourResistance
- stackShieldResistance
- stringifySlef
- sumMaterials
- sumSourceModuleValues
- sumWeaponMetrics
- sustainedDamagePerSecond
- sustainedFireFactor
- systemsResistance
- thrusterMassCurveMultiplier
- toSlef
- totalRange
- unresolvedModifiers
- validateLoadout
- weaponMetrics
- weaponsCapacitorMetrics