@shipload/sdk 0.7.1 → 1.0.0-next.0
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 +2730 -287
- package/lib/shipload.js +10862 -2229
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +10434 -2171
- package/lib/shipload.m.js.map +1 -1
- package/package.json +11 -20
- package/src/capabilities/crafting.ts +22 -0
- package/src/capabilities/gathering.ts +36 -0
- package/src/capabilities/guards.ts +38 -0
- package/src/capabilities/hauling.ts +22 -0
- package/src/capabilities/index.ts +8 -0
- package/src/capabilities/loading.ts +8 -0
- package/src/capabilities/modules.ts +86 -0
- package/src/capabilities/movement.ts +29 -0
- package/src/capabilities/storage.ts +159 -0
- package/src/contracts/server.ts +1389 -285
- package/src/data/capabilities.ts +408 -0
- package/src/data/catalog.ts +135 -0
- package/src/data/categories.ts +55 -0
- package/src/data/colors.ts +84 -0
- package/src/data/entities.json +50 -0
- package/src/data/item-ids.ts +75 -0
- package/src/data/items.json +252 -0
- package/src/data/locations.ts +53 -0
- package/src/data/metadata.ts +208 -0
- package/src/data/nebula-adjectives.json +211 -0
- package/src/data/nebula-nouns.json +151 -0
- package/src/data/recipes-runtime.ts +65 -0
- package/src/data/recipes.json +878 -0
- package/src/data/syllables.json +1790 -0
- package/src/data/tiers.ts +45 -0
- package/src/derivation/crafting.ts +350 -0
- package/src/derivation/index.ts +32 -0
- package/src/derivation/location-size.ts +15 -0
- package/src/derivation/resources.ts +112 -0
- package/src/derivation/stats.ts +146 -0
- package/src/derivation/strata.ts +43 -0
- package/src/derivation/stratum.ts +134 -0
- package/src/derivation/tiers.ts +54 -0
- package/src/entities/cargo-utils.ts +84 -0
- package/src/entities/container.ts +108 -0
- package/src/entities/entity-inventory.ts +39 -0
- package/src/entities/gamestate.ts +152 -0
- package/src/entities/inventory-accessor.ts +42 -0
- package/src/entities/location.ts +60 -0
- package/src/entities/makers.ts +196 -0
- package/src/entities/player.ts +15 -0
- package/src/entities/ship-deploy.ts +258 -0
- package/src/entities/ship.ts +204 -0
- package/src/entities/warehouse.ts +119 -0
- package/src/errors.ts +100 -9
- package/src/format.ts +12 -0
- package/src/index-module.ts +317 -7
- package/src/managers/actions.ts +250 -0
- package/src/managers/base.ts +25 -0
- package/src/managers/context.ts +114 -0
- package/src/managers/entities.ts +103 -0
- package/src/managers/epochs.ts +47 -0
- package/src/managers/index.ts +9 -0
- package/src/managers/locations.ts +68 -0
- package/src/managers/players.ts +13 -0
- package/src/nft/description.ts +176 -0
- package/src/nft/deserializers.ts +83 -0
- package/src/nft/index.ts +2 -0
- package/src/resolution/describe-module.ts +166 -0
- package/src/resolution/display-name.ts +39 -0
- package/src/resolution/resolve-item.ts +358 -0
- package/src/scheduling/accessor.ts +82 -0
- package/src/{epoch.ts → scheduling/epoch.ts} +1 -1
- package/src/scheduling/projection.ts +463 -0
- package/src/scheduling/schedule.ts +179 -0
- package/src/shipload.ts +47 -160
- package/src/subscriptions/connection.ts +154 -0
- package/src/subscriptions/debug.ts +17 -0
- package/src/subscriptions/index.ts +5 -0
- package/src/subscriptions/manager.ts +240 -0
- package/src/subscriptions/mappers.ts +28 -0
- package/src/subscriptions/types.ts +143 -0
- package/src/travel/travel.ts +500 -0
- package/src/types/capabilities.ts +76 -0
- package/src/types/entity-traits.ts +69 -0
- package/src/types/entity.ts +39 -0
- package/src/types/index.ts +3 -0
- package/src/types.ts +140 -35
- package/src/{hash.ts → utils/hash.ts} +2 -2
- package/src/utils/system.ts +168 -0
- package/src/goods.ts +0 -124
- package/src/market.ts +0 -214
- package/src/rolls.ts +0 -8
- package/src/ship.ts +0 -36
- package/src/state.ts +0 -0
- package/src/syllables.ts +0 -1184
- package/src/system.ts +0 -37
- package/src/travel.ts +0 -259
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {ServerContract} from '../contracts'
|
|
2
|
+
import {Ship} from '../entities/ship'
|
|
3
|
+
import {Warehouse} from '../entities/warehouse'
|
|
4
|
+
import {Container} from '../entities/container'
|
|
5
|
+
import type {WireEntity} from './types'
|
|
6
|
+
|
|
7
|
+
export function mapEntity(ei: ServerContract.Types.entity_info): Ship | Warehouse | Container {
|
|
8
|
+
if (ei.type.equals('ship')) return new Ship(ei)
|
|
9
|
+
if (ei.type.equals('warehouse')) return new Warehouse(ei)
|
|
10
|
+
if (ei.type.equals('container')) return new Container(ei)
|
|
11
|
+
throw new Error(`mapEntity: unknown entity type ${ei.type.toString()}`)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function parseWireEntity(raw: WireEntity): ServerContract.Types.entity_info {
|
|
15
|
+
const shaped: Record<string, unknown> = {...raw}
|
|
16
|
+
|
|
17
|
+
if (typeof shaped.type === 'number' && typeof shaped.type_name === 'string') {
|
|
18
|
+
shaped.type = shaped.type_name
|
|
19
|
+
}
|
|
20
|
+
delete shaped.type_name
|
|
21
|
+
|
|
22
|
+
if (shaped.entity_name === undefined && typeof shaped.name === 'string') {
|
|
23
|
+
shaped.entity_name = shaped.name
|
|
24
|
+
}
|
|
25
|
+
delete shaped.name
|
|
26
|
+
|
|
27
|
+
return ServerContract.Types.entity_info.from(shaped)
|
|
28
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type {ServerContract} from '../contracts'
|
|
2
|
+
|
|
3
|
+
export type EntityInfo = ServerContract.Types.entity_info
|
|
4
|
+
|
|
5
|
+
export interface BoundingBox {
|
|
6
|
+
min_x: number
|
|
7
|
+
min_y: number
|
|
8
|
+
max_x: number
|
|
9
|
+
max_y: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface WireCoordinates {
|
|
13
|
+
x: number
|
|
14
|
+
y: number
|
|
15
|
+
z?: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// --- Client → Server ---
|
|
19
|
+
|
|
20
|
+
export type SubscribeMessage = {
|
|
21
|
+
type: 'subscribe'
|
|
22
|
+
sub_id: string
|
|
23
|
+
bounds?: BoundingBox
|
|
24
|
+
owner?: string
|
|
25
|
+
prioritize_owner?: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type UpdateBoundsMessage = {
|
|
29
|
+
type: 'update_bounds'
|
|
30
|
+
sub_id: string
|
|
31
|
+
bounds: BoundingBox
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type UnsubscribeMessage = {
|
|
35
|
+
type: 'unsubscribe'
|
|
36
|
+
sub_id: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type SubscribeEntityMessage = {
|
|
40
|
+
type: 'subscribe_entity'
|
|
41
|
+
sub_id: string
|
|
42
|
+
entity_type: 'ship' | 'warehouse' | 'container'
|
|
43
|
+
entity_id: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type UnsubscribeEntityMessage = {
|
|
47
|
+
type: 'unsubscribe_entity'
|
|
48
|
+
sub_id: string
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type SubscribeEventsMessage = {
|
|
52
|
+
type: 'subscribe_events'
|
|
53
|
+
sub_id: string
|
|
54
|
+
event_filter?: Record<string, unknown>
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type UnsubscribeEventsMessage = {
|
|
58
|
+
type: 'unsubscribe_events'
|
|
59
|
+
sub_id: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type PingMessage = {type: 'ping'}
|
|
63
|
+
|
|
64
|
+
export type ClientMessage =
|
|
65
|
+
| SubscribeMessage
|
|
66
|
+
| UpdateBoundsMessage
|
|
67
|
+
| UnsubscribeMessage
|
|
68
|
+
| SubscribeEntityMessage
|
|
69
|
+
| UnsubscribeEntityMessage
|
|
70
|
+
| SubscribeEventsMessage
|
|
71
|
+
| UnsubscribeEventsMessage
|
|
72
|
+
| PingMessage
|
|
73
|
+
|
|
74
|
+
// --- Server → Client ---
|
|
75
|
+
|
|
76
|
+
export type AckMessage = {
|
|
77
|
+
type: 'subscribed' | 'unsubscribed' | 'bounds_updated'
|
|
78
|
+
sub_id: string
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type WireEntity = Record<string, unknown> & {
|
|
82
|
+
type: number
|
|
83
|
+
type_name: 'ship' | 'warehouse' | 'container'
|
|
84
|
+
id: string | number
|
|
85
|
+
owner: string
|
|
86
|
+
coordinates: WireCoordinates
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export type SnapshotMessage = {
|
|
90
|
+
type: 'snapshot'
|
|
91
|
+
sub_id: string
|
|
92
|
+
seq: number
|
|
93
|
+
entities: WireEntity[]
|
|
94
|
+
truncated?: boolean
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export type UpdateMessage = {
|
|
98
|
+
type: 'update'
|
|
99
|
+
sub_ids: string[]
|
|
100
|
+
entity_id: number
|
|
101
|
+
entity: WireEntity
|
|
102
|
+
seq: number
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export type BoundsDeltaMessage = {
|
|
106
|
+
type: 'bounds_delta'
|
|
107
|
+
sub_id: string
|
|
108
|
+
entered: WireEntity[]
|
|
109
|
+
exited: number[]
|
|
110
|
+
seq: number
|
|
111
|
+
truncated?: boolean
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export type EventMessage = {
|
|
115
|
+
type: 'event'
|
|
116
|
+
sub_id: string
|
|
117
|
+
catchup: boolean
|
|
118
|
+
events: Array<Record<string, unknown>>
|
|
119
|
+
seq?: number
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export type EventCatchupCompleteMessage = {
|
|
123
|
+
type: 'event_catchup_complete'
|
|
124
|
+
sub_id: string
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export type PongMessage = {type: 'pong'}
|
|
128
|
+
|
|
129
|
+
export type ErrorMessage = {
|
|
130
|
+
type: 'error'
|
|
131
|
+
error: string
|
|
132
|
+
sub_id?: string
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export type ServerMessage =
|
|
136
|
+
| AckMessage
|
|
137
|
+
| SnapshotMessage
|
|
138
|
+
| UpdateMessage
|
|
139
|
+
| BoundsDeltaMessage
|
|
140
|
+
| EventMessage
|
|
141
|
+
| EventCatchupCompleteMessage
|
|
142
|
+
| PongMessage
|
|
143
|
+
| ErrorMessage
|
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Travel calculations for ship movement, energy usage, and flight times.
|
|
3
|
+
*
|
|
4
|
+
* Functions prefixed with `calc_` are contract-parity functions that mirror
|
|
5
|
+
* the C++ implementation in the server contract (schedule.cpp, ship.cpp).
|
|
6
|
+
* These use snake_case intentionally to match the contract naming convention
|
|
7
|
+
* and signal that they must produce identical results to the on-chain code.
|
|
8
|
+
*
|
|
9
|
+
* Functions prefixed with `calculate` are higher-level SDK helpers that may
|
|
10
|
+
* combine multiple contract calculations for convenience.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
type Checksum256,
|
|
15
|
+
Int64,
|
|
16
|
+
type Int64Type,
|
|
17
|
+
UInt16,
|
|
18
|
+
UInt32,
|
|
19
|
+
type UInt32Type,
|
|
20
|
+
UInt64,
|
|
21
|
+
type UInt64Type,
|
|
22
|
+
} from '@wharfkit/antelope'
|
|
23
|
+
|
|
24
|
+
import type {ServerContract} from '../contracts'
|
|
25
|
+
import {
|
|
26
|
+
BASE_ORBITAL_MASS,
|
|
27
|
+
type CargoMassInfo,
|
|
28
|
+
type Distance,
|
|
29
|
+
MAX_ORBITAL_ALTITUDE,
|
|
30
|
+
MIN_ORBITAL_ALTITUDE,
|
|
31
|
+
PRECISION,
|
|
32
|
+
type ShipLike,
|
|
33
|
+
TaskType,
|
|
34
|
+
} from '../types'
|
|
35
|
+
import {getItem} from '../data/catalog'
|
|
36
|
+
import {hasSystem} from '../utils/system'
|
|
37
|
+
|
|
38
|
+
export function calc_orbital_altitude(mass: number): number {
|
|
39
|
+
if (mass <= BASE_ORBITAL_MASS) {
|
|
40
|
+
return MIN_ORBITAL_ALTITUDE
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const ratio = mass / BASE_ORBITAL_MASS
|
|
44
|
+
const capRatio = 10.0
|
|
45
|
+
let scale = Math.log(ratio) / Math.log(capRatio)
|
|
46
|
+
scale = Math.min(scale, 1.0)
|
|
47
|
+
|
|
48
|
+
return MIN_ORBITAL_ALTITUDE + Math.floor((MAX_ORBITAL_ALTITUDE - MIN_ORBITAL_ALTITUDE) * scale)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function distanceBetweenCoordinates(
|
|
52
|
+
origin: ServerContract.ActionParams.Type.coordinates,
|
|
53
|
+
destination: ServerContract.ActionParams.Type.coordinates
|
|
54
|
+
): UInt64 {
|
|
55
|
+
return distanceBetweenPoints(origin.x, origin.y, destination.x, destination.y)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function distanceBetweenPoints(
|
|
59
|
+
x1: Int64Type,
|
|
60
|
+
y1: Int64Type,
|
|
61
|
+
x2: Int64Type,
|
|
62
|
+
y2: Int64Type
|
|
63
|
+
): UInt64 {
|
|
64
|
+
const x = (x1 - x2) ** 2
|
|
65
|
+
const y = (y1 - y2) ** 2
|
|
66
|
+
return UInt64.from(Math.sqrt(x + y) * PRECISION)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function lerp(
|
|
70
|
+
origin: ServerContract.ActionParams.Type.coordinates,
|
|
71
|
+
destination: ServerContract.ActionParams.Type.coordinates,
|
|
72
|
+
time: number
|
|
73
|
+
): ServerContract.ActionParams.Type.coordinates {
|
|
74
|
+
return {
|
|
75
|
+
x: (1 - time) * Number(origin.x) + time * Number(destination.x),
|
|
76
|
+
y: (1 - time) * Number(origin.y) + time * Number(destination.y),
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function rotation(
|
|
81
|
+
origin: ServerContract.ActionParams.Type.coordinates,
|
|
82
|
+
destination: ServerContract.ActionParams.Type.coordinates
|
|
83
|
+
) {
|
|
84
|
+
return Math.atan2(destination.y - origin.y, destination.x - origin.x) * (180 / Math.PI) + 90
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function findNearbyPlanets(
|
|
88
|
+
seed: Checksum256,
|
|
89
|
+
origin: ServerContract.ActionParams.Type.coordinates,
|
|
90
|
+
maxDistance: UInt64Type = 20 * PRECISION
|
|
91
|
+
): Distance[] {
|
|
92
|
+
const nearbySystems: Distance[] = []
|
|
93
|
+
|
|
94
|
+
const max = UInt64.from(maxDistance / PRECISION)
|
|
95
|
+
const xMin = Int64.from(origin.x).subtracting(max)
|
|
96
|
+
const xMax = Int64.from(origin.x).adding(max)
|
|
97
|
+
const yMin = Int64.from(origin.y).subtracting(max)
|
|
98
|
+
const yMax = Int64.from(origin.y).adding(max)
|
|
99
|
+
|
|
100
|
+
for (let x = Number(xMin); x <= Number(xMax); x++) {
|
|
101
|
+
for (let y = Number(yMin); y <= Number(yMax); y++) {
|
|
102
|
+
const samePlace = x === Number(origin.x) && y === Number(origin.y)
|
|
103
|
+
if (!samePlace) {
|
|
104
|
+
const distance = distanceBetweenPoints(origin.x, origin.y, x, y)
|
|
105
|
+
if (Number(distance) <= Number(maxDistance)) {
|
|
106
|
+
const system = hasSystem(seed, {x, y})
|
|
107
|
+
if (system) {
|
|
108
|
+
nearbySystems.push({origin, destination: {x, y}, distance})
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return nearbySystems
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function calc_rechargetime(
|
|
119
|
+
capacity: UInt32Type,
|
|
120
|
+
energy: UInt32Type,
|
|
121
|
+
recharge: UInt32Type
|
|
122
|
+
): UInt32 {
|
|
123
|
+
const cap = UInt32.from(capacity)
|
|
124
|
+
const eng = UInt32.from(energy)
|
|
125
|
+
if (eng.gte(cap)) return UInt32.zero
|
|
126
|
+
return cap.subtracting(eng).dividing(recharge)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function calc_ship_rechargetime(ship: ShipLike): UInt32 {
|
|
130
|
+
if (!ship.generator) return UInt32.from(0)
|
|
131
|
+
return calc_rechargetime(
|
|
132
|
+
ship.generator.capacity,
|
|
133
|
+
ship.energy ?? UInt16.from(0),
|
|
134
|
+
ship.generator.recharge
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function calc_flighttime(distance: UInt64Type, acceleration: number): UInt32 {
|
|
139
|
+
return UInt32.from(2 * Math.sqrt(Number(distance) / acceleration))
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function calc_loader_flighttime(ship: ShipLike, mass: UInt64, altitude?: number): UInt32 {
|
|
143
|
+
const z = altitude ?? ship.coordinates.z?.toNumber() ?? calc_orbital_altitude(Number(mass))
|
|
144
|
+
return calc_flighttime(z, calc_loader_acceleration(ship, mass))
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function calc_loader_acceleration(ship: ShipLike, mass: UInt64): number {
|
|
148
|
+
const thrust = ship.loaders ? Number(ship.loaders.thrust) : 0
|
|
149
|
+
const loaderMass = ship.loaders ? Number(ship.loaders.mass) : 0
|
|
150
|
+
return calc_acceleration(thrust, Number(mass) + loaderMass)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function calc_ship_flighttime(ship: ShipLike, mass: UInt64, distance: UInt64): UInt32 {
|
|
154
|
+
const acceleration = calc_ship_acceleration(ship, mass)
|
|
155
|
+
return calc_flighttime(distance, acceleration)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function calc_ship_acceleration(ship: ShipLike, mass: UInt64): number {
|
|
159
|
+
const thrust = ship.engines ? Number(ship.engines.thrust) : 0
|
|
160
|
+
return calc_acceleration(thrust, Number(mass))
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function calc_acceleration(thrust: number, mass: number): number {
|
|
164
|
+
return (thrust / mass) * PRECISION
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function calc_ship_mass(ship: ShipLike, cargos: CargoMassInfo[]): UInt64 {
|
|
168
|
+
const mass = UInt64.from(0)
|
|
169
|
+
|
|
170
|
+
mass.add(ship.hullmass)
|
|
171
|
+
|
|
172
|
+
if (ship.loaders && ship.loaders.quantity.gt(UInt32.zero)) {
|
|
173
|
+
mass.add(ship.loaders.mass.multiplying(ship.loaders.quantity))
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
for (const cargo of cargos) {
|
|
177
|
+
const cargoMass = getItem(cargo.item_id).mass * Number(UInt32.from(cargo.quantity))
|
|
178
|
+
mass.add(UInt64.from(cargoMass))
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return mass
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function calc_energyusage(distance: UInt64Type, drain: UInt32Type): UInt32 {
|
|
185
|
+
return UInt64.from(distance).dividing(PRECISION).multiplying(drain)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function calculateTransferTime(
|
|
189
|
+
ship: ShipLike,
|
|
190
|
+
cargos: CargoMassInfo[],
|
|
191
|
+
quantities?: Map<number, number>
|
|
192
|
+
): UInt32 {
|
|
193
|
+
let mass = UInt64.from(0)
|
|
194
|
+
|
|
195
|
+
for (const cargo of cargos) {
|
|
196
|
+
const qty = quantities?.get(Number(cargo.item_id)) ?? 0
|
|
197
|
+
if (qty > 0) {
|
|
198
|
+
const good_mass = getItem(cargo.item_id).mass
|
|
199
|
+
const cargo_mass = good_mass * qty
|
|
200
|
+
mass = UInt64.from(mass).adding(UInt64.from(cargo_mass))
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (mass.equals(UInt64.zero)) {
|
|
205
|
+
return UInt32.from(0)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (!ship.loaders) return UInt32.from(0)
|
|
209
|
+
mass = UInt64.from(mass).adding(ship.loaders.mass)
|
|
210
|
+
const transfer_time = calc_loader_flighttime(ship, mass)
|
|
211
|
+
return transfer_time.dividing(ship.loaders.quantity)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function calculateRefuelingTime(ship: ShipLike): UInt32 {
|
|
215
|
+
return calc_ship_rechargetime(ship)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function calculateFlightTime(
|
|
219
|
+
ship: ShipLike,
|
|
220
|
+
cargos: CargoMassInfo[],
|
|
221
|
+
distance: UInt64Type
|
|
222
|
+
): UInt32 {
|
|
223
|
+
const mass = calc_ship_mass(ship, cargos)
|
|
224
|
+
return calc_ship_flighttime(ship, mass, distance)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export interface LoadTimeBreakdown {
|
|
228
|
+
unloadTime: number
|
|
229
|
+
loadTime: number
|
|
230
|
+
totalTime: number
|
|
231
|
+
unloadMass: number
|
|
232
|
+
loadMass: number
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function calculateLoadTimeBreakdown(
|
|
236
|
+
ship: ShipLike,
|
|
237
|
+
cargos: CargoMassInfo[],
|
|
238
|
+
loadQuantities?: Map<number, number>,
|
|
239
|
+
unloadQuantities?: Map<number, number>
|
|
240
|
+
): LoadTimeBreakdown {
|
|
241
|
+
let mass_unload = UInt64.from(0)
|
|
242
|
+
let mass_load = UInt64.from(0)
|
|
243
|
+
|
|
244
|
+
for (const cargo of cargos) {
|
|
245
|
+
const goodId = Number(cargo.item_id)
|
|
246
|
+
const loadQty = loadQuantities?.get(goodId) ?? 0
|
|
247
|
+
const unloadQty = unloadQuantities?.get(goodId) ?? 0
|
|
248
|
+
|
|
249
|
+
if (loadQty > 0 || unloadQty > 0) {
|
|
250
|
+
const good = getItem(cargo.item_id)
|
|
251
|
+
|
|
252
|
+
if (loadQty > 0) {
|
|
253
|
+
const cargo_mass = good.mass * loadQty
|
|
254
|
+
mass_load = UInt64.from(mass_load).adding(UInt64.from(cargo_mass))
|
|
255
|
+
}
|
|
256
|
+
if (unloadQty > 0) {
|
|
257
|
+
const cargo_mass = good.mass * unloadQty
|
|
258
|
+
mass_unload = UInt64.from(mass_unload).adding(UInt64.from(cargo_mass))
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
let unloadTime = 0
|
|
264
|
+
let loadTime = 0
|
|
265
|
+
|
|
266
|
+
if (mass_unload.gt(UInt64.zero) && ship.loaders) {
|
|
267
|
+
const totalMass = UInt64.from(mass_unload).adding(ship.loaders.mass)
|
|
268
|
+
unloadTime = Number(calc_loader_flighttime(ship, totalMass))
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (mass_load.gt(UInt64.zero) && ship.loaders) {
|
|
272
|
+
const totalMass = UInt64.from(mass_load).adding(ship.loaders.mass)
|
|
273
|
+
loadTime = Number(calc_loader_flighttime(ship, totalMass))
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const numLoaders = ship.loaders ? Number(ship.loaders.quantity) : 0
|
|
277
|
+
const totalTime = numLoaders > 0 ? (unloadTime + loadTime) / numLoaders : 0
|
|
278
|
+
const unloadTimePerLoader = numLoaders > 0 ? unloadTime / numLoaders : 0
|
|
279
|
+
const loadTimePerLoader = numLoaders > 0 ? loadTime / numLoaders : 0
|
|
280
|
+
|
|
281
|
+
return {
|
|
282
|
+
unloadTime: unloadTimePerLoader,
|
|
283
|
+
loadTime: loadTimePerLoader,
|
|
284
|
+
totalTime,
|
|
285
|
+
unloadMass: Number(mass_unload),
|
|
286
|
+
loadMass: Number(mass_load),
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export interface EstimatedTravelTime {
|
|
291
|
+
flightTime: UInt32
|
|
292
|
+
rechargeTime: UInt32
|
|
293
|
+
loadTime: UInt32
|
|
294
|
+
unloadTime: UInt32
|
|
295
|
+
total: UInt32
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export interface EstimateTravelTimeOptions {
|
|
299
|
+
needsRecharge?: boolean
|
|
300
|
+
loadMass?: UInt32Type
|
|
301
|
+
unloadMass?: UInt32Type
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export function estimateTravelTime(
|
|
305
|
+
ship: ShipLike,
|
|
306
|
+
travelMass: UInt64Type,
|
|
307
|
+
distance: UInt64Type,
|
|
308
|
+
options: EstimateTravelTimeOptions = {}
|
|
309
|
+
): EstimatedTravelTime {
|
|
310
|
+
const {needsRecharge = false, loadMass, unloadMass} = options
|
|
311
|
+
|
|
312
|
+
const flightTime = calc_ship_flighttime(ship, UInt64.from(travelMass), UInt64.from(distance))
|
|
313
|
+
const rechargeTime = needsRecharge ? calc_ship_rechargetime(ship) : UInt32.zero
|
|
314
|
+
|
|
315
|
+
let loadTime = UInt32.zero
|
|
316
|
+
let unloadTime = UInt32.zero
|
|
317
|
+
|
|
318
|
+
if (
|
|
319
|
+
loadMass &&
|
|
320
|
+
UInt32.from(loadMass).gt(UInt32.zero) &&
|
|
321
|
+
ship.loaders &&
|
|
322
|
+
ship.loaders.quantity.gt(UInt32.zero)
|
|
323
|
+
) {
|
|
324
|
+
const totalMass = UInt64.from(loadMass).adding(ship.loaders.mass)
|
|
325
|
+
loadTime = calc_loader_flighttime(ship, totalMass).dividing(ship.loaders.quantity)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (
|
|
329
|
+
unloadMass &&
|
|
330
|
+
UInt32.from(unloadMass).gt(UInt32.zero) &&
|
|
331
|
+
ship.loaders &&
|
|
332
|
+
ship.loaders.quantity.gt(UInt32.zero)
|
|
333
|
+
) {
|
|
334
|
+
const totalMass = UInt64.from(unloadMass).adding(ship.loaders.mass)
|
|
335
|
+
unloadTime = calc_loader_flighttime(ship, totalMass).dividing(ship.loaders.quantity)
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
flightTime,
|
|
340
|
+
rechargeTime,
|
|
341
|
+
loadTime,
|
|
342
|
+
unloadTime,
|
|
343
|
+
total: flightTime.adding(rechargeTime).adding(loadTime).adding(unloadTime),
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export function estimateDealTravelTime(
|
|
348
|
+
ship: ShipLike,
|
|
349
|
+
shipMass: UInt64Type,
|
|
350
|
+
distance: UInt64Type,
|
|
351
|
+
loadMass: UInt32Type
|
|
352
|
+
): UInt32 {
|
|
353
|
+
const needsRecharge = !hasEnergyForDistance(ship, distance)
|
|
354
|
+
const estimate = estimateTravelTime(ship, shipMass, distance, {
|
|
355
|
+
needsRecharge,
|
|
356
|
+
loadMass,
|
|
357
|
+
})
|
|
358
|
+
return estimate.total
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export function hasEnergyForDistance(ship: ShipLike, distance: UInt64Type): boolean {
|
|
362
|
+
if (!ship.engines) return false
|
|
363
|
+
const energyNeeded = UInt64.from(distance).dividing(PRECISION).multiplying(ship.engines.drain)
|
|
364
|
+
return UInt64.from(ship.energy ?? 0).gte(energyNeeded)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export interface TransferEntity {
|
|
368
|
+
location: {z?: {toNumber(): number} | number}
|
|
369
|
+
loaders?: {
|
|
370
|
+
thrust: {toNumber(): number} | number
|
|
371
|
+
mass: {toNumber(): number} | number
|
|
372
|
+
quantity: {toNumber(): number} | number
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export interface HasScheduleAndLocation {
|
|
377
|
+
coordinates: ServerContract.ActionParams.Type.coordinates
|
|
378
|
+
schedule?: ServerContract.Types.schedule
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export function getFlightOrigin(
|
|
382
|
+
entity: HasScheduleAndLocation,
|
|
383
|
+
flightTaskIndex: number
|
|
384
|
+
): ServerContract.ActionParams.Type.coordinates {
|
|
385
|
+
if (!entity.schedule) return entity.coordinates
|
|
386
|
+
|
|
387
|
+
let origin = entity.coordinates
|
|
388
|
+
for (let i = 0; i < flightTaskIndex && i < entity.schedule.tasks.length; i++) {
|
|
389
|
+
const task = entity.schedule.tasks[i]
|
|
390
|
+
if (task.type.equals(TaskType.TRAVEL) && task.coordinates) {
|
|
391
|
+
origin = task.coordinates
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return origin
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export function getDestinationLocation(
|
|
398
|
+
entity: HasScheduleAndLocation
|
|
399
|
+
): ServerContract.ActionParams.Type.coordinates | undefined {
|
|
400
|
+
if (!entity.schedule) return undefined
|
|
401
|
+
|
|
402
|
+
for (let i = entity.schedule.tasks.length - 1; i >= 0; i--) {
|
|
403
|
+
const task = entity.schedule.tasks[i]
|
|
404
|
+
if (task.type.equals(TaskType.TRAVEL) && task.coordinates) {
|
|
405
|
+
return task.coordinates
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return undefined
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
export function getPositionAt(
|
|
412
|
+
entity: HasScheduleAndLocation,
|
|
413
|
+
taskIndex: number,
|
|
414
|
+
taskProgress: number
|
|
415
|
+
): ServerContract.ActionParams.Type.coordinates {
|
|
416
|
+
if (!entity.schedule || entity.schedule.tasks.length === 0 || taskIndex < 0) {
|
|
417
|
+
return entity.coordinates
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const task = entity.schedule.tasks[taskIndex]
|
|
421
|
+
|
|
422
|
+
if (!task.type.equals(TaskType.TRAVEL) || !task.coordinates) {
|
|
423
|
+
return getFlightOrigin(entity, taskIndex)
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const origin = getFlightOrigin(entity, taskIndex)
|
|
427
|
+
const destination = task.coordinates
|
|
428
|
+
|
|
429
|
+
const interpolated = lerp(origin, destination, taskProgress)
|
|
430
|
+
return {
|
|
431
|
+
x: Math.round(interpolated.x),
|
|
432
|
+
y: Math.round(interpolated.y),
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export function calc_transfer_duration(
|
|
437
|
+
source: TransferEntity,
|
|
438
|
+
dest: TransferEntity,
|
|
439
|
+
cargoMass: number
|
|
440
|
+
): number {
|
|
441
|
+
if (cargoMass === 0) {
|
|
442
|
+
return 0
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
let totalThrust = 0
|
|
446
|
+
let totalLoaderMass = 0
|
|
447
|
+
let totalQuantity = 0
|
|
448
|
+
|
|
449
|
+
if (source.loaders) {
|
|
450
|
+
const thrust =
|
|
451
|
+
typeof source.loaders.thrust === 'number'
|
|
452
|
+
? source.loaders.thrust
|
|
453
|
+
: source.loaders.thrust.toNumber()
|
|
454
|
+
const mass =
|
|
455
|
+
typeof source.loaders.mass === 'number'
|
|
456
|
+
? source.loaders.mass
|
|
457
|
+
: source.loaders.mass.toNumber()
|
|
458
|
+
const qty =
|
|
459
|
+
typeof source.loaders.quantity === 'number'
|
|
460
|
+
? source.loaders.quantity
|
|
461
|
+
: source.loaders.quantity.toNumber()
|
|
462
|
+
totalThrust += thrust * qty
|
|
463
|
+
totalLoaderMass += mass * qty
|
|
464
|
+
totalQuantity += qty
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (dest.loaders) {
|
|
468
|
+
const thrust =
|
|
469
|
+
typeof dest.loaders.thrust === 'number'
|
|
470
|
+
? dest.loaders.thrust
|
|
471
|
+
: dest.loaders.thrust.toNumber()
|
|
472
|
+
const mass =
|
|
473
|
+
typeof dest.loaders.mass === 'number' ? dest.loaders.mass : dest.loaders.mass.toNumber()
|
|
474
|
+
const qty =
|
|
475
|
+
typeof dest.loaders.quantity === 'number'
|
|
476
|
+
? dest.loaders.quantity
|
|
477
|
+
: dest.loaders.quantity.toNumber()
|
|
478
|
+
totalThrust += thrust * qty
|
|
479
|
+
totalLoaderMass += mass * qty
|
|
480
|
+
totalQuantity += qty
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (totalThrust === 0 || totalQuantity === 0) {
|
|
484
|
+
return 0
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const sourceZ =
|
|
488
|
+
typeof source.location.z === 'number'
|
|
489
|
+
? source.location.z
|
|
490
|
+
: (source.location.z?.toNumber() ?? 0)
|
|
491
|
+
const destZ =
|
|
492
|
+
typeof dest.location.z === 'number' ? dest.location.z : (dest.location.z?.toNumber() ?? 0)
|
|
493
|
+
const distance = Math.abs(sourceZ - destZ)
|
|
494
|
+
|
|
495
|
+
const totalMass = cargoMass + totalLoaderMass
|
|
496
|
+
const acceleration = calc_acceleration(totalThrust, totalMass)
|
|
497
|
+
const flightTime = 2 * Math.sqrt(distance / acceleration)
|
|
498
|
+
|
|
499
|
+
return Math.floor(flightTime / totalQuantity)
|
|
500
|
+
}
|