@shipload/sdk 1.0.0-next.34 → 1.0.0-next.36
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 +398 -51
- package/lib/shipload.js +1481 -400
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +1442 -401
- package/lib/shipload.m.js.map +1 -1
- package/lib/testing.d.ts +101 -20
- package/lib/testing.js +201 -57
- package/lib/testing.js.map +1 -1
- package/lib/testing.m.js +201 -57
- package/lib/testing.m.js.map +1 -1
- package/package.json +1 -1
- package/src/capabilities/crafting.ts +2 -3
- package/src/capabilities/gathering.test.ts +16 -0
- package/src/capabilities/gathering.ts +8 -11
- package/src/contracts/server.ts +147 -29
- 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/capability-formulas.ts +0 -1
- package/src/data/entities.json +4 -4
- package/src/data/items.json +5 -5
- package/src/data/recipes.json +39 -65
- package/src/derivation/capabilities.test.ts +133 -0
- package/src/derivation/capabilities.ts +66 -14
- package/src/derivation/rollups.test.ts +55 -0
- package/src/derivation/rollups.ts +56 -0
- package/src/derivation/wormhole.ts +115 -0
- package/src/entities/makers.ts +30 -3
- package/src/errors.ts +2 -0
- package/src/index-module.ts +38 -2
- package/src/managers/actions.ts +79 -5
- package/src/managers/construction.ts +6 -4
- package/src/managers/context.ts +9 -0
- package/src/managers/coordinates.ts +14 -0
- package/src/managers/plot.ts +2 -4
- package/src/nft/description.ts +25 -6
- package/src/planner/index.ts +127 -0
- package/src/planner/planner.test.ts +319 -0
- package/src/resolution/resolve-item.ts +4 -1
- package/src/scheduling/availability.ts +1 -1
- package/src/scheduling/cancel.test.ts +348 -0
- package/src/scheduling/cancel.ts +209 -0
- package/src/scheduling/lanes.test.ts +249 -0
- package/src/scheduling/lanes.ts +140 -2
- package/src/scheduling/projection.ts +75 -16
- package/src/scheduling/schedule.ts +3 -1
- package/src/shipload.ts +5 -0
- package/src/testing/projection-parity.ts +26 -2
- package/src/travel/travel.ts +116 -105
- package/src/types/capabilities.ts +23 -6
- package/src/types/entity.ts +3 -3
- package/src/types.ts +2 -1
- package/src/utils/system.ts +11 -0
|
@@ -2,7 +2,6 @@ import {Name, UInt16, UInt32, UInt64} from '@wharfkit/antelope'
|
|
|
2
2
|
import {ServerContract} from '../contracts'
|
|
3
3
|
import {Coordinates, TaskType} from '../types'
|
|
4
4
|
import {
|
|
5
|
-
capsHasLoaders,
|
|
6
5
|
capsHasMovement,
|
|
7
6
|
capsHasStorage,
|
|
8
7
|
type EntityCapabilities,
|
|
@@ -18,7 +17,7 @@ import {
|
|
|
18
17
|
} from '../errors'
|
|
19
18
|
import {getEntityLayout, getRecipe, type RecipeInput} from '../data/recipes-runtime'
|
|
20
19
|
import {computeEntityCapabilities} from '../derivation/capabilities'
|
|
21
|
-
import {decodeCraftedItemStats} from '../derivation/crafting'
|
|
20
|
+
import {decodeCraftedItemStats, decodeStat} from '../derivation/crafting'
|
|
22
21
|
import {packedModulesToInstalled, type InstalledModule} from '../entities/slot-multiplier'
|
|
23
22
|
import {lerp} from '../travel/travel'
|
|
24
23
|
import {
|
|
@@ -39,11 +38,13 @@ export interface ProjectedEntity {
|
|
|
39
38
|
shipMass: UInt32
|
|
40
39
|
capacity?: UInt64
|
|
41
40
|
engines?: ServerContract.Types.movement_stats
|
|
42
|
-
|
|
41
|
+
loaderLanes: ServerContract.Types.loader_lane[]
|
|
43
42
|
generator?: ServerContract.Types.energy_stats
|
|
44
43
|
hauler?: ServerContract.Types.hauler_stats
|
|
45
44
|
readonly cargoMass: UInt64
|
|
46
45
|
readonly totalMass: UInt64
|
|
46
|
+
readonly gathererLanes: ServerContract.Types.gatherer_lane[]
|
|
47
|
+
readonly crafterLanes: ServerContract.Types.crafter_lane[]
|
|
47
48
|
|
|
48
49
|
hasMovement(): boolean
|
|
49
50
|
hasStorage(): boolean
|
|
@@ -59,7 +60,9 @@ export interface Projectable extends ScheduleData {
|
|
|
59
60
|
hullmass?: UInt32
|
|
60
61
|
generator?: ServerContract.Types.energy_stats
|
|
61
62
|
engines?: ServerContract.Types.movement_stats
|
|
62
|
-
|
|
63
|
+
loader_lanes?: ServerContract.Types.loader_lane[]
|
|
64
|
+
gatherer_lanes?: ServerContract.Types.gatherer_lane[]
|
|
65
|
+
crafter_lanes?: ServerContract.Types.crafter_lane[]
|
|
63
66
|
hauler?: ServerContract.Types.hauler_stats
|
|
64
67
|
capacity?: UInt32
|
|
65
68
|
cargo: ServerContract.Types.cargo_item[]
|
|
@@ -84,7 +87,9 @@ interface ProjectedCaps {
|
|
|
84
87
|
capacity?: UInt32
|
|
85
88
|
engines?: ServerContract.Types.movement_stats
|
|
86
89
|
generator?: ServerContract.Types.energy_stats
|
|
87
|
-
|
|
90
|
+
loaderLanes: ServerContract.Types.loader_lane[]
|
|
91
|
+
gathererLanes: ServerContract.Types.gatherer_lane[]
|
|
92
|
+
crafterLanes: ServerContract.Types.crafter_lane[]
|
|
88
93
|
hauler?: ServerContract.Types.hauler_stats
|
|
89
94
|
}
|
|
90
95
|
|
|
@@ -101,10 +106,53 @@ function recomputeCaps(entity: Projectable): ProjectedCaps | undefined {
|
|
|
101
106
|
typeof entity.item_id === 'number' ? entity.item_id : entity.item_id.value
|
|
102
107
|
)
|
|
103
108
|
const hullStats = decodeCraftedItemStats(itemId, entity.stats)
|
|
109
|
+
if (hullStats.strength === undefined) hullStats.strength = decodeStat(entity.stats, 0)
|
|
110
|
+
if (hullStats.hardness === undefined) hullStats.hardness = decodeStat(entity.stats, 2)
|
|
104
111
|
const layout = getEntityLayout(itemId)?.slots ?? []
|
|
105
112
|
const installed = toInstalledModules(entity.modules)
|
|
106
113
|
const caps = computeEntityCapabilities(hullStats, itemId, installed, layout)
|
|
107
114
|
|
|
115
|
+
const toLoaderLane = (l: {
|
|
116
|
+
slotIndex: number
|
|
117
|
+
mass: number
|
|
118
|
+
thrust: number
|
|
119
|
+
outputPct: number
|
|
120
|
+
}): ServerContract.Types.loader_lane =>
|
|
121
|
+
ServerContract.Types.loader_lane.from({
|
|
122
|
+
slot_index: l.slotIndex,
|
|
123
|
+
mass: l.mass,
|
|
124
|
+
thrust: l.thrust,
|
|
125
|
+
output_pct: l.outputPct,
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
const toGathererLane = (l: {
|
|
129
|
+
slotIndex: number
|
|
130
|
+
yield: number
|
|
131
|
+
drain: number
|
|
132
|
+
depth: number
|
|
133
|
+
outputPct: number
|
|
134
|
+
}): ServerContract.Types.gatherer_lane =>
|
|
135
|
+
ServerContract.Types.gatherer_lane.from({
|
|
136
|
+
slot_index: l.slotIndex,
|
|
137
|
+
yield: l.yield,
|
|
138
|
+
drain: l.drain,
|
|
139
|
+
depth: l.depth,
|
|
140
|
+
output_pct: l.outputPct,
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
const toCrafterLane = (l: {
|
|
144
|
+
slotIndex: number
|
|
145
|
+
speed: number
|
|
146
|
+
drain: number
|
|
147
|
+
outputPct: number
|
|
148
|
+
}): ServerContract.Types.crafter_lane =>
|
|
149
|
+
ServerContract.Types.crafter_lane.from({
|
|
150
|
+
slot_index: l.slotIndex,
|
|
151
|
+
speed: l.speed,
|
|
152
|
+
drain: l.drain,
|
|
153
|
+
output_pct: l.outputPct,
|
|
154
|
+
})
|
|
155
|
+
|
|
108
156
|
return {
|
|
109
157
|
hullmass: UInt32.from(caps.hullmass),
|
|
110
158
|
capacity: UInt32.from(caps.capacity),
|
|
@@ -112,15 +160,23 @@ function recomputeCaps(entity: Projectable): ProjectedCaps | undefined {
|
|
|
112
160
|
generator: caps.generator
|
|
113
161
|
? ServerContract.Types.energy_stats.from(caps.generator)
|
|
114
162
|
: undefined,
|
|
115
|
-
|
|
163
|
+
loaderLanes: (caps.loaderLanes ?? []).map(toLoaderLane),
|
|
164
|
+
gathererLanes: (caps.gathererLanes ?? []).map(toGathererLane),
|
|
165
|
+
crafterLanes: (caps.crafterLanes ?? []).map(toCrafterLane),
|
|
116
166
|
hauler: caps.hauler ? ServerContract.Types.hauler_stats.from(caps.hauler) : undefined,
|
|
117
167
|
}
|
|
118
168
|
}
|
|
119
169
|
|
|
170
|
+
function loaderLanesTotalMass(lanes: ServerContract.Types.loader_lane[]): UInt64 {
|
|
171
|
+
let total = 0
|
|
172
|
+
for (const l of lanes) total += Number(l.mass)
|
|
173
|
+
return UInt64.from(total)
|
|
174
|
+
}
|
|
175
|
+
|
|
120
176
|
export function createProjectedEntity(entity: Projectable): ProjectedEntity {
|
|
121
177
|
const needsRecompute =
|
|
122
178
|
entity.hullmass === undefined ||
|
|
123
|
-
entity.
|
|
179
|
+
entity.loader_lanes === undefined ||
|
|
124
180
|
entity.engines === undefined ||
|
|
125
181
|
entity.generator === undefined ||
|
|
126
182
|
entity.hauler === undefined ||
|
|
@@ -128,7 +184,9 @@ export function createProjectedEntity(entity: Projectable): ProjectedEntity {
|
|
|
128
184
|
const caps = needsRecompute ? recomputeCaps(entity) : undefined
|
|
129
185
|
|
|
130
186
|
const shipMass = UInt32.from(entity.hullmass ?? caps?.hullmass ?? 0)
|
|
131
|
-
const
|
|
187
|
+
const loaderLanes = entity.loader_lanes ?? caps?.loaderLanes ?? []
|
|
188
|
+
const gathererLanes = entity.gatherer_lanes ?? caps?.gathererLanes ?? []
|
|
189
|
+
const crafterLanes = entity.crafter_lanes ?? caps?.crafterLanes ?? []
|
|
132
190
|
const engines = entity.engines ?? caps?.engines
|
|
133
191
|
const generator = entity.generator ?? caps?.generator
|
|
134
192
|
const hauler = entity.hauler ?? caps?.hauler
|
|
@@ -145,18 +203,18 @@ export function createProjectedEntity(entity: Projectable): ProjectedEntity {
|
|
|
145
203
|
engines,
|
|
146
204
|
generator,
|
|
147
205
|
hauler,
|
|
148
|
-
|
|
206
|
+
loaderLanes,
|
|
207
|
+
gathererLanes,
|
|
208
|
+
crafterLanes,
|
|
149
209
|
|
|
150
210
|
get cargoMass() {
|
|
151
211
|
return calcStacksMass(this.cargo)
|
|
152
212
|
},
|
|
153
213
|
|
|
154
214
|
get totalMass() {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
return mass
|
|
215
|
+
return UInt64.from(this.shipMass)
|
|
216
|
+
.adding(this.cargoMass)
|
|
217
|
+
.adding(loaderLanesTotalMass(this.loaderLanes))
|
|
160
218
|
},
|
|
161
219
|
|
|
162
220
|
hasMovement() {
|
|
@@ -168,7 +226,7 @@ export function createProjectedEntity(entity: Projectable): ProjectedEntity {
|
|
|
168
226
|
},
|
|
169
227
|
|
|
170
228
|
hasLoaders() {
|
|
171
|
-
return
|
|
229
|
+
return this.loaderLanes.length > 0
|
|
172
230
|
},
|
|
173
231
|
|
|
174
232
|
capabilities(): EntityCapabilities {
|
|
@@ -177,7 +235,6 @@ export function createProjectedEntity(entity: Projectable): ProjectedEntity {
|
|
|
177
235
|
capacity: this.capacity ? UInt32.from(this.capacity) : undefined,
|
|
178
236
|
engines: this.engines,
|
|
179
237
|
generator: this.generator,
|
|
180
|
-
loaders: this.loaders,
|
|
181
238
|
}
|
|
182
239
|
},
|
|
183
240
|
|
|
@@ -303,6 +360,7 @@ function applyTask(projected: ProjectedEntity, task: ServerContract.Types.task):
|
|
|
303
360
|
break
|
|
304
361
|
case TaskType.TRAVEL:
|
|
305
362
|
case TaskType.WARP:
|
|
363
|
+
case TaskType.TRANSIT:
|
|
306
364
|
applyFlightTask(projected, task, {complete: true})
|
|
307
365
|
break
|
|
308
366
|
case TaskType.LOAD:
|
|
@@ -461,6 +519,7 @@ export function projectEntityAt(entity: Projectable, now: Date): ProjectedEntity
|
|
|
461
519
|
break
|
|
462
520
|
case TaskType.TRAVEL:
|
|
463
521
|
case TaskType.WARP:
|
|
522
|
+
case TaskType.TRANSIT:
|
|
464
523
|
applyFlightTask(projected, task, {complete: taskComplete, progress})
|
|
465
524
|
break
|
|
466
525
|
case TaskType.LOAD:
|
|
@@ -294,7 +294,9 @@ function entityDoesTaskType(entity: ScheduleData, taskType: TaskType, now: Date)
|
|
|
294
294
|
|
|
295
295
|
export function isInFlight(entity: ScheduleData, now: Date): boolean {
|
|
296
296
|
const lane = mobilityLane(entity)
|
|
297
|
-
|
|
297
|
+
if (!lane) return false
|
|
298
|
+
const t = core.currentTaskType(lane.schedule, now)
|
|
299
|
+
return t === TaskType.TRAVEL || t === TaskType.TRANSIT
|
|
298
300
|
}
|
|
299
301
|
|
|
300
302
|
export function isRecharging(entity: ScheduleData, now: Date): boolean {
|
package/src/shipload.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {GameContext} from './managers/context'
|
|
|
7
7
|
import type {EntitiesManager} from './managers/entities'
|
|
8
8
|
import type {PlayersManager} from './managers/players'
|
|
9
9
|
import type {LocationsManager} from './managers/locations'
|
|
10
|
+
import type {CoordinatesManager} from './managers/coordinates'
|
|
10
11
|
import type {EpochsManager} from './managers/epochs'
|
|
11
12
|
import type {ActionsManager} from './managers/actions'
|
|
12
13
|
import type {NftManager} from './managers/nft'
|
|
@@ -110,6 +111,10 @@ export class Shipload {
|
|
|
110
111
|
return this._context.locations
|
|
111
112
|
}
|
|
112
113
|
|
|
114
|
+
get coordinates(): CoordinatesManager {
|
|
115
|
+
return this._context.coordinates
|
|
116
|
+
}
|
|
117
|
+
|
|
113
118
|
get epochs(): EpochsManager {
|
|
114
119
|
return this._context.epochs
|
|
115
120
|
}
|
|
@@ -12,9 +12,11 @@ export interface ContractProjectedState {
|
|
|
12
12
|
hullmass?: UInt32
|
|
13
13
|
capacity?: UInt32
|
|
14
14
|
engines?: ServerContract.Types.movement_stats
|
|
15
|
-
loaders?: ServerContract.Types.loader_stats
|
|
16
15
|
generator?: ServerContract.Types.energy_stats
|
|
17
16
|
hauler?: ServerContract.Types.hauler_stats
|
|
17
|
+
gatherer_lanes: ServerContract.Types.gatherer_lane[]
|
|
18
|
+
crafter_lanes: ServerContract.Types.crafter_lane[]
|
|
19
|
+
loader_lanes: ServerContract.Types.loader_lane[]
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
export interface ProjectionComparisonOptions {
|
|
@@ -55,10 +57,32 @@ export function assertProjectionEquals(
|
|
|
55
57
|
record('capacity', toNum(contract.capacity), sdk.capacity ? Number(sdk.capacity) : undefined)
|
|
56
58
|
|
|
57
59
|
recordStatBlock('engines', contract.engines, sdk.engines)
|
|
58
|
-
recordStatBlock('loaders', contract.loaders, sdk.loaders)
|
|
59
60
|
recordStatBlock('generator', contract.generator, sdk.generator)
|
|
60
61
|
recordStatBlock('hauler', contract.hauler, sdk.hauler)
|
|
61
62
|
|
|
63
|
+
const normLane = (l: {slot_index?: unknown; [k: string]: unknown}) =>
|
|
64
|
+
Object.fromEntries(
|
|
65
|
+
Object.entries(l)
|
|
66
|
+
.filter(([k]) => k !== 'slot_index')
|
|
67
|
+
.map(([k, v]) => [k, toNum(v) ?? 0])
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
const compareLanes = (name: string, cLanes: unknown[], sLanes: unknown[]) => {
|
|
71
|
+
if (cLanes.length !== sLanes.length) {
|
|
72
|
+
mismatches.push(` ${name}.length: contract=${cLanes.length} sdk=${sLanes.length}`)
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
for (let i = 0; i < cLanes.length; i++) {
|
|
76
|
+
const cn = JSON.stringify(normLane(cLanes[i] as Record<string, unknown>))
|
|
77
|
+
const sn = JSON.stringify(normLane(sLanes[i] as Record<string, unknown>))
|
|
78
|
+
if (cn !== sn) mismatches.push(` ${name}[${i}]: contract=${cn} sdk=${sn}`)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
compareLanes('gatherer_lanes', contract.gatherer_lanes as unknown[], sdk.gathererLanes ?? [])
|
|
83
|
+
compareLanes('crafter_lanes', contract.crafter_lanes as unknown[], sdk.crafterLanes ?? [])
|
|
84
|
+
compareLanes('loader_lanes', contract.loader_lanes as unknown[], sdk.loaderLanes ?? [])
|
|
85
|
+
|
|
62
86
|
if (contract.cargo.length > 0 || sdk.cargo.length > 0) {
|
|
63
87
|
const contractCargo = normaliseCargo(mergeContractCargo(contract.cargo))
|
|
64
88
|
const sdkCargo = normaliseCargo(sdk.cargo)
|
package/src/travel/travel.ts
CHANGED
|
@@ -37,9 +37,14 @@ import {
|
|
|
37
37
|
import {EntityClass} from '../data/kind-registry'
|
|
38
38
|
import {getItem} from '../data/catalog'
|
|
39
39
|
import {hasSystem} from '../utils/system'
|
|
40
|
+
import {WH} from '../derivation/wormhole'
|
|
40
41
|
import * as scheduleModel from '../scheduling/schedule'
|
|
41
42
|
import type {ScheduleData} from '../scheduling/schedule'
|
|
42
43
|
|
|
44
|
+
function isPositionalTask(task: ServerContract.Types.task): boolean {
|
|
45
|
+
return task.type.equals(TaskType.TRAVEL) || task.type.equals(TaskType.TRANSIT)
|
|
46
|
+
}
|
|
47
|
+
|
|
43
48
|
export function calc_orbital_altitude(mass: number): number {
|
|
44
49
|
if (mass <= BASE_ORBITAL_MASS) {
|
|
45
50
|
return MIN_ORBITAL_ALTITUDE
|
|
@@ -125,7 +130,7 @@ export function getInterpolatedPosition(
|
|
|
125
130
|
return {x: Number(settled.x), y: Number(settled.y)}
|
|
126
131
|
}
|
|
127
132
|
const task = tasks[taskIndex]
|
|
128
|
-
if (!task
|
|
133
|
+
if (!isPositionalTask(task) || !task.coordinates) {
|
|
129
134
|
const origin = getFlightOrigin(entity, taskIndex)
|
|
130
135
|
return {x: Number(origin.x), y: Number(origin.y)}
|
|
131
136
|
}
|
|
@@ -198,15 +203,31 @@ export function calc_flighttime(distance: UInt64Type, acceleration: number): UIn
|
|
|
198
203
|
return UInt32.from(2 * Math.sqrt(Number(distance) / acceleration))
|
|
199
204
|
}
|
|
200
205
|
|
|
206
|
+
export function calc_transit_duration(ax: number, ay: number, bx: number, by: number): UInt32 {
|
|
207
|
+
const distance = distanceBetweenPoints(ax, ay, bx, by)
|
|
208
|
+
return UInt32.from(Math.floor(distance.toNumber() / (PRECISION * WH.TRANSIT_SPEED)))
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// The active entity's chosen loader lane (lowest slot), mirroring cargo.cpp lane selection.
|
|
212
|
+
export function shipLoaderLane(ship: ShipLike): {thrust: number; mass: number} | undefined {
|
|
213
|
+
const lanes = ship.loader_lanes ?? []
|
|
214
|
+
if (lanes.length === 0) return undefined
|
|
215
|
+
let lowest = lanes[0]
|
|
216
|
+
for (const lane of lanes) {
|
|
217
|
+
if (Number(lane.slot_index) < Number(lowest.slot_index)) lowest = lane
|
|
218
|
+
}
|
|
219
|
+
return {thrust: Number(lowest.thrust), mass: Number(lowest.mass)}
|
|
220
|
+
}
|
|
221
|
+
|
|
201
222
|
export function calc_loader_flighttime(ship: ShipLike, mass: UInt64, altitude?: number): UInt32 {
|
|
202
223
|
const z = altitude ?? ship.coordinates.z?.toNumber() ?? calc_orbital_altitude(Number(mass))
|
|
203
224
|
return calc_flighttime(z, calc_loader_acceleration(ship, mass))
|
|
204
225
|
}
|
|
205
226
|
|
|
206
227
|
export function calc_loader_acceleration(ship: ShipLike, mass: UInt64): number {
|
|
207
|
-
const
|
|
208
|
-
const
|
|
209
|
-
return calc_acceleration(thrust, Number(mass)
|
|
228
|
+
const lane = shipLoaderLane(ship)
|
|
229
|
+
const thrust = lane ? lane.thrust : 0
|
|
230
|
+
return calc_acceleration(thrust, Number(mass))
|
|
210
231
|
}
|
|
211
232
|
|
|
212
233
|
export function calc_ship_flighttime(ship: ShipLike, mass: UInt64, distance: UInt64): UInt32 {
|
|
@@ -228,8 +249,10 @@ export function calc_ship_mass(ship: ShipLike, cargos: CargoMassInfo[]): UInt64
|
|
|
228
249
|
|
|
229
250
|
mass.add(ship.hullmass)
|
|
230
251
|
|
|
231
|
-
if (ship.
|
|
232
|
-
|
|
252
|
+
if (ship.loader_lanes && ship.loader_lanes.length > 0) {
|
|
253
|
+
for (const l of ship.loader_lanes) {
|
|
254
|
+
mass.add(UInt64.from(l.mass))
|
|
255
|
+
}
|
|
233
256
|
}
|
|
234
257
|
|
|
235
258
|
for (const cargo of cargos) {
|
|
@@ -264,10 +287,10 @@ export function calculateTransferTime(
|
|
|
264
287
|
return UInt32.from(0)
|
|
265
288
|
}
|
|
266
289
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
return
|
|
290
|
+
const lane = shipLoaderLane(ship)
|
|
291
|
+
if (!lane) return UInt32.from(0)
|
|
292
|
+
mass = UInt64.from(mass).adding(UInt64.from(lane.mass))
|
|
293
|
+
return calc_loader_flighttime(ship, mass)
|
|
271
294
|
}
|
|
272
295
|
|
|
273
296
|
export function calculateRefuelingTime(ship: ShipLike): UInt32 {
|
|
@@ -322,25 +345,22 @@ export function calculateLoadTimeBreakdown(
|
|
|
322
345
|
let unloadTime = 0
|
|
323
346
|
let loadTime = 0
|
|
324
347
|
|
|
325
|
-
|
|
326
|
-
|
|
348
|
+
const lane = shipLoaderLane(ship)
|
|
349
|
+
|
|
350
|
+
if (mass_unload.gt(UInt64.zero) && lane) {
|
|
351
|
+
const totalMass = UInt64.from(mass_unload).adding(UInt64.from(lane.mass))
|
|
327
352
|
unloadTime = Number(calc_loader_flighttime(ship, totalMass))
|
|
328
353
|
}
|
|
329
354
|
|
|
330
|
-
if (mass_load.gt(UInt64.zero) &&
|
|
331
|
-
const totalMass = UInt64.from(mass_load).adding(
|
|
355
|
+
if (mass_load.gt(UInt64.zero) && lane) {
|
|
356
|
+
const totalMass = UInt64.from(mass_load).adding(UInt64.from(lane.mass))
|
|
332
357
|
loadTime = Number(calc_loader_flighttime(ship, totalMass))
|
|
333
358
|
}
|
|
334
359
|
|
|
335
|
-
const numLoaders = ship.loaders ? Number(ship.loaders.quantity) : 0
|
|
336
|
-
const totalTime = numLoaders > 0 ? (unloadTime + loadTime) / numLoaders : 0
|
|
337
|
-
const unloadTimePerLoader = numLoaders > 0 ? unloadTime / numLoaders : 0
|
|
338
|
-
const loadTimePerLoader = numLoaders > 0 ? loadTime / numLoaders : 0
|
|
339
|
-
|
|
340
360
|
return {
|
|
341
|
-
unloadTime
|
|
342
|
-
loadTime
|
|
343
|
-
totalTime,
|
|
361
|
+
unloadTime,
|
|
362
|
+
loadTime,
|
|
363
|
+
totalTime: unloadTime + loadTime,
|
|
344
364
|
unloadMass: Number(mass_unload),
|
|
345
365
|
loadMass: Number(mass_load),
|
|
346
366
|
}
|
|
@@ -374,24 +394,16 @@ export function estimateTravelTime(
|
|
|
374
394
|
let loadTime = UInt32.zero
|
|
375
395
|
let unloadTime = UInt32.zero
|
|
376
396
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
ship
|
|
382
|
-
) {
|
|
383
|
-
const totalMass = UInt64.from(loadMass).adding(ship.loaders.mass)
|
|
384
|
-
loadTime = calc_loader_flighttime(ship, totalMass).dividing(ship.loaders.quantity)
|
|
397
|
+
const lane = shipLoaderLane(ship)
|
|
398
|
+
|
|
399
|
+
if (loadMass && UInt32.from(loadMass).gt(UInt32.zero) && lane) {
|
|
400
|
+
const totalMass = UInt64.from(loadMass).adding(UInt64.from(lane.mass))
|
|
401
|
+
loadTime = calc_loader_flighttime(ship, totalMass)
|
|
385
402
|
}
|
|
386
403
|
|
|
387
|
-
if (
|
|
388
|
-
unloadMass
|
|
389
|
-
|
|
390
|
-
ship.loaders &&
|
|
391
|
-
ship.loaders.quantity.gt(UInt32.zero)
|
|
392
|
-
) {
|
|
393
|
-
const totalMass = UInt64.from(unloadMass).adding(ship.loaders.mass)
|
|
394
|
-
unloadTime = calc_loader_flighttime(ship, totalMass).dividing(ship.loaders.quantity)
|
|
404
|
+
if (unloadMass && UInt32.from(unloadMass).gt(UInt32.zero) && lane) {
|
|
405
|
+
const totalMass = UInt64.from(unloadMass).adding(UInt64.from(lane.mass))
|
|
406
|
+
unloadTime = calc_loader_flighttime(ship, totalMass)
|
|
395
407
|
}
|
|
396
408
|
|
|
397
409
|
return {
|
|
@@ -423,14 +435,32 @@ export function hasEnergyForDistance(ship: ShipLike, distance: UInt64Type): bool
|
|
|
423
435
|
return UInt64.from(ship.energy ?? 0).gte(energyNeeded)
|
|
424
436
|
}
|
|
425
437
|
|
|
438
|
+
export interface TransferLoaderLane {
|
|
439
|
+
slot_index?: {toNumber(): number} | number
|
|
440
|
+
thrust: {toNumber(): number} | number
|
|
441
|
+
mass: {toNumber(): number} | number
|
|
442
|
+
}
|
|
443
|
+
|
|
426
444
|
export interface TransferEntity {
|
|
427
445
|
location: {z?: {toNumber(): number} | number}
|
|
428
446
|
entityClass: EntityClass
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
447
|
+
loaderLanes?: TransferLoaderLane[]
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function toNum(v: {toNumber(): number} | number | undefined): number {
|
|
451
|
+
if (v === undefined) return 0
|
|
452
|
+
return typeof v === 'number' ? v : v.toNumber()
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Mirrors cargo.cpp worker_lane_key_or_mobility: lowest-slot loader lane (display has no busy context).
|
|
456
|
+
function chosenLoaderLane(entity: TransferEntity): TransferLoaderLane | undefined {
|
|
457
|
+
const lanes = entity.loaderLanes ?? []
|
|
458
|
+
if (lanes.length === 0) return undefined
|
|
459
|
+
let lowest = lanes[0]
|
|
460
|
+
for (const lane of lanes) {
|
|
461
|
+
if (toNum(lane.slot_index) < toNum(lowest.slot_index)) lowest = lane
|
|
433
462
|
}
|
|
463
|
+
return lowest
|
|
434
464
|
}
|
|
435
465
|
|
|
436
466
|
export interface HasScheduleAndLocation extends ScheduleData {
|
|
@@ -449,7 +479,7 @@ export function getFlightOrigin(
|
|
|
449
479
|
let origin = entity.coordinates
|
|
450
480
|
for (let i = 0; i < flightTaskIndex && i < tasks.length; i++) {
|
|
451
481
|
const task = tasks[i]
|
|
452
|
-
if (task
|
|
482
|
+
if (isPositionalTask(task) && task.coordinates) {
|
|
453
483
|
origin = task.coordinates
|
|
454
484
|
}
|
|
455
485
|
}
|
|
@@ -462,7 +492,7 @@ export function getDestinationLocation(
|
|
|
462
492
|
const tasks = mobilityTasks(entity)
|
|
463
493
|
for (let i = tasks.length - 1; i >= 0; i--) {
|
|
464
494
|
const task = tasks[i]
|
|
465
|
-
if (task
|
|
495
|
+
if (isPositionalTask(task) && task.coordinates) {
|
|
466
496
|
return task.coordinates
|
|
467
497
|
}
|
|
468
498
|
}
|
|
@@ -485,7 +515,7 @@ export function getPositionAt(
|
|
|
485
515
|
|
|
486
516
|
const task = tasks[taskIndex]
|
|
487
517
|
|
|
488
|
-
if (!task
|
|
518
|
+
if (!isPositionalTask(task) || !task.coordinates) {
|
|
489
519
|
return getFlightOrigin(entity, taskIndex)
|
|
490
520
|
}
|
|
491
521
|
|
|
@@ -505,73 +535,54 @@ export function minTransferDistance(entityClass: EntityClass): number {
|
|
|
505
535
|
: MIN_TRANSFER_DISTANCE_PLANETARY_STRUCTURE
|
|
506
536
|
}
|
|
507
537
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
538
|
+
// Mirrors cargo.cpp calc_onesided_duration: single active loader's thrust + mass, no ÷quantity.
|
|
539
|
+
export function calc_onesided_duration(
|
|
540
|
+
loaderThrust: number,
|
|
541
|
+
loaderMass: number,
|
|
542
|
+
activeZ: number,
|
|
543
|
+
counterpartZ: number,
|
|
544
|
+
activeEntityClass: EntityClass,
|
|
545
|
+
counterpartEntityClass: EntityClass,
|
|
511
546
|
cargoMass: number
|
|
512
547
|
): number {
|
|
513
|
-
if (cargoMass === 0) {
|
|
514
|
-
return 0
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
let totalThrust = 0
|
|
518
|
-
let totalLoaderMass = 0
|
|
519
|
-
let totalQuantity = 0
|
|
520
|
-
|
|
521
|
-
if (source.loaders) {
|
|
522
|
-
const thrust =
|
|
523
|
-
typeof source.loaders.thrust === 'number'
|
|
524
|
-
? source.loaders.thrust
|
|
525
|
-
: source.loaders.thrust.toNumber()
|
|
526
|
-
const mass =
|
|
527
|
-
typeof source.loaders.mass === 'number'
|
|
528
|
-
? source.loaders.mass
|
|
529
|
-
: source.loaders.mass.toNumber()
|
|
530
|
-
const qty =
|
|
531
|
-
typeof source.loaders.quantity === 'number'
|
|
532
|
-
? source.loaders.quantity
|
|
533
|
-
: source.loaders.quantity.toNumber()
|
|
534
|
-
totalThrust += thrust * qty
|
|
535
|
-
totalLoaderMass += mass * qty
|
|
536
|
-
totalQuantity += qty
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
if (dest.loaders) {
|
|
540
|
-
const thrust =
|
|
541
|
-
typeof dest.loaders.thrust === 'number'
|
|
542
|
-
? dest.loaders.thrust
|
|
543
|
-
: dest.loaders.thrust.toNumber()
|
|
544
|
-
const mass =
|
|
545
|
-
typeof dest.loaders.mass === 'number' ? dest.loaders.mass : dest.loaders.mass.toNumber()
|
|
546
|
-
const qty =
|
|
547
|
-
typeof dest.loaders.quantity === 'number'
|
|
548
|
-
? dest.loaders.quantity
|
|
549
|
-
: dest.loaders.quantity.toNumber()
|
|
550
|
-
totalThrust += thrust * qty
|
|
551
|
-
totalLoaderMass += mass * qty
|
|
552
|
-
totalQuantity += qty
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
if (totalThrust === 0 || totalQuantity === 0) {
|
|
548
|
+
if (cargoMass === 0 || loaderThrust === 0) {
|
|
556
549
|
return 0
|
|
557
550
|
}
|
|
558
|
-
|
|
559
|
-
const sourceZ =
|
|
560
|
-
typeof source.location.z === 'number'
|
|
561
|
-
? source.location.z
|
|
562
|
-
: (source.location.z?.toNumber() ?? 0)
|
|
563
|
-
const destZ =
|
|
564
|
-
typeof dest.location.z === 'number' ? dest.location.z : (dest.location.z?.toNumber() ?? 0)
|
|
565
|
-
const rawDistance = Math.abs(sourceZ - destZ)
|
|
551
|
+
const rawDistance = Math.abs(activeZ - counterpartZ)
|
|
566
552
|
const minDistance = Math.max(
|
|
567
|
-
minTransferDistance(
|
|
568
|
-
minTransferDistance(
|
|
553
|
+
minTransferDistance(activeEntityClass),
|
|
554
|
+
minTransferDistance(counterpartEntityClass)
|
|
569
555
|
)
|
|
570
556
|
const distance = rawDistance < minDistance ? minDistance : rawDistance
|
|
557
|
+
const totalMass = cargoMass + loaderMass
|
|
558
|
+
const acceleration = calc_acceleration(loaderThrust, totalMass)
|
|
559
|
+
const flightTime = Math.floor(2 * Math.sqrt(distance / acceleration))
|
|
560
|
+
return flightTime === 0 ? 1 : flightTime
|
|
561
|
+
}
|
|
571
562
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
563
|
+
// Mirrors cargo.cpp: the active (loader-bearing) entity's chosen loader lane drives the duration.
|
|
564
|
+
export function calc_transfer_duration(
|
|
565
|
+
source: TransferEntity,
|
|
566
|
+
dest: TransferEntity,
|
|
567
|
+
cargoMass: number
|
|
568
|
+
): number {
|
|
569
|
+
const active = chosenLoaderLane(source) ? source : dest
|
|
570
|
+
const counterpart = active === source ? dest : source
|
|
571
|
+
const lane = chosenLoaderLane(active)
|
|
572
|
+
if (!lane) {
|
|
573
|
+
return 0
|
|
574
|
+
}
|
|
575
575
|
|
|
576
|
-
|
|
576
|
+
const activeZ = toNum(active.location.z)
|
|
577
|
+
const counterpartZ = toNum(counterpart.location.z)
|
|
578
|
+
|
|
579
|
+
return calc_onesided_duration(
|
|
580
|
+
toNum(lane.thrust),
|
|
581
|
+
toNum(lane.mass),
|
|
582
|
+
activeZ,
|
|
583
|
+
counterpartZ,
|
|
584
|
+
active.entityClass,
|
|
585
|
+
counterpart.entityClass,
|
|
586
|
+
cargoMass
|
|
587
|
+
)
|
|
577
588
|
}
|
|
@@ -1,6 +1,23 @@
|
|
|
1
|
-
import type {Name, UInt16, UInt32} from '@wharfkit/antelope'
|
|
1
|
+
import type {Name, UInt16, UInt32, UInt8} from '@wharfkit/antelope'
|
|
2
2
|
import type {ServerContract} from '../contracts'
|
|
3
3
|
|
|
4
|
+
export interface LoaderStats {
|
|
5
|
+
mass: {toNumber(): number; multiplying(v: unknown): {toNumber(): number}}
|
|
6
|
+
thrust: {toNumber(): number}
|
|
7
|
+
quantity: {toNumber(): number; gt(v: unknown): boolean}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface GathererStats {
|
|
11
|
+
yield: {toNumber(): number}
|
|
12
|
+
drain: {toNumber(): number}
|
|
13
|
+
depth: {toNumber(): number; toString(): string}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface CrafterStats {
|
|
17
|
+
speed: {toNumber(): number}
|
|
18
|
+
drain: {toNumber(): number}
|
|
19
|
+
}
|
|
20
|
+
|
|
4
21
|
export interface MovementCapability {
|
|
5
22
|
engines: ServerContract.Types.movement_stats
|
|
6
23
|
generator: ServerContract.Types.energy_stats
|
|
@@ -17,11 +34,11 @@ export interface StorageCapability {
|
|
|
17
34
|
}
|
|
18
35
|
|
|
19
36
|
export interface LoaderCapability {
|
|
20
|
-
loaders:
|
|
37
|
+
loaders: LoaderStats
|
|
21
38
|
}
|
|
22
39
|
|
|
23
40
|
export interface GathererCapability {
|
|
24
|
-
gatherer:
|
|
41
|
+
gatherer: GathererStats
|
|
25
42
|
}
|
|
26
43
|
|
|
27
44
|
export interface MassCapability {
|
|
@@ -38,9 +55,9 @@ export interface EntityCapabilities {
|
|
|
38
55
|
capacity?: UInt32
|
|
39
56
|
engines?: ServerContract.Types.movement_stats
|
|
40
57
|
generator?: ServerContract.Types.energy_stats
|
|
41
|
-
loaders?:
|
|
42
|
-
gatherer?:
|
|
43
|
-
crafter?:
|
|
58
|
+
loaders?: LoaderStats
|
|
59
|
+
gatherer?: GathererStats
|
|
60
|
+
crafter?: CrafterStats
|
|
44
61
|
hauler?: ServerContract.Types.hauler_stats
|
|
45
62
|
}
|
|
46
63
|
|
package/src/types/entity.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type {ServerContract} from '../contracts'
|
|
|
3
3
|
import type {Coordinates} from '../types'
|
|
4
4
|
import type {
|
|
5
5
|
EnergyCapability,
|
|
6
|
+
GathererCapability,
|
|
6
7
|
LoaderCapability,
|
|
7
8
|
MassCapability,
|
|
8
9
|
MovementCapability,
|
|
@@ -24,9 +25,8 @@ export type ShipEntity = Entity &
|
|
|
24
25
|
StorageCapability &
|
|
25
26
|
Partial<LoaderCapability> &
|
|
26
27
|
MassCapability &
|
|
27
|
-
ScheduleCapability &
|
|
28
|
-
|
|
29
|
-
}
|
|
28
|
+
ScheduleCapability &
|
|
29
|
+
Partial<GathererCapability>
|
|
30
30
|
|
|
31
31
|
export type WarehouseEntity = Entity &
|
|
32
32
|
StorageCapability &
|