@shipload/sdk 1.0.0-next.4 → 1.0.0-next.41

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 (134) hide show
  1. package/lib/scan.d.ts +34 -0
  2. package/lib/scan.js +136 -0
  3. package/lib/scan.js.map +1 -0
  4. package/lib/scan.m.js +129 -0
  5. package/lib/scan.m.js.map +1 -0
  6. package/lib/shipload.d.ts +2473 -973
  7. package/lib/shipload.js +11529 -5211
  8. package/lib/shipload.js.map +1 -1
  9. package/lib/shipload.m.js +11338 -5162
  10. package/lib/shipload.m.js.map +1 -1
  11. package/lib/testing.d.ts +970 -0
  12. package/lib/testing.js +4013 -0
  13. package/lib/testing.js.map +1 -0
  14. package/lib/testing.m.js +4007 -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 +5 -6
  20. package/src/capabilities/gathering.test.ts +16 -0
  21. package/src/capabilities/gathering.ts +35 -18
  22. package/src/capabilities/index.ts +0 -1
  23. package/src/capabilities/modules.ts +9 -0
  24. package/src/capabilities/storage.ts +16 -1
  25. package/src/contracts/platform.ts +231 -3
  26. package/src/contracts/server.ts +1021 -481
  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 +12 -5
  37. package/src/data/capability-formulas.ts +14 -7
  38. package/src/data/catalog.ts +0 -5
  39. package/src/data/colors.ts +14 -47
  40. package/src/data/entities.json +76 -10
  41. package/src/data/item-ids.ts +18 -12
  42. package/src/data/items.json +321 -38
  43. package/src/data/kind-registry.json +109 -0
  44. package/src/data/kind-registry.ts +165 -0
  45. package/src/data/metadata.ts +119 -33
  46. package/src/data/recipes-runtime.ts +3 -23
  47. package/src/data/recipes.json +238 -117
  48. package/src/derivation/build-methods.ts +45 -0
  49. package/src/derivation/capabilities.test.ts +151 -0
  50. package/src/derivation/capabilities.ts +512 -0
  51. package/src/derivation/capability-mappings.ts +9 -12
  52. package/src/derivation/crafting.ts +23 -24
  53. package/src/derivation/index.ts +25 -2
  54. package/src/derivation/recipe-usage.test.ts +78 -0
  55. package/src/derivation/recipe-usage.ts +141 -0
  56. package/src/derivation/reserve-regen.ts +34 -0
  57. package/src/derivation/resources.ts +125 -38
  58. package/src/derivation/rollups.test.ts +55 -0
  59. package/src/derivation/rollups.ts +56 -0
  60. package/src/derivation/stars.test.ts +51 -0
  61. package/src/derivation/stars.ts +15 -0
  62. package/src/derivation/stats.ts +6 -6
  63. package/src/derivation/stratum.ts +17 -20
  64. package/src/derivation/tiers.ts +40 -7
  65. package/src/derivation/wormhole.ts +136 -0
  66. package/src/entities/entity.ts +98 -0
  67. package/src/entities/gamestate.ts +3 -28
  68. package/src/entities/makers.ts +124 -134
  69. package/src/entities/slot-multiplier.ts +43 -0
  70. package/src/errors.ts +12 -16
  71. package/src/format.ts +26 -4
  72. package/src/index-module.ts +267 -47
  73. package/src/managers/actions.ts +528 -95
  74. package/src/managers/base.ts +6 -2
  75. package/src/managers/construction-types.ts +80 -0
  76. package/src/managers/construction.ts +412 -0
  77. package/src/managers/context.ts +20 -1
  78. package/src/managers/coordinates.ts +14 -0
  79. package/src/managers/entities.ts +18 -66
  80. package/src/managers/epochs.ts +40 -0
  81. package/src/managers/index.ts +17 -1
  82. package/src/managers/locations.ts +25 -29
  83. package/src/managers/nft.test.ts +14 -0
  84. package/src/managers/nft.ts +70 -0
  85. package/src/managers/plot.ts +122 -0
  86. package/src/nft/atomicassets.abi.json +1342 -0
  87. package/src/nft/atomicassets.ts +237 -0
  88. package/src/nft/atomicdata.ts +130 -0
  89. package/src/nft/buildImmutableData.ts +338 -0
  90. package/src/nft/description.ts +98 -24
  91. package/src/nft/index.ts +3 -0
  92. package/src/planner/index.ts +127 -0
  93. package/src/planner/planner.test.ts +319 -0
  94. package/src/resolution/describe-module.ts +18 -13
  95. package/src/resolution/display-name.ts +38 -10
  96. package/src/resolution/resolve-item.test.ts +37 -0
  97. package/src/resolution/resolve-item.ts +55 -24
  98. package/src/scan/index.ts +180 -0
  99. package/src/scan/scan-wasm.base64.ts +2 -0
  100. package/src/scheduling/accessor.ts +68 -22
  101. package/src/scheduling/availability.ts +108 -0
  102. package/src/scheduling/cancel.test.ts +348 -0
  103. package/src/scheduling/cancel.ts +209 -0
  104. package/src/scheduling/energy.ts +47 -0
  105. package/src/scheduling/idle-resolve.ts +45 -0
  106. package/src/scheduling/lane-core.ts +128 -0
  107. package/src/scheduling/lanes.test.ts +249 -0
  108. package/src/scheduling/lanes.ts +198 -0
  109. package/src/scheduling/projection.ts +209 -105
  110. package/src/scheduling/schedule.ts +241 -104
  111. package/src/scheduling/task-cargo.ts +46 -0
  112. package/src/shipload.ts +21 -1
  113. package/src/subscriptions/manager.ts +229 -142
  114. package/src/subscriptions/mappers.ts +5 -8
  115. package/src/subscriptions/types.ts +11 -3
  116. package/src/testing/catalog-hash.ts +19 -0
  117. package/src/testing/index.ts +2 -0
  118. package/src/testing/projection-parity.ts +167 -0
  119. package/src/travel/reach.ts +23 -0
  120. package/src/travel/route-planner.ts +196 -0
  121. package/src/travel/travel.ts +200 -112
  122. package/src/types/capabilities.ts +29 -6
  123. package/src/types/entity.ts +3 -3
  124. package/src/types/index.ts +0 -1
  125. package/src/types.ts +28 -13
  126. package/src/utils/cargo.ts +27 -0
  127. package/src/utils/display-name.ts +70 -0
  128. package/src/utils/system.ts +36 -24
  129. package/src/capabilities/loading.ts +0 -8
  130. package/src/entities/container.ts +0 -108
  131. package/src/entities/ship-deploy.ts +0 -259
  132. package/src/entities/ship.ts +0 -204
  133. package/src/entities/warehouse.ts +0 -119
  134. 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,108 +0,0 @@
1
- import {UInt64, type UInt64Type} from '@wharfkit/antelope'
2
- import {ServerContract} from '../contracts'
3
- import type {CoordinatesType} from '../types'
4
- import {Location} from './location'
5
- import {ScheduleAccessor} from '../scheduling/accessor'
6
- import * as schedule from '../scheduling/schedule'
7
-
8
- export interface ContainerStateInput {
9
- id: UInt64Type
10
- owner: string
11
- name: string
12
- coordinates: CoordinatesType | {x: number; y: number; z?: number}
13
- hullmass: number
14
- capacity: number
15
- cargomass?: number
16
- cargo?: ServerContract.Types.cargo_item[]
17
- schedule?: ServerContract.Types.schedule
18
- }
19
-
20
- export class Container extends ServerContract.Types.entity_info {
21
- private _sched?: ScheduleAccessor
22
-
23
- get name(): string {
24
- return this.entity_name
25
- }
26
-
27
- get sched(): ScheduleAccessor {
28
- this._sched ??= new ScheduleAccessor(this)
29
- return this._sched
30
- }
31
-
32
- get isIdle(): boolean {
33
- return this.is_idle
34
- }
35
-
36
- isLoading(now: Date): boolean {
37
- return schedule.isLoading(this, now)
38
- }
39
-
40
- isUnloading(now: Date): boolean {
41
- return schedule.isUnloading(this, now)
42
- }
43
-
44
- get location(): Location {
45
- return Location.from(this.coordinates)
46
- }
47
-
48
- get totalMass(): UInt64 {
49
- return UInt64.from(this.hullmass ?? 0).adding(this.cargomass)
50
- }
51
-
52
- get maxCapacity(): UInt64 {
53
- return UInt64.from(this.capacity)
54
- }
55
-
56
- get availableCapacity(): UInt64 {
57
- const cargo = UInt64.from(this.cargomass)
58
- return cargo.gte(this.maxCapacity) ? UInt64.from(0) : this.maxCapacity.subtracting(cargo)
59
- }
60
-
61
- hasSpace(additionalMass: UInt64): boolean {
62
- return UInt64.from(this.cargomass).adding(additionalMass).lte(this.maxCapacity)
63
- }
64
-
65
- get isFull(): boolean {
66
- return UInt64.from(this.cargomass).gte(this.maxCapacity)
67
- }
68
-
69
- get orbitalAltitude(): number {
70
- return this.coordinates.z?.toNumber() || 0
71
- }
72
- }
73
-
74
- export function computeContainerCapabilities(stats: Record<string, number>): {
75
- hullmass: number
76
- capacity: number
77
- } {
78
- const density = stats.density
79
- const strength = stats.strength
80
- const hardness = stats.hardness
81
- const saturation = stats.saturation
82
-
83
- const hullmass = 25000 + 75 * density
84
-
85
- const statSum = strength + hardness + saturation
86
- const exponent = statSum / 2997
87
- const capacity = Math.floor(1000000 * 10 ** exponent)
88
-
89
- return {hullmass, capacity}
90
- }
91
-
92
- export function computeContainerT2Capabilities(stats: Record<string, number>): {
93
- hullmass: number
94
- capacity: number
95
- } {
96
- const strength = stats.strength
97
- const density = stats.density
98
- const hardness = stats.hardness
99
- const saturation = stats.saturation
100
-
101
- const hullmass = 20000 + 50 * density
102
-
103
- const statSum = strength + hardness + saturation
104
- const exponent = statSum / 2500
105
- const capacity = Math.floor(1500000 * 10 ** exponent)
106
-
107
- return {hullmass, capacity}
108
- }
@@ -1,259 +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
-
12
- export function computeShipHullCapabilities(stats: Record<string, number>): {
13
- hullmass: number
14
- capacity: number
15
- } {
16
- const density = stats.density
17
- const strength = stats.strength
18
- const hardness = stats.hardness
19
- const saturation = stats.saturation
20
-
21
- const hullmass = 25000 + 75 * density
22
- const statSum = strength + hardness + saturation
23
- const exponent = statSum / 2997.0
24
- const capacity = Math.floor(1000000 * 10 ** exponent)
25
-
26
- return {hullmass, capacity}
27
- }
28
-
29
- export function computeEngineCapabilities(stats: Record<string, number>): {
30
- thrust: number
31
- drain: number
32
- } {
33
- const vol = stats.volatility
34
- const thm = stats.thermal
35
-
36
- return {
37
- thrust: 400 + Math.floor((vol * 3) / 4),
38
- drain: Math.max(30, 50 - Math.floor(thm / 70)),
39
- }
40
- }
41
-
42
- export function computeGeneratorCapabilities(stats: Record<string, number>): {
43
- capacity: number
44
- recharge: number
45
- } {
46
- const com = stats.composition
47
- const fin = stats.fineness
48
-
49
- return {
50
- capacity: 300 + Math.floor(com / 6),
51
- recharge: 1 + Math.floor((fin * 3) / 1000),
52
- }
53
- }
54
-
55
- export function computeGathererCapabilities(stats: Record<string, number>): {
56
- yield: number
57
- drain: number
58
- depth: number
59
- speed: number
60
- } {
61
- const str = stats.strength
62
- const con = stats.conductivity
63
- const ref = stats.reflectivity
64
- const tol = stats.tolerance
65
-
66
- return {
67
- yield: 200 + str,
68
- drain: Math.max(250, 1250 - Math.floor((con * 25) / 20)),
69
- depth: 200 + Math.floor((tol * 3) / 2),
70
- speed: 100 + Math.floor((ref * 4) / 5),
71
- }
72
- }
73
-
74
- export function computeLoaderCapabilities(stats: Record<string, number>): {
75
- mass: number
76
- thrust: number
77
- quantity: number
78
- } {
79
- const insulation = stats.insulation
80
- const plasticity = stats.plasticity
81
-
82
- return {
83
- mass: Math.max(200, 2000 - Math.floor(insulation * 2)),
84
- thrust: 1 + Math.floor(plasticity / 500),
85
- quantity: 1,
86
- }
87
- }
88
-
89
- export function computeCrafterCapabilities(stats: Record<string, number>): {
90
- speed: number
91
- drain: number
92
- } {
93
- const rea = stats.reactivity
94
- const fin = stats.fineness
95
-
96
- return {
97
- speed: 100 + Math.floor((rea * 4) / 5),
98
- drain: Math.max(5, 30 - Math.floor(fin / 33)),
99
- }
100
- }
101
-
102
- export function computeHaulerCapabilities(stats: Record<string, number>): {
103
- capacity: number
104
- efficiency: number
105
- drain: number
106
- } {
107
- const fineness = stats.fineness
108
- const conductivity = stats.conductivity
109
- const composition = stats.composition
110
-
111
- return {
112
- capacity: Math.max(1, 1 + Math.floor(fineness / 400)),
113
- efficiency: 2000 + conductivity * 6,
114
- drain: Math.max(3, 15 - Math.floor(composition / 80)),
115
- }
116
- }
117
-
118
- export function computeStorageCapabilities(
119
- stats: Record<string, number>,
120
- baseCapacity: number
121
- ): {
122
- capacityBonus: number
123
- } {
124
- const strength = stats.strength
125
- const density = stats.density
126
- const hardness = stats.hardness
127
- const saturation = stats.saturation
128
-
129
- const statSum = strength + density + hardness + saturation
130
- const capacityBonus = Math.floor(
131
- (baseCapacity * (10 + Math.floor((statSum * 10) / 2997))) / 100
132
- )
133
-
134
- return {capacityBonus}
135
- }
136
-
137
- export function computeWarehouseHullCapabilities(stats: Record<string, number>): {
138
- hullmass: number
139
- capacity: number
140
- } {
141
- const density = stats.density
142
- const strength = stats.strength
143
- const hardness = stats.hardness
144
- const saturation = stats.saturation
145
-
146
- const hullmass = 25000 + 75 * density
147
- const statSum = strength + hardness + saturation
148
- const exponent = statSum / 2997.0
149
- const capacity = Math.floor(20000000 * 10 ** exponent)
150
-
151
- return {hullmass, capacity}
152
- }
153
-
154
- export interface ShipCapabilities {
155
- engines?: {thrust: number; drain: number}
156
- generator?: {capacity: number; recharge: number}
157
- gatherer?: {yield: number; drain: number; depth: number; speed: number}
158
- hauler?: {capacity: number; efficiency: number; drain: number}
159
- loaders?: {mass: number; thrust: number; quantity: number}
160
- crafter?: {speed: number; drain: number}
161
- }
162
-
163
- export function computeShipCapabilities(
164
- modules: {itemId: number; stats: bigint}[]
165
- ): ShipCapabilities {
166
- const ship: ShipCapabilities = {}
167
-
168
- const engineModules = modules.filter((m) => getModuleCapabilityType(m.itemId) === MODULE_ENGINE)
169
- if (engineModules.length > 0) {
170
- let totalThrust = 0
171
- let totalDrain = 0
172
- for (const m of engineModules) {
173
- const caps = computeEngineCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
174
- totalThrust += caps.thrust
175
- totalDrain += caps.drain
176
- }
177
- ship.engines = {thrust: totalThrust, drain: totalDrain}
178
- }
179
-
180
- const generatorModules = modules.filter(
181
- (m) => getModuleCapabilityType(m.itemId) === MODULE_GENERATOR
182
- )
183
- if (generatorModules.length > 0) {
184
- let totalCapacity = 0
185
- let totalRecharge = 0
186
- for (const m of generatorModules) {
187
- const caps = computeGeneratorCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
188
- totalCapacity += caps.capacity
189
- totalRecharge += caps.recharge
190
- }
191
- ship.generator = {capacity: totalCapacity, recharge: totalRecharge}
192
- }
193
-
194
- const gathererModules = modules.filter(
195
- (m) => getModuleCapabilityType(m.itemId) === MODULE_GATHERER
196
- )
197
- if (gathererModules.length > 0) {
198
- let totalYield = 0
199
- let totalDrain = 0
200
- let totalDepth = 0
201
- let totalSpeed = 0
202
- for (const m of gathererModules) {
203
- const caps = computeGathererCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
204
- totalYield += caps.yield
205
- totalDrain += caps.drain
206
- totalDepth += caps.depth
207
- totalSpeed += caps.speed
208
- }
209
- ship.gatherer = {yield: totalYield, drain: totalDrain, depth: totalDepth, speed: totalSpeed}
210
- }
211
-
212
- const haulerModules = modules.filter((m) => getModuleCapabilityType(m.itemId) === MODULE_HAULER)
213
- if (haulerModules.length > 0) {
214
- let totalCapacity = 0
215
- let weightedEffNum = 0
216
- let totalDrain = 0
217
- for (const m of haulerModules) {
218
- const caps = computeHaulerCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
219
- totalCapacity += caps.capacity
220
- weightedEffNum += caps.efficiency * caps.capacity
221
- totalDrain += caps.drain
222
- }
223
- ship.hauler = {
224
- capacity: totalCapacity,
225
- efficiency: totalCapacity > 0 ? Math.floor(weightedEffNum / totalCapacity) : 0,
226
- drain: totalDrain,
227
- }
228
- }
229
-
230
- const loaderModules = modules.filter((m) => getModuleCapabilityType(m.itemId) === MODULE_LOADER)
231
- if (loaderModules.length > 0) {
232
- let totalMass = 0
233
- let totalThrust = 0
234
- let totalQuantity = 0
235
- for (const m of loaderModules) {
236
- const caps = computeLoaderCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
237
- totalMass += caps.mass
238
- totalThrust += caps.thrust
239
- totalQuantity += caps.quantity
240
- }
241
- ship.loaders = {mass: totalMass, thrust: totalThrust, quantity: totalQuantity}
242
- }
243
-
244
- const crafterModules = modules.filter(
245
- (m) => getModuleCapabilityType(m.itemId) === MODULE_CRAFTER
246
- )
247
- if (crafterModules.length > 0) {
248
- let totalSpeed = 0
249
- let totalDrain = 0
250
- for (const m of crafterModules) {
251
- const caps = computeCrafterCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
252
- totalSpeed += caps.speed
253
- totalDrain += caps.drain
254
- }
255
- ship.crafter = {speed: totalSpeed, drain: totalDrain}
256
- }
257
-
258
- return ship
259
- }