@shipload/sdk 1.0.0-next.38 → 1.0.0-next.39
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 +86 -3
- package/lib/shipload.js +536 -198
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +527 -198
- package/lib/shipload.m.js.map +1 -1
- package/lib/testing.d.ts +15 -0
- package/lib/testing.js +71 -1
- package/lib/testing.js.map +1 -1
- package/lib/testing.m.js +71 -1
- package/lib/testing.m.js.map +1 -1
- package/package.json +1 -1
- package/src/capabilities/modules.ts +3 -0
- package/src/contracts/server.ts +30 -1
- package/src/data/entities.json +30 -0
- package/src/data/item-ids.ts +3 -0
- package/src/data/items.json +19 -0
- package/src/data/kind-registry.json +24 -0
- package/src/data/kind-registry.ts +15 -0
- package/src/data/metadata.ts +18 -0
- package/src/derivation/capabilities.test.ts +18 -0
- package/src/derivation/capabilities.ts +50 -4
- package/src/entities/makers.ts +7 -0
- package/src/index-module.ts +13 -1
- package/src/managers/actions.ts +176 -1
- package/src/nft/description.ts +10 -0
- package/src/resolution/resolve-item.test.ts +37 -0
- package/src/resolution/resolve-item.ts +6 -1
- package/src/scheduling/projection.ts +19 -0
- package/src/types/capabilities.ts +5 -0
- package/src/types.ts +1 -0
|
@@ -131,6 +131,17 @@ export function computeHaulerCapabilities(stats: Record<string, number>): {
|
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
export function computeLauncherCapabilities(
|
|
135
|
+
stats: {charge_rate: number; velocity: number; drain: number},
|
|
136
|
+
amp = 100
|
|
137
|
+
): {chargeRate: number; velocity: number; drain: number} {
|
|
138
|
+
return {
|
|
139
|
+
chargeRate: Math.floor((stats.charge_rate * amp) / 100),
|
|
140
|
+
velocity: Math.floor((stats.velocity * amp) / 100),
|
|
141
|
+
drain: stats.drain,
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
134
145
|
export function computeStorageCapabilities(stats: Record<string, number>): {
|
|
135
146
|
capacity: number
|
|
136
147
|
} {
|
|
@@ -160,6 +171,8 @@ import {
|
|
|
160
171
|
ITEM_CONTAINER_T2_PACKED,
|
|
161
172
|
ITEM_EXTRACTOR_T1_PACKED,
|
|
162
173
|
ITEM_FACTORY_T1_PACKED,
|
|
174
|
+
ITEM_MASS_CATCHER_T1_PACKED,
|
|
175
|
+
ITEM_MASS_DRIVER_T1_PACKED,
|
|
163
176
|
ITEM_SHIP_T1_PACKED,
|
|
164
177
|
ITEM_WAREHOUSE_T1_PACKED,
|
|
165
178
|
} from '../data/item-ids'
|
|
@@ -174,9 +187,10 @@ import {
|
|
|
174
187
|
MODULE_CRAFTER,
|
|
175
188
|
MODULE_HAULER,
|
|
176
189
|
MODULE_WARP,
|
|
190
|
+
MODULE_LAUNCHER,
|
|
177
191
|
} from '../capabilities/modules'
|
|
178
192
|
import {getItem} from '../data/catalog'
|
|
179
|
-
import {decodeCraftedItemStats} from './crafting'
|
|
193
|
+
import {decodeCraftedItemStats, decodeStat} from './crafting'
|
|
180
194
|
import {
|
|
181
195
|
applySlotMultiplier,
|
|
182
196
|
applySlotMultiplierUint32,
|
|
@@ -186,6 +200,7 @@ import {
|
|
|
186
200
|
type InstalledModule,
|
|
187
201
|
} from '../entities/slot-multiplier'
|
|
188
202
|
import type {EntitySlot} from '../data/recipes-runtime'
|
|
203
|
+
import {computeTravelDrain} from '../nft/description'
|
|
189
204
|
|
|
190
205
|
export function computeBaseCapacity(itemId: number, stats: Record<string, number>): number {
|
|
191
206
|
switch (itemId) {
|
|
@@ -193,6 +208,8 @@ export function computeBaseCapacity(itemId: number, stats: Record<string, number
|
|
|
193
208
|
return computeShipHullCapabilities(stats).capacity
|
|
194
209
|
case ITEM_EXTRACTOR_T1_PACKED:
|
|
195
210
|
case ITEM_FACTORY_T1_PACKED:
|
|
211
|
+
case ITEM_MASS_DRIVER_T1_PACKED:
|
|
212
|
+
case ITEM_MASS_CATCHER_T1_PACKED:
|
|
196
213
|
case ITEM_CONTAINER_T1_PACKED:
|
|
197
214
|
return computeContainerCapabilities(stats).capacity
|
|
198
215
|
case ITEM_WAREHOUSE_T1_PACKED:
|
|
@@ -258,6 +275,7 @@ export interface ComputedCapabilities {
|
|
|
258
275
|
crafterLanes?: CrafterLaneEntry[]
|
|
259
276
|
hauler?: {capacity: number; efficiency: number; drain: number}
|
|
260
277
|
warp?: {range: number}
|
|
278
|
+
launcher?: {chargeRate: number; velocity: number; drain: number}
|
|
261
279
|
}
|
|
262
280
|
|
|
263
281
|
export function computeEntityCapabilities(
|
|
@@ -267,7 +285,8 @@ export function computeEntityCapabilities(
|
|
|
267
285
|
layout: EntitySlot[]
|
|
268
286
|
): ComputedCapabilities {
|
|
269
287
|
let totalThrust = 0
|
|
270
|
-
let
|
|
288
|
+
let totalEngineThm = 0
|
|
289
|
+
let engineCount = 0
|
|
271
290
|
let hasEngine = false
|
|
272
291
|
|
|
273
292
|
let totalGenCapacity = 0
|
|
@@ -300,6 +319,11 @@ export function computeEntityCapabilities(
|
|
|
300
319
|
let totalWarpRange = 0
|
|
301
320
|
let hasWarp = false
|
|
302
321
|
|
|
322
|
+
let totalLauncherChargeRate = 0
|
|
323
|
+
let totalLauncherVelocity = 0
|
|
324
|
+
let totalLauncherDrain = 0
|
|
325
|
+
let hasLauncher = false
|
|
326
|
+
|
|
303
327
|
let totalBatteryCapacity = 0
|
|
304
328
|
|
|
305
329
|
const gathererLanes: GathererLaneEntry[] = []
|
|
@@ -317,7 +341,8 @@ export function computeEntityCapabilities(
|
|
|
317
341
|
hasEngine = true
|
|
318
342
|
const caps = computeEngineCapabilities(decodedStats)
|
|
319
343
|
totalThrust += applySlotMultiplier(caps.thrust, amp)
|
|
320
|
-
|
|
344
|
+
totalEngineThm += decodedStats.thermal ?? 0
|
|
345
|
+
engineCount += 1
|
|
321
346
|
} else if (modType === MODULE_GENERATOR) {
|
|
322
347
|
hasGenerator = true
|
|
323
348
|
const caps = computeGeneratorCapabilities(decodedStats)
|
|
@@ -376,6 +401,19 @@ export function computeEntityCapabilities(
|
|
|
376
401
|
hasWarp = true
|
|
377
402
|
const caps = computeWarpCapabilities(decodedStats)
|
|
378
403
|
totalWarpRange += applySlotMultiplier(caps.range, amp)
|
|
404
|
+
} else if (modType === MODULE_LAUNCHER) {
|
|
405
|
+
hasLauncher = true
|
|
406
|
+
const caps = computeLauncherCapabilities(
|
|
407
|
+
{
|
|
408
|
+
charge_rate: decodedStats.charge_rate ?? decodeStat(mod.stats, 0),
|
|
409
|
+
velocity: decodedStats.velocity ?? decodeStat(mod.stats, 1),
|
|
410
|
+
drain: decodedStats.drain ?? decodeStat(mod.stats, 2),
|
|
411
|
+
},
|
|
412
|
+
amp
|
|
413
|
+
)
|
|
414
|
+
totalLauncherChargeRate = clampUint16(totalLauncherChargeRate + caps.chargeRate)
|
|
415
|
+
totalLauncherVelocity = clampUint16(totalLauncherVelocity + caps.velocity)
|
|
416
|
+
totalLauncherDrain = clampUint16(totalLauncherDrain + caps.drain)
|
|
379
417
|
} else if (modType === MODULE_BATTERY) {
|
|
380
418
|
const caps = computeBatteryCapabilities(decodedStats)
|
|
381
419
|
totalBatteryCapacity += applySlotMultiplierUint32(caps.capacity, amp)
|
|
@@ -392,7 +430,8 @@ export function computeEntityCapabilities(
|
|
|
392
430
|
}
|
|
393
431
|
|
|
394
432
|
if (hasEngine) {
|
|
395
|
-
|
|
433
|
+
const avgThm = engineCount > 0 ? Math.trunc(totalEngineThm / engineCount) : 0
|
|
434
|
+
result.engines = {thrust: totalThrust, drain: computeTravelDrain(totalThrust, avgThm)}
|
|
396
435
|
}
|
|
397
436
|
if (hasGenerator) {
|
|
398
437
|
result.generator = {
|
|
@@ -432,6 +471,13 @@ export function computeEntityCapabilities(
|
|
|
432
471
|
if (hasWarp) {
|
|
433
472
|
result.warp = {range: totalWarpRange}
|
|
434
473
|
}
|
|
474
|
+
if (hasLauncher) {
|
|
475
|
+
result.launcher = {
|
|
476
|
+
chargeRate: totalLauncherChargeRate,
|
|
477
|
+
velocity: totalLauncherVelocity,
|
|
478
|
+
drain: totalLauncherDrain,
|
|
479
|
+
}
|
|
480
|
+
}
|
|
435
481
|
|
|
436
482
|
return result
|
|
437
483
|
}
|
package/src/entities/makers.ts
CHANGED
|
@@ -142,6 +142,13 @@ export function makeEntity(packedItemId: number, state: EntityStateInput): Entit
|
|
|
142
142
|
if (caps.generator) info.generator = caps.generator
|
|
143
143
|
if (caps.hauler) info.hauler = caps.hauler
|
|
144
144
|
if (caps.warp) info.warp = caps.warp
|
|
145
|
+
if (caps.launcher) {
|
|
146
|
+
info.launcher = ServerContract.Types.launcher_stats.from({
|
|
147
|
+
charge_rate: caps.launcher.chargeRate,
|
|
148
|
+
velocity: caps.launcher.velocity,
|
|
149
|
+
drain: caps.launcher.drain,
|
|
150
|
+
})
|
|
151
|
+
}
|
|
145
152
|
|
|
146
153
|
info.gatherer_lanes = (caps.gathererLanes ?? []).map((l) =>
|
|
147
154
|
ServerContract.Types.gatherer_lane.from({
|
package/src/index-module.ts
CHANGED
|
@@ -62,7 +62,14 @@ export type {
|
|
|
62
62
|
ScheduledBuild,
|
|
63
63
|
Reservation,
|
|
64
64
|
} from './managers'
|
|
65
|
-
export type {
|
|
65
|
+
export type {
|
|
66
|
+
EntityRefInput,
|
|
67
|
+
LaunchNumericInput,
|
|
68
|
+
LaunchQuote,
|
|
69
|
+
LaunchQuoteCatcher,
|
|
70
|
+
LaunchQuoteLauncher,
|
|
71
|
+
LaunchStatsInput,
|
|
72
|
+
} from './managers/actions'
|
|
66
73
|
export type {WrapDeposit} from './managers/nft'
|
|
67
74
|
export {resolveLockedAmount} from './managers/nft'
|
|
68
75
|
|
|
@@ -383,6 +390,7 @@ export {
|
|
|
383
390
|
computeContainerCapabilities,
|
|
384
391
|
computeContainerT2Capabilities,
|
|
385
392
|
computeWarpCapabilities,
|
|
393
|
+
computeLauncherCapabilities,
|
|
386
394
|
computeBaseCapacity,
|
|
387
395
|
computeEntityCapabilities,
|
|
388
396
|
GATHERER_DEPTH_TABLE,
|
|
@@ -485,6 +493,10 @@ export {
|
|
|
485
493
|
computeBaseCapacityWarehouse,
|
|
486
494
|
computeEngineThrust,
|
|
487
495
|
computeEngineDrain,
|
|
496
|
+
computeTravelDrain,
|
|
497
|
+
ENGINE_DRAIN_BASE,
|
|
498
|
+
ENGINE_DRAIN_REF_THRUST,
|
|
499
|
+
ENGINE_DRAIN_REF_THM,
|
|
488
500
|
computeGeneratorCap,
|
|
489
501
|
computeGeneratorRech,
|
|
490
502
|
computeGathererYield,
|
package/src/managers/actions.ts
CHANGED
|
@@ -17,9 +17,144 @@ import {
|
|
|
17
17
|
type UInt64Type,
|
|
18
18
|
} from '@wharfkit/antelope'
|
|
19
19
|
import {BaseManager} from './base'
|
|
20
|
-
import type
|
|
20
|
+
import {Coordinates, PRECISION, type CoordinatesType} from '../types'
|
|
21
21
|
import {ServerContract} from '../contracts'
|
|
22
22
|
import {ATOMICASSETS_ABI, SHIPLOAD_COLLECTION} from '../nft/atomicassets'
|
|
23
|
+
import {getItem} from '../data/catalog'
|
|
24
|
+
|
|
25
|
+
const CHARGE_K = 1n
|
|
26
|
+
const ENERGY_DIVISOR = 1_000_000n
|
|
27
|
+
const UINT32_MAX = 4_294_967_295
|
|
28
|
+
const UINT32_MAX_BIGINT = 4_294_967_295n
|
|
29
|
+
const UINT32_MOD = 4_294_967_296n
|
|
30
|
+
const UINT64_MAX = 18_446_744_073_709_551_615n
|
|
31
|
+
const PRECISION_BIGINT = BigInt(PRECISION)
|
|
32
|
+
|
|
33
|
+
export type LaunchNumericInput =
|
|
34
|
+
| number
|
|
35
|
+
| bigint
|
|
36
|
+
| string
|
|
37
|
+
| {toNumber(): number}
|
|
38
|
+
| {toString(): string}
|
|
39
|
+
|
|
40
|
+
export interface LaunchStatsInput {
|
|
41
|
+
charge_rate?: LaunchNumericInput
|
|
42
|
+
chargeRate?: LaunchNumericInput
|
|
43
|
+
velocity: LaunchNumericInput
|
|
44
|
+
drain: LaunchNumericInput
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface LaunchQuoteLauncher {
|
|
48
|
+
coordinates: CoordinatesType
|
|
49
|
+
launcher: LaunchStatsInput
|
|
50
|
+
generator?: {capacity: LaunchNumericInput}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface LaunchQuoteCatcher {
|
|
54
|
+
coordinates: CoordinatesType
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface LaunchQuote {
|
|
58
|
+
chargeTime: number
|
|
59
|
+
flightTime: number
|
|
60
|
+
arrival: Date
|
|
61
|
+
energyCost: number
|
|
62
|
+
maxReach: bigint
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function toNumber(value: LaunchNumericInput): number {
|
|
66
|
+
if (typeof value === 'number') return Math.trunc(value)
|
|
67
|
+
if (typeof value === 'bigint') return Number(value)
|
|
68
|
+
if (typeof value === 'string') return Number(value)
|
|
69
|
+
if ('toNumber' in value && typeof value.toNumber === 'function') return value.toNumber()
|
|
70
|
+
return Number(value.toString())
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function requiredNumber(value: LaunchNumericInput | undefined, label: string): number {
|
|
74
|
+
if (value === undefined) throw new Error(`launch quote requires ${label}`)
|
|
75
|
+
return toNumber(value)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function toBigInt(value: LaunchNumericInput | undefined): bigint {
|
|
79
|
+
if (value === undefined) return 0n
|
|
80
|
+
if (typeof value === 'bigint') return value
|
|
81
|
+
if (typeof value === 'number') return BigInt(Math.trunc(value))
|
|
82
|
+
if (typeof value === 'string') return BigInt(value)
|
|
83
|
+
return BigInt(value.toString())
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function saturatingMul(lhs: bigint, rhs: bigint): bigint {
|
|
87
|
+
if (lhs !== 0n && rhs > UINT64_MAX / lhs) {
|
|
88
|
+
return UINT64_MAX
|
|
89
|
+
}
|
|
90
|
+
return lhs * rhs
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function clampLaunchResult(value: bigint): number {
|
|
94
|
+
if (value < 1n) return 1
|
|
95
|
+
if (value > UINT32_MAX_BIGINT) return UINT32_MAX
|
|
96
|
+
return Number(value)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function toUint32(value: bigint): bigint {
|
|
100
|
+
return value % UINT32_MOD
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function calcDistance(origin: CoordinatesType, destination: CoordinatesType): bigint {
|
|
104
|
+
const a = Coordinates.from(origin)
|
|
105
|
+
const b = Coordinates.from(destination)
|
|
106
|
+
const dx = toNumber(a.x) - toNumber(b.x)
|
|
107
|
+
const dy = toNumber(a.y) - toNumber(b.y)
|
|
108
|
+
return BigInt(Math.trunc(Math.sqrt(dx * dx + dy * dy) * PRECISION))
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function calcCargoItemMassUint32(item: ServerContract.ActionParams.Type.cargo_item): bigint {
|
|
112
|
+
let mass = toUint32(BigInt(getItem(item.item_id).mass) * toUint32(toBigInt(item.quantity)))
|
|
113
|
+
|
|
114
|
+
for (const mod of item.modules) {
|
|
115
|
+
if (mod.installed) {
|
|
116
|
+
mass = toUint32(mass + BigInt(getItem(mod.installed.item_id).mass))
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return mass
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function calcPayloadMass(items: ServerContract.ActionParams.Type.cargo_item[]): bigint {
|
|
124
|
+
let mass = 0n
|
|
125
|
+
for (const item of items) {
|
|
126
|
+
mass = toUint32(mass + calcCargoItemMassUint32(item))
|
|
127
|
+
}
|
|
128
|
+
return mass
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function calcChargeTime(chargeRate: number, mass: bigint): number {
|
|
132
|
+
const rate = BigInt(chargeRate || 1)
|
|
133
|
+
return clampLaunchResult((mass * CHARGE_K) / rate)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function calcFlightTime(velocity: number, distance: bigint): number {
|
|
137
|
+
const v = BigInt(velocity || 1)
|
|
138
|
+
return clampLaunchResult(distance / (v * PRECISION_BIGINT))
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function calcLaunchEnergy(drain: number, mass: bigint, distance: bigint): number {
|
|
142
|
+
const e =
|
|
143
|
+
saturatingMul(saturatingMul(mass, distance / PRECISION_BIGINT), BigInt(drain)) /
|
|
144
|
+
ENERGY_DIVISOR
|
|
145
|
+
return clampLaunchResult(e)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function calcMaxReach(energyBudget: bigint, mass: bigint, drain: number): bigint {
|
|
149
|
+
if (energyBudget < 1n) return 0n
|
|
150
|
+
if (energyBudget >= UINT32_MAX_BIGINT || mass === 0n || drain === 0) return UINT64_MAX
|
|
151
|
+
|
|
152
|
+
const numerator = (energyBudget + 1n) * ENERGY_DIVISOR - 1n
|
|
153
|
+
const denominator = mass * BigInt(drain)
|
|
154
|
+
const distanceUnits = numerator / denominator
|
|
155
|
+
const maxDistance = distanceUnits * PRECISION_BIGINT + (PRECISION_BIGINT - 1n)
|
|
156
|
+
return maxDistance > UINT64_MAX ? UINT64_MAX : maxDistance
|
|
157
|
+
}
|
|
23
158
|
|
|
24
159
|
export type EntityRefInput = {
|
|
25
160
|
entityType: NameType
|
|
@@ -166,6 +301,46 @@ export class ActionsManager extends BaseManager {
|
|
|
166
301
|
})
|
|
167
302
|
}
|
|
168
303
|
|
|
304
|
+
launch(
|
|
305
|
+
launcherId: UInt64Type,
|
|
306
|
+
catcherId: UInt64Type,
|
|
307
|
+
items: ServerContract.ActionParams.Type.cargo_item[]
|
|
308
|
+
): Action {
|
|
309
|
+
return this.server.action('launch', {
|
|
310
|
+
launcher_id: UInt64.from(launcherId),
|
|
311
|
+
catcher_id: UInt64.from(catcherId),
|
|
312
|
+
items,
|
|
313
|
+
})
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
getLaunchQuote(
|
|
317
|
+
launcher: LaunchQuoteLauncher,
|
|
318
|
+
catcher: LaunchQuoteCatcher,
|
|
319
|
+
items: ServerContract.ActionParams.Type.cargo_item[],
|
|
320
|
+
start = new Date()
|
|
321
|
+
): LaunchQuote {
|
|
322
|
+
const chargeRate = requiredNumber(
|
|
323
|
+
launcher.launcher.charge_rate ?? launcher.launcher.chargeRate,
|
|
324
|
+
'launcher charge rate'
|
|
325
|
+
)
|
|
326
|
+
const velocity = requiredNumber(launcher.launcher.velocity, 'launcher velocity')
|
|
327
|
+
const drain = requiredNumber(launcher.launcher.drain, 'launcher drain')
|
|
328
|
+
const mass = calcPayloadMass(items)
|
|
329
|
+
const distance = calcDistance(launcher.coordinates, catcher.coordinates)
|
|
330
|
+
const chargeTime = calcChargeTime(chargeRate, mass)
|
|
331
|
+
const flightTime = calcFlightTime(velocity, distance)
|
|
332
|
+
const energyCost = calcLaunchEnergy(drain, mass, distance)
|
|
333
|
+
const maxReach = calcMaxReach(toBigInt(launcher.generator?.capacity), mass, drain)
|
|
334
|
+
|
|
335
|
+
return {
|
|
336
|
+
chargeTime,
|
|
337
|
+
flightTime,
|
|
338
|
+
arrival: new Date(start.getTime() + (chargeTime + flightTime) * 1000),
|
|
339
|
+
energyCost,
|
|
340
|
+
maxReach,
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
169
344
|
foundCompany(account: NameType, name: string): Action {
|
|
170
345
|
return this.platform.action('foundcompany', {
|
|
171
346
|
account: Name.from(account),
|
package/src/nft/description.ts
CHANGED
|
@@ -62,6 +62,16 @@ export function computeBaseCapacityWarehouse(stats: bigint): number {
|
|
|
62
62
|
|
|
63
63
|
export const computeEngineThrust = (vol: number): number => 400 + idiv(vol * 3, 4)
|
|
64
64
|
export const computeEngineDrain = (thm: number): number => 2 * Math.max(30, 50 - idiv(thm, 70))
|
|
65
|
+
export const ENGINE_DRAIN_BASE = 118
|
|
66
|
+
export const ENGINE_DRAIN_REF_THRUST = 775
|
|
67
|
+
export const ENGINE_DRAIN_REF_THM = 500
|
|
68
|
+
|
|
69
|
+
export const computeTravelDrain = (totalThrust: number, avgThm: number): number => {
|
|
70
|
+
if (totalThrust <= 0) return 0
|
|
71
|
+
const num = ENGINE_DRAIN_BASE * ENGINE_DRAIN_REF_THRUST * computeEngineDrain(avgThm)
|
|
72
|
+
const den = totalThrust * computeEngineDrain(ENGINE_DRAIN_REF_THM)
|
|
73
|
+
return idiv(num, den)
|
|
74
|
+
}
|
|
65
75
|
export const computeGeneratorCap = (com: number): number => 950 + idiv(com, 2)
|
|
66
76
|
export const computeGeneratorRech = (fin: number): number => 2 * (1 + idiv(fin * 3, 1000))
|
|
67
77
|
export const computeGathererYield = (str: number): number => 200 + str
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {expect, test} from 'bun:test'
|
|
2
|
+
import {UInt16, UInt64} from '@wharfkit/antelope'
|
|
3
|
+
import {resolveItem} from './resolve-item'
|
|
4
|
+
import {encodeStats} from '../derivation/crafting'
|
|
5
|
+
import {computeContainerCapabilities} from '../derivation/capabilities'
|
|
6
|
+
import {
|
|
7
|
+
ITEM_EXTRACTOR_T1_PACKED,
|
|
8
|
+
ITEM_FACTORY_T1_PACKED,
|
|
9
|
+
ITEM_MASS_DRIVER_T1_PACKED,
|
|
10
|
+
ITEM_MASS_CATCHER_T1_PACKED,
|
|
11
|
+
} from '../data/item-ids'
|
|
12
|
+
|
|
13
|
+
function hullStats(strength: number, density: number, hardness: number): UInt64 {
|
|
14
|
+
return UInt64.from(encodeStats([strength, density, hardness]).toString())
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const CONTAINER_ENTITIES = [
|
|
18
|
+
['factory', ITEM_FACTORY_T1_PACKED],
|
|
19
|
+
['extractor', ITEM_EXTRACTOR_T1_PACKED],
|
|
20
|
+
['mass driver', ITEM_MASS_DRIVER_T1_PACKED],
|
|
21
|
+
['mass catcher', ITEM_MASS_CATCHER_T1_PACKED],
|
|
22
|
+
] as const
|
|
23
|
+
|
|
24
|
+
for (const [label, itemId] of CONTAINER_ENTITIES) {
|
|
25
|
+
test(`resolveItem resolves ${label} hull capacity via container formula`, () => {
|
|
26
|
+
const stats = hullStats(300, 100, 400)
|
|
27
|
+
const resolved = resolveItem(UInt16.from(itemId), stats)
|
|
28
|
+
const hull = resolved.attributes?.find((g) => g.capability === 'Hull')
|
|
29
|
+
const capacity = hull?.attributes.find((a) => a.label === 'Capacity')?.value
|
|
30
|
+
const expected = computeContainerCapabilities({
|
|
31
|
+
strength: 300,
|
|
32
|
+
hardness: 400,
|
|
33
|
+
density: 100,
|
|
34
|
+
}).capacity
|
|
35
|
+
expect(capacity).toBe(expected)
|
|
36
|
+
})
|
|
37
|
+
}
|
|
@@ -39,6 +39,9 @@ import {
|
|
|
39
39
|
ITEM_CONTAINER_T1_PACKED,
|
|
40
40
|
ITEM_CONTAINER_T2_PACKED,
|
|
41
41
|
ITEM_EXTRACTOR_T1_PACKED,
|
|
42
|
+
ITEM_FACTORY_T1_PACKED,
|
|
43
|
+
ITEM_MASS_CATCHER_T1_PACKED,
|
|
44
|
+
ITEM_MASS_DRIVER_T1_PACKED,
|
|
42
45
|
ITEM_SHIP_T1_PACKED,
|
|
43
46
|
ITEM_WAREHOUSE_T1_PACKED,
|
|
44
47
|
} from '../data/item-ids'
|
|
@@ -287,7 +290,9 @@ function hullCapsForEntity(
|
|
|
287
290
|
case ITEM_WAREHOUSE_T1_PACKED:
|
|
288
291
|
return computeWarehouseHullCapabilities(decoded)
|
|
289
292
|
case ITEM_EXTRACTOR_T1_PACKED:
|
|
290
|
-
|
|
293
|
+
case ITEM_FACTORY_T1_PACKED:
|
|
294
|
+
case ITEM_MASS_DRIVER_T1_PACKED:
|
|
295
|
+
case ITEM_MASS_CATCHER_T1_PACKED:
|
|
291
296
|
case ITEM_CONTAINER_T1_PACKED:
|
|
292
297
|
return computeContainerCapabilities(decoded)
|
|
293
298
|
case ITEM_CONTAINER_T2_PACKED:
|
|
@@ -41,6 +41,7 @@ export interface ProjectedEntity {
|
|
|
41
41
|
loaderLanes: ServerContract.Types.loader_lane[]
|
|
42
42
|
generator?: ServerContract.Types.energy_stats
|
|
43
43
|
hauler?: ServerContract.Types.hauler_stats
|
|
44
|
+
launcher?: ServerContract.Types.launcher_stats
|
|
44
45
|
readonly cargoMass: UInt64
|
|
45
46
|
readonly totalMass: UInt64
|
|
46
47
|
readonly gathererLanes: ServerContract.Types.gatherer_lane[]
|
|
@@ -49,6 +50,7 @@ export interface ProjectedEntity {
|
|
|
49
50
|
hasMovement(): boolean
|
|
50
51
|
hasStorage(): boolean
|
|
51
52
|
hasLoaders(): boolean
|
|
53
|
+
hasLauncher(): boolean
|
|
52
54
|
|
|
53
55
|
capabilities(): EntityCapabilities
|
|
54
56
|
state(): EntityState
|
|
@@ -64,6 +66,7 @@ export interface Projectable extends ScheduleData {
|
|
|
64
66
|
gatherer_lanes?: ServerContract.Types.gatherer_lane[]
|
|
65
67
|
crafter_lanes?: ServerContract.Types.crafter_lane[]
|
|
66
68
|
hauler?: ServerContract.Types.hauler_stats
|
|
69
|
+
launcher?: ServerContract.Types.launcher_stats
|
|
67
70
|
capacity?: UInt32
|
|
68
71
|
cargo: ServerContract.Types.cargo_item[]
|
|
69
72
|
cargomass: UInt32
|
|
@@ -91,6 +94,7 @@ interface ProjectedCaps {
|
|
|
91
94
|
gathererLanes: ServerContract.Types.gatherer_lane[]
|
|
92
95
|
crafterLanes: ServerContract.Types.crafter_lane[]
|
|
93
96
|
hauler?: ServerContract.Types.hauler_stats
|
|
97
|
+
launcher?: ServerContract.Types.launcher_stats
|
|
94
98
|
}
|
|
95
99
|
|
|
96
100
|
function recomputeCaps(entity: Projectable): ProjectedCaps | undefined {
|
|
@@ -164,6 +168,13 @@ function recomputeCaps(entity: Projectable): ProjectedCaps | undefined {
|
|
|
164
168
|
gathererLanes: (caps.gathererLanes ?? []).map(toGathererLane),
|
|
165
169
|
crafterLanes: (caps.crafterLanes ?? []).map(toCrafterLane),
|
|
166
170
|
hauler: caps.hauler ? ServerContract.Types.hauler_stats.from(caps.hauler) : undefined,
|
|
171
|
+
launcher: caps.launcher
|
|
172
|
+
? ServerContract.Types.launcher_stats.from({
|
|
173
|
+
charge_rate: caps.launcher.chargeRate,
|
|
174
|
+
velocity: caps.launcher.velocity,
|
|
175
|
+
drain: caps.launcher.drain,
|
|
176
|
+
})
|
|
177
|
+
: undefined,
|
|
167
178
|
}
|
|
168
179
|
}
|
|
169
180
|
|
|
@@ -180,6 +191,7 @@ export function createProjectedEntity(entity: Projectable): ProjectedEntity {
|
|
|
180
191
|
entity.engines === undefined ||
|
|
181
192
|
entity.generator === undefined ||
|
|
182
193
|
entity.hauler === undefined ||
|
|
194
|
+
entity.launcher === undefined ||
|
|
183
195
|
entity.capacity === undefined
|
|
184
196
|
const caps = needsRecompute ? recomputeCaps(entity) : undefined
|
|
185
197
|
|
|
@@ -190,6 +202,7 @@ export function createProjectedEntity(entity: Projectable): ProjectedEntity {
|
|
|
190
202
|
const engines = entity.engines ?? caps?.engines
|
|
191
203
|
const generator = entity.generator ?? caps?.generator
|
|
192
204
|
const hauler = entity.hauler ?? caps?.hauler
|
|
205
|
+
const launcher = entity.launcher ?? caps?.launcher
|
|
193
206
|
const capacity = entity.capacity ?? caps?.capacity
|
|
194
207
|
|
|
195
208
|
const cargo: CargoStack[] = entity.cargo.map(cargoItemToStack)
|
|
@@ -203,6 +216,7 @@ export function createProjectedEntity(entity: Projectable): ProjectedEntity {
|
|
|
203
216
|
engines,
|
|
204
217
|
generator,
|
|
205
218
|
hauler,
|
|
219
|
+
launcher,
|
|
206
220
|
loaderLanes,
|
|
207
221
|
gathererLanes,
|
|
208
222
|
crafterLanes,
|
|
@@ -229,12 +243,17 @@ export function createProjectedEntity(entity: Projectable): ProjectedEntity {
|
|
|
229
243
|
return this.loaderLanes.length > 0
|
|
230
244
|
},
|
|
231
245
|
|
|
246
|
+
hasLauncher() {
|
|
247
|
+
return this.launcher !== undefined
|
|
248
|
+
},
|
|
249
|
+
|
|
232
250
|
capabilities(): EntityCapabilities {
|
|
233
251
|
return {
|
|
234
252
|
hullmass: this.shipMass,
|
|
235
253
|
capacity: this.capacity ? UInt32.from(this.capacity) : undefined,
|
|
236
254
|
engines: this.engines,
|
|
237
255
|
generator: this.generator,
|
|
256
|
+
launcher: this.launcher,
|
|
238
257
|
}
|
|
239
258
|
},
|
|
240
259
|
|
|
@@ -59,6 +59,7 @@ export interface EntityCapabilities {
|
|
|
59
59
|
gatherer?: GathererStats
|
|
60
60
|
crafter?: CrafterStats
|
|
61
61
|
hauler?: ServerContract.Types.hauler_stats
|
|
62
|
+
launcher?: ServerContract.Types.launcher_stats
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
export interface EntityState {
|
|
@@ -92,3 +93,7 @@ export function capsHasMass(caps: EntityCapabilities): boolean {
|
|
|
92
93
|
export function capsHasHauler(caps: EntityCapabilities): boolean {
|
|
93
94
|
return caps.hauler !== undefined
|
|
94
95
|
}
|
|
96
|
+
|
|
97
|
+
export function capsHasLauncher(caps: EntityCapabilities): boolean {
|
|
98
|
+
return caps.launcher !== undefined
|
|
99
|
+
}
|