@shipload/sdk 1.0.0-next.74 → 1.0.0-next.76
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/scan.js +1 -1
- package/lib/scan.js.map +1 -1
- package/lib/scan.m.js +1 -1
- package/lib/scan.m.js.map +1 -1
- package/lib/shipload.d.ts +81 -35
- package/lib/shipload.js +13576 -11311
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +13554 -11311
- package/lib/shipload.m.js.map +1 -1
- package/lib/testing.d.ts +20 -16
- package/lib/testing.js +229 -53
- package/lib/testing.js.map +1 -1
- package/lib/testing.m.js +229 -53
- package/lib/testing.m.js.map +1 -1
- package/package.json +6 -6
- package/src/capabilities/storage.ts +45 -10
- package/src/contracts/server.ts +49 -41
- package/src/data/component-process.test.ts +119 -0
- package/src/data/component-process.ts +33 -0
- package/src/data/entities.json +367 -2
- package/src/data/item-ids.ts +15 -0
- package/src/data/items.json +90 -0
- package/src/data/kind-registry.json +97 -7
- package/src/data/metadata.ts +83 -5
- package/src/data/recipes-runtime.ts +1 -1
- package/src/data/recipes.json +1476 -10
- package/src/data/task-types.json +9 -0
- package/src/derivation/capabilities.test.ts +25 -2
- package/src/derivation/capabilities.ts +16 -3
- package/src/derivation/capacity-fn-registry.ts +8 -1
- package/src/derivation/reserve-regen.ts +5 -9
- package/src/derivation/stratum.ts +1 -1
- package/src/derivation/tiers.ts +1 -1
- package/src/derivation/upgrades.ts +5 -4
- package/src/index-module.ts +10 -1
- package/src/influence/influence.test.ts +1 -1
- package/src/managers/actions.ts +12 -19
- package/src/managers/entities.test.ts +23 -0
- package/src/managers/entities.ts +8 -1
- package/src/managers/shuttle.test.ts +67 -0
- package/src/managers/shuttle.ts +17 -0
- package/src/nft/description.ts +6 -0
- package/src/resolution/resolve-item.test.ts +6 -6
- package/src/resolution/resolve-item.ts +4 -0
- package/src/scan/scan-wasm.base64.ts +1 -1
- package/src/scheduling/availability.test.ts +3 -3
- package/src/scheduling/availability.ts +3 -4
- package/src/scheduling/lanes.ts +1 -1
- package/src/scheduling/projection.ts +15 -13
- package/src/scheduling/schedule.test.ts +24 -0
- package/src/scheduling/schedule.ts +11 -4
- package/src/scheduling/task-effects.ts +3 -1
- package/src/scheduling/unwrap.test.ts +25 -6
- package/src/scheduling/unwrap.ts +19 -3
- package/src/travel/travel.ts +4 -0
- package/src/types.ts +2 -0
package/src/data/task-types.json
CHANGED
|
@@ -179,6 +179,15 @@
|
|
|
179
179
|
"isTerminal": false,
|
|
180
180
|
"capsPlan": false,
|
|
181
181
|
"cargoEffectResolve": false
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"type": 23,
|
|
185
|
+
"positionBound": false,
|
|
186
|
+
"mobility": false,
|
|
187
|
+
"consumer": false,
|
|
188
|
+
"isTerminal": false,
|
|
189
|
+
"capsPlan": false,
|
|
190
|
+
"cargoEffectResolve": true
|
|
182
191
|
}
|
|
183
192
|
]
|
|
184
193
|
}
|
|
@@ -10,7 +10,9 @@ import {
|
|
|
10
10
|
computeLoaderCapabilities,
|
|
11
11
|
computeBaseCapacity,
|
|
12
12
|
computeContainerCapabilities,
|
|
13
|
+
computeStructureBaseCapacity,
|
|
13
14
|
applyCapacityTier,
|
|
15
|
+
CARGO_BAY_CAPACITY_TIER_PCT,
|
|
14
16
|
GATHERER_YIELD_TIER_TABLE,
|
|
15
17
|
ENGINE_THRUST_TIER_PCT,
|
|
16
18
|
GENERATOR_CAPACITY_TIER_PCT,
|
|
@@ -34,6 +36,8 @@ import {applySlotMultiplier, U16_MAX} from '../entities/slot-multiplier'
|
|
|
34
36
|
import {getItem} from '../data/catalog'
|
|
35
37
|
import {encodeStats} from './crafting'
|
|
36
38
|
import {
|
|
39
|
+
ITEM_CONTAINER_T1_PACKED,
|
|
40
|
+
ITEM_CONTAINER_T2_PACKED,
|
|
37
41
|
ITEM_EXTRACTOR_T2_PACKED,
|
|
38
42
|
ITEM_FACTORY_T2_PACKED,
|
|
39
43
|
ITEM_MASS_DRIVER_T1_PACKED,
|
|
@@ -74,9 +78,9 @@ function makeBuilderStats(resonance: number, fineness: number): bigint {
|
|
|
74
78
|
return encodeStats([resonance, fineness])
|
|
75
79
|
}
|
|
76
80
|
|
|
77
|
-
test('computeBaseCapacity uses
|
|
81
|
+
test('computeBaseCapacity uses the structure formula for structure entities', () => {
|
|
78
82
|
const stats = {strength: 300, hardness: 400, density: 100}
|
|
79
|
-
const base =
|
|
83
|
+
const base = computeStructureBaseCapacity(stats)
|
|
80
84
|
for (const itemId of [
|
|
81
85
|
ITEM_EXTRACTOR_T2_PACKED,
|
|
82
86
|
ITEM_FACTORY_T2_PACKED,
|
|
@@ -89,6 +93,25 @@ test('computeBaseCapacity uses container formula for all container-class entitie
|
|
|
89
93
|
}
|
|
90
94
|
})
|
|
91
95
|
|
|
96
|
+
test('containers hold what a Storage module of their tier holds and weigh their packed mass', () => {
|
|
97
|
+
for (const [base, t1, t2, t2Mass] of [
|
|
98
|
+
[213, 206_600, 227_260, 27_698],
|
|
99
|
+
[500, 345_687, 380_255, 23_250],
|
|
100
|
+
]) {
|
|
101
|
+
const stats = {strength: base, density: base, hardness: base}
|
|
102
|
+
expect(computeBaseCapacity(ITEM_CONTAINER_T1_PACKED, stats)).toBe(t1)
|
|
103
|
+
expect(computeBaseCapacity(ITEM_CONTAINER_T2_PACKED, stats)).toBe(t2)
|
|
104
|
+
expect(computeContainerCapabilities(stats, ITEM_CONTAINER_T2_PACKED).hullmass).toBe(t2Mass)
|
|
105
|
+
expect(t2).toBe(
|
|
106
|
+
Math.floor(
|
|
107
|
+
(computeContainerCapabilities(stats).capacity *
|
|
108
|
+
moduleTierPct(CARGO_BAY_CAPACITY_TIER_PCT, 2)) /
|
|
109
|
+
100
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
|
|
92
115
|
test('computeBaseCapacity uses ship-hull formula for every ship-class entity', () => {
|
|
93
116
|
const stats = {strength: 300, hardness: 400, density: 100}
|
|
94
117
|
const expected = computeBaseCapacity(ITEM_SHIP_T1_PACKED, stats)
|
|
@@ -322,6 +322,12 @@ const CAPACITY_FN_BY_NAME: Partial<
|
|
|
322
322
|
depot: (stats) => computeDepotHullCapabilities(stats).capacity,
|
|
323
323
|
workshop: (stats) => computeWorkshopHullCapabilities(stats).capacity,
|
|
324
324
|
container: (stats) => computeContainerCapabilities(stats).capacity,
|
|
325
|
+
structure: computeStructureBaseCapacity,
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export function computeStructureBaseCapacity(stats: Record<string, number>): number {
|
|
329
|
+
const statSum = (stats.strength ?? 0) + (stats.hardness ?? 0)
|
|
330
|
+
return Math.floor(220000 * 6 ** (statSum / 1998.0))
|
|
325
331
|
}
|
|
326
332
|
|
|
327
333
|
export function computeBaseCapacity(itemId: number, stats: Record<string, number>): number {
|
|
@@ -333,6 +339,10 @@ export function computeBaseCapacity(itemId: number, stats: Record<string, number
|
|
|
333
339
|
const capacityFn = fnName ? CAPACITY_FN_BY_NAME[fnName] : undefined
|
|
334
340
|
if (!capacityFn) return 0
|
|
335
341
|
base = capacityFn(stats)
|
|
342
|
+
if (fnName === 'container') {
|
|
343
|
+
const pct = moduleTierPct(CARGO_BAY_CAPACITY_TIER_PCT, getItem(itemId).tier)
|
|
344
|
+
return clampUint32(Math.floor((base * pct) / 100))
|
|
345
|
+
}
|
|
336
346
|
}
|
|
337
347
|
return applyCapacityTier(base, getItem(itemId).tier)
|
|
338
348
|
}
|
|
@@ -692,14 +702,17 @@ export function computeEntityCapabilities(
|
|
|
692
702
|
return result
|
|
693
703
|
}
|
|
694
704
|
|
|
695
|
-
export function computeContainerCapabilities(
|
|
705
|
+
export function computeContainerCapabilities(
|
|
706
|
+
stats: Record<string, number>,
|
|
707
|
+
itemId: number = ITEM_CONTAINER_T1_PACKED
|
|
708
|
+
): {
|
|
696
709
|
hullmass: number
|
|
697
710
|
capacity: number
|
|
698
711
|
} {
|
|
699
712
|
const statSum = (stats.strength ?? 0) + (stats.hardness ?? 0)
|
|
700
713
|
const exponent = statSum / 1998.0
|
|
701
714
|
return {
|
|
702
|
-
hullmass: computeBaseHullmass(
|
|
703
|
-
capacity: Math.floor(
|
|
715
|
+
hullmass: computeBaseHullmass(itemId, stats),
|
|
716
|
+
capacity: Math.floor(141000 * 6 ** exponent),
|
|
704
717
|
}
|
|
705
718
|
}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import {getBaseCapacityFnName} from '../data/kind-registry'
|
|
2
2
|
|
|
3
|
-
export const CAPACITY_FN_NAMES = [
|
|
3
|
+
export const CAPACITY_FN_NAMES = [
|
|
4
|
+
'ship',
|
|
5
|
+
'warehouse',
|
|
6
|
+
'depot',
|
|
7
|
+
'container',
|
|
8
|
+
'structure',
|
|
9
|
+
'workshop',
|
|
10
|
+
] as const
|
|
4
11
|
|
|
5
12
|
export type CapacityFnName = (typeof CAPACITY_FN_NAMES)[number]
|
|
6
13
|
|
|
@@ -10,13 +10,6 @@ function toNumber(value: UInt32 | number): number {
|
|
|
10
10
|
return typeof value === 'number' ? value : Number(value)
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
function slotsBetween(now: BlockTimestamp, last: BlockTimestamp): number {
|
|
14
|
-
const nowMs = now.toMilliseconds()
|
|
15
|
-
const lastMs = last.toMilliseconds()
|
|
16
|
-
if (nowMs <= lastMs) return 0
|
|
17
|
-
return Math.floor((nowMs - lastMs) / 500)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
13
|
export function getEffectiveReserve(
|
|
21
14
|
row: EffectiveReserveInput,
|
|
22
15
|
now: BlockTimestamp,
|
|
@@ -27,8 +20,11 @@ export function getEffectiveReserve(
|
|
|
27
20
|
if (remaining >= max) return max
|
|
28
21
|
const epochSlots = epochSeconds * 2
|
|
29
22
|
if (epochSlots === 0) return remaining
|
|
30
|
-
const
|
|
31
|
-
const
|
|
23
|
+
const nowSlot = Number(now.value)
|
|
24
|
+
const lastSlot = Number(row.last_block.value)
|
|
25
|
+
if (nowSlot <= lastSlot) return remaining
|
|
26
|
+
const regen =
|
|
27
|
+
Math.floor((max * nowSlot) / epochSlots) - Math.floor((max * lastSlot) / epochSlots)
|
|
32
28
|
const effective = remaining + regen
|
|
33
29
|
return effective >= max ? max : effective
|
|
34
30
|
}
|
|
@@ -67,7 +67,7 @@ export function deriveStratum(
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
const tierRoll = ((bytes[18] << 8) | bytes[19]) >>> 0
|
|
70
|
+
const tierRoll = ((bytes[16] << 24) | (bytes[17] << 16) | (bytes[18] << 8) | bytes[19]) >>> 0
|
|
71
71
|
const withinRoll = ((bytes[20] << 8) | bytes[21]) >>> 0
|
|
72
72
|
const tier = rollTier(tierRoll, stratum)
|
|
73
73
|
const selected = getItem(selectedItemId)
|
package/src/derivation/tiers.ts
CHANGED
|
@@ -3,10 +3,11 @@ import type {Recipe} from '../data/recipes-runtime'
|
|
|
3
3
|
|
|
4
4
|
const bySource = new Map<number, Recipe[]>()
|
|
5
5
|
for (const r of recipes as Recipe[]) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
for (const source of r.sourceSubclasses ?? []) {
|
|
7
|
+
const list = bySource.get(source) ?? []
|
|
8
|
+
list.push(r)
|
|
9
|
+
bySource.set(source, list)
|
|
10
|
+
}
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export function eligibleUpgrades(entityItemId: number): Recipe[] {
|
package/src/index-module.ts
CHANGED
|
@@ -71,6 +71,7 @@ export {
|
|
|
71
71
|
OMITTED_REASONS,
|
|
72
72
|
} from './managers/shuttle'
|
|
73
73
|
export type {
|
|
74
|
+
ShuttleMissingItem,
|
|
74
75
|
ShuttleMode,
|
|
75
76
|
ShuttleOption,
|
|
76
77
|
ShuttleOptions,
|
|
@@ -141,7 +142,13 @@ export {
|
|
|
141
142
|
categoryLabelFromIndex,
|
|
142
143
|
} from './data/catalog'
|
|
143
144
|
export {getItemFamily, itemFamilies, itemFamilyKey} from './data/metadata'
|
|
144
|
-
export type {ItemFamily} from './data/metadata'
|
|
145
|
+
export type {ComponentProcess, ItemFamily} from './data/metadata'
|
|
146
|
+
export {
|
|
147
|
+
COMPONENT_PROCESS_ORDER,
|
|
148
|
+
completeComponentSet,
|
|
149
|
+
getComponentProcess,
|
|
150
|
+
} from './data/component-process'
|
|
151
|
+
export type {CompleteComponentSet} from './data/component-process'
|
|
145
152
|
export {getCurrentEpoch, getEpochInfo} from './scheduling/epoch'
|
|
146
153
|
export type {EpochInfo} from './scheduling/epoch'
|
|
147
154
|
export {
|
|
@@ -563,6 +570,7 @@ export {
|
|
|
563
570
|
computeStorageCapabilities,
|
|
564
571
|
computeBatteryCapabilities,
|
|
565
572
|
computeContainerCapabilities,
|
|
573
|
+
computeStructureBaseCapacity,
|
|
566
574
|
computeWarpCapabilities,
|
|
567
575
|
computeLauncherCapabilities,
|
|
568
576
|
computeBaseCapacity,
|
|
@@ -683,6 +691,7 @@ export {
|
|
|
683
691
|
computeBaseCapacityWarehouse,
|
|
684
692
|
computeBaseCapacityDepot,
|
|
685
693
|
computeBaseCapacityContainer,
|
|
694
|
+
computeBaseCapacityStructure,
|
|
686
695
|
computeEngineThrust,
|
|
687
696
|
computeEngineDrain,
|
|
688
697
|
computeTravelDrain,
|
|
@@ -136,7 +136,7 @@ describe('decomposition', () => {
|
|
|
136
136
|
for (let i = 1; i < DECOMP_REGISTRY.length; i++) {
|
|
137
137
|
expect(DECOMP_REGISTRY[i].itemId).toBeGreaterThan(DECOMP_REGISTRY[i - 1].itemId)
|
|
138
138
|
}
|
|
139
|
-
expect(DECOMP_REGISTRY.length).toBe(
|
|
139
|
+
expect(DECOMP_REGISTRY.length).toBe(72)
|
|
140
140
|
})
|
|
141
141
|
|
|
142
142
|
test('the retired Workshop has no decomposition entry', () => {
|
package/src/managers/actions.ts
CHANGED
|
@@ -120,15 +120,16 @@ function calcDistance(origin: CoordinatesType, destination: CoordinatesType): bi
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
function calcCargoItemMassUint32(item: ServerContract.ActionParams.Type.cargo_item): bigint {
|
|
123
|
-
let
|
|
123
|
+
let unitMass = BigInt(getItem(item.item_id).mass)
|
|
124
124
|
|
|
125
125
|
for (const mod of item.modules) {
|
|
126
126
|
if (mod.installed) {
|
|
127
|
-
|
|
127
|
+
unitMass += BigInt(getItem(mod.installed.item_id).mass)
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
|
|
131
|
+
const mass = unitMass * toBigInt(item.quantity)
|
|
132
|
+
return mass > UINT32_MAX_BIGINT ? UINT32_MAX_BIGINT : mass
|
|
132
133
|
}
|
|
133
134
|
|
|
134
135
|
function calcPayloadMass(items: ServerContract.ActionParams.Type.cargo_item[]): bigint {
|
|
@@ -1051,9 +1052,11 @@ export class ActionsManager extends BaseManager {
|
|
|
1051
1052
|
})
|
|
1052
1053
|
}
|
|
1053
1054
|
|
|
1054
|
-
closeepoch(epoch: UInt64Type): Action {
|
|
1055
|
+
closeepoch(oracleId: NameType, epoch: UInt64Type, entropy: Checksum256Type): Action {
|
|
1055
1056
|
return this.server.action('closeepoch', {
|
|
1057
|
+
oracle_id: Name.from(oracleId),
|
|
1056
1058
|
epoch: UInt64.from(epoch),
|
|
1059
|
+
entropy: Checksum256.from(entropy),
|
|
1057
1060
|
})
|
|
1058
1061
|
}
|
|
1059
1062
|
|
|
@@ -1108,27 +1111,17 @@ export class ActionsManager extends BaseManager {
|
|
|
1108
1111
|
entityId: UInt64Type,
|
|
1109
1112
|
location: CoordinatesType,
|
|
1110
1113
|
bundle: ServerContract.ActionParams.Type.cargo_item[]
|
|
1111
|
-
): Action {
|
|
1112
|
-
|
|
1113
|
-
game: this.server.account,
|
|
1114
|
+
): Action[] {
|
|
1115
|
+
const args = {
|
|
1114
1116
|
player: Name.from(player),
|
|
1115
1117
|
entity_id: UInt64.from(entityId),
|
|
1116
1118
|
x: Int64.from(location.x),
|
|
1117
1119
|
y: Int64.from(location.y),
|
|
1118
1120
|
bundle,
|
|
1119
|
-
}
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
// addcontrib asserts the citizenship rows exist, so naturalize must precede contribute.
|
|
1123
|
-
firstContribution(
|
|
1124
|
-
player: NameType,
|
|
1125
|
-
entityId: UInt64Type,
|
|
1126
|
-
location: CoordinatesType,
|
|
1127
|
-
bundle: ServerContract.ActionParams.Type.cargo_item[]
|
|
1128
|
-
): Action[] {
|
|
1121
|
+
}
|
|
1129
1122
|
return [
|
|
1130
|
-
this.
|
|
1131
|
-
this.contribute
|
|
1123
|
+
this.platform.action('contribute', {game: this.server.account, ...args}),
|
|
1124
|
+
this.server.action('contribute', args),
|
|
1132
1125
|
]
|
|
1133
1126
|
}
|
|
1134
1127
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {describe, expect, it} from 'bun:test'
|
|
2
|
+
import {UInt64} from '@wharfkit/antelope'
|
|
3
|
+
import {EntitiesManager} from './entities'
|
|
4
|
+
|
|
5
|
+
function managerWith(getImpl: (key: unknown) => Promise<unknown>) {
|
|
6
|
+
const table = () => ({get: getImpl})
|
|
7
|
+
const ctx = {server: {table}} as never
|
|
8
|
+
return new EntitiesManager(ctx)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
describe('EntitiesManager.getHullStats', () => {
|
|
12
|
+
it('reads the encoded stats off the raw entity row', async () => {
|
|
13
|
+
const m = managerWith(async () => ({stats: UInt64.from('123456789')}))
|
|
14
|
+
const stats = await m.getHullStats(23)
|
|
15
|
+
expect(stats.equals(UInt64.from('123456789'))).toBe(true)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('returns zero when the chain has no row for the id', async () => {
|
|
19
|
+
const m = managerWith(async () => undefined)
|
|
20
|
+
const stats = await m.getHullStats(23)
|
|
21
|
+
expect(stats.equals(UInt64.from(0))).toBe(true)
|
|
22
|
+
})
|
|
23
|
+
})
|
package/src/managers/entities.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {Name, type NameType, type UInt64Type} from '@wharfkit/antelope'
|
|
1
|
+
import {Name, UInt64, type NameType, type UInt64Type} from '@wharfkit/antelope'
|
|
2
2
|
import {BaseManager} from './base'
|
|
3
3
|
import {Entity} from '../entities/entity'
|
|
4
4
|
import type {EntityTypeName} from '../data/kind-registry'
|
|
@@ -43,6 +43,13 @@ export class EntitiesManager extends BaseManager {
|
|
|
43
43
|
return rows.filter((row) => wanted.equals(row.kind))
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
async getHullStats(id: UInt64Type): Promise<UInt64> {
|
|
47
|
+
const row = (await this.server.table('entity').get(UInt64.from(id))) as
|
|
48
|
+
| ServerContract.Types.entity_row
|
|
49
|
+
| undefined
|
|
50
|
+
return row?.stats ?? UInt64.from(0)
|
|
51
|
+
}
|
|
52
|
+
|
|
46
53
|
async getSummaries(
|
|
47
54
|
owner: NameType | ServerContract.Types.player_row,
|
|
48
55
|
kind?: EntityTypeName
|
|
@@ -144,6 +144,7 @@ describe('ShuttleManager', () => {
|
|
|
144
144
|
need: null,
|
|
145
145
|
cap: null,
|
|
146
146
|
task_type: null,
|
|
147
|
+
missing: [],
|
|
147
148
|
},
|
|
148
149
|
}),
|
|
149
150
|
],
|
|
@@ -153,6 +154,67 @@ describe('ShuttleManager', () => {
|
|
|
153
154
|
expect(out.options[0].blocked?.at?.getTime()).toBe(2000 * 1000)
|
|
154
155
|
})
|
|
155
156
|
|
|
157
|
+
it('an inputs rejection lists the missing items, merged by item', async () => {
|
|
158
|
+
const cargo = (item_id: number, stats: number, quantity: number) => ({
|
|
159
|
+
item_id,
|
|
160
|
+
stats,
|
|
161
|
+
modules: [],
|
|
162
|
+
quantity,
|
|
163
|
+
entity_id: null,
|
|
164
|
+
})
|
|
165
|
+
const m = managerWithReadonly(async () => ({
|
|
166
|
+
options: [
|
|
167
|
+
rawOption({
|
|
168
|
+
rejection: {
|
|
169
|
+
reason: 'Insufficient inputs for recipe.',
|
|
170
|
+
at: t(2000),
|
|
171
|
+
until: null,
|
|
172
|
+
have: null,
|
|
173
|
+
need: null,
|
|
174
|
+
cap: null,
|
|
175
|
+
task_type: null,
|
|
176
|
+
missing: [cargo(101, 1, 10), cargo(201, 0, 4), cargo(101, 2, 5)],
|
|
177
|
+
},
|
|
178
|
+
}),
|
|
179
|
+
],
|
|
180
|
+
}))
|
|
181
|
+
const out = await m.craft({
|
|
182
|
+
shipId: 5,
|
|
183
|
+
workshopId: 9,
|
|
184
|
+
recipeId: 10001,
|
|
185
|
+
quantity: 1,
|
|
186
|
+
inputs: [],
|
|
187
|
+
candidates: [],
|
|
188
|
+
})
|
|
189
|
+
const blocked = out.options[0].blocked!
|
|
190
|
+
expect(blocked.code).toBe('inputs-unavailable')
|
|
191
|
+
expect(blocked.missing).toEqual([
|
|
192
|
+
{itemId: 101, quantity: 15},
|
|
193
|
+
{itemId: 201, quantity: 4},
|
|
194
|
+
])
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
it('a rejection with nothing missing carries no missing list', async () => {
|
|
198
|
+
const m = managerWithReadonly(async () => ({
|
|
199
|
+
options: [
|
|
200
|
+
rawOption({
|
|
201
|
+
rejection: {
|
|
202
|
+
reason: 'fabricator queue is full',
|
|
203
|
+
at: t(2000),
|
|
204
|
+
until: null,
|
|
205
|
+
have: null,
|
|
206
|
+
need: null,
|
|
207
|
+
cap: null,
|
|
208
|
+
task_type: null,
|
|
209
|
+
missing: [],
|
|
210
|
+
},
|
|
211
|
+
}),
|
|
212
|
+
],
|
|
213
|
+
}))
|
|
214
|
+
const out = await m.store({shipId: 5, depotId: 9, items: [], candidates: []})
|
|
215
|
+
expect(out.options[0].blocked?.missing).toBeUndefined()
|
|
216
|
+
})
|
|
217
|
+
|
|
156
218
|
it('a never-shuttle reason is omitted', async () => {
|
|
157
219
|
const m = managerWithReadonly(async () => ({
|
|
158
220
|
options: [
|
|
@@ -167,6 +229,7 @@ describe('ShuttleManager', () => {
|
|
|
167
229
|
need: null,
|
|
168
230
|
cap: null,
|
|
169
231
|
task_type: null,
|
|
232
|
+
missing: [],
|
|
170
233
|
},
|
|
171
234
|
}),
|
|
172
235
|
],
|
|
@@ -184,6 +247,7 @@ describe('ShuttleManager', () => {
|
|
|
184
247
|
need: 200,
|
|
185
248
|
cap: 1000,
|
|
186
249
|
task_type: null,
|
|
250
|
+
missing: [],
|
|
187
251
|
}
|
|
188
252
|
const m = managerWithReadonly(async () => ({
|
|
189
253
|
options: [
|
|
@@ -206,6 +270,7 @@ describe('ShuttleManager', () => {
|
|
|
206
270
|
need: null,
|
|
207
271
|
cap: null,
|
|
208
272
|
task_type: 3,
|
|
273
|
+
missing: [],
|
|
209
274
|
}
|
|
210
275
|
const m = managerWithReadonly(async () => ({
|
|
211
276
|
options: [
|
|
@@ -368,6 +433,7 @@ describe('ShuttleManager', () => {
|
|
|
368
433
|
need: null,
|
|
369
434
|
cap: null,
|
|
370
435
|
task_type: null,
|
|
436
|
+
missing: [],
|
|
371
437
|
})
|
|
372
438
|
const m = managerWithReadonly(async () => ({
|
|
373
439
|
options: [
|
|
@@ -394,6 +460,7 @@ describe('ShuttleManager', () => {
|
|
|
394
460
|
need: null,
|
|
395
461
|
cap: null,
|
|
396
462
|
task_type: null,
|
|
463
|
+
missing: [],
|
|
397
464
|
}
|
|
398
465
|
const m = managerWithReadonly(async () => ({
|
|
399
466
|
options: [
|
package/src/managers/shuttle.ts
CHANGED
|
@@ -42,6 +42,11 @@ export type ShuttleReasonCode =
|
|
|
42
42
|
| 'not-ready'
|
|
43
43
|
| 'unknown'
|
|
44
44
|
|
|
45
|
+
export interface ShuttleMissingItem {
|
|
46
|
+
itemId: number
|
|
47
|
+
quantity: number
|
|
48
|
+
}
|
|
49
|
+
|
|
45
50
|
export interface ShuttleReason {
|
|
46
51
|
code: ShuttleReasonCode
|
|
47
52
|
reason: string
|
|
@@ -51,6 +56,7 @@ export interface ShuttleReason {
|
|
|
51
56
|
need?: number
|
|
52
57
|
cap?: number
|
|
53
58
|
taskType?: number
|
|
59
|
+
missing?: ShuttleMissingItem[]
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
export interface ShuttleOption {
|
|
@@ -166,6 +172,16 @@ function toNum(v: {toNumber(): number} | undefined): number | undefined {
|
|
|
166
172
|
return v === undefined ? undefined : v.toNumber()
|
|
167
173
|
}
|
|
168
174
|
|
|
175
|
+
function mapMissing(items: ServerContract.Types.cargo_item[]): ShuttleMissingItem[] | undefined {
|
|
176
|
+
if (items.length === 0) return undefined
|
|
177
|
+
const byItem = new Map<number, number>()
|
|
178
|
+
for (const item of items) {
|
|
179
|
+
const id = item.item_id.toNumber()
|
|
180
|
+
byItem.set(id, (byItem.get(id) ?? 0) + item.quantity.toNumber())
|
|
181
|
+
}
|
|
182
|
+
return [...byItem].map(([itemId, quantity]) => ({itemId, quantity}))
|
|
183
|
+
}
|
|
184
|
+
|
|
169
185
|
function mapReason(r: ServerContract.Types.shuttle_rejection): ShuttleReason {
|
|
170
186
|
return {
|
|
171
187
|
code: shuttleReasonCode(r.reason),
|
|
@@ -176,6 +192,7 @@ function mapReason(r: ServerContract.Types.shuttle_rejection): ShuttleReason {
|
|
|
176
192
|
need: toNum(r.need),
|
|
177
193
|
cap: toNum(r.cap),
|
|
178
194
|
taskType: toNum(r.task_type),
|
|
195
|
+
missing: mapMissing(r.missing),
|
|
179
196
|
}
|
|
180
197
|
}
|
|
181
198
|
|
package/src/nft/description.ts
CHANGED
|
@@ -71,6 +71,11 @@ export function computeBaseCapacityShip(stats: bigint): number {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
export function computeBaseCapacityContainer(stats: bigint): number {
|
|
74
|
+
const s = decodeStat(stats, 0) + decodeStat(stats, 2)
|
|
75
|
+
return Math.floor(141_000 * 6 ** (s / 1998))
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function computeBaseCapacityStructure(stats: bigint): number {
|
|
74
79
|
const s = decodeStat(stats, 0) + decodeStat(stats, 2)
|
|
75
80
|
return Math.floor(220_000 * 6 ** (s / 1998))
|
|
76
81
|
}
|
|
@@ -95,6 +100,7 @@ const CAPACITY_FN_BY_NAME: Record<CapacityFnName, (stats: bigint) => number> = {
|
|
|
95
100
|
warehouse: computeBaseCapacityWarehouse,
|
|
96
101
|
workshop: computeBaseCapacityWorkshop,
|
|
97
102
|
container: computeBaseCapacityContainer,
|
|
103
|
+
structure: computeBaseCapacityStructure,
|
|
98
104
|
depot: computeBaseCapacityDepot,
|
|
99
105
|
}
|
|
100
106
|
|
|
@@ -4,7 +4,7 @@ import {resolveItem} from './resolve-item'
|
|
|
4
4
|
import {encodeStats} from '../derivation/crafting'
|
|
5
5
|
import {
|
|
6
6
|
computeBaseHullmass,
|
|
7
|
-
|
|
7
|
+
computeStructureBaseCapacity,
|
|
8
8
|
applyCapacityTier,
|
|
9
9
|
} from '../derivation/capabilities'
|
|
10
10
|
import {getItem} from '../data/catalog'
|
|
@@ -20,24 +20,24 @@ function hullStats(strength: number, density: number, hardness: number): UInt64
|
|
|
20
20
|
return UInt64.from(encodeStats([strength, density, hardness]).toString())
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
const
|
|
23
|
+
const STRUCTURE_ENTITIES = [
|
|
24
24
|
['factory', ITEM_FACTORY_T2_PACKED],
|
|
25
25
|
['extractor', ITEM_EXTRACTOR_T2_PACKED],
|
|
26
26
|
['mass driver', ITEM_MASS_DRIVER_T1_PACKED],
|
|
27
27
|
['mass catcher', ITEM_MASS_CATCHER_T1_PACKED],
|
|
28
28
|
] as const
|
|
29
29
|
|
|
30
|
-
for (const [label, itemId] of
|
|
31
|
-
test(`resolveItem resolves ${label} hull capacity via
|
|
30
|
+
for (const [label, itemId] of STRUCTURE_ENTITIES) {
|
|
31
|
+
test(`resolveItem resolves ${label} hull capacity via structure formula`, () => {
|
|
32
32
|
const stats = hullStats(300, 100, 400)
|
|
33
33
|
const resolved = resolveItem(UInt16.from(itemId), stats)
|
|
34
34
|
const hull = resolved.attributes?.find((g) => g.capability === 'Hull')
|
|
35
35
|
const capacity = hull?.attributes.find((a) => a.label === 'Capacity')?.value
|
|
36
|
-
const base =
|
|
36
|
+
const base = computeStructureBaseCapacity({
|
|
37
37
|
strength: 300,
|
|
38
38
|
hardness: 400,
|
|
39
39
|
density: 100,
|
|
40
|
-
})
|
|
40
|
+
})
|
|
41
41
|
expect(capacity).toBe(applyCapacityTier(base, getItem(itemId).tier))
|
|
42
42
|
})
|
|
43
43
|
}
|
|
@@ -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
|
}
|