@shipload/sdk 1.0.0-next.3 → 1.0.0-next.30
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 +1847 -962
- package/lib/shipload.js +9088 -4854
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +8957 -4805
- package/lib/shipload.m.js.map +1 -1
- package/lib/testing.d.ts +856 -0
- package/lib/testing.js +3739 -0
- package/lib/testing.js.map +1 -0
- package/lib/testing.m.js +3733 -0
- package/lib/testing.m.js.map +1 -0
- package/package.json +15 -2
- package/src/capabilities/craftable.ts +51 -0
- package/src/capabilities/crafting.test.ts +7 -0
- package/src/capabilities/crafting.ts +3 -3
- package/src/capabilities/gathering.ts +17 -7
- package/src/capabilities/index.ts +0 -1
- package/src/capabilities/modules.ts +6 -0
- package/src/capabilities/storage.ts +16 -1
- package/src/contracts/platform.ts +231 -3
- package/src/contracts/server.ts +816 -471
- package/src/data/capabilities.ts +14 -329
- package/src/data/capability-formulas.ts +76 -0
- package/src/data/catalog.ts +0 -5
- package/src/data/colors.ts +14 -47
- package/src/data/entities.json +46 -10
- package/src/data/item-ids.ts +15 -12
- package/src/data/items.json +302 -38
- package/src/data/kind-registry.json +85 -0
- package/src/data/kind-registry.ts +150 -0
- package/src/data/metadata.ts +100 -31
- package/src/data/recipes-runtime.ts +3 -23
- package/src/data/recipes.json +250 -113
- package/src/derivation/build-methods.ts +45 -0
- package/src/derivation/capabilities.ts +415 -0
- package/src/derivation/capability-mappings.ts +117 -0
- package/src/derivation/crafting.ts +23 -24
- package/src/derivation/index.ts +17 -2
- package/src/derivation/reserve-regen.ts +34 -0
- package/src/derivation/resources.ts +125 -38
- package/src/derivation/stars.test.ts +51 -0
- package/src/derivation/stars.ts +15 -0
- package/src/derivation/stats.ts +6 -6
- package/src/derivation/stratum.ts +15 -19
- package/src/derivation/tiers.ts +28 -7
- package/src/entities/entity.ts +98 -0
- package/src/entities/gamestate.ts +3 -28
- package/src/entities/makers.ts +91 -136
- package/src/entities/slot-multiplier.ts +39 -0
- package/src/errors.ts +10 -15
- package/src/format.ts +26 -4
- package/src/index-module.ts +189 -47
- package/src/managers/actions.ts +252 -83
- package/src/managers/base.ts +6 -2
- package/src/managers/construction-types.ts +79 -0
- package/src/managers/construction.ts +396 -0
- package/src/managers/context.ts +11 -1
- package/src/managers/entities.ts +18 -66
- package/src/managers/epochs.ts +40 -0
- package/src/managers/index.ts +17 -1
- package/src/managers/locations.ts +25 -29
- package/src/managers/nft.ts +28 -0
- package/src/managers/plot.ts +127 -0
- package/src/nft/atomicassets.abi.json +1342 -0
- package/src/nft/atomicassets.ts +237 -0
- package/src/nft/atomicdata.ts +130 -0
- package/src/nft/buildImmutableData.ts +321 -0
- package/src/nft/description.ts +37 -15
- package/src/nft/index.ts +3 -0
- package/src/resolution/describe-module.ts +5 -8
- package/src/resolution/display-name.ts +38 -10
- package/src/resolution/resolve-item.ts +22 -20
- package/src/scheduling/accessor.ts +68 -22
- package/src/scheduling/availability.ts +108 -0
- package/src/scheduling/energy.ts +48 -0
- package/src/scheduling/lane-core.ts +130 -0
- package/src/scheduling/lanes.ts +60 -0
- package/src/scheduling/projection.ts +121 -94
- package/src/scheduling/schedule.ts +237 -103
- package/src/scheduling/task-cargo.ts +46 -0
- package/src/shipload.ts +16 -1
- package/src/subscriptions/manager.ts +40 -6
- package/src/subscriptions/mappers.ts +3 -8
- package/src/subscriptions/types.ts +3 -2
- package/src/testing/catalog-hash.ts +19 -0
- package/src/testing/index.ts +2 -0
- package/src/testing/projection-parity.ts +143 -0
- package/src/travel/travel.ts +90 -13
- package/src/types/capabilities.ts +1 -0
- package/src/types/index.ts +0 -1
- package/src/types.ts +19 -12
- package/src/utils/cargo.ts +27 -0
- package/src/utils/display-name.ts +61 -0
- package/src/utils/system.ts +25 -24
- package/src/capabilities/loading.ts +0 -8
- package/src/entities/container.ts +0 -108
- package/src/entities/ship-deploy.ts +0 -258
- package/src/entities/ship.ts +0 -204
- package/src/entities/warehouse.ts +0 -119
- package/src/types/entity-traits.ts +0 -69
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type {UInt16, UInt32} from '@wharfkit/antelope'
|
|
2
|
+
import type {ServerContract} from '../contracts'
|
|
3
|
+
import type {ProjectedEntity} from '../scheduling/projection'
|
|
4
|
+
import {type CargoStack, cargoItemToStack, mergeStacks} from '../capabilities/storage'
|
|
5
|
+
|
|
6
|
+
export interface ContractProjectedState {
|
|
7
|
+
owner: {toString(): string}
|
|
8
|
+
coordinates: ServerContract.Types.coordinates
|
|
9
|
+
energy?: UInt16
|
|
10
|
+
cargomass: UInt32
|
|
11
|
+
cargo: ServerContract.Types.cargo_view[]
|
|
12
|
+
hullmass?: UInt32
|
|
13
|
+
capacity?: UInt32
|
|
14
|
+
engines?: ServerContract.Types.movement_stats
|
|
15
|
+
loaders?: ServerContract.Types.loader_stats
|
|
16
|
+
generator?: ServerContract.Types.energy_stats
|
|
17
|
+
hauler?: ServerContract.Types.hauler_stats
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ProjectionComparisonOptions {
|
|
21
|
+
step?: number
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function assertProjectionEquals(
|
|
25
|
+
contract: ContractProjectedState,
|
|
26
|
+
sdk: ProjectedEntity,
|
|
27
|
+
options: ProjectionComparisonOptions = {}
|
|
28
|
+
): void {
|
|
29
|
+
const mismatches: string[] = []
|
|
30
|
+
|
|
31
|
+
const record = (name: string, c: unknown, s: unknown) => {
|
|
32
|
+
if (c !== s) mismatches.push(` ${name}: contract=${fmt(c)} sdk=${fmt(s)}`)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const recordStatBlock = (name: string, c: unknown, s: unknown) => {
|
|
36
|
+
const cPresent = c !== undefined && c !== null
|
|
37
|
+
const sPresent = s !== undefined && s !== null
|
|
38
|
+
if (cPresent !== sPresent) {
|
|
39
|
+
mismatches.push(
|
|
40
|
+
` ${name}: contract=${cPresent ? 'present' : 'absent'} sdk=${sPresent ? 'present' : 'absent'}`
|
|
41
|
+
)
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
if (!cPresent) return
|
|
45
|
+
const cn = JSON.stringify(normaliseStatBlock(c))
|
|
46
|
+
const sn = JSON.stringify(normaliseStatBlock(s))
|
|
47
|
+
if (cn !== sn) mismatches.push(` ${name}: contract=${cn} sdk=${sn}`)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
record('coordinates.x', toNum(contract.coordinates.x), Number(sdk.location.x))
|
|
51
|
+
record('coordinates.y', toNum(contract.coordinates.y), Number(sdk.location.y))
|
|
52
|
+
record('energy', toNum(contract.energy), Number(sdk.energy))
|
|
53
|
+
record('cargomass', toNum(contract.cargomass), Number(sdk.cargoMass))
|
|
54
|
+
record('hullmass', toNum(contract.hullmass), Number(sdk.shipMass))
|
|
55
|
+
record('capacity', toNum(contract.capacity), sdk.capacity ? Number(sdk.capacity) : undefined)
|
|
56
|
+
|
|
57
|
+
recordStatBlock('engines', contract.engines, sdk.engines)
|
|
58
|
+
recordStatBlock('loaders', contract.loaders, sdk.loaders)
|
|
59
|
+
recordStatBlock('generator', contract.generator, sdk.generator)
|
|
60
|
+
recordStatBlock('hauler', contract.hauler, sdk.hauler)
|
|
61
|
+
|
|
62
|
+
if (contract.cargo.length > 0 || sdk.cargo.length > 0) {
|
|
63
|
+
const contractCargo = normaliseCargo(mergeContractCargo(contract.cargo))
|
|
64
|
+
const sdkCargo = normaliseCargo(sdk.cargo)
|
|
65
|
+
if (contractCargo.length !== sdkCargo.length) {
|
|
66
|
+
mismatches.push(
|
|
67
|
+
` cargo.length: contract=${contractCargo.length} sdk=${sdkCargo.length}`
|
|
68
|
+
)
|
|
69
|
+
} else {
|
|
70
|
+
for (let i = 0; i < contractCargo.length; i++) {
|
|
71
|
+
const c = contractCargo[i]
|
|
72
|
+
const s = sdkCargo[i]
|
|
73
|
+
if (c.itemId !== s.itemId || c.stats !== s.stats || c.quantity !== s.quantity) {
|
|
74
|
+
mismatches.push(
|
|
75
|
+
` cargo[${i}]: contract={item:${c.itemId},stats:${c.stats},qty:${c.quantity}} sdk={item:${s.itemId},stats:${s.stats},qty:${s.quantity}}`
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (mismatches.length > 0) {
|
|
83
|
+
const header =
|
|
84
|
+
options.step !== undefined
|
|
85
|
+
? `projection divergence at step ${options.step}:`
|
|
86
|
+
: 'projection divergence:'
|
|
87
|
+
throw new Error([header, ...mismatches].join('\n'))
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
interface NormalisedStack {
|
|
92
|
+
itemId: number
|
|
93
|
+
stats: string
|
|
94
|
+
quantity: string
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function mergeContractCargo(cargo: ServerContract.Types.cargo_view[]): CargoStack[] {
|
|
98
|
+
return cargo.reduce<CargoStack[]>(
|
|
99
|
+
(acc, row) =>
|
|
100
|
+
mergeStacks(acc, cargoItemToStack(row as unknown as ServerContract.Types.cargo_item)),
|
|
101
|
+
[]
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function normaliseCargo(cargo: CargoStack[]): NormalisedStack[] {
|
|
106
|
+
return cargo
|
|
107
|
+
.map((s) => ({
|
|
108
|
+
itemId: Number(s.item_id),
|
|
109
|
+
stats: BigInt(s.stats.toString()).toString(),
|
|
110
|
+
quantity: BigInt(s.quantity.toString()).toString(),
|
|
111
|
+
}))
|
|
112
|
+
.sort(stackSort)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function stackSort(a: NormalisedStack, b: NormalisedStack): number {
|
|
116
|
+
if (a.itemId !== b.itemId) return a.itemId - b.itemId
|
|
117
|
+
return a.stats < b.stats ? -1 : a.stats > b.stats ? 1 : 0
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function toNum(v: unknown): number | undefined {
|
|
121
|
+
if (v === undefined || v === null) return undefined
|
|
122
|
+
if (typeof v === 'number') return v
|
|
123
|
+
if (typeof v === 'bigint') return Number(v)
|
|
124
|
+
if (typeof (v as {toNumber?: unknown}).toNumber === 'function') {
|
|
125
|
+
return (v as {toNumber(): number}).toNumber()
|
|
126
|
+
}
|
|
127
|
+
return Number(v as number)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function fmt(v: unknown): string {
|
|
131
|
+
if (v === undefined) return 'undefined'
|
|
132
|
+
if (v === null) return 'null'
|
|
133
|
+
return String(v)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function normaliseStatBlock(block: unknown): Record<string, number> {
|
|
137
|
+
const out: Record<string, number> = {}
|
|
138
|
+
const obj = block as Record<string, unknown>
|
|
139
|
+
for (const k of Object.keys(obj).sort()) {
|
|
140
|
+
out[k] = toNum(obj[k]) ?? 0
|
|
141
|
+
}
|
|
142
|
+
return out
|
|
143
|
+
}
|
package/src/travel/travel.ts
CHANGED
|
@@ -28,12 +28,17 @@ import {
|
|
|
28
28
|
type Distance,
|
|
29
29
|
MAX_ORBITAL_ALTITUDE,
|
|
30
30
|
MIN_ORBITAL_ALTITUDE,
|
|
31
|
+
MIN_TRANSFER_DISTANCE_ORBITAL_VESSEL,
|
|
32
|
+
MIN_TRANSFER_DISTANCE_PLANETARY_STRUCTURE,
|
|
31
33
|
PRECISION,
|
|
32
34
|
type ShipLike,
|
|
33
35
|
TaskType,
|
|
34
36
|
} from '../types'
|
|
37
|
+
import {EntityClass} from '../data/kind-registry'
|
|
35
38
|
import {getItem} from '../data/catalog'
|
|
36
39
|
import {hasSystem} from '../utils/system'
|
|
40
|
+
import * as scheduleModel from '../scheduling/schedule'
|
|
41
|
+
import type {ScheduleData} from '../scheduling/schedule'
|
|
37
42
|
|
|
38
43
|
export function calc_orbital_altitude(mass: number): number {
|
|
39
44
|
if (mass <= BASE_ORBITAL_MASS) {
|
|
@@ -77,6 +82,60 @@ export function lerp(
|
|
|
77
82
|
}
|
|
78
83
|
}
|
|
79
84
|
|
|
85
|
+
export interface FloatPosition {
|
|
86
|
+
x: number
|
|
87
|
+
y: number
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function easeFlightProgress(t: number): number {
|
|
91
|
+
if (t <= 0) return 0
|
|
92
|
+
if (t >= 1) return 1
|
|
93
|
+
return t < 0.5 ? 2 * t * t : 1 - 2 * (1 - t) * (1 - t)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function flightSpeedFactor(t: number): number {
|
|
97
|
+
if (t <= 0 || t >= 1) return 0
|
|
98
|
+
return t < 0.5 ? 4 * t : 4 * (1 - t)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function interpolateFlightPosition(
|
|
102
|
+
origin: {x: Int64Type | number; y: Int64Type | number},
|
|
103
|
+
destination: {x: Int64Type | number; y: Int64Type | number},
|
|
104
|
+
taskProgress: number,
|
|
105
|
+
options?: {easing?: 'physics' | 'linear'}
|
|
106
|
+
): FloatPosition {
|
|
107
|
+
const t = options?.easing === 'linear' ? taskProgress : easeFlightProgress(taskProgress)
|
|
108
|
+
return {
|
|
109
|
+
x: (1 - t) * Number(origin.x) + t * Number(destination.x),
|
|
110
|
+
y: (1 - t) * Number(origin.y) + t * Number(destination.y),
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function getInterpolatedPosition(
|
|
115
|
+
entity: HasScheduleAndLocation,
|
|
116
|
+
taskIndex: number,
|
|
117
|
+
taskProgress: number
|
|
118
|
+
): FloatPosition {
|
|
119
|
+
const tasks = mobilityTasks(entity)
|
|
120
|
+
if (tasks.length === 0) {
|
|
121
|
+
return {x: Number(entity.coordinates.x), y: Number(entity.coordinates.y)}
|
|
122
|
+
}
|
|
123
|
+
if (taskIndex < 0) {
|
|
124
|
+
const settled = getFlightOrigin(entity, tasks.length)
|
|
125
|
+
return {x: Number(settled.x), y: Number(settled.y)}
|
|
126
|
+
}
|
|
127
|
+
const task = tasks[taskIndex]
|
|
128
|
+
if (!task.type.equals(TaskType.TRAVEL) || !task.coordinates) {
|
|
129
|
+
const origin = getFlightOrigin(entity, taskIndex)
|
|
130
|
+
return {x: Number(origin.x), y: Number(origin.y)}
|
|
131
|
+
}
|
|
132
|
+
return interpolateFlightPosition(
|
|
133
|
+
getFlightOrigin(entity, taskIndex),
|
|
134
|
+
task.coordinates,
|
|
135
|
+
taskProgress
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
80
139
|
export function rotation(
|
|
81
140
|
origin: ServerContract.ActionParams.Type.coordinates,
|
|
82
141
|
destination: ServerContract.ActionParams.Type.coordinates
|
|
@@ -366,6 +425,7 @@ export function hasEnergyForDistance(ship: ShipLike, distance: UInt64Type): bool
|
|
|
366
425
|
|
|
367
426
|
export interface TransferEntity {
|
|
368
427
|
location: {z?: {toNumber(): number} | number}
|
|
428
|
+
entityClass: EntityClass
|
|
369
429
|
loaders?: {
|
|
370
430
|
thrust: {toNumber(): number} | number
|
|
371
431
|
mass: {toNumber(): number} | number
|
|
@@ -373,20 +433,22 @@ export interface TransferEntity {
|
|
|
373
433
|
}
|
|
374
434
|
}
|
|
375
435
|
|
|
376
|
-
export interface HasScheduleAndLocation {
|
|
436
|
+
export interface HasScheduleAndLocation extends ScheduleData {
|
|
377
437
|
coordinates: ServerContract.ActionParams.Type.coordinates
|
|
378
|
-
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function mobilityTasks(entity: HasScheduleAndLocation): ServerContract.Types.task[] {
|
|
441
|
+
return scheduleModel.mobilityLane(entity)?.schedule.tasks ?? []
|
|
379
442
|
}
|
|
380
443
|
|
|
381
444
|
export function getFlightOrigin(
|
|
382
445
|
entity: HasScheduleAndLocation,
|
|
383
446
|
flightTaskIndex: number
|
|
384
447
|
): ServerContract.ActionParams.Type.coordinates {
|
|
385
|
-
|
|
386
|
-
|
|
448
|
+
const tasks = mobilityTasks(entity)
|
|
387
449
|
let origin = entity.coordinates
|
|
388
|
-
for (let i = 0; i < flightTaskIndex && i <
|
|
389
|
-
const task =
|
|
450
|
+
for (let i = 0; i < flightTaskIndex && i < tasks.length; i++) {
|
|
451
|
+
const task = tasks[i]
|
|
390
452
|
if (task.type.equals(TaskType.TRAVEL) && task.coordinates) {
|
|
391
453
|
origin = task.coordinates
|
|
392
454
|
}
|
|
@@ -397,10 +459,9 @@ export function getFlightOrigin(
|
|
|
397
459
|
export function getDestinationLocation(
|
|
398
460
|
entity: HasScheduleAndLocation
|
|
399
461
|
): ServerContract.ActionParams.Type.coordinates | undefined {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
const task = entity.schedule.tasks[i]
|
|
462
|
+
const tasks = mobilityTasks(entity)
|
|
463
|
+
for (let i = tasks.length - 1; i >= 0; i--) {
|
|
464
|
+
const task = tasks[i]
|
|
404
465
|
if (task.type.equals(TaskType.TRAVEL) && task.coordinates) {
|
|
405
466
|
return task.coordinates
|
|
406
467
|
}
|
|
@@ -408,16 +469,21 @@ export function getDestinationLocation(
|
|
|
408
469
|
return undefined
|
|
409
470
|
}
|
|
410
471
|
|
|
472
|
+
/** Returns chain-tile coordinates (rounded). For visual position use getInterpolatedPosition. */
|
|
411
473
|
export function getPositionAt(
|
|
412
474
|
entity: HasScheduleAndLocation,
|
|
413
475
|
taskIndex: number,
|
|
414
476
|
taskProgress: number
|
|
415
477
|
): ServerContract.ActionParams.Type.coordinates {
|
|
416
|
-
|
|
478
|
+
const tasks = mobilityTasks(entity)
|
|
479
|
+
if (tasks.length === 0) {
|
|
417
480
|
return entity.coordinates
|
|
418
481
|
}
|
|
482
|
+
if (taskIndex < 0) {
|
|
483
|
+
return getFlightOrigin(entity, tasks.length)
|
|
484
|
+
}
|
|
419
485
|
|
|
420
|
-
const task =
|
|
486
|
+
const task = tasks[taskIndex]
|
|
421
487
|
|
|
422
488
|
if (!task.type.equals(TaskType.TRAVEL) || !task.coordinates) {
|
|
423
489
|
return getFlightOrigin(entity, taskIndex)
|
|
@@ -433,6 +499,12 @@ export function getPositionAt(
|
|
|
433
499
|
}
|
|
434
500
|
}
|
|
435
501
|
|
|
502
|
+
export function minTransferDistance(entityClass: EntityClass): number {
|
|
503
|
+
return entityClass === EntityClass.OrbitalVessel
|
|
504
|
+
? MIN_TRANSFER_DISTANCE_ORBITAL_VESSEL
|
|
505
|
+
: MIN_TRANSFER_DISTANCE_PLANETARY_STRUCTURE
|
|
506
|
+
}
|
|
507
|
+
|
|
436
508
|
export function calc_transfer_duration(
|
|
437
509
|
source: TransferEntity,
|
|
438
510
|
dest: TransferEntity,
|
|
@@ -490,7 +562,12 @@ export function calc_transfer_duration(
|
|
|
490
562
|
: (source.location.z?.toNumber() ?? 0)
|
|
491
563
|
const destZ =
|
|
492
564
|
typeof dest.location.z === 'number' ? dest.location.z : (dest.location.z?.toNumber() ?? 0)
|
|
493
|
-
const
|
|
565
|
+
const rawDistance = Math.abs(sourceZ - destZ)
|
|
566
|
+
const minDistance = Math.max(
|
|
567
|
+
minTransferDistance(source.entityClass),
|
|
568
|
+
minTransferDistance(dest.entityClass)
|
|
569
|
+
)
|
|
570
|
+
const distance = rawDistance < minDistance ? minDistance : rawDistance
|
|
494
571
|
|
|
495
572
|
const totalMass = cargoMass + totalLoaderMass
|
|
496
573
|
const acceleration = calc_acceleration(totalThrust, totalMass)
|
package/src/types/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type Int64Type,
|
|
3
|
-
Name,
|
|
4
3
|
type UInt16,
|
|
5
4
|
type UInt16Type,
|
|
6
5
|
type UInt32,
|
|
@@ -12,7 +11,7 @@ import {ServerContract} from './contracts'
|
|
|
12
11
|
export const PRECISION = 10000
|
|
13
12
|
export const CRAFT_ENERGY_DIVISOR = 150000
|
|
14
13
|
|
|
15
|
-
export const
|
|
14
|
+
export const PLANETARY_STRUCTURE_Z = 0
|
|
16
15
|
|
|
17
16
|
export const CONTAINER_Z = 300
|
|
18
17
|
|
|
@@ -23,6 +22,9 @@ export const MAX_ORBITAL_ALTITUDE = 3000
|
|
|
23
22
|
|
|
24
23
|
export const BASE_ORBITAL_MASS = 100000
|
|
25
24
|
|
|
25
|
+
export const MIN_TRANSFER_DISTANCE_PLANETARY_STRUCTURE = 100
|
|
26
|
+
export const MIN_TRANSFER_DISTANCE_ORBITAL_VESSEL = 300
|
|
27
|
+
|
|
26
28
|
export interface ShipLike {
|
|
27
29
|
coordinates: ServerContract.Types.coordinates
|
|
28
30
|
hullmass?: UInt32
|
|
@@ -49,8 +51,12 @@ export enum TaskType {
|
|
|
49
51
|
WARP = 6,
|
|
50
52
|
CRAFT = 7,
|
|
51
53
|
DEPLOY = 8,
|
|
52
|
-
WRAP = 9,
|
|
53
54
|
UNWRAP = 10,
|
|
55
|
+
UNDEPLOY = 11,
|
|
56
|
+
DEMOLISH = 13,
|
|
57
|
+
CLAIMPLOT = 14,
|
|
58
|
+
BUILDPLOT = 15,
|
|
59
|
+
RESERVED = 16,
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
export enum LocationType {
|
|
@@ -58,6 +64,7 @@ export enum LocationType {
|
|
|
58
64
|
PLANET = 1,
|
|
59
65
|
ASTEROID = 2,
|
|
60
66
|
NEBULA = 3,
|
|
67
|
+
ICE_FIELD = 4,
|
|
61
68
|
}
|
|
62
69
|
|
|
63
70
|
export enum TaskCancelable {
|
|
@@ -66,14 +73,6 @@ export enum TaskCancelable {
|
|
|
66
73
|
ALWAYS = 2,
|
|
67
74
|
}
|
|
68
75
|
|
|
69
|
-
export const EntityType = {
|
|
70
|
-
SHIP: Name.from('ship'),
|
|
71
|
-
WAREHOUSE: Name.from('warehouse'),
|
|
72
|
-
CONTAINER: Name.from('container'),
|
|
73
|
-
} as const
|
|
74
|
-
|
|
75
|
-
export type EntityTypeName = (typeof EntityType)[keyof typeof EntityType]
|
|
76
|
-
|
|
77
76
|
export type CoordinatesType =
|
|
78
77
|
| Coordinates
|
|
79
78
|
| ServerContract.Types.coordinates
|
|
@@ -122,8 +121,9 @@ export type ModuleType =
|
|
|
122
121
|
| 'launcher'
|
|
123
122
|
| 'storage'
|
|
124
123
|
| 'hauler'
|
|
124
|
+
| 'battery'
|
|
125
125
|
|
|
126
|
-
export const
|
|
126
|
+
export const RESOURCE_TIER_ADJECTIVES: Record<number, string> = {
|
|
127
127
|
1: 'Crude',
|
|
128
128
|
2: 'Dense',
|
|
129
129
|
3: 'Pure',
|
|
@@ -136,6 +136,9 @@ export const TIER_ADJECTIVES: Record<number, string> = {
|
|
|
136
136
|
10: 'Ascendant',
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
export const COMPONENT_TIER_PREFIXES: Record<number, string> = {}
|
|
140
|
+
export const MODULE_TIER_PREFIXES: Record<number, string> = {}
|
|
141
|
+
|
|
139
142
|
export const CATEGORY_LABELS: Record<ResourceCategory, string> = {
|
|
140
143
|
ore: 'Ore',
|
|
141
144
|
crystal: 'Crystal',
|
|
@@ -159,3 +162,7 @@ export interface Item {
|
|
|
159
162
|
export function formatTier(tier: number): string {
|
|
160
163
|
return 'T' + tier
|
|
161
164
|
}
|
|
165
|
+
|
|
166
|
+
export function tierAdjective(tier: number): string {
|
|
167
|
+
return RESOURCE_TIER_ADJECTIVES[tier] ?? `T${tier}`
|
|
168
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type {ServerContract} from '../contracts'
|
|
2
|
+
|
|
3
|
+
export function cargoRef(src: {
|
|
4
|
+
item_id: number
|
|
5
|
+
stats: bigint | number
|
|
6
|
+
modules?: ServerContract.Types.module_entry[]
|
|
7
|
+
}): ServerContract.ActionParams.Type.cargo_ref {
|
|
8
|
+
return {
|
|
9
|
+
item_id: src.item_id,
|
|
10
|
+
stats: src.stats,
|
|
11
|
+
modules: src.modules ?? [],
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function cargoItem(
|
|
16
|
+
src: {
|
|
17
|
+
item_id: number
|
|
18
|
+
stats: bigint | number
|
|
19
|
+
modules?: ServerContract.Types.module_entry[]
|
|
20
|
+
},
|
|
21
|
+
quantity: bigint | number
|
|
22
|
+
): ServerContract.ActionParams.Type.cargo_item {
|
|
23
|
+
return {
|
|
24
|
+
...cargoRef(src),
|
|
25
|
+
quantity,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export interface DisplayNameResult {
|
|
2
|
+
valid: boolean
|
|
3
|
+
reason?: string
|
|
4
|
+
name: string
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const MAX_DISPLAY_NAME_BYTES = 32
|
|
8
|
+
const ASCII_SPACE = 0x20
|
|
9
|
+
const ZERO_WIDTH_CODE_POINTS = new Set([0x200b, 0x200c, 0x200d, 0x2060, 0xfeff])
|
|
10
|
+
const textEncoder = new TextEncoder()
|
|
11
|
+
|
|
12
|
+
function isControlCharacter(codePoint: number): boolean {
|
|
13
|
+
return codePoint <= 0x1f || codePoint === 0x7f || (codePoint >= 0x80 && codePoint <= 0x9f)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function isLoneSurrogate(codePoint: number): boolean {
|
|
17
|
+
return codePoint >= 0xd800 && codePoint <= 0xdfff
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function isBidiControl(codePoint: number): boolean {
|
|
21
|
+
return (
|
|
22
|
+
(codePoint >= 0x202a && codePoint <= 0x202e) || (codePoint >= 0x2066 && codePoint <= 0x2069)
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function isInvalidDisplayNameCodePoint(codePoint: number): boolean {
|
|
27
|
+
return (
|
|
28
|
+
isControlCharacter(codePoint) ||
|
|
29
|
+
isLoneSurrogate(codePoint) ||
|
|
30
|
+
ZERO_WIDTH_CODE_POINTS.has(codePoint) ||
|
|
31
|
+
isBidiControl(codePoint)
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function normalizeDisplayName(input: string): string {
|
|
36
|
+
let start = 0
|
|
37
|
+
let end = input.length
|
|
38
|
+
|
|
39
|
+
while (start < end && input.charCodeAt(start) === ASCII_SPACE) start++
|
|
40
|
+
while (end > start && input.charCodeAt(end - 1) === ASCII_SPACE) end--
|
|
41
|
+
|
|
42
|
+
return input.slice(start, end)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function validateDisplayName(input: string): DisplayNameResult {
|
|
46
|
+
const name = normalizeDisplayName(input)
|
|
47
|
+
|
|
48
|
+
if (name.length === 0) return {valid: false, reason: 'empty', name}
|
|
49
|
+
if (textEncoder.encode(name).length > MAX_DISPLAY_NAME_BYTES) {
|
|
50
|
+
return {valid: false, reason: 'too_long', name}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
for (const character of name) {
|
|
54
|
+
const codePoint = character.codePointAt(0)!
|
|
55
|
+
if (isInvalidDisplayNameCodePoint(codePoint)) {
|
|
56
|
+
return {valid: false, reason: 'invalid_char', name}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {valid: true, name}
|
|
61
|
+
}
|
package/src/utils/system.ts
CHANGED
|
@@ -8,7 +8,6 @@ import nebulaAdjectives from '../data/nebula-adjectives.json'
|
|
|
8
8
|
import nebulaNouns from '../data/nebula-nouns.json'
|
|
9
9
|
|
|
10
10
|
const LOCATION_EXISTS_THRESHOLD = 0x10
|
|
11
|
-
const LOCATION_ACTIVE_THRESHOLD = 0x80
|
|
12
11
|
|
|
13
12
|
export function getLocationType(
|
|
14
13
|
gameSeed: Checksum256Type,
|
|
@@ -22,12 +21,10 @@ export function getLocationType(
|
|
|
22
21
|
return LocationType.EMPTY
|
|
23
22
|
}
|
|
24
23
|
|
|
25
|
-
if (hashResult.array[1] < 96)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
return LocationType.NEBULA
|
|
24
|
+
if (hashResult.array[1] < 96) return LocationType.PLANET
|
|
25
|
+
if (hashResult.array[1] < 149) return LocationType.ASTEROID
|
|
26
|
+
if (hashResult.array[1] < 202) return LocationType.NEBULA
|
|
27
|
+
return LocationType.ICE_FIELD
|
|
31
28
|
}
|
|
32
29
|
|
|
33
30
|
export function isGatherableLocation(locationType: LocationType): boolean {
|
|
@@ -44,6 +41,8 @@ export function getLocationTypeName(type: LocationType): string {
|
|
|
44
41
|
return 'Asteroid'
|
|
45
42
|
case LocationType.NEBULA:
|
|
46
43
|
return 'Nebula'
|
|
44
|
+
case LocationType.ICE_FIELD:
|
|
45
|
+
return 'Ice Field'
|
|
47
46
|
}
|
|
48
47
|
}
|
|
49
48
|
|
|
@@ -76,6 +75,15 @@ function generateNebulaName(hashResult: Checksum512): string {
|
|
|
76
75
|
return `${nebulaAdjectives[adjIdx]} ${nebulaNouns[nounIdx]}`
|
|
77
76
|
}
|
|
78
77
|
|
|
78
|
+
function generateIceFieldName(hashResult: Checksum512): string {
|
|
79
|
+
const A = 65
|
|
80
|
+
const letter1 = String.fromCharCode(A + (hashResult.array[0] % 26))
|
|
81
|
+
const letter2 = String.fromCharCode(A + (hashResult.array[1] % 26))
|
|
82
|
+
const subId = (hashResult.array[2] % 9) + 1
|
|
83
|
+
const num = (uint16(hashResult, 3) % 9000) + 1000
|
|
84
|
+
return `${letter1}${letter2}-${subId}/${num}`
|
|
85
|
+
}
|
|
86
|
+
|
|
79
87
|
export function getSystemName(gameSeed: Checksum256Type, location: CoordinatesType): string {
|
|
80
88
|
const seed = Checksum256.from(gameSeed)
|
|
81
89
|
const locationType = getLocationType(seed, location)
|
|
@@ -91,6 +99,8 @@ export function getSystemName(gameSeed: Checksum256Type, location: CoordinatesTy
|
|
|
91
99
|
return generateAsteroidName(hashResult)
|
|
92
100
|
case LocationType.NEBULA:
|
|
93
101
|
return generateNebulaName(hashResult)
|
|
102
|
+
case LocationType.ICE_FIELD:
|
|
103
|
+
return generateIceFieldName(hashResult)
|
|
94
104
|
default:
|
|
95
105
|
return generatePlanetName(hashResult)
|
|
96
106
|
}
|
|
@@ -123,10 +133,12 @@ export function deriveLocationStatic(
|
|
|
123
133
|
|
|
124
134
|
if (hashResult.array[1] < 96) {
|
|
125
135
|
loc.type = UInt8.from(LocationType.PLANET)
|
|
126
|
-
} else if (hashResult.array[1] <
|
|
136
|
+
} else if (hashResult.array[1] < 149) {
|
|
127
137
|
loc.type = UInt8.from(LocationType.ASTEROID)
|
|
128
|
-
} else {
|
|
138
|
+
} else if (hashResult.array[1] < 202) {
|
|
129
139
|
loc.type = UInt8.from(LocationType.NEBULA)
|
|
140
|
+
} else {
|
|
141
|
+
loc.type = UInt8.from(LocationType.ICE_FIELD)
|
|
130
142
|
}
|
|
131
143
|
|
|
132
144
|
loc.subtype = UInt8.from(
|
|
@@ -138,31 +150,20 @@ export function deriveLocationStatic(
|
|
|
138
150
|
return loc
|
|
139
151
|
}
|
|
140
152
|
|
|
141
|
-
export function
|
|
142
|
-
|
|
153
|
+
export function isLocationBuildable(
|
|
154
|
+
gameSeed: Checksum256Type,
|
|
143
155
|
coordinates: CoordinatesType
|
|
144
|
-
):
|
|
145
|
-
|
|
146
|
-
const coords = Coordinates.from(coordinates)
|
|
147
|
-
const str = `system-epoch-${coords.x}-${coords.y}`
|
|
148
|
-
const hashResult = hash512(seed, str)
|
|
149
|
-
|
|
150
|
-
return ServerContract.Types.location_epoch.from({
|
|
151
|
-
active: hashResult.array[0] < LOCATION_ACTIVE_THRESHOLD,
|
|
152
|
-
seed0: hashResult.array[1],
|
|
153
|
-
seed1: hashResult.array[2],
|
|
154
|
-
})
|
|
156
|
+
): boolean {
|
|
157
|
+
return getLocationType(gameSeed, coordinates) === LocationType.PLANET
|
|
155
158
|
}
|
|
156
159
|
|
|
157
160
|
export function deriveLocation(
|
|
158
161
|
gameSeed: Checksum256Type,
|
|
159
|
-
epochSeed: Checksum256Type,
|
|
160
162
|
coordinates: CoordinatesType
|
|
161
163
|
): ServerContract.Types.location_derived {
|
|
162
164
|
const staticProps = deriveLocationStatic(gameSeed, coordinates)
|
|
163
165
|
return ServerContract.Types.location_derived.from({
|
|
164
166
|
static_props: staticProps,
|
|
165
|
-
epoch_props: deriveLocationEpoch(epochSeed, coordinates),
|
|
166
167
|
size: deriveLocationSize(staticProps),
|
|
167
168
|
})
|
|
168
169
|
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import {UInt32, type UInt64} from '@wharfkit/antelope'
|
|
2
|
-
import type {LoaderCapability} from '../types/capabilities'
|
|
3
|
-
|
|
4
|
-
export function calcLoadDuration(entity: LoaderCapability, cargoMass: UInt64): UInt32 {
|
|
5
|
-
const totalThrust = entity.loaders.thrust.toNumber() * entity.loaders.quantity.toNumber()
|
|
6
|
-
if (totalThrust === 0) return UInt32.from(0)
|
|
7
|
-
return UInt32.from(Math.ceil(Number(cargoMass) / totalThrust))
|
|
8
|
-
}
|