@shipload/sdk 1.0.0-next.4 → 1.0.0-next.6

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.
@@ -11,7 +11,7 @@ export interface EntityMetadata {
11
11
  }
12
12
 
13
13
  export const itemMetadata: Record<number, ItemMetadata> = {
14
- // === Resources (raw) ===
14
+ // === Resources / Ore ===
15
15
  101: {name: 'Ore', description: 'Crude metallic ore.', color: '#C26D3F'},
16
16
  102: {name: 'Ore', description: 'Refined metallic ore with improved purity.', color: '#C26D3F'},
17
17
  103: {
@@ -19,6 +19,15 @@ export const itemMetadata: Record<number, ItemMetadata> = {
19
19
  description: 'High-grade metallic ore with exceptional density.',
20
20
  color: '#C26D3F',
21
21
  },
22
+ 104: {name: 'Ore', description: '', color: '#C26D3F'},
23
+ 105: {name: 'Ore', description: '', color: '#C26D3F'},
24
+ 106: {name: 'Ore', description: '', color: '#C26D3F'},
25
+ 107: {name: 'Ore', description: '', color: '#C26D3F'},
26
+ 108: {name: 'Ore', description: '', color: '#C26D3F'},
27
+ 109: {name: 'Ore', description: '', color: '#C26D3F'},
28
+ 110: {name: 'Ore', description: '', color: '#C26D3F'},
29
+
30
+ // === Resources / Crystal ===
22
31
  201: {name: 'Crystal', description: 'Raw resonant crystal.', color: '#4ADBFF'},
23
32
  202: {
24
33
  name: 'Crystal',
@@ -30,6 +39,15 @@ export const itemMetadata: Record<number, ItemMetadata> = {
30
39
  description: 'High-grade resonant crystal with exceptional purity.',
31
40
  color: '#4ADBFF',
32
41
  },
42
+ 204: {name: 'Crystal', description: '', color: '#4ADBFF'},
43
+ 205: {name: 'Crystal', description: '', color: '#4ADBFF'},
44
+ 206: {name: 'Crystal', description: '', color: '#4ADBFF'},
45
+ 207: {name: 'Crystal', description: '', color: '#4ADBFF'},
46
+ 208: {name: 'Crystal', description: '', color: '#4ADBFF'},
47
+ 209: {name: 'Crystal', description: '', color: '#4ADBFF'},
48
+ 210: {name: 'Crystal', description: '', color: '#4ADBFF'},
49
+
50
+ // === Resources / Gas ===
33
51
  301: {name: 'Gas', description: 'Raw volatile gas.', color: '#B8E4A0'},
34
52
  302: {
35
53
  name: 'Gas',
@@ -41,6 +59,15 @@ export const itemMetadata: Record<number, ItemMetadata> = {
41
59
  description: 'High-grade volatile gas with exceptional energy density.',
42
60
  color: '#B8E4A0',
43
61
  },
62
+ 304: {name: 'Gas', description: '', color: '#B8E4A0'},
63
+ 305: {name: 'Gas', description: '', color: '#B8E4A0'},
64
+ 306: {name: 'Gas', description: '', color: '#B8E4A0'},
65
+ 307: {name: 'Gas', description: '', color: '#B8E4A0'},
66
+ 308: {name: 'Gas', description: '', color: '#B8E4A0'},
67
+ 309: {name: 'Gas', description: '', color: '#B8E4A0'},
68
+ 310: {name: 'Gas', description: '', color: '#B8E4A0'},
69
+
70
+ // === Resources / Regolith ===
44
71
  401: {name: 'Regolith', description: 'Crude regolith dust.', color: '#C4A57B'},
45
72
  402: {
46
73
  name: 'Regolith',
@@ -52,6 +79,15 @@ export const itemMetadata: Record<number, ItemMetadata> = {
52
79
  description: 'High-grade regolith with exceptional uniformity.',
53
80
  color: '#C4A57B',
54
81
  },
82
+ 404: {name: 'Regolith', description: '', color: '#C4A57B'},
83
+ 405: {name: 'Regolith', description: '', color: '#C4A57B'},
84
+ 406: {name: 'Regolith', description: '', color: '#C4A57B'},
85
+ 407: {name: 'Regolith', description: '', color: '#C4A57B'},
86
+ 408: {name: 'Regolith', description: '', color: '#C4A57B'},
87
+ 409: {name: 'Regolith', description: '', color: '#C4A57B'},
88
+ 410: {name: 'Regolith', description: '', color: '#C4A57B'},
89
+
90
+ // === Resources / Biomass ===
55
91
  501: {name: 'Biomass', description: 'Crude organic biomass.', color: '#5A8B3E'},
56
92
  502: {
57
93
  name: 'Biomass',
@@ -63,6 +99,13 @@ export const itemMetadata: Record<number, ItemMetadata> = {
63
99
  description: 'High-grade biomass with exceptional saturation.',
64
100
  color: '#5A8B3E',
65
101
  },
102
+ 504: {name: 'Biomass', description: '', color: '#5A8B3E'},
103
+ 505: {name: 'Biomass', description: '', color: '#5A8B3E'},
104
+ 506: {name: 'Biomass', description: '', color: '#5A8B3E'},
105
+ 507: {name: 'Biomass', description: '', color: '#5A8B3E'},
106
+ 508: {name: 'Biomass', description: '', color: '#5A8B3E'},
107
+ 509: {name: 'Biomass', description: '', color: '#5A8B3E'},
108
+ 510: {name: 'Biomass', description: '', color: '#5A8B3E'},
66
109
 
67
110
  // === Components (T1) ===
68
111
  10001: {
@@ -7,6 +7,7 @@ export {
7
7
  getEligibleResources,
8
8
  getResourceWeight,
9
9
  getLocationCandidates,
10
+ getLocationProfile,
10
11
  getDepthThreshold,
11
12
  getResourceTier,
12
13
  DEPTH_THRESHOLD_T1,
@@ -1,10 +1,16 @@
1
1
  import {getItem} from '../data/catalog'
2
+ import {LocationType} from '../types'
2
3
 
3
4
  export const DEPTH_THRESHOLD_T1 = 0
4
- export const DEPTH_THRESHOLD_T2 = 2000
5
- export const DEPTH_THRESHOLD_T3 = 10000
6
- export const DEPTH_THRESHOLD_T4 = 30000
7
- export const DEPTH_THRESHOLD_T5 = 55000
5
+ export const DEPTH_THRESHOLD_T2 = 1500
6
+ export const DEPTH_THRESHOLD_T3 = 5000
7
+ export const DEPTH_THRESHOLD_T4 = 12000
8
+ export const DEPTH_THRESHOLD_T5 = 22000
9
+ export const DEPTH_THRESHOLD_T6 = 32000
10
+ export const DEPTH_THRESHOLD_T7 = 42000
11
+ export const DEPTH_THRESHOLD_T8 = 50000
12
+ export const DEPTH_THRESHOLD_T9 = 57000
13
+ export const DEPTH_THRESHOLD_T10 = 63000
8
14
 
9
15
  export const LOCATION_MIN_DEPTH = 500
10
16
  export const LOCATION_MAX_DEPTH = 65535
@@ -18,19 +24,22 @@ export const PLANET_SUBTYPE_ICY = 3
18
24
  export const PLANET_SUBTYPE_OCEAN = 4
19
25
  export const PLANET_SUBTYPE_INDUSTRIAL = 5
20
26
 
27
+ const DEPTH_THRESHOLD_TABLE = [
28
+ DEPTH_THRESHOLD_T1,
29
+ DEPTH_THRESHOLD_T2,
30
+ DEPTH_THRESHOLD_T3,
31
+ DEPTH_THRESHOLD_T4,
32
+ DEPTH_THRESHOLD_T5,
33
+ DEPTH_THRESHOLD_T6,
34
+ DEPTH_THRESHOLD_T7,
35
+ DEPTH_THRESHOLD_T8,
36
+ DEPTH_THRESHOLD_T9,
37
+ DEPTH_THRESHOLD_T10,
38
+ ]
39
+
21
40
  export function getDepthThreshold(tier: number): number {
22
- switch (tier) {
23
- case 1:
24
- return DEPTH_THRESHOLD_T1
25
- case 2:
26
- return DEPTH_THRESHOLD_T2
27
- case 3:
28
- return DEPTH_THRESHOLD_T3
29
- case 4:
30
- return DEPTH_THRESHOLD_T4
31
- default:
32
- return DEPTH_THRESHOLD_T5
33
- }
41
+ if (tier < 1 || tier > 10) return 65535
42
+ return DEPTH_THRESHOLD_TABLE[tier - 1]
34
43
  }
35
44
 
36
45
  export function getResourceTier(itemId: number): number {
@@ -46,9 +55,9 @@ export function getResourceWeight(itemId: number, stratum: number): number {
46
55
 
47
56
  switch (tier) {
48
57
  case 1:
49
- if (stratum < 2000) return 100
50
- if (stratum < 10000) return 80
51
- if (stratum < 30000) return 50
58
+ if (stratum < DEPTH_THRESHOLD_T2) return 100
59
+ if (stratum < DEPTH_THRESHOLD_T3) return 80
60
+ if (stratum < DEPTH_THRESHOLD_T4) return 50
52
61
  return 30
53
62
  case 2:
54
63
  if (depthAbove < 3000) return 40
@@ -67,37 +76,107 @@ export function getResourceWeight(itemId: number, stratum: number): number {
67
76
  }
68
77
  }
69
78
 
70
- const ASTEROID_RESOURCES = [101, 102, 103, 201, 202]
71
- const NEBULA_RESOURCES = [202, 203, 301, 302, 303]
72
- const GAS_GIANT_RESOURCES = [301, 302, 303, 401, 501]
73
- const ROCKY_RESOURCES = [101, 102, 103, 401, 402, 403, 503]
74
- const TERRESTRIAL_RESOURCES = [201, 202, 401, 402, 501, 502, 503]
75
- const ICY_RESOURCES = [101, 301, 302, 401, 403, 501, 502]
76
- const OCEAN_RESOURCES = [201, 203, 301, 303, 501, 502, 503]
77
- const INDUSTRIAL_RESOURCES = [101, 102, 103, 201, 203, 402, 403]
79
+ const RESOURCE_ORE = 0
80
+ const RESOURCE_GAS = 1
81
+ const RESOURCE_REGOLITH = 2
82
+ const RESOURCE_BIOMASS = 3
83
+ const RESOURCE_CRYSTAL = 4
78
84
 
79
- export function getLocationCandidates(locationType: number, subtype: number): number[] {
80
- if (locationType === 2) return ASTEROID_RESOURCES
81
- if (locationType === 3) return NEBULA_RESOURCES
82
- if (locationType === 1) {
85
+ interface LocationProfileEntry {
86
+ category: number
87
+ maxTier: number
88
+ }
89
+
90
+ function categoryBaseId(category: number): number {
91
+ switch (category) {
92
+ case RESOURCE_ORE:
93
+ return 100
94
+ case RESOURCE_CRYSTAL:
95
+ return 200
96
+ case RESOURCE_GAS:
97
+ return 300
98
+ case RESOURCE_REGOLITH:
99
+ return 400
100
+ case RESOURCE_BIOMASS:
101
+ return 500
102
+ default:
103
+ return 0
104
+ }
105
+ }
106
+
107
+ function resourceId(category: number, tier: number): number {
108
+ return categoryBaseId(category) + tier
109
+ }
110
+
111
+ export function getLocationProfile(locationType: number, subtype: number): LocationProfileEntry[] {
112
+ if (locationType === LocationType.ASTEROID) {
113
+ return [
114
+ {category: RESOURCE_ORE, maxTier: 5},
115
+ {category: RESOURCE_CRYSTAL, maxTier: 5},
116
+ ]
117
+ }
118
+ if (locationType === LocationType.NEBULA) {
119
+ return [
120
+ {category: RESOURCE_GAS, maxTier: 5},
121
+ {category: RESOURCE_REGOLITH, maxTier: 5},
122
+ ]
123
+ }
124
+ if (locationType === LocationType.ICE_FIELD) {
125
+ return [
126
+ {category: RESOURCE_GAS, maxTier: 5},
127
+ {category: RESOURCE_BIOMASS, maxTier: 5},
128
+ ]
129
+ }
130
+ if (locationType === LocationType.PLANET) {
83
131
  switch (subtype) {
84
132
  case PLANET_SUBTYPE_GAS_GIANT:
85
- return GAS_GIANT_RESOURCES
133
+ return [
134
+ {category: RESOURCE_GAS, maxTier: 10},
135
+ {category: RESOURCE_CRYSTAL, maxTier: 3},
136
+ ]
86
137
  case PLANET_SUBTYPE_ROCKY:
87
- return ROCKY_RESOURCES
138
+ return [
139
+ {category: RESOURCE_REGOLITH, maxTier: 10},
140
+ {category: RESOURCE_ORE, maxTier: 3},
141
+ ]
88
142
  case PLANET_SUBTYPE_TERRESTRIAL:
89
- return TERRESTRIAL_RESOURCES
143
+ return [
144
+ {category: RESOURCE_ORE, maxTier: 10},
145
+ {category: RESOURCE_BIOMASS, maxTier: 3},
146
+ ]
90
147
  case PLANET_SUBTYPE_ICY:
91
- return ICY_RESOURCES
148
+ return [
149
+ {category: RESOURCE_CRYSTAL, maxTier: 10},
150
+ {category: RESOURCE_REGOLITH, maxTier: 3},
151
+ ]
92
152
  case PLANET_SUBTYPE_OCEAN:
93
- return OCEAN_RESOURCES
153
+ return [
154
+ {category: RESOURCE_BIOMASS, maxTier: 10},
155
+ {category: RESOURCE_GAS, maxTier: 3},
156
+ ]
94
157
  case PLANET_SUBTYPE_INDUSTRIAL:
95
- return INDUSTRIAL_RESOURCES
158
+ return [
159
+ {category: RESOURCE_ORE, maxTier: 3},
160
+ {category: RESOURCE_CRYSTAL, maxTier: 3},
161
+ {category: RESOURCE_REGOLITH, maxTier: 3},
162
+ {category: RESOURCE_BIOMASS, maxTier: 3},
163
+ ]
96
164
  }
97
165
  }
98
166
  return []
99
167
  }
100
168
 
169
+ export function getLocationCandidates(locationType: number, subtype: number): number[] {
170
+ const profile = getLocationProfile(locationType, subtype)
171
+ const ids: number[] = []
172
+ for (const {category, maxTier} of profile) {
173
+ for (let tier = 1; tier <= maxTier; tier++) {
174
+ ids.push(resourceId(category, tier))
175
+ }
176
+ }
177
+ return ids
178
+ }
179
+
101
180
  export function getEligibleResources(
102
181
  locationType: number,
103
182
  subtype: number,
@@ -1,6 +1,7 @@
1
1
  import {UInt64, type UInt64Type} from '@wharfkit/antelope'
2
2
  import {ServerContract} from '../contracts'
3
3
  import type {CoordinatesType} from '../types'
4
+ import {type FloatPosition, getInterpolatedPosition} from '../travel/travel'
4
5
  import {Location} from './location'
5
6
  import {ScheduleAccessor} from '../scheduling/accessor'
6
7
  import * as schedule from '../scheduling/schedule'
@@ -24,6 +25,14 @@ export class Container extends ServerContract.Types.entity_info {
24
25
  return this.entity_name
25
26
  }
26
27
 
28
+ get entityClass(): 'mobile' {
29
+ return 'mobile'
30
+ }
31
+
32
+ get canUndeploy(): boolean {
33
+ return true
34
+ }
35
+
27
36
  get sched(): ScheduleAccessor {
28
37
  this._sched ??= new ScheduleAccessor(this)
29
38
  return this._sched
@@ -33,6 +42,12 @@ export class Container extends ServerContract.Types.entity_info {
33
42
  return this.is_idle
34
43
  }
35
44
 
45
+ interpolatedPositionAt(now: Date): FloatPosition {
46
+ const taskIndex = this.sched.currentTaskIndex(now)
47
+ const progress = this.sched.currentTaskProgressFloat(now)
48
+ return getInterpolatedPosition(this, taskIndex, progress)
49
+ }
50
+
36
51
  isLoading(now: Date): boolean {
37
52
  return schedule.isLoading(this, now)
38
53
  }
@@ -8,6 +8,7 @@ import {
8
8
  MODULE_HAULER,
9
9
  MODULE_LOADER,
10
10
  } from '../capabilities/modules'
11
+ import {getItem} from '../data/catalog'
11
12
 
12
13
  export function computeShipHullCapabilities(stats: Record<string, number>): {
13
14
  hullmass: number
@@ -52,7 +53,38 @@ export function computeGeneratorCapabilities(stats: Record<string, number>): {
52
53
  }
53
54
  }
54
55
 
55
- export function computeGathererCapabilities(stats: Record<string, number>): {
56
+ export interface GathererDepthParams {
57
+ readonly floor: number
58
+ readonly slope: number
59
+ }
60
+
61
+ export const GATHERER_DEPTH_TABLE: readonly GathererDepthParams[] = [
62
+ {floor: 500, slope: 5},
63
+ {floor: 2000, slope: 11},
64
+ {floor: 7000, slope: 16},
65
+ {floor: 15000, slope: 18},
66
+ {floor: 25000, slope: 19},
67
+ {floor: 35000, slope: 16},
68
+ {floor: 46000, slope: 12},
69
+ {floor: 53500, slope: 10},
70
+ {floor: 60000, slope: 5},
71
+ {floor: 63500, slope: 2},
72
+ ]
73
+
74
+ export const GATHERER_DEPTH_MAX_TIER = 10
75
+
76
+ export function gathererDepthForTier(tol: number, tier: number): number {
77
+ if (tier < 1 || tier > GATHERER_DEPTH_MAX_TIER) {
78
+ throw new Error(`gatherer tier out of range: ${tier}`)
79
+ }
80
+ const p = GATHERER_DEPTH_TABLE[tier - 1]
81
+ return p.floor + tol * p.slope
82
+ }
83
+
84
+ export function computeGathererCapabilities(
85
+ stats: Record<string, number>,
86
+ tier: number
87
+ ): {
56
88
  yield: number
57
89
  drain: number
58
90
  depth: number
@@ -66,7 +98,7 @@ export function computeGathererCapabilities(stats: Record<string, number>): {
66
98
  return {
67
99
  yield: 200 + str,
68
100
  drain: Math.max(250, 1250 - Math.floor((con * 25) / 20)),
69
- depth: 200 + Math.floor((tol * 3) / 2),
101
+ depth: gathererDepthForTier(tol, tier),
70
102
  speed: 100 + Math.floor((ref * 4) / 5),
71
103
  }
72
104
  }
@@ -197,16 +229,20 @@ export function computeShipCapabilities(
197
229
  if (gathererModules.length > 0) {
198
230
  let totalYield = 0
199
231
  let totalDrain = 0
200
- let totalDepth = 0
232
+ let maxDepth = 0
201
233
  let totalSpeed = 0
202
234
  for (const m of gathererModules) {
203
- const caps = computeGathererCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
235
+ const tier = getItem(m.itemId).tier
236
+ const caps = computeGathererCapabilities(
237
+ decodeCraftedItemStats(m.itemId, m.stats),
238
+ tier
239
+ )
204
240
  totalYield += caps.yield
205
241
  totalDrain += caps.drain
206
- totalDepth += caps.depth
242
+ if (caps.depth > maxDepth) maxDepth = caps.depth
207
243
  totalSpeed += caps.speed
208
244
  }
209
- ship.gatherer = {yield: totalYield, drain: totalDrain, depth: totalDepth, speed: totalSpeed}
245
+ ship.gatherer = {yield: totalYield, drain: totalDrain, depth: maxDepth, speed: totalSpeed}
210
246
  }
211
247
 
212
248
  const haulerModules = modules.filter((m) => getModuleCapabilityType(m.itemId) === MODULE_HAULER)
@@ -2,7 +2,9 @@ import {type UInt16, type UInt16Type, UInt32, UInt64, type UInt64Type} from '@wh
2
2
  import {ServerContract} from '../contracts'
3
3
  import {Coordinates, type CoordinatesType} from '../types'
4
4
  import {
5
+ type FloatPosition,
5
6
  getDestinationLocation,
7
+ getInterpolatedPosition,
6
8
  getPositionAt,
7
9
  getFlightOrigin as travelGetFlightOrigin,
8
10
  } from '../travel/travel'
@@ -55,6 +57,14 @@ export class Ship extends ServerContract.Types.entity_info {
55
57
  return this.entity_name
56
58
  }
57
59
 
60
+ get entityClass(): 'mobile' {
61
+ return 'mobile'
62
+ }
63
+
64
+ get canUndeploy(): boolean {
65
+ return true
66
+ }
67
+
58
68
  get inv(): InventoryAccessor {
59
69
  this._inv ??= new InventoryAccessor(this)
60
70
  return this._inv
@@ -87,12 +97,19 @@ export class Ship extends ServerContract.Types.entity_info {
87
97
  return dest ? Coordinates.from(dest) : undefined
88
98
  }
89
99
 
100
+ /** Chain-tile coordinates at `now`. For smooth visual position use interpolatedPositionAt. */
90
101
  positionAt(now: Date): Coordinates {
91
102
  const taskIndex = this.sched.currentTaskIndex(now)
92
103
  const progress = this.sched.currentTaskProgress(now)
93
104
  return Coordinates.from(getPositionAt(this, taskIndex, progress))
94
105
  }
95
106
 
107
+ interpolatedPositionAt(now: Date): FloatPosition {
108
+ const taskIndex = this.sched.currentTaskIndex(now)
109
+ const progress = this.sched.currentTaskProgressFloat(now)
110
+ return getInterpolatedPosition(this, taskIndex, progress)
111
+ }
112
+
96
113
  isInFlight(now: Date): boolean {
97
114
  return schedule.isInFlight(this, now)
98
115
  }
@@ -31,6 +31,14 @@ export class Warehouse extends ServerContract.Types.entity_info {
31
31
  return this.entity_name
32
32
  }
33
33
 
34
+ get entityClass(): 'building' {
35
+ return 'building'
36
+ }
37
+
38
+ get canDemolish(): boolean {
39
+ return true
40
+ }
41
+
34
42
  get inv(): InventoryAccessor {
35
43
  this._inv ??= new InventoryAccessor(this)
36
44
  return this._inv
@@ -86,6 +86,7 @@ export {
86
86
  getEligibleResources,
87
87
  getResourceWeight,
88
88
  getLocationCandidates,
89
+ getLocationProfile,
89
90
  getDepthThreshold,
90
91
  getResourceTier,
91
92
  DEPTH_THRESHOLD_T1,
@@ -117,30 +118,35 @@ export {
117
118
  distanceBetweenCoordinates,
118
119
  distanceBetweenPoints,
119
120
  findNearbyPlanets,
120
- lerp,
121
- rotation,
122
- calc_ship_mass,
123
121
  calc_acceleration,
122
+ calc_energyusage,
124
123
  calc_flighttime,
125
- calc_ship_flighttime,
126
- calc_ship_acceleration,
127
- calc_rechargetime,
128
- calc_ship_rechargetime,
129
- calc_loader_flighttime,
130
124
  calc_loader_acceleration,
131
- calc_energyusage,
125
+ calc_loader_flighttime,
132
126
  calc_orbital_altitude,
127
+ calc_rechargetime,
128
+ calc_ship_acceleration,
129
+ calc_ship_flighttime,
130
+ calc_ship_mass,
131
+ calc_ship_rechargetime,
133
132
  calc_transfer_duration,
134
- calculateTransferTime,
133
+ calculateFlightTime,
135
134
  calculateLoadTimeBreakdown,
136
135
  calculateRefuelingTime,
137
- calculateFlightTime,
138
- estimateTravelTime,
136
+ calculateTransferTime,
137
+ easeFlightProgress,
139
138
  estimateDealTravelTime,
140
- hasEnergyForDistance,
141
- getFlightOrigin,
139
+ estimateTravelTime,
140
+ flightSpeedFactor,
141
+ type FloatPosition,
142
142
  getDestinationLocation,
143
+ getFlightOrigin,
144
+ getInterpolatedPosition,
143
145
  getPositionAt,
146
+ hasEnergyForDistance,
147
+ interpolateFlightPosition,
148
+ lerp,
149
+ rotation,
144
150
  } from './travel/travel'
145
151
  export type {
146
152
  LoadTimeBreakdown,
@@ -246,8 +252,11 @@ export {
246
252
  computeWarehouseHullCapabilities,
247
253
  computeStorageCapabilities,
248
254
  computeShipCapabilities,
255
+ GATHERER_DEPTH_TABLE,
256
+ GATHERER_DEPTH_MAX_TIER,
257
+ gathererDepthForTier,
249
258
  } from './entities/ship-deploy'
250
- export type {ShipCapabilities} from './entities/ship-deploy'
259
+ export type {ShipCapabilities, GathererDepthParams} from './entities/ship-deploy'
251
260
 
252
261
  export {resolveItem} from './resolution/resolve-item'
253
262
  export type {
@@ -15,7 +15,7 @@ import {type CoordinatesType, EntityType, type EntityTypeName} from '../types'
15
15
  import {ServerContract} from '../contracts'
16
16
 
17
17
  export type EntityRefInput = {
18
- entityType: EntityTypeName
18
+ entityType: NameType
19
19
  entityId: UInt64Type
20
20
  }
21
21
 
@@ -244,6 +244,29 @@ export class ActionsManager extends BaseManager {
244
244
  })
245
245
  }
246
246
 
247
+ undeploy(host: EntityRefInput, target: EntityRefInput): Action {
248
+ return this.server.action('undeploy', {
249
+ host_type: Name.from(host.entityType),
250
+ host_id: UInt64.from(host.entityId),
251
+ target_type: Name.from(target.entityType),
252
+ target_id: UInt64.from(target.entityId),
253
+ })
254
+ }
255
+
256
+ wrapEntity(entity: EntityRefInput): Action {
257
+ return this.server.action('wrapentity', {
258
+ entity_type: Name.from(entity.entityType),
259
+ entity_id: UInt64.from(entity.entityId),
260
+ })
261
+ }
262
+
263
+ demolish(entity: EntityRefInput): Action {
264
+ return this.server.action('demolish', {
265
+ entity_type: Name.from(entity.entityType),
266
+ entity_id: UInt64.from(entity.entityId),
267
+ })
268
+ }
269
+
247
270
  joinGame(account: NameType, companyName: string): Action[] {
248
271
  return [this.foundCompany(account, companyName), this.join(account)]
249
272
  }
@@ -4,6 +4,7 @@ import {
4
4
  MODULE_ENGINE,
5
5
  MODULE_GATHERER,
6
6
  MODULE_GENERATOR,
7
+ MODULE_HAULER,
7
8
  MODULE_LOADER,
8
9
  MODULE_STORAGE,
9
10
  MODULE_WARP,
@@ -15,6 +16,7 @@ import {
15
16
  ITEM_ENGINE_T1,
16
17
  ITEM_GATHERER_T1,
17
18
  ITEM_GENERATOR_T1,
19
+ ITEM_HAULER_T1,
18
20
  ITEM_LOADER_T1,
19
21
  ITEM_SHIP_T1_PACKED,
20
22
  ITEM_STORAGE_T1,
@@ -22,6 +24,8 @@ import {
22
24
  ITEM_WARP_T1,
23
25
  } from '../data/item-ids'
24
26
  import {decodeStat} from '../derivation/crafting'
27
+ import {gathererDepthForTier} from '../entities/ship-deploy'
28
+ import {getItem} from '../data/catalog'
25
29
 
26
30
  function idiv(a: number, b: number): number {
27
31
  return Math.floor(a / b)
@@ -49,12 +53,16 @@ export const computeGeneratorRech = (fin: number): number => 1 + idiv(fin * 3, 1
49
53
  export const computeGathererYield = (str: number): number => 200 + str
50
54
  export const computeGathererDrain = (con: number): number =>
51
55
  Math.max(250, 1250 - idiv(con * 25, 20))
52
- export const computeGathererDepth = (tol: number): number => 200 + idiv(tol * 3, 2)
56
+ export const computeGathererDepth = (tol: number, tier: number): number =>
57
+ gathererDepthForTier(tol, tier)
53
58
  export const computeGathererSpeed = (ref: number): number => 100 + idiv(ref * 4, 5)
54
59
  export const computeLoaderMass = (ins: number): number => Math.max(200, 2000 - ins * 2)
55
60
  export const computeLoaderThrust = (pla: number): number => 1 + idiv(pla, 500)
56
61
  export const computeCrafterSpeed = (rea: number): number => 100 + idiv(rea * 4, 5)
57
62
  export const computeCrafterDrain = (fin: number): number => Math.max(5, 30 - idiv(fin, 33))
63
+ export const computeHaulerCapacity = (fin: number): number => Math.max(1, 1 + idiv(fin, 400))
64
+ export const computeHaulerEfficiency = (con: number): number => 2000 + con * 6
65
+ export const computeHaulerDrain = (com: number): number => Math.max(3, 15 - idiv(com, 80))
58
66
  export const computeWarpRange = (stat: number): number => 100 + stat * 3
59
67
 
60
68
  export function entityDisplayName(itemId: number): string {
@@ -86,6 +94,8 @@ export function moduleDisplayName(itemId: number): string {
86
94
  return 'Crafter'
87
95
  case ITEM_STORAGE_T1:
88
96
  return 'Storage'
97
+ case ITEM_HAULER_T1:
98
+ return 'Hauler'
89
99
  case ITEM_WARP_T1:
90
100
  return 'Warp'
91
101
  default:
@@ -121,8 +131,10 @@ export function formatModuleLine(slot: number, itemId: number, stats: bigint): s
121
131
  const tol = decodeStat(stats, 1)
122
132
  const con = decodeStat(stats, 3)
123
133
  const ref = decodeStat(stats, 4)
134
+ const tier = getItem(itemId).tier
124
135
  out += ` Yield ${computeGathererYield(str)} Depth ${computeGathererDepth(
125
- tol
136
+ tol,
137
+ tier
126
138
  )} Speed ${computeGathererSpeed(ref)} Drain ${computeGathererDrain(con)}`
127
139
  break
128
140
  }
@@ -147,6 +159,13 @@ export function formatModuleLine(slot: number, itemId: number, stats: bigint): s
147
159
  out += ` +${pct}% capacity`
148
160
  break
149
161
  }
162
+ case MODULE_HAULER: {
163
+ const fin = decodeStat(stats, 0)
164
+ const con = decodeStat(stats, 1)
165
+ const com = decodeStat(stats, 2)
166
+ out += ` Capacity ${computeHaulerCapacity(fin)} Efficiency ${computeHaulerEfficiency(con)} Drain ${computeHaulerDrain(com)}`
167
+ break
168
+ }
150
169
  case MODULE_WARP: {
151
170
  const stat = decodeStat(stats, 0)
152
171
  out += ` Range ${computeWarpRange(stat)}`