@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.
- package/lib/shipload.d.ts +187 -117
- package/lib/shipload.js +2963 -1949
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +2908 -1936
- package/lib/shipload.m.js.map +1 -1
- package/package.json +1 -1
- package/src/capabilities/modules.ts +36 -7
- package/src/capabilities/storage.ts +1 -1
- package/src/contracts/server.ts +69 -1
- package/src/data/entities.json +50 -0
- package/src/data/item-ids.ts +75 -0
- package/src/data/items.json +250 -15
- package/src/data/metadata.ts +208 -0
- package/src/data/recipes-runtime.ts +65 -0
- package/src/data/recipes.json +878 -0
- package/src/derivation/crafting.ts +172 -111
- package/src/derivation/resources.ts +14 -38
- package/src/entities/container.ts +4 -4
- package/src/entities/entity-inventory.ts +2 -2
- package/src/entities/makers.ts +18 -10
- package/src/entities/ship-deploy.ts +8 -8
- package/src/index-module.ts +2 -47
- package/src/market/items.ts +23 -75
- package/src/nft/description.ts +7 -7
- package/src/nft/deserializers.ts +6 -4
- package/src/resolution/display-name.ts +8 -4
- package/src/resolution/resolve-item.ts +80 -65
- package/src/scheduling/projection.ts +8 -28
- package/src/travel/travel.ts +8 -7
- package/src/types.ts +27 -33
- package/src/data/recipes.ts +0 -587
package/src/market/items.ts
CHANGED
|
@@ -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
|
|
8
|
-
const synthesizedCache: Map<number, Item> = new Map()
|
|
6
|
+
const itemsById = new Map<number, Item>()
|
|
9
7
|
|
|
10
|
-
for (const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
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
|
|
70
|
-
if (
|
|
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
|
-
|
|
91
|
-
itemsById.set(id, item)
|
|
92
|
-
synthesizedCache.delete(id)
|
|
40
|
+
itemsById.set(item.id, item)
|
|
93
41
|
}
|
package/src/nft/description.ts
CHANGED
|
@@ -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/
|
|
21
|
+
} from '../data/item-ids'
|
|
22
22
|
import {decodeStat} from '../derivation/crafting'
|
|
23
23
|
|
|
24
24
|
function idiv(a: number, b: number): number {
|
package/src/nft/deserializers.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
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 =
|
|
57
|
+
const layout = getEntityLayout(itemId)
|
|
58
|
+
const slots = layout?.slots ?? []
|
|
57
59
|
|
|
58
|
-
const modules: NFTModuleSlot[] =
|
|
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
|
|
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:
|
|
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[
|
|
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${
|
|
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
|
|
3
|
+
import type {ResourceCategory} from '../types'
|
|
4
4
|
import {getItem} from '../market/items'
|
|
5
|
-
import {
|
|
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
|
|
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:
|
|
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.
|
|
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:
|
|
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
|
|
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:
|
|
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:
|
|
146
|
+
name: item.name,
|
|
143
147
|
icon: itemAbbreviations[id] ?? componentIcon,
|
|
144
148
|
abbreviation: itemAbbreviations[id] ?? null,
|
|
145
|
-
tier:
|
|
146
|
-
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
|
|
227
|
+
const hrd = stats.hardness ?? 500
|
|
224
228
|
const sat = stats.saturation ?? 500
|
|
225
|
-
const statSum = str +
|
|
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
|
|
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:
|
|
249
|
+
name: item.name,
|
|
246
250
|
icon: itemAbbreviations[id] ?? moduleIcon,
|
|
247
251
|
abbreviation: itemAbbreviations[id] ?? null,
|
|
248
|
-
tier:
|
|
249
|
-
mass:
|
|
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
|
|
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
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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 (
|
|
297
|
-
|
|
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
|
-
|
|
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:
|
|
321
|
+
name: modName,
|
|
308
322
|
installed: true,
|
|
309
323
|
attributes: group?.attributes,
|
|
310
324
|
}
|
|
311
325
|
}
|
|
312
|
-
return {
|
|
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:
|
|
335
|
+
name: item.name,
|
|
319
336
|
icon: itemAbbreviations[id] ?? componentIcon,
|
|
320
337
|
abbreviation: itemAbbreviations[id] ?? null,
|
|
321
|
-
tier:
|
|
322
|
-
mass:
|
|
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 (
|
|
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
|
|
300
|
-
const
|
|
301
|
-
|
|
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 =
|
|
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 (
|
|
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
|
|
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) {
|
package/src/travel/travel.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
tier
|
|
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
|
}
|