@shipload/sdk 1.0.0-next.59 → 1.0.0-next.60
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/shipload.d.ts +66 -27
- package/lib/shipload.js +833 -336
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +822 -337
- package/lib/shipload.m.js.map +1 -1
- package/lib/testing.d.ts +12 -0
- package/lib/testing.js +110 -1
- package/lib/testing.js.map +1 -1
- package/lib/testing.m.js +110 -1
- package/lib/testing.m.js.map +1 -1
- package/package.json +1 -1
- package/src/capabilities/crafting.test.ts +30 -2
- package/src/capabilities/crafting.ts +19 -0
- package/src/capabilities/modules.ts +4 -0
- package/src/contracts/server.ts +37 -1
- package/src/data/item-ids.ts +6 -0
- package/src/data/items.json +42 -0
- package/src/data/metadata.ts +35 -0
- package/src/data/recipes.json +246 -0
- package/src/derivation/capabilities.test.ts +106 -8
- package/src/derivation/capabilities.ts +101 -43
- package/src/derivation/wormhole.ts +17 -4
- package/src/index-module.ts +7 -1
- package/src/managers/actions.ts +23 -0
- package/src/nft/buildImmutableData.ts +12 -9
- package/src/nft/description.ts +52 -17
- package/src/resolution/resolve-item.ts +5 -5
- package/src/scheduling/availability.test.ts +47 -0
- package/src/scheduling/availability.ts +24 -3
- package/src/scheduling/cluster-stock.test.ts +31 -0
- package/src/scheduling/cluster-stock.ts +15 -0
- package/src/scheduling/lanes.test.ts +10 -9
- package/src/scheduling/lanes.ts +6 -6
- package/src/scheduling/projection.ts +7 -3
- package/src/scheduling/task-cargo.test.ts +44 -0
- package/src/scheduling/task-cargo.ts +1 -1
- package/src/types.ts +1 -0
package/src/data/recipes.json
CHANGED
|
@@ -2086,6 +2086,88 @@
|
|
|
2086
2086
|
],
|
|
2087
2087
|
"blendWeights": []
|
|
2088
2088
|
},
|
|
2089
|
+
{
|
|
2090
|
+
"outputItemId": 20100,
|
|
2091
|
+
"outputMass": 1536000,
|
|
2092
|
+
"inputs": [
|
|
2093
|
+
{
|
|
2094
|
+
"itemId": 20003,
|
|
2095
|
+
"quantity": 600
|
|
2096
|
+
},
|
|
2097
|
+
{
|
|
2098
|
+
"itemId": 10100,
|
|
2099
|
+
"quantity": 1
|
|
2100
|
+
}
|
|
2101
|
+
],
|
|
2102
|
+
"statSlots": [
|
|
2103
|
+
{
|
|
2104
|
+
"sources": [
|
|
2105
|
+
{
|
|
2106
|
+
"inputIndex": 0,
|
|
2107
|
+
"statIndex": 0
|
|
2108
|
+
},
|
|
2109
|
+
{
|
|
2110
|
+
"inputIndex": 1,
|
|
2111
|
+
"statIndex": 0
|
|
2112
|
+
}
|
|
2113
|
+
]
|
|
2114
|
+
},
|
|
2115
|
+
{
|
|
2116
|
+
"sources": [
|
|
2117
|
+
{
|
|
2118
|
+
"inputIndex": 0,
|
|
2119
|
+
"statIndex": 1
|
|
2120
|
+
},
|
|
2121
|
+
{
|
|
2122
|
+
"inputIndex": 1,
|
|
2123
|
+
"statIndex": 1
|
|
2124
|
+
}
|
|
2125
|
+
]
|
|
2126
|
+
}
|
|
2127
|
+
],
|
|
2128
|
+
"blendWeights": []
|
|
2129
|
+
},
|
|
2130
|
+
{
|
|
2131
|
+
"outputItemId": 20101,
|
|
2132
|
+
"outputMass": 1536000,
|
|
2133
|
+
"inputs": [
|
|
2134
|
+
{
|
|
2135
|
+
"itemId": 20004,
|
|
2136
|
+
"quantity": 600
|
|
2137
|
+
},
|
|
2138
|
+
{
|
|
2139
|
+
"itemId": 10101,
|
|
2140
|
+
"quantity": 1
|
|
2141
|
+
}
|
|
2142
|
+
],
|
|
2143
|
+
"statSlots": [
|
|
2144
|
+
{
|
|
2145
|
+
"sources": [
|
|
2146
|
+
{
|
|
2147
|
+
"inputIndex": 0,
|
|
2148
|
+
"statIndex": 0
|
|
2149
|
+
},
|
|
2150
|
+
{
|
|
2151
|
+
"inputIndex": 1,
|
|
2152
|
+
"statIndex": 0
|
|
2153
|
+
}
|
|
2154
|
+
]
|
|
2155
|
+
},
|
|
2156
|
+
{
|
|
2157
|
+
"sources": [
|
|
2158
|
+
{
|
|
2159
|
+
"inputIndex": 0,
|
|
2160
|
+
"statIndex": 1
|
|
2161
|
+
},
|
|
2162
|
+
{
|
|
2163
|
+
"inputIndex": 1,
|
|
2164
|
+
"statIndex": 1
|
|
2165
|
+
}
|
|
2166
|
+
]
|
|
2167
|
+
}
|
|
2168
|
+
],
|
|
2169
|
+
"blendWeights": []
|
|
2170
|
+
},
|
|
2089
2171
|
{
|
|
2090
2172
|
"outputItemId": 20102,
|
|
2091
2173
|
"outputMass": 1536000,
|
|
@@ -2143,6 +2225,92 @@
|
|
|
2143
2225
|
],
|
|
2144
2226
|
"blendWeights": []
|
|
2145
2227
|
},
|
|
2228
|
+
{
|
|
2229
|
+
"outputItemId": 20103,
|
|
2230
|
+
"outputMass": 1536000,
|
|
2231
|
+
"inputs": [
|
|
2232
|
+
{
|
|
2233
|
+
"itemId": 20007,
|
|
2234
|
+
"quantity": 600
|
|
2235
|
+
},
|
|
2236
|
+
{
|
|
2237
|
+
"itemId": 10103,
|
|
2238
|
+
"quantity": 1
|
|
2239
|
+
}
|
|
2240
|
+
],
|
|
2241
|
+
"statSlots": [
|
|
2242
|
+
{
|
|
2243
|
+
"sources": [
|
|
2244
|
+
{
|
|
2245
|
+
"inputIndex": 0,
|
|
2246
|
+
"statIndex": 1
|
|
2247
|
+
},
|
|
2248
|
+
{
|
|
2249
|
+
"inputIndex": 1,
|
|
2250
|
+
"statIndex": 0
|
|
2251
|
+
}
|
|
2252
|
+
]
|
|
2253
|
+
},
|
|
2254
|
+
{
|
|
2255
|
+
"sources": [
|
|
2256
|
+
{
|
|
2257
|
+
"inputIndex": 0,
|
|
2258
|
+
"statIndex": 0
|
|
2259
|
+
},
|
|
2260
|
+
{
|
|
2261
|
+
"inputIndex": 1,
|
|
2262
|
+
"statIndex": 1
|
|
2263
|
+
}
|
|
2264
|
+
]
|
|
2265
|
+
}
|
|
2266
|
+
],
|
|
2267
|
+
"blendWeights": []
|
|
2268
|
+
},
|
|
2269
|
+
{
|
|
2270
|
+
"outputItemId": 20104,
|
|
2271
|
+
"outputMass": 1536000,
|
|
2272
|
+
"inputs": [
|
|
2273
|
+
{
|
|
2274
|
+
"itemId": 20009,
|
|
2275
|
+
"quantity": 300
|
|
2276
|
+
},
|
|
2277
|
+
{
|
|
2278
|
+
"itemId": 20006,
|
|
2279
|
+
"quantity": 300
|
|
2280
|
+
},
|
|
2281
|
+
{
|
|
2282
|
+
"itemId": 10104,
|
|
2283
|
+
"quantity": 1
|
|
2284
|
+
}
|
|
2285
|
+
],
|
|
2286
|
+
"statSlots": [
|
|
2287
|
+
{
|
|
2288
|
+
"sources": [
|
|
2289
|
+
{
|
|
2290
|
+
"inputIndex": 0,
|
|
2291
|
+
"statIndex": 2
|
|
2292
|
+
},
|
|
2293
|
+
{
|
|
2294
|
+
"inputIndex": 2,
|
|
2295
|
+
"statIndex": 0
|
|
2296
|
+
}
|
|
2297
|
+
]
|
|
2298
|
+
},
|
|
2299
|
+
{
|
|
2300
|
+
"sources": [
|
|
2301
|
+
{
|
|
2302
|
+
"inputIndex": 1,
|
|
2303
|
+
"statIndex": 0
|
|
2304
|
+
},
|
|
2305
|
+
{
|
|
2306
|
+
"inputIndex": 2,
|
|
2307
|
+
"statIndex": 1
|
|
2308
|
+
}
|
|
2309
|
+
]
|
|
2310
|
+
}
|
|
2311
|
+
],
|
|
2312
|
+
"blendWeights": []
|
|
2313
|
+
},
|
|
2146
2314
|
{
|
|
2147
2315
|
"outputItemId": 20106,
|
|
2148
2316
|
"outputMass": 1536000,
|
|
@@ -2200,6 +2368,84 @@
|
|
|
2200
2368
|
],
|
|
2201
2369
|
"blendWeights": []
|
|
2202
2370
|
},
|
|
2371
|
+
{
|
|
2372
|
+
"outputItemId": 20107,
|
|
2373
|
+
"outputMass": 1536000,
|
|
2374
|
+
"inputs": [
|
|
2375
|
+
{
|
|
2376
|
+
"itemId": 20004,
|
|
2377
|
+
"quantity": 300
|
|
2378
|
+
},
|
|
2379
|
+
{
|
|
2380
|
+
"itemId": 20009,
|
|
2381
|
+
"quantity": 300
|
|
2382
|
+
},
|
|
2383
|
+
{
|
|
2384
|
+
"itemId": 10107,
|
|
2385
|
+
"quantity": 1
|
|
2386
|
+
}
|
|
2387
|
+
],
|
|
2388
|
+
"statSlots": [
|
|
2389
|
+
{
|
|
2390
|
+
"sources": [
|
|
2391
|
+
{
|
|
2392
|
+
"inputIndex": 0,
|
|
2393
|
+
"statIndex": 1
|
|
2394
|
+
},
|
|
2395
|
+
{
|
|
2396
|
+
"inputIndex": 2,
|
|
2397
|
+
"statIndex": 0
|
|
2398
|
+
}
|
|
2399
|
+
]
|
|
2400
|
+
}
|
|
2401
|
+
],
|
|
2402
|
+
"blendWeights": []
|
|
2403
|
+
},
|
|
2404
|
+
{
|
|
2405
|
+
"outputItemId": 20110,
|
|
2406
|
+
"outputMass": 1536000,
|
|
2407
|
+
"inputs": [
|
|
2408
|
+
{
|
|
2409
|
+
"itemId": 20006,
|
|
2410
|
+
"quantity": 300
|
|
2411
|
+
},
|
|
2412
|
+
{
|
|
2413
|
+
"itemId": 20008,
|
|
2414
|
+
"quantity": 300
|
|
2415
|
+
},
|
|
2416
|
+
{
|
|
2417
|
+
"itemId": 10110,
|
|
2418
|
+
"quantity": 1
|
|
2419
|
+
}
|
|
2420
|
+
],
|
|
2421
|
+
"statSlots": [
|
|
2422
|
+
{
|
|
2423
|
+
"sources": [
|
|
2424
|
+
{
|
|
2425
|
+
"inputIndex": 0,
|
|
2426
|
+
"statIndex": 2
|
|
2427
|
+
},
|
|
2428
|
+
{
|
|
2429
|
+
"inputIndex": 2,
|
|
2430
|
+
"statIndex": 0
|
|
2431
|
+
}
|
|
2432
|
+
]
|
|
2433
|
+
},
|
|
2434
|
+
{
|
|
2435
|
+
"sources": [
|
|
2436
|
+
{
|
|
2437
|
+
"inputIndex": 1,
|
|
2438
|
+
"statIndex": 1
|
|
2439
|
+
},
|
|
2440
|
+
{
|
|
2441
|
+
"inputIndex": 2,
|
|
2442
|
+
"statIndex": 1
|
|
2443
|
+
}
|
|
2444
|
+
]
|
|
2445
|
+
}
|
|
2446
|
+
],
|
|
2447
|
+
"blendWeights": []
|
|
2448
|
+
},
|
|
2203
2449
|
{
|
|
2204
2450
|
"outputItemId": 20200,
|
|
2205
2451
|
"outputMass": 3100000,
|
|
@@ -1,15 +1,34 @@
|
|
|
1
|
-
import {expect, test} from 'bun:test'
|
|
1
|
+
import {describe, expect, test} from 'bun:test'
|
|
2
2
|
import {
|
|
3
3
|
computeEntityCapabilities,
|
|
4
4
|
computeEngineCapabilities,
|
|
5
5
|
computeGathererCapabilities,
|
|
6
|
+
computeGathererYield,
|
|
6
7
|
computeGeneratorCapabilities,
|
|
7
8
|
computeCrafterCapabilities,
|
|
8
9
|
computeBuilderCapabilities,
|
|
9
10
|
computeLoaderCapabilities,
|
|
10
11
|
computeBaseCapacity,
|
|
11
12
|
computeContainerCapabilities,
|
|
13
|
+
GATHERER_YIELD_TIER_TABLE,
|
|
14
|
+
ENGINE_THRUST_TIER_PCT,
|
|
15
|
+
GENERATOR_CAPACITY_TIER_PCT,
|
|
16
|
+
GENERATOR_RECHARGE_TIER_PCT,
|
|
17
|
+
CRAFTER_SPEED_TIER_PCT,
|
|
18
|
+
BUILDER_SPEED_TIER_PCT,
|
|
19
|
+
WARP_RANGE_TIER_PCT,
|
|
20
|
+
LOADER_THRUST_TIER_PCT,
|
|
21
|
+
moduleTierPct,
|
|
12
22
|
} from './capabilities'
|
|
23
|
+
import {
|
|
24
|
+
computeEngineThrust,
|
|
25
|
+
computeGeneratorCap,
|
|
26
|
+
computeGeneratorRech,
|
|
27
|
+
computeCrafterSpeed,
|
|
28
|
+
computeBuilderSpeed,
|
|
29
|
+
computeWarpRange,
|
|
30
|
+
computeLoaderThrust,
|
|
31
|
+
} from '../nft/description'
|
|
13
32
|
import {applySlotMultiplier, U16_MAX} from '../entities/slot-multiplier'
|
|
14
33
|
import {encodeStats} from './crafting'
|
|
15
34
|
import {
|
|
@@ -147,7 +166,7 @@ test('computeEntityCapabilities emits crafterLanes alongside legacy crafter sum'
|
|
|
147
166
|
expect(result.crafterLanes!.length).toBe(1)
|
|
148
167
|
expect(result.crafterLanes![0].slotIndex).toBe(0)
|
|
149
168
|
|
|
150
|
-
const caps = computeCrafterCapabilities({fineness: 400, conductivity: 300})
|
|
169
|
+
const caps = computeCrafterCapabilities({fineness: 400, conductivity: 300}, 1)
|
|
151
170
|
const expectedSpeed = applySlotMultiplier(caps.speed, 120)
|
|
152
171
|
expect(result.crafterLanes![0].speed).toBe(expectedSpeed)
|
|
153
172
|
expect(result.crafterLanes![0].drain).toBe(caps.drain)
|
|
@@ -159,7 +178,7 @@ test('computeEntityCapabilities emits crafterLanes alongside legacy crafter sum'
|
|
|
159
178
|
})
|
|
160
179
|
|
|
161
180
|
test('builder capabilities read canonical resonance and fineness slots', () => {
|
|
162
|
-
expect(computeBuilderCapabilities({resonance: 500, fineness: 330})).toEqual({
|
|
181
|
+
expect(computeBuilderCapabilities({resonance: 500, fineness: 330}, 1)).toEqual({
|
|
163
182
|
speed: 500,
|
|
164
183
|
drain: 20_000,
|
|
165
184
|
})
|
|
@@ -189,7 +208,7 @@ test('computeEntityCapabilities emits loaderLanes alongside legacy loaders sum',
|
|
|
189
208
|
expect(result.loaderLanes!.length).toBe(1)
|
|
190
209
|
expect(result.loaderLanes![0].slotIndex).toBe(0)
|
|
191
210
|
|
|
192
|
-
const caps = computeLoaderCapabilities({insulation: 600, plasticity: 500})
|
|
211
|
+
const caps = computeLoaderCapabilities({insulation: 600, plasticity: 500}, 1)
|
|
193
212
|
// mass is unscaled (raw); thrust is amp-scaled
|
|
194
213
|
expect(result.loaderLanes![0].mass).toBe(caps.mass)
|
|
195
214
|
expect(result.loaderLanes![0].thrust).toBe(applySlotMultiplier(caps.thrust, 80))
|
|
@@ -206,7 +225,7 @@ test('per-lane amp-scaled stats clamp to UInt16, matching the contract clamp_to_
|
|
|
206
225
|
})
|
|
207
226
|
|
|
208
227
|
test('generator capacity and recharge are denominated to milli-energy', () => {
|
|
209
|
-
const caps = computeGeneratorCapabilities({resonance: 213, reflectivity: 213})
|
|
228
|
+
const caps = computeGeneratorCapabilities({resonance: 213, reflectivity: 213}, 1)
|
|
210
229
|
expect(caps.capacity).toBe(1_406_500)
|
|
211
230
|
expect(caps.recharge).toBe(3_278)
|
|
212
231
|
})
|
|
@@ -222,14 +241,14 @@ test('engine and generator capabilities use tapered quality consistently', () =>
|
|
|
222
241
|
]
|
|
223
242
|
|
|
224
243
|
const result = computeEntityCapabilities({}, ITEM_ROUSTABOUT_T1A_PACKED, modules, layout)
|
|
225
|
-
const engines = computeEngineCapabilities({volatility: 500, thermal: 500})
|
|
244
|
+
const engines = computeEngineCapabilities({volatility: 500, thermal: 500}, 1)
|
|
226
245
|
|
|
227
246
|
expect(result.engines).toEqual({
|
|
228
247
|
thrust: engines.thrust,
|
|
229
248
|
drain: computeTravelDrain(engines.thrust, computeEffectiveModuleStat(500)),
|
|
230
249
|
})
|
|
231
250
|
expect(result.generator).toEqual(
|
|
232
|
-
computeGeneratorCapabilities({resonance: 500, reflectivity: 500})
|
|
251
|
+
computeGeneratorCapabilities({resonance: 500, reflectivity: 500}, 1)
|
|
233
252
|
)
|
|
234
253
|
})
|
|
235
254
|
|
|
@@ -237,7 +256,7 @@ test('gatherer/crafter/hauler drains are denominated', () => {
|
|
|
237
256
|
expect(computeGathererCapabilities({strength: 0, hardness: 0, saturation: 213}, 1).drain).toBe(
|
|
238
257
|
1_967_500
|
|
239
258
|
)
|
|
240
|
-
expect(computeCrafterCapabilities({fineness: 0, conductivity: 213}).drain).toBe(23_546)
|
|
259
|
+
expect(computeCrafterCapabilities({fineness: 0, conductivity: 213}, 1).drain).toBe(23_546)
|
|
241
260
|
})
|
|
242
261
|
|
|
243
262
|
test('gatherer depth accepts canonical tolerance and legacy recipe-labelled hardness', () => {
|
|
@@ -248,3 +267,82 @@ test('gatherer depth accepts canonical tolerance and legacy recipe-labelled hard
|
|
|
248
267
|
1_565
|
|
249
268
|
)
|
|
250
269
|
})
|
|
270
|
+
|
|
271
|
+
describe('computeGathererYield', () => {
|
|
272
|
+
test('table is the k=0.2 integer-percent curve T1..T10', () => {
|
|
273
|
+
expect([...GATHERER_YIELD_TIER_TABLE]).toEqual([
|
|
274
|
+
100, 120, 140, 160, 180, 200, 220, 240, 260, 280,
|
|
275
|
+
])
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
test('T1 equals the base yield 200 + str (no change)', () => {
|
|
279
|
+
expect(computeGathererYield(500, 1)).toBe(700)
|
|
280
|
+
expect(computeGathererYield(0, 1)).toBe(200)
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
test('T2 scales base yield by 1.2, floored', () => {
|
|
284
|
+
expect(computeGathererYield(500, 2)).toBe(840) // floor(700 * 120 / 100)
|
|
285
|
+
expect(computeGathererYield(213, 2)).toBe(495) // floor(413 * 120 / 100) = floor(495.6)
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
test('T10 scales base yield by 2.8, floored', () => {
|
|
289
|
+
expect(computeGathererYield(500, 10)).toBe(1960) // 700 * 280 / 100
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
test('tier is clamped to [1, 10]', () => {
|
|
293
|
+
expect(computeGathererYield(500, 0)).toBe(700)
|
|
294
|
+
expect(computeGathererYield(500, 99)).toBe(1960)
|
|
295
|
+
})
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
describe('module tier tables', () => {
|
|
299
|
+
const ALL_TABLES = [
|
|
300
|
+
ENGINE_THRUST_TIER_PCT,
|
|
301
|
+
GENERATOR_CAPACITY_TIER_PCT,
|
|
302
|
+
GENERATOR_RECHARGE_TIER_PCT,
|
|
303
|
+
CRAFTER_SPEED_TIER_PCT,
|
|
304
|
+
BUILDER_SPEED_TIER_PCT,
|
|
305
|
+
WARP_RANGE_TIER_PCT,
|
|
306
|
+
LOADER_THRUST_TIER_PCT,
|
|
307
|
+
]
|
|
308
|
+
test('every table has 10 rows and T1 = 100', () => {
|
|
309
|
+
for (const table of ALL_TABLES) {
|
|
310
|
+
expect(table.length).toBe(10)
|
|
311
|
+
expect(table[0]).toBe(100)
|
|
312
|
+
}
|
|
313
|
+
})
|
|
314
|
+
test('moduleTierPct clamps out-of-range tiers', () => {
|
|
315
|
+
expect(moduleTierPct(ENGINE_THRUST_TIER_PCT, 0)).toBe(100)
|
|
316
|
+
expect(moduleTierPct(ENGINE_THRUST_TIER_PCT, 11)).toBe(280)
|
|
317
|
+
})
|
|
318
|
+
test('engine thrust tiers (stat 500 pre-effective)', () => {
|
|
319
|
+
expect(computeEngineThrust(500, 1)).toBe(775)
|
|
320
|
+
expect(computeEngineThrust(500, 2)).toBe(930)
|
|
321
|
+
expect(computeEngineThrust(500, 10)).toBe(2170)
|
|
322
|
+
})
|
|
323
|
+
test('generator capacity k=0.1, recharge k=0.2', () => {
|
|
324
|
+
expect(computeGeneratorCap(500, 1)).toBe(1_550_000)
|
|
325
|
+
expect(computeGeneratorCap(500, 2)).toBe(1_705_000)
|
|
326
|
+
expect(computeGeneratorCap(500, 10)).toBe(2_945_000)
|
|
327
|
+
expect(computeGeneratorRech(500, 1)).toBe(5000)
|
|
328
|
+
expect(computeGeneratorRech(500, 2)).toBe(6000)
|
|
329
|
+
expect(computeGeneratorRech(500, 10)).toBe(14000)
|
|
330
|
+
})
|
|
331
|
+
test('crafter/builder speed tiers', () => {
|
|
332
|
+
expect(computeCrafterSpeed(500, 1)).toBe(500)
|
|
333
|
+
expect(computeCrafterSpeed(500, 2)).toBe(600)
|
|
334
|
+
expect(computeBuilderSpeed(500, 2)).toBe(600)
|
|
335
|
+
expect(computeBuilderSpeed(500, 10)).toBe(1400)
|
|
336
|
+
})
|
|
337
|
+
test('warp range tiers', () => {
|
|
338
|
+
expect(computeWarpRange(500, 1)).toBe(1600)
|
|
339
|
+
expect(computeWarpRange(500, 2)).toBe(1920)
|
|
340
|
+
expect(computeWarpRange(500, 10)).toBe(4480)
|
|
341
|
+
})
|
|
342
|
+
test('loader thrust tiers floor correctly', () => {
|
|
343
|
+
expect(computeLoaderThrust(500, 1)).toBe(26)
|
|
344
|
+
expect(computeLoaderThrust(500, 2)).toBe(31)
|
|
345
|
+
expect(computeLoaderThrust(500, 10)).toBe(72)
|
|
346
|
+
expect(computeLoaderThrust(0, 2)).toBe(1)
|
|
347
|
+
})
|
|
348
|
+
})
|
|
@@ -39,29 +39,33 @@ export function computeShipHullCapabilities(
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
export function computeEngineCapabilities(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
} {
|
|
42
|
+
export function computeEngineCapabilities(
|
|
43
|
+
stats: Record<string, number>,
|
|
44
|
+
tier: number
|
|
45
|
+
): {thrust: number; drain: number} {
|
|
46
46
|
const vol = computeEffectiveModuleStat(stats.volatility)
|
|
47
47
|
const thm = computeEffectiveModuleStat(stats.thermal)
|
|
48
|
-
|
|
49
48
|
return {
|
|
50
|
-
thrust:
|
|
49
|
+
thrust: Math.floor(
|
|
50
|
+
((400 + Math.floor((vol * 3) / 4)) * moduleTierPct(ENGINE_THRUST_TIER_PCT, tier)) / 100
|
|
51
|
+
),
|
|
51
52
|
drain: 2 * Math.max(30, 50 - Math.floor(thm / 70)),
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
export function computeGeneratorCapabilities(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
} {
|
|
56
|
+
export function computeGeneratorCapabilities(
|
|
57
|
+
stats: Record<string, number>,
|
|
58
|
+
tier: number
|
|
59
|
+
): {capacity: number; recharge: number} {
|
|
59
60
|
const res = computeEffectiveModuleStat(stats.resonance)
|
|
60
61
|
const ref = computeEffectiveModuleStat(stats.reflectivity)
|
|
61
|
-
|
|
62
62
|
return {
|
|
63
|
-
capacity:
|
|
64
|
-
|
|
63
|
+
capacity: Math.floor(
|
|
64
|
+
((1_300_000 + res * 500) * moduleTierPct(GENERATOR_CAPACITY_TIER_PCT, tier)) / 100
|
|
65
|
+
),
|
|
66
|
+
recharge: Math.floor(
|
|
67
|
+
((2000 + ref * 6) * moduleTierPct(GENERATOR_RECHARGE_TIER_PCT, tier)) / 100
|
|
68
|
+
),
|
|
65
69
|
}
|
|
66
70
|
}
|
|
67
71
|
|
|
@@ -109,49 +113,55 @@ export function computeGathererCapabilities(
|
|
|
109
113
|
const tol = stats.tolerance ?? stats.hardness
|
|
110
114
|
|
|
111
115
|
return {
|
|
112
|
-
yield:
|
|
116
|
+
yield: computeGathererYield(str, tier),
|
|
113
117
|
drain: 2 * Math.max(250_000, 1_250_000 - con * 1250),
|
|
114
118
|
depth: gathererDepthForTier(tol, tier),
|
|
115
119
|
}
|
|
116
120
|
}
|
|
117
121
|
|
|
118
|
-
export function computeLoaderCapabilities(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
} {
|
|
122
|
+
export function computeLoaderCapabilities(
|
|
123
|
+
stats: Record<string, number>,
|
|
124
|
+
tier: number
|
|
125
|
+
): {mass: number; thrust: number; quantity: number} {
|
|
123
126
|
const insulation = stats.insulation
|
|
124
127
|
const plasticity = stats.plasticity
|
|
125
|
-
|
|
126
128
|
return {
|
|
127
129
|
mass: Math.max(200, 2000 - Math.floor(insulation * 2)),
|
|
128
|
-
thrust:
|
|
130
|
+
thrust: Math.floor(
|
|
131
|
+
((1 + Math.floor((plasticity * plasticity) / 10000)) *
|
|
132
|
+
moduleTierPct(LOADER_THRUST_TIER_PCT, tier)) /
|
|
133
|
+
100
|
|
134
|
+
),
|
|
129
135
|
quantity: 1,
|
|
130
136
|
}
|
|
131
137
|
}
|
|
132
138
|
|
|
133
|
-
export function computeCrafterCapabilities(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
} {
|
|
139
|
+
export function computeCrafterCapabilities(
|
|
140
|
+
stats: Record<string, number>,
|
|
141
|
+
tier: number
|
|
142
|
+
): {speed: number; drain: number} {
|
|
137
143
|
const fin = stats.fineness
|
|
138
144
|
const con = stats.conductivity
|
|
139
|
-
|
|
140
145
|
return {
|
|
141
|
-
speed:
|
|
146
|
+
speed: Math.floor(
|
|
147
|
+
((100 + Math.floor((fin * 4) / 5)) * moduleTierPct(CRAFTER_SPEED_TIER_PCT, tier)) / 100
|
|
148
|
+
),
|
|
142
149
|
drain: Math.max(5000, 30000 - Math.floor((con * 1000) / 33)),
|
|
143
150
|
}
|
|
144
151
|
}
|
|
145
152
|
|
|
146
|
-
export function computeBuilderCapabilities(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
} {
|
|
153
|
+
export function computeBuilderCapabilities(
|
|
154
|
+
stats: Record<string, number>,
|
|
155
|
+
tier: number
|
|
156
|
+
): {speed: number; drain: number} {
|
|
150
157
|
const resonance = stats.resonance
|
|
151
158
|
const fineness = stats.fineness
|
|
152
|
-
|
|
153
159
|
return {
|
|
154
|
-
speed:
|
|
160
|
+
speed: Math.floor(
|
|
161
|
+
((100 + Math.floor((resonance * 4) / 5)) *
|
|
162
|
+
moduleTierPct(BUILDER_SPEED_TIER_PCT, tier)) /
|
|
163
|
+
100
|
|
164
|
+
),
|
|
155
165
|
drain: Math.max(5000, 30000 - Math.floor((fineness * 1000) / 33)),
|
|
156
166
|
}
|
|
157
167
|
}
|
|
@@ -225,6 +235,7 @@ import {
|
|
|
225
235
|
ITEM_MASS_DRIVER_T1_PACKED,
|
|
226
236
|
ITEM_SHIP_T1_PACKED,
|
|
227
237
|
ITEM_WAREHOUSE_T1_PACKED,
|
|
238
|
+
ITEM_WORKSHOP_T1_PACKED,
|
|
228
239
|
} from '../data/item-ids'
|
|
229
240
|
import {
|
|
230
241
|
getModuleCapabilityType,
|
|
@@ -270,6 +281,33 @@ export function applyCapacityTier(baseCapacity: number, tier: number): number {
|
|
|
270
281
|
return clampUint32(Math.floor(baseCapacity * capacityTierMultiplier(tier)))
|
|
271
282
|
}
|
|
272
283
|
|
|
284
|
+
export const GATHERER_YIELD_TIER_TABLE = [100, 120, 140, 160, 180, 200, 220, 240, 260, 280] as const
|
|
285
|
+
|
|
286
|
+
export const MODULE_MAX_TIER = 10
|
|
287
|
+
|
|
288
|
+
export const ENGINE_THRUST_TIER_PCT = [100, 120, 140, 160, 180, 200, 220, 240, 260, 280] as const
|
|
289
|
+
export const GENERATOR_CAPACITY_TIER_PCT = [
|
|
290
|
+
100, 110, 120, 130, 140, 150, 160, 170, 180, 190,
|
|
291
|
+
] as const
|
|
292
|
+
export const GENERATOR_RECHARGE_TIER_PCT = [
|
|
293
|
+
100, 120, 140, 160, 180, 200, 220, 240, 260, 280,
|
|
294
|
+
] as const
|
|
295
|
+
export const CRAFTER_SPEED_TIER_PCT = [100, 120, 140, 160, 180, 200, 220, 240, 260, 280] as const
|
|
296
|
+
export const BUILDER_SPEED_TIER_PCT = [100, 120, 140, 160, 180, 200, 220, 240, 260, 280] as const
|
|
297
|
+
export const WARP_RANGE_TIER_PCT = [100, 120, 140, 160, 180, 200, 220, 240, 260, 280] as const
|
|
298
|
+
export const LOADER_THRUST_TIER_PCT = [100, 120, 140, 160, 180, 200, 220, 240, 260, 280] as const
|
|
299
|
+
|
|
300
|
+
export function moduleTierPct(table: readonly number[], tier: number): number {
|
|
301
|
+
const clamped = Math.min(Math.max(tier, 1), MODULE_MAX_TIER)
|
|
302
|
+
return table[clamped - 1]
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export function computeGathererYield(str: number, tier: number): number {
|
|
306
|
+
const clampedTier = Math.min(Math.max(tier, 1), GATHERER_YIELD_TIER_TABLE.length)
|
|
307
|
+
const pct = GATHERER_YIELD_TIER_TABLE[clampedTier - 1]
|
|
308
|
+
return Math.floor(((200 + str) * pct) / 100)
|
|
309
|
+
}
|
|
310
|
+
|
|
273
311
|
export function computeBaseCapacity(itemId: number, stats: Record<string, number>): number {
|
|
274
312
|
let base: number
|
|
275
313
|
if (isShipHull(itemId)) {
|
|
@@ -288,6 +326,9 @@ export function computeBaseCapacity(itemId: number, stats: Record<string, number
|
|
|
288
326
|
case ITEM_WAREHOUSE_T1_PACKED:
|
|
289
327
|
base = computeWarehouseHullCapabilities(stats).capacity
|
|
290
328
|
break
|
|
329
|
+
case ITEM_WORKSHOP_T1_PACKED:
|
|
330
|
+
base = computeWorkshopHullCapabilities(stats).capacity
|
|
331
|
+
break
|
|
291
332
|
default:
|
|
292
333
|
return 0
|
|
293
334
|
}
|
|
@@ -295,11 +336,16 @@ export function computeBaseCapacity(itemId: number, stats: Record<string, number
|
|
|
295
336
|
return applyCapacityTier(base, getItem(itemId).tier)
|
|
296
337
|
}
|
|
297
338
|
|
|
298
|
-
export function computeWarpCapabilities(
|
|
299
|
-
|
|
300
|
-
|
|
339
|
+
export function computeWarpCapabilities(
|
|
340
|
+
stats: Record<string, number>,
|
|
341
|
+
tier: number
|
|
342
|
+
): {range: number} {
|
|
301
343
|
const reflectivity = stats.reflectivity
|
|
302
|
-
return {
|
|
344
|
+
return {
|
|
345
|
+
range: Math.floor(
|
|
346
|
+
((100 + reflectivity * 3) * moduleTierPct(WARP_RANGE_TIER_PCT, tier)) / 100
|
|
347
|
+
),
|
|
348
|
+
}
|
|
303
349
|
}
|
|
304
350
|
|
|
305
351
|
export function computeWarehouseHullCapabilities(stats: Record<string, number>): {
|
|
@@ -314,6 +360,18 @@ export function computeWarehouseHullCapabilities(stats: Record<string, number>):
|
|
|
314
360
|
}
|
|
315
361
|
}
|
|
316
362
|
|
|
363
|
+
export function computeWorkshopHullCapabilities(stats: Record<string, number>): {
|
|
364
|
+
hullmass: number
|
|
365
|
+
capacity: number
|
|
366
|
+
} {
|
|
367
|
+
const statSum = (stats.strength ?? 0) + (stats.hardness ?? 0)
|
|
368
|
+
const exponent = statSum / 1998.0
|
|
369
|
+
return {
|
|
370
|
+
hullmass: computeBaseHullmass(ITEM_WORKSHOP_T1_PACKED, stats),
|
|
371
|
+
capacity: Math.floor(5000000 * 6 ** exponent),
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
317
375
|
export interface GathererLaneEntry {
|
|
318
376
|
slotIndex: number
|
|
319
377
|
yield: number
|
|
@@ -442,13 +500,13 @@ export function computeEntityCapabilities(
|
|
|
442
500
|
|
|
443
501
|
if (modType === MODULE_ENGINE) {
|
|
444
502
|
hasEngine = true
|
|
445
|
-
const caps = computeEngineCapabilities(decodedStats)
|
|
503
|
+
const caps = computeEngineCapabilities(decodedStats, item.tier)
|
|
446
504
|
totalThrust += applySlotMultiplier(caps.thrust, amp)
|
|
447
505
|
totalEngineThm += computeEffectiveModuleStat(decodedStats.thermal ?? 0)
|
|
448
506
|
engineCount += 1
|
|
449
507
|
} else if (modType === MODULE_GENERATOR) {
|
|
450
508
|
hasGenerator = true
|
|
451
|
-
const caps = computeGeneratorCapabilities(decodedStats)
|
|
509
|
+
const caps = computeGeneratorCapabilities(decodedStats, item.tier)
|
|
452
510
|
totalGenCapacity += applySlotMultiplierUint32(caps.capacity, amp)
|
|
453
511
|
totalGenRecharge += applySlotMultiplierUint32(caps.recharge, amp)
|
|
454
512
|
} else if (modType === MODULE_GATHERER) {
|
|
@@ -475,7 +533,7 @@ export function computeEntityCapabilities(
|
|
|
475
533
|
})
|
|
476
534
|
} else if (modType === MODULE_LOADER) {
|
|
477
535
|
hasLoader = true
|
|
478
|
-
const caps = computeLoaderCapabilities(decodedStats)
|
|
536
|
+
const caps = computeLoaderCapabilities(decodedStats, item.tier)
|
|
479
537
|
totalLoaderMass += caps.mass
|
|
480
538
|
totalLoaderThrust += applySlotMultiplier(caps.thrust, amp)
|
|
481
539
|
totalLoaderQuantity += caps.quantity
|
|
@@ -491,7 +549,7 @@ export function computeEntityCapabilities(
|
|
|
491
549
|
totalCargoHoldDrain += caps.drain
|
|
492
550
|
} else if (modType === MODULE_CRAFTER) {
|
|
493
551
|
hasCrafter = true
|
|
494
|
-
const caps = computeCrafterCapabilities(decodedStats)
|
|
552
|
+
const caps = computeCrafterCapabilities(decodedStats, item.tier)
|
|
495
553
|
const scaledSpeed = applySlotMultiplier(caps.speed, amp)
|
|
496
554
|
totalCrafterSpeed += scaledSpeed
|
|
497
555
|
totalCrafterDrain += caps.drain
|
|
@@ -503,7 +561,7 @@ export function computeEntityCapabilities(
|
|
|
503
561
|
})
|
|
504
562
|
} else if (modType === MODULE_BUILDER) {
|
|
505
563
|
hasBuilder = true
|
|
506
|
-
const caps = computeBuilderCapabilities(decodedStats)
|
|
564
|
+
const caps = computeBuilderCapabilities(decodedStats, item.tier)
|
|
507
565
|
const scaledSpeed = applySlotMultiplier(caps.speed, amp)
|
|
508
566
|
totalBuilderSpeed += scaledSpeed
|
|
509
567
|
totalBuilderDrain += caps.drain
|
|
@@ -523,7 +581,7 @@ export function computeEntityCapabilities(
|
|
|
523
581
|
totalHaulerDrain += caps.drain
|
|
524
582
|
} else if (modType === MODULE_WARP) {
|
|
525
583
|
hasWarp = true
|
|
526
|
-
const caps = computeWarpCapabilities(decodedStats)
|
|
584
|
+
const caps = computeWarpCapabilities(decodedStats, item.tier)
|
|
527
585
|
totalWarpRange += applySlotMultiplier(caps.range, amp)
|
|
528
586
|
} else if (modType === MODULE_LAUNCHER) {
|
|
529
587
|
hasLauncher = true
|