@shipload/sdk 1.0.0-next.74 → 1.0.0-next.75
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 +13 -1
- package/lib/shipload.js +44 -5
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +42 -6
- package/lib/shipload.m.js.map +1 -1
- package/lib/testing.js +15 -5
- package/lib/testing.js.map +1 -1
- package/lib/testing.m.js +15 -5
- package/lib/testing.m.js.map +1 -1
- package/package.json +1 -1
- package/src/data/component-process.test.ts +119 -0
- package/src/data/component-process.ts +33 -0
- package/src/data/metadata.ts +18 -5
- package/src/index-module.ts +7 -1
- package/src/resolution/resolve-item.ts +4 -0
package/package.json
CHANGED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {describe, expect, test} from 'bun:test'
|
|
2
|
+
import {
|
|
3
|
+
ITEM_BEAM,
|
|
4
|
+
ITEM_ENGINE_T1,
|
|
5
|
+
ITEM_ORE_T1,
|
|
6
|
+
ITEM_PLATE,
|
|
7
|
+
ITEM_PLATE_T2,
|
|
8
|
+
ITEM_PROSPECTOR_T1A_PACKED,
|
|
9
|
+
ITEM_PROSPECTOR_T2A_PACKED,
|
|
10
|
+
ITEM_PROSPECTOR_T2B_PACKED,
|
|
11
|
+
ITEM_ROUSTABOUT_T1A_PACKED,
|
|
12
|
+
ITEM_STORAGE_T1,
|
|
13
|
+
} from './item-ids'
|
|
14
|
+
import {getComponents} from './catalog'
|
|
15
|
+
import {getRecipe} from './recipes-runtime'
|
|
16
|
+
import {completeComponentSet, getComponentProcess} from './component-process'
|
|
17
|
+
import {resolveItem} from '../resolution/resolve-item'
|
|
18
|
+
|
|
19
|
+
function inputsOf(itemId: number) {
|
|
20
|
+
const recipe = getRecipe(itemId)
|
|
21
|
+
if (!recipe) throw new Error(`no recipe for ${itemId}`)
|
|
22
|
+
return recipe.inputs
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe('component process', () => {
|
|
26
|
+
test('every component at every tier is refined or machined, five of each per tier', () => {
|
|
27
|
+
const tiers = new Set(getComponents().map((item) => item.tier))
|
|
28
|
+
for (const tier of tiers) {
|
|
29
|
+
const kinds = getComponents({tier}).map((item) => getComponentProcess(item.id))
|
|
30
|
+
expect(kinds.filter((k) => k === 'refined').length).toBe(5)
|
|
31
|
+
expect(kinds.filter((k) => k === 'machined').length).toBe(5)
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('the refined five are the single-resource components', () => {
|
|
36
|
+
const refined = getComponents({tier: 1})
|
|
37
|
+
.filter((item) => getComponentProcess(item.id) === 'refined')
|
|
38
|
+
.map((item) => item.name)
|
|
39
|
+
.sort()
|
|
40
|
+
expect(refined).toEqual(['Ceramic', 'Plasma Cell', 'Plate', 'Polymer', 'Sensor'])
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('the kind holds across tiers', () => {
|
|
44
|
+
expect(getComponentProcess(ITEM_PLATE)).toBe('refined')
|
|
45
|
+
expect(getComponentProcess(ITEM_PLATE_T2)).toBe('refined')
|
|
46
|
+
expect(getComponentProcess(ITEM_BEAM)).toBe('machined')
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('non-components and unknown ids have no kind', () => {
|
|
50
|
+
expect(getComponentProcess(ITEM_ORE_T1)).toBeUndefined()
|
|
51
|
+
expect(getComponentProcess(ITEM_ENGINE_T1)).toBeUndefined()
|
|
52
|
+
expect(getComponentProcess(ITEM_ROUSTABOUT_T1A_PACKED)).toBeUndefined()
|
|
53
|
+
expect(getComponentProcess(65535)).toBeUndefined()
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('resolveItem carries the kind for components only', () => {
|
|
57
|
+
expect(resolveItem(ITEM_BEAM).process).toBe('machined')
|
|
58
|
+
expect(resolveItem(ITEM_PLATE_T2).process).toBe('refined')
|
|
59
|
+
expect(resolveItem(ITEM_ORE_T1).process).toBeUndefined()
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
describe('completeComponentSet', () => {
|
|
64
|
+
test('recognises the hull First and Second step recipes', () => {
|
|
65
|
+
expect(completeComponentSet(inputsOf(ITEM_ROUSTABOUT_T1A_PACKED))).toEqual({
|
|
66
|
+
process: 'refined',
|
|
67
|
+
tier: 1,
|
|
68
|
+
quantity: 200,
|
|
69
|
+
})
|
|
70
|
+
expect(completeComponentSet(inputsOf(ITEM_PROSPECTOR_T1A_PACKED))).toEqual({
|
|
71
|
+
process: 'machined',
|
|
72
|
+
tier: 1,
|
|
73
|
+
quantity: 100,
|
|
74
|
+
})
|
|
75
|
+
expect(completeComponentSet(inputsOf(ITEM_PROSPECTOR_T2A_PACKED))).toEqual({
|
|
76
|
+
process: 'refined',
|
|
77
|
+
tier: 2,
|
|
78
|
+
quantity: 200,
|
|
79
|
+
})
|
|
80
|
+
expect(completeComponentSet(inputsOf(ITEM_PROSPECTOR_T2B_PACKED))).toEqual({
|
|
81
|
+
process: 'machined',
|
|
82
|
+
tier: 2,
|
|
83
|
+
quantity: 150,
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
test('a mixed module recipe is not a complete set', () => {
|
|
88
|
+
expect(completeComponentSet(inputsOf(ITEM_STORAGE_T1))).toBeNull()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
test('unequal quantities keep the set but drop the shared quantity', () => {
|
|
92
|
+
const inputs = inputsOf(ITEM_ROUSTABOUT_T1A_PACKED).map((input, index) =>
|
|
93
|
+
index === 0 ? {...input, quantity: 50} : input
|
|
94
|
+
)
|
|
95
|
+
expect(completeComponentSet(inputs)).toEqual({process: 'refined', tier: 1, quantity: null})
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test('one machined part among four refined is not a set', () => {
|
|
99
|
+
const inputs = inputsOf(ITEM_ROUSTABOUT_T1A_PACKED).map((input, index) =>
|
|
100
|
+
index === 0 ? {...input, itemId: ITEM_BEAM} : input
|
|
101
|
+
)
|
|
102
|
+
expect(completeComponentSet(inputs)).toBeNull()
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
test('a repeated family is not a set', () => {
|
|
106
|
+
const inputs = inputsOf(ITEM_ROUSTABOUT_T1A_PACKED).map((input) => ({
|
|
107
|
+
...input,
|
|
108
|
+
itemId: ITEM_PLATE,
|
|
109
|
+
}))
|
|
110
|
+
expect(completeComponentSet(inputs)).toBeNull()
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
test('mixed tiers are not a set', () => {
|
|
114
|
+
const inputs = inputsOf(ITEM_ROUSTABOUT_T1A_PACKED).map((input, index) =>
|
|
115
|
+
index === 0 ? {...input, itemId: ITEM_PLATE_T2} : input
|
|
116
|
+
)
|
|
117
|
+
expect(completeComponentSet(inputs)).toBeNull()
|
|
118
|
+
})
|
|
119
|
+
})
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {getItem} from './catalog'
|
|
2
|
+
import {getItemFamily, type ComponentProcess} from './metadata'
|
|
3
|
+
import type {RecipeInput} from './recipes-runtime'
|
|
4
|
+
|
|
5
|
+
export const COMPONENT_PROCESS_ORDER: readonly ComponentProcess[] = ['refined', 'machined']
|
|
6
|
+
|
|
7
|
+
export function getComponentProcess(itemId: number): ComponentProcess | undefined {
|
|
8
|
+
return getItemFamily(itemId)?.process
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface CompleteComponentSet {
|
|
12
|
+
process: ComponentProcess
|
|
13
|
+
tier: number
|
|
14
|
+
quantity: number | null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function completeComponentSet(inputs: readonly RecipeInput[]): CompleteComponentSet | null {
|
|
18
|
+
const parts = inputs.filter((input) => getComponentProcess(input.itemId) !== undefined)
|
|
19
|
+
if (parts.length !== 5) return null
|
|
20
|
+
const process = getComponentProcess(parts[0].itemId)
|
|
21
|
+
const tier = getItem(parts[0].itemId).tier
|
|
22
|
+
const families = new Set<string>()
|
|
23
|
+
for (const part of parts) {
|
|
24
|
+
const item = getItem(part.itemId)
|
|
25
|
+
if (getComponentProcess(part.itemId) !== process || item.tier !== tier) return null
|
|
26
|
+
families.add(item.family)
|
|
27
|
+
}
|
|
28
|
+
if (!process || families.size !== 5) return null
|
|
29
|
+
const quantity = parts.every((part) => part.quantity === parts[0].quantity)
|
|
30
|
+
? parts[0].quantity
|
|
31
|
+
: null
|
|
32
|
+
return {process, tier, quantity}
|
|
33
|
+
}
|
package/src/data/metadata.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import items from './items.json'
|
|
2
2
|
|
|
3
|
+
export type ComponentProcess = 'refined' | 'machined'
|
|
4
|
+
|
|
3
5
|
export interface ItemFamily {
|
|
4
6
|
name: string
|
|
5
7
|
description: string
|
|
6
8
|
color: string
|
|
9
|
+
process?: ComponentProcess
|
|
7
10
|
}
|
|
8
11
|
|
|
9
12
|
export interface EntityMetadata {
|
|
@@ -14,27 +17,27 @@ export const itemFamilies: Record<string, ItemFamily> = {
|
|
|
14
17
|
// === Resources ===
|
|
15
18
|
ore: {
|
|
16
19
|
name: 'Ore',
|
|
17
|
-
description: '
|
|
20
|
+
description: 'Goes into Plate and Beam.',
|
|
18
21
|
color: '#C26D3F',
|
|
19
22
|
},
|
|
20
23
|
crystal: {
|
|
21
24
|
name: 'Crystal',
|
|
22
|
-
description: '
|
|
25
|
+
description: 'Goes into Sensors and Resonators.',
|
|
23
26
|
color: '#4ADBFF',
|
|
24
27
|
},
|
|
25
28
|
gas: {
|
|
26
29
|
name: 'Gas',
|
|
27
|
-
description: '
|
|
30
|
+
description: 'Goes into Plasma Cells and Reactors.',
|
|
28
31
|
color: '#B877FF',
|
|
29
32
|
},
|
|
30
33
|
regolith: {
|
|
31
34
|
name: 'Regolith',
|
|
32
|
-
description: '
|
|
35
|
+
description: 'Goes into Ceramic and Frame.',
|
|
33
36
|
color: '#C4A57B',
|
|
34
37
|
},
|
|
35
38
|
biomass: {
|
|
36
39
|
name: 'Biomass',
|
|
37
|
-
description: '
|
|
40
|
+
description: 'Goes into Polymer and Resin.',
|
|
38
41
|
color: '#5A8B3E',
|
|
39
42
|
},
|
|
40
43
|
|
|
@@ -43,51 +46,61 @@ export const itemFamilies: Record<string, ItemFamily> = {
|
|
|
43
46
|
name: 'Plate',
|
|
44
47
|
description: 'Goes into Cargo Holds, Containers, and hulls.',
|
|
45
48
|
color: '#7B8D9E',
|
|
49
|
+
process: 'refined',
|
|
46
50
|
},
|
|
47
51
|
frame: {
|
|
48
52
|
name: 'Frame',
|
|
49
53
|
description: 'Goes into Limpet Bays, Cargo Holds, and hulls.',
|
|
50
54
|
color: '#C4A57B',
|
|
55
|
+
process: 'machined',
|
|
51
56
|
},
|
|
52
57
|
'plasma-cell': {
|
|
53
58
|
name: 'Plasma Cell',
|
|
54
59
|
description: 'Goes into Engines, Battery Banks, and hulls.',
|
|
55
60
|
color: '#E86344',
|
|
61
|
+
process: 'refined',
|
|
56
62
|
},
|
|
57
63
|
resonator: {
|
|
58
64
|
name: 'Resonator',
|
|
59
65
|
description: 'Goes into Power Cores, Tractor Beams, Warp Drives, and hulls.',
|
|
60
66
|
color: '#4ADBFF',
|
|
67
|
+
process: 'machined',
|
|
61
68
|
},
|
|
62
69
|
beam: {
|
|
63
70
|
name: 'Beam',
|
|
64
71
|
description: 'Goes into Limpet Bays and hulls.',
|
|
65
72
|
color: '#7B8D9E',
|
|
73
|
+
process: 'machined',
|
|
66
74
|
},
|
|
67
75
|
sensor: {
|
|
68
76
|
name: 'Sensor',
|
|
69
77
|
description: 'Goes into Fabricators, Assembly Arms, and hulls.',
|
|
70
78
|
color: '#4ADBFF',
|
|
79
|
+
process: 'refined',
|
|
71
80
|
},
|
|
72
81
|
polymer: {
|
|
73
82
|
name: 'Polymer',
|
|
74
83
|
description: 'Goes into Shuttle Bays, Battery Banks, and hulls.',
|
|
75
84
|
color: '#5A8B3E',
|
|
85
|
+
process: 'refined',
|
|
76
86
|
},
|
|
77
87
|
ceramic: {
|
|
78
88
|
name: 'Ceramic',
|
|
79
89
|
description: 'Goes into Assembly Arms, Containers, and hulls.',
|
|
80
90
|
color: '#C4A57B',
|
|
91
|
+
process: 'refined',
|
|
81
92
|
},
|
|
82
93
|
reactor: {
|
|
83
94
|
name: 'Reactor',
|
|
84
95
|
description: 'Goes into Fabricators, Warp Drives, and hulls.',
|
|
85
96
|
color: '#B877FF',
|
|
97
|
+
process: 'machined',
|
|
86
98
|
},
|
|
87
99
|
resin: {
|
|
88
100
|
name: 'Resin',
|
|
89
101
|
description: 'Goes into Tractor Beams and hulls.',
|
|
90
102
|
color: '#5A8B3E',
|
|
103
|
+
process: 'machined',
|
|
91
104
|
},
|
|
92
105
|
|
|
93
106
|
// === Modules ===
|
package/src/index-module.ts
CHANGED
|
@@ -141,7 +141,13 @@ export {
|
|
|
141
141
|
categoryLabelFromIndex,
|
|
142
142
|
} from './data/catalog'
|
|
143
143
|
export {getItemFamily, itemFamilies, itemFamilyKey} from './data/metadata'
|
|
144
|
-
export type {ItemFamily} from './data/metadata'
|
|
144
|
+
export type {ComponentProcess, ItemFamily} from './data/metadata'
|
|
145
|
+
export {
|
|
146
|
+
COMPONENT_PROCESS_ORDER,
|
|
147
|
+
completeComponentSet,
|
|
148
|
+
getComponentProcess,
|
|
149
|
+
} from './data/component-process'
|
|
150
|
+
export type {CompleteComponentSet} from './data/component-process'
|
|
145
151
|
export {getCurrentEpoch, getEpochInfo} from './scheduling/epoch'
|
|
146
152
|
export type {EpochInfo} from './scheduling/epoch'
|
|
147
153
|
export {
|
|
@@ -5,6 +5,8 @@ import {getItem, getModules} from '../data/catalog'
|
|
|
5
5
|
import {ENTITY_SHIP, getPackedEntityType} from '../data/kind-registry'
|
|
6
6
|
import {getEntityLayout} from '../data/recipes-runtime'
|
|
7
7
|
import {entityMetadata, getItemFamily} from '../data/metadata'
|
|
8
|
+
import type {ComponentProcess} from '../data/metadata'
|
|
9
|
+
import {getComponentProcess} from '../data/component-process'
|
|
8
10
|
import {
|
|
9
11
|
getModuleCapabilityType,
|
|
10
12
|
isModuleItem,
|
|
@@ -75,6 +77,7 @@ export interface ResolvedItem {
|
|
|
75
77
|
tier: number
|
|
76
78
|
mass: number
|
|
77
79
|
itemType: ResolvedItemType
|
|
80
|
+
process?: ComponentProcess
|
|
78
81
|
stats?: ResolvedItemStat[]
|
|
79
82
|
attributes?: ResolvedAttributeGroup[]
|
|
80
83
|
moduleSlots?: ResolvedModuleSlot[]
|
|
@@ -149,6 +152,7 @@ function resolveComponent(id: number, stats?: UInt64Type): ResolvedItem {
|
|
|
149
152
|
tier: item.tier,
|
|
150
153
|
mass: item.mass,
|
|
151
154
|
itemType: 'component',
|
|
155
|
+
process: getComponentProcess(id),
|
|
152
156
|
stats: resolvedStats,
|
|
153
157
|
}
|
|
154
158
|
}
|