Skip to content

ships.Class.ShipLoadout

github-actions[bot] edited this page Aug 23, 2026 · 79 revisions

@elite-dangerous-almanac/core / ships / ShipLoadout

Class: ShipLoadout

Defined in: src/ships/ship-loadout.ts:696

A fitted ship — read a SLEF export, or assemble a hull from scratch.

Example

Read a build a player already flies, and ask it what an outfitting screen shows. Every figure below is one build's — a Krait Phantom explorer. Figures the capture already stated — unladenMass here — are trusted verbatim while the fit they describe survives import; the rest are computed from the fit.

import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
import type { LoadoutEvent } from '@elite-dangerous-almanac/core/ships/slef';

// A `Loadout` line lifted from a player journal, parsed.
declare const event: LoadoutEvent;

const build = ShipLoadout.fromLoadout(event);

build.shipSymbol; // -> 'krait_light'
build.shipName; // -> 'Jenny Longuet'
build.unladenMass; // -> 388.830017   (tonnes)

build.maxJumpRange(); // -> 60.5478   (ly, best single jump)
build.powerBudget().withinBudget; // -> true
build.shieldMetrics()?.strength; // -> 743.12  (MJ)
build.armourMetrics().hitPoints; // -> 307.8

Accessors

cargoCapacity

Get Signature

get cargoCapacity(): number

Defined in: src/ships/ship-loadout.ts:991

Cargo capacity, in tonnes — a capture's CargoCapacity on the same terms as unladenMass, otherwise the sum of the fitted racks.

Returns

number


frameShiftDrive

Get Signature

get frameShiftDrive(): FrameShiftDriveParams

Defined in: src/ships/ship-loadout.ts:2515

The resolved frame-shift-drive constants for this build — post-engineering, with any Guardian FSD Booster folded into jumpBoost.

Throws

If the fitted drive's record is missing any of its required jump constants.

Returns

FrameShiftDriveParams


fuelCapacity

Get Signature

get fuelCapacity(): FuelCapacity

Defined in: src/ships/ship-loadout.ts:975

Fuel-tank capacities, in tonnes — a capture's FuelCapacity on the same terms as unladenMass, otherwise the fitted tanks plus the hull's own reserve.

Returns

FuelCapacity


hullValue

Get Signature

get hullValue(): number | null

Defined in: src/ships/ship-loadout.ts:1013

Hull cost in credits represented by the build, or null if unknown.

Remarks

This is the live figure, kept coherent with edits: an import's own HullValue until something invalidates it. For the capture's figure as captured — which no edit changes — read sourcePurchase.

Returns

number | null


importOutcomes

Get Signature

get importOutcomes(): readonly LoadoutImportOutcome[]

Defined in: src/ships/ship-loadout.ts:1089

Changes made while importing this build, in source order, followed by the fixed mounts stocked from the hull defaults because the source named none, in the defaults' own order.

Remarks

Each entry names the exact slot, and the source identity where the source gave one. emptied means an unknown module was removed from a removable mount; defaulted names the stock article fitted to armour, a core internal or the cargo hatch, with a null sourceSymbol when the source named nothing there at all.

Returns

readonly LoadoutImportOutcome[]

A deeply frozen list. It is empty for builds created with ShipLoadout.empty or ShipLoadout.default, and for imports that needed no normalization.


modulesValue

Get Signature

get modulesValue(): number | null

Defined in: src/ships/ship-loadout.ts:1024

Fitted-modules cost in credits represented by the build, or null if unknown — including after an edit or import normalization discarded an import's figure, since no catalogue records what a replaced module was bought for. Unlike mass and capacity it is not recomputed from what remains; sourcePurchase keeps the captured figure and buildCost prices the current fit.

Returns

number | null


rebuy

Get Signature

get rebuy(): number | null

Defined in: src/ships/ship-loadout.ts:1034

Insurance rebuy cost in credits represented by the build, or null if unknown. Discarded by an edit or by import normalization for the same reason as modulesValue, and likewise kept by sourcePurchase; buildCost rebuys the current fit at catalogue prices instead.

Returns

number | null


shipIdent

Get Signature

get shipIdent(): string | null

Defined in: src/ships/ship-loadout.ts:946

The player-given ID plate, or null if the build has none.

Returns

string | null


shipName

Get Signature

get shipName(): string | null

Defined in: src/ships/ship-loadout.ts:941

The player-given ship name, or null if the build has none.

Returns

string | null


shipSymbol

Get Signature

get shipSymbol(): string

Defined in: src/ships/ship-loadout.ts:936

The hull's internal id, e.g. "explorer_nx".

Returns

string


sourcePurchase

Get Signature

get sourcePurchase(): SourcePurchaseRecord | null

Defined in: src/ships/ship-loadout.ts:1071

What the capture this build came from said was paid for it — a read-only SourcePurchaseRecord, or null for a build assembled here or imported from a capture that quoted no credits at all.

Remarks

The record is provenance about the source, so it is fixed at import and survives every edit: fit, remove or engineer whatever you like and it still reports the figures the capture carried, for the modules the capture carried them for. That is what hullValue, modulesValue and rebuy cannot do — they describe the build in hand, so an edit that invalidates one drops it.

A captured price belongs to one commander's purchase history, discounts included; the library's own figures are catalogue retail. Export quotes retail unless asked otherwise — see LoadoutExportOptions.credits.

Example
import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
import { getSourceModuleValue } from '@elite-dangerous-almanac/core/ships/source-purchase';

declare const slefJson: string;

const build = ShipLoadout.fromSlef(slefJson);
const paid = build.sourcePurchase!;
paid.hullValue; // -> 189326510, as captured
getSourceModuleValue(paid, 'powerplant')?.value; // -> what that plant cost its owner

build.removeModule('Slot05_Size4');
build.modulesValue;                 // -> null   unavailable after the edit
paid.modulesValue;                  // -> 192625195, the captured figure
Returns

SourcePurchaseRecord | null


unladenMass

Get Signature

get unladenMass(): number

Defined in: src/ships/ship-loadout.ts:959

Hull + modules mass with an empty tank and no cargo, in tonnes.

Remarks

A capture's own UnladenMass stands while the fit it described survives import (see fromLoadout); otherwise this is the hull's hullMass plus every fitted module's post-engineering mass, and importOutcomes is the only report that the figure is the normalized fit's rather than the capture's.

Returns

number


validation

Get Signature

get validation(): LoadoutValidation

Defined in: src/ships/ship-loadout.ts:1104

Structural validity and operational completeness of this build.

Remarks

valid asks whether the fit is legal: a module in a nonexistent or incompatible slot, a duplicated exclusive family, or a module count past the build's allowance makes it false. complete asks that and whether armour and the seven core mounts are filled — every build fills those, so on a build the two answers agree. Neither question reports import normalization, so read importOutcomes beside them.

Returns

LoadoutValidation

Methods

applyBlueprint()

applyBlueprint(slotKey, fdname, options): this

Defined in: src/ships/ship-loadout.ts:1664

Engineer the module in a slot — apply a blueprint (with a grade and quality) and an optional experimental effect, computing the resulting stat modifiers.

The modifiers are stored on the fitted module with journal-equivalent labels and Frontier's float32 arithmetic, so the build's own calculations pick them up. The block keeps the BlueprintName you passed. Weapon recipe internals such as BurstInterval are exposed as the derived RateOfFire and DamagePerSecond labels a journal writes, and module-specific aliases use the journal spelling too (MaximumRange, Range); recipe-only values a journal serializes no label for stay available through FittedModule.effectiveStats, which is what keeps burst and reload-cycle calculations faithful.

Which recipe an id names can depend on the module. The game writes Sensor_LongRange for both a sensor suite's modification and a utility scanner's, and the two roll different stats in opposite directions, so the id is resolved against the module's own menu before anything is computed. Reading a stored block back means resolving it the same way: resolveBlueprintForModule in ships/blueprint-journal is that lookup.

Parameters

slotKey

string

The slot whose module to engineer, matched case-insensitively (journal spelling).

fdname

string

The blueprint recipe's Frontier fdname, e.g. "FSD_LongRange".

options

ApplyBlueprintOptions

ApplyBlueprintOptions: grade (1–5), optional quality (0–1, default 1), and optional experimental effect fdname. A nullish experimental means no effect, the same as leaving it out. Each is read once, before anything is checked, so an accessor cannot answer the check and the use differently.

Returns

this

this, for chaining.

Throws

If the slot is empty, or the blueprint/grade/experimental is unknown, or quality is outside [0, 1].

Throws

If slotKey or fdname is not a string, options is not an object, or options.experimental carries a value that is not a string — a nullish one is no effect, not a wrong type. Also if the fitted module has no stats to engineer, is final and accepts no further engineering, is not offered the blueprint by its own menu, is not offered the experimental effect by it, or the id names a fixed event-reward identity rather than a craftable recipe — use setPreEngineeredVariant for those. Finally, if the catalogue does not carry every base stat the recipe modifies: incomplete engineering is rejected rather than stored as a partial journal modifier block.

Example

import { getModuleBySymbol } from '@elite-dangerous-almanac/core/ships/modules';
import { CORE_MODULES } from '@elite-dangerous-almanac/core/ships/modules-core';
import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;

const fsd = getModuleBySymbol('Int_Hyperdrive_Size6_Class5', CORE_MODULES)!;

build.setModule('FrameShiftDrive', fsd)
     .applyBlueprint('FrameShiftDrive', 'FSD_LongRange', {
         grade: 5,
         experimental: 'special_fsd_heavy',
     });
build.maxJumpRange(); // uses the engineered optimal mass

armourMetrics()

armourMetrics(): ArmourMetrics

Defined in: src/ships/ship-loadout.ts:3180

The build's armour: hull hit points, the bulkhead and reinforcement each contribute, and the effective resistances.

Returns

ArmourMetrics

The ArmourMetrics, read off the fitted bulkhead.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;

const hull = build.armourMetrics();
hull.hitPoints;                  // -> total hull points
hull.resistances.explosive;      // -> lightweight alloy is explosively weak
hull.effectiveHitPoints.thermal; // -> thermal damage the hull can soak

availableBlueprints()

availableBlueprints(slotKey): readonly AvailableBlueprint[]

Defined in: src/ships/ship-loadout.ts:1253

Return the computable blueprint candidates for a fitted module symbol.

Parameters

slotKey

string

Slot key, matched case-insensitively.

Returns

readonly AvailableBlueprint[]

Frozen blueprint descriptors: the ordinary engineering menu first, then bespoke Mercenary upgrade recipes. An 'ordinary' candidate is available to the stock module; a 'mercenary' candidate is purchase-specific. Applying that bespoke blueprint identifies the matching Mercenary article even though its bare module symbol does not. Returns an empty array when the slot is empty, unresolved or final, or the module symbol has neither route.

Throws

If slotKey is not a string.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;

build.availableBlueprints('FrameShiftDrive').map(({ fdname }) => fdname);

availableExperimentalEffects()

availableExperimentalEffects(slotKey): readonly string[]

Defined in: src/ships/ship-loadout.ts:1277

Return the computable experimental effects offered to a fitted module.

Parameters

slotKey

string

Slot key, matched case-insensitively.

Returns

readonly string[]

Frozen Frontier effect ids in engineering-menu order, or an empty array when the slot is empty, unresolved, final, or has no experimental menu.

Throws

If slotKey is not a string.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;

build.availableExperimentalEffects('FrameShiftDrive');
// -> ['special_fsd_heavy', ...]

buildCost()

buildCost(): BuildCost

Defined in: src/ships/ship-loadout.ts:3110

Price the whole build from the catalogues: shop credits, Merc Coin and the engineering materials its modifications consume.

No modification is charged twice. A Mercenary article arrives at the grade it was sold at, so only the climb above that grade bills materials and further Merc Coin, and an experimental effect the article came with is free while one added on top is not. A fixed reward article — festive, Guardian, community-goal — identifies a recipe it was never rolled from, so it contributes no materials at all.

Returns

BuildCost

A frozen BuildCost. credits.modules, credits.total and credits.rebuy are lower bounds while BuildCredits.unpriced is non-empty; built-in hull fittings are free rather than unpriced.

Remarks

This is the one place ShipLoadout reads the material and Merc Coin cost catalogues, which is why the facade carries them; import getBlueprintCost and getExperimentalEffectCost directly to price one recipe without a build.

Examples

import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

const build = ShipLoadout.default('Anaconda');
build.buildCost().credits.hull; // -> 142456440
build.applyBlueprint('FrameShiftDrive', 'FSD_LongRange', { grade: 5 });
build.buildCost().materials.find((material) => material.symbol === 'Arsenic')?.count; // -> 5
import { getPreEngineeredVariants } from '@elite-dangerous-almanac/core/ships/pre-engineered';
import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

const variant = getPreEngineeredVariants('Hpt_Railgun_Fixed_Medium')
    .find((candidate) => candidate.acquisition === 'mercenary')!;
const build = ShipLoadout.default('Python')
    .setPreEngineeredVariant('MediumHardpoint1', variant);
build.buildCost().mercCoins; // -> 950

cellBanks()

cellBanks(): CellBankSummary

Defined in: src/ships/ship-loadout.ts:3060

Every fitted shield cell bank and the usable rearmed reinforcement pool.

Every fitted bank remains in banks, where powered says whether it is switched on and its priority group is fed with hardpoints deployed. The totals include only those powered banks, so a build whose plant is switched off or outdrawn reports every bank unpowered and zero totals.

Returns

CellBankSummary

A frozen CellBankSummary; no banks is an empty list and zero totals.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;
build.cellBanks().totalRestorable; // -> MJ across every powered fitted cell

clearEngineering()

clearEngineering(slotKey): this

Defined in: src/ships/ship-loadout.ts:2316

Strip engineering from a slot's module, restoring its base stats.

Parameters

slotKey

string

The slot to de-engineer, matched case-insensitively (journal spelling).

Returns

this

this, for chaining. A no-op if the slot is empty or unmodified.

Throws

If slotKey is not a string, or the fitted article is final pre-engineered and its baked engineering cannot be removed.

Remarks

Clearing a Mercenary article removes its purchase-exclusive blueprint identity. Its FittedModule.preEngineeredVariant then reads null.


completeEngineeringGrade()

completeEngineeringGrade(slotKey): EngineeringNormalizationResult

Defined in: src/ships/ship-loadout.ts:2059

Recompute the fitted module's current engineering identity at quality 1.

Imported modifier values remain authoritative until this method is called. An ordinary or Mercenary recipe is rerolled through the package calculator; a fixed reward rebuilds its hand-authored modifiers and optional effect without losing its purchase identity. A refusal never changes the loadout.

Parameters

slotKey

string

The engineered slot, matched case-insensitively.

Returns

EngineeringNormalizationResult

A frozen result identifying a normalized, unchanged or unsupported state.

Throws

If slotKey is not a string.

Example

import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

const partial = ShipLoadout.default('SideWinder').applyBlueprint(
    'FrameShiftDrive',
    'FSD_LongRange',
    { grade: 5, quality: 0.42 },
);
const imported = ShipLoadout.fromLoadout(partial.toLoadoutEvent());
imported.completeEngineeringGrade('FrameShiftDrive').kind; // -> 'normalized'
imported.fittedModuleAt('FrameShiftDrive')?.engineering?.Quality; // -> 1

distributorMetrics()

distributorMetrics(options?): DistributorMetrics | null

Defined in: src/ships/ship-loadout.ts:3305

All three power-distributor capacitors at selected pip allocations.

Parameters

options?

DistributorOptions = {}

SYS, ENG and WEP pips in [0, 4], each defaulting independently to 4. The allocations need not sum to six, which permits independent comparisons of the three maxima.

Returns

DistributorMetrics | null

Capacity, rated four-pip recharge and actual pip-scaled recharge for SYS, ENG and WEP, or null when the distributor is switched off, its six capacitor stats cannot be resolved, or the retracted power budget sheds it. That retracted state represents the distributor itself; firing endurance in weaponsCapacitorMetrics separately applies the deployed state.

Throws

If any pip allocation is outside [0, 4] or not finite.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;
const distributor = build.distributorMetrics({
  systemsPips: 2,
  enginesPips: 2,
  weaponsPips: 2,
});
distributor?.engines.rechargeRate; // MJ/s

fittedModuleAt()

fittedModuleAt(slotKey): FittedModule | null

Defined in: src/ships/ship-loadout.ts:1187

A deeply frozen, point-in-time view of the module in a slot.

Parameters

slotKey

string

Slot key, matched case-insensitively.

Returns

FittedModule | null

A detached, frozen view, or null when the slot is empty or unknown.

Throws

If slotKey is not a string.


fittedModules()

fittedModules(): readonly FittedModule[]

Defined in: src/ships/ship-loadout.ts:1223

Every fitted module as a deeply frozen point-in-time view.

Returns

readonly FittedModule[]

Detached module snapshots in the order the build carries them. The array and every nested record are frozen; query again after an edit for current state.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;

build.fittedModules().map((module) => `${module.slot}: ${module.symbol}`);

frameShiftDriveMassFactor()

frameShiftDriveMassFactor(options?): number

Defined in: src/ships/ship-loadout.ts:2549

The fitted frame shift drive's dimensionless mass factor at a chosen load.

Parameters

options?

JumpOptions = {}

JumpOptions. fuel defaults to a full main tank and cargo to 0.

Returns

number

optMass / loadedMass: 1 at the drive's optimised mass, below 1 above it and above 1 below it.

Remarks

This is the mass term used by the jump equation, not the three-point performance curve used by thrusters and shield generators. Main-tank fuel contributes to the loaded mass; the Guardian FSD Booster's additive range does not contribute to the factor.

Throws

If the build has no usable frame shift drive.

Throws

If fuel or cargo is not finite and non-negative, or loaded mass is zero.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;
build.frameShiftDriveMassFactor({ fuel: 8, cargo: 32 }); // dimensionless

fuelPerJump()

fuelPerJump(distance, options?): number

Defined in: src/ships/ship-loadout.ts:2609

The fuel a single jump of a given distance costs, in tonnes.

Parameters

distance

number

The jump distance, in light-years.

options?

JumpOptions = {}

JumpOptions. fuel defaults to a full main tank, cargo to 0.

Returns

number

Fuel used, in tonnes (capped at the drive's max fuel per jump).

Throws

If the build has no usable frame shift drive.

Throws

If fuel or cargo is not finite and non-negative.


heatMetrics()

heatMetrics(): HeatMetrics | null

Defined in: src/ships/ship-loadout.ts:2841

The build's heat: what it idles at, what it runs at flying and jumping, and whether firing everything cooks it.

Every figure is post-engineering. The heat a build makes follows what the plant actually feeds, so a module switched off — or one in a priority group the plant cannot keep lit — contributes nothing.

Returns

HeatMetrics | null

The HeatMetrics, or null when the build has no powered power plant.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;

const heat = build.heatMetrics();
heat?.idle.gauge;                      // -> 0.23, i.e. the gauge reads 23%
heat?.firingSustained.overheats;       // -> false: the guns run cool enough to hold
heat?.firingDrained.secondsToOverheat; // -> how long an alpha strike has on an empty WEP

jumpRange()

jumpRange(options?): number

Defined in: src/ships/ship-loadout.ts:2582

The range of a single jump for a chosen fuel and cargo load, in light-years.

Parameters

options?

JumpOptions = {}

JumpOptions. fuel defaults to a full main tank, cargo to 0.

Returns

number

The jump's range, in light-years.

Throws

If the build has no usable frame shift drive.

Throws

If fuel or cargo is not finite and non-negative.


jumpRangeSummary()

jumpRangeSummary(): JumpRangeSummary

Defined in: src/ships/ship-loadout.ts:2768

Every jump figure at once — best, unladen, laden, and each load's total.

Returns

JumpRangeSummary

The JumpRangeSummary. Single-jump figures and each total's range are in light-years. For a partial load, call jumpRange for one jump or totalRange for every jump with the fuel and cargo you actually have.

Throws

If the build has no usable frame shift drive.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;

const jumps = build.jumpRangeSummary();
jumps.max;    // -> 89.41  (one jump's fuel, empty hold)
jumps.laden;  // -> the range with the hold full
jumps.totalMax.jumps; // the best jump expressed as a total
// Half a tank and 32 t aboard:
build.jumpRange({ fuel: build.fuelCapacity.main / 2, cargo: 32 });

ladenJumpRange()

ladenJumpRange(): number

Defined in: src/ships/ship-loadout.ts:2595

Single-jump range on a full tank with a full cargo hold, in light-years.

Returns

number

The jump's range, in light-years.

Throws

If the build has no usable frame shift drive.


maxJumpRange()

maxJumpRange(): number

Defined in: src/ships/ship-loadout.ts:2568

Best single-jump range, in light-years — no cargo, and exactly one jump's fuel aboard (the lightest the ship jumps). This is the figure the game and EDSY label "maximum jump range".

Returns

number

The best single jump, in light-years, or 0 for a capture that states a main tank of 0.

Throws

If the build has no usable frame shift drive.


mobilityMetrics()

mobilityMetrics(options?): MobilityMetrics | null

Defined in: src/ships/ship-loadout.ts:2874

The build's speed, boost and rotation rates at a chosen load and ENG allocation.

Parameters

options?

MobilityOptions = {}

Fuel defaults to a full main tank, cargo to 0, and ENG pips to 4.

Returns

MobilityMetrics | null

Loaded MobilityMetrics, or null when no fully described thrusters are powered with hardpoints retracted. Use mobilityMetricsResult to distinguish the unavailable conditions.

Remarks

Main-tank fuel contributes to the flight model's loaded mass. Reserve-tank fuel does not: although the statistics panel includes it in the displayed current mass, ten observed builds reproduce their angular rates only when the reserve is excluded from the thruster mass curve.

Throws

If fuel or cargo is not finite and non-negative, or enginesPips is outside [0, 4].

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;
build.mobilityMetrics({ cargo: 32, fuel: 8, enginesPips: 2 })?.speed; // -> m/s

mobilityMetricsResult()

mobilityMetricsResult(options?): CalculationResult<MobilityMetrics>

Defined in: src/ships/ship-loadout.ts:2899

The build's mobility with a diagnostic when its thrusters or retracted power supply is unavailable.

Parameters

options?

MobilityOptions = {}

Fuel defaults to a full main tank, cargo to 0, and ENG pips to 4.

Returns

CalculationResult<MobilityMetrics>

A complete MobilityMetrics value, otherwise null plus the input or fitted-module state that prevented the calculation.

Throws

If fuel or cargo is not finite and non-negative, or enginesPips is outside [0, 4].

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;
const result = build.mobilityMetricsResult({ enginesPips: 2 });
if (result.complete) result.value.speed; // metres per second
else result.issues[0].reason;            // unavailable-state discriminator

modulesForSlot()

modulesForSlot(slotKey): OutfittingModule[]

Defined in: src/ships/ship-loadout.ts:1300

The modules that fit a given slot — its size, kind and any restriction all satisfied, with candidates that would worsen a one-per-ship or module-count limit omitted.

Parameters

slotKey

string

The slot key to fit, matched case-insensitively (journal spelling).

Returns

OutfittingModule[]

The fitting modules, in complete-catalogue order.

Throws

If the hull has no slot with that key.

Throws

If slotKey is not a string.

Example

import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

ShipLoadout.empty('Anaconda').modulesForSlot('FrameShiftDrive');

powerBudget()

powerBudget(): PowerBudget

Defined in: src/ships/ship-loadout.ts:2806

The build's power budget: what the plant makes, what the modules draw with hardpoints retracted and deployed, and which priority groups stay lit.

Draws are post-engineering, modules switched off in the journal are skipped, and weapons (plus the utility fittings that are not always powered) count only towards the deployed total.

Returns

PowerBudget

The PowerBudget. consumers includes modules with positive draw; passive and zero-draw fittings are absent.

Throws

If a power capacity or module draw is negative or not finite.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;

const power = build.powerBudget();
power.available;                  // -> 20.4 MW generated
power.deployed;                   // -> 19.02 MW drawn, hardpoints out
power.withinBudget;               // -> true
power.bands[4]?.poweredDeployed;  // -> is priority group 5 still lit?

removeModule()

removeModule(slotKey): this

Defined in: src/ships/ship-loadout.ts:1563

Empty a slot.

Parameters

slotKey

string

The slot key to clear, matched case-insensitively (journal spelling).

Returns

this

this, for chaining. Clearing an already-empty removable slot is a no-op.

Throws

If slotKey is not a string.

Throws

If the slot is the built-in cargo hatch; is a required core or armour mount; or removing the module would worsen a per-ship module-count excess. Required mounts may be replaced with setModule but cannot be emptied.


repairFixedMount()

repairFixedMount(slotKey): FixedMountRepairResult

Defined in: src/ships/ship-loadout.ts:1341

Refit a fixed mount from this hull's stock loadout.

Parameters

slotKey

string

Fixed slot key, matched case-insensitively.

Returns

FixedMountRepairResult

A frozen FixedMountRepairResult. Refusals leave the build unchanged.

Remarks

This is the repair path for the mounts setModule does not expose as ordinary edits — in practice the built-in cargo hatch. The stock article keeps the mount's On, Priority and Health and none of the replaced module's engineering or captured value, as import normalization does. Every entry point already fills these mounts, so a build this package produced answers unchanged.

Throws

If slotKey is not a string.

Throws

If a known hull has no slot with that key.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const imported: ShipLoadout;
imported.repairFixedMount('CargoHatch').status; // -> 'unchanged'

setExperimentalEffect()

setExperimentalEffect(slotKey, experimental): ExperimentalEffectMutationResult

Defined in: src/ships/ship-loadout.ts:1860

Add, replace or remove only the fitted module's experimental effect.

Ordinary and Mercenary engineering is recomputed at its current blueprint, grade and quality. A fixed reward instead retains its hand-authored modifiers and purchase identity while the requested effect is composed with them. Refused edits leave the build unchanged and return stable structured data.

Parameters

slotKey

string

The engineered slot, matched case-insensitively.

experimental

string | null

Experimental-effect fdname, or null to remove the effect.

Returns

ExperimentalEffectMutationResult

A frozen result identifying an update, no-op or lossless refusal.

Throws

If slotKey or a non-null experimental is not a string.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;

build.setExperimentalEffect('FrameShiftDrive', 'special_fsd_heavy');
build.setExperimentalEffect('FrameShiftDrive', null);

setModule()

setModule(slotKey, module): this

Defined in: src/ships/ship-loadout.ts:1445

Fit a module into a slot, replacing whatever is there.

Parameters

slotKey

string

The slot key to fit into, matched case-insensitively (journal spelling). An occupied slot keeps the key the build already spells it with, so fitting into an import never renames one of its mounts.

module

OutfittingModule

The module to fit (resolve it from a catalogue first, e.g. with getModuleBySymbol). The complete record is snapshotted, so a result from getPreEngineeredStats or a record you adjusted yourself keeps its stats — but it must name an article the built-in catalogue carries, and it may not drop that article's mass, cargoCapacity or fuelCapacity, which every build sums, nor state one as anything but a finite number.

Returns

this

this, for chaining.

Remarks

This is an incremental editor: every call must avoid worsening the current build's module-count excess, so fit an allowance-increasing module before the weapons it permits. Use ShipLoadout.fromLoadout to consume a complete snapshot instead, where order does not matter.

Fitting is a fresh mount: the slot's On, Priority and Health are reset. Set them again if your screen keeps a priority group across a swap.

Throws

If the hull has no slot with that key.

Throws

If slotKey is not a string, or module fails any of the conditions above — null/undefined (e.g. a getModuleBySymbol miss), not an outfitting module, an uncatalogued symbol, or a missing or non-finite summed stat.

Throws

If the module does not fit the slot (wrong kind, too large, or a restriction the module does not satisfy), conflicts with a one-per-ship family already fitted elsewhere, or worsens a per-ship module-count excess.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;

import { getModuleBySymbol } from '@elite-dangerous-almanac/core/ships/modules';
import { CORE_MODULES } from '@elite-dangerous-almanac/core/ships/modules-core';
const fsd = getModuleBySymbol('Int_Hyperdrive_Size6_Class5', CORE_MODULES)!;
const tank = getModuleBySymbol('Int_FuelTank_Size6_Class3', CORE_MODULES)!;
build.setModule('FrameShiftDrive', fsd).setModule('Slot01_Size7', tank);

setModuleEnabled()

setModuleEnabled(slotKey, on): this

Defined in: src/ships/ship-loadout.ts:2355

Switch a fitted module on or off.

Parameters

slotKey

string

The slot's journal key, e.g. "PowerPlant", matched case-insensitively.

on

boolean

true to power it, false to switch it off.

Returns

this

this, for chaining.

Throws

If the slot is empty.

Throws

If slotKey is not a string.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;

build.setModuleEnabled('TinyHardpoint6', false); // an unpowered heat sink

setModulePriority()

setModulePriority(slotKey, priority): this

Defined in: src/ships/ship-loadout.ts:2371

Set a fitted module's power-priority group.

Parameters

slotKey

string

The slot's journal key, matched case-insensitively.

priority

number

The journal's zero-based group, 04. Note that the outfitting panel — and powerBudget's bands[].priority — number the same five groups 15.

Returns

this

this, for chaining.

Throws

If the slot is empty, or priority is not an integer in [0, 4].

Throws

If slotKey is not a string.


setPreEngineeredVariant()

setPreEngineeredVariant(slotKey, variant): this

Defined in: src/ships/ship-loadout.ts:2247

Fit a pre-engineered variant into a slot, replacing whatever is there.

The variant's fixed stats and journal engineering block are resolved together. Articles carry Level, Quality: 1, any baked experimental effect and their fixed modifiers. Because the variant names its base module, a decorative identity cannot be applied to an unrelated damage-bearing module. A Mercenary variant whose fixed modifier block has not been published retains the stock catalogue stats and omits Modifiers rather than claiming it changes none.

Parameters

slotKey

string

The slot key to fit into, matched case-insensitively.

variant

PreEngineeredVariant

The pre-engineered catalogue variant to fit.

Returns

this

this, for chaining.

Throws

If variant is not a pre-engineered variant or one of its authored modifier labels cannot be resolved for its base module.

Throws

If no catalogue row matches the supplied module, blueprint, grade, experimental effect and acquisition route.

Throws

If the variant's base module does not fit the slot or violates a fitted-module limit.

Example

import { getPreEngineeredVariants } from '@elite-dangerous-almanac/core/ships/pre-engineered';
import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

const festive = getPreEngineeredVariants('Hpt_FlakMortar_Turret_Medium')
    .find((variant) => variant.blueprint === 'Decorative_Red')!;
const build = ShipLoadout.empty('Krait_MkII')
    .setPreEngineeredVariant('MediumHardpoint1', festive);
build.fittedModuleAt('MediumHardpoint1')?.effectiveStats?.damage; // -> 0.34

shieldMetrics()

shieldMetrics(options?): ShieldMetrics | null

Defined in: src/ships/ship-loadout.ts:2948

The build's shields: strength in megajoules, where it comes from, and the effective resistances.

Shield strength scales with the hull's mass, not the build's, so fitting more modules never weakens it. Boosters, Guardian shield reinforcement and any engineering are all folded in; switched-off or shed boosters and reinforcement are ignored, while a switched-off or shed generator makes the metric unavailable.

Parameters

options?

DefenceOptions = {}

DefenceOptions. systemsPips (0–4) folds the SYS capacitor's own resistance into the reported figures; it defaults to 0, which is what an outfitting screen shows.

Returns

ShieldMetrics | null

The ShieldMetrics, or null when the build has no shield generator powered with hardpoints retracted. Use shieldMetricsResult to distinguish the unavailable conditions.

Throws

If systemsPips is outside [0, 4] or not finite.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;

const shields = build.shieldMetrics();
shields?.strength;              // -> MJ
shields?.resistances.thermal;   // -> negative on a stock generator
build.shieldMetrics({ systemsPips: 4 })?.resistances.thermal; // -> with 4 pips to SYS

shieldMetricsResult()

shieldMetricsResult(options?): CalculationResult<ShieldMetrics>

Defined in: src/ships/ship-loadout.ts:2971

The build's shields with a diagnostic when its hull, generator or retracted power supply is unavailable.

Parameters

options?

DefenceOptions = {}

DefenceOptions. systemsPips defaults to 0.

Returns

CalculationResult<ShieldMetrics>

A complete ShieldMetrics value, otherwise null plus the input or fitted-module state that prevented the calculation.

Throws

If systemsPips is outside [0, 4] or not finite.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;
const result = build.shieldMetricsResult();
if (result.complete) result.value.strength; // megajoules
else result.issues[0].reason;               // unavailable-state discriminator

shieldRecovery()

shieldRecovery(options?): ShieldRecovery | null

Defined in: src/ships/ship-loadout.ts:3004

Time for this build's shield to rise after collapse and then regenerate to full.

Parameters

options?

DefenceOptions = {}

SYS pips in [0, 4], defaulting to 4.

Returns

ShieldRecovery | null

Recovery rates and seconds, or null when no shield generator is powered with hardpoints retracted. Use shieldRecoveryResult to distinguish the unavailable conditions. Insufficient zero-pip recharge produces Infinity.

Throws

If systemsPips is outside [0, 4] or not finite.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;
build.shieldRecovery({ systemsPips: 4 })?.recoveryTime; // -> seconds from collapse to 50%

shieldRecoveryResult()

shieldRecoveryResult(options?): CalculationResult<ShieldRecovery>

Defined in: src/ships/ship-loadout.ts:3027

The build's shield recovery with a diagnostic when its hull, generator or retracted power supply is unavailable.

Parameters

options?

DefenceOptions = {}

SYS pips in [0, 4], defaulting to 4.

Returns

CalculationResult<ShieldRecovery>

A complete ShieldRecovery value, otherwise null plus the input or fitted-module state that prevented the calculation.

Throws

If systemsPips is outside [0, 4] or not finite.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;
const result = build.shieldRecoveryResult();
if (result.complete) result.value.recoveryTime; // seconds
else result.issues[0].reason; // unavailable-state discriminator

slots()

slots(kind?): readonly LoadoutSlot[]

Defined in: src/ships/ship-loadout.ts:1147

Frozen point-in-time views of the hull's mounts in outfitting-panel order.

Parameters

kind?

SlotKind

Optionally keep only one mount kind. Omit it for every mount.

Returns

readonly LoadoutSlot[]

Detached, frozen slot views. Repeated reads for the same kind reuse the same snapshots until a state-changing edit.

Example

import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

const emptyHardpoints = ShipLoadout.empty('Sidewinder').slots('hardpoint');
emptyHardpoints.every((slot) => slot.module === null); // true

standardLoadResult()

standardLoadResult(load): CalculationResult<StandardLoadInputs>

Defined in: src/ships/ship-loadout.ts:2661

Resolve one of the package's standard load conditions for jump and mobility views.

Parameters

load

StandardLoad

'maximum' for one jump's fuel and no cargo, 'unladen' for a full main tank and no cargo, or 'laden' for a full main tank and full hold.

Returns

CalculationResult<StandardLoadInputs>

Fuel and cargo in tonnes. Only 'maximum' can come back incomplete: it validates the whole fitted drive, jump booster included, so a complete one can be passed straight to jumpRange.

Throws

If load is not a recognised standard load.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;
const load = build.standardLoadResult('maximum');
if (load.complete) build.mobilityMetrics({ ...load.value, enginesPips: 2 });

toLoadoutEvent()

toLoadoutEvent(options?): LoadoutEvent

Defined in: src/ships/ship-loadout.ts:2429

This build as a journal Loadout event — the data half of a SLEF entry.

Parameters

options?

LoadoutExportOptions = {}

Module ordering and how sparse to be about power state.

Returns

LoadoutEvent

A fresh event. Every top-level figure is recomputed from the hull and the fitted modules rather than echoed from whatever an import supplied — the one exception being the credits, when credits: 'source' asks for the capture's own. Any figure that cannot be worked out is left out rather than emitted as a stale or zero value — SLEF requires nothing beyond Ship and Modules.

Credits are quoted at retail by default: the bare hull's hullCost plus every fitted module's catalogue list price, with Rebuy 5% of the two. A source's own HullValue / ModulesValue / Value figures are deliberately not quoted here, because they record one commander's purchase history — the Deep Black's modules are all 12.25% off list — and purchase discounts are not a property of the build. They are not lost either: pass credits: 'source' to export the sourcePurchase record instead, as provenance rather than as a price.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;

const event = build.toLoadoutEvent();
event.MaxJumpRange; // recomputed, not the exporter's claim
event.HullValue;    // the catalogue's list price

build.toLoadoutEvent({ credits: 'source' }).HullValue; // what the capture paid

toSlef()

toSlef(options): Slef

Defined in: src/ships/ship-loadout.ts:2470

This build as a one-entry SLEF export.

Parameters

options

SlefExportOptions

Ordering, power state, and the envelope header.

Returns

Slef

The export. Several builds travel together as toSlef([a.toLoadoutEvent(), b.toLoadoutEvent()]) using the function of the same name from ./slef.


toSlefString()

toSlefString(options): string

Defined in: src/ships/ship-loadout.ts:2487

This build as SLEF JSON — ready to write to a file or put on the clipboard.

Parameters

options

SlefExportOptions

As toSlef, plus indent (compact by default).

Returns

string

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;

build.toSlefString({ header: { appName: 'MyApp', appVersion: '1.0.0' } });

totalRange()

totalRange(options?): TotalRangeDetails

Defined in: src/ships/ship-loadout.ts:2634

Total range and jump count for a chosen fuel and cargo load.

Parameters

options?

JumpOptions = {}

JumpOptions. fuel defaults to a full main tank, cargo to 0.

Returns

TotalRangeDetails

Summed range in light-years and the jumps made before the tank is empty.

Throws

If the build has no usable frame shift drive.

Throws

If fuel or cargo is not finite and non-negative, or the fuel load would require more than 100,000 jumps.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;
build.totalRange().jumps; // jumps available from one full main tank
build.totalRange({ fuel: 8, cargo: 32 }).range; // range for that partial load

weaponMetrics()

weaponMetrics(): BuildWeaponMetrics

Defined in: src/ships/ship-loadout.ts:3213

The build's firepower: DPS, sustained DPS, weapons-capacitor draw, heat and power draw for every fitted weapon, plus the totals.

Every figure is post-engineering. A weapon switched off in the journal is still listed — with its own metrics — but left out of the totals.

Returns

BuildWeaponMetrics

The BuildWeaponMetrics.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;

const guns = build.weaponMetrics();
guns.total.damagePerSecond;          // -> burst DPS across the hardpoints
guns.total.sustainedDamagePerSecond; // -> with reloads folded in
guns.total.energyPerSecond;          // -> MW asked of the WEP capacitor
guns.total.powerDraw;                // -> MW asked of the power plant when deployed
guns.weapons[0]?.metrics.damageByType.thermal;
guns.weapons[0]?.maximumRange;        // post-engineering metres, when known
guns.weapons[0]?.armourPiercing;      // post-engineering rating, when known
guns.weapons[0]?.ammunition?.total;  // -> rounds aboard when fully rearmed

weaponsCapacitorMetrics()

weaponsCapacitorMetrics(options?): WeaponsCapacitorMetrics

Defined in: src/ships/ship-loadout.ts:3263

WEP-capacitor recharge and endurance while every powered weapon fires.

Parameters

options?

WeaponsOptions = {}

WEP pips in [0, 4], defaulting to 4.

Returns

WeaponsCapacitorMetrics

Actual recharge, sustained draw, net drain and seconds from full to empty. The deployed power budget is applied to the distributor and weapons, so a module the plant sheds contributes nothing. With no powered distributor, capacity and recharge are zero. A load that draws no more than recharge reports Infinity for timeToDrain.

Throws

If weaponsPips is outside [0, 4] or not finite.

Example

import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

declare const build: ShipLoadout;
build.weaponsCapacitorMetrics({ weaponsPips: 2 }).timeToDrain; // seconds

default()

static default(shipSymbol): ShipLoadout

Defined in: src/ships/ship-loadout.ts:913

Start a new build with the modules supplied on a stock ship.

Parameters

shipSymbol

string

The hull's internal symbol, e.g. "SideWinder" (case-insensitive).

Returns

ShipLoadout

A complete, ready-to-edit stock loadout. The build is independent of the frozen shared catalogue: edits affect this instance only.

Throws

If shipSymbol is not a string, or no default loadout exists for that hull.

Remarks

This batteries-included factory resolves calculations through the complete module catalogue already used by ShipLoadout. If only the stock slot/module identities are needed, getDefaultLoadout from ./default-loadouts avoids that cost.

Example

import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

const stock = ShipLoadout.default('SideWinder');
stock.validation.complete; // -> true
stock.fittedModuleAt('FrameShiftDrive')?.symbol;
// -> 'Int_Hyperdrive_Size2_Class1'

empty()

static empty(shipSymbol): ShipLoadout

Defined in: src/ships/ship-loadout.ts:863

Start a new build for a hull with only its stock core modules fitted.

Parameters

shipSymbol

string

The hull's internal symbol, e.g. "Anaconda" (case-insensitive).

Returns

ShipLoadout

A loadout on the hull's stock bulkhead, core internals and cargo hatch, with every hardpoint, utility mount and optional internal left open. Use default for a build that also carries the hull's stock weapons and optional internals.

Throws

If shipSymbol is not a string, or no hull with that symbol has a known slot layout.

Example

import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';

ShipLoadout.empty('Sidewinder').slots('hardpoint').length; // -> 2

fromLoadout()

static fromLoadout(event): ShipLoadout

Defined in: src/ships/ship-loadout.ts:812

Build from a bare journal Loadout event (the data half of a SLEF entry).

Parameters

event

LoadoutEvent

A Loadout event object.

Returns

ShipLoadout

The loadout.

Remarks

Capture and instance state (timestamp, ShipID, HullHealth, Hot) and engineering provenance (Engineer, EngineerID, BlueprintID) stay out of the durable build. A pre-engineered article — a reward, a Mercenary purchase, a Guardian weapon — is identified where the capture's evidence names one uniquely, and the catalogue's stat block then supplies the values the capture omits; the capture's own modifiers stay authoritative over it.

Modules are imported as one complete snapshot, so their order does not affect per-ship count allowances. An entry stands as the event stated it when the mount can hold the article the catalogue resolves, when its slot is a known cosmetic or hull-geometry key (PaintJob, ShipCockpit, a numbered decal, …), or when it is the built-in cargo hatch. Everything else is normalized, and every change is recorded by importOutcomes: an unresolved module in a removable mount is discarded, while armour, the seven core internals and the cargo hatch are filled from the hull defaults whenever the event left no article that mount can hold — an unresolved symbol, a resolved one the mount refuses, and no entry at all are corrected alike, each keeping the source's On, Priority and Health. A removable mount may stand empty, so an article it refuses stays where the event put it and is reported by validation instead.

Normalization makes the captured aggregates untrustworthy, so they are dropped: unladenMass, cargoCapacity and fuelCapacity are recomputed from the fit that remains, modulesValue and rebuy read null, and sourcePurchase still reports what the capture stated. A mount stocked from absence is the exception where its stock article is free and weightless — the bulkhead and the cargo hatch both are — and every figure stands.

Use this factory rather than replaying a complete loadout through the incremental setModule editor.

Throws

If the event is not shaped like one. What is checked is the structure a build is assembled from and the fields that name things in it: event must be an object with an array of module objects in Modules, each carrying a string Slot and Item, no two claiming the same slot; event.Ship must name a known hull; an Engineering block must be an object and its Modifiers an array of objects each carrying a string Label, whenever the key is there at all; that block's BlueprintName and ExperimentalEffect must be strings when they carry a value. A modifier's Label is required rather than checked-when-present because it is the only thing saying which stat moved. Every remaining field — every number, every flag, a modifier's value beside its label — is trusted, so use ShipLoadout.fromSlef (or parseSlef) for input you did not produce, which reports all of them.


fromSlef()

static fromSlef(input, index?): ShipLoadout

Defined in: src/ships/ship-loadout.ts:745

Build from a SLEF export.

Parameters

input

unknown

The SLEF JSON string, or an already-parsed SLEF object (see parseSlef for accepted shapes).

index?

number = 0

Which entry to take when the export holds several builds. Defaults to the first.

Returns

ShipLoadout

The loadout for that entry.

Remarks

Module normalization follows ShipLoadout.fromLoadout; inspect importOutcomes for modules that were emptied or defaulted.

Throws

If input is a string that is not valid JSON.

Throws

If the export holds no usable loadout, index is out of range, or the selected entry names a hull absent from the catalogue.

API

Guides
astro
Classes (1)
ProceduralSystem

Properties

Accessors

Methods

Interfaces (28)
Type Aliases (6)
Variables (22)
Functions (57)
Subpath modules (3)
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)
materials
Enumerations (2)
Interfaces (2)
Type Aliases (2)
Variables (9)
Functions (9)
ships
Classes (3)
BuildMetrics

Methods

LoadoutEditError

Constructors

Properties

Methods

ShipLoadout

Accessors

Methods

Interfaces (125)
Type Aliases (44)
Variables (10)
Functions (85)
Subpath modules (8)

Clone this wiki locally