@shipload/sdk 1.0.0-next.6 → 1.0.0-next.60
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.d.ts +52 -0
- package/lib/scan.js +162 -0
- package/lib/scan.js.map +1 -0
- package/lib/scan.m.js +152 -0
- package/lib/scan.m.js.map +1 -0
- package/lib/shipload.d.ts +3589 -1363
- package/lib/shipload.js +15700 -5454
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +15423 -5402
- package/lib/shipload.m.js.map +1 -1
- package/lib/testing.d.ts +1173 -0
- package/lib/testing.js +4947 -0
- package/lib/testing.js.map +1 -0
- package/lib/testing.m.js +4941 -0
- package/lib/testing.m.js.map +1 -0
- package/package.json +20 -2
- package/src/capabilities/craftable.test.ts +82 -0
- package/src/capabilities/craftable.ts +82 -0
- package/src/capabilities/crafting.test.ts +35 -0
- package/src/capabilities/crafting.ts +34 -7
- package/src/capabilities/gathering.test.ts +29 -0
- package/src/capabilities/gathering.ts +37 -19
- package/src/capabilities/hauling.ts +0 -5
- package/src/capabilities/index.ts +0 -1
- package/src/capabilities/modules.ts +42 -0
- package/src/capabilities/movement.ts +1 -1
- package/src/capabilities/storage.ts +16 -1
- package/src/contracts/platform.ts +231 -3
- package/src/contracts/server.ts +1518 -501
- package/src/coordinates/address.ts +88 -0
- package/src/coordinates/constants.test.ts +15 -0
- package/src/coordinates/constants.ts +23 -0
- package/src/coordinates/index.ts +15 -0
- package/src/coordinates/memo.test.ts +47 -0
- package/src/coordinates/memo.ts +20 -0
- package/src/coordinates/permutation.ts +77 -0
- package/src/coordinates/regions.ts +48 -0
- package/src/coordinates/sectors.ts +115 -0
- package/src/data/capabilities.ts +49 -18
- package/src/data/capability-formulas.ts +26 -14
- package/src/data/catalog.ts +0 -5
- package/src/data/colors.ts +13 -47
- package/src/data/entities.json +379 -14
- package/src/data/item-ids.ts +48 -12
- package/src/data/items.json +301 -74
- package/src/data/kind-registry.json +195 -0
- package/src/data/kind-registry.ts +182 -0
- package/src/data/metadata.ts +277 -48
- package/src/data/recipes-runtime.ts +6 -23
- package/src/data/recipes.json +2074 -173
- package/src/derivation/build-methods.test.ts +13 -0
- package/src/derivation/build-methods.ts +48 -0
- package/src/derivation/capabilities.test.ts +348 -0
- package/src/derivation/capabilities.ts +692 -0
- package/src/derivation/capability-mappings.ts +60 -16
- package/src/derivation/crafting.test.ts +17 -0
- package/src/derivation/crafting.ts +56 -33
- package/src/derivation/index.ts +27 -2
- package/src/derivation/recipe-usage.test.ts +88 -0
- package/src/derivation/recipe-usage.ts +141 -0
- package/src/derivation/reserve-regen.ts +34 -0
- package/src/derivation/resources.ts +9 -1
- package/src/derivation/rollups.test.ts +55 -0
- package/src/derivation/rollups.ts +72 -0
- package/src/derivation/stars.test.ts +67 -0
- package/src/derivation/stars.ts +25 -0
- package/src/derivation/stat-scaling.ts +12 -0
- package/src/derivation/stats.ts +6 -6
- package/src/derivation/stratum.ts +26 -22
- package/src/derivation/tiers.ts +40 -7
- package/src/derivation/upgrades.ts +14 -0
- package/src/derivation/wormhole.ts +154 -0
- package/src/entities/entity.ts +102 -0
- package/src/entities/gamestate.ts +3 -28
- package/src/entities/makers.ts +141 -135
- package/src/entities/slot-multiplier.ts +43 -0
- package/src/errors.ts +12 -16
- package/src/format.ts +26 -4
- package/src/index-module.ts +333 -35
- package/src/managers/actions.ts +668 -103
- package/src/managers/base.ts +6 -2
- package/src/managers/cluster.test.ts +39 -0
- package/src/managers/cluster.ts +53 -0
- package/src/managers/construction-types.ts +80 -0
- package/src/managers/construction.ts +443 -0
- package/src/managers/context.ts +29 -1
- package/src/managers/coordinates.ts +14 -0
- package/src/managers/entities.ts +27 -66
- package/src/managers/epochs.ts +40 -0
- package/src/managers/index.ts +20 -1
- package/src/managers/locations.ts +25 -29
- package/src/managers/nft.test.ts +14 -0
- package/src/managers/nft.ts +70 -0
- package/src/managers/players.ts +25 -0
- package/src/managers/plot.ts +122 -0
- package/src/nft/atomicassets.abi.json +1342 -0
- package/src/nft/atomicassets.ts +237 -0
- package/src/nft/atomicdata.ts +130 -0
- package/src/nft/buildImmutableData.ts +385 -0
- package/src/nft/description.ts +225 -52
- package/src/nft/index.ts +3 -0
- package/src/planner/index.ts +242 -0
- package/src/planner/planner.test.ts +334 -0
- package/src/resolution/describe-module.ts +43 -25
- package/src/resolution/display-name.ts +38 -10
- package/src/resolution/resolve-item.test.ts +48 -0
- package/src/resolution/resolve-item.ts +112 -58
- package/src/scan/index.ts +244 -0
- package/src/scan/scan-wasm.base64.ts +2 -0
- package/src/scheduling/accessor.ts +65 -23
- package/src/scheduling/availability.test.ts +204 -0
- package/src/scheduling/availability.ts +211 -0
- package/src/scheduling/cancel.test.ts +542 -0
- package/src/scheduling/cancel.ts +254 -0
- package/src/scheduling/cluster-stock.test.ts +31 -0
- package/src/scheduling/cluster-stock.ts +15 -0
- package/src/scheduling/energy.ts +47 -0
- package/src/scheduling/idle-resolve.ts +45 -0
- package/src/scheduling/jobs.ts +71 -0
- package/src/scheduling/lane-core.ts +128 -0
- package/src/scheduling/lanes.test.ts +250 -0
- package/src/scheduling/lanes.ts +227 -0
- package/src/scheduling/projection.ts +257 -133
- package/src/scheduling/schedule.test.ts +119 -0
- package/src/scheduling/schedule.ts +257 -117
- package/src/scheduling/task-cargo.test.ts +44 -0
- package/src/scheduling/task-cargo.ts +46 -0
- package/src/scheduling/unwrap.test.ts +86 -0
- package/src/scheduling/unwrap.ts +190 -0
- package/src/shipload.ts +26 -1
- package/src/subscriptions/manager.cluster.test.ts +51 -0
- package/src/subscriptions/manager.ts +245 -172
- package/src/subscriptions/mappers.ts +5 -8
- package/src/subscriptions/types.ts +28 -3
- package/src/testing/catalog-hash.ts +19 -0
- package/src/testing/index.ts +2 -0
- package/src/testing/projection-parity.ts +167 -0
- package/src/travel/reach.ts +21 -0
- package/src/travel/route-planner.ts +288 -0
- package/src/travel/route-simulator.ts +170 -0
- package/src/travel/travel.ts +188 -122
- package/src/types/capabilities.ts +37 -8
- package/src/types/entity.ts +3 -3
- package/src/types/index.ts +0 -1
- package/src/types.ts +51 -16
- package/src/utils/cargo.test.ts +14 -0
- package/src/utils/cargo.ts +29 -0
- package/src/utils/display-name.ts +70 -0
- package/src/utils/system.ts +15 -16
- package/src/capabilities/loading.ts +0 -8
- package/src/entities/container.ts +0 -123
- package/src/entities/ship-deploy.ts +0 -295
- package/src/entities/ship.ts +0 -221
- package/src/entities/warehouse.ts +0 -127
- package/src/types/entity-traits.ts +0 -69
package/src/nft/description.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getModuleCapabilityType,
|
|
3
|
+
MODULE_BUILDER,
|
|
3
4
|
MODULE_CRAFTER,
|
|
4
5
|
MODULE_ENGINE,
|
|
5
6
|
MODULE_GATHERER,
|
|
6
7
|
MODULE_GENERATOR,
|
|
8
|
+
MODULE_BATTERY,
|
|
7
9
|
MODULE_HAULER,
|
|
8
10
|
MODULE_LOADER,
|
|
9
11
|
MODULE_STORAGE,
|
|
@@ -12,69 +14,196 @@ import {
|
|
|
12
14
|
import {
|
|
13
15
|
ITEM_CONTAINER_T1_PACKED,
|
|
14
16
|
ITEM_CONTAINER_T2_PACKED,
|
|
17
|
+
ITEM_CONSTRUCTION_DOCK_T1_PACKED,
|
|
18
|
+
ITEM_BUILDER_T1,
|
|
19
|
+
ITEM_BUILDER_T2,
|
|
15
20
|
ITEM_CRAFTER_T1,
|
|
21
|
+
ITEM_CRAFTER_T2,
|
|
16
22
|
ITEM_ENGINE_T1,
|
|
23
|
+
ITEM_ENGINE_T2,
|
|
24
|
+
ITEM_EXTRACTOR_T1_PACKED,
|
|
25
|
+
ITEM_FACTORY_T1_PACKED,
|
|
17
26
|
ITEM_GATHERER_T1,
|
|
27
|
+
ITEM_GATHERER_T2,
|
|
18
28
|
ITEM_GENERATOR_T1,
|
|
29
|
+
ITEM_GENERATOR_T2,
|
|
19
30
|
ITEM_HAULER_T1,
|
|
31
|
+
ITEM_HAULER_T2,
|
|
32
|
+
ITEM_BATTERY_T1,
|
|
33
|
+
ITEM_LAUNCHER_T1,
|
|
20
34
|
ITEM_LOADER_T1,
|
|
35
|
+
ITEM_LOADER_T2,
|
|
36
|
+
ITEM_PROSPECTOR_T1A_PACKED,
|
|
37
|
+
ITEM_PROSPECTOR_T2A_PACKED,
|
|
38
|
+
ITEM_PROSPECTOR_T2B_PACKED,
|
|
39
|
+
ITEM_ROUSTABOUT_T1A_PACKED,
|
|
21
40
|
ITEM_SHIP_T1_PACKED,
|
|
41
|
+
ITEM_SMITH_T1A_PACKED,
|
|
22
42
|
ITEM_STORAGE_T1,
|
|
43
|
+
ITEM_TENDER_T1A_PACKED,
|
|
44
|
+
ITEM_TUG_T1A_PACKED,
|
|
45
|
+
ITEM_PORTER_T1A_PACKED,
|
|
46
|
+
ITEM_WRIGHT_T1A_PACKED,
|
|
47
|
+
ITEM_DREDGER_T2A_PACKED,
|
|
23
48
|
ITEM_WAREHOUSE_T1_PACKED,
|
|
24
49
|
ITEM_WARP_T1,
|
|
50
|
+
ITEM_WARP_T2,
|
|
25
51
|
} from '../data/item-ids'
|
|
26
52
|
import {decodeStat} from '../derivation/crafting'
|
|
27
|
-
import {
|
|
53
|
+
import {
|
|
54
|
+
gathererDepthForTier,
|
|
55
|
+
computeGathererYield,
|
|
56
|
+
moduleTierPct,
|
|
57
|
+
ENGINE_THRUST_TIER_PCT,
|
|
58
|
+
GENERATOR_CAPACITY_TIER_PCT,
|
|
59
|
+
GENERATOR_RECHARGE_TIER_PCT,
|
|
60
|
+
CRAFTER_SPEED_TIER_PCT,
|
|
61
|
+
BUILDER_SPEED_TIER_PCT,
|
|
62
|
+
WARP_RANGE_TIER_PCT,
|
|
63
|
+
LOADER_THRUST_TIER_PCT,
|
|
64
|
+
} from '../derivation/capabilities'
|
|
28
65
|
import {getItem} from '../data/catalog'
|
|
66
|
+
import {ENTITY_SHIP, getPackedEntityType} from '../data/kind-registry'
|
|
67
|
+
import {getBaseHullmassFor} from '../derivation/capabilities'
|
|
68
|
+
import {computeEffectiveModuleStat} from '../derivation/stat-scaling'
|
|
29
69
|
|
|
30
70
|
function idiv(a: number, b: number): number {
|
|
31
71
|
return Math.floor(a / b)
|
|
32
72
|
}
|
|
33
73
|
|
|
34
|
-
export function
|
|
74
|
+
export function toWholeEnergy(milli: number): number {
|
|
75
|
+
return idiv(milli + 500, 1000)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function isShipHull(itemId: number): boolean {
|
|
79
|
+
return getPackedEntityType(itemId)?.equals(ENTITY_SHIP) ?? false
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function sumPackedShipChannels(stats: bigint): number {
|
|
83
|
+
let sum = 0
|
|
84
|
+
for (let slot = 0; slot < 5; slot++) sum += decodeStat(stats, slot)
|
|
85
|
+
return sum
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function computeBaseHullmass(itemId: number, stats: bigint): number {
|
|
89
|
+
if (isShipHull(itemId)) {
|
|
90
|
+
return Math.floor(
|
|
91
|
+
(getBaseHullmassFor(itemId) * (10_000 - sumPackedShipChannels(stats))) / 10_000
|
|
92
|
+
)
|
|
93
|
+
}
|
|
35
94
|
const density = decodeStat(stats, 1)
|
|
36
|
-
return
|
|
95
|
+
return Math.floor((getBaseHullmassFor(itemId) * (2000 - density)) / 2000)
|
|
37
96
|
}
|
|
38
97
|
|
|
39
98
|
export function computeBaseCapacityShip(stats: bigint): number {
|
|
40
|
-
|
|
41
|
-
|
|
99
|
+
return Math.floor(5_000_000 * 6 ** (sumPackedShipChannels(stats) / 4995))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function computeBaseCapacityContainer(stats: bigint): number {
|
|
103
|
+
const s = decodeStat(stats, 0) + decodeStat(stats, 2)
|
|
104
|
+
return Math.floor(22_000_000 * 6 ** (s / 1998))
|
|
42
105
|
}
|
|
43
106
|
|
|
44
107
|
export function computeBaseCapacityWarehouse(stats: bigint): number {
|
|
45
|
-
const s = decodeStat(stats, 0) + decodeStat(stats, 2)
|
|
46
|
-
return Math.floor(
|
|
108
|
+
const s = decodeStat(stats, 0) + decodeStat(stats, 2)
|
|
109
|
+
return Math.floor(100_000_000 * 6 ** (s / 1998))
|
|
47
110
|
}
|
|
48
111
|
|
|
49
|
-
export const computeEngineThrust = (vol: number): number =>
|
|
50
|
-
|
|
51
|
-
export const
|
|
52
|
-
export const
|
|
53
|
-
export const
|
|
112
|
+
export const computeEngineThrust = (vol: number, tier: number): number =>
|
|
113
|
+
idiv((400 + idiv(vol * 3, 4)) * moduleTierPct(ENGINE_THRUST_TIER_PCT, tier), 100)
|
|
114
|
+
export const computeEngineDrain = (thm: number): number => 2 * Math.max(30, 50 - idiv(thm, 70))
|
|
115
|
+
export const ENGINE_DRAIN_BASE = 156
|
|
116
|
+
export const ENGINE_DRAIN_REF_THRUST = 775
|
|
117
|
+
export const ENGINE_DRAIN_REF_THM = 500
|
|
118
|
+
|
|
119
|
+
export const computeTravelDrain = (totalThrust: number, avgThm: number): number => {
|
|
120
|
+
if (totalThrust <= 0) return 0
|
|
121
|
+
const thermalFactor = computeEngineDrain(avgThm) / computeEngineDrain(ENGINE_DRAIN_REF_THM)
|
|
122
|
+
const thrustFactor = Math.sqrt(ENGINE_DRAIN_REF_THRUST / totalThrust)
|
|
123
|
+
return Math.floor(ENGINE_DRAIN_BASE * 1000 * thermalFactor * thrustFactor)
|
|
124
|
+
}
|
|
125
|
+
export const computeGeneratorCap = (com: number, tier: number): number =>
|
|
126
|
+
idiv((1_300_000 + com * 500) * moduleTierPct(GENERATOR_CAPACITY_TIER_PCT, tier), 100)
|
|
127
|
+
export const computeGeneratorRech = (fin: number, tier: number): number =>
|
|
128
|
+
idiv((2000 + fin * 6) * moduleTierPct(GENERATOR_RECHARGE_TIER_PCT, tier), 100)
|
|
54
129
|
export const computeGathererDrain = (con: number): number =>
|
|
55
|
-
Math.max(
|
|
130
|
+
2 * Math.max(250_000, 1_250_000 - con * 1250)
|
|
56
131
|
export const computeGathererDepth = (tol: number, tier: number): number =>
|
|
57
132
|
gathererDepthForTier(tol, tier)
|
|
58
|
-
export const computeGathererSpeed = (ref: number): number => 100 + idiv(ref * 4, 5)
|
|
59
133
|
export const computeLoaderMass = (ins: number): number => Math.max(200, 2000 - ins * 2)
|
|
60
|
-
export const computeLoaderThrust = (pla: number): number =>
|
|
61
|
-
|
|
62
|
-
export const
|
|
63
|
-
|
|
134
|
+
export const computeLoaderThrust = (pla: number, tier: number): number =>
|
|
135
|
+
idiv((1 + idiv(pla * pla, 10000)) * moduleTierPct(LOADER_THRUST_TIER_PCT, tier), 100)
|
|
136
|
+
export const computeCrafterSpeed = (rea: number, tier: number): number =>
|
|
137
|
+
idiv((100 + idiv(rea * 4, 5)) * moduleTierPct(CRAFTER_SPEED_TIER_PCT, tier), 100)
|
|
138
|
+
export const computeCrafterDrain = (fin: number): number =>
|
|
139
|
+
Math.max(5000, 30000 - idiv(fin * 1000, 33))
|
|
140
|
+
export const computeBuilderSpeed = (resonance: number, tier: number): number =>
|
|
141
|
+
idiv((100 + idiv(resonance * 4, 5)) * moduleTierPct(BUILDER_SPEED_TIER_PCT, tier), 100)
|
|
142
|
+
export const computeBuilderDrain = (fineness: number): number =>
|
|
143
|
+
Math.max(5000, 30000 - idiv(fineness * 1000, 33))
|
|
144
|
+
export const computeHaulerCapacity = (fin: number, tier: number): number =>
|
|
145
|
+
Math.max(tier, tier + idiv(fin, 400))
|
|
64
146
|
export const computeHaulerEfficiency = (con: number): number => 2000 + con * 6
|
|
65
|
-
export const
|
|
66
|
-
|
|
147
|
+
export const supportDrainTierPercent = (tier: number): number => {
|
|
148
|
+
const clampedTier = Math.min(10, Math.max(1, Math.trunc(tier)))
|
|
149
|
+
return Math.max(50, 110 - clampedTier * 10)
|
|
150
|
+
}
|
|
151
|
+
const computeT1LogisticsDrain = (stat: number): number =>
|
|
152
|
+
Math.max(3000, 15000 - Math.min(12000, idiv(stat * 1000, 80)))
|
|
153
|
+
export const computeHaulerDrain = (conductivity: number, tier: number): number =>
|
|
154
|
+
idiv(computeT1LogisticsDrain(conductivity) * supportDrainTierPercent(tier), 100)
|
|
155
|
+
export const computeCargoBayDrain = (cohesion: number, tier: number): number =>
|
|
156
|
+
idiv(computeT1LogisticsDrain(cohesion) * 3 * supportDrainTierPercent(tier), 400)
|
|
157
|
+
export const computeWarpRange = (stat: number, tier: number): number =>
|
|
158
|
+
idiv((100 + stat * 3) * moduleTierPct(WARP_RANGE_TIER_PCT, tier), 100)
|
|
159
|
+
export const computeCargoBayCapacity = (
|
|
160
|
+
strength: number,
|
|
161
|
+
density: number,
|
|
162
|
+
hardness: number
|
|
163
|
+
): number => 10_000_000 + idiv((strength + density + hardness) * 50_000_000, 2997)
|
|
164
|
+
export const computeBatteryBankCapacity = (
|
|
165
|
+
volatility: number,
|
|
166
|
+
thermal: number,
|
|
167
|
+
plasticity: number,
|
|
168
|
+
insulation: number
|
|
169
|
+
): number => 2_500_000 + idiv((volatility + thermal + plasticity + insulation) * 7_500_000, 3996)
|
|
67
170
|
|
|
68
171
|
export function entityDisplayName(itemId: number): string {
|
|
69
172
|
switch (itemId) {
|
|
70
173
|
case ITEM_SHIP_T1_PACKED:
|
|
71
174
|
return 'Ship'
|
|
175
|
+
case ITEM_ROUSTABOUT_T1A_PACKED:
|
|
176
|
+
return 'Roustabout'
|
|
177
|
+
case ITEM_PROSPECTOR_T1A_PACKED:
|
|
178
|
+
return 'Prospector'
|
|
179
|
+
case ITEM_TENDER_T1A_PACKED:
|
|
180
|
+
return 'Tender'
|
|
181
|
+
case ITEM_TUG_T1A_PACKED:
|
|
182
|
+
return 'Tug'
|
|
183
|
+
case ITEM_PORTER_T1A_PACKED:
|
|
184
|
+
return 'Porter'
|
|
185
|
+
case ITEM_WRIGHT_T1A_PACKED:
|
|
186
|
+
return 'Wright'
|
|
187
|
+
case ITEM_SMITH_T1A_PACKED:
|
|
188
|
+
return 'Smith'
|
|
72
189
|
case ITEM_WAREHOUSE_T1_PACKED:
|
|
73
190
|
return 'Warehouse'
|
|
191
|
+
case ITEM_EXTRACTOR_T1_PACKED:
|
|
192
|
+
return 'Mining Rig'
|
|
193
|
+
case ITEM_FACTORY_T1_PACKED:
|
|
194
|
+
return 'Factory'
|
|
195
|
+
case ITEM_CONSTRUCTION_DOCK_T1_PACKED:
|
|
196
|
+
return 'Construction Dock'
|
|
74
197
|
case ITEM_CONTAINER_T1_PACKED:
|
|
75
198
|
return 'Container'
|
|
76
199
|
case ITEM_CONTAINER_T2_PACKED:
|
|
77
200
|
return 'Container'
|
|
201
|
+
case ITEM_PROSPECTOR_T2A_PACKED:
|
|
202
|
+
return 'Prospector'
|
|
203
|
+
case ITEM_PROSPECTOR_T2B_PACKED:
|
|
204
|
+
return 'Prospector'
|
|
205
|
+
case ITEM_DREDGER_T2A_PACKED:
|
|
206
|
+
return 'Dredger'
|
|
78
207
|
default:
|
|
79
208
|
return 'Entity'
|
|
80
209
|
}
|
|
@@ -83,21 +212,35 @@ export function entityDisplayName(itemId: number): string {
|
|
|
83
212
|
export function moduleDisplayName(itemId: number): string {
|
|
84
213
|
switch (itemId) {
|
|
85
214
|
case ITEM_ENGINE_T1:
|
|
215
|
+
case ITEM_ENGINE_T2:
|
|
86
216
|
return 'Engine'
|
|
87
217
|
case ITEM_GENERATOR_T1:
|
|
88
|
-
|
|
218
|
+
case ITEM_GENERATOR_T2:
|
|
219
|
+
return 'Power Core'
|
|
89
220
|
case ITEM_GATHERER_T1:
|
|
90
|
-
|
|
221
|
+
case ITEM_GATHERER_T2:
|
|
222
|
+
return 'Limpet Bay'
|
|
91
223
|
case ITEM_LOADER_T1:
|
|
92
|
-
|
|
224
|
+
case ITEM_LOADER_T2:
|
|
225
|
+
return 'Shuttle Bay'
|
|
93
226
|
case ITEM_CRAFTER_T1:
|
|
94
|
-
|
|
227
|
+
case ITEM_CRAFTER_T2:
|
|
228
|
+
return 'Fabricator'
|
|
229
|
+
case ITEM_BUILDER_T1:
|
|
230
|
+
case ITEM_BUILDER_T2:
|
|
231
|
+
return 'Assembly Arm'
|
|
95
232
|
case ITEM_STORAGE_T1:
|
|
96
|
-
return '
|
|
233
|
+
return 'Cargo Hold'
|
|
97
234
|
case ITEM_HAULER_T1:
|
|
98
|
-
|
|
235
|
+
case ITEM_HAULER_T2:
|
|
236
|
+
return 'Tractor Beam'
|
|
99
237
|
case ITEM_WARP_T1:
|
|
100
|
-
|
|
238
|
+
case ITEM_WARP_T2:
|
|
239
|
+
return 'Warp Drive'
|
|
240
|
+
case ITEM_BATTERY_T1:
|
|
241
|
+
return 'Battery Bank'
|
|
242
|
+
case ITEM_LAUNCHER_T1:
|
|
243
|
+
return 'Drive Coil'
|
|
101
244
|
default:
|
|
102
245
|
return 'Module'
|
|
103
246
|
}
|
|
@@ -115,60 +258,82 @@ export function formatModuleLine(slot: number, itemId: number, stats: bigint): s
|
|
|
115
258
|
|
|
116
259
|
switch (subtype) {
|
|
117
260
|
case MODULE_ENGINE: {
|
|
118
|
-
const vol = decodeStat(stats, 0)
|
|
119
|
-
const thm = decodeStat(stats, 1)
|
|
120
|
-
|
|
261
|
+
const vol = computeEffectiveModuleStat(decodeStat(stats, 0))
|
|
262
|
+
const thm = computeEffectiveModuleStat(decodeStat(stats, 1))
|
|
263
|
+
const tier = getItem(itemId).tier
|
|
264
|
+
out += ` Thrust ${computeEngineThrust(vol, tier)} Drain ${computeEngineDrain(thm)}`
|
|
121
265
|
break
|
|
122
266
|
}
|
|
123
267
|
case MODULE_GENERATOR: {
|
|
124
|
-
const res = decodeStat(stats, 0)
|
|
125
|
-
const ref = decodeStat(stats, 1)
|
|
126
|
-
|
|
268
|
+
const res = computeEffectiveModuleStat(decodeStat(stats, 0))
|
|
269
|
+
const ref = computeEffectiveModuleStat(decodeStat(stats, 1))
|
|
270
|
+
const tier = getItem(itemId).tier
|
|
271
|
+
out += ` Capacity ${toWholeEnergy(computeGeneratorCap(res, tier))} Recharge ${toWholeEnergy(
|
|
272
|
+
computeGeneratorRech(ref, tier)
|
|
273
|
+
)}`
|
|
127
274
|
break
|
|
128
275
|
}
|
|
129
276
|
case MODULE_GATHERER: {
|
|
130
277
|
const str = decodeStat(stats, 0)
|
|
131
278
|
const tol = decodeStat(stats, 1)
|
|
132
|
-
const con = decodeStat(stats,
|
|
133
|
-
const ref = decodeStat(stats, 4)
|
|
279
|
+
const con = decodeStat(stats, 2)
|
|
134
280
|
const tier = getItem(itemId).tier
|
|
135
|
-
out += ` Yield ${computeGathererYield(str)} Depth ${computeGathererDepth(
|
|
281
|
+
out += ` Yield ${computeGathererYield(str, tier)} Depth ${computeGathererDepth(
|
|
136
282
|
tol,
|
|
137
283
|
tier
|
|
138
|
-
)}
|
|
284
|
+
)} Drain ${toWholeEnergy(computeGathererDrain(con))}`
|
|
139
285
|
break
|
|
140
286
|
}
|
|
141
287
|
case MODULE_LOADER: {
|
|
142
288
|
const fin = decodeStat(stats, 0)
|
|
143
289
|
const pla = decodeStat(stats, 1)
|
|
144
|
-
|
|
290
|
+
const tier = getItem(itemId).tier
|
|
291
|
+
out += ` Mass ${computeLoaderMass(fin)} Thrust ${computeLoaderThrust(pla, tier)}`
|
|
145
292
|
break
|
|
146
293
|
}
|
|
147
294
|
case MODULE_CRAFTER: {
|
|
148
295
|
const rea = decodeStat(stats, 0)
|
|
149
|
-
const
|
|
150
|
-
|
|
296
|
+
const con = decodeStat(stats, 1)
|
|
297
|
+
const tier = getItem(itemId).tier
|
|
298
|
+
out += ` Speed ${computeCrafterSpeed(rea, tier)} Drain ${toWholeEnergy(computeCrafterDrain(con))}`
|
|
299
|
+
break
|
|
300
|
+
}
|
|
301
|
+
case MODULE_BUILDER: {
|
|
302
|
+
const res = decodeStat(stats, 0)
|
|
303
|
+
const fin = decodeStat(stats, 1)
|
|
304
|
+
const tier = getItem(itemId).tier
|
|
305
|
+
out += ` Speed ${computeBuilderSpeed(res, tier)} Drain ${toWholeEnergy(computeBuilderDrain(fin))}`
|
|
151
306
|
break
|
|
152
307
|
}
|
|
153
308
|
case MODULE_STORAGE: {
|
|
154
309
|
const str = decodeStat(stats, 0)
|
|
155
|
-
const
|
|
156
|
-
const
|
|
157
|
-
const
|
|
158
|
-
const
|
|
159
|
-
out += `
|
|
310
|
+
const den = decodeStat(stats, 1)
|
|
311
|
+
const hrd = decodeStat(stats, 2)
|
|
312
|
+
const coh = decodeStat(stats, 3)
|
|
313
|
+
const tier = getItem(itemId).tier
|
|
314
|
+
out += ` Cargo Capacity ${computeCargoBayCapacity(str, den, hrd)} Drain ${toWholeEnergy(computeCargoBayDrain(coh, tier))}`
|
|
160
315
|
break
|
|
161
316
|
}
|
|
162
317
|
case MODULE_HAULER: {
|
|
163
|
-
const
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
|
|
318
|
+
const res = decodeStat(stats, 0)
|
|
319
|
+
const pla = decodeStat(stats, 1)
|
|
320
|
+
const con = decodeStat(stats, 2)
|
|
321
|
+
const tier = getItem(itemId).tier
|
|
322
|
+
out += ` Capacity ${computeHaulerCapacity(res, tier)} Efficiency ${computeHaulerEfficiency(pla)} Drain ${toWholeEnergy(computeHaulerDrain(con, tier))}`
|
|
167
323
|
break
|
|
168
324
|
}
|
|
169
325
|
case MODULE_WARP: {
|
|
170
326
|
const stat = decodeStat(stats, 0)
|
|
171
|
-
|
|
327
|
+
const tier = getItem(itemId).tier
|
|
328
|
+
out += ` Range ${computeWarpRange(stat, tier)}`
|
|
329
|
+
break
|
|
330
|
+
}
|
|
331
|
+
case MODULE_BATTERY: {
|
|
332
|
+
const vol = decodeStat(stats, 0)
|
|
333
|
+
const thm = decodeStat(stats, 1)
|
|
334
|
+
const pla = decodeStat(stats, 2)
|
|
335
|
+
const ins = decodeStat(stats, 3)
|
|
336
|
+
out += ` Energy Capacity ${toWholeEnergy(computeBatteryBankCapacity(vol, thm, pla, ins))}`
|
|
172
337
|
break
|
|
173
338
|
}
|
|
174
339
|
}
|
|
@@ -181,12 +346,20 @@ export function buildEntityDescription(
|
|
|
181
346
|
moduleItems: number[],
|
|
182
347
|
moduleStats: bigint[]
|
|
183
348
|
): string {
|
|
184
|
-
const hullMass = computeBaseHullmass(hullStats)
|
|
349
|
+
const hullMass = computeBaseHullmass(itemId, hullStats)
|
|
185
350
|
let baseCapacity = 0
|
|
186
|
-
if (itemId
|
|
351
|
+
if (isShipHull(itemId)) {
|
|
187
352
|
baseCapacity = computeBaseCapacityShip(hullStats)
|
|
188
353
|
} else if (itemId === ITEM_WAREHOUSE_T1_PACKED) {
|
|
189
354
|
baseCapacity = computeBaseCapacityWarehouse(hullStats)
|
|
355
|
+
} else if (
|
|
356
|
+
itemId === ITEM_EXTRACTOR_T1_PACKED ||
|
|
357
|
+
itemId === ITEM_FACTORY_T1_PACKED ||
|
|
358
|
+
itemId === ITEM_CONSTRUCTION_DOCK_T1_PACKED ||
|
|
359
|
+
itemId === ITEM_CONTAINER_T1_PACKED ||
|
|
360
|
+
itemId === ITEM_CONTAINER_T2_PACKED
|
|
361
|
+
) {
|
|
362
|
+
baseCapacity = computeBaseCapacityContainer(hullStats)
|
|
190
363
|
}
|
|
191
364
|
|
|
192
365
|
let out = entityDisplayName(itemId)
|
package/src/nft/index.ts
CHANGED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import type {GathererStats} from '../types/capabilities'
|
|
2
|
+
import type {ServerContract} from '../contracts'
|
|
3
|
+
import {calc_gather_duration, calc_gather_energy} from '../capabilities/gathering'
|
|
4
|
+
import {calc_rechargetime} from '../travel/travel'
|
|
5
|
+
import {projectRemainingAt, type Projectable} from '../scheduling/projection'
|
|
6
|
+
|
|
7
|
+
export interface LanePlanEntry {
|
|
8
|
+
slot: number
|
|
9
|
+
quantity: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type PlanTarget = {quantity: number} | 'max'
|
|
13
|
+
|
|
14
|
+
export interface GatherPlanEntity extends Projectable {
|
|
15
|
+
gatherer_lanes: ServerContract.Types.gatherer_lane[]
|
|
16
|
+
loader_lanes: ServerContract.Types.loader_lane[]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface GatherLimpet {
|
|
20
|
+
slot: number
|
|
21
|
+
quantity: number
|
|
22
|
+
durationSeconds: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface GatherCycle {
|
|
26
|
+
rechargeBefore: boolean
|
|
27
|
+
rechargeSeconds: number
|
|
28
|
+
limpets: GatherLimpet[]
|
|
29
|
+
gatherSeconds: number
|
|
30
|
+
batchOre: number
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type FillCap = 'reserve' | 'hold' | 'requested'
|
|
34
|
+
|
|
35
|
+
export interface GatherPlan {
|
|
36
|
+
cycles: GatherCycle[]
|
|
37
|
+
cycleCount: number
|
|
38
|
+
totalOre: number
|
|
39
|
+
totalSeconds: number
|
|
40
|
+
cap: FillCap
|
|
41
|
+
reachingCount: number
|
|
42
|
+
totalLimpets: number
|
|
43
|
+
warnings: string[]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface BuildGatherPlanOpts {
|
|
47
|
+
richness: number
|
|
48
|
+
itemMass: number
|
|
49
|
+
holdRoom: number
|
|
50
|
+
reserveRemaining: number
|
|
51
|
+
now: Date
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const MAX_CYCLES = 10_000
|
|
55
|
+
|
|
56
|
+
const MAX_TRANSFER_QTY = 10000
|
|
57
|
+
|
|
58
|
+
export function allocateProportional(
|
|
59
|
+
lanes: {slot: number; weight: number}[],
|
|
60
|
+
total: number
|
|
61
|
+
): LanePlanEntry[] {
|
|
62
|
+
if (lanes.length === 0) return []
|
|
63
|
+
const weightSum = lanes.reduce((s, l) => s + l.weight, 0)
|
|
64
|
+
if (weightSum === 0) return []
|
|
65
|
+
|
|
66
|
+
const entries: LanePlanEntry[] = lanes.map((l) => ({
|
|
67
|
+
slot: l.slot,
|
|
68
|
+
quantity: Math.floor((total * l.weight) / weightSum),
|
|
69
|
+
}))
|
|
70
|
+
|
|
71
|
+
let remainder = total - entries.reduce((s, e) => s + e.quantity, 0)
|
|
72
|
+
for (let i = 0; remainder > 0; i = (i + 1) % entries.length) {
|
|
73
|
+
entries[i].quantity++
|
|
74
|
+
remainder--
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return entries
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function laneStats(lane: ServerContract.Types.gatherer_lane): GathererStats {
|
|
81
|
+
return lane as unknown as GathererStats
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function gatherEnergyCost(
|
|
85
|
+
lane: ServerContract.Types.gatherer_lane,
|
|
86
|
+
quantity: number,
|
|
87
|
+
stratum: number,
|
|
88
|
+
itemMass: number,
|
|
89
|
+
richness: number
|
|
90
|
+
): number {
|
|
91
|
+
if (quantity <= 0) return 0
|
|
92
|
+
const dur = Number(calc_gather_duration(laneStats(lane), itemMass, quantity, stratum, richness))
|
|
93
|
+
return Number(calc_gather_energy(laneStats(lane), dur))
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function splitCost(
|
|
97
|
+
reaching: ServerContract.Types.gatherer_lane[],
|
|
98
|
+
quantity: number,
|
|
99
|
+
stratum: number,
|
|
100
|
+
itemMass: number,
|
|
101
|
+
richness: number
|
|
102
|
+
): number {
|
|
103
|
+
if (quantity <= 0) return 0
|
|
104
|
+
const weights = reaching.map((l) => ({
|
|
105
|
+
slot: l.slot_index.toNumber(),
|
|
106
|
+
weight: l.yield.toNumber(),
|
|
107
|
+
}))
|
|
108
|
+
const split = allocateProportional(weights, quantity)
|
|
109
|
+
return split.reduce((sum, e) => {
|
|
110
|
+
const lane = reaching.find((l) => l.slot_index.toNumber() === e.slot)!
|
|
111
|
+
return sum + gatherEnergyCost(lane, e.quantity, stratum, itemMass, richness)
|
|
112
|
+
}, 0)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Binary search is exact because splitCost is monotonic non-decreasing in Q.
|
|
116
|
+
export function maxQtyForCharge(
|
|
117
|
+
reaching: ServerContract.Types.gatherer_lane[],
|
|
118
|
+
hi: number,
|
|
119
|
+
capacity: number,
|
|
120
|
+
stratum: number,
|
|
121
|
+
itemMass: number,
|
|
122
|
+
richness: number
|
|
123
|
+
): number {
|
|
124
|
+
if (hi <= 0) return 0
|
|
125
|
+
if (splitCost(reaching, hi, stratum, itemMass, richness) <= capacity) return hi
|
|
126
|
+
let lo = 0
|
|
127
|
+
let high = hi
|
|
128
|
+
while (lo < high) {
|
|
129
|
+
const mid = Math.ceil((lo + high) / 2)
|
|
130
|
+
if (splitCost(reaching, mid, stratum, itemMass, richness) <= capacity) lo = mid
|
|
131
|
+
else high = mid - 1
|
|
132
|
+
}
|
|
133
|
+
return lo
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function buildGatherPlan(
|
|
137
|
+
entity: GatherPlanEntity,
|
|
138
|
+
stratum: number,
|
|
139
|
+
target: PlanTarget,
|
|
140
|
+
opts: BuildGatherPlanOpts
|
|
141
|
+
): GatherPlan {
|
|
142
|
+
const {richness, itemMass, holdRoom, reserveRemaining, now} = opts
|
|
143
|
+
const warnings: string[] = []
|
|
144
|
+
|
|
145
|
+
const reaching = entity.gatherer_lanes.filter((l) => l.depth.toNumber() >= stratum)
|
|
146
|
+
if (reaching.length === 0) throw new Error('no gatherer reaches this stratum')
|
|
147
|
+
if (!entity.generator) throw new Error('entity has no generator')
|
|
148
|
+
|
|
149
|
+
const blocked = entity.gatherer_lanes.length - reaching.length
|
|
150
|
+
if (blocked > 0) {
|
|
151
|
+
warnings.push(
|
|
152
|
+
`${blocked} of ${entity.gatherer_lanes.length} limpets can't reach this depth`
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const capacity = entity.generator.capacity.toNumber()
|
|
157
|
+
const rechargeRate = entity.generator.recharge.toNumber()
|
|
158
|
+
let energy = Number(projectRemainingAt(entity, now).energy)
|
|
159
|
+
|
|
160
|
+
const requested = target === 'max' ? Infinity : target.quantity
|
|
161
|
+
let fillTarget = requested
|
|
162
|
+
let cap: FillCap = 'requested'
|
|
163
|
+
if (holdRoom < fillTarget) {
|
|
164
|
+
fillTarget = holdRoom
|
|
165
|
+
cap = 'hold'
|
|
166
|
+
}
|
|
167
|
+
if (reserveRemaining < fillTarget) {
|
|
168
|
+
fillTarget = reserveRemaining
|
|
169
|
+
cap = 'reserve'
|
|
170
|
+
}
|
|
171
|
+
fillTarget = Math.max(0, Math.floor(fillTarget))
|
|
172
|
+
|
|
173
|
+
const cycles: GatherCycle[] = []
|
|
174
|
+
let remaining = fillTarget
|
|
175
|
+
let guard = 0
|
|
176
|
+
while (remaining > 0 && guard++ < MAX_CYCLES) {
|
|
177
|
+
const batch = maxQtyForCharge(reaching, remaining, capacity, stratum, itemMass, richness)
|
|
178
|
+
if (batch <= 0) {
|
|
179
|
+
warnings.push('a single gather cannot fit within one full charge')
|
|
180
|
+
break
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const costFull = splitCost(reaching, batch, stratum, itemMass, richness)
|
|
184
|
+
const rechargeBefore = energy < costFull
|
|
185
|
+
const rechargeSeconds = rechargeBefore
|
|
186
|
+
? Number(calc_rechargetime(capacity, energy, rechargeRate))
|
|
187
|
+
: 0
|
|
188
|
+
if (rechargeBefore) energy = capacity
|
|
189
|
+
|
|
190
|
+
const weights = reaching.map((l) => ({
|
|
191
|
+
slot: l.slot_index.toNumber(),
|
|
192
|
+
weight: l.yield.toNumber(),
|
|
193
|
+
}))
|
|
194
|
+
const split = allocateProportional(weights, batch).filter((e) => e.quantity > 0)
|
|
195
|
+
const limpets: GatherLimpet[] = split.map((e) => {
|
|
196
|
+
const lane = reaching.find((l) => l.slot_index.toNumber() === e.slot)!
|
|
197
|
+
const dur = Number(
|
|
198
|
+
calc_gather_duration(laneStats(lane), itemMass, e.quantity, stratum, richness)
|
|
199
|
+
)
|
|
200
|
+
return {slot: e.slot, quantity: e.quantity, durationSeconds: dur}
|
|
201
|
+
})
|
|
202
|
+
const actualCost = limpets.reduce((sum, l) => {
|
|
203
|
+
const lane = reaching.find((r) => r.slot_index.toNumber() === l.slot)!
|
|
204
|
+
return sum + Number(calc_gather_energy(laneStats(lane), l.durationSeconds))
|
|
205
|
+
}, 0)
|
|
206
|
+
energy = Math.max(0, energy - actualCost)
|
|
207
|
+
|
|
208
|
+
const gatherSeconds = limpets.reduce((m, l) => Math.max(m, l.durationSeconds), 0)
|
|
209
|
+
cycles.push({rechargeBefore, rechargeSeconds, limpets, gatherSeconds, batchOre: batch})
|
|
210
|
+
remaining -= batch
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const totalOre = cycles.reduce((s, c) => s + c.batchOre, 0)
|
|
214
|
+
const totalSeconds = cycles.reduce((s, c) => s + c.rechargeSeconds + c.gatherSeconds, 0)
|
|
215
|
+
return {
|
|
216
|
+
cycles,
|
|
217
|
+
cycleCount: cycles.length,
|
|
218
|
+
totalOre,
|
|
219
|
+
totalSeconds,
|
|
220
|
+
cap,
|
|
221
|
+
reachingCount: reaching.length,
|
|
222
|
+
totalLimpets: entity.gatherer_lanes.length,
|
|
223
|
+
warnings,
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function planParallelTransfer(
|
|
228
|
+
entity: GatherPlanEntity,
|
|
229
|
+
target: PlanTarget
|
|
230
|
+
): LanePlanEntry[] {
|
|
231
|
+
const lanes = entity.loader_lanes.filter((l) => l.thrust.toNumber() > 0)
|
|
232
|
+
if (lanes.length === 0) return []
|
|
233
|
+
|
|
234
|
+
const requestedQty = target === 'max' ? MAX_TRANSFER_QTY : target.quantity
|
|
235
|
+
|
|
236
|
+
const laneWeights = lanes.map((l) => ({
|
|
237
|
+
slot: l.slot_index.toNumber(),
|
|
238
|
+
weight: l.thrust.toNumber(),
|
|
239
|
+
}))
|
|
240
|
+
|
|
241
|
+
return allocateProportional(laneWeights, requestedQty).filter((e) => e.quantity > 0)
|
|
242
|
+
}
|