@shipload/sdk 2.0.0-rc20 → 2.0.0-rc21

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.
@@ -1,93 +1,41 @@
1
1
  import {UInt16, UInt16Type} from '@wharfkit/antelope'
2
+ import items from '../data/items.json'
3
+ import {itemMetadata} from '../data/metadata'
2
4
  import {Item} from '../types'
3
- import itemsData from '../data/items.json'
4
- import {computeInputMass} from '../derivation/crafting'
5
- import {getComponentById, getEntityRecipeByItemId, getModuleRecipeByItemId} from '../data/recipes'
6
5
 
7
- const itemsById: Map<number, Item> = new Map()
8
- const synthesizedCache: Map<number, Item> = new Map()
6
+ const itemsById = new Map<number, Item>()
9
7
 
10
- for (const g of itemsData) {
11
- const item = Item.from({
12
- id: g.id,
13
- name: g.name,
14
- description: g.description,
15
- mass: g.mass,
16
- category: g.category,
17
- tier: g.tier,
18
- color: g.color,
19
- })
20
- itemsById.set(item.id.toNumber(), item)
21
- }
22
-
23
- export const itemIds = Array.from(itemsById.values(), (i) => i.id)
24
-
25
- interface RecipeSource {
26
- name: string
27
- description: string
28
- mass: number
29
- color: string
30
- }
31
-
32
- function synthesizeItem(id: number, source: RecipeSource): Item {
33
- return Item.from({
34
- id,
35
- name: source.name,
36
- description: source.description,
37
- mass: source.mass,
38
- category: 'ore',
39
- tier: 't1',
40
- color: source.color,
8
+ for (const raw of items as any[]) {
9
+ const meta = itemMetadata[raw.id]
10
+ if (!meta) {
11
+ throw new Error(`Missing metadata for item ${raw.id}. Add an entry to metadata.ts.`)
12
+ }
13
+ itemsById.set(raw.id, {
14
+ id: raw.id,
15
+ name: meta.name,
16
+ description: meta.description,
17
+ color: meta.color,
18
+ mass: raw.mass,
19
+ type: raw.type,
20
+ tier: raw.tier,
21
+ category: raw.category,
22
+ moduleType: raw.type === 'module' ? raw.subtype : undefined,
41
23
  })
42
24
  }
43
25
 
44
- function synthesizeFromRecipes(id: number): Item | undefined {
45
- const component = getComponentById(id)
46
- if (component) return synthesizeItem(id, component)
47
-
48
- const entityRecipe = getEntityRecipeByItemId(id)
49
- if (entityRecipe) {
50
- return synthesizeItem(id, {
51
- ...entityRecipe,
52
- mass: computeInputMass(entityRecipe.id, 'entity'),
53
- })
54
- }
55
-
56
- const moduleRecipe = getModuleRecipeByItemId(id)
57
- if (moduleRecipe) {
58
- return synthesizeItem(id, {
59
- ...moduleRecipe,
60
- mass: computeInputMass(moduleRecipe.id, 'module'),
61
- })
62
- }
63
-
64
- return undefined
65
- }
26
+ export const itemIds = Array.from(itemsById.keys())
66
27
 
67
28
  export function getItem(itemId: UInt16Type): Item {
68
29
  const id = UInt16.from(itemId).toNumber()
69
- const existing = itemsById.get(id) ?? synthesizedCache.get(id)
70
- if (existing) return existing
71
-
72
- const synthesized = synthesizeFromRecipes(id)
73
- if (synthesized) {
74
- synthesizedCache.set(id, synthesized)
75
- return synthesized
76
- }
77
-
78
- throw new Error(`Item with id ${id} not found`)
30
+ const item = itemsById.get(id)
31
+ if (!item) throw new Error(`Unknown item id: ${id}`)
32
+ return item
79
33
  }
80
34
 
81
35
  export function getItems(): Item[] {
82
36
  return Array.from(itemsById.values())
83
37
  }
84
38
 
85
- /**
86
- * @internal Test-only: registers an item into the in-memory map. Tests should
87
- * use `test/item-mock.ts`'s `registerMockItem()` instead of calling this directly.
88
- */
89
39
  export function __registerItemInternal(item: Item): void {
90
- const id = item.id.toNumber()
91
- itemsById.set(id, item)
92
- synthesizedCache.delete(id)
40
+ itemsById.set(item.id, item)
93
41
  }
@@ -1,11 +1,5 @@
1
1
  import {
2
2
  getModuleCapabilityType,
3
- ITEM_CRAFTER_T1,
4
- ITEM_ENGINE_T1,
5
- ITEM_GATHERER_T1,
6
- ITEM_GENERATOR_T1,
7
- ITEM_LOADER_T1,
8
- ITEM_STORAGE_T1,
9
3
  MODULE_CRAFTER,
10
4
  MODULE_ENGINE,
11
5
  MODULE_GATHERER,
@@ -16,9 +10,15 @@ import {
16
10
  import {
17
11
  ITEM_CONTAINER_T1_PACKED,
18
12
  ITEM_CONTAINER_T2_PACKED,
13
+ ITEM_CRAFTER_T1,
14
+ ITEM_ENGINE_T1,
15
+ ITEM_GATHERER_T1,
16
+ ITEM_GENERATOR_T1,
17
+ ITEM_LOADER_T1,
19
18
  ITEM_SHIP_T1_PACKED,
19
+ ITEM_STORAGE_T1,
20
20
  ITEM_WAREHOUSE_T1_PACKED,
21
- } from '../data/recipes'
21
+ } from '../data/item-ids'
22
22
  import {decodeStat} from '../derivation/crafting'
23
23
 
24
24
  function idiv(a: number, b: number): number {
@@ -1,4 +1,5 @@
1
- import {getEntitySlotLayout} from '../data/recipes'
1
+ import {getEntityLayout} from '../data/recipes-runtime'
2
+ import {moduleSlotTypeToCode} from '../capabilities/modules'
2
3
  import {
3
4
  ITEM_TYPE_COMPONENT,
4
5
  ITEM_TYPE_ENTITY,
@@ -53,10 +54,11 @@ export function deserializeEntity(data: Record<string, any>, itemId: number): NF
53
54
  const base = readCommonBase(data)
54
55
  const moduleItems: number[] = (data.module_items ?? []).map((v: any) => Number(v))
55
56
  const moduleStats: string[] = (data.module_stats ?? []).map((v: any) => String(v))
56
- const layout = getEntitySlotLayout(itemId)
57
+ const layout = getEntityLayout(itemId)
58
+ const slots = layout?.slots ?? []
57
59
 
58
- const modules: NFTModuleSlot[] = layout.map((slot, i) => ({
59
- type: slot.type,
60
+ const modules: NFTModuleSlot[] = slots.map((slot, i) => ({
61
+ type: moduleSlotTypeToCode(slot.type),
60
62
  installed:
61
63
  moduleItems[i] && moduleItems[i] !== 0
62
64
  ? {item_id: moduleItems[i], stats: moduleStats[i]}
@@ -1,18 +1,22 @@
1
1
  import type {ResolvedItem} from './resolve-item'
2
- import type {ResourceCategory, ResourceTier} from '../types'
2
+ import type {ResourceCategory} from '../types'
3
3
  import {CATEGORY_LABELS, TIER_ADJECTIVES, tierNumber} from '../types'
4
4
  import {formatMass as defaultFormatMass} from '../format'
5
5
 
6
6
  export interface DisplayNameInput {
7
7
  itemType: 'resource' | 'component' | 'module' | 'entity' | string
8
- tier: ResourceTier | string
8
+ tier: number | string
9
9
  category?: ResourceCategory
10
10
  name: string
11
11
  }
12
12
 
13
+ function asTierNumber(tier: number | string): number {
14
+ return typeof tier === 'number' ? tier : tierNumber(tier)
15
+ }
16
+
13
17
  export function displayName(resolved: DisplayNameInput): string {
14
18
  if (resolved.itemType === 'resource') {
15
- const adj = TIER_ADJECTIVES[tierNumber(resolved.tier)] ?? 'Unknown'
19
+ const adj = TIER_ADJECTIVES[asTierNumber(resolved.tier)] ?? 'Unknown'
16
20
  const cat = resolved.category ? CATEGORY_LABELS[resolved.category] : 'Resource'
17
21
  return `${adj} ${cat}`
18
22
  }
@@ -28,7 +32,7 @@ export interface DescribeOptions {
28
32
  export function describeItem(resolved: ResolvedItem, opts?: DescribeOptions): string {
29
33
  const massFmt = opts?.formatMass ?? defaultFormatMass
30
34
  const mass = massFmt(resolved.mass)
31
- const tier = `T${tierNumber(resolved.tier)}`
35
+ const tier = `T${asTierNumber(resolved.tier)}`
32
36
  if (resolved.itemType === 'resource') {
33
37
  const cat = resolved.category ? CATEGORY_LABELS[resolved.category] : 'Resource'
34
38
  const header = `${tier} ${cat}`
@@ -1,8 +1,9 @@
1
1
  import {UInt16, UInt64} from '@wharfkit/antelope'
2
2
  import type {UInt16Type, UInt64Type} from '@wharfkit/antelope'
3
- import type {ResourceCategory, ResourceTier} from '../types'
3
+ import type {ResourceCategory} from '../types'
4
4
  import {getItem} from '../market/items'
5
- import {getComponentById, getEntityRecipeByItemId, getModuleRecipeByItemId} from '../data/recipes'
5
+ import {getEntityLayout} from '../data/recipes-runtime'
6
+ import {entityMetadata, itemMetadata} from '../data/metadata'
6
7
  import {
7
8
  getModuleCapabilityType,
8
9
  isModuleItem,
@@ -35,6 +36,12 @@ import {
35
36
  moduleIcon,
36
37
  } from '../data/colors'
37
38
  import {ServerContract} from '../contracts'
39
+ import {
40
+ ITEM_CONTAINER_T1_PACKED,
41
+ ITEM_CONTAINER_T2_PACKED,
42
+ ITEM_SHIP_T1_PACKED,
43
+ ITEM_WAREHOUSE_T1_PACKED,
44
+ } from '../data/item-ids'
38
45
 
39
46
  export interface ResolvedItemStat {
40
47
  key: string
@@ -42,7 +49,7 @@ export interface ResolvedItemStat {
42
49
  abbreviation: string
43
50
  value: number
44
51
  color: string
45
- category: ResourceCategory
52
+ category?: ResourceCategory
46
53
  inverted?: boolean
47
54
  }
48
55
 
@@ -65,7 +72,7 @@ export interface ResolvedItem {
65
72
  icon: string
66
73
  abbreviation: string | null
67
74
  category?: ResourceCategory
68
- tier: ResourceTier
75
+ tier: number
69
76
  mass: number
70
77
  itemType: ResolvedItemType
71
78
  stats?: ResolvedItemStat[]
@@ -85,7 +92,7 @@ function resolveResource(id: number, stats?: UInt64Type): ResolvedItem {
85
92
  const item = getItem(id)
86
93
  const cat = item.category
87
94
  let resolvedStats: ResolvedItemStat[] | undefined
88
- if (stats !== undefined) {
95
+ if (stats !== undefined && cat) {
89
96
  const bigStats = toBigStats(stats)
90
97
  const defs = getStatDefinitions(cat)
91
98
  const values = [decodeStat(bigStats, 0), decodeStat(bigStats, 1), decodeStat(bigStats, 2)]
@@ -101,19 +108,19 @@ function resolveResource(id: number, stats?: UInt64Type): ResolvedItem {
101
108
  }
102
109
  return {
103
110
  itemId: id,
104
- name: item.displayName,
105
- icon: categoryIcons[cat] ?? '⬡',
111
+ name: item.name,
112
+ icon: cat ? categoryIcons[cat] : '⬡',
106
113
  abbreviation: null,
107
114
  category: cat,
108
115
  tier: item.tier,
109
- mass: Number(item.mass.value.toString()),
116
+ mass: item.mass,
110
117
  itemType: 'resource',
111
118
  stats: resolvedStats,
112
119
  }
113
120
  }
114
121
 
115
122
  function resolveComponent(id: number, stats?: UInt64Type): ResolvedItem {
116
- const comp = getComponentById(id)!
123
+ const item = getItem(id)
117
124
  let resolvedStats: ResolvedItemStat[] | undefined
118
125
  if (stats !== undefined) {
119
126
  const decoded = decodeCraftedItemStats(id, toBigStats(stats))
@@ -124,26 +131,23 @@ function resolveComponent(id: number, stats?: UInt64Type): ResolvedItem {
124
131
  .concat(getStatDefinitions('regolith'))
125
132
  .concat(getStatDefinitions('biomass'))
126
133
  const def = allDefs.find((d) => d.key === key)
127
- const statDef = comp.stats.find((s) => s.key === key)
128
- const cat = (statDef?.source ?? 'ore') as ResourceCategory
129
134
  return {
130
135
  key,
131
136
  label: def?.label ?? key,
132
137
  abbreviation: def?.abbreviation ?? key.slice(0, 3).toUpperCase(),
133
138
  value,
134
- color: categoryColors[cat],
135
- category: cat,
139
+ color: '#9BADB8',
136
140
  inverted: def?.inverted,
137
141
  }
138
142
  })
139
143
  }
140
144
  return {
141
145
  itemId: id,
142
- name: comp.name,
146
+ name: item.name,
143
147
  icon: itemAbbreviations[id] ?? componentIcon,
144
148
  abbreviation: itemAbbreviations[id] ?? null,
145
- tier: 't1' as ResourceTier,
146
- mass: comp.mass,
149
+ tier: item.tier,
150
+ mass: item.mass,
147
151
  itemType: 'component',
148
152
  stats: resolvedStats,
149
153
  }
@@ -220,9 +224,9 @@ function computeCapabilityGroup(
220
224
  }
221
225
  case MODULE_STORAGE: {
222
226
  const str = stats.strength ?? 500
223
- const fin = stats.fineness ?? 500
227
+ const hrd = stats.hardness ?? 500
224
228
  const sat = stats.saturation ?? 500
225
- const statSum = str + fin + sat
229
+ const statSum = str + hrd + sat
226
230
  const pct = 10 + Math.floor((statSum * 10) / 2997)
227
231
  return {capability: 'Storage', attributes: [{label: 'Capacity Bonus', value: pct}]}
228
232
  }
@@ -232,7 +236,7 @@ function computeCapabilityGroup(
232
236
  }
233
237
 
234
238
  function resolveModule(id: number, stats?: UInt64Type): ResolvedItem {
235
- const recipe = getModuleRecipeByItemId(id)!
239
+ const item = getItem(id)
236
240
  let attributes: ResolvedAttributeGroup[] | undefined
237
241
  if (stats !== undefined) {
238
242
  const decoded = decodeCraftedItemStats(id, toBigStats(stats))
@@ -242,59 +246,64 @@ function resolveModule(id: number, stats?: UInt64Type): ResolvedItem {
242
246
  }
243
247
  return {
244
248
  itemId: id,
245
- name: recipe.name,
249
+ name: item.name,
246
250
  icon: itemAbbreviations[id] ?? moduleIcon,
247
251
  abbreviation: itemAbbreviations[id] ?? null,
248
- tier: 't1' as ResourceTier,
249
- mass: 0,
252
+ tier: item.tier,
253
+ mass: item.mass,
250
254
  itemType: 'module',
251
255
  attributes,
252
256
  }
253
257
  }
254
258
 
259
+ function hullCapsForEntity(
260
+ itemId: number,
261
+ decoded: Record<string, number>
262
+ ): {
263
+ hullmass: number
264
+ capacity: number
265
+ } {
266
+ switch (itemId) {
267
+ case ITEM_SHIP_T1_PACKED:
268
+ return computeShipHullCapabilities(decoded)
269
+ case ITEM_WAREHOUSE_T1_PACKED:
270
+ return computeWarehouseHullCapabilities(decoded)
271
+ case ITEM_CONTAINER_T1_PACKED:
272
+ return computeContainerCapabilities(decoded)
273
+ case ITEM_CONTAINER_T2_PACKED:
274
+ return computeContainerT2Capabilities(decoded)
275
+ default:
276
+ throw new Error(`resolveItem: no capacity formula wired for entity item ${itemId}`)
277
+ }
278
+ }
279
+
255
280
  function resolveEntity(
256
281
  id: number,
257
282
  stats?: UInt64Type,
258
283
  modules?: ServerContract.Types.module_entry[]
259
284
  ): ResolvedItem {
260
- const recipe = getEntityRecipeByItemId(id)!
285
+ const item = getItem(id)
286
+ const layout = getEntityLayout(id)
261
287
  let attributes: ResolvedAttributeGroup[] | undefined
262
288
  let moduleSlots: ResolvedModuleSlot[] | undefined
263
289
 
264
290
  if (stats !== undefined) {
265
291
  const decoded = decodeCraftedItemStats(id, toBigStats(stats))
266
- attributes = []
267
-
268
- let hullCaps: {hullmass: number; capacity: number}
269
- switch (recipe.id) {
270
- case 'ship-t1':
271
- hullCaps = computeShipHullCapabilities(decoded)
272
- break
273
- case 'warehouse-t1':
274
- hullCaps = computeWarehouseHullCapabilities(decoded)
275
- break
276
- case 'container':
277
- hullCaps = computeContainerCapabilities(decoded)
278
- break
279
- case 'container-t2':
280
- hullCaps = computeContainerT2Capabilities(decoded)
281
- break
282
- default:
283
- throw new Error(
284
- `resolveItem: no capacity formula wired for entity recipe "${recipe.id}"`
285
- )
286
- }
287
- attributes.push({
288
- capability: 'Hull',
289
- attributes: [
290
- {label: 'Mass', value: hullCaps.hullmass},
291
- {label: 'Capacity', value: hullCaps.capacity},
292
- ],
293
- })
292
+ const hullCaps = hullCapsForEntity(id, decoded)
293
+ attributes = [
294
+ {
295
+ capability: 'Hull',
296
+ attributes: [
297
+ {label: 'Mass', value: hullCaps.hullmass},
298
+ {label: 'Capacity', value: hullCaps.capacity},
299
+ ],
300
+ },
301
+ ]
294
302
  }
295
303
 
296
- if (recipe.moduleSlots) {
297
- moduleSlots = recipe.moduleSlots.map((slot, i) => {
304
+ if (layout && layout.slots.length > 0) {
305
+ const slotLabels = entityMetadata[id]?.moduleSlotLabels ?? []
306
+ moduleSlots = layout.slots.map((slot, i) => {
298
307
  const mod = modules?.[i]
299
308
  if (mod?.installed) {
300
309
  const modItemId = Number(mod.installed.item_id.value.toString())
@@ -302,24 +311,32 @@ function resolveEntity(
302
311
  const decodedStats = decodeCraftedItemStats(modItemId, modStats)
303
312
  const modType = getModuleCapabilityType(modItemId)
304
313
  const group = computeCapabilityGroup(modType, decodedStats)
305
- const modRecipe = getModuleRecipeByItemId(modItemId)
314
+ let modName = 'Module'
315
+ try {
316
+ modName = getItem(modItemId).name
317
+ } catch {
318
+ modName = itemMetadata[modItemId]?.name ?? 'Module'
319
+ }
306
320
  return {
307
- name: modRecipe?.name ?? 'Module',
321
+ name: modName,
308
322
  installed: true,
309
323
  attributes: group?.attributes,
310
324
  }
311
325
  }
312
- return {installed: false}
326
+ return {
327
+ name: slotLabels[i] ?? slot.type,
328
+ installed: false,
329
+ }
313
330
  })
314
331
  }
315
332
 
316
333
  return {
317
334
  itemId: id,
318
- name: recipe.name,
335
+ name: item.name,
319
336
  icon: itemAbbreviations[id] ?? componentIcon,
320
337
  abbreviation: itemAbbreviations[id] ?? null,
321
- tier: 't1' as ResourceTier,
322
- mass: 0,
338
+ tier: item.tier,
339
+ mass: item.mass,
323
340
  itemType: 'entity',
324
341
  attributes,
325
342
  moduleSlots,
@@ -332,12 +349,10 @@ export function resolveItem(
332
349
  modules?: ServerContract.Types.module_entry[]
333
350
  ): ResolvedItem {
334
351
  const id = toNum(itemId)
352
+ const item = getItem(id)
335
353
 
336
- if (isModuleItem(id)) return resolveModule(id, stats)
337
-
338
- if (getComponentById(id)) return resolveComponent(id, stats)
339
-
340
- if (getEntityRecipeByItemId(id)) return resolveEntity(id, stats, modules)
341
-
354
+ if (item.type === 'module' || isModuleItem(id)) return resolveModule(id, stats)
355
+ if (item.type === 'component') return resolveComponent(id, stats)
356
+ if (item.type === 'entity') return resolveEntity(id, stats, modules)
342
357
  return resolveResource(id, stats)
343
358
  }
@@ -13,16 +13,10 @@ import {
13
13
  RECIPE_INPUTS_EXCESS,
14
14
  RECIPE_INPUTS_INSUFFICIENT,
15
15
  RECIPE_INPUTS_INVALID,
16
- RECIPE_INPUTS_MIXED,
17
16
  RECIPE_NOT_FOUND,
18
17
  SHIP_CARGO_NOT_LOADED,
19
18
  } from '../errors'
20
- import {
21
- getComponentById,
22
- getEntityRecipeByItemId,
23
- getModuleRecipeByItemId,
24
- RecipeInput,
25
- } from '../data/recipes'
19
+ import {getRecipe, RecipeInput} from '../data/recipes-runtime'
26
20
  import {getItem} from '../market/items'
27
21
  import {distanceBetweenCoordinates, lerp} from '../travel/travel'
28
22
  import {
@@ -296,14 +290,9 @@ export function projectEntity(entity: Projectable, options?: ProjectionOptions):
296
290
  return projected
297
291
  }
298
292
 
299
- function getRecipeForOutput(outputItemId: number): RecipeInput[] | undefined {
300
- const component = getComponentById(outputItemId)
301
- if (component) return component.recipe
302
- const moduleRecipe = getModuleRecipeByItemId(outputItemId)
303
- if (moduleRecipe) return moduleRecipe.recipe
304
- const entityRecipe = getEntityRecipeByItemId(outputItemId)
305
- if (entityRecipe) return entityRecipe.recipe
306
- return undefined
293
+ function getRecipeInputsForOutput(outputItemId: number): RecipeInput[] | undefined {
294
+ const recipe = getRecipe(outputItemId)
295
+ return recipe?.inputs
307
296
  }
308
297
 
309
298
  function validateCraftTask(task: ServerContract.Types.task, projected: ProjectedEntity): void {
@@ -313,7 +302,7 @@ function validateCraftTask(task: ServerContract.Types.task, projected: Projected
313
302
  const inputs = task.cargo.slice(0, -1)
314
303
  const craftQuantity = output.quantity.toNumber()
315
304
 
316
- const recipe = getRecipeForOutput(output.item_id.toNumber())
305
+ const recipe = getRecipeInputsForOutput(output.item_id.toNumber())
317
306
  if (!recipe) throw new Error(RECIPE_NOT_FOUND)
318
307
 
319
308
  const groupedInputs: ServerContract.Types.cargo_item[][] = recipe.map(() => [])
@@ -321,15 +310,15 @@ function validateCraftTask(task: ServerContract.Types.task, projected: Projected
321
310
  let matched = false
322
311
  for (let ri = 0; ri < recipe.length; ri++) {
323
312
  const req = recipe[ri]
324
- if (req.itemId && req.itemId > 0) {
313
+ if ('itemId' in req) {
325
314
  if (input.item_id.toNumber() === req.itemId) {
326
315
  groupedInputs[ri].push(input)
327
316
  matched = true
328
317
  break
329
318
  }
330
- } else if (req.category) {
319
+ } else {
331
320
  const item = getItem(input.item_id)
332
- if (item.category === req.category) {
321
+ if (item.category === req.category && item.tier === req.tier) {
333
322
  groupedInputs[ri].push(input)
334
323
  matched = true
335
324
  break
@@ -348,15 +337,6 @@ function validateCraftTask(task: ServerContract.Types.task, projected: Projected
348
337
  const required = recipe[ri].quantity * craftQuantity
349
338
  if (provided < required) throw new Error(RECIPE_INPUTS_INSUFFICIENT)
350
339
  if (provided !== required) throw new Error(RECIPE_INPUTS_EXCESS)
351
-
352
- if (!recipe[ri].itemId && stacks.length > 1) {
353
- const firstItemId = stacks[0].item_id.toNumber()
354
- for (let si = 1; si < stacks.length; si++) {
355
- if (stacks[si].item_id.toNumber() !== firstItemId) {
356
- throw new Error(RECIPE_INPUTS_MIXED)
357
- }
358
- }
359
- }
360
340
  }
361
341
 
362
342
  for (const input of inputs) {
@@ -174,7 +174,8 @@ export function calc_ship_mass(ship: ShipLike, cargos: CargoMassInfo[]): UInt64
174
174
  }
175
175
 
176
176
  for (const cargo of cargos) {
177
- mass.add(getItem(cargo.item_id).mass.multiplying(cargo.quantity))
177
+ const cargoMass = getItem(cargo.item_id).mass * Number(UInt32.from(cargo.quantity))
178
+ mass.add(UInt64.from(cargoMass))
178
179
  }
179
180
 
180
181
  return mass
@@ -195,8 +196,8 @@ export function calculateTransferTime(
195
196
  const qty = quantities?.get(Number(cargo.item_id)) ?? 0
196
197
  if (qty > 0) {
197
198
  const good_mass = getItem(cargo.item_id).mass
198
- const cargo_mass = good_mass.multiplying(qty)
199
- mass = UInt64.from(mass).adding(cargo_mass)
199
+ const cargo_mass = good_mass * qty
200
+ mass = UInt64.from(mass).adding(UInt64.from(cargo_mass))
200
201
  }
201
202
  }
202
203
 
@@ -249,12 +250,12 @@ export function calculateLoadTimeBreakdown(
249
250
  const good = getItem(cargo.item_id)
250
251
 
251
252
  if (loadQty > 0) {
252
- const cargo_mass = good.mass.multiplying(loadQty)
253
- mass_load = UInt64.from(mass_load).adding(cargo_mass)
253
+ const cargo_mass = good.mass * loadQty
254
+ mass_load = UInt64.from(mass_load).adding(UInt64.from(cargo_mass))
254
255
  }
255
256
  if (unloadQty > 0) {
256
- const cargo_mass = good.mass.multiplying(unloadQty)
257
- mass_unload = UInt64.from(mass_unload).adding(cargo_mass)
257
+ const cargo_mass = good.mass * unloadQty
258
+ mass_unload = UInt64.from(mass_unload).adding(UInt64.from(cargo_mass))
258
259
  }
259
260
  }
260
261
  }
package/src/types.ts CHANGED
@@ -1,13 +1,4 @@
1
- import {
2
- Int64Type,
3
- Name,
4
- Struct,
5
- UInt16,
6
- UInt16Type,
7
- UInt32,
8
- UInt32Type,
9
- UInt64,
10
- } from '@wharfkit/antelope'
1
+ import {Int64Type, Name, UInt16, UInt16Type, UInt32, UInt32Type, UInt64} from '@wharfkit/antelope'
11
2
  import {ServerContract} from './contracts'
12
3
 
13
4
  export const PRECISION = 10000
@@ -110,8 +101,20 @@ export interface Distance {
110
101
  distance: UInt16
111
102
  }
112
103
 
104
+ export type ItemType = 'resource' | 'component' | 'module' | 'entity'
113
105
  export type ResourceCategory = 'ore' | 'crystal' | 'gas' | 'regolith' | 'biomass'
114
106
  export type ResourceTier = 't1' | 't2' | 't3' | 't4' | 't5'
107
+ export type ModuleType =
108
+ | 'any'
109
+ | 'engine'
110
+ | 'generator'
111
+ | 'gatherer'
112
+ | 'loader'
113
+ | 'warp'
114
+ | 'crafter'
115
+ | 'launcher'
116
+ | 'storage'
117
+ | 'hauler'
115
118
 
116
119
  export const TIER_ADJECTIVES: Record<number, string> = {
117
120
  1: 'Crude',
@@ -138,27 +141,18 @@ export function tierNumber(tier: string): number {
138
141
  return Number(String(tier).replace(/^t/i, ''))
139
142
  }
140
143
 
141
- @Struct.type('item')
142
- export class Item extends Struct {
143
- @Struct.field(UInt16)
144
- id!: UInt16
145
- @Struct.field('string')
146
- name!: string
147
- @Struct.field('string')
148
- description!: string
149
- @Struct.field(UInt32)
150
- mass!: UInt32
151
- @Struct.field('string')
152
- category!: ResourceCategory
153
- @Struct.field('string')
154
- tier!: ResourceTier
155
- @Struct.field('string')
156
- color!: string
157
-
158
- get displayName(): string {
159
- if (this.name && this.name.length > 0) return this.name
160
- const adj = TIER_ADJECTIVES[tierNumber(this.tier)] ?? 'Unknown'
161
- const cat = CATEGORY_LABELS[this.category] ?? 'Resource'
162
- return `${adj} ${cat}`
163
- }
144
+ export interface Item {
145
+ id: number
146
+ name: string
147
+ description: string
148
+ color: string
149
+ mass: number
150
+ type: ItemType
151
+ tier: number
152
+ category?: ResourceCategory
153
+ moduleType?: ModuleType
154
+ }
155
+
156
+ export function formatTier(tier: number): string {
157
+ return 'T' + tier
164
158
  }