@shipload/sdk 1.0.0-next.5 → 1.0.0-next.51

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.
Files changed (145) hide show
  1. package/lib/scan.d.ts +52 -0
  2. package/lib/scan.js +162 -0
  3. package/lib/scan.js.map +1 -0
  4. package/lib/scan.m.js +152 -0
  5. package/lib/scan.m.js.map +1 -0
  6. package/lib/shipload.d.ts +3167 -1353
  7. package/lib/shipload.js +13319 -5640
  8. package/lib/shipload.js.map +1 -1
  9. package/lib/shipload.m.js +13091 -5589
  10. package/lib/shipload.m.js.map +1 -1
  11. package/lib/testing.d.ts +1037 -0
  12. package/lib/testing.js +4363 -0
  13. package/lib/testing.js.map +1 -0
  14. package/lib/testing.m.js +4357 -0
  15. package/lib/testing.m.js.map +1 -0
  16. package/package.json +20 -2
  17. package/src/capabilities/craftable.ts +51 -0
  18. package/src/capabilities/crafting.test.ts +7 -0
  19. package/src/capabilities/crafting.ts +6 -7
  20. package/src/capabilities/gathering.test.ts +29 -0
  21. package/src/capabilities/gathering.ts +37 -19
  22. package/src/capabilities/index.ts +0 -1
  23. package/src/capabilities/modules.ts +22 -0
  24. package/src/capabilities/storage.ts +16 -1
  25. package/src/contracts/platform.ts +231 -3
  26. package/src/contracts/server.ts +1178 -511
  27. package/src/coordinates/address.ts +88 -0
  28. package/src/coordinates/constants.test.ts +15 -0
  29. package/src/coordinates/constants.ts +23 -0
  30. package/src/coordinates/index.ts +15 -0
  31. package/src/coordinates/memo.test.ts +47 -0
  32. package/src/coordinates/memo.ts +20 -0
  33. package/src/coordinates/permutation.ts +77 -0
  34. package/src/coordinates/regions.ts +48 -0
  35. package/src/coordinates/sectors.ts +115 -0
  36. package/src/data/capabilities.ts +31 -16
  37. package/src/data/capability-formulas.ts +21 -14
  38. package/src/data/catalog.ts +0 -5
  39. package/src/data/colors.ts +13 -47
  40. package/src/data/entities.json +154 -10
  41. package/src/data/item-ids.ts +31 -12
  42. package/src/data/items.json +192 -74
  43. package/src/data/kind-registry.json +131 -0
  44. package/src/data/kind-registry.ts +172 -0
  45. package/src/data/metadata.ts +166 -48
  46. package/src/data/recipes-runtime.ts +5 -23
  47. package/src/data/recipes.json +1003 -163
  48. package/src/derivation/build-methods.test.ts +13 -0
  49. package/src/derivation/build-methods.ts +48 -0
  50. package/src/derivation/capabilities.test.ts +151 -0
  51. package/src/derivation/capabilities.ts +527 -0
  52. package/src/derivation/capability-mappings.ts +58 -16
  53. package/src/derivation/crafting.ts +36 -33
  54. package/src/derivation/index.ts +28 -2
  55. package/src/derivation/recipe-usage.test.ts +81 -0
  56. package/src/derivation/recipe-usage.ts +141 -0
  57. package/src/derivation/reserve-regen.ts +34 -0
  58. package/src/derivation/resources.ts +98 -19
  59. package/src/derivation/rollups.test.ts +55 -0
  60. package/src/derivation/rollups.ts +56 -0
  61. package/src/derivation/stars.test.ts +67 -0
  62. package/src/derivation/stars.ts +25 -0
  63. package/src/derivation/stats.ts +6 -6
  64. package/src/derivation/stratum.ts +26 -22
  65. package/src/derivation/tiers.ts +40 -7
  66. package/src/derivation/upgrades.ts +14 -0
  67. package/src/derivation/wormhole.ts +141 -0
  68. package/src/entities/entity.ts +98 -0
  69. package/src/entities/gamestate.ts +3 -28
  70. package/src/entities/makers.ts +131 -134
  71. package/src/entities/slot-multiplier.ts +43 -0
  72. package/src/errors.ts +12 -16
  73. package/src/format.ts +26 -4
  74. package/src/index-module.ts +294 -34
  75. package/src/managers/actions.ts +616 -105
  76. package/src/managers/base.ts +6 -2
  77. package/src/managers/cluster.test.ts +39 -0
  78. package/src/managers/cluster.ts +53 -0
  79. package/src/managers/construction-types.ts +80 -0
  80. package/src/managers/construction.ts +412 -0
  81. package/src/managers/context.ts +29 -1
  82. package/src/managers/coordinates.ts +14 -0
  83. package/src/managers/entities.ts +27 -66
  84. package/src/managers/epochs.ts +40 -0
  85. package/src/managers/flatten-gather-plan.test.ts +80 -0
  86. package/src/managers/index.ts +20 -1
  87. package/src/managers/locations.ts +25 -29
  88. package/src/managers/nft.test.ts +14 -0
  89. package/src/managers/nft.ts +70 -0
  90. package/src/managers/players.ts +25 -0
  91. package/src/managers/plot.ts +122 -0
  92. package/src/nft/atomicassets.abi.json +1342 -0
  93. package/src/nft/atomicassets.ts +237 -0
  94. package/src/nft/atomicdata.ts +130 -0
  95. package/src/nft/buildImmutableData.ts +336 -0
  96. package/src/nft/description.ts +104 -36
  97. package/src/nft/index.ts +3 -0
  98. package/src/planner/index.ts +242 -0
  99. package/src/planner/planner.test.ts +325 -0
  100. package/src/resolution/describe-module.ts +43 -25
  101. package/src/resolution/display-name.ts +38 -10
  102. package/src/resolution/resolve-item.test.ts +48 -0
  103. package/src/resolution/resolve-item.ts +74 -47
  104. package/src/scan/index.ts +244 -0
  105. package/src/scan/scan-wasm.base64.ts +2 -0
  106. package/src/scheduling/accessor.ts +65 -23
  107. package/src/scheduling/availability.ts +108 -0
  108. package/src/scheduling/cancel.test.ts +348 -0
  109. package/src/scheduling/cancel.ts +209 -0
  110. package/src/scheduling/energy.ts +47 -0
  111. package/src/scheduling/idle-resolve.ts +45 -0
  112. package/src/scheduling/lane-core.ts +128 -0
  113. package/src/scheduling/lanes.test.ts +249 -0
  114. package/src/scheduling/lanes.ts +198 -0
  115. package/src/scheduling/projection.ts +210 -107
  116. package/src/scheduling/schedule.ts +240 -117
  117. package/src/scheduling/task-cargo.ts +46 -0
  118. package/src/scheduling/unwrap.test.ts +86 -0
  119. package/src/scheduling/unwrap.ts +190 -0
  120. package/src/shipload.ts +26 -1
  121. package/src/subscriptions/manager.cluster.test.ts +51 -0
  122. package/src/subscriptions/manager.ts +245 -172
  123. package/src/subscriptions/mappers.ts +5 -8
  124. package/src/subscriptions/types.ts +28 -3
  125. package/src/testing/catalog-hash.ts +19 -0
  126. package/src/testing/index.ts +2 -0
  127. package/src/testing/projection-parity.ts +167 -0
  128. package/src/travel/reach.ts +23 -0
  129. package/src/travel/route-planner.ts +277 -0
  130. package/src/travel/route-simulator.ts +174 -0
  131. package/src/travel/travel.ts +175 -118
  132. package/src/types/capabilities.ts +29 -6
  133. package/src/types/entity.ts +3 -3
  134. package/src/types/index.ts +0 -1
  135. package/src/types.ts +39 -14
  136. package/src/utils/cargo.test.ts +14 -0
  137. package/src/utils/cargo.ts +29 -0
  138. package/src/utils/display-name.ts +70 -0
  139. package/src/utils/system.ts +36 -24
  140. package/src/capabilities/loading.ts +0 -8
  141. package/src/entities/container.ts +0 -123
  142. package/src/entities/ship-deploy.ts +0 -295
  143. package/src/entities/ship.ts +0 -221
  144. package/src/entities/warehouse.ts +0 -127
  145. package/src/types/entity-traits.ts +0 -69
@@ -1,7 +1,7 @@
1
1
  import {UInt16, UInt64} from '@wharfkit/antelope'
2
2
  import type {UInt16Type, UInt64Type} from '@wharfkit/antelope'
3
3
  import type {ResourceCategory} from '../types'
4
- import {getItem} from '../data/catalog'
4
+ import {getItem, getModules} from '../data/catalog'
5
5
  import {getEntityLayout} from '../data/recipes-runtime'
6
6
  import {entityMetadata, itemMetadata} from '../data/metadata'
7
7
  import {
@@ -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,29 +20,19 @@ 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,
25
27
  computeHaulerCapabilities,
26
28
  computeLoaderCapabilities,
27
- computeShipHullCapabilities,
28
- computeWarehouseHullCapabilities,
29
- } from '../entities/ship-deploy'
30
- import {computeContainerCapabilities, computeContainerT2Capabilities} from '../entities/container'
31
- import {
32
- categoryColors,
33
- categoryIcons,
34
- componentIcon,
35
- itemAbbreviations,
36
- moduleIcon,
37
- } from '../data/colors'
29
+ computeBaseCapacity,
30
+ computeBaseHullmass,
31
+ computeStorageCapabilities,
32
+ } from '../derivation/capabilities'
33
+ import {applySlotMultiplierUint32} from '../entities/slot-multiplier'
34
+ import {categoryColors, componentIcon, itemAbbreviations, moduleIcon} from '../data/colors'
38
35
  import type {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'
45
36
 
46
37
  export interface ResolvedItemStat {
47
38
  key: string
@@ -62,8 +53,13 @@ export type ResolvedItemType = 'resource' | 'component' | 'module' | 'entity'
62
53
 
63
54
  export interface ResolvedModuleSlot {
64
55
  name?: string
56
+ capability?: string
65
57
  installed: boolean
66
58
  attributes?: {label: string; value: number}[]
59
+ slotLabel?: string
60
+ slotType?: string
61
+ outputPct?: number
62
+ maxTier?: number
67
63
  }
68
64
 
69
65
  export interface ResolvedItem {
@@ -109,7 +105,7 @@ function resolveResource(id: number, stats?: UInt64Type): ResolvedItem {
109
105
  return {
110
106
  itemId: id,
111
107
  name: item.name,
112
- icon: cat ? categoryIcons[cat] : '',
108
+ icon: '',
113
109
  abbreviation: null,
114
110
  category: cat,
115
111
  tier: item.tier,
@@ -156,7 +152,8 @@ function resolveComponent(id: number, stats?: UInt64Type): ResolvedItem {
156
152
  function computeCapabilityGroup(
157
153
  moduleType: number,
158
154
  stats: Record<string, number>,
159
- tier: number
155
+ tier: number,
156
+ outputPct = 100
160
157
  ): ResolvedAttributeGroup | undefined {
161
158
  switch (moduleType) {
162
159
  case MODULE_ENGINE: {
@@ -187,14 +184,13 @@ function computeCapabilityGroup(
187
184
  {label: 'Yield', value: caps.yield},
188
185
  {label: 'Drain', value: caps.drain},
189
186
  {label: 'Depth', value: caps.depth},
190
- {label: 'Speed', value: caps.speed},
191
187
  ],
192
188
  }
193
189
  }
194
190
  case MODULE_LOADER: {
195
191
  const caps = computeLoaderCapabilities(stats)
196
192
  return {
197
- capability: 'Loader',
193
+ capability: 'Loading',
198
194
  attributes: [
199
195
  {label: 'Mass', value: caps.mass},
200
196
  {label: 'Thrust', value: caps.thrust},
@@ -205,7 +201,7 @@ function computeCapabilityGroup(
205
201
  case MODULE_CRAFTER: {
206
202
  const caps = computeCrafterCapabilities(stats)
207
203
  return {
208
- capability: 'Crafter',
204
+ capability: 'Crafting',
209
205
  attributes: [
210
206
  {label: 'Speed', value: caps.speed},
211
207
  {label: 'Drain', value: caps.drain},
@@ -213,9 +209,9 @@ function computeCapabilityGroup(
213
209
  }
214
210
  }
215
211
  case MODULE_HAULER: {
216
- const caps = computeHaulerCapabilities(stats)
212
+ const caps = computeHaulerCapabilities(stats, tier)
217
213
  return {
218
- capability: 'Hauler',
214
+ capability: 'Hauling',
219
215
  attributes: [
220
216
  {label: 'Capacity', value: caps.capacity},
221
217
  {label: 'Efficiency', value: caps.efficiency},
@@ -224,24 +220,50 @@ function computeCapabilityGroup(
224
220
  }
225
221
  }
226
222
  case MODULE_STORAGE: {
227
- const str = stats.strength
228
- const den = stats.density
229
- const hrd = stats.hardness
230
- const sat = stats.saturation
231
- const statSum = str + den + hrd + sat
232
- const pct = 10 + Math.floor((statSum * 10) / 2997)
233
- return {capability: 'Storage', attributes: [{label: 'Capacity Bonus', value: pct}]}
223
+ const caps = computeStorageCapabilities(stats)
224
+ return {
225
+ capability: 'Storage',
226
+ attributes: [
227
+ {
228
+ label: 'Cargo Capacity',
229
+ value: applySlotMultiplierUint32(caps.capacity, outputPct),
230
+ },
231
+ ],
232
+ }
233
+ }
234
+ case MODULE_BATTERY: {
235
+ const caps = computeBatteryCapabilities(stats)
236
+ return {
237
+ capability: 'Energy',
238
+ attributes: [
239
+ {
240
+ label: 'Energy Capacity',
241
+ value: applySlotMultiplierUint32(caps.capacity, outputPct),
242
+ },
243
+ ],
244
+ }
234
245
  }
235
246
  default:
236
247
  return undefined
237
248
  }
238
249
  }
239
250
 
251
+ // Recipe-less higher-tier modules reuse the T1 stat layout of their module type.
252
+ function decodeModuleStats(id: number, big: bigint): Record<string, number> {
253
+ const decoded = decodeCraftedItemStats(id, big)
254
+ if (Object.keys(decoded).length > 0) return decoded
255
+ const moduleType = getItem(id).moduleType
256
+ if (!moduleType) return decoded
257
+ const t1 = getModules({moduleType, tier: 1})[0]
258
+ if (!t1) return decoded
259
+ return decodeCraftedItemStats(Number(t1.id), big)
260
+ }
261
+
240
262
  function resolveModule(id: number, stats?: UInt64Type): ResolvedItem {
241
263
  const item = getItem(id)
242
264
  let attributes: ResolvedAttributeGroup[] | undefined
243
265
  if (stats !== undefined) {
244
- const decoded = decodeCraftedItemStats(id, toBigStats(stats))
266
+ const decoded = decodeModuleStats(id, toBigStats(stats))
245
267
  const modType = getModuleCapabilityType(id)
246
268
  const group = computeCapabilityGroup(modType, decoded, item.tier)
247
269
  if (group) attributes = [group]
@@ -265,17 +287,9 @@ function hullCapsForEntity(
265
287
  hullmass: number
266
288
  capacity: number
267
289
  } {
268
- switch (itemId) {
269
- case ITEM_SHIP_T1_PACKED:
270
- return computeShipHullCapabilities(decoded)
271
- case ITEM_WAREHOUSE_T1_PACKED:
272
- return computeWarehouseHullCapabilities(decoded)
273
- case ITEM_CONTAINER_T1_PACKED:
274
- return computeContainerCapabilities(decoded)
275
- case ITEM_CONTAINER_T2_PACKED:
276
- return computeContainerT2Capabilities(decoded)
277
- default:
278
- throw new Error(`resolveItem: no capacity formula wired for entity item ${itemId}`)
290
+ return {
291
+ hullmass: computeBaseHullmass(decoded),
292
+ capacity: computeBaseCapacity(itemId, decoded),
279
293
  }
280
294
  }
281
295
 
@@ -290,7 +304,11 @@ function resolveEntity(
290
304
  let moduleSlots: ResolvedModuleSlot[] | undefined
291
305
 
292
306
  if (stats !== undefined) {
293
- const decoded = decodeCraftedItemStats(id, toBigStats(stats))
307
+ const bigStats = toBigStats(stats)
308
+ const decoded = decodeCraftedItemStats(id, bigStats)
309
+ if (decoded.strength === undefined) decoded.strength = decodeStat(bigStats, 0)
310
+ if (decoded.density === undefined) decoded.density = decodeStat(bigStats, 1)
311
+ if (decoded.hardness === undefined) decoded.hardness = decodeStat(bigStats, 2)
294
312
  const hullCaps = hullCapsForEntity(id, decoded)
295
313
  attributes = [
296
314
  {
@@ -306,11 +324,17 @@ function resolveEntity(
306
324
  if (layout && layout.slots.length > 0) {
307
325
  const slotLabels = entityMetadata[id]?.moduleSlotLabels ?? []
308
326
  moduleSlots = layout.slots.map((slot, i) => {
327
+ const slotInfo = {
328
+ slotLabel: slotLabels[i] ?? slot.type,
329
+ slotType: slot.type,
330
+ outputPct: slot.outputPct,
331
+ maxTier: slot.maxTier,
332
+ }
309
333
  const mod = modules?.[i]
310
334
  if (mod?.installed) {
311
335
  const modItemId = Number(mod.installed.item_id.value.toString())
312
336
  const modStats = BigInt(mod.installed.stats.toString())
313
- const decodedStats = decodeCraftedItemStats(modItemId, modStats)
337
+ const decodedStats = decodeModuleStats(modItemId, modStats)
314
338
  const modType = getModuleCapabilityType(modItemId)
315
339
  let modName = 'Module'
316
340
  let modTier = 1
@@ -321,16 +345,19 @@ function resolveEntity(
321
345
  } catch {
322
346
  modName = itemMetadata[modItemId]?.name ?? 'Module'
323
347
  }
324
- const group = computeCapabilityGroup(modType, decodedStats, modTier)
348
+ const group = computeCapabilityGroup(modType, decodedStats, modTier, slot.outputPct)
325
349
  return {
326
350
  name: modName,
351
+ capability: group?.capability,
327
352
  installed: true,
328
353
  attributes: group?.attributes,
354
+ ...slotInfo,
329
355
  }
330
356
  }
331
357
  return {
332
358
  name: slotLabels[i] ?? slot.type,
333
359
  installed: false,
360
+ ...slotInfo,
334
361
  }
335
362
  })
336
363
  }
@@ -0,0 +1,244 @@
1
+ import {SCAN_WASM_B64} from './scan-wasm.base64'
2
+
3
+ const stubImports: WebAssembly.Imports = new Proxy(
4
+ {},
5
+ {
6
+ get: () => new Proxy({}, {get: () => () => 0}),
7
+ }
8
+ ) as any
9
+
10
+ let inst: WebAssembly.Instance | null = null
11
+ let readyPromise: Promise<void> | null = null
12
+
13
+ function bytes(): Uint8Array {
14
+ return Uint8Array.from(atob(SCAN_WASM_B64), (c) => c.charCodeAt(0))
15
+ }
16
+
17
+ function finish(i: WebAssembly.Instance) {
18
+ const ex = i.exports as any
19
+ if (typeof ex._initialize === 'function') ex._initialize()
20
+ inst = i
21
+ }
22
+
23
+ export function scanReady(): Promise<void> {
24
+ if (inst) return Promise.resolve()
25
+ if (!readyPromise)
26
+ readyPromise = (
27
+ WebAssembly.instantiate(bytes().buffer as ArrayBuffer, stubImports) as Promise<{
28
+ instance: WebAssembly.Instance
29
+ }>
30
+ ).then((r) => finish(r.instance))
31
+ return readyPromise
32
+ }
33
+
34
+ function ex(): any {
35
+ if (!inst)
36
+ finish(
37
+ new WebAssembly.Instance(
38
+ new WebAssembly.Module(bytes().buffer as ArrayBuffer),
39
+ stubImports
40
+ )
41
+ )
42
+ return inst!.exports
43
+ }
44
+
45
+ const hex = (h: string) => Uint8Array.from(h.match(/../g)!.map((b) => parseInt(b, 16)))
46
+
47
+ // Shared marshalling for the `*_in_box` exports; grow-and-retry once on overflow (export returns -needed).
48
+ function boxScan<T>(
49
+ exportName: string,
50
+ stride: number,
51
+ gameSeed: string,
52
+ xMin: number,
53
+ yMin: number,
54
+ xMax: number,
55
+ yMax: number,
56
+ decode: (dv: DataView, o: number) => T
57
+ ): T[] {
58
+ const e = ex()
59
+ const mem = e.memory as WebAssembly.Memory
60
+ const g = e.malloc(32)
61
+ new Uint8Array(mem.buffer, g, 32).set(hex(gameSeed))
62
+ let cap = 256
63
+ let out = e.malloc(cap * stride)
64
+ let n = e[exportName](g, xMin, yMin, xMax, yMax, out, cap)
65
+ if (n < 0) {
66
+ e.free(out)
67
+ cap = -n
68
+ out = e.malloc(cap * stride)
69
+ n = e[exportName](g, xMin, yMin, xMax, yMax, out, cap)
70
+ }
71
+ const dv = new DataView(mem.buffer, out, n * stride)
72
+ const res: T[] = []
73
+ for (let i = 0; i < n; i++) res.push(decode(dv, i * stride))
74
+ e.free(g)
75
+ e.free(out)
76
+ return res
77
+ }
78
+
79
+ export function getLocationType(gameSeed: string, x: number, y: number): number {
80
+ const e = ex()
81
+ const mem = e.memory as WebAssembly.Memory
82
+ const g = e.malloc(32)
83
+ new Uint8Array(mem.buffer, g, 32).set(hex(gameSeed))
84
+ const t = e.get_location_type(g, BigInt(x), BigInt(y))
85
+ e.free(g)
86
+ return t
87
+ }
88
+
89
+ export interface SystemCell {
90
+ x: number
91
+ y: number
92
+ locType: number
93
+ }
94
+
95
+ export interface Coord {
96
+ x: number
97
+ y: number
98
+ }
99
+
100
+ export interface Deposit {
101
+ x: number
102
+ y: number
103
+ depth: number
104
+ itemId: number
105
+ richness: number
106
+ reserve: number
107
+ stats: [number, number, number]
108
+ }
109
+
110
+ export interface DerivedCell {
111
+ location: {x: number; y: number; locType: number; subtype: number; size: number}
112
+ deposits: Deposit[]
113
+ }
114
+
115
+ export function systemsInBox(
116
+ gameSeed: string,
117
+ xMin: number,
118
+ yMin: number,
119
+ xMax: number,
120
+ yMax: number
121
+ ): SystemCell[] {
122
+ return boxScan('systems_in_box', 12, gameSeed, xMin, yMin, xMax, yMax, (dv, o) => ({
123
+ x: dv.getInt32(o, true),
124
+ y: dv.getInt32(o + 4, true),
125
+ locType: dv.getUint32(o + 8, true),
126
+ }))
127
+ }
128
+
129
+ export interface LocationCell {
130
+ x: number
131
+ y: number
132
+ locType: number
133
+ subtype: number
134
+ size: number
135
+ }
136
+
137
+ export function locationsInBox(
138
+ gameSeed: string,
139
+ xMin: number,
140
+ yMin: number,
141
+ xMax: number,
142
+ yMax: number
143
+ ): LocationCell[] {
144
+ return boxScan('locations_in_box', 16, gameSeed, xMin, yMin, xMax, yMax, (dv, o) => ({
145
+ x: dv.getInt32(o, true),
146
+ y: dv.getInt32(o + 4, true),
147
+ locType: dv.getUint8(o + 8),
148
+ subtype: dv.getUint8(o + 9),
149
+ size: dv.getUint32(o + 12, true),
150
+ }))
151
+ }
152
+
153
+ export interface WormholeCell {
154
+ x: number
155
+ y: number
156
+ exit: {x: number; y: number}
157
+ }
158
+
159
+ export function wormholesInBox(
160
+ gameSeed: string,
161
+ xMin: number,
162
+ yMin: number,
163
+ xMax: number,
164
+ yMax: number
165
+ ): WormholeCell[] {
166
+ return boxScan('wormholes_in_box', 16, gameSeed, xMin, yMin, xMax, yMax, (dv, o) => ({
167
+ x: dv.getInt32(o, true),
168
+ y: dv.getInt32(o + 4, true),
169
+ exit: {x: dv.getInt32(o + 8, true), y: dv.getInt32(o + 12, true)},
170
+ }))
171
+ }
172
+
173
+ export async function scanCells(
174
+ gameSeed: string,
175
+ epochSeed: string,
176
+ cells: Coord[]
177
+ ): Promise<DerivedCell[]> {
178
+ await scanReady()
179
+ return scanCellsCore(gameSeed, epochSeed, cells)
180
+ }
181
+
182
+ // Sync sibling of scanCells; caller must warm the instance via scanReady() first.
183
+ export function scanCellsSync(gameSeed: string, epochSeed: string, cells: Coord[]): DerivedCell[] {
184
+ return scanCellsCore(gameSeed, epochSeed, cells)
185
+ }
186
+
187
+ function scanCellsCore(gameSeed: string, epochSeed: string, cells: Coord[]): DerivedCell[] {
188
+ const e = ex()
189
+ const mem = e.memory as WebAssembly.Memory
190
+ const write = (b: Uint8Array) => {
191
+ const p = e.malloc(b.length)
192
+ new Uint8Array(mem.buffer, p, b.length).set(b)
193
+ return p
194
+ }
195
+ const gp = write(hex(gameSeed))
196
+ const ep = write(hex(epochSeed))
197
+ const cellArr = new Int32Array(cells.length * 2)
198
+ cells.forEach((c, i) => {
199
+ cellArr[i * 2] = c.x
200
+ cellArr[i * 2 + 1] = c.y
201
+ })
202
+ const cp = write(new Uint8Array(cellArr.buffer))
203
+ const locOut = e.malloc(cells.length * 8)
204
+ let cap = Math.max(64, cells.length * 8)
205
+ let depOut = e.malloc(cap * 40)
206
+ let n = e.scan_cells(gp, ep, cp, cells.length, locOut, depOut, cap)
207
+ if (n < 0) {
208
+ e.free(depOut)
209
+ cap = -n
210
+ depOut = e.malloc(cap * 40)
211
+ n = e.scan_cells(gp, ep, cp, cells.length, locOut, depOut, cap)
212
+ }
213
+ const locView = new DataView(mem.buffer, locOut, cells.length * 8)
214
+ const depView = new DataView(mem.buffer, depOut, n * 40)
215
+ const out: DerivedCell[] = cells.map((c, i) => ({
216
+ location: {
217
+ x: c.x,
218
+ y: c.y,
219
+ locType: locView.getUint8(i * 8),
220
+ subtype: locView.getUint8(i * 8 + 1),
221
+ size: locView.getUint32(i * 8 + 4, true),
222
+ },
223
+ deposits: [],
224
+ }))
225
+ for (let i = 0; i < n; i++) {
226
+ const o = i * 40
227
+ const ci = depView.getUint32(o, true)
228
+ out[ci].deposits.push({
229
+ x: cells[ci].x,
230
+ y: cells[ci].y,
231
+ depth: depView.getUint32(o + 4, true),
232
+ itemId: depView.getUint32(o + 8, true),
233
+ richness: depView.getUint32(o + 12, true),
234
+ reserve: depView.getFloat64(o + 32, true),
235
+ stats: [
236
+ depView.getUint32(o + 16, true),
237
+ depView.getUint32(o + 20, true),
238
+ depView.getUint32(o + 24, true),
239
+ ],
240
+ })
241
+ }
242
+ for (const p of [gp, ep, cp, locOut, depOut]) e.free(p)
243
+ return out
244
+ }