@shipload/sdk 1.0.0-next.5 → 1.0.0-next.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/scan.d.ts +52 -0
- package/lib/scan.js +162 -0
- package/lib/scan.js.map +1 -0
- package/lib/scan.m.js +152 -0
- package/lib/scan.m.js.map +1 -0
- package/lib/shipload.d.ts +2881 -1149
- package/lib/shipload.js +13031 -5511
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +12807 -5460
- package/lib/shipload.m.js.map +1 -1
- package/lib/testing.d.ts +1028 -0
- package/lib/testing.js +4336 -0
- package/lib/testing.js.map +1 -0
- package/lib/testing.m.js +4330 -0
- package/lib/testing.m.js.map +1 -0
- package/package.json +20 -2
- package/src/capabilities/craftable.ts +51 -0
- package/src/capabilities/crafting.test.ts +7 -0
- package/src/capabilities/crafting.ts +6 -7
- package/src/capabilities/gathering.test.ts +29 -0
- package/src/capabilities/gathering.ts +37 -19
- package/src/capabilities/index.ts +0 -1
- package/src/capabilities/modules.ts +22 -0
- package/src/capabilities/storage.ts +16 -1
- package/src/contracts/platform.ts +231 -3
- package/src/contracts/server.ts +1160 -520
- package/src/coordinates/address.ts +88 -0
- package/src/coordinates/constants.test.ts +15 -0
- package/src/coordinates/constants.ts +23 -0
- package/src/coordinates/index.ts +15 -0
- package/src/coordinates/memo.test.ts +47 -0
- package/src/coordinates/memo.ts +20 -0
- package/src/coordinates/permutation.ts +77 -0
- package/src/coordinates/regions.ts +48 -0
- package/src/coordinates/sectors.ts +115 -0
- package/src/data/capabilities.ts +31 -16
- package/src/data/capability-formulas.ts +21 -14
- package/src/data/catalog.ts +0 -5
- package/src/data/colors.ts +13 -47
- package/src/data/entities.json +154 -10
- package/src/data/item-ids.ts +31 -12
- package/src/data/items.json +192 -74
- package/src/data/kind-registry.json +131 -0
- package/src/data/kind-registry.ts +172 -0
- package/src/data/metadata.ts +166 -48
- package/src/data/recipes-runtime.ts +5 -23
- package/src/data/recipes.json +1003 -163
- package/src/derivation/build-methods.test.ts +13 -0
- package/src/derivation/build-methods.ts +48 -0
- package/src/derivation/capabilities.test.ts +151 -0
- package/src/derivation/capabilities.ts +527 -0
- package/src/derivation/capability-mappings.ts +58 -16
- package/src/derivation/crafting.ts +36 -33
- package/src/derivation/index.ts +28 -2
- package/src/derivation/recipe-usage.test.ts +81 -0
- package/src/derivation/recipe-usage.ts +141 -0
- package/src/derivation/reserve-regen.ts +34 -0
- package/src/derivation/resources.ts +98 -19
- package/src/derivation/rollups.test.ts +55 -0
- package/src/derivation/rollups.ts +56 -0
- package/src/derivation/stars.test.ts +67 -0
- package/src/derivation/stars.ts +25 -0
- package/src/derivation/stats.ts +6 -6
- package/src/derivation/stratum.ts +26 -22
- package/src/derivation/tiers.ts +40 -7
- package/src/derivation/upgrades.ts +14 -0
- package/src/derivation/wormhole.ts +141 -0
- package/src/entities/entity.ts +98 -0
- package/src/entities/gamestate.ts +3 -28
- package/src/entities/makers.ts +131 -134
- package/src/entities/slot-multiplier.ts +43 -0
- package/src/errors.ts +12 -16
- package/src/format.ts +26 -4
- package/src/index-module.ts +290 -34
- package/src/managers/actions.ts +602 -105
- package/src/managers/base.ts +6 -2
- package/src/managers/cluster.test.ts +39 -0
- package/src/managers/cluster.ts +53 -0
- package/src/managers/construction-types.ts +80 -0
- package/src/managers/construction.ts +412 -0
- package/src/managers/context.ts +29 -1
- package/src/managers/coordinates.ts +14 -0
- package/src/managers/entities.ts +27 -66
- package/src/managers/epochs.ts +40 -0
- package/src/managers/flatten-gather-plan.test.ts +80 -0
- package/src/managers/index.ts +20 -1
- package/src/managers/locations.ts +25 -29
- package/src/managers/nft.test.ts +14 -0
- package/src/managers/nft.ts +70 -0
- package/src/managers/players.ts +25 -0
- package/src/managers/plot.ts +122 -0
- package/src/nft/atomicassets.abi.json +1342 -0
- package/src/nft/atomicassets.ts +237 -0
- package/src/nft/atomicdata.ts +130 -0
- package/src/nft/buildImmutableData.ts +336 -0
- package/src/nft/description.ts +104 -36
- package/src/nft/index.ts +3 -0
- package/src/planner/index.ts +242 -0
- package/src/planner/planner.test.ts +325 -0
- package/src/resolution/describe-module.ts +43 -25
- package/src/resolution/display-name.ts +38 -10
- package/src/resolution/resolve-item.test.ts +48 -0
- package/src/resolution/resolve-item.ts +74 -47
- package/src/scan/index.ts +244 -0
- package/src/scan/scan-wasm.base64.ts +2 -0
- package/src/scheduling/accessor.ts +65 -23
- package/src/scheduling/availability.ts +108 -0
- package/src/scheduling/cancel.test.ts +348 -0
- package/src/scheduling/cancel.ts +209 -0
- package/src/scheduling/energy.ts +47 -0
- package/src/scheduling/idle-resolve.ts +45 -0
- package/src/scheduling/lane-core.ts +128 -0
- package/src/scheduling/lanes.test.ts +249 -0
- package/src/scheduling/lanes.ts +198 -0
- package/src/scheduling/projection.ts +210 -107
- package/src/scheduling/schedule.ts +240 -117
- package/src/scheduling/task-cargo.ts +46 -0
- package/src/scheduling/unwrap.test.ts +86 -0
- package/src/scheduling/unwrap.ts +190 -0
- package/src/shipload.ts +26 -1
- package/src/subscriptions/manager.cluster.test.ts +51 -0
- package/src/subscriptions/manager.ts +245 -172
- package/src/subscriptions/mappers.ts +5 -8
- package/src/subscriptions/types.ts +28 -3
- package/src/testing/catalog-hash.ts +19 -0
- package/src/testing/index.ts +2 -0
- package/src/testing/projection-parity.ts +167 -0
- package/src/travel/reach.ts +23 -0
- package/src/travel/route-planner.ts +277 -0
- package/src/travel/travel.ts +148 -117
- package/src/types/capabilities.ts +29 -6
- package/src/types/entity.ts +3 -3
- package/src/types/index.ts +0 -1
- package/src/types.ts +36 -14
- package/src/utils/cargo.test.ts +14 -0
- package/src/utils/cargo.ts +29 -0
- package/src/utils/display-name.ts +70 -0
- package/src/utils/system.ts +36 -24
- package/src/capabilities/loading.ts +0 -8
- package/src/entities/container.ts +0 -123
- package/src/entities/ship-deploy.ts +0 -295
- package/src/entities/ship.ts +0 -221
- package/src/entities/warehouse.ts +0 -127
- package/src/types/entity-traits.ts +0 -69
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
export function computeBaseHullmass(stats: Record<string, number>): number {
|
|
2
|
+
return 100000 - 75 * stats.density
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function computeShipHullCapabilities(stats: Record<string, number>): {
|
|
6
|
+
hullmass: number
|
|
7
|
+
capacity: number
|
|
8
|
+
} {
|
|
9
|
+
const statSum = (stats.strength ?? 0) + (stats.hardness ?? 0)
|
|
10
|
+
const exponent = statSum / 1998.0
|
|
11
|
+
return {
|
|
12
|
+
hullmass: computeBaseHullmass(stats),
|
|
13
|
+
capacity: Math.floor(5000000 * 6 ** exponent),
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function computeEngineCapabilities(stats: Record<string, number>): {
|
|
18
|
+
thrust: number
|
|
19
|
+
drain: number
|
|
20
|
+
} {
|
|
21
|
+
const vol = stats.volatility
|
|
22
|
+
const thm = stats.thermal
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
thrust: 400 + Math.floor((vol * 3) / 4),
|
|
26
|
+
drain: 2 * Math.max(30, 50 - Math.floor(thm / 70)),
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function computeGeneratorCapabilities(stats: Record<string, number>): {
|
|
31
|
+
capacity: number
|
|
32
|
+
recharge: number
|
|
33
|
+
} {
|
|
34
|
+
const res = stats.resonance
|
|
35
|
+
const ref = stats.reflectivity
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
capacity: 1300 + Math.floor(res / 2),
|
|
39
|
+
recharge: 2 * (1 + Math.floor((ref * 3) / 1000)),
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface GathererDepthParams {
|
|
44
|
+
readonly floor: number
|
|
45
|
+
readonly slope: number
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const GATHERER_DEPTH_TABLE: readonly GathererDepthParams[] = [
|
|
49
|
+
{floor: 500, slope: 5},
|
|
50
|
+
{floor: 2000, slope: 11},
|
|
51
|
+
{floor: 7000, slope: 16},
|
|
52
|
+
{floor: 15000, slope: 18},
|
|
53
|
+
{floor: 25000, slope: 19},
|
|
54
|
+
{floor: 35000, slope: 16},
|
|
55
|
+
{floor: 46000, slope: 12},
|
|
56
|
+
{floor: 53500, slope: 10},
|
|
57
|
+
{floor: 60000, slope: 5},
|
|
58
|
+
{floor: 63537, slope: 2},
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
export const GATHERER_DEPTH_MAX_TIER = 10
|
|
62
|
+
|
|
63
|
+
export function gathererDepthForTier(tol: number, tier: number): number {
|
|
64
|
+
if (tier < 1 || tier > GATHERER_DEPTH_MAX_TIER) {
|
|
65
|
+
throw new Error(`gatherer tier out of range: ${tier}`)
|
|
66
|
+
}
|
|
67
|
+
const p = GATHERER_DEPTH_TABLE[tier - 1]
|
|
68
|
+
return p.floor + tol * p.slope
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function computeGathererCapabilities(
|
|
72
|
+
stats: Record<string, number>,
|
|
73
|
+
tier: number
|
|
74
|
+
): {
|
|
75
|
+
yield: number
|
|
76
|
+
drain: number
|
|
77
|
+
depth: number
|
|
78
|
+
} {
|
|
79
|
+
const str = stats.strength
|
|
80
|
+
const con = stats.saturation
|
|
81
|
+
const hrd = stats.hardness
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
yield: 200 + str,
|
|
85
|
+
drain: 2 * Math.max(250, 1250 - Math.floor((con * 25) / 20)),
|
|
86
|
+
depth: gathererDepthForTier(hrd, tier),
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function computeLoaderCapabilities(stats: Record<string, number>): {
|
|
91
|
+
mass: number
|
|
92
|
+
thrust: number
|
|
93
|
+
quantity: number
|
|
94
|
+
} {
|
|
95
|
+
const insulation = stats.insulation
|
|
96
|
+
const plasticity = stats.plasticity
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
mass: Math.max(200, 2000 - Math.floor(insulation * 2)),
|
|
100
|
+
thrust: 1 + Math.floor((plasticity * plasticity) / 10000),
|
|
101
|
+
quantity: 1,
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function computeCrafterCapabilities(stats: Record<string, number>): {
|
|
106
|
+
speed: number
|
|
107
|
+
drain: number
|
|
108
|
+
} {
|
|
109
|
+
const fin = stats.fineness
|
|
110
|
+
const con = stats.conductivity
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
speed: 100 + Math.floor((fin * 4) / 5),
|
|
114
|
+
drain: Math.max(5, 30 - Math.floor(con / 33)),
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function computeHaulerCapabilities(
|
|
119
|
+
stats: Record<string, number>,
|
|
120
|
+
tier: number
|
|
121
|
+
): {
|
|
122
|
+
capacity: number
|
|
123
|
+
efficiency: number
|
|
124
|
+
drain: number
|
|
125
|
+
} {
|
|
126
|
+
const resonance = stats.resonance
|
|
127
|
+
const plasticity = stats.plasticity
|
|
128
|
+
const conductivity = stats.conductivity
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
capacity: computeHaulerCapacity(resonance, tier),
|
|
132
|
+
efficiency: 2000 + plasticity * 6,
|
|
133
|
+
drain: Math.max(3, 15 - Math.floor(conductivity / 80)),
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function computeLauncherCapabilities(
|
|
138
|
+
stats: {charge_rate: number; velocity: number; drain: number},
|
|
139
|
+
amp = 100
|
|
140
|
+
): {chargeRate: number; velocity: number; drain: number} {
|
|
141
|
+
return {
|
|
142
|
+
chargeRate: Math.floor((stats.charge_rate * amp) / 100),
|
|
143
|
+
velocity: Math.floor((stats.velocity * amp) / 100),
|
|
144
|
+
drain: stats.drain,
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function computeStorageCapabilities(stats: Record<string, number>): {
|
|
149
|
+
capacity: number
|
|
150
|
+
} {
|
|
151
|
+
const strength = stats.strength ?? 0
|
|
152
|
+
const density = stats.density ?? 0
|
|
153
|
+
const hardness = stats.hardness ?? 0
|
|
154
|
+
const cohesion = stats.cohesion ?? 0
|
|
155
|
+
|
|
156
|
+
const statSum = strength + density + hardness + cohesion
|
|
157
|
+
return {capacity: 10_000_000 + Math.floor((statSum * 50_000_000) / 3996)}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function computeBatteryCapabilities(stats: Record<string, number>): {
|
|
161
|
+
capacity: number
|
|
162
|
+
} {
|
|
163
|
+
const volatility = stats.volatility ?? 0
|
|
164
|
+
const thermal = stats.thermal ?? 0
|
|
165
|
+
const plasticity = stats.plasticity ?? 0
|
|
166
|
+
const insulation = stats.insulation ?? 0
|
|
167
|
+
|
|
168
|
+
const statSum = volatility + thermal + plasticity + insulation
|
|
169
|
+
return {capacity: 2_500 + Math.floor((statSum * 7_500) / 3996)}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
import {
|
|
173
|
+
ITEM_CONTAINER_T1_PACKED,
|
|
174
|
+
ITEM_CONTAINER_T2_PACKED,
|
|
175
|
+
ITEM_EXTRACTOR_T1_PACKED,
|
|
176
|
+
ITEM_FACTORY_T1_PACKED,
|
|
177
|
+
ITEM_HAULER_SHIP_T2_PACKED,
|
|
178
|
+
ITEM_MASS_CATCHER_T1_PACKED,
|
|
179
|
+
ITEM_MASS_DRIVER_T1_PACKED,
|
|
180
|
+
ITEM_PROSPECTOR_T2_PACKED,
|
|
181
|
+
ITEM_SHIP_T1_PACKED,
|
|
182
|
+
ITEM_WAREHOUSE_T1_PACKED,
|
|
183
|
+
} from '../data/item-ids'
|
|
184
|
+
import {
|
|
185
|
+
getModuleCapabilityType,
|
|
186
|
+
MODULE_BATTERY,
|
|
187
|
+
MODULE_ENGINE,
|
|
188
|
+
MODULE_GENERATOR,
|
|
189
|
+
MODULE_GATHERER,
|
|
190
|
+
MODULE_LOADER,
|
|
191
|
+
MODULE_STORAGE,
|
|
192
|
+
MODULE_CRAFTER,
|
|
193
|
+
MODULE_HAULER,
|
|
194
|
+
MODULE_WARP,
|
|
195
|
+
MODULE_LAUNCHER,
|
|
196
|
+
} from '../capabilities/modules'
|
|
197
|
+
import {getItem} from '../data/catalog'
|
|
198
|
+
import {decodeCraftedItemStats, decodeStat} from './crafting'
|
|
199
|
+
import {
|
|
200
|
+
applySlotMultiplier,
|
|
201
|
+
applySlotMultiplierUint32,
|
|
202
|
+
clampUint16,
|
|
203
|
+
clampUint32,
|
|
204
|
+
getSlotAmp,
|
|
205
|
+
type InstalledModule,
|
|
206
|
+
} from '../entities/slot-multiplier'
|
|
207
|
+
import type {EntitySlot} from '../data/recipes-runtime'
|
|
208
|
+
import {computeHaulerCapacity, computeTravelDrain} from '../nft/description'
|
|
209
|
+
|
|
210
|
+
export const CAPACITY_TIER_TABLE = [1.0, 1.4, 1.8, 2.2, 2.6, 3.0, 3.4, 3.8, 4.2, 4.6]
|
|
211
|
+
|
|
212
|
+
export function capacityTierMultiplier(tier: number): number {
|
|
213
|
+
const clampedTier = tier >= 1 && tier <= 10 ? tier : 1
|
|
214
|
+
return CAPACITY_TIER_TABLE[clampedTier - 1]
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function applyCapacityTier(baseCapacity: number, tier: number): number {
|
|
218
|
+
return clampUint32(Math.floor(baseCapacity * capacityTierMultiplier(tier)))
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function computeBaseCapacity(itemId: number, stats: Record<string, number>): number {
|
|
222
|
+
let base: number
|
|
223
|
+
switch (itemId) {
|
|
224
|
+
case ITEM_SHIP_T1_PACKED:
|
|
225
|
+
case ITEM_PROSPECTOR_T2_PACKED:
|
|
226
|
+
case ITEM_HAULER_SHIP_T2_PACKED:
|
|
227
|
+
base = computeShipHullCapabilities(stats).capacity
|
|
228
|
+
break
|
|
229
|
+
case ITEM_EXTRACTOR_T1_PACKED:
|
|
230
|
+
case ITEM_FACTORY_T1_PACKED:
|
|
231
|
+
case ITEM_MASS_DRIVER_T1_PACKED:
|
|
232
|
+
case ITEM_MASS_CATCHER_T1_PACKED:
|
|
233
|
+
case ITEM_CONTAINER_T1_PACKED:
|
|
234
|
+
case ITEM_CONTAINER_T2_PACKED:
|
|
235
|
+
base = computeContainerCapabilities(stats).capacity
|
|
236
|
+
break
|
|
237
|
+
case ITEM_WAREHOUSE_T1_PACKED:
|
|
238
|
+
base = computeWarehouseHullCapabilities(stats).capacity
|
|
239
|
+
break
|
|
240
|
+
default:
|
|
241
|
+
return 0
|
|
242
|
+
}
|
|
243
|
+
return applyCapacityTier(base, getItem(itemId).tier)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export function computeWarpCapabilities(stats: Record<string, number>): {
|
|
247
|
+
range: number
|
|
248
|
+
} {
|
|
249
|
+
const reflectivity = stats.reflectivity
|
|
250
|
+
return {range: 100 + reflectivity * 3}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function computeWarehouseHullCapabilities(stats: Record<string, number>): {
|
|
254
|
+
hullmass: number
|
|
255
|
+
capacity: number
|
|
256
|
+
} {
|
|
257
|
+
const statSum = (stats.strength ?? 0) + (stats.hardness ?? 0)
|
|
258
|
+
const exponent = statSum / 1998.0
|
|
259
|
+
return {
|
|
260
|
+
hullmass: computeBaseHullmass(stats),
|
|
261
|
+
capacity: Math.floor(100000000 * 6 ** exponent),
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export interface GathererLaneEntry {
|
|
266
|
+
slotIndex: number
|
|
267
|
+
yield: number
|
|
268
|
+
drain: number
|
|
269
|
+
depth: number
|
|
270
|
+
outputPct: number
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export interface CrafterLaneEntry {
|
|
274
|
+
slotIndex: number
|
|
275
|
+
speed: number
|
|
276
|
+
drain: number
|
|
277
|
+
outputPct: number
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export interface LoaderLaneEntry {
|
|
281
|
+
slotIndex: number
|
|
282
|
+
mass: number
|
|
283
|
+
thrust: number
|
|
284
|
+
outputPct: number
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export interface ComputedCapabilities {
|
|
288
|
+
hullmass: number
|
|
289
|
+
capacity: number
|
|
290
|
+
engines?: {thrust: number; drain: number}
|
|
291
|
+
generator?: {capacity: number; recharge: number}
|
|
292
|
+
gatherer?: {yield: number; drain: number; depth: number}
|
|
293
|
+
gathererLanes?: GathererLaneEntry[]
|
|
294
|
+
loaders?: {mass: number; thrust: number; quantity: number}
|
|
295
|
+
loaderLanes?: LoaderLaneEntry[]
|
|
296
|
+
crafter?: {speed: number; drain: number}
|
|
297
|
+
crafterLanes?: CrafterLaneEntry[]
|
|
298
|
+
hauler?: {
|
|
299
|
+
capacity: number
|
|
300
|
+
efficiency: number
|
|
301
|
+
drain: number
|
|
302
|
+
capacityByTier: {tier: number; capacity: number}[]
|
|
303
|
+
}
|
|
304
|
+
warp?: {range: number}
|
|
305
|
+
launcher?: {chargeRate: number; velocity: number; drain: number}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export function computeEntityCapabilities(
|
|
309
|
+
stats: Record<string, number>,
|
|
310
|
+
itemId: number,
|
|
311
|
+
modules: InstalledModule[],
|
|
312
|
+
layout: EntitySlot[]
|
|
313
|
+
): ComputedCapabilities {
|
|
314
|
+
let totalThrust = 0
|
|
315
|
+
let totalEngineThm = 0
|
|
316
|
+
let engineCount = 0
|
|
317
|
+
let hasEngine = false
|
|
318
|
+
|
|
319
|
+
let totalGenCapacity = 0
|
|
320
|
+
let totalGenRecharge = 0
|
|
321
|
+
let hasGenerator = false
|
|
322
|
+
|
|
323
|
+
let totalLoaderMass = 0
|
|
324
|
+
let totalLoaderThrust = 0
|
|
325
|
+
let totalLoaderQuantity = 0
|
|
326
|
+
let hasLoader = false
|
|
327
|
+
|
|
328
|
+
let totalGathYield = 0
|
|
329
|
+
let totalGathDrain = 0
|
|
330
|
+
let maxGathDepth = 0
|
|
331
|
+
let hasGatherer = false
|
|
332
|
+
|
|
333
|
+
let totalStorageCapacity = 0
|
|
334
|
+
const baseCapacity = computeBaseCapacity(itemId, stats)
|
|
335
|
+
let installedModuleMass = 0
|
|
336
|
+
|
|
337
|
+
let totalCrafterSpeed = 0
|
|
338
|
+
let totalCrafterDrain = 0
|
|
339
|
+
let hasCrafter = false
|
|
340
|
+
|
|
341
|
+
let totalHaulerCapacity = 0
|
|
342
|
+
let weightedHaulerEffNum = 0n
|
|
343
|
+
let totalHaulerDrain = 0
|
|
344
|
+
let hasHauler = false
|
|
345
|
+
const haulerCapByTier = new Map<number, number>()
|
|
346
|
+
|
|
347
|
+
let totalWarpRange = 0
|
|
348
|
+
let hasWarp = false
|
|
349
|
+
|
|
350
|
+
let totalLauncherChargeRate = 0
|
|
351
|
+
let totalLauncherVelocity = 0
|
|
352
|
+
let totalLauncherDrain = 0
|
|
353
|
+
let hasLauncher = false
|
|
354
|
+
|
|
355
|
+
let totalBatteryCapacity = 0
|
|
356
|
+
|
|
357
|
+
const gathererLanes: GathererLaneEntry[] = []
|
|
358
|
+
const crafterLanes: CrafterLaneEntry[] = []
|
|
359
|
+
const loaderLanes: LoaderLaneEntry[] = []
|
|
360
|
+
|
|
361
|
+
for (const mod of modules) {
|
|
362
|
+
const item = getItem(mod.itemId)
|
|
363
|
+
const modType = getModuleCapabilityType(mod.itemId)
|
|
364
|
+
const amp = getSlotAmp(layout, mod.slotIndex)
|
|
365
|
+
const decodedStats = decodeCraftedItemStats(mod.itemId, mod.stats)
|
|
366
|
+
installedModuleMass += item.mass
|
|
367
|
+
|
|
368
|
+
if (modType === MODULE_ENGINE) {
|
|
369
|
+
hasEngine = true
|
|
370
|
+
const caps = computeEngineCapabilities(decodedStats)
|
|
371
|
+
totalThrust += applySlotMultiplier(caps.thrust, amp)
|
|
372
|
+
totalEngineThm += decodedStats.thermal ?? 0
|
|
373
|
+
engineCount += 1
|
|
374
|
+
} else if (modType === MODULE_GENERATOR) {
|
|
375
|
+
hasGenerator = true
|
|
376
|
+
const caps = computeGeneratorCapabilities(decodedStats)
|
|
377
|
+
totalGenCapacity += applySlotMultiplier(caps.capacity, amp)
|
|
378
|
+
totalGenRecharge += applySlotMultiplier(caps.recharge, amp)
|
|
379
|
+
} else if (modType === MODULE_GATHERER) {
|
|
380
|
+
hasGatherer = true
|
|
381
|
+
const tier = item.tier
|
|
382
|
+
const caps = computeGathererCapabilities(decodedStats, tier)
|
|
383
|
+
const scaledYield = applySlotMultiplier(caps.yield, amp)
|
|
384
|
+
totalGathYield += scaledYield
|
|
385
|
+
totalGathDrain += caps.drain
|
|
386
|
+
if (caps.depth > maxGathDepth) maxGathDepth = caps.depth
|
|
387
|
+
gathererLanes.push({
|
|
388
|
+
slotIndex: mod.slotIndex,
|
|
389
|
+
yield: scaledYield,
|
|
390
|
+
drain: caps.drain,
|
|
391
|
+
depth: caps.depth,
|
|
392
|
+
outputPct: amp,
|
|
393
|
+
})
|
|
394
|
+
} else if (modType === MODULE_LOADER) {
|
|
395
|
+
hasLoader = true
|
|
396
|
+
const caps = computeLoaderCapabilities(decodedStats)
|
|
397
|
+
totalLoaderMass += caps.mass
|
|
398
|
+
totalLoaderThrust += applySlotMultiplier(caps.thrust, amp)
|
|
399
|
+
totalLoaderQuantity += caps.quantity
|
|
400
|
+
loaderLanes.push({
|
|
401
|
+
slotIndex: mod.slotIndex,
|
|
402
|
+
mass: caps.mass,
|
|
403
|
+
thrust: applySlotMultiplier(caps.thrust, amp),
|
|
404
|
+
outputPct: amp,
|
|
405
|
+
})
|
|
406
|
+
} else if (modType === MODULE_STORAGE) {
|
|
407
|
+
const caps = computeStorageCapabilities(decodedStats)
|
|
408
|
+
totalStorageCapacity += applySlotMultiplierUint32(caps.capacity, amp)
|
|
409
|
+
} else if (modType === MODULE_CRAFTER) {
|
|
410
|
+
hasCrafter = true
|
|
411
|
+
const caps = computeCrafterCapabilities(decodedStats)
|
|
412
|
+
const scaledSpeed = applySlotMultiplier(caps.speed, amp)
|
|
413
|
+
totalCrafterSpeed += scaledSpeed
|
|
414
|
+
totalCrafterDrain += caps.drain
|
|
415
|
+
crafterLanes.push({
|
|
416
|
+
slotIndex: mod.slotIndex,
|
|
417
|
+
speed: scaledSpeed,
|
|
418
|
+
drain: caps.drain,
|
|
419
|
+
outputPct: amp,
|
|
420
|
+
})
|
|
421
|
+
} else if (modType === MODULE_HAULER) {
|
|
422
|
+
hasHauler = true
|
|
423
|
+
const caps = computeHaulerCapabilities(decodedStats, item.tier)
|
|
424
|
+
const eff = applySlotMultiplier(caps.efficiency, amp)
|
|
425
|
+
totalHaulerCapacity += caps.capacity
|
|
426
|
+
haulerCapByTier.set(item.tier, (haulerCapByTier.get(item.tier) ?? 0) + caps.capacity)
|
|
427
|
+
weightedHaulerEffNum += BigInt(eff) * BigInt(caps.capacity)
|
|
428
|
+
totalHaulerDrain += caps.drain
|
|
429
|
+
} else if (modType === MODULE_WARP) {
|
|
430
|
+
hasWarp = true
|
|
431
|
+
const caps = computeWarpCapabilities(decodedStats)
|
|
432
|
+
totalWarpRange += applySlotMultiplier(caps.range, amp)
|
|
433
|
+
} else if (modType === MODULE_LAUNCHER) {
|
|
434
|
+
hasLauncher = true
|
|
435
|
+
const caps = computeLauncherCapabilities(
|
|
436
|
+
{
|
|
437
|
+
charge_rate: decodedStats.charge_rate ?? decodeStat(mod.stats, 0),
|
|
438
|
+
velocity: decodedStats.velocity ?? decodeStat(mod.stats, 1),
|
|
439
|
+
drain: decodedStats.drain ?? decodeStat(mod.stats, 2),
|
|
440
|
+
},
|
|
441
|
+
amp
|
|
442
|
+
)
|
|
443
|
+
totalLauncherChargeRate = clampUint16(totalLauncherChargeRate + caps.chargeRate)
|
|
444
|
+
totalLauncherVelocity = clampUint16(totalLauncherVelocity + caps.velocity)
|
|
445
|
+
totalLauncherDrain = clampUint16(totalLauncherDrain + caps.drain)
|
|
446
|
+
} else if (modType === MODULE_BATTERY) {
|
|
447
|
+
const caps = computeBatteryCapabilities(decodedStats)
|
|
448
|
+
totalBatteryCapacity += applySlotMultiplierUint32(caps.capacity, amp)
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (hasGenerator && totalBatteryCapacity > 0) {
|
|
453
|
+
totalGenCapacity += totalBatteryCapacity
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const result: ComputedCapabilities = {
|
|
457
|
+
hullmass: computeBaseHullmass(stats) + installedModuleMass,
|
|
458
|
+
capacity: clampUint32(baseCapacity + totalStorageCapacity),
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (hasEngine) {
|
|
462
|
+
const avgThm = engineCount > 0 ? Math.trunc(totalEngineThm / engineCount) : 0
|
|
463
|
+
result.engines = {thrust: totalThrust, drain: computeTravelDrain(totalThrust, avgThm)}
|
|
464
|
+
}
|
|
465
|
+
if (hasGenerator) {
|
|
466
|
+
result.generator = {
|
|
467
|
+
capacity: clampUint32(totalGenCapacity),
|
|
468
|
+
recharge: clampUint32(totalGenRecharge),
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
if (hasGatherer) {
|
|
472
|
+
result.gatherer = {
|
|
473
|
+
yield: clampUint16(totalGathYield),
|
|
474
|
+
drain: totalGathDrain,
|
|
475
|
+
depth: maxGathDepth,
|
|
476
|
+
}
|
|
477
|
+
result.gathererLanes = gathererLanes
|
|
478
|
+
}
|
|
479
|
+
if (hasLoader) {
|
|
480
|
+
result.loaders = {
|
|
481
|
+
mass: totalLoaderMass,
|
|
482
|
+
thrust: clampUint16(totalLoaderThrust),
|
|
483
|
+
quantity: totalLoaderQuantity,
|
|
484
|
+
}
|
|
485
|
+
result.loaderLanes = loaderLanes
|
|
486
|
+
}
|
|
487
|
+
if (hasCrafter) {
|
|
488
|
+
result.crafter = {speed: clampUint16(totalCrafterSpeed), drain: totalCrafterDrain}
|
|
489
|
+
result.crafterLanes = crafterLanes
|
|
490
|
+
}
|
|
491
|
+
if (hasHauler) {
|
|
492
|
+
const efficiency =
|
|
493
|
+
totalHaulerCapacity > 0 ? Number(weightedHaulerEffNum / BigInt(totalHaulerCapacity)) : 0
|
|
494
|
+
result.hauler = {
|
|
495
|
+
capacity: totalHaulerCapacity,
|
|
496
|
+
efficiency: clampUint16(efficiency),
|
|
497
|
+
drain: totalHaulerDrain,
|
|
498
|
+
capacityByTier: [...haulerCapByTier.entries()]
|
|
499
|
+
.sort((a, b) => a[0] - b[0])
|
|
500
|
+
.map(([tier, capacity]) => ({tier, capacity})),
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
if (hasWarp) {
|
|
504
|
+
result.warp = {range: totalWarpRange}
|
|
505
|
+
}
|
|
506
|
+
if (hasLauncher) {
|
|
507
|
+
result.launcher = {
|
|
508
|
+
chargeRate: totalLauncherChargeRate,
|
|
509
|
+
velocity: totalLauncherVelocity,
|
|
510
|
+
drain: totalLauncherDrain,
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return result
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
export function computeContainerCapabilities(stats: Record<string, number>): {
|
|
518
|
+
hullmass: number
|
|
519
|
+
capacity: number
|
|
520
|
+
} {
|
|
521
|
+
const statSum = (stats.strength ?? 0) + (stats.hardness ?? 0)
|
|
522
|
+
const exponent = statSum / 1998.0
|
|
523
|
+
return {
|
|
524
|
+
hullmass: computeBaseHullmass(stats),
|
|
525
|
+
capacity: Math.floor(22000000 * 6 ** exponent),
|
|
526
|
+
}
|
|
527
|
+
}
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import {SLOT_FORMULAS, type SlotConsumerKind} from '../data/capability-formulas'
|
|
2
2
|
import {getStatDefinitions, type StatDefinition} from './stats'
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
type Recipe,
|
|
6
|
-
type RecipeInput,
|
|
7
|
-
type RecipeInputCategory,
|
|
8
|
-
} from '../data/recipes-runtime'
|
|
3
|
+
import {getRecipe, type Recipe} from '../data/recipes-runtime'
|
|
4
|
+
import {getItem} from '../data/catalog'
|
|
9
5
|
import {
|
|
10
6
|
ITEM_ENGINE_T1,
|
|
7
|
+
ITEM_EXTRACTOR_T1_PACKED,
|
|
11
8
|
ITEM_GENERATOR_T1,
|
|
12
9
|
ITEM_GATHERER_T1,
|
|
13
10
|
ITEM_LOADER_T1,
|
|
@@ -15,12 +12,14 @@ import {
|
|
|
15
12
|
ITEM_STORAGE_T1,
|
|
16
13
|
ITEM_HAULER_T1,
|
|
17
14
|
ITEM_WARP_T1,
|
|
15
|
+
ITEM_BATTERY_T1,
|
|
18
16
|
ITEM_SHIP_T1_PACKED,
|
|
19
17
|
ITEM_CONTAINER_T1_PACKED,
|
|
20
18
|
ITEM_WAREHOUSE_T1_PACKED,
|
|
21
19
|
ITEM_CONTAINER_T2_PACKED,
|
|
22
20
|
} from '../data/item-ids'
|
|
23
|
-
import type {StatMapping} from '../data/capabilities'
|
|
21
|
+
import type {StatMapping, CapabilityAttributeRow} from '../data/capabilities'
|
|
22
|
+
import {capabilityAttributes} from '../data/capabilities'
|
|
24
23
|
|
|
25
24
|
export const KIND_TO_ITEM_ID: Record<SlotConsumerKind, number> = {
|
|
26
25
|
engine: ITEM_ENGINE_T1,
|
|
@@ -31,16 +30,14 @@ export const KIND_TO_ITEM_ID: Record<SlotConsumerKind, number> = {
|
|
|
31
30
|
storage: ITEM_STORAGE_T1,
|
|
32
31
|
hauler: ITEM_HAULER_T1,
|
|
33
32
|
warp: ITEM_WARP_T1,
|
|
33
|
+
battery: ITEM_BATTERY_T1,
|
|
34
34
|
'ship-t1': ITEM_SHIP_T1_PACKED,
|
|
35
35
|
'container-t1': ITEM_CONTAINER_T1_PACKED,
|
|
36
36
|
'warehouse-t1': ITEM_WAREHOUSE_T1_PACKED,
|
|
37
|
+
'extractor-t1': ITEM_EXTRACTOR_T1_PACKED,
|
|
37
38
|
'container-t2': ITEM_CONTAINER_T2_PACKED,
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
function isCategoryInput(input: RecipeInput): input is RecipeInputCategory {
|
|
41
|
-
return 'category' in input
|
|
42
|
-
}
|
|
43
|
-
|
|
44
41
|
/**
|
|
45
42
|
* Walk a recipe's slot source down to the raw category stat that ultimately
|
|
46
43
|
* lands in that slot. Returns the StatDefinition or undefined if the trace
|
|
@@ -56,8 +53,9 @@ function traceToRawCategoryStat(
|
|
|
56
53
|
): StatDefinition | undefined {
|
|
57
54
|
const input = recipe.inputs[source.inputIndex]
|
|
58
55
|
if (!input) return undefined
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
const inputItem = getItem(input.itemId)
|
|
57
|
+
if (inputItem.type === 'resource' && inputItem.category) {
|
|
58
|
+
const defs = getStatDefinitions(inputItem.category)
|
|
61
59
|
return defs[source.statIndex]
|
|
62
60
|
}
|
|
63
61
|
if (visited.has(input.itemId)) return undefined
|
|
@@ -72,6 +70,12 @@ function traceToRawCategoryStat(
|
|
|
72
70
|
return traceToRawCategoryStat(subRecipe, subSource, nextVisited)
|
|
73
71
|
}
|
|
74
72
|
|
|
73
|
+
// Producing role for a capability·attribute: entity hull slots all roll up to "Hull"; modules use their own name.
|
|
74
|
+
export function sourceLabelForOutput(itemId: number): string {
|
|
75
|
+
const item = getItem(itemId)
|
|
76
|
+
return item.type === 'entity' ? 'Hull' : item.name
|
|
77
|
+
}
|
|
78
|
+
|
|
75
79
|
let cached: StatMapping[] | undefined
|
|
76
80
|
|
|
77
81
|
export function deriveStatMappings(): StatMapping[] {
|
|
@@ -85,20 +89,22 @@ export function deriveStatMappings(): StatMapping[] {
|
|
|
85
89
|
const itemId = KIND_TO_ITEM_ID[kind]
|
|
86
90
|
const recipe = getRecipe(itemId)
|
|
87
91
|
if (!recipe) continue
|
|
92
|
+
const source = sourceLabelForOutput(itemId)
|
|
88
93
|
for (const [slotIdxStr, consumer] of Object.entries(slots)) {
|
|
89
94
|
const slotIdx = Number(slotIdxStr)
|
|
90
95
|
const slot = recipe.statSlots[slotIdx]
|
|
91
96
|
if (!slot) continue
|
|
92
|
-
for (const
|
|
93
|
-
const stat = traceToRawCategoryStat(recipe,
|
|
97
|
+
for (const src of slot.sources) {
|
|
98
|
+
const stat = traceToRawCategoryStat(recipe, src)
|
|
94
99
|
if (!stat) continue
|
|
95
|
-
const key = `${stat.label}|${consumer.capability}|${consumer.attribute}`
|
|
100
|
+
const key = `${stat.label}|${consumer.capability}|${consumer.attribute}|${source}`
|
|
96
101
|
if (seen.has(key)) continue
|
|
97
102
|
seen.add(key)
|
|
98
103
|
out.push({
|
|
99
104
|
stat: stat.label,
|
|
100
105
|
capability: consumer.capability,
|
|
101
106
|
attribute: consumer.attribute,
|
|
107
|
+
source,
|
|
102
108
|
})
|
|
103
109
|
}
|
|
104
110
|
}
|
|
@@ -118,3 +124,39 @@ export function getStatMappingsForStat(stat: string): StatMapping[] {
|
|
|
118
124
|
export function getStatMappingsForCapability(capability: string): StatMapping[] {
|
|
119
125
|
return deriveStatMappings().filter((m) => m.capability === capability)
|
|
120
126
|
}
|
|
127
|
+
|
|
128
|
+
export function getProducersForAttribute(capability: string, attribute: string): string[] {
|
|
129
|
+
const seen = new Set<string>()
|
|
130
|
+
const out: string[] = []
|
|
131
|
+
for (const m of deriveStatMappings()) {
|
|
132
|
+
if (m.capability !== capability || m.attribute !== attribute) continue
|
|
133
|
+
if (seen.has(m.source)) continue
|
|
134
|
+
seen.add(m.source)
|
|
135
|
+
out.push(m.source)
|
|
136
|
+
}
|
|
137
|
+
return out
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function getCapabilityAttributeRows(): CapabilityAttributeRow[] {
|
|
141
|
+
const rows: CapabilityAttributeRow[] = []
|
|
142
|
+
for (const ca of capabilityAttributes) {
|
|
143
|
+
const producers = getProducersForAttribute(ca.capability, ca.attribute)
|
|
144
|
+
if (producers.length === 0) {
|
|
145
|
+
rows.push({
|
|
146
|
+
capability: ca.capability,
|
|
147
|
+
attribute: ca.attribute,
|
|
148
|
+
description: ca.description,
|
|
149
|
+
})
|
|
150
|
+
continue
|
|
151
|
+
}
|
|
152
|
+
for (const source of producers) {
|
|
153
|
+
rows.push({
|
|
154
|
+
capability: ca.capability,
|
|
155
|
+
attribute: ca.attribute,
|
|
156
|
+
description: ca.description,
|
|
157
|
+
source,
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return rows
|
|
162
|
+
}
|