@shipload/sdk 1.0.0-next.1 → 1.0.0-next.11
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/shipload.d.ts +618 -353
- package/lib/shipload.js +2250 -1059
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +2193 -1044
- package/lib/shipload.m.js.map +1 -1
- package/package.json +2 -2
- package/src/capabilities/modules.ts +3 -0
- package/src/capabilities/storage.ts +1 -1
- package/src/contracts/platform.ts +13 -1
- package/src/contracts/server.ts +227 -282
- package/src/data/capabilities.ts +5 -330
- package/src/data/capability-formulas.ts +70 -0
- package/src/data/catalog.ts +0 -5
- package/src/data/colors.ts +2 -16
- package/src/data/entities.json +46 -10
- package/src/data/item-ids.ts +4 -1
- package/src/data/items.json +264 -0
- package/src/data/metadata.ts +63 -1
- package/src/data/recipes-runtime.ts +1 -0
- package/src/data/recipes.json +139 -11
- package/src/derivation/capability-mappings.ts +122 -0
- package/src/derivation/index.ts +1 -0
- package/src/derivation/resources.ts +116 -37
- package/src/derivation/stats.ts +1 -2
- package/src/entities/container.ts +25 -10
- package/src/entities/extractor.ts +144 -0
- package/src/entities/factory.ts +135 -0
- package/src/entities/gamestate.ts +0 -9
- package/src/entities/makers.ts +130 -20
- package/src/entities/nexus.ts +29 -0
- package/src/entities/ship-deploy.ts +114 -56
- package/src/entities/ship.ts +17 -0
- package/src/entities/slot-multiplier.ts +21 -0
- package/src/entities/warehouse.ts +20 -3
- package/src/errors.ts +10 -13
- package/src/index-module.ts +81 -26
- package/src/managers/actions.ts +53 -80
- package/src/managers/entities.ts +65 -17
- package/src/managers/locations.ts +2 -20
- package/src/nft/atomicdata.ts +128 -0
- package/src/nft/description.ts +41 -7
- package/src/nft/index.ts +1 -0
- package/src/resolution/resolve-item.ts +17 -9
- package/src/scheduling/accessor.ts +4 -0
- package/src/scheduling/projection.ts +10 -2
- package/src/scheduling/schedule.ts +15 -1
- package/src/scheduling/task-cargo.ts +47 -0
- package/src/subscriptions/connection.ts +50 -2
- package/src/subscriptions/manager.ts +84 -4
- package/src/subscriptions/mappers.ts +5 -1
- package/src/subscriptions/types.ts +2 -2
- package/src/travel/travel.ts +61 -2
- package/src/types/entity-traits.ts +103 -1
- package/src/types.ts +11 -1
- package/src/utils/cargo.ts +27 -0
- package/src/utils/system.ts +25 -24
|
@@ -58,13 +58,6 @@ export class GameState extends ServerContract.Types.state_row {
|
|
|
58
58
|
return this.enabled
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
/**
|
|
62
|
-
* Get the total number of ships in the game
|
|
63
|
-
*/
|
|
64
|
-
get shipCount(): number {
|
|
65
|
-
return Number(this.ships)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
61
|
/**
|
|
69
62
|
* Get the current salt value (used for random number generation)
|
|
70
63
|
*/
|
|
@@ -137,14 +130,12 @@ export class GameState extends ServerContract.Types.state_row {
|
|
|
137
130
|
get summary(): {
|
|
138
131
|
enabled: boolean
|
|
139
132
|
epoch: string
|
|
140
|
-
ships: number
|
|
141
133
|
hasSeed: boolean
|
|
142
134
|
hasCommit: boolean
|
|
143
135
|
} {
|
|
144
136
|
return {
|
|
145
137
|
enabled: this.enabled,
|
|
146
138
|
epoch: this.epoch.toString(),
|
|
147
|
-
ships: this.shipCount,
|
|
148
139
|
hasSeed: !this.seed.equals(Checksum256.from('0'.repeat(64))),
|
|
149
140
|
hasCommit: !this.commit.equals(Checksum256.from('0'.repeat(64))),
|
|
150
141
|
}
|
package/src/entities/makers.ts
CHANGED
|
@@ -3,8 +3,16 @@ import {ServerContract} from '../contracts'
|
|
|
3
3
|
import {type PackedModuleInput, Ship, type ShipStateInput} from './ship'
|
|
4
4
|
import {computeWarehouseCapabilities, Warehouse, type WarehouseStateInput} from './warehouse'
|
|
5
5
|
import {Container, type ContainerStateInput} from './container'
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import {Nexus, type NexusStateInput} from './nexus'
|
|
7
|
+
import {Extractor, computeExtractorCapabilities, type ExtractorStateInput} from './extractor'
|
|
8
|
+
import {Factory, computeFactoryCapabilities, type FactoryStateInput} from './factory'
|
|
9
|
+
import {
|
|
10
|
+
ITEM_EXTRACTOR_T1_PACKED,
|
|
11
|
+
ITEM_FACTORY_T1_PACKED,
|
|
12
|
+
ITEM_SHIP_T1_PACKED,
|
|
13
|
+
ITEM_WAREHOUSE_T1_PACKED,
|
|
14
|
+
} from '../data/item-ids'
|
|
15
|
+
import {getEntityLayout, type EntitySlot} from '../data/recipes-runtime'
|
|
8
16
|
import {itemMetadata} from '../data/metadata'
|
|
9
17
|
import {getItem} from '../data/catalog'
|
|
10
18
|
import {
|
|
@@ -14,6 +22,7 @@ import {
|
|
|
14
22
|
moduleSlotTypeToCode,
|
|
15
23
|
} from '../capabilities/modules'
|
|
16
24
|
import {computeShipCapabilities, computeStorageCapabilities} from './ship-deploy'
|
|
25
|
+
import type {InstalledModule} from './slot-multiplier'
|
|
17
26
|
import {decodeCraftedItemStats} from '../derivation/crafting'
|
|
18
27
|
|
|
19
28
|
function assignModulesToSlots(
|
|
@@ -56,19 +65,22 @@ function assignModulesToSlots(
|
|
|
56
65
|
)
|
|
57
66
|
}
|
|
58
67
|
|
|
59
|
-
function
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
68
|
+
function toInstalledModules(entries: ServerContract.Types.module_entry[]): InstalledModule[] {
|
|
69
|
+
const installed: InstalledModule[] = []
|
|
70
|
+
entries.forEach((entry, slotIndex) => {
|
|
71
|
+
if (!entry.installed) return
|
|
72
|
+
installed.push({
|
|
73
|
+
slotIndex,
|
|
74
|
+
itemId: Number(UInt16.from(entry.installed.item_id).value.toString()),
|
|
75
|
+
stats: BigInt(UInt64.from(entry.installed.stats).toString()),
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
return installed
|
|
64
79
|
}
|
|
65
80
|
|
|
66
|
-
function computeStorageBonus(
|
|
67
|
-
decoded: {itemId: number; stats: bigint}[],
|
|
68
|
-
baseCapacity: number
|
|
69
|
-
): number {
|
|
81
|
+
function computeStorageBonus(modules: InstalledModule[], baseCapacity: number): number {
|
|
70
82
|
let totalBonus = 0
|
|
71
|
-
for (const m of
|
|
83
|
+
for (const m of modules) {
|
|
72
84
|
if (getModuleCapabilityType(m.itemId) !== MODULE_STORAGE) continue
|
|
73
85
|
const stats = decodeCraftedItemStats(m.itemId, m.stats)
|
|
74
86
|
const {capacityBonus} = computeStorageCapabilities(stats, baseCapacity)
|
|
@@ -78,15 +90,16 @@ function computeStorageBonus(
|
|
|
78
90
|
}
|
|
79
91
|
|
|
80
92
|
function deriveShipFromModules(
|
|
81
|
-
|
|
93
|
+
moduleEntries: ServerContract.Types.module_entry[],
|
|
94
|
+
layout: EntitySlot[],
|
|
82
95
|
baseCapacity: number
|
|
83
96
|
): {
|
|
84
97
|
capabilities: ReturnType<typeof computeShipCapabilities>
|
|
85
98
|
finalCapacity: number
|
|
86
99
|
} {
|
|
87
|
-
const
|
|
88
|
-
const capabilities = computeShipCapabilities(
|
|
89
|
-
const totalBonus = computeStorageBonus(
|
|
100
|
+
const installed = toInstalledModules(moduleEntries)
|
|
101
|
+
const capabilities = computeShipCapabilities(installed, layout)
|
|
102
|
+
const totalBonus = computeStorageBonus(installed, baseCapacity)
|
|
90
103
|
return {capabilities, finalCapacity: baseCapacity + totalBonus}
|
|
91
104
|
}
|
|
92
105
|
|
|
@@ -109,10 +122,12 @@ export function makeShip(state: ShipStateInput): Ship {
|
|
|
109
122
|
if (state.schedule) info.schedule = state.schedule
|
|
110
123
|
|
|
111
124
|
let moduleEntries: ServerContract.Types.module_entry[] = []
|
|
125
|
+
const shipLayout = getEntityLayout(ITEM_SHIP_T1_PACKED)?.slots ?? []
|
|
112
126
|
if (state.modules && state.modules.length > 0) {
|
|
113
127
|
moduleEntries = assignModulesToSlots(ITEM_SHIP_T1_PACKED, state.modules, 'Ship T1')
|
|
114
128
|
const {capabilities, finalCapacity} = deriveShipFromModules(
|
|
115
|
-
|
|
129
|
+
moduleEntries,
|
|
130
|
+
shipLayout,
|
|
116
131
|
state.capacity ?? 0
|
|
117
132
|
)
|
|
118
133
|
if (capabilities.engines) info.engines = capabilities.engines
|
|
@@ -152,17 +167,18 @@ export function makeWarehouse(state: WarehouseStateInput): Warehouse {
|
|
|
152
167
|
if (state.schedule) info.schedule = state.schedule
|
|
153
168
|
|
|
154
169
|
let moduleEntries: ServerContract.Types.module_entry[] = []
|
|
170
|
+
const warehouseLayout = getEntityLayout(ITEM_WAREHOUSE_T1_PACKED)?.slots ?? []
|
|
155
171
|
if (state.modules && state.modules.length > 0) {
|
|
156
172
|
moduleEntries = assignModulesToSlots(
|
|
157
173
|
ITEM_WAREHOUSE_T1_PACKED,
|
|
158
174
|
state.modules,
|
|
159
175
|
'Warehouse T1'
|
|
160
176
|
)
|
|
161
|
-
const
|
|
162
|
-
const capabilities = computeWarehouseCapabilities(
|
|
177
|
+
const installed = toInstalledModules(moduleEntries)
|
|
178
|
+
const capabilities = computeWarehouseCapabilities(installed, warehouseLayout)
|
|
163
179
|
if (capabilities.loaders) info.loaders = capabilities.loaders
|
|
164
180
|
|
|
165
|
-
const totalBonus = computeStorageBonus(
|
|
181
|
+
const totalBonus = computeStorageBonus(installed, state.capacity)
|
|
166
182
|
info.capacity = UInt32.from(state.capacity + totalBonus)
|
|
167
183
|
} else {
|
|
168
184
|
moduleEntries = assignModulesToSlots(ITEM_WAREHOUSE_T1_PACKED, [], 'Warehouse T1')
|
|
@@ -174,6 +190,82 @@ export function makeWarehouse(state: WarehouseStateInput): Warehouse {
|
|
|
174
190
|
return new Warehouse(entityInfo)
|
|
175
191
|
}
|
|
176
192
|
|
|
193
|
+
export function makeExtractor(state: ExtractorStateInput): Extractor {
|
|
194
|
+
const info: Record<string, unknown> = {
|
|
195
|
+
type: Name.from('extractor'),
|
|
196
|
+
id: UInt64.from(state.id),
|
|
197
|
+
owner: Name.from(state.owner),
|
|
198
|
+
entity_name: state.name,
|
|
199
|
+
coordinates: ServerContract.Types.coordinates.from(state.coordinates),
|
|
200
|
+
cargomass: UInt32.from(0),
|
|
201
|
+
cargo: state.cargo || [],
|
|
202
|
+
is_idle: !state.schedule,
|
|
203
|
+
current_task_elapsed: UInt32.from(0),
|
|
204
|
+
current_task_remaining: UInt32.from(0),
|
|
205
|
+
pending_tasks: [],
|
|
206
|
+
}
|
|
207
|
+
if (state.hullmass !== undefined) info.hullmass = UInt32.from(state.hullmass)
|
|
208
|
+
if (state.energy !== undefined) info.energy = UInt16.from(state.energy)
|
|
209
|
+
if (state.schedule) info.schedule = state.schedule
|
|
210
|
+
if (state.capacity !== undefined) info.capacity = UInt32.from(state.capacity)
|
|
211
|
+
|
|
212
|
+
const moduleEntries = assignModulesToSlots(
|
|
213
|
+
ITEM_EXTRACTOR_T1_PACKED,
|
|
214
|
+
state.modules ?? [],
|
|
215
|
+
'Extractor T1'
|
|
216
|
+
)
|
|
217
|
+
if (state.modules && state.modules.length > 0) {
|
|
218
|
+
const layout = getEntityLayout(ITEM_EXTRACTOR_T1_PACKED)?.slots ?? []
|
|
219
|
+
const installed = toInstalledModules(moduleEntries)
|
|
220
|
+
const capabilities = computeExtractorCapabilities(installed, layout)
|
|
221
|
+
if (capabilities.generator) info.generator = capabilities.generator
|
|
222
|
+
if (capabilities.gatherer) info.gatherer = capabilities.gatherer
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
info.modules = moduleEntries
|
|
226
|
+
|
|
227
|
+
const entityInfo = ServerContract.Types.entity_info.from(info)
|
|
228
|
+
return new Extractor(entityInfo)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export function makeFactory(state: FactoryStateInput): Factory {
|
|
232
|
+
const info: Record<string, unknown> = {
|
|
233
|
+
type: Name.from('factory'),
|
|
234
|
+
id: UInt64.from(state.id),
|
|
235
|
+
owner: Name.from(state.owner),
|
|
236
|
+
entity_name: state.name,
|
|
237
|
+
coordinates: ServerContract.Types.coordinates.from(state.coordinates),
|
|
238
|
+
cargomass: UInt32.from(0),
|
|
239
|
+
cargo: state.cargo || [],
|
|
240
|
+
is_idle: !state.schedule,
|
|
241
|
+
current_task_elapsed: UInt32.from(0),
|
|
242
|
+
current_task_remaining: UInt32.from(0),
|
|
243
|
+
pending_tasks: [],
|
|
244
|
+
}
|
|
245
|
+
if (state.hullmass !== undefined) info.hullmass = UInt32.from(state.hullmass)
|
|
246
|
+
if (state.energy !== undefined) info.energy = UInt16.from(state.energy)
|
|
247
|
+
if (state.schedule) info.schedule = state.schedule
|
|
248
|
+
if (state.capacity !== undefined) info.capacity = UInt32.from(state.capacity)
|
|
249
|
+
|
|
250
|
+
const moduleEntries = assignModulesToSlots(
|
|
251
|
+
ITEM_FACTORY_T1_PACKED,
|
|
252
|
+
state.modules ?? [],
|
|
253
|
+
'Factory T1'
|
|
254
|
+
)
|
|
255
|
+
if (state.modules && state.modules.length > 0) {
|
|
256
|
+
const layout = getEntityLayout(ITEM_FACTORY_T1_PACKED)?.slots ?? []
|
|
257
|
+
const installed = toInstalledModules(moduleEntries)
|
|
258
|
+
const capabilities = computeFactoryCapabilities(installed, layout)
|
|
259
|
+
if (capabilities.generator) info.generator = capabilities.generator
|
|
260
|
+
if (capabilities.crafter) info.crafter = capabilities.crafter
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
info.modules = moduleEntries
|
|
264
|
+
|
|
265
|
+
const entityInfo = ServerContract.Types.entity_info.from(info)
|
|
266
|
+
return new Factory(entityInfo)
|
|
267
|
+
}
|
|
268
|
+
|
|
177
269
|
export function makeContainer(state: ContainerStateInput): Container {
|
|
178
270
|
const entityInfo = ServerContract.Types.entity_info.from({
|
|
179
271
|
type: Name.from('container'),
|
|
@@ -194,3 +286,21 @@ export function makeContainer(state: ContainerStateInput): Container {
|
|
|
194
286
|
})
|
|
195
287
|
return new Container(entityInfo)
|
|
196
288
|
}
|
|
289
|
+
|
|
290
|
+
export function makeNexus(state: NexusStateInput): Nexus {
|
|
291
|
+
const entityInfo = ServerContract.Types.entity_info.from({
|
|
292
|
+
type: Name.from('nexus'),
|
|
293
|
+
id: UInt64.from(state.id),
|
|
294
|
+
owner: Name.from(state.owner),
|
|
295
|
+
entity_name: state.name,
|
|
296
|
+
coordinates: ServerContract.Types.coordinates.from(state.coordinates),
|
|
297
|
+
cargomass: UInt32.from(0),
|
|
298
|
+
cargo: [],
|
|
299
|
+
modules: [],
|
|
300
|
+
is_idle: true,
|
|
301
|
+
current_task_elapsed: UInt32.from(0),
|
|
302
|
+
current_task_remaining: UInt32.from(0),
|
|
303
|
+
pending_tasks: [],
|
|
304
|
+
})
|
|
305
|
+
return new Nexus(entityInfo)
|
|
306
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type {UInt64Type} from '@wharfkit/antelope'
|
|
2
|
+
import {ServerContract} from '../contracts'
|
|
3
|
+
import type {CoordinatesType} from '../types'
|
|
4
|
+
import {Location} from './location'
|
|
5
|
+
|
|
6
|
+
export interface NexusStateInput {
|
|
7
|
+
id: UInt64Type
|
|
8
|
+
owner: string
|
|
9
|
+
name: string
|
|
10
|
+
coordinates: CoordinatesType | {x: number; y: number; z?: number}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class Nexus extends ServerContract.Types.entity_info {
|
|
14
|
+
get name(): string {
|
|
15
|
+
return this.entity_name
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get entityClass(): 'orbital' {
|
|
19
|
+
return 'orbital'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get location(): Location {
|
|
23
|
+
return Location.from(this.coordinates)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get orbitalAltitude(): number {
|
|
27
|
+
return this.coordinates.z?.toNumber() || 0
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -8,17 +8,22 @@ import {
|
|
|
8
8
|
MODULE_HAULER,
|
|
9
9
|
MODULE_LOADER,
|
|
10
10
|
} from '../capabilities/modules'
|
|
11
|
+
import {getItem} from '../data/catalog'
|
|
12
|
+
import type {EntitySlot} from '../data/recipes-runtime'
|
|
13
|
+
import {applySlotMultiplier, clampUint16, getSlotAmp, type InstalledModule} from './slot-multiplier'
|
|
14
|
+
|
|
15
|
+
export type {InstalledModule}
|
|
11
16
|
|
|
12
17
|
export function computeShipHullCapabilities(stats: Record<string, number>): {
|
|
13
18
|
hullmass: number
|
|
14
19
|
capacity: number
|
|
15
20
|
} {
|
|
16
|
-
const density = stats.density
|
|
17
|
-
const strength = stats.strength
|
|
18
|
-
const hardness = stats.hardness
|
|
19
|
-
const saturation = stats.saturation
|
|
21
|
+
const density = stats.density
|
|
22
|
+
const strength = stats.strength
|
|
23
|
+
const hardness = stats.hardness
|
|
24
|
+
const saturation = stats.saturation
|
|
20
25
|
|
|
21
|
-
const hullmass =
|
|
26
|
+
const hullmass = 100000 - 75 * density
|
|
22
27
|
const statSum = strength + hardness + saturation
|
|
23
28
|
const exponent = statSum / 2997.0
|
|
24
29
|
const capacity = Math.floor(1000000 * 10 ** exponent)
|
|
@@ -30,8 +35,8 @@ export function computeEngineCapabilities(stats: Record<string, number>): {
|
|
|
30
35
|
thrust: number
|
|
31
36
|
drain: number
|
|
32
37
|
} {
|
|
33
|
-
const vol = stats.volatility
|
|
34
|
-
const thm = stats.thermal
|
|
38
|
+
const vol = stats.volatility
|
|
39
|
+
const thm = stats.thermal
|
|
35
40
|
|
|
36
41
|
return {
|
|
37
42
|
thrust: 400 + Math.floor((vol * 3) / 4),
|
|
@@ -43,30 +48,61 @@ export function computeGeneratorCapabilities(stats: Record<string, number>): {
|
|
|
43
48
|
capacity: number
|
|
44
49
|
recharge: number
|
|
45
50
|
} {
|
|
46
|
-
const
|
|
47
|
-
const
|
|
51
|
+
const com = stats.composition
|
|
52
|
+
const fin = stats.fineness
|
|
48
53
|
|
|
49
54
|
return {
|
|
50
|
-
capacity: 300 + Math.floor(
|
|
51
|
-
recharge: 1 + Math.floor((
|
|
55
|
+
capacity: 300 + Math.floor(com / 6),
|
|
56
|
+
recharge: 1 + Math.floor((fin * 3) / 1000),
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface GathererDepthParams {
|
|
61
|
+
readonly floor: number
|
|
62
|
+
readonly slope: number
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const GATHERER_DEPTH_TABLE: readonly GathererDepthParams[] = [
|
|
66
|
+
{floor: 500, slope: 5},
|
|
67
|
+
{floor: 2000, slope: 11},
|
|
68
|
+
{floor: 7000, slope: 16},
|
|
69
|
+
{floor: 15000, slope: 18},
|
|
70
|
+
{floor: 25000, slope: 19},
|
|
71
|
+
{floor: 35000, slope: 16},
|
|
72
|
+
{floor: 46000, slope: 12},
|
|
73
|
+
{floor: 53500, slope: 10},
|
|
74
|
+
{floor: 60000, slope: 5},
|
|
75
|
+
{floor: 63500, slope: 2},
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
export const GATHERER_DEPTH_MAX_TIER = 10
|
|
79
|
+
|
|
80
|
+
export function gathererDepthForTier(tol: number, tier: number): number {
|
|
81
|
+
if (tier < 1 || tier > GATHERER_DEPTH_MAX_TIER) {
|
|
82
|
+
throw new Error(`gatherer tier out of range: ${tier}`)
|
|
52
83
|
}
|
|
84
|
+
const p = GATHERER_DEPTH_TABLE[tier - 1]
|
|
85
|
+
return p.floor + tol * p.slope
|
|
53
86
|
}
|
|
54
87
|
|
|
55
|
-
export function computeGathererCapabilities(
|
|
88
|
+
export function computeGathererCapabilities(
|
|
89
|
+
stats: Record<string, number>,
|
|
90
|
+
tier: number
|
|
91
|
+
): {
|
|
56
92
|
yield: number
|
|
57
93
|
drain: number
|
|
58
94
|
depth: number
|
|
59
95
|
speed: number
|
|
60
96
|
} {
|
|
61
|
-
const str = stats.strength
|
|
62
|
-
const con = stats.conductivity
|
|
63
|
-
const ref = stats.reflectivity
|
|
64
|
-
const tol = stats.tolerance
|
|
97
|
+
const str = stats.strength
|
|
98
|
+
const con = stats.conductivity
|
|
99
|
+
const ref = stats.reflectivity
|
|
100
|
+
const tol = stats.tolerance
|
|
65
101
|
|
|
66
102
|
return {
|
|
67
103
|
yield: 200 + str,
|
|
68
104
|
drain: Math.max(250, 1250 - Math.floor((con * 25) / 20)),
|
|
69
|
-
depth:
|
|
105
|
+
depth: gathererDepthForTier(tol, tier),
|
|
70
106
|
speed: 100 + Math.floor((ref * 4) / 5),
|
|
71
107
|
}
|
|
72
108
|
}
|
|
@@ -76,12 +112,12 @@ export function computeLoaderCapabilities(stats: Record<string, number>): {
|
|
|
76
112
|
thrust: number
|
|
77
113
|
quantity: number
|
|
78
114
|
} {
|
|
79
|
-
const
|
|
80
|
-
const
|
|
115
|
+
const insulation = stats.insulation
|
|
116
|
+
const plasticity = stats.plasticity
|
|
81
117
|
|
|
82
118
|
return {
|
|
83
|
-
mass: Math.max(200, 2000 - Math.floor(
|
|
84
|
-
thrust: 1 + Math.floor(
|
|
119
|
+
mass: Math.max(200, 2000 - Math.floor(insulation * 2)),
|
|
120
|
+
thrust: 1 + Math.floor(plasticity / 500),
|
|
85
121
|
quantity: 1,
|
|
86
122
|
}
|
|
87
123
|
}
|
|
@@ -90,12 +126,12 @@ export function computeCrafterCapabilities(stats: Record<string, number>): {
|
|
|
90
126
|
speed: number
|
|
91
127
|
drain: number
|
|
92
128
|
} {
|
|
93
|
-
const rea = stats.reactivity
|
|
94
|
-
const
|
|
129
|
+
const rea = stats.reactivity
|
|
130
|
+
const fin = stats.fineness
|
|
95
131
|
|
|
96
132
|
return {
|
|
97
133
|
speed: 100 + Math.floor((rea * 4) / 5),
|
|
98
|
-
drain: Math.max(5, 30 - Math.floor(
|
|
134
|
+
drain: Math.max(5, 30 - Math.floor(fin / 33)),
|
|
99
135
|
}
|
|
100
136
|
}
|
|
101
137
|
|
|
@@ -104,14 +140,14 @@ export function computeHaulerCapabilities(stats: Record<string, number>): {
|
|
|
104
140
|
efficiency: number
|
|
105
141
|
drain: number
|
|
106
142
|
} {
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
const
|
|
143
|
+
const fineness = stats.fineness
|
|
144
|
+
const conductivity = stats.conductivity
|
|
145
|
+
const composition = stats.composition
|
|
110
146
|
|
|
111
147
|
return {
|
|
112
|
-
capacity: Math.max(1, 1 + Math.floor(
|
|
113
|
-
efficiency: 2000 +
|
|
114
|
-
drain: Math.max(3, 15 - Math.floor(
|
|
148
|
+
capacity: Math.max(1, 1 + Math.floor(fineness / 400)),
|
|
149
|
+
efficiency: 2000 + conductivity * 6,
|
|
150
|
+
drain: Math.max(3, 15 - Math.floor(composition / 80)),
|
|
115
151
|
}
|
|
116
152
|
}
|
|
117
153
|
|
|
@@ -121,11 +157,12 @@ export function computeStorageCapabilities(
|
|
|
121
157
|
): {
|
|
122
158
|
capacityBonus: number
|
|
123
159
|
} {
|
|
124
|
-
const strength = stats.strength
|
|
125
|
-
const
|
|
126
|
-
const
|
|
160
|
+
const strength = stats.strength
|
|
161
|
+
const density = stats.density
|
|
162
|
+
const hardness = stats.hardness
|
|
163
|
+
const saturation = stats.saturation
|
|
127
164
|
|
|
128
|
-
const statSum = strength + hardness + saturation
|
|
165
|
+
const statSum = strength + density + hardness + saturation
|
|
129
166
|
const capacityBonus = Math.floor(
|
|
130
167
|
(baseCapacity * (10 + Math.floor((statSum * 10) / 2997))) / 100
|
|
131
168
|
)
|
|
@@ -137,12 +174,12 @@ export function computeWarehouseHullCapabilities(stats: Record<string, number>):
|
|
|
137
174
|
hullmass: number
|
|
138
175
|
capacity: number
|
|
139
176
|
} {
|
|
140
|
-
const density = stats.density
|
|
141
|
-
const strength = stats.strength
|
|
142
|
-
const hardness = stats.hardness
|
|
143
|
-
const saturation = stats.saturation
|
|
177
|
+
const density = stats.density
|
|
178
|
+
const strength = stats.strength
|
|
179
|
+
const hardness = stats.hardness
|
|
180
|
+
const saturation = stats.saturation
|
|
144
181
|
|
|
145
|
-
const hullmass =
|
|
182
|
+
const hullmass = 100000 - 75 * density
|
|
146
183
|
const statSum = strength + hardness + saturation
|
|
147
184
|
const exponent = statSum / 2997.0
|
|
148
185
|
const capacity = Math.floor(20000000 * 10 ** exponent)
|
|
@@ -160,7 +197,8 @@ export interface ShipCapabilities {
|
|
|
160
197
|
}
|
|
161
198
|
|
|
162
199
|
export function computeShipCapabilities(
|
|
163
|
-
modules:
|
|
200
|
+
modules: InstalledModule[],
|
|
201
|
+
layout: EntitySlot[]
|
|
164
202
|
): ShipCapabilities {
|
|
165
203
|
const ship: ShipCapabilities = {}
|
|
166
204
|
|
|
@@ -170,7 +208,7 @@ export function computeShipCapabilities(
|
|
|
170
208
|
let totalDrain = 0
|
|
171
209
|
for (const m of engineModules) {
|
|
172
210
|
const caps = computeEngineCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
|
|
173
|
-
totalThrust += caps.thrust
|
|
211
|
+
totalThrust += applySlotMultiplier(caps.thrust, getSlotAmp(layout, m.slotIndex))
|
|
174
212
|
totalDrain += caps.drain
|
|
175
213
|
}
|
|
176
214
|
ship.engines = {thrust: totalThrust, drain: totalDrain}
|
|
@@ -184,10 +222,14 @@ export function computeShipCapabilities(
|
|
|
184
222
|
let totalRecharge = 0
|
|
185
223
|
for (const m of generatorModules) {
|
|
186
224
|
const caps = computeGeneratorCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
|
|
187
|
-
|
|
188
|
-
|
|
225
|
+
const amp = getSlotAmp(layout, m.slotIndex)
|
|
226
|
+
totalCapacity += applySlotMultiplier(caps.capacity, amp)
|
|
227
|
+
totalRecharge += applySlotMultiplier(caps.recharge, amp)
|
|
228
|
+
}
|
|
229
|
+
ship.generator = {
|
|
230
|
+
capacity: clampUint16(totalCapacity),
|
|
231
|
+
recharge: clampUint16(totalRecharge),
|
|
189
232
|
}
|
|
190
|
-
ship.generator = {capacity: totalCapacity, recharge: totalRecharge}
|
|
191
233
|
}
|
|
192
234
|
|
|
193
235
|
const gathererModules = modules.filter(
|
|
@@ -196,16 +238,26 @@ export function computeShipCapabilities(
|
|
|
196
238
|
if (gathererModules.length > 0) {
|
|
197
239
|
let totalYield = 0
|
|
198
240
|
let totalDrain = 0
|
|
199
|
-
let
|
|
241
|
+
let maxDepth = 0
|
|
200
242
|
let totalSpeed = 0
|
|
201
243
|
for (const m of gathererModules) {
|
|
202
|
-
const
|
|
203
|
-
|
|
244
|
+
const tier = getItem(m.itemId).tier
|
|
245
|
+
const caps = computeGathererCapabilities(
|
|
246
|
+
decodeCraftedItemStats(m.itemId, m.stats),
|
|
247
|
+
tier
|
|
248
|
+
)
|
|
249
|
+
const amp = getSlotAmp(layout, m.slotIndex)
|
|
250
|
+
totalYield += applySlotMultiplier(caps.yield, amp)
|
|
204
251
|
totalDrain += caps.drain
|
|
205
|
-
|
|
206
|
-
totalSpeed += caps.speed
|
|
252
|
+
if (caps.depth > maxDepth) maxDepth = caps.depth
|
|
253
|
+
totalSpeed += applySlotMultiplier(caps.speed, amp)
|
|
254
|
+
}
|
|
255
|
+
ship.gatherer = {
|
|
256
|
+
yield: clampUint16(totalYield),
|
|
257
|
+
drain: totalDrain,
|
|
258
|
+
depth: maxDepth,
|
|
259
|
+
speed: clampUint16(totalSpeed),
|
|
207
260
|
}
|
|
208
|
-
ship.gatherer = {yield: totalYield, drain: totalDrain, depth: totalDepth, speed: totalSpeed}
|
|
209
261
|
}
|
|
210
262
|
|
|
211
263
|
const haulerModules = modules.filter((m) => getModuleCapabilityType(m.itemId) === MODULE_HAULER)
|
|
@@ -215,13 +267,15 @@ export function computeShipCapabilities(
|
|
|
215
267
|
let totalDrain = 0
|
|
216
268
|
for (const m of haulerModules) {
|
|
217
269
|
const caps = computeHaulerCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
|
|
270
|
+
const eff = applySlotMultiplier(caps.efficiency, getSlotAmp(layout, m.slotIndex))
|
|
218
271
|
totalCapacity += caps.capacity
|
|
219
|
-
weightedEffNum +=
|
|
272
|
+
weightedEffNum += eff * caps.capacity
|
|
220
273
|
totalDrain += caps.drain
|
|
221
274
|
}
|
|
275
|
+
const efficiency = totalCapacity > 0 ? Math.floor(weightedEffNum / totalCapacity) : 0
|
|
222
276
|
ship.hauler = {
|
|
223
277
|
capacity: totalCapacity,
|
|
224
|
-
efficiency:
|
|
278
|
+
efficiency: clampUint16(efficiency),
|
|
225
279
|
drain: totalDrain,
|
|
226
280
|
}
|
|
227
281
|
}
|
|
@@ -234,10 +288,14 @@ export function computeShipCapabilities(
|
|
|
234
288
|
for (const m of loaderModules) {
|
|
235
289
|
const caps = computeLoaderCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
|
|
236
290
|
totalMass += caps.mass
|
|
237
|
-
totalThrust += caps.thrust
|
|
291
|
+
totalThrust += applySlotMultiplier(caps.thrust, getSlotAmp(layout, m.slotIndex))
|
|
238
292
|
totalQuantity += caps.quantity
|
|
239
293
|
}
|
|
240
|
-
ship.loaders = {
|
|
294
|
+
ship.loaders = {
|
|
295
|
+
mass: totalMass,
|
|
296
|
+
thrust: clampUint16(totalThrust),
|
|
297
|
+
quantity: totalQuantity,
|
|
298
|
+
}
|
|
241
299
|
}
|
|
242
300
|
|
|
243
301
|
const crafterModules = modules.filter(
|
|
@@ -248,10 +306,10 @@ export function computeShipCapabilities(
|
|
|
248
306
|
let totalDrain = 0
|
|
249
307
|
for (const m of crafterModules) {
|
|
250
308
|
const caps = computeCrafterCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
|
|
251
|
-
totalSpeed += caps.speed
|
|
309
|
+
totalSpeed += applySlotMultiplier(caps.speed, getSlotAmp(layout, m.slotIndex))
|
|
252
310
|
totalDrain += caps.drain
|
|
253
311
|
}
|
|
254
|
-
ship.crafter = {speed: totalSpeed, drain: totalDrain}
|
|
312
|
+
ship.crafter = {speed: clampUint16(totalSpeed), drain: totalDrain}
|
|
255
313
|
}
|
|
256
314
|
|
|
257
315
|
return ship
|
package/src/entities/ship.ts
CHANGED
|
@@ -2,7 +2,9 @@ import {type UInt16, type UInt16Type, UInt32, UInt64, type UInt64Type} from '@wh
|
|
|
2
2
|
import {ServerContract} from '../contracts'
|
|
3
3
|
import {Coordinates, type CoordinatesType} from '../types'
|
|
4
4
|
import {
|
|
5
|
+
type FloatPosition,
|
|
5
6
|
getDestinationLocation,
|
|
7
|
+
getInterpolatedPosition,
|
|
6
8
|
getPositionAt,
|
|
7
9
|
getFlightOrigin as travelGetFlightOrigin,
|
|
8
10
|
} from '../travel/travel'
|
|
@@ -55,6 +57,14 @@ export class Ship extends ServerContract.Types.entity_info {
|
|
|
55
57
|
return this.entity_name
|
|
56
58
|
}
|
|
57
59
|
|
|
60
|
+
get entityClass(): 'orbital' {
|
|
61
|
+
return 'orbital'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get canUndeploy(): boolean {
|
|
65
|
+
return true
|
|
66
|
+
}
|
|
67
|
+
|
|
58
68
|
get inv(): InventoryAccessor {
|
|
59
69
|
this._inv ??= new InventoryAccessor(this)
|
|
60
70
|
return this._inv
|
|
@@ -87,12 +97,19 @@ export class Ship extends ServerContract.Types.entity_info {
|
|
|
87
97
|
return dest ? Coordinates.from(dest) : undefined
|
|
88
98
|
}
|
|
89
99
|
|
|
100
|
+
/** Chain-tile coordinates at `now`. For smooth visual position use interpolatedPositionAt. */
|
|
90
101
|
positionAt(now: Date): Coordinates {
|
|
91
102
|
const taskIndex = this.sched.currentTaskIndex(now)
|
|
92
103
|
const progress = this.sched.currentTaskProgress(now)
|
|
93
104
|
return Coordinates.from(getPositionAt(this, taskIndex, progress))
|
|
94
105
|
}
|
|
95
106
|
|
|
107
|
+
interpolatedPositionAt(now: Date): FloatPosition {
|
|
108
|
+
const taskIndex = this.sched.currentTaskIndex(now)
|
|
109
|
+
const progress = this.sched.currentTaskProgressFloat(now)
|
|
110
|
+
return getInterpolatedPosition(this, taskIndex, progress)
|
|
111
|
+
}
|
|
112
|
+
|
|
96
113
|
isInFlight(now: Date): boolean {
|
|
97
114
|
return schedule.isInFlight(this, now)
|
|
98
115
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type {EntitySlot} from '../data/recipes-runtime'
|
|
2
|
+
|
|
3
|
+
export const U16_MAX = 65535
|
|
4
|
+
|
|
5
|
+
export interface InstalledModule {
|
|
6
|
+
slotIndex: number
|
|
7
|
+
itemId: number
|
|
8
|
+
stats: bigint
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function clampUint16(value: number): number {
|
|
12
|
+
return Math.min(value, U16_MAX)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function applySlotMultiplier(value: number, outputPct: number): number {
|
|
16
|
+
return clampUint16(Math.floor((value * outputPct) / 100))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getSlotAmp(layout: EntitySlot[], slotIndex: number): number {
|
|
20
|
+
return layout[slotIndex]?.outputPct ?? 100
|
|
21
|
+
}
|