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

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 (144) 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 +2881 -1149
  7. package/lib/shipload.js +13031 -5511
  8. package/lib/shipload.js.map +1 -1
  9. package/lib/shipload.m.js +12807 -5460
  10. package/lib/shipload.m.js.map +1 -1
  11. package/lib/testing.d.ts +1028 -0
  12. package/lib/testing.js +4336 -0
  13. package/lib/testing.js.map +1 -0
  14. package/lib/testing.m.js +4330 -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 +1160 -520
  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 +290 -34
  75. package/src/managers/actions.ts +602 -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/travel.ts +148 -117
  131. package/src/types/capabilities.ts +29 -6
  132. package/src/types/entity.ts +3 -3
  133. package/src/types/index.ts +0 -1
  134. package/src/types.ts +36 -14
  135. package/src/utils/cargo.test.ts +14 -0
  136. package/src/utils/cargo.ts +29 -0
  137. package/src/utils/display-name.ts +70 -0
  138. package/src/utils/system.ts +36 -24
  139. package/src/capabilities/loading.ts +0 -8
  140. package/src/entities/container.ts +0 -123
  141. package/src/entities/ship-deploy.ts +0 -295
  142. package/src/entities/ship.ts +0 -221
  143. package/src/entities/warehouse.ts +0 -127
  144. package/src/types/entity-traits.ts +0 -69
@@ -0,0 +1,70 @@
1
+ export interface DisplayNameResult {
2
+ valid: boolean
3
+ reason?: string
4
+ name: string
5
+ }
6
+
7
+ const MAX_DISPLAY_NAME_BYTES = 32
8
+ const ASCII_SPACE = 0x20
9
+ const ZERO_WIDTH_CODE_POINTS = new Set([0x200b, 0x200c, 0x200d, 0x2060, 0xfeff])
10
+ const textEncoder = new TextEncoder()
11
+
12
+ function isControlCharacter(codePoint: number): boolean {
13
+ return codePoint <= 0x1f || codePoint === 0x7f || (codePoint >= 0x80 && codePoint <= 0x9f)
14
+ }
15
+
16
+ function isLoneSurrogate(codePoint: number): boolean {
17
+ return codePoint >= 0xd800 && codePoint <= 0xdfff
18
+ }
19
+
20
+ function isBidiControl(codePoint: number): boolean {
21
+ return (
22
+ (codePoint >= 0x202a && codePoint <= 0x202e) || (codePoint >= 0x2066 && codePoint <= 0x2069)
23
+ )
24
+ }
25
+
26
+ function isInvalidDisplayNameCodePoint(codePoint: number): boolean {
27
+ return (
28
+ isControlCharacter(codePoint) ||
29
+ isLoneSurrogate(codePoint) ||
30
+ ZERO_WIDTH_CODE_POINTS.has(codePoint) ||
31
+ isBidiControl(codePoint)
32
+ )
33
+ }
34
+
35
+ export function normalizeDisplayName(input: string): string {
36
+ let start = 0
37
+ let end = input.length
38
+
39
+ while (start < end && input.charCodeAt(start) === ASCII_SPACE) start++
40
+ while (end > start && input.charCodeAt(end - 1) === ASCII_SPACE) end--
41
+
42
+ return input.slice(start, end)
43
+ }
44
+
45
+ export interface ValidateDisplayNameOptions {
46
+ allowEmpty?: boolean
47
+ }
48
+
49
+ export function validateDisplayName(
50
+ input: string,
51
+ opts: ValidateDisplayNameOptions = {}
52
+ ): DisplayNameResult {
53
+ const name = normalizeDisplayName(input)
54
+
55
+ if (name.length === 0) {
56
+ return opts.allowEmpty ? {valid: true, name} : {valid: false, reason: 'empty', name}
57
+ }
58
+ if (textEncoder.encode(name).length > MAX_DISPLAY_NAME_BYTES) {
59
+ return {valid: false, reason: 'too_long', name}
60
+ }
61
+
62
+ for (const character of name) {
63
+ const codePoint = character.codePointAt(0)!
64
+ if (isInvalidDisplayNameCodePoint(codePoint)) {
65
+ return {valid: false, reason: 'invalid_char', name}
66
+ }
67
+ }
68
+
69
+ return {valid: true, name}
70
+ }
@@ -3,12 +3,12 @@ import {hash512} from './hash'
3
3
  import {Coordinates, type CoordinatesType, LocationType} from '../types'
4
4
  import {ServerContract} from '../contracts'
5
5
  import {deriveLocationSize} from '../derivation/location-size'
6
+ import {wormholeAt} from '../derivation/wormhole'
6
7
  import syllables from '../data/syllables.json'
7
8
  import nebulaAdjectives from '../data/nebula-adjectives.json'
8
9
  import nebulaNouns from '../data/nebula-nouns.json'
9
10
 
10
11
  const LOCATION_EXISTS_THRESHOLD = 0x10
11
- const LOCATION_ACTIVE_THRESHOLD = 0x80
12
12
 
13
13
  export function getLocationType(
14
14
  gameSeed: Checksum256Type,
@@ -22,12 +22,10 @@ export function getLocationType(
22
22
  return LocationType.EMPTY
23
23
  }
24
24
 
25
- if (hashResult.array[1] < 96) {
26
- return LocationType.PLANET
27
- } else if (hashResult.array[1] < 176) {
28
- return LocationType.ASTEROID
29
- }
30
- return LocationType.NEBULA
25
+ if (hashResult.array[1] < 96) return LocationType.PLANET
26
+ if (hashResult.array[1] < 149) return LocationType.ASTEROID
27
+ if (hashResult.array[1] < 202) return LocationType.NEBULA
28
+ return LocationType.ICE_FIELD
31
29
  }
32
30
 
33
31
  export function isGatherableLocation(locationType: LocationType): boolean {
@@ -44,6 +42,8 @@ export function getLocationTypeName(type: LocationType): string {
44
42
  return 'Asteroid'
45
43
  case LocationType.NEBULA:
46
44
  return 'Nebula'
45
+ case LocationType.ICE_FIELD:
46
+ return 'Ice Field'
47
47
  }
48
48
  }
49
49
 
@@ -76,6 +76,15 @@ function generateNebulaName(hashResult: Checksum512): string {
76
76
  return `${nebulaAdjectives[adjIdx]} ${nebulaNouns[nounIdx]}`
77
77
  }
78
78
 
79
+ function generateIceFieldName(hashResult: Checksum512): string {
80
+ const A = 65
81
+ const letter1 = String.fromCharCode(A + (hashResult.array[0] % 26))
82
+ const letter2 = String.fromCharCode(A + (hashResult.array[1] % 26))
83
+ const subId = (hashResult.array[2] % 9) + 1
84
+ const num = (uint16(hashResult, 3) % 9000) + 1000
85
+ return `${letter1}${letter2}-${subId}/${num}`
86
+ }
87
+
79
88
  export function getSystemName(gameSeed: Checksum256Type, location: CoordinatesType): string {
80
89
  const seed = Checksum256.from(gameSeed)
81
90
  const locationType = getLocationType(seed, location)
@@ -91,6 +100,8 @@ export function getSystemName(gameSeed: Checksum256Type, location: CoordinatesTy
91
100
  return generateAsteroidName(hashResult)
92
101
  case LocationType.NEBULA:
93
102
  return generateNebulaName(hashResult)
103
+ case LocationType.ICE_FIELD:
104
+ return generateIceFieldName(hashResult)
94
105
  default:
95
106
  return generatePlanetName(hashResult)
96
107
  }
@@ -100,6 +111,16 @@ export function hasSystem(gameSeed: Checksum256Type, coordinates: CoordinatesTyp
100
111
  return getLocationType(gameSeed, coordinates) !== LocationType.EMPTY
101
112
  }
102
113
 
114
+ export function getLocationKind(
115
+ gameSeed: Checksum256Type,
116
+ x: number,
117
+ y: number
118
+ ): 'wormhole' | 'system' | 'empty' {
119
+ if (wormholeAt(gameSeed, x, y)) return 'wormhole'
120
+ if (hasSystem(gameSeed, {x, y})) return 'system'
121
+ return 'empty'
122
+ }
123
+
103
124
  export function deriveLocationStatic(
104
125
  gameSeed: Checksum256Type,
105
126
  coordinates: CoordinatesType
@@ -123,10 +144,12 @@ export function deriveLocationStatic(
123
144
 
124
145
  if (hashResult.array[1] < 96) {
125
146
  loc.type = UInt8.from(LocationType.PLANET)
126
- } else if (hashResult.array[1] < 176) {
147
+ } else if (hashResult.array[1] < 149) {
127
148
  loc.type = UInt8.from(LocationType.ASTEROID)
128
- } else {
149
+ } else if (hashResult.array[1] < 202) {
129
150
  loc.type = UInt8.from(LocationType.NEBULA)
151
+ } else {
152
+ loc.type = UInt8.from(LocationType.ICE_FIELD)
130
153
  }
131
154
 
132
155
  loc.subtype = UInt8.from(
@@ -138,31 +161,20 @@ export function deriveLocationStatic(
138
161
  return loc
139
162
  }
140
163
 
141
- export function deriveLocationEpoch(
142
- epochSeed: Checksum256Type,
164
+ export function isLocationBuildable(
165
+ gameSeed: Checksum256Type,
143
166
  coordinates: CoordinatesType
144
- ): ServerContract.Types.location_epoch {
145
- const seed = Checksum256.from(epochSeed)
146
- const coords = Coordinates.from(coordinates)
147
- const str = `system-epoch-${coords.x}-${coords.y}`
148
- const hashResult = hash512(seed, str)
149
-
150
- return ServerContract.Types.location_epoch.from({
151
- active: hashResult.array[0] < LOCATION_ACTIVE_THRESHOLD,
152
- seed0: hashResult.array[1],
153
- seed1: hashResult.array[2],
154
- })
167
+ ): boolean {
168
+ return getLocationType(gameSeed, coordinates) === LocationType.PLANET
155
169
  }
156
170
 
157
171
  export function deriveLocation(
158
172
  gameSeed: Checksum256Type,
159
- epochSeed: Checksum256Type,
160
173
  coordinates: CoordinatesType
161
174
  ): ServerContract.Types.location_derived {
162
175
  const staticProps = deriveLocationStatic(gameSeed, coordinates)
163
176
  return ServerContract.Types.location_derived.from({
164
177
  static_props: staticProps,
165
- epoch_props: deriveLocationEpoch(epochSeed, coordinates),
166
178
  size: deriveLocationSize(staticProps),
167
179
  })
168
180
  }
@@ -1,8 +0,0 @@
1
- import {UInt32, type UInt64} from '@wharfkit/antelope'
2
- import type {LoaderCapability} from '../types/capabilities'
3
-
4
- export function calcLoadDuration(entity: LoaderCapability, cargoMass: UInt64): UInt32 {
5
- const totalThrust = entity.loaders.thrust.toNumber() * entity.loaders.quantity.toNumber()
6
- if (totalThrust === 0) return UInt32.from(0)
7
- return UInt32.from(Math.ceil(Number(cargoMass) / totalThrust))
8
- }
@@ -1,123 +0,0 @@
1
- import {UInt64, type UInt64Type} from '@wharfkit/antelope'
2
- import {ServerContract} from '../contracts'
3
- import type {CoordinatesType} from '../types'
4
- import {type FloatPosition, getInterpolatedPosition} from '../travel/travel'
5
- import {Location} from './location'
6
- import {ScheduleAccessor} from '../scheduling/accessor'
7
- import * as schedule from '../scheduling/schedule'
8
-
9
- export interface ContainerStateInput {
10
- id: UInt64Type
11
- owner: string
12
- name: string
13
- coordinates: CoordinatesType | {x: number; y: number; z?: number}
14
- hullmass: number
15
- capacity: number
16
- cargomass?: number
17
- cargo?: ServerContract.Types.cargo_item[]
18
- schedule?: ServerContract.Types.schedule
19
- }
20
-
21
- export class Container extends ServerContract.Types.entity_info {
22
- private _sched?: ScheduleAccessor
23
-
24
- get name(): string {
25
- return this.entity_name
26
- }
27
-
28
- get entityClass(): 'mobile' {
29
- return 'mobile'
30
- }
31
-
32
- get canUndeploy(): boolean {
33
- return true
34
- }
35
-
36
- get sched(): ScheduleAccessor {
37
- this._sched ??= new ScheduleAccessor(this)
38
- return this._sched
39
- }
40
-
41
- get isIdle(): boolean {
42
- return this.is_idle
43
- }
44
-
45
- interpolatedPositionAt(now: Date): FloatPosition {
46
- const taskIndex = this.sched.currentTaskIndex(now)
47
- const progress = this.sched.currentTaskProgressFloat(now)
48
- return getInterpolatedPosition(this, taskIndex, progress)
49
- }
50
-
51
- isLoading(now: Date): boolean {
52
- return schedule.isLoading(this, now)
53
- }
54
-
55
- isUnloading(now: Date): boolean {
56
- return schedule.isUnloading(this, now)
57
- }
58
-
59
- get location(): Location {
60
- return Location.from(this.coordinates)
61
- }
62
-
63
- get totalMass(): UInt64 {
64
- return UInt64.from(this.hullmass ?? 0).adding(this.cargomass)
65
- }
66
-
67
- get maxCapacity(): UInt64 {
68
- return UInt64.from(this.capacity)
69
- }
70
-
71
- get availableCapacity(): UInt64 {
72
- const cargo = UInt64.from(this.cargomass)
73
- return cargo.gte(this.maxCapacity) ? UInt64.from(0) : this.maxCapacity.subtracting(cargo)
74
- }
75
-
76
- hasSpace(additionalMass: UInt64): boolean {
77
- return UInt64.from(this.cargomass).adding(additionalMass).lte(this.maxCapacity)
78
- }
79
-
80
- get isFull(): boolean {
81
- return UInt64.from(this.cargomass).gte(this.maxCapacity)
82
- }
83
-
84
- get orbitalAltitude(): number {
85
- return this.coordinates.z?.toNumber() || 0
86
- }
87
- }
88
-
89
- export function computeContainerCapabilities(stats: Record<string, number>): {
90
- hullmass: number
91
- capacity: number
92
- } {
93
- const density = stats.density
94
- const strength = stats.strength
95
- const hardness = stats.hardness
96
- const saturation = stats.saturation
97
-
98
- const hullmass = 25000 + 75 * density
99
-
100
- const statSum = strength + hardness + saturation
101
- const exponent = statSum / 2997
102
- const capacity = Math.floor(1000000 * 10 ** exponent)
103
-
104
- return {hullmass, capacity}
105
- }
106
-
107
- export function computeContainerT2Capabilities(stats: Record<string, number>): {
108
- hullmass: number
109
- capacity: number
110
- } {
111
- const strength = stats.strength
112
- const density = stats.density
113
- const hardness = stats.hardness
114
- const saturation = stats.saturation
115
-
116
- const hullmass = 20000 + 50 * density
117
-
118
- const statSum = strength + hardness + saturation
119
- const exponent = statSum / 2500
120
- const capacity = Math.floor(1500000 * 10 ** exponent)
121
-
122
- return {hullmass, capacity}
123
- }
@@ -1,295 +0,0 @@
1
- import {decodeCraftedItemStats} from '../derivation/crafting'
2
- import {
3
- getModuleCapabilityType,
4
- MODULE_CRAFTER,
5
- MODULE_ENGINE,
6
- MODULE_GATHERER,
7
- MODULE_GENERATOR,
8
- MODULE_HAULER,
9
- MODULE_LOADER,
10
- } from '../capabilities/modules'
11
- import {getItem} from '../data/catalog'
12
-
13
- export function computeShipHullCapabilities(stats: Record<string, number>): {
14
- hullmass: number
15
- capacity: number
16
- } {
17
- const density = stats.density
18
- const strength = stats.strength
19
- const hardness = stats.hardness
20
- const saturation = stats.saturation
21
-
22
- const hullmass = 25000 + 75 * density
23
- const statSum = strength + hardness + saturation
24
- const exponent = statSum / 2997.0
25
- const capacity = Math.floor(1000000 * 10 ** exponent)
26
-
27
- return {hullmass, capacity}
28
- }
29
-
30
- export function computeEngineCapabilities(stats: Record<string, number>): {
31
- thrust: number
32
- drain: number
33
- } {
34
- const vol = stats.volatility
35
- const thm = stats.thermal
36
-
37
- return {
38
- thrust: 400 + Math.floor((vol * 3) / 4),
39
- drain: Math.max(30, 50 - Math.floor(thm / 70)),
40
- }
41
- }
42
-
43
- export function computeGeneratorCapabilities(stats: Record<string, number>): {
44
- capacity: number
45
- recharge: number
46
- } {
47
- const com = stats.composition
48
- const fin = stats.fineness
49
-
50
- return {
51
- capacity: 300 + Math.floor(com / 6),
52
- recharge: 1 + Math.floor((fin * 3) / 1000),
53
- }
54
- }
55
-
56
- export interface GathererDepthParams {
57
- readonly floor: number
58
- readonly slope: number
59
- }
60
-
61
- export const GATHERER_DEPTH_TABLE: readonly GathererDepthParams[] = [
62
- {floor: 500, slope: 5},
63
- {floor: 2000, slope: 11},
64
- {floor: 7000, slope: 16},
65
- {floor: 15000, slope: 18},
66
- {floor: 25000, slope: 19},
67
- {floor: 35000, slope: 16},
68
- {floor: 46000, slope: 12},
69
- {floor: 53500, slope: 10},
70
- {floor: 60000, slope: 5},
71
- {floor: 63500, slope: 2},
72
- ]
73
-
74
- export const GATHERER_DEPTH_MAX_TIER = 10
75
-
76
- export function gathererDepthForTier(tol: number, tier: number): number {
77
- if (tier < 1 || tier > GATHERER_DEPTH_MAX_TIER) {
78
- throw new Error(`gatherer tier out of range: ${tier}`)
79
- }
80
- const p = GATHERER_DEPTH_TABLE[tier - 1]
81
- return p.floor + tol * p.slope
82
- }
83
-
84
- export function computeGathererCapabilities(
85
- stats: Record<string, number>,
86
- tier: number
87
- ): {
88
- yield: number
89
- drain: number
90
- depth: number
91
- speed: number
92
- } {
93
- const str = stats.strength
94
- const con = stats.conductivity
95
- const ref = stats.reflectivity
96
- const tol = stats.tolerance
97
-
98
- return {
99
- yield: 200 + str,
100
- drain: Math.max(250, 1250 - Math.floor((con * 25) / 20)),
101
- depth: gathererDepthForTier(tol, tier),
102
- speed: 100 + Math.floor((ref * 4) / 5),
103
- }
104
- }
105
-
106
- export function computeLoaderCapabilities(stats: Record<string, number>): {
107
- mass: number
108
- thrust: number
109
- quantity: number
110
- } {
111
- const insulation = stats.insulation
112
- const plasticity = stats.plasticity
113
-
114
- return {
115
- mass: Math.max(200, 2000 - Math.floor(insulation * 2)),
116
- thrust: 1 + Math.floor(plasticity / 500),
117
- quantity: 1,
118
- }
119
- }
120
-
121
- export function computeCrafterCapabilities(stats: Record<string, number>): {
122
- speed: number
123
- drain: number
124
- } {
125
- const rea = stats.reactivity
126
- const fin = stats.fineness
127
-
128
- return {
129
- speed: 100 + Math.floor((rea * 4) / 5),
130
- drain: Math.max(5, 30 - Math.floor(fin / 33)),
131
- }
132
- }
133
-
134
- export function computeHaulerCapabilities(stats: Record<string, number>): {
135
- capacity: number
136
- efficiency: number
137
- drain: number
138
- } {
139
- const fineness = stats.fineness
140
- const conductivity = stats.conductivity
141
- const composition = stats.composition
142
-
143
- return {
144
- capacity: Math.max(1, 1 + Math.floor(fineness / 400)),
145
- efficiency: 2000 + conductivity * 6,
146
- drain: Math.max(3, 15 - Math.floor(composition / 80)),
147
- }
148
- }
149
-
150
- export function computeStorageCapabilities(
151
- stats: Record<string, number>,
152
- baseCapacity: number
153
- ): {
154
- capacityBonus: number
155
- } {
156
- const strength = stats.strength
157
- const density = stats.density
158
- const hardness = stats.hardness
159
- const saturation = stats.saturation
160
-
161
- const statSum = strength + density + hardness + saturation
162
- const capacityBonus = Math.floor(
163
- (baseCapacity * (10 + Math.floor((statSum * 10) / 2997))) / 100
164
- )
165
-
166
- return {capacityBonus}
167
- }
168
-
169
- export function computeWarehouseHullCapabilities(stats: Record<string, number>): {
170
- hullmass: number
171
- capacity: number
172
- } {
173
- const density = stats.density
174
- const strength = stats.strength
175
- const hardness = stats.hardness
176
- const saturation = stats.saturation
177
-
178
- const hullmass = 25000 + 75 * density
179
- const statSum = strength + hardness + saturation
180
- const exponent = statSum / 2997.0
181
- const capacity = Math.floor(20000000 * 10 ** exponent)
182
-
183
- return {hullmass, capacity}
184
- }
185
-
186
- export interface ShipCapabilities {
187
- engines?: {thrust: number; drain: number}
188
- generator?: {capacity: number; recharge: number}
189
- gatherer?: {yield: number; drain: number; depth: number; speed: number}
190
- hauler?: {capacity: number; efficiency: number; drain: number}
191
- loaders?: {mass: number; thrust: number; quantity: number}
192
- crafter?: {speed: number; drain: number}
193
- }
194
-
195
- export function computeShipCapabilities(
196
- modules: {itemId: number; stats: bigint}[]
197
- ): ShipCapabilities {
198
- const ship: ShipCapabilities = {}
199
-
200
- const engineModules = modules.filter((m) => getModuleCapabilityType(m.itemId) === MODULE_ENGINE)
201
- if (engineModules.length > 0) {
202
- let totalThrust = 0
203
- let totalDrain = 0
204
- for (const m of engineModules) {
205
- const caps = computeEngineCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
206
- totalThrust += caps.thrust
207
- totalDrain += caps.drain
208
- }
209
- ship.engines = {thrust: totalThrust, drain: totalDrain}
210
- }
211
-
212
- const generatorModules = modules.filter(
213
- (m) => getModuleCapabilityType(m.itemId) === MODULE_GENERATOR
214
- )
215
- if (generatorModules.length > 0) {
216
- let totalCapacity = 0
217
- let totalRecharge = 0
218
- for (const m of generatorModules) {
219
- const caps = computeGeneratorCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
220
- totalCapacity += caps.capacity
221
- totalRecharge += caps.recharge
222
- }
223
- ship.generator = {capacity: totalCapacity, recharge: totalRecharge}
224
- }
225
-
226
- const gathererModules = modules.filter(
227
- (m) => getModuleCapabilityType(m.itemId) === MODULE_GATHERER
228
- )
229
- if (gathererModules.length > 0) {
230
- let totalYield = 0
231
- let totalDrain = 0
232
- let maxDepth = 0
233
- let totalSpeed = 0
234
- for (const m of gathererModules) {
235
- const tier = getItem(m.itemId).tier
236
- const caps = computeGathererCapabilities(
237
- decodeCraftedItemStats(m.itemId, m.stats),
238
- tier
239
- )
240
- totalYield += caps.yield
241
- totalDrain += caps.drain
242
- if (caps.depth > maxDepth) maxDepth = caps.depth
243
- totalSpeed += caps.speed
244
- }
245
- ship.gatherer = {yield: totalYield, drain: totalDrain, depth: maxDepth, speed: totalSpeed}
246
- }
247
-
248
- const haulerModules = modules.filter((m) => getModuleCapabilityType(m.itemId) === MODULE_HAULER)
249
- if (haulerModules.length > 0) {
250
- let totalCapacity = 0
251
- let weightedEffNum = 0
252
- let totalDrain = 0
253
- for (const m of haulerModules) {
254
- const caps = computeHaulerCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
255
- totalCapacity += caps.capacity
256
- weightedEffNum += caps.efficiency * caps.capacity
257
- totalDrain += caps.drain
258
- }
259
- ship.hauler = {
260
- capacity: totalCapacity,
261
- efficiency: totalCapacity > 0 ? Math.floor(weightedEffNum / totalCapacity) : 0,
262
- drain: totalDrain,
263
- }
264
- }
265
-
266
- const loaderModules = modules.filter((m) => getModuleCapabilityType(m.itemId) === MODULE_LOADER)
267
- if (loaderModules.length > 0) {
268
- let totalMass = 0
269
- let totalThrust = 0
270
- let totalQuantity = 0
271
- for (const m of loaderModules) {
272
- const caps = computeLoaderCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
273
- totalMass += caps.mass
274
- totalThrust += caps.thrust
275
- totalQuantity += caps.quantity
276
- }
277
- ship.loaders = {mass: totalMass, thrust: totalThrust, quantity: totalQuantity}
278
- }
279
-
280
- const crafterModules = modules.filter(
281
- (m) => getModuleCapabilityType(m.itemId) === MODULE_CRAFTER
282
- )
283
- if (crafterModules.length > 0) {
284
- let totalSpeed = 0
285
- let totalDrain = 0
286
- for (const m of crafterModules) {
287
- const caps = computeCrafterCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
288
- totalSpeed += caps.speed
289
- totalDrain += caps.drain
290
- }
291
- ship.crafter = {speed: totalSpeed, drain: totalDrain}
292
- }
293
-
294
- return ship
295
- }