@shipload/sdk 1.0.0-next.4 → 1.0.0-next.40
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 +2473 -973
- package/lib/shipload.js +11529 -5211
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +11338 -5162
- package/lib/shipload.m.js.map +1 -1
- package/lib/testing.d.ts +970 -0
- package/lib/testing.js +4013 -0
- package/lib/testing.js.map +1 -0
- package/lib/testing.m.js +4007 -0
- package/lib/testing.m.js.map +1 -0
- package/package.json +15 -2
- package/src/capabilities/craftable.ts +51 -0
- package/src/capabilities/crafting.test.ts +7 -0
- package/src/capabilities/crafting.ts +5 -6
- package/src/capabilities/gathering.test.ts +16 -0
- package/src/capabilities/gathering.ts +35 -18
- package/src/capabilities/index.ts +0 -1
- package/src/capabilities/modules.ts +9 -0
- package/src/capabilities/storage.ts +16 -1
- package/src/contracts/platform.ts +231 -3
- package/src/contracts/server.ts +1021 -481
- package/src/coordinates/address.ts +88 -0
- package/src/coordinates/constants.test.ts +15 -0
- package/src/coordinates/constants.ts +23 -0
- package/src/coordinates/index.ts +15 -0
- package/src/coordinates/memo.test.ts +47 -0
- package/src/coordinates/memo.ts +20 -0
- package/src/coordinates/permutation.ts +77 -0
- package/src/coordinates/regions.ts +48 -0
- package/src/coordinates/sectors.ts +115 -0
- package/src/data/capabilities.ts +12 -5
- package/src/data/capability-formulas.ts +14 -7
- package/src/data/catalog.ts +0 -5
- package/src/data/colors.ts +14 -47
- package/src/data/entities.json +76 -10
- package/src/data/item-ids.ts +18 -12
- package/src/data/items.json +321 -38
- package/src/data/kind-registry.json +109 -0
- package/src/data/kind-registry.ts +165 -0
- package/src/data/metadata.ts +119 -33
- package/src/data/recipes-runtime.ts +3 -23
- package/src/data/recipes.json +238 -117
- package/src/derivation/build-methods.ts +45 -0
- package/src/derivation/capabilities.test.ts +151 -0
- package/src/derivation/capabilities.ts +512 -0
- package/src/derivation/capability-mappings.ts +9 -12
- package/src/derivation/crafting.ts +23 -24
- package/src/derivation/index.ts +25 -2
- package/src/derivation/recipe-usage.test.ts +78 -0
- package/src/derivation/recipe-usage.ts +141 -0
- package/src/derivation/reserve-regen.ts +34 -0
- package/src/derivation/resources.ts +125 -38
- package/src/derivation/rollups.test.ts +55 -0
- package/src/derivation/rollups.ts +56 -0
- package/src/derivation/stars.test.ts +51 -0
- package/src/derivation/stars.ts +15 -0
- package/src/derivation/stats.ts +6 -6
- package/src/derivation/stratum.ts +17 -20
- package/src/derivation/tiers.ts +40 -7
- package/src/derivation/wormhole.ts +136 -0
- package/src/entities/entity.ts +98 -0
- package/src/entities/gamestate.ts +3 -28
- package/src/entities/makers.ts +124 -134
- package/src/entities/slot-multiplier.ts +43 -0
- package/src/errors.ts +12 -16
- package/src/format.ts +26 -4
- package/src/index-module.ts +267 -47
- package/src/managers/actions.ts +528 -95
- package/src/managers/base.ts +6 -2
- package/src/managers/construction-types.ts +80 -0
- package/src/managers/construction.ts +412 -0
- package/src/managers/context.ts +20 -1
- package/src/managers/coordinates.ts +14 -0
- package/src/managers/entities.ts +18 -66
- package/src/managers/epochs.ts +40 -0
- package/src/managers/index.ts +17 -1
- package/src/managers/locations.ts +25 -29
- package/src/managers/nft.test.ts +14 -0
- package/src/managers/nft.ts +70 -0
- package/src/managers/plot.ts +122 -0
- package/src/nft/atomicassets.abi.json +1342 -0
- package/src/nft/atomicassets.ts +237 -0
- package/src/nft/atomicdata.ts +130 -0
- package/src/nft/buildImmutableData.ts +338 -0
- package/src/nft/description.ts +98 -24
- package/src/nft/index.ts +3 -0
- package/src/planner/index.ts +127 -0
- package/src/planner/planner.test.ts +319 -0
- package/src/resolution/describe-module.ts +18 -13
- package/src/resolution/display-name.ts +38 -10
- package/src/resolution/resolve-item.test.ts +37 -0
- package/src/resolution/resolve-item.ts +55 -24
- package/src/scheduling/accessor.ts +68 -22
- package/src/scheduling/availability.ts +108 -0
- package/src/scheduling/cancel.test.ts +348 -0
- package/src/scheduling/cancel.ts +209 -0
- package/src/scheduling/energy.ts +47 -0
- package/src/scheduling/idle-resolve.ts +45 -0
- package/src/scheduling/lane-core.ts +128 -0
- package/src/scheduling/lanes.test.ts +249 -0
- package/src/scheduling/lanes.ts +198 -0
- package/src/scheduling/projection.ts +209 -105
- package/src/scheduling/schedule.ts +241 -104
- package/src/scheduling/task-cargo.ts +46 -0
- package/src/shipload.ts +21 -1
- package/src/subscriptions/manager.ts +229 -142
- package/src/subscriptions/mappers.ts +5 -8
- package/src/subscriptions/types.ts +11 -3
- package/src/testing/catalog-hash.ts +19 -0
- package/src/testing/index.ts +2 -0
- package/src/testing/projection-parity.ts +167 -0
- package/src/travel/reach.ts +23 -0
- package/src/travel/route-planner.ts +196 -0
- package/src/travel/travel.ts +200 -112
- package/src/types/capabilities.ts +29 -6
- package/src/types/entity.ts +3 -3
- package/src/types/index.ts +0 -1
- package/src/types.ts +28 -13
- package/src/utils/cargo.ts +27 -0
- package/src/utils/display-name.ts +70 -0
- package/src/utils/system.ts +36 -24
- package/src/capabilities/loading.ts +0 -8
- package/src/entities/container.ts +0 -108
- package/src/entities/ship-deploy.ts +0 -259
- package/src/entities/ship.ts +0 -204
- package/src/entities/warehouse.ts +0 -119
- package/src/types/entity-traits.ts +0 -69
|
@@ -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}
|
|
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
|
-
['
|
|
60
|
+
['drain', 'Drain'],
|
|
62
61
|
],
|
|
63
|
-
highlightKeys: ['yield', 'depth', '
|
|
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: ['
|
|
71
|
+
highlightKeys: ['thrust', 'mass'],
|
|
75
72
|
},
|
|
76
73
|
crafter: {
|
|
77
74
|
id: 'module.crafter.description',
|
|
@@ -84,9 +81,15 @@ const TEMPLATES: Record<string, TemplateSpec> = {
|
|
|
84
81
|
},
|
|
85
82
|
storage: {
|
|
86
83
|
id: 'module.storage.description',
|
|
87
|
-
template: '
|
|
88
|
-
params: [['
|
|
89
|
-
highlightKeys: ['
|
|
84
|
+
template: 'adds {capacity} cargo capacity',
|
|
85
|
+
params: [['capacity', 'Cargo Capacity']],
|
|
86
|
+
highlightKeys: ['capacity'],
|
|
87
|
+
},
|
|
88
|
+
energy: {
|
|
89
|
+
id: 'module.energy-capacity.description',
|
|
90
|
+
template: 'adds {capacity} energy capacity',
|
|
91
|
+
params: [['capacity', 'Energy Capacity']],
|
|
92
|
+
highlightKeys: ['capacity'],
|
|
90
93
|
},
|
|
91
94
|
hauler: {
|
|
92
95
|
id: 'module.hauler.description',
|
|
@@ -127,8 +130,10 @@ export function describeModuleForItem(resolved: ResolvedItem): ModuleDescription
|
|
|
127
130
|
}
|
|
128
131
|
|
|
129
132
|
export function describeModuleForSlot(slot: ResolvedModuleSlot): ModuleDescription | null {
|
|
130
|
-
if (!slot.installed || !slot.
|
|
131
|
-
|
|
133
|
+
if (!slot.installed || !slot.attributes) return null
|
|
134
|
+
const capability = slot.capability ?? slot.name
|
|
135
|
+
if (!capability) return null
|
|
136
|
+
return describeModule({capability, attributes: slot.attributes})
|
|
132
137
|
}
|
|
133
138
|
|
|
134
139
|
export function renderDescription(
|
|
@@ -1,22 +1,50 @@
|
|
|
1
1
|
import type {ResolvedItem} from './resolve-item'
|
|
2
2
|
import type {ResourceCategory} from '../types'
|
|
3
|
-
import {
|
|
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
|
-
|
|
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
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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 {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {expect, test} from 'bun:test'
|
|
2
|
+
import {UInt16, UInt64} from '@wharfkit/antelope'
|
|
3
|
+
import {resolveItem} from './resolve-item'
|
|
4
|
+
import {encodeStats} from '../derivation/crafting'
|
|
5
|
+
import {computeContainerCapabilities} from '../derivation/capabilities'
|
|
6
|
+
import {
|
|
7
|
+
ITEM_EXTRACTOR_T1_PACKED,
|
|
8
|
+
ITEM_FACTORY_T1_PACKED,
|
|
9
|
+
ITEM_MASS_DRIVER_T1_PACKED,
|
|
10
|
+
ITEM_MASS_CATCHER_T1_PACKED,
|
|
11
|
+
} from '../data/item-ids'
|
|
12
|
+
|
|
13
|
+
function hullStats(strength: number, density: number, hardness: number): UInt64 {
|
|
14
|
+
return UInt64.from(encodeStats([strength, density, hardness]).toString())
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const CONTAINER_ENTITIES = [
|
|
18
|
+
['factory', ITEM_FACTORY_T1_PACKED],
|
|
19
|
+
['extractor', ITEM_EXTRACTOR_T1_PACKED],
|
|
20
|
+
['mass driver', ITEM_MASS_DRIVER_T1_PACKED],
|
|
21
|
+
['mass catcher', ITEM_MASS_CATCHER_T1_PACKED],
|
|
22
|
+
] as const
|
|
23
|
+
|
|
24
|
+
for (const [label, itemId] of CONTAINER_ENTITIES) {
|
|
25
|
+
test(`resolveItem resolves ${label} hull capacity via container formula`, () => {
|
|
26
|
+
const stats = hullStats(300, 100, 400)
|
|
27
|
+
const resolved = resolveItem(UInt16.from(itemId), stats)
|
|
28
|
+
const hull = resolved.attributes?.find((g) => g.capability === 'Hull')
|
|
29
|
+
const capacity = hull?.attributes.find((a) => a.label === 'Capacity')?.value
|
|
30
|
+
const expected = computeContainerCapabilities({
|
|
31
|
+
strength: 300,
|
|
32
|
+
hardness: 400,
|
|
33
|
+
density: 100,
|
|
34
|
+
}).capacity
|
|
35
|
+
expect(capacity).toBe(expected)
|
|
36
|
+
})
|
|
37
|
+
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
isModuleItem,
|
|
10
10
|
MODULE_CRAFTER,
|
|
11
11
|
MODULE_ENGINE,
|
|
12
|
+
MODULE_BATTERY,
|
|
12
13
|
MODULE_GATHERER,
|
|
13
14
|
MODULE_GENERATOR,
|
|
14
15
|
MODULE_HAULER,
|
|
@@ -19,6 +20,7 @@ import {decodeCraftedItemStats, decodeStat} from '../derivation/crafting'
|
|
|
19
20
|
import {getStatDefinitions} from '../derivation/stats'
|
|
20
21
|
import {
|
|
21
22
|
computeCrafterCapabilities,
|
|
23
|
+
computeBatteryCapabilities,
|
|
22
24
|
computeEngineCapabilities,
|
|
23
25
|
computeGathererCapabilities,
|
|
24
26
|
computeGeneratorCapabilities,
|
|
@@ -26,19 +28,20 @@ import {
|
|
|
26
28
|
computeLoaderCapabilities,
|
|
27
29
|
computeShipHullCapabilities,
|
|
28
30
|
computeWarehouseHullCapabilities,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
itemAbbreviations,
|
|
36
|
-
moduleIcon,
|
|
37
|
-
} from '../data/colors'
|
|
31
|
+
computeContainerCapabilities,
|
|
32
|
+
computeContainerT2Capabilities,
|
|
33
|
+
computeStorageCapabilities,
|
|
34
|
+
} from '../derivation/capabilities'
|
|
35
|
+
import {applySlotMultiplierUint32} from '../entities/slot-multiplier'
|
|
36
|
+
import {categoryColors, componentIcon, itemAbbreviations, moduleIcon} from '../data/colors'
|
|
38
37
|
import type {ServerContract} from '../contracts'
|
|
39
38
|
import {
|
|
40
39
|
ITEM_CONTAINER_T1_PACKED,
|
|
41
40
|
ITEM_CONTAINER_T2_PACKED,
|
|
41
|
+
ITEM_EXTRACTOR_T1_PACKED,
|
|
42
|
+
ITEM_FACTORY_T1_PACKED,
|
|
43
|
+
ITEM_MASS_CATCHER_T1_PACKED,
|
|
44
|
+
ITEM_MASS_DRIVER_T1_PACKED,
|
|
42
45
|
ITEM_SHIP_T1_PACKED,
|
|
43
46
|
ITEM_WAREHOUSE_T1_PACKED,
|
|
44
47
|
} from '../data/item-ids'
|
|
@@ -62,6 +65,7 @@ export type ResolvedItemType = 'resource' | 'component' | 'module' | 'entity'
|
|
|
62
65
|
|
|
63
66
|
export interface ResolvedModuleSlot {
|
|
64
67
|
name?: string
|
|
68
|
+
capability?: string
|
|
65
69
|
installed: boolean
|
|
66
70
|
attributes?: {label: string; value: number}[]
|
|
67
71
|
}
|
|
@@ -109,7 +113,7 @@ function resolveResource(id: number, stats?: UInt64Type): ResolvedItem {
|
|
|
109
113
|
return {
|
|
110
114
|
itemId: id,
|
|
111
115
|
name: item.name,
|
|
112
|
-
icon:
|
|
116
|
+
icon: '',
|
|
113
117
|
abbreviation: null,
|
|
114
118
|
category: cat,
|
|
115
119
|
tier: item.tier,
|
|
@@ -155,7 +159,9 @@ function resolveComponent(id: number, stats?: UInt64Type): ResolvedItem {
|
|
|
155
159
|
|
|
156
160
|
function computeCapabilityGroup(
|
|
157
161
|
moduleType: number,
|
|
158
|
-
stats: Record<string, number
|
|
162
|
+
stats: Record<string, number>,
|
|
163
|
+
tier: number,
|
|
164
|
+
outputPct = 100
|
|
159
165
|
): ResolvedAttributeGroup | undefined {
|
|
160
166
|
switch (moduleType) {
|
|
161
167
|
case MODULE_ENGINE: {
|
|
@@ -179,14 +185,13 @@ function computeCapabilityGroup(
|
|
|
179
185
|
}
|
|
180
186
|
}
|
|
181
187
|
case MODULE_GATHERER: {
|
|
182
|
-
const caps = computeGathererCapabilities(stats)
|
|
188
|
+
const caps = computeGathererCapabilities(stats, tier)
|
|
183
189
|
return {
|
|
184
190
|
capability: 'Gatherer',
|
|
185
191
|
attributes: [
|
|
186
192
|
{label: 'Yield', value: caps.yield},
|
|
187
193
|
{label: 'Drain', value: caps.drain},
|
|
188
194
|
{label: 'Depth', value: caps.depth},
|
|
189
|
-
{label: 'Speed', value: caps.speed},
|
|
190
195
|
],
|
|
191
196
|
}
|
|
192
197
|
}
|
|
@@ -223,13 +228,28 @@ function computeCapabilityGroup(
|
|
|
223
228
|
}
|
|
224
229
|
}
|
|
225
230
|
case MODULE_STORAGE: {
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
231
|
+
const caps = computeStorageCapabilities(stats)
|
|
232
|
+
return {
|
|
233
|
+
capability: 'Storage',
|
|
234
|
+
attributes: [
|
|
235
|
+
{
|
|
236
|
+
label: 'Cargo Capacity',
|
|
237
|
+
value: applySlotMultiplierUint32(caps.capacity, outputPct),
|
|
238
|
+
},
|
|
239
|
+
],
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
case MODULE_BATTERY: {
|
|
243
|
+
const caps = computeBatteryCapabilities(stats)
|
|
244
|
+
return {
|
|
245
|
+
capability: 'Energy',
|
|
246
|
+
attributes: [
|
|
247
|
+
{
|
|
248
|
+
label: 'Energy Capacity',
|
|
249
|
+
value: applySlotMultiplierUint32(caps.capacity, outputPct),
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
}
|
|
233
253
|
}
|
|
234
254
|
default:
|
|
235
255
|
return undefined
|
|
@@ -242,7 +262,7 @@ function resolveModule(id: number, stats?: UInt64Type): ResolvedItem {
|
|
|
242
262
|
if (stats !== undefined) {
|
|
243
263
|
const decoded = decodeCraftedItemStats(id, toBigStats(stats))
|
|
244
264
|
const modType = getModuleCapabilityType(id)
|
|
245
|
-
const group = computeCapabilityGroup(modType, decoded)
|
|
265
|
+
const group = computeCapabilityGroup(modType, decoded, item.tier)
|
|
246
266
|
if (group) attributes = [group]
|
|
247
267
|
}
|
|
248
268
|
return {
|
|
@@ -269,6 +289,10 @@ function hullCapsForEntity(
|
|
|
269
289
|
return computeShipHullCapabilities(decoded)
|
|
270
290
|
case ITEM_WAREHOUSE_T1_PACKED:
|
|
271
291
|
return computeWarehouseHullCapabilities(decoded)
|
|
292
|
+
case ITEM_EXTRACTOR_T1_PACKED:
|
|
293
|
+
case ITEM_FACTORY_T1_PACKED:
|
|
294
|
+
case ITEM_MASS_DRIVER_T1_PACKED:
|
|
295
|
+
case ITEM_MASS_CATCHER_T1_PACKED:
|
|
272
296
|
case ITEM_CONTAINER_T1_PACKED:
|
|
273
297
|
return computeContainerCapabilities(decoded)
|
|
274
298
|
case ITEM_CONTAINER_T2_PACKED:
|
|
@@ -289,7 +313,10 @@ function resolveEntity(
|
|
|
289
313
|
let moduleSlots: ResolvedModuleSlot[] | undefined
|
|
290
314
|
|
|
291
315
|
if (stats !== undefined) {
|
|
292
|
-
const
|
|
316
|
+
const bigStats = toBigStats(stats)
|
|
317
|
+
const decoded = decodeCraftedItemStats(id, bigStats)
|
|
318
|
+
if (decoded.strength === undefined) decoded.strength = decodeStat(bigStats, 0)
|
|
319
|
+
if (decoded.hardness === undefined) decoded.hardness = decodeStat(bigStats, 2)
|
|
293
320
|
const hullCaps = hullCapsForEntity(id, decoded)
|
|
294
321
|
attributes = [
|
|
295
322
|
{
|
|
@@ -311,15 +338,19 @@ function resolveEntity(
|
|
|
311
338
|
const modStats = BigInt(mod.installed.stats.toString())
|
|
312
339
|
const decodedStats = decodeCraftedItemStats(modItemId, modStats)
|
|
313
340
|
const modType = getModuleCapabilityType(modItemId)
|
|
314
|
-
const group = computeCapabilityGroup(modType, decodedStats)
|
|
315
341
|
let modName = 'Module'
|
|
342
|
+
let modTier = 1
|
|
316
343
|
try {
|
|
317
|
-
|
|
344
|
+
const modItem = getItem(modItemId)
|
|
345
|
+
modName = modItem.name
|
|
346
|
+
modTier = modItem.tier
|
|
318
347
|
} catch {
|
|
319
348
|
modName = itemMetadata[modItemId]?.name ?? 'Module'
|
|
320
349
|
}
|
|
350
|
+
const group = computeCapabilityGroup(modType, decodedStats, modTier, slot.outputPct)
|
|
321
351
|
return {
|
|
322
352
|
name: modName,
|
|
353
|
+
capability: group?.capability,
|
|
323
354
|
installed: true,
|
|
324
355
|
attributes: group?.attributes,
|
|
325
356
|
}
|
|
@@ -1,82 +1,128 @@
|
|
|
1
1
|
import type {ServerContract} from '../contracts'
|
|
2
2
|
import type {TaskType} from '../types'
|
|
3
|
-
import
|
|
4
|
-
import
|
|
3
|
+
import * as core from './lane-core'
|
|
4
|
+
import {
|
|
5
|
+
activeTasks,
|
|
6
|
+
getLane,
|
|
7
|
+
getLanes,
|
|
8
|
+
hasSchedule,
|
|
9
|
+
isIdle,
|
|
10
|
+
LANE_MOBILITY,
|
|
11
|
+
type LaneView,
|
|
12
|
+
type ScheduleData,
|
|
13
|
+
} from './schedule'
|
|
5
14
|
|
|
6
15
|
type Task = ServerContract.Types.task
|
|
7
16
|
|
|
8
17
|
export class ScheduleAccessor {
|
|
9
|
-
|
|
18
|
+
private _laneResolved = false
|
|
19
|
+
private _lane: LaneView | undefined
|
|
20
|
+
|
|
21
|
+
constructor(
|
|
22
|
+
private entity: ScheduleData,
|
|
23
|
+
private laneKey: number = LANE_MOBILITY
|
|
24
|
+
) {}
|
|
25
|
+
|
|
26
|
+
private get lane(): LaneView | undefined {
|
|
27
|
+
if (!this._laneResolved) {
|
|
28
|
+
this._lane = getLane(this.entity, this.laneKey)
|
|
29
|
+
this._laneResolved = true
|
|
30
|
+
}
|
|
31
|
+
return this._lane
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
forLane(laneKey: number): ScheduleAccessor {
|
|
35
|
+
return new ScheduleAccessor(this.entity, laneKey)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get lanes(): LaneView[] {
|
|
39
|
+
return getLanes(this.entity)
|
|
40
|
+
}
|
|
10
41
|
|
|
11
42
|
get hasSchedule(): boolean {
|
|
12
|
-
return
|
|
43
|
+
return hasSchedule(this.entity)
|
|
13
44
|
}
|
|
14
45
|
|
|
15
46
|
get isIdle(): boolean {
|
|
16
|
-
return
|
|
47
|
+
return isIdle(this.entity)
|
|
17
48
|
}
|
|
18
49
|
|
|
19
50
|
get tasks(): Task[] {
|
|
20
|
-
return schedule.
|
|
51
|
+
return this.lane?.schedule.tasks ?? []
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
activeTasks(now: Date): Task[] {
|
|
55
|
+
return activeTasks(this.entity, now)
|
|
21
56
|
}
|
|
22
57
|
|
|
23
58
|
duration(): number {
|
|
24
|
-
return
|
|
59
|
+
return this.lane ? core.laneDuration(this.lane.schedule) : 0
|
|
25
60
|
}
|
|
26
61
|
|
|
27
62
|
elapsed(now: Date): number {
|
|
28
|
-
return
|
|
63
|
+
return this.lane ? core.laneElapsed(this.lane.schedule, now) : 0
|
|
29
64
|
}
|
|
30
65
|
|
|
31
66
|
remaining(now: Date): number {
|
|
32
|
-
return
|
|
67
|
+
return this.lane ? core.laneRemaining(this.lane.schedule, now) : 0
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
startsIn(now: Date): number {
|
|
71
|
+
return this.lane ? core.laneStartsIn(this.lane.schedule, now) : 0
|
|
33
72
|
}
|
|
34
73
|
|
|
35
74
|
complete(now: Date): boolean {
|
|
36
|
-
return
|
|
75
|
+
return this.lane ? core.laneComplete(this.lane.schedule, now) : false
|
|
37
76
|
}
|
|
38
77
|
|
|
39
78
|
currentTaskIndex(now: Date): number {
|
|
40
|
-
return
|
|
79
|
+
return this.lane ? core.currentTaskIndexForLane(this.lane.schedule, now) : -1
|
|
41
80
|
}
|
|
42
81
|
|
|
43
82
|
currentTask(now: Date): Task | undefined {
|
|
44
|
-
return
|
|
83
|
+
return this.lane ? core.currentTask(this.lane.schedule, now) : undefined
|
|
45
84
|
}
|
|
46
85
|
|
|
47
86
|
currentTaskType(now: Date): TaskType | undefined {
|
|
48
|
-
return
|
|
87
|
+
return this.lane ? core.currentTaskType(this.lane.schedule, now) : undefined
|
|
49
88
|
}
|
|
50
89
|
|
|
51
90
|
taskStartTime(index: number): number {
|
|
52
|
-
return
|
|
91
|
+
return this.lane ? core.laneTaskStartTime(this.lane.schedule, index) : 0
|
|
53
92
|
}
|
|
54
93
|
|
|
55
94
|
taskElapsed(index: number, now: Date): number {
|
|
56
|
-
return
|
|
95
|
+
return this.lane ? core.laneTaskElapsed(this.lane.schedule, index, now) : 0
|
|
57
96
|
}
|
|
58
97
|
|
|
59
98
|
taskRemaining(index: number, now: Date): number {
|
|
60
|
-
return
|
|
99
|
+
return this.lane ? core.laneTaskRemaining(this.lane.schedule, index, now) : 0
|
|
61
100
|
}
|
|
62
101
|
|
|
63
102
|
taskComplete(index: number, now: Date): boolean {
|
|
64
|
-
return
|
|
103
|
+
return this.lane ? core.laneTaskComplete(this.lane.schedule, index, now) : false
|
|
65
104
|
}
|
|
66
105
|
|
|
67
106
|
taskInProgress(index: number, now: Date): boolean {
|
|
68
|
-
return
|
|
107
|
+
return this.lane ? core.laneTaskInProgress(this.lane.schedule, index, now) : false
|
|
69
108
|
}
|
|
70
109
|
|
|
71
110
|
currentTaskProgress(now: Date): number {
|
|
72
|
-
return
|
|
111
|
+
return this.lane ? core.currentTaskProgress(this.lane.schedule, now) : 0
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
currentTaskProgressFloat(now: Date): number {
|
|
115
|
+
return this.lane ? core.currentTaskProgressFloatForLane(this.lane.schedule, now) : 0
|
|
73
116
|
}
|
|
74
117
|
|
|
75
118
|
progress(now: Date): number {
|
|
76
|
-
return
|
|
119
|
+
return this.lane ? core.laneProgress(this.lane.schedule, now) : 0
|
|
77
120
|
}
|
|
78
121
|
}
|
|
79
122
|
|
|
80
|
-
export function createScheduleAccessor(
|
|
81
|
-
|
|
123
|
+
export function createScheduleAccessor(
|
|
124
|
+
entity: ScheduleData,
|
|
125
|
+
laneKey: number = LANE_MOBILITY
|
|
126
|
+
): ScheduleAccessor {
|
|
127
|
+
return new ScheduleAccessor(entity, laneKey)
|
|
82
128
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type {ServerContract} from '../contracts'
|
|
2
|
+
import {TaskType} from '../types'
|
|
3
|
+
import * as schedule from './schedule'
|
|
4
|
+
|
|
5
|
+
type Task = ServerContract.Types.task
|
|
6
|
+
type CargoItem = ServerContract.Types.cargo_item
|
|
7
|
+
|
|
8
|
+
export interface CargoEffect {
|
|
9
|
+
added: CargoItem[]
|
|
10
|
+
removed: CargoItem[]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface AvailabilityInput extends schedule.ScheduleData {
|
|
14
|
+
cargo: CargoItem[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function taskCargoEffect(task: Task): CargoEffect {
|
|
18
|
+
switch (task.type.toNumber()) {
|
|
19
|
+
case TaskType.LOAD:
|
|
20
|
+
case TaskType.UNWRAP:
|
|
21
|
+
case TaskType.UNDEPLOY:
|
|
22
|
+
return {added: task.cargo, removed: []}
|
|
23
|
+
case TaskType.UNLOAD:
|
|
24
|
+
return {added: [], removed: task.cargo}
|
|
25
|
+
case TaskType.GATHER:
|
|
26
|
+
return task.entitytarget ? {added: [], removed: []} : {added: task.cargo, removed: []}
|
|
27
|
+
case TaskType.CRAFT:
|
|
28
|
+
if (task.cargo.length === 0) return {added: [], removed: []}
|
|
29
|
+
return {added: [task.cargo[task.cargo.length - 1]], removed: task.cargo.slice(0, -1)}
|
|
30
|
+
case TaskType.DEPLOY:
|
|
31
|
+
return task.cargo.length > 0
|
|
32
|
+
? {added: [], removed: [task.cargo[0]]}
|
|
33
|
+
: {added: [], removed: []}
|
|
34
|
+
default:
|
|
35
|
+
return {added: [], removed: []}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function cargoKey(item: CargoItem): string {
|
|
40
|
+
const base = `${item.item_id.toNumber()}:${item.stats.toString()}`
|
|
41
|
+
const modules = item.modules ?? []
|
|
42
|
+
const entityId = item.entity_id?.toString()
|
|
43
|
+
const normalizedEntityId = entityId && entityId !== '0' ? entityId : ''
|
|
44
|
+
if (modules.length === 0 && normalizedEntityId === '') return base
|
|
45
|
+
return `${base}:modules=${JSON.stringify(modules)}:entity=${normalizedEntityId}`
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function cargoQuantity(item: CargoItem): bigint {
|
|
49
|
+
return BigInt(item.quantity.toString())
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function projectedCargoAvailableAt(
|
|
53
|
+
entity: AvailabilityInput,
|
|
54
|
+
at: Date
|
|
55
|
+
): Map<string, bigint> {
|
|
56
|
+
const avail = new Map<string, bigint>()
|
|
57
|
+
|
|
58
|
+
for (const item of entity.cargo) {
|
|
59
|
+
const key = cargoKey(item)
|
|
60
|
+
avail.set(key, (avail.get(key) ?? 0n) + cargoQuantity(item))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Every scheduled task reserves inputs against the unsettled cargo base, even already-elapsed ones.
|
|
64
|
+
const tasks = schedule.orderedTasks(entity)
|
|
65
|
+
|
|
66
|
+
for (const ordered of tasks) {
|
|
67
|
+
if (ordered.completesAt.getTime() >= at.getTime()) continue
|
|
68
|
+
|
|
69
|
+
for (const item of taskCargoEffect(ordered.task).added) {
|
|
70
|
+
const key = cargoKey(item)
|
|
71
|
+
avail.set(key, (avail.get(key) ?? 0n) + cargoQuantity(item))
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
for (const ordered of tasks) {
|
|
76
|
+
for (const item of taskCargoEffect(ordered.task).removed) {
|
|
77
|
+
const key = cargoKey(item)
|
|
78
|
+
const current = avail.get(key) ?? 0n
|
|
79
|
+
const quantity = cargoQuantity(item)
|
|
80
|
+
avail.set(key, current > quantity ? current - quantity : 0n)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return avail
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Latest completion among scheduled tasks producing any of the given inputs (a craft starts no earlier).
|
|
88
|
+
export function cargoReadyAt(entity: AvailabilityInput, inputItemIds: readonly number[]): Date {
|
|
89
|
+
let readyMs = 0
|
|
90
|
+
for (const ordered of schedule.orderedTasks(entity)) {
|
|
91
|
+
for (const item of taskCargoEffect(ordered.task).added) {
|
|
92
|
+
if (inputItemIds.includes(item.item_id.toNumber())) {
|
|
93
|
+
readyMs = Math.max(readyMs, ordered.completesAt.getTime())
|
|
94
|
+
break
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return new Date(readyMs)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function availableForItem(avail: Map<string, bigint>, itemId: number): bigint {
|
|
102
|
+
const prefix = `${itemId}:`
|
|
103
|
+
let total = 0n
|
|
104
|
+
for (const [key, quantity] of avail) {
|
|
105
|
+
if (key.startsWith(prefix)) total += quantity
|
|
106
|
+
}
|
|
107
|
+
return total
|
|
108
|
+
}
|