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

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 +1709 -1044
  2. package/lib/shipload.js +6762 -4658
  3. package/lib/shipload.js.map +1 -1
  4. package/lib/shipload.m.js +6653 -4614
  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 +149 -40
  46. package/src/managers/actions.ts +184 -82
  47. package/src/managers/base.ts +2 -2
  48. package/src/managers/construction-types.ts +47 -0
  49. package/src/managers/construction.ts +147 -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 +14 -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,130 @@
1
+ export interface SchemaField {
2
+ name: string
3
+ type: string
4
+ }
5
+
6
+ export type RawData =
7
+ | Uint8Array
8
+ | string
9
+ | number[]
10
+ | {immutable_serialized_data: Uint8Array | string | number[]}
11
+
12
+ export function deserializeAtomicData(
13
+ data: RawData,
14
+ schema: SchemaField[]
15
+ ): Record<string, unknown> {
16
+ let rawData: Uint8Array | string | number[]
17
+ if (data && typeof data === 'object' && 'immutable_serialized_data' in (data as object)) {
18
+ rawData = (data as {immutable_serialized_data: Uint8Array | string | number[]})
19
+ .immutable_serialized_data
20
+ } else {
21
+ rawData = data as Uint8Array | string | number[]
22
+ }
23
+
24
+ let bytes: Uint8Array
25
+ if (typeof rawData === 'string') {
26
+ const hex = rawData
27
+ bytes = new Uint8Array(hex.length / 2)
28
+ for (let i = 0; i < hex.length; i += 2) {
29
+ bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16)
30
+ }
31
+ } else if (Array.isArray(rawData)) {
32
+ bytes = new Uint8Array(rawData)
33
+ } else {
34
+ bytes = rawData
35
+ }
36
+
37
+ let offset = 0
38
+
39
+ function readVarint(): number {
40
+ let result = 0
41
+ let multiplier = 1
42
+ while (bytes[offset] >= 128) {
43
+ result += (bytes[offset] - 128) * multiplier
44
+ offset++
45
+ multiplier *= 128
46
+ }
47
+ result += bytes[offset] * multiplier
48
+ offset++
49
+ return result
50
+ }
51
+
52
+ function readVarint64(): bigint {
53
+ let result = 0n
54
+ let multiplier = 1n
55
+ while (bytes[offset] >= 128) {
56
+ result += BigInt(bytes[offset] - 128) * multiplier
57
+ offset++
58
+ multiplier *= 128n
59
+ }
60
+ result += BigInt(bytes[offset]) * multiplier
61
+ offset++
62
+ return result
63
+ }
64
+
65
+ function readZigzagInt64(): bigint {
66
+ const unsigned = readVarint64()
67
+ if (unsigned % 2n === 0n) {
68
+ return unsigned / 2n
69
+ } else {
70
+ return -(unsigned / 2n) - 1n
71
+ }
72
+ }
73
+
74
+ function readString(): string {
75
+ const length = readVarint()
76
+ const str = new TextDecoder().decode(bytes.slice(offset, offset + length))
77
+ offset += length
78
+ return str
79
+ }
80
+
81
+ const RESERVED = 4
82
+ const result: Record<string, unknown> = {}
83
+
84
+ while (offset < bytes.length) {
85
+ const fieldIndex = readVarint() - RESERVED
86
+ const field = schema[fieldIndex]
87
+ if (!field) break
88
+
89
+ switch (field.type) {
90
+ case 'uint16':
91
+ result[field.name] = readVarint()
92
+ break
93
+ case 'uint32':
94
+ result[field.name] = readVarint()
95
+ break
96
+ case 'uint64':
97
+ result[field.name] = readVarint64()
98
+ break
99
+ case 'int32':
100
+ result[field.name] = readZigzagInt64()
101
+ break
102
+ case 'int64':
103
+ result[field.name] = readZigzagInt64()
104
+ break
105
+ case 'string':
106
+ case 'image':
107
+ case 'ipfs':
108
+ result[field.name] = readString()
109
+ break
110
+ case 'uint16[]': {
111
+ const len = readVarint()
112
+ const arr: number[] = []
113
+ for (let i = 0; i < len; i++) arr.push(readVarint())
114
+ result[field.name] = arr
115
+ break
116
+ }
117
+ case 'uint64[]': {
118
+ const len = readVarint()
119
+ const arr: bigint[] = []
120
+ for (let i = 0; i < len; i++) arr.push(readVarint64())
121
+ result[field.name] = arr
122
+ break
123
+ }
124
+ default:
125
+ throw new Error(`Unknown type: ${field.type}`)
126
+ }
127
+ }
128
+
129
+ return result
130
+ }
@@ -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 {