@shipload/sdk 1.0.0-next.2 → 1.0.0-next.21

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.
Files changed (85) hide show
  1. package/lib/shipload.d.ts +1731 -1044
  2. package/lib/shipload.js +6758 -4523
  3. package/lib/shipload.js.map +1 -1
  4. package/lib/shipload.m.js +6649 -4479
  5. package/lib/shipload.m.js.map +1 -1
  6. package/lib/testing.d.ts +833 -0
  7. package/lib/testing.js +3647 -0
  8. package/lib/testing.js.map +1 -0
  9. package/lib/testing.m.js +3641 -0
  10. package/lib/testing.m.js.map +1 -0
  11. package/package.json +15 -2
  12. package/src/capabilities/gathering.ts +17 -7
  13. package/src/capabilities/modules.ts +9 -0
  14. package/src/capabilities/storage.ts +1 -1
  15. package/src/contracts/platform.ts +211 -3
  16. package/src/contracts/server.ts +723 -438
  17. package/src/data/capabilities.ts +9 -329
  18. package/src/data/capability-formulas.ts +76 -0
  19. package/src/data/catalog.ts +0 -5
  20. package/src/data/colors.ts +14 -28
  21. package/src/data/entities.json +46 -10
  22. package/src/data/item-ids.ts +17 -13
  23. package/src/data/items.json +308 -37
  24. package/src/data/kind-registry.json +85 -0
  25. package/src/data/kind-registry.ts +150 -0
  26. package/src/data/metadata.ts +99 -24
  27. package/src/data/recipes-runtime.ts +3 -23
  28. package/src/data/recipes.json +265 -96
  29. package/src/derivation/build-methods.ts +45 -0
  30. package/src/derivation/capabilities.ts +414 -0
  31. package/src/derivation/capability-mappings.ts +117 -0
  32. package/src/derivation/crafting.ts +23 -24
  33. package/src/derivation/index.ts +8 -2
  34. package/src/derivation/reserve-regen.ts +34 -0
  35. package/src/derivation/resources.ts +125 -38
  36. package/src/derivation/stats.ts +1 -2
  37. package/src/derivation/stratum.ts +15 -19
  38. package/src/derivation/tiers.ts +28 -7
  39. package/src/entities/entity.ts +98 -0
  40. package/src/entities/gamestate.ts +3 -28
  41. package/src/entities/makers.ts +75 -129
  42. package/src/entities/slot-multiplier.ts +37 -0
  43. package/src/errors.ts +10 -15
  44. package/src/format.ts +26 -4
  45. package/src/index-module.ts +151 -40
  46. package/src/managers/actions.ts +184 -82
  47. package/src/managers/base.ts +2 -2
  48. package/src/managers/construction-types.ts +68 -0
  49. package/src/managers/construction.ts +292 -0
  50. package/src/managers/context.ts +9 -0
  51. package/src/managers/entities.ts +18 -66
  52. package/src/managers/epochs.ts +40 -0
  53. package/src/managers/index.ts +16 -1
  54. package/src/managers/locations.ts +2 -20
  55. package/src/managers/nft.ts +28 -0
  56. package/src/managers/plot.ts +123 -0
  57. package/src/nft/atomicassets.ts +231 -0
  58. package/src/nft/atomicdata.ts +130 -0
  59. package/src/nft/buildImmutableData.ts +319 -0
  60. package/src/nft/description.ts +45 -13
  61. package/src/nft/index.ts +3 -0
  62. package/src/resolution/describe-module.ts +5 -8
  63. package/src/resolution/display-name.ts +38 -10
  64. package/src/resolution/resolve-item.ts +20 -12
  65. package/src/scheduling/accessor.ts +4 -0
  66. package/src/scheduling/projection.ts +79 -27
  67. package/src/scheduling/schedule.ts +15 -1
  68. package/src/scheduling/task-cargo.ts +46 -0
  69. package/src/shipload.ts +5 -0
  70. package/src/subscriptions/manager.ts +40 -6
  71. package/src/subscriptions/mappers.ts +3 -8
  72. package/src/subscriptions/types.ts +3 -2
  73. package/src/testing/catalog-hash.ts +19 -0
  74. package/src/testing/index.ts +2 -0
  75. package/src/testing/projection-parity.ts +143 -0
  76. package/src/travel/travel.ts +61 -2
  77. package/src/types/index.ts +0 -1
  78. package/src/types.ts +17 -12
  79. package/src/utils/cargo.ts +27 -0
  80. package/src/utils/system.ts +25 -24
  81. package/src/entities/container.ts +0 -108
  82. package/src/entities/ship-deploy.ts +0 -258
  83. package/src/entities/ship.ts +0 -204
  84. package/src/entities/warehouse.ts +0 -119
  85. package/src/types/entity-traits.ts +0 -69
@@ -0,0 +1,319 @@
1
+ import {Serializer} from '@wharfkit/antelope'
2
+ import {getItem} from '../data/catalog'
3
+ import {
4
+ getModuleCapabilityType,
5
+ MODULE_CRAFTER,
6
+ MODULE_ENGINE,
7
+ MODULE_GATHERER,
8
+ MODULE_GENERATOR,
9
+ MODULE_HAULER,
10
+ MODULE_LOADER,
11
+ MODULE_STORAGE,
12
+ MODULE_WARP,
13
+ } from '../capabilities/modules'
14
+ import {decodeStat, decodeCraftedItemStats} from '../derivation/crafting'
15
+ import {getStatDefinitions} from '../derivation/stats'
16
+ import type {ResourceCategory} from '../types'
17
+ import {Types as ServerTypes} from '../contracts/server'
18
+ import {
19
+ buildEntityDescription,
20
+ computeCrafterDrain,
21
+ computeCrafterSpeed,
22
+ computeEngineDrain,
23
+ computeEngineThrust,
24
+ computeGathererDepth,
25
+ computeGathererDrain,
26
+ computeGathererYield,
27
+ computeGeneratorCap,
28
+ computeGeneratorRech,
29
+ computeHaulerCapacity,
30
+ computeHaulerDrain,
31
+ computeHaulerEfficiency,
32
+ computeLoaderMass,
33
+ computeLoaderThrust,
34
+ computeWarpRange,
35
+ } from './description'
36
+
37
+ export type AtomicAttributeType =
38
+ | 'string'
39
+ | 'uint8'
40
+ | 'uint16'
41
+ | 'uint32'
42
+ | 'uint64'
43
+ | 'int32'
44
+ | 'image'
45
+ | 'ipfs'
46
+ | 'UINT16_VEC'
47
+ | 'UINT64_VEC'
48
+
49
+ export interface ImmutableEntry {
50
+ first: string
51
+ second: [AtomicAttributeType, unknown]
52
+ }
53
+
54
+ export interface ImmutableModuleSlot {
55
+ type?: number | string | bigint
56
+ installed?: {item_id: number | string | bigint; stats: number | string | bigint}
57
+ }
58
+
59
+ export function moduleSlotsForImmutable(
60
+ modules: ServerTypes.module_entry[]
61
+ ): ImmutableModuleSlot[] {
62
+ return modules.map((m) => ({
63
+ type: Number(m.type.toString()),
64
+ installed: m.installed
65
+ ? {
66
+ item_id: Number(m.installed.item_id.toString()),
67
+ stats: BigInt(m.installed.stats.toString()),
68
+ }
69
+ : undefined,
70
+ }))
71
+ }
72
+
73
+ const IMAGE_HOST_URL = 'https://item.shiploadgame.com/item'
74
+
75
+ function bytesToBase64Url(bytes: Uint8Array): string {
76
+ let binary = ''
77
+ for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]!)
78
+ const b64 = btoa(binary)
79
+ return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
80
+ }
81
+
82
+ export function computeNftImageUrl(
83
+ item: {item_id: number; stats: bigint; modules: ImmutableModuleSlot[]; quantity: number},
84
+ originX: number,
85
+ originY: number
86
+ ): string {
87
+ const payload = ServerTypes.nft_item_payload.from({
88
+ item: {
89
+ item_id: item.item_id,
90
+ stats: String(item.stats),
91
+ modules: item.modules,
92
+ quantity: item.quantity,
93
+ },
94
+ location: {x: String(originX), y: String(originY)},
95
+ })
96
+ const bytes = Serializer.encode({object: payload}).array
97
+ return `${IMAGE_HOST_URL}/${bytesToBase64Url(bytes)}.png`
98
+ }
99
+
100
+ function commonBaseImmutable(
101
+ quantity: number,
102
+ stats: bigint,
103
+ originX: number,
104
+ originY: number,
105
+ img: string
106
+ ): ImmutableEntry[] {
107
+ return [
108
+ {first: 'quantity', second: ['uint32', quantity]},
109
+ {first: 'stats', second: ['uint64', String(stats)]},
110
+ {first: 'origin_x', second: ['int32', originX]},
111
+ {first: 'origin_y', second: ['int32', originY]},
112
+ {first: 'img', second: ['string', img]},
113
+ {first: 'deposit_amount', second: ['uint64', '0']},
114
+ {first: 'deposit_token', second: ['uint64', '0']},
115
+ {first: 'deposit_symbol', second: ['uint64', '0']},
116
+ ]
117
+ }
118
+
119
+ export function buildResourceImmutable(
120
+ itemId: number,
121
+ quantity: number,
122
+ stats: bigint,
123
+ originX: number,
124
+ originY: number
125
+ ): ImmutableEntry[] {
126
+ const item = getItem(itemId)
127
+ const cat = item.category
128
+ if (!cat) throw new Error(`Resource item ${itemId} has no category`)
129
+ const definitions = getStatDefinitions(cat as ResourceCategory)
130
+ const img = computeNftImageUrl(
131
+ {item_id: itemId, stats, modules: [], quantity},
132
+ originX,
133
+ originY
134
+ )
135
+ const base = commonBaseImmutable(quantity, stats, originX, originY, img)
136
+ base.push({first: definitions[0].key, second: ['uint16', decodeStat(stats, 0)]})
137
+ base.push({first: definitions[1].key, second: ['uint16', decodeStat(stats, 1)]})
138
+ base.push({first: definitions[2].key, second: ['uint16', decodeStat(stats, 2)]})
139
+ return base
140
+ }
141
+
142
+ export function buildComponentImmutable(
143
+ itemId: number,
144
+ quantity: number,
145
+ stats: bigint,
146
+ originX: number,
147
+ originY: number
148
+ ): ImmutableEntry[] {
149
+ const img = computeNftImageUrl(
150
+ {item_id: itemId, stats, modules: [], quantity},
151
+ originX,
152
+ originY
153
+ )
154
+ const base = commonBaseImmutable(quantity, stats, originX, originY, img)
155
+ const decoded = decodeCraftedItemStats(itemId, stats)
156
+ for (const [key, value] of Object.entries(decoded)) {
157
+ base.push({first: key, second: ['uint16', value]})
158
+ }
159
+ return base
160
+ }
161
+
162
+ export function buildModuleImmutable(
163
+ itemId: number,
164
+ quantity: number,
165
+ stats: bigint,
166
+ originX: number,
167
+ originY: number
168
+ ): ImmutableEntry[] {
169
+ const img = computeNftImageUrl(
170
+ {item_id: itemId, stats, modules: [], quantity},
171
+ originX,
172
+ originY
173
+ )
174
+ const base = commonBaseImmutable(quantity, stats, originX, originY, img)
175
+ const subtype = getModuleCapabilityType(itemId)
176
+ const item = getItem(itemId)
177
+ switch (subtype) {
178
+ case MODULE_ENGINE: {
179
+ const vol = decodeStat(stats, 0)
180
+ const thm = decodeStat(stats, 1)
181
+ base.push({first: 'volatility', second: ['uint16', vol]})
182
+ base.push({first: 'thermal', second: ['uint16', thm]})
183
+ base.push({first: 'thrust', second: ['uint32', computeEngineThrust(vol)]})
184
+ base.push({first: 'drain', second: ['uint16', computeEngineDrain(thm)]})
185
+ break
186
+ }
187
+ case MODULE_GENERATOR: {
188
+ const res = decodeStat(stats, 0)
189
+ const ref = decodeStat(stats, 1)
190
+ base.push({first: 'resonance', second: ['uint16', res]})
191
+ base.push({first: 'reflectivity', second: ['uint16', ref]})
192
+ base.push({first: 'capacity', second: ['uint16', computeGeneratorCap(res)]})
193
+ base.push({first: 'recharge', second: ['uint16', computeGeneratorRech(ref)]})
194
+ break
195
+ }
196
+ case MODULE_GATHERER: {
197
+ const str = decodeStat(stats, 0)
198
+ const tol = decodeStat(stats, 1)
199
+ const con = decodeStat(stats, 2)
200
+ const ref = decodeStat(stats, 3)
201
+ base.push({first: 'strength', second: ['uint16', str]})
202
+ base.push({first: 'tolerance', second: ['uint16', tol]})
203
+ base.push({first: 'conductivity', second: ['uint16', con]})
204
+ base.push({first: 'reflectivity', second: ['uint16', ref]})
205
+ base.push({first: 'yield', second: ['uint16', computeGathererYield(str)]})
206
+ base.push({first: 'drain', second: ['uint16', computeGathererDrain(con)]})
207
+ base.push({first: 'depth', second: ['uint16', computeGathererDepth(tol, item.tier)]})
208
+ break
209
+ }
210
+ case MODULE_LOADER: {
211
+ const fin = decodeStat(stats, 0)
212
+ const pla = decodeStat(stats, 1)
213
+ base.push({first: 'fineness', second: ['uint16', fin]})
214
+ base.push({first: 'plasticity', second: ['uint16', pla]})
215
+ base.push({first: 'mass', second: ['uint32', computeLoaderMass(fin)]})
216
+ base.push({first: 'thrust', second: ['uint16', computeLoaderThrust(pla)]})
217
+ break
218
+ }
219
+ case MODULE_WARP: {
220
+ const res = decodeStat(stats, 0)
221
+ base.push({first: 'resonance', second: ['uint16', res]})
222
+ base.push({first: 'range', second: ['uint32', computeWarpRange(res)]})
223
+ break
224
+ }
225
+ case MODULE_CRAFTER: {
226
+ const rea = decodeStat(stats, 0)
227
+ const com = decodeStat(stats, 1)
228
+ base.push({first: 'reactivity', second: ['uint16', rea]})
229
+ base.push({first: 'composition', second: ['uint16', com]})
230
+ base.push({first: 'speed', second: ['uint16', computeCrafterSpeed(rea)]})
231
+ base.push({first: 'drain', second: ['uint16', computeCrafterDrain(com)]})
232
+ break
233
+ }
234
+ case MODULE_STORAGE: {
235
+ const str = decodeStat(stats, 0)
236
+ const fin = decodeStat(stats, 1)
237
+ const sat = decodeStat(stats, 2)
238
+ const sum = str + fin + sat
239
+ base.push({first: 'strength', second: ['uint16', str]})
240
+ base.push({first: 'fineness', second: ['uint16', fin]})
241
+ base.push({first: 'saturation', second: ['uint16', sat]})
242
+ base.push({
243
+ first: 'capacity_bonus_pct',
244
+ second: ['uint16', 10 + Math.floor((sum * 10) / 2997)],
245
+ })
246
+ break
247
+ }
248
+ case MODULE_HAULER: {
249
+ const res = decodeStat(stats, 0)
250
+ const con = decodeStat(stats, 1)
251
+ const ref = decodeStat(stats, 2)
252
+ base.push({first: 'resonance', second: ['uint16', res]})
253
+ base.push({first: 'conductivity', second: ['uint16', con]})
254
+ base.push({first: 'reflectivity', second: ['uint16', ref]})
255
+ base.push({first: 'capacity', second: ['uint8', computeHaulerCapacity(res)]})
256
+ base.push({first: 'efficiency', second: ['uint16', computeHaulerEfficiency(con)]})
257
+ base.push({first: 'drain', second: ['uint16', computeHaulerDrain(ref)]})
258
+ break
259
+ }
260
+ }
261
+ return base
262
+ }
263
+
264
+ export function buildEntityImmutable(
265
+ itemId: number,
266
+ quantity: number,
267
+ stats: bigint,
268
+ originX: number,
269
+ originY: number,
270
+ modules: ImmutableModuleSlot[]
271
+ ): ImmutableEntry[] {
272
+ const moduleItems: number[] = []
273
+ const moduleStats: string[] = []
274
+ for (const m of modules) {
275
+ if (m.installed) {
276
+ moduleItems.push(Number(m.installed.item_id))
277
+ moduleStats.push(String(m.installed.stats))
278
+ } else {
279
+ moduleItems.push(0)
280
+ moduleStats.push('0')
281
+ }
282
+ }
283
+ const img = computeNftImageUrl({item_id: itemId, stats, modules, quantity}, originX, originY)
284
+ const base = commonBaseImmutable(quantity, stats, originX, originY, img)
285
+ base.push({first: 'module_items', second: ['UINT16_VEC', moduleItems]})
286
+ base.push({first: 'module_stats', second: ['UINT64_VEC', moduleStats]})
287
+ const description = buildEntityDescription(
288
+ itemId,
289
+ stats,
290
+ moduleItems,
291
+ moduleStats.map((s) => BigInt(s))
292
+ )
293
+ base.push({first: 'description', second: ['string', description]})
294
+ return base
295
+ }
296
+
297
+ export function buildImmutableData(
298
+ itemId: number,
299
+ quantity: number,
300
+ stats: bigint,
301
+ originX: number,
302
+ originY: number,
303
+ modules: ImmutableModuleSlot[] = []
304
+ ): ImmutableEntry[] {
305
+ const item = getItem(itemId)
306
+ if (item.type === 'resource') {
307
+ return buildResourceImmutable(itemId, quantity, stats, originX, originY)
308
+ }
309
+ if (item.type === 'component') {
310
+ return buildComponentImmutable(itemId, quantity, stats, originX, originY)
311
+ }
312
+ if (item.type === 'module') {
313
+ return buildModuleImmutable(itemId, quantity, stats, originX, originY)
314
+ }
315
+ if (item.type === 'entity') {
316
+ return buildEntityImmutable(itemId, quantity, stats, originX, originY, modules)
317
+ }
318
+ throw new Error(`Unsupported item type for wrap: ${item.type}`)
319
+ }
@@ -4,22 +4,29 @@ import {
4
4
  MODULE_ENGINE,
5
5
  MODULE_GATHERER,
6
6
  MODULE_GENERATOR,
7
+ MODULE_HAULER,
7
8
  MODULE_LOADER,
8
9
  MODULE_STORAGE,
10
+ MODULE_WARP,
9
11
  } from '../capabilities/modules'
10
12
  import {
11
13
  ITEM_CONTAINER_T1_PACKED,
12
14
  ITEM_CONTAINER_T2_PACKED,
13
15
  ITEM_CRAFTER_T1,
14
16
  ITEM_ENGINE_T1,
17
+ ITEM_EXTRACTOR_T1_PACKED,
15
18
  ITEM_GATHERER_T1,
16
19
  ITEM_GENERATOR_T1,
20
+ ITEM_HAULER_T1,
17
21
  ITEM_LOADER_T1,
18
22
  ITEM_SHIP_T1_PACKED,
19
23
  ITEM_STORAGE_T1,
20
24
  ITEM_WAREHOUSE_T1_PACKED,
25
+ ITEM_WARP_T1,
21
26
  } from '../data/item-ids'
22
27
  import {decodeStat} from '../derivation/crafting'
28
+ import {gathererDepthForTier} from '../derivation/capabilities'
29
+ import {getItem} from '../data/catalog'
23
30
 
24
31
  function idiv(a: number, b: number): number {
25
32
  return Math.floor(a / b)
@@ -27,32 +34,36 @@ function idiv(a: number, b: number): number {
27
34
 
28
35
  export function computeBaseHullmass(stats: bigint): number {
29
36
  const density = decodeStat(stats, 1)
30
- return 25000 + 75 * density
37
+ return 100000 - 75 * density
31
38
  }
32
39
 
33
40
  export function computeBaseCapacityShip(stats: bigint): number {
34
41
  const s = decodeStat(stats, 0) + decodeStat(stats, 2) + decodeStat(stats, 3)
35
- return Math.floor(1_000_000 * 10 ** (s / 2997))
42
+ return Math.floor(5_000_000 * 6 ** (s / 2997))
36
43
  }
37
44
 
38
45
  export function computeBaseCapacityWarehouse(stats: bigint): number {
39
46
  const s = decodeStat(stats, 0) + decodeStat(stats, 2) + decodeStat(stats, 3)
40
- return Math.floor(20_000_000 * 10 ** (s / 2997))
47
+ return Math.floor(100_000_000 * 6 ** (s / 2997))
41
48
  }
42
49
 
43
50
  export const computeEngineThrust = (vol: number): number => 400 + idiv(vol * 3, 4)
44
51
  export const computeEngineDrain = (thm: number): number => Math.max(30, 50 - idiv(thm, 70))
45
- export const computeGeneratorCap = (res: number): number => 300 + idiv(res, 6)
46
- export const computeGeneratorRech = (ref: number): number => 1 + idiv(ref * 3, 1000)
52
+ export const computeGeneratorCap = (com: number): number => 300 + idiv(com, 6)
53
+ export const computeGeneratorRech = (fin: number): number => 1 + idiv(fin * 3, 1000)
47
54
  export const computeGathererYield = (str: number): number => 200 + str
48
55
  export const computeGathererDrain = (con: number): number =>
49
56
  Math.max(250, 1250 - idiv(con * 25, 20))
50
- export const computeGathererDepth = (tol: number): number => 200 + idiv(tol * 3, 2)
51
- export const computeGathererSpeed = (ref: number): number => 100 + idiv(ref * 4, 5)
52
- export const computeLoaderMass = (fin: number): number => Math.max(200, 2000 - fin * 2)
57
+ export const computeGathererDepth = (tol: number, tier: number): number =>
58
+ gathererDepthForTier(tol, tier)
59
+ export const computeLoaderMass = (ins: number): number => Math.max(200, 2000 - ins * 2)
53
60
  export const computeLoaderThrust = (pla: number): number => 1 + idiv(pla, 500)
54
61
  export const computeCrafterSpeed = (rea: number): number => 100 + idiv(rea * 4, 5)
55
- export const computeCrafterDrain = (com: number): number => Math.max(5, 30 - idiv(com, 33))
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))
66
+ export const computeWarpRange = (stat: number): number => 100 + stat * 3
56
67
 
57
68
  export function entityDisplayName(itemId: number): string {
58
69
  switch (itemId) {
@@ -60,6 +71,8 @@ export function entityDisplayName(itemId: number): string {
60
71
  return 'Ship'
61
72
  case ITEM_WAREHOUSE_T1_PACKED:
62
73
  return 'Warehouse'
74
+ case ITEM_EXTRACTOR_T1_PACKED:
75
+ return 'Extractor'
63
76
  case ITEM_CONTAINER_T1_PACKED:
64
77
  return 'Container'
65
78
  case ITEM_CONTAINER_T2_PACKED:
@@ -83,6 +96,10 @@ export function moduleDisplayName(itemId: number): string {
83
96
  return 'Crafter'
84
97
  case ITEM_STORAGE_T1:
85
98
  return 'Storage'
99
+ case ITEM_HAULER_T1:
100
+ return 'Hauler'
101
+ case ITEM_WARP_T1:
102
+ return 'Warp'
86
103
  default:
87
104
  return 'Module'
88
105
  }
@@ -114,11 +131,12 @@ export function formatModuleLine(slot: number, itemId: number, stats: bigint): s
114
131
  case MODULE_GATHERER: {
115
132
  const str = decodeStat(stats, 0)
116
133
  const tol = decodeStat(stats, 1)
117
- const con = decodeStat(stats, 3)
118
- const ref = decodeStat(stats, 4)
134
+ const con = decodeStat(stats, 2)
135
+ const tier = getItem(itemId).tier
119
136
  out += ` Yield ${computeGathererYield(str)} Depth ${computeGathererDepth(
120
- tol
121
- )} Speed ${computeGathererSpeed(ref)} Drain ${computeGathererDrain(con)}`
137
+ tol,
138
+ tier
139
+ )} Drain ${computeGathererDrain(con)}`
122
140
  break
123
141
  }
124
142
  case MODULE_LOADER: {
@@ -142,6 +160,18 @@ export function formatModuleLine(slot: number, itemId: number, stats: bigint): s
142
160
  out += ` +${pct}% capacity`
143
161
  break
144
162
  }
163
+ case MODULE_HAULER: {
164
+ const res = decodeStat(stats, 0)
165
+ const con = decodeStat(stats, 1)
166
+ const ref = decodeStat(stats, 2)
167
+ out += ` Capacity ${computeHaulerCapacity(res)} Efficiency ${computeHaulerEfficiency(con)} Drain ${computeHaulerDrain(ref)}`
168
+ break
169
+ }
170
+ case MODULE_WARP: {
171
+ const stat = decodeStat(stats, 0)
172
+ out += ` Range ${computeWarpRange(stat)}`
173
+ break
174
+ }
145
175
  }
146
176
  return out
147
177
  }
@@ -158,6 +188,8 @@ export function buildEntityDescription(
158
188
  baseCapacity = computeBaseCapacityShip(hullStats)
159
189
  } else if (itemId === ITEM_WAREHOUSE_T1_PACKED) {
160
190
  baseCapacity = computeBaseCapacityWarehouse(hullStats)
191
+ } else if (itemId === ITEM_EXTRACTOR_T1_PACKED) {
192
+ baseCapacity = computeBaseCapacityShip(hullStats)
161
193
  }
162
194
 
163
195
  let out = entityDisplayName(itemId)
package/src/nft/index.ts CHANGED
@@ -1,2 +1,5 @@
1
1
  export * from './deserializers'
2
2
  export * from './description'
3
+ export * from './atomicdata'
4
+ export * from './atomicassets'
5
+ export * from './buildImmutableData'
@@ -53,25 +53,22 @@ const TEMPLATES: Record<string, TemplateSpec> = {
53
53
  gatherer: {
54
54
  id: 'module.gatherer.description',
55
55
  template:
56
- 'mines resources at {yield} speed to a max depth of {depth} with {speed} gather speed while draining {drain} energy per second',
56
+ 'mines resources at {yield} yield to a max depth of {depth} while draining {drain} energy per second',
57
57
  params: [
58
58
  ['yield', 'Yield'],
59
- ['drain', 'Drain'],
60
59
  ['depth', 'Depth'],
61
- ['speed', 'Speed'],
60
+ ['drain', 'Drain'],
62
61
  ],
63
- highlightKeys: ['yield', 'depth', 'speed', 'drain'],
62
+ highlightKeys: ['yield', 'depth', 'drain'],
64
63
  },
65
64
  loader: {
66
65
  id: 'module.loader.description',
67
- template:
68
- '{quantity} loader that generates {thrust} thrust with a weight of {mass} per unit',
66
+ template: 'generates {thrust} thrust with a weight of {mass} per unit',
69
67
  params: [
70
- ['quantity', 'Quantity'],
71
68
  ['thrust', 'Thrust'],
72
69
  ['mass', 'Mass'],
73
70
  ],
74
- highlightKeys: ['quantity', 'thrust', 'mass'],
71
+ highlightKeys: ['thrust', 'mass'],
75
72
  },
76
73
  crafter: {
77
74
  id: 'module.crafter.description',
@@ -1,22 +1,50 @@
1
1
  import type {ResolvedItem} from './resolve-item'
2
2
  import type {ResourceCategory} from '../types'
3
- import {CATEGORY_LABELS, TIER_ADJECTIVES} from '../types'
3
+ import {
4
+ CATEGORY_LABELS,
5
+ RESOURCE_TIER_ADJECTIVES,
6
+ COMPONENT_TIER_PREFIXES,
7
+ MODULE_TIER_PREFIXES,
8
+ } from '../types'
4
9
  import {formatMass as defaultFormatMass} from '../format'
5
10
 
6
- export interface DisplayNameInput {
7
- itemType: 'resource' | 'component' | 'module' | 'entity' | string
11
+ interface DisplayNameInputCommon {
8
12
  tier: number
9
13
  category?: ResourceCategory
10
14
  name: string
11
15
  }
12
16
 
13
- export function displayName(resolved: DisplayNameInput): string {
14
- if (resolved.itemType === 'resource') {
15
- const adj = TIER_ADJECTIVES[resolved.tier] ?? 'Unknown'
16
- const cat = resolved.category ? CATEGORY_LABELS[resolved.category] : 'Resource'
17
- return `${adj} ${cat}`
18
- }
19
- return resolved.name
17
+ export type DisplayNameInput =
18
+ | (DisplayNameInputCommon & {itemType: 'resource' | 'component' | 'module' | 'entity' | string})
19
+ | (DisplayNameInputCommon & {type: string})
20
+
21
+ function itemTypeOf(item: DisplayNameInput): string {
22
+ return 'itemType' in item ? item.itemType : item.type
23
+ }
24
+
25
+ function tierPrefix(item: DisplayNameInput): string | null {
26
+ const t = itemTypeOf(item)
27
+ if (t === 'resource') return RESOURCE_TIER_ADJECTIVES[item.tier] ?? null
28
+ if (t === 'component') return COMPONENT_TIER_PREFIXES[item.tier] ?? null
29
+ if (t === 'module') return MODULE_TIER_PREFIXES[item.tier] ?? null
30
+ return null
31
+ }
32
+
33
+ function rootName(item: DisplayNameInput): string {
34
+ if (itemTypeOf(item) !== 'resource') return item.name
35
+ return item.category ? CATEGORY_LABELS[item.category] : 'Resource'
36
+ }
37
+
38
+ // Tier-free display name: includes the resource tier adjective / component-module
39
+ // prefix, but no "(T#)" suffix. Use this when the tier is shown separately.
40
+ export function baseName(item: DisplayNameInput): string {
41
+ const prefix = tierPrefix(item)
42
+ const root = rootName(item)
43
+ return prefix ? `${prefix} ${root}` : root
44
+ }
45
+
46
+ export function displayName(item: DisplayNameInput): string {
47
+ return `${baseName(item)} (T${item.tier})`
20
48
  }
21
49
 
22
50
  export interface DescribeOptions {
@@ -26,8 +26,9 @@ import {
26
26
  computeLoaderCapabilities,
27
27
  computeShipHullCapabilities,
28
28
  computeWarehouseHullCapabilities,
29
- } from '../entities/ship-deploy'
30
- import {computeContainerCapabilities, computeContainerT2Capabilities} from '../entities/container'
29
+ computeContainerCapabilities,
30
+ computeContainerT2Capabilities,
31
+ } from '../derivation/capabilities'
31
32
  import {
32
33
  categoryColors,
33
34
  categoryIcons,
@@ -39,6 +40,7 @@ import type {ServerContract} from '../contracts'
39
40
  import {
40
41
  ITEM_CONTAINER_T1_PACKED,
41
42
  ITEM_CONTAINER_T2_PACKED,
43
+ ITEM_EXTRACTOR_T1_PACKED,
42
44
  ITEM_SHIP_T1_PACKED,
43
45
  ITEM_WAREHOUSE_T1_PACKED,
44
46
  } from '../data/item-ids'
@@ -155,7 +157,8 @@ function resolveComponent(id: number, stats?: UInt64Type): ResolvedItem {
155
157
 
156
158
  function computeCapabilityGroup(
157
159
  moduleType: number,
158
- stats: Record<string, number>
160
+ stats: Record<string, number>,
161
+ tier: number
159
162
  ): ResolvedAttributeGroup | undefined {
160
163
  switch (moduleType) {
161
164
  case MODULE_ENGINE: {
@@ -179,14 +182,13 @@ function computeCapabilityGroup(
179
182
  }
180
183
  }
181
184
  case MODULE_GATHERER: {
182
- const caps = computeGathererCapabilities(stats)
185
+ const caps = computeGathererCapabilities(stats, tier)
183
186
  return {
184
187
  capability: 'Gatherer',
185
188
  attributes: [
186
189
  {label: 'Yield', value: caps.yield},
187
190
  {label: 'Drain', value: caps.drain},
188
191
  {label: 'Depth', value: caps.depth},
189
- {label: 'Speed', value: caps.speed},
190
192
  ],
191
193
  }
192
194
  }
@@ -223,10 +225,11 @@ function computeCapabilityGroup(
223
225
  }
224
226
  }
225
227
  case MODULE_STORAGE: {
226
- const str = stats.strength ?? 500
227
- const hrd = stats.hardness ?? 500
228
- const sat = stats.saturation ?? 500
229
- const statSum = str + hrd + sat
228
+ const str = stats.strength
229
+ const den = stats.density
230
+ const hrd = stats.hardness
231
+ const sat = stats.saturation
232
+ const statSum = str + den + hrd + sat
230
233
  const pct = 10 + Math.floor((statSum * 10) / 2997)
231
234
  return {capability: 'Storage', attributes: [{label: 'Capacity Bonus', value: pct}]}
232
235
  }
@@ -241,7 +244,7 @@ function resolveModule(id: number, stats?: UInt64Type): ResolvedItem {
241
244
  if (stats !== undefined) {
242
245
  const decoded = decodeCraftedItemStats(id, toBigStats(stats))
243
246
  const modType = getModuleCapabilityType(id)
244
- const group = computeCapabilityGroup(modType, decoded)
247
+ const group = computeCapabilityGroup(modType, decoded, item.tier)
245
248
  if (group) attributes = [group]
246
249
  }
247
250
  return {
@@ -268,6 +271,8 @@ function hullCapsForEntity(
268
271
  return computeShipHullCapabilities(decoded)
269
272
  case ITEM_WAREHOUSE_T1_PACKED:
270
273
  return computeWarehouseHullCapabilities(decoded)
274
+ case ITEM_EXTRACTOR_T1_PACKED:
275
+ return computeShipHullCapabilities(decoded)
271
276
  case ITEM_CONTAINER_T1_PACKED:
272
277
  return computeContainerCapabilities(decoded)
273
278
  case ITEM_CONTAINER_T2_PACKED:
@@ -310,13 +315,16 @@ function resolveEntity(
310
315
  const modStats = BigInt(mod.installed.stats.toString())
311
316
  const decodedStats = decodeCraftedItemStats(modItemId, modStats)
312
317
  const modType = getModuleCapabilityType(modItemId)
313
- const group = computeCapabilityGroup(modType, decodedStats)
314
318
  let modName = 'Module'
319
+ let modTier = 1
315
320
  try {
316
- modName = getItem(modItemId).name
321
+ const modItem = getItem(modItemId)
322
+ modName = modItem.name
323
+ modTier = modItem.tier
317
324
  } catch {
318
325
  modName = itemMetadata[modItemId]?.name ?? 'Module'
319
326
  }
327
+ const group = computeCapabilityGroup(modType, decodedStats, modTier)
320
328
  return {
321
329
  name: modName,
322
330
  installed: true,
@@ -72,6 +72,10 @@ export class ScheduleAccessor {
72
72
  return schedule.currentTaskProgress(this.entity, now)
73
73
  }
74
74
 
75
+ currentTaskProgressFloat(now: Date): number {
76
+ return schedule.currentTaskProgressFloat(this.entity, now)
77
+ }
78
+
75
79
  progress(now: Date): number {
76
80
  return schedule.scheduleProgress(this.entity, now)
77
81
  }