@shipload/sdk 0.7.0 → 1.0.0-beta1
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 +2730 -287
- package/lib/shipload.js +10862 -2228
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +10434 -2170
- package/lib/shipload.m.js.map +1 -1
- package/package.json +11 -20
- package/src/capabilities/crafting.ts +22 -0
- package/src/capabilities/gathering.ts +36 -0
- package/src/capabilities/guards.ts +38 -0
- package/src/capabilities/hauling.ts +22 -0
- package/src/capabilities/index.ts +8 -0
- package/src/capabilities/loading.ts +8 -0
- package/src/capabilities/modules.ts +86 -0
- package/src/capabilities/movement.ts +29 -0
- package/src/capabilities/storage.ts +159 -0
- package/src/contracts/platform.ts +30 -30
- package/src/contracts/server.ts +1387 -283
- package/src/data/capabilities.ts +408 -0
- package/src/data/catalog.ts +135 -0
- package/src/data/categories.ts +55 -0
- package/src/data/colors.ts +84 -0
- package/src/data/entities.json +50 -0
- package/src/data/item-ids.ts +75 -0
- package/src/data/items.json +252 -0
- package/src/data/locations.ts +53 -0
- package/src/data/metadata.ts +208 -0
- package/src/data/nebula-adjectives.json +211 -0
- package/src/data/nebula-nouns.json +151 -0
- package/src/data/recipes-runtime.ts +65 -0
- package/src/data/recipes.json +878 -0
- package/src/data/syllables.json +1790 -0
- package/src/data/tiers.ts +45 -0
- package/src/derivation/crafting.ts +350 -0
- package/src/derivation/index.ts +32 -0
- package/src/derivation/location-size.ts +15 -0
- package/src/derivation/resources.ts +112 -0
- package/src/derivation/stats.ts +146 -0
- package/src/derivation/strata.ts +43 -0
- package/src/derivation/stratum.ts +134 -0
- package/src/derivation/tiers.ts +54 -0
- package/src/entities/cargo-utils.ts +84 -0
- package/src/entities/container.ts +108 -0
- package/src/entities/entity-inventory.ts +39 -0
- package/src/entities/gamestate.ts +152 -0
- package/src/entities/inventory-accessor.ts +42 -0
- package/src/entities/location.ts +60 -0
- package/src/entities/makers.ts +196 -0
- package/src/entities/player.ts +15 -0
- package/src/entities/ship-deploy.ts +258 -0
- package/src/entities/ship.ts +204 -0
- package/src/entities/warehouse.ts +119 -0
- package/src/errors.ts +100 -9
- package/src/format.ts +12 -0
- package/src/index-module.ts +317 -7
- package/src/managers/actions.ts +250 -0
- package/src/managers/base.ts +25 -0
- package/src/managers/context.ts +114 -0
- package/src/managers/entities.ts +103 -0
- package/src/managers/epochs.ts +47 -0
- package/src/managers/index.ts +9 -0
- package/src/managers/locations.ts +68 -0
- package/src/managers/players.ts +13 -0
- package/src/nft/description.ts +176 -0
- package/src/nft/deserializers.ts +83 -0
- package/src/nft/index.ts +2 -0
- package/src/resolution/describe-module.ts +166 -0
- package/src/resolution/display-name.ts +39 -0
- package/src/resolution/resolve-item.ts +358 -0
- package/src/scheduling/accessor.ts +82 -0
- package/src/{epoch.ts → scheduling/epoch.ts} +1 -1
- package/src/scheduling/projection.ts +463 -0
- package/src/scheduling/schedule.ts +179 -0
- package/src/shipload.ts +47 -160
- package/src/subscriptions/connection.ts +154 -0
- package/src/subscriptions/debug.ts +17 -0
- package/src/subscriptions/index.ts +5 -0
- package/src/subscriptions/manager.ts +240 -0
- package/src/subscriptions/mappers.ts +28 -0
- package/src/subscriptions/types.ts +143 -0
- package/src/travel/travel.ts +500 -0
- package/src/types/capabilities.ts +76 -0
- package/src/types/entity-traits.ts +69 -0
- package/src/types/entity.ts +39 -0
- package/src/types/index.ts +3 -0
- package/src/types.ts +140 -35
- package/src/{hash.ts → utils/hash.ts} +2 -2
- package/src/utils/system.ts +168 -0
- package/src/goods.ts +0 -124
- package/src/market.ts +0 -214
- package/src/rolls.ts +0 -8
- package/src/ship.ts +0 -36
- package/src/state.ts +0 -0
- package/src/syllables.ts +0 -1184
- package/src/system.ts +0 -36
- package/src/travel.ts +0 -259
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {Checksum256, type Checksum256Type, type UInt16Type, type UInt64} from '@wharfkit/antelope'
|
|
2
|
+
import {Coordinates, type CoordinatesType, type Distance, type LocationType} from '../types'
|
|
3
|
+
import {getLocationType, hasSystem, isGatherableLocation} from '../utils/system'
|
|
4
|
+
import {findNearbyPlanets} from '../travel/travel'
|
|
5
|
+
|
|
6
|
+
export class Location {
|
|
7
|
+
readonly coordinates: Coordinates
|
|
8
|
+
private _gameSeed?: Checksum256
|
|
9
|
+
private _hasSystem?: boolean
|
|
10
|
+
private _epoch?: UInt64
|
|
11
|
+
|
|
12
|
+
constructor(coordinates: CoordinatesType) {
|
|
13
|
+
this.coordinates = Coordinates.from(coordinates)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
static from(coordinates: CoordinatesType): Location {
|
|
17
|
+
return new Location(Coordinates.from(coordinates))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
hasSystemAt(gameSeed: Checksum256Type): boolean {
|
|
21
|
+
const seed = Checksum256.from(gameSeed)
|
|
22
|
+
if (this._hasSystem === undefined || !this._gameSeed?.equals(seed)) {
|
|
23
|
+
this._gameSeed = seed
|
|
24
|
+
this._hasSystem = hasSystem(seed, this.coordinates)
|
|
25
|
+
}
|
|
26
|
+
return this._hasSystem
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
getLocationTypeAt(gameSeed: Checksum256Type): LocationType {
|
|
30
|
+
return getLocationType(gameSeed, this.coordinates)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
isGatherableAt(gameSeed: Checksum256Type): boolean {
|
|
34
|
+
return isGatherableLocation(this.getLocationTypeAt(gameSeed))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
findNearby(gameSeed: Checksum256Type, maxDistance: UInt16Type = 20): Distance[] {
|
|
38
|
+
return findNearbyPlanets(Checksum256.from(gameSeed), this.coordinates, maxDistance)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
equals(other: CoordinatesType | Location): boolean {
|
|
42
|
+
const otherCoords = other instanceof Location ? other.coordinates : Coordinates.from(other)
|
|
43
|
+
return this.coordinates.equals(otherCoords)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get epoch(): UInt64 | undefined {
|
|
47
|
+
return this._epoch
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
clearCache(): void {
|
|
51
|
+
this._epoch = undefined
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function toLocation(coords: CoordinatesType | Location): Location {
|
|
56
|
+
if (coords instanceof Location) {
|
|
57
|
+
return coords
|
|
58
|
+
}
|
|
59
|
+
return Location.from(coords)
|
|
60
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import {Name, UInt16, UInt32, UInt64, UInt8} from '@wharfkit/antelope'
|
|
2
|
+
import {ServerContract} from '../contracts'
|
|
3
|
+
import {type PackedModuleInput, Ship, type ShipStateInput} from './ship'
|
|
4
|
+
import {computeWarehouseCapabilities, Warehouse, type WarehouseStateInput} from './warehouse'
|
|
5
|
+
import {Container, type ContainerStateInput} from './container'
|
|
6
|
+
import {ITEM_SHIP_T1_PACKED, ITEM_WAREHOUSE_T1_PACKED} from '../data/item-ids'
|
|
7
|
+
import {getEntityLayout} from '../data/recipes-runtime'
|
|
8
|
+
import {itemMetadata} from '../data/metadata'
|
|
9
|
+
import {getItem} from '../data/catalog'
|
|
10
|
+
import {
|
|
11
|
+
getModuleCapabilityType,
|
|
12
|
+
MODULE_STORAGE,
|
|
13
|
+
moduleAccepts,
|
|
14
|
+
moduleSlotTypeToCode,
|
|
15
|
+
} from '../capabilities/modules'
|
|
16
|
+
import {computeShipCapabilities, computeStorageCapabilities} from './ship-deploy'
|
|
17
|
+
import {decodeCraftedItemStats} from '../derivation/crafting'
|
|
18
|
+
|
|
19
|
+
function assignModulesToSlots(
|
|
20
|
+
packedEntityItemId: number,
|
|
21
|
+
modules: PackedModuleInput[],
|
|
22
|
+
entityLabel: string
|
|
23
|
+
): ServerContract.Types.module_entry[] {
|
|
24
|
+
const layout = getEntityLayout(packedEntityItemId)
|
|
25
|
+
const slots = layout?.slots ?? []
|
|
26
|
+
const result: Array<{type: number; installed?: ServerContract.Types.packed_module}> = slots.map(
|
|
27
|
+
(s) => ({type: moduleSlotTypeToCode(s.type), installed: undefined})
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
for (const mod of modules) {
|
|
31
|
+
const itemId = Number(UInt16.from(mod.itemId).value.toString())
|
|
32
|
+
const modType = getModuleCapabilityType(itemId)
|
|
33
|
+
const slotIdx = result.findIndex((r) => !r.installed && moduleAccepts(r.type, modType))
|
|
34
|
+
if (slotIdx === -1) {
|
|
35
|
+
let modName: string
|
|
36
|
+
try {
|
|
37
|
+
modName = getItem(itemId).name
|
|
38
|
+
} catch {
|
|
39
|
+
modName = itemMetadata[itemId]?.name ?? `item ${itemId}`
|
|
40
|
+
}
|
|
41
|
+
throw new Error(
|
|
42
|
+
`No compatible slot for module ${modName} (type ${modType}) on ${entityLabel}`
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
result[slotIdx].installed = ServerContract.Types.packed_module.from({
|
|
46
|
+
item_id: UInt16.from(mod.itemId),
|
|
47
|
+
stats: UInt64.from(mod.stats),
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return result.map((r) =>
|
|
52
|
+
ServerContract.Types.module_entry.from({
|
|
53
|
+
type: UInt8.from(r.type),
|
|
54
|
+
installed: r.installed,
|
|
55
|
+
})
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function decodePackedInput(m: PackedModuleInput): {itemId: number; stats: bigint} {
|
|
60
|
+
return {
|
|
61
|
+
itemId: Number(UInt16.from(m.itemId).value.toString()),
|
|
62
|
+
stats: BigInt(UInt64.from(m.stats).toString()),
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function computeStorageBonus(
|
|
67
|
+
decoded: {itemId: number; stats: bigint}[],
|
|
68
|
+
baseCapacity: number
|
|
69
|
+
): number {
|
|
70
|
+
let totalBonus = 0
|
|
71
|
+
for (const m of decoded) {
|
|
72
|
+
if (getModuleCapabilityType(m.itemId) !== MODULE_STORAGE) continue
|
|
73
|
+
const stats = decodeCraftedItemStats(m.itemId, m.stats)
|
|
74
|
+
const {capacityBonus} = computeStorageCapabilities(stats, baseCapacity)
|
|
75
|
+
totalBonus += capacityBonus
|
|
76
|
+
}
|
|
77
|
+
return totalBonus
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function deriveShipFromModules(
|
|
81
|
+
modules: PackedModuleInput[],
|
|
82
|
+
baseCapacity: number
|
|
83
|
+
): {
|
|
84
|
+
capabilities: ReturnType<typeof computeShipCapabilities>
|
|
85
|
+
finalCapacity: number
|
|
86
|
+
} {
|
|
87
|
+
const decoded = modules.map(decodePackedInput)
|
|
88
|
+
const capabilities = computeShipCapabilities(decoded)
|
|
89
|
+
const totalBonus = computeStorageBonus(decoded, baseCapacity)
|
|
90
|
+
return {capabilities, finalCapacity: baseCapacity + totalBonus}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function makeShip(state: ShipStateInput): Ship {
|
|
94
|
+
const info: Record<string, unknown> = {
|
|
95
|
+
type: Name.from('ship'),
|
|
96
|
+
id: UInt64.from(state.id),
|
|
97
|
+
owner: Name.from(state.owner),
|
|
98
|
+
entity_name: state.name,
|
|
99
|
+
coordinates: ServerContract.Types.coordinates.from(state.coordinates),
|
|
100
|
+
cargomass: UInt32.from(0),
|
|
101
|
+
cargo: state.cargo || [],
|
|
102
|
+
is_idle: !state.schedule,
|
|
103
|
+
current_task_elapsed: UInt32.from(0),
|
|
104
|
+
current_task_remaining: UInt32.from(0),
|
|
105
|
+
pending_tasks: [],
|
|
106
|
+
}
|
|
107
|
+
if (state.hullmass !== undefined) info.hullmass = UInt32.from(state.hullmass)
|
|
108
|
+
if (state.energy !== undefined) info.energy = UInt16.from(state.energy)
|
|
109
|
+
if (state.schedule) info.schedule = state.schedule
|
|
110
|
+
|
|
111
|
+
let moduleEntries: ServerContract.Types.module_entry[] = []
|
|
112
|
+
if (state.modules && state.modules.length > 0) {
|
|
113
|
+
moduleEntries = assignModulesToSlots(ITEM_SHIP_T1_PACKED, state.modules, 'Ship T1')
|
|
114
|
+
const {capabilities, finalCapacity} = deriveShipFromModules(
|
|
115
|
+
state.modules,
|
|
116
|
+
state.capacity ?? 0
|
|
117
|
+
)
|
|
118
|
+
if (capabilities.engines) info.engines = capabilities.engines
|
|
119
|
+
if (capabilities.generator) info.generator = capabilities.generator
|
|
120
|
+
if (capabilities.gatherer) info.gatherer = capabilities.gatherer
|
|
121
|
+
if (capabilities.hauler) info.hauler = capabilities.hauler
|
|
122
|
+
if (capabilities.loaders) info.loaders = capabilities.loaders
|
|
123
|
+
if (capabilities.crafter) info.crafter = capabilities.crafter
|
|
124
|
+
if (state.capacity !== undefined) info.capacity = UInt32.from(finalCapacity)
|
|
125
|
+
} else {
|
|
126
|
+
moduleEntries = assignModulesToSlots(ITEM_SHIP_T1_PACKED, [], 'Ship T1')
|
|
127
|
+
if (state.capacity !== undefined) info.capacity = UInt32.from(state.capacity)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
info.modules = moduleEntries
|
|
131
|
+
|
|
132
|
+
const entityInfo = ServerContract.Types.entity_info.from(info)
|
|
133
|
+
return new Ship(entityInfo)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function makeWarehouse(state: WarehouseStateInput): Warehouse {
|
|
137
|
+
const info: Record<string, unknown> = {
|
|
138
|
+
type: Name.from('warehouse'),
|
|
139
|
+
id: UInt64.from(state.id),
|
|
140
|
+
owner: Name.from(state.owner),
|
|
141
|
+
entity_name: state.name,
|
|
142
|
+
coordinates: ServerContract.Types.coordinates.from(state.coordinates),
|
|
143
|
+
capacity: UInt32.from(state.capacity),
|
|
144
|
+
cargomass: UInt32.from(0),
|
|
145
|
+
cargo: state.cargo || [],
|
|
146
|
+
is_idle: !state.schedule,
|
|
147
|
+
current_task_elapsed: UInt32.from(0),
|
|
148
|
+
current_task_remaining: UInt32.from(0),
|
|
149
|
+
pending_tasks: [],
|
|
150
|
+
}
|
|
151
|
+
if (state.hullmass !== undefined) info.hullmass = UInt32.from(state.hullmass)
|
|
152
|
+
if (state.schedule) info.schedule = state.schedule
|
|
153
|
+
|
|
154
|
+
let moduleEntries: ServerContract.Types.module_entry[] = []
|
|
155
|
+
if (state.modules && state.modules.length > 0) {
|
|
156
|
+
moduleEntries = assignModulesToSlots(
|
|
157
|
+
ITEM_WAREHOUSE_T1_PACKED,
|
|
158
|
+
state.modules,
|
|
159
|
+
'Warehouse T1'
|
|
160
|
+
)
|
|
161
|
+
const decoded = state.modules.map(decodePackedInput)
|
|
162
|
+
const capabilities = computeWarehouseCapabilities(decoded)
|
|
163
|
+
if (capabilities.loaders) info.loaders = capabilities.loaders
|
|
164
|
+
|
|
165
|
+
const totalBonus = computeStorageBonus(decoded, state.capacity)
|
|
166
|
+
info.capacity = UInt32.from(state.capacity + totalBonus)
|
|
167
|
+
} else {
|
|
168
|
+
moduleEntries = assignModulesToSlots(ITEM_WAREHOUSE_T1_PACKED, [], 'Warehouse T1')
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
info.modules = moduleEntries
|
|
172
|
+
|
|
173
|
+
const entityInfo = ServerContract.Types.entity_info.from(info)
|
|
174
|
+
return new Warehouse(entityInfo)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function makeContainer(state: ContainerStateInput): Container {
|
|
178
|
+
const entityInfo = ServerContract.Types.entity_info.from({
|
|
179
|
+
type: Name.from('container'),
|
|
180
|
+
id: UInt64.from(state.id),
|
|
181
|
+
owner: Name.from(state.owner),
|
|
182
|
+
entity_name: state.name,
|
|
183
|
+
coordinates: ServerContract.Types.coordinates.from(state.coordinates),
|
|
184
|
+
hullmass: UInt32.from(state.hullmass),
|
|
185
|
+
capacity: UInt32.from(state.capacity),
|
|
186
|
+
cargomass: UInt32.from(state.cargomass || 0),
|
|
187
|
+
cargo: state.cargo || [],
|
|
188
|
+
modules: [],
|
|
189
|
+
is_idle: !state.schedule,
|
|
190
|
+
current_task_elapsed: UInt32.from(0),
|
|
191
|
+
current_task_remaining: UInt32.from(0),
|
|
192
|
+
pending_tasks: [],
|
|
193
|
+
schedule: state.schedule,
|
|
194
|
+
})
|
|
195
|
+
return new Container(entityInfo)
|
|
196
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {Name, type NameType} from '@wharfkit/antelope'
|
|
2
|
+
import {ServerContract} from '../contracts'
|
|
3
|
+
|
|
4
|
+
export interface PlayerStateInput {
|
|
5
|
+
owner: NameType
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class Player extends ServerContract.Types.player_row {
|
|
9
|
+
static fromState(state: PlayerStateInput): Player {
|
|
10
|
+
const playerRow = ServerContract.Types.player_row.from({
|
|
11
|
+
owner: Name.from(state.owner),
|
|
12
|
+
})
|
|
13
|
+
return new Player(playerRow)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
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 ?? 500
|
|
17
|
+
const strength = stats.strength ?? 500
|
|
18
|
+
const hardness = stats.hardness ?? 500
|
|
19
|
+
const saturation = stats.saturation ?? 500
|
|
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 ?? 500
|
|
34
|
+
const thm = stats.thermal ?? 500
|
|
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 res = stats.resonance ?? 500
|
|
47
|
+
const ref = stats.reflectivity ?? 500
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
capacity: 300 + Math.floor(res / 6),
|
|
51
|
+
recharge: 1 + Math.floor((ref * 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 ?? 500
|
|
62
|
+
const con = stats.conductivity ?? 500
|
|
63
|
+
const ref = stats.reflectivity ?? 500
|
|
64
|
+
const tol = stats.tolerance ?? 500
|
|
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 hrd = stats.hardness ?? 500
|
|
80
|
+
const pla = stats.plasticity ?? 500
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
mass: Math.max(200, 2000 - Math.floor(hrd * 2)),
|
|
84
|
+
thrust: 1 + Math.floor(pla / 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 ?? 500
|
|
94
|
+
const com = stats.composition ?? 500
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
speed: 100 + Math.floor((rea * 4) / 5),
|
|
98
|
+
drain: Math.max(5, 30 - Math.floor(com / 33)),
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function computeHaulerCapabilities(stats: Record<string, number>): {
|
|
103
|
+
capacity: number
|
|
104
|
+
efficiency: number
|
|
105
|
+
drain: number
|
|
106
|
+
} {
|
|
107
|
+
const res = stats.resonance ?? 500
|
|
108
|
+
const con = stats.conductivity ?? 500
|
|
109
|
+
const ref = stats.reflectivity ?? 500
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
capacity: Math.max(1, 1 + Math.floor(res / 400)),
|
|
113
|
+
efficiency: 2000 + con * 6,
|
|
114
|
+
drain: Math.max(3, 15 - Math.floor(ref / 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 ?? 500
|
|
125
|
+
const hardness = stats.hardness ?? 500
|
|
126
|
+
const saturation = stats.saturation ?? 500
|
|
127
|
+
|
|
128
|
+
const statSum = strength + hardness + saturation
|
|
129
|
+
const capacityBonus = Math.floor(
|
|
130
|
+
(baseCapacity * (10 + Math.floor((statSum * 10) / 2997))) / 100
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
return {capacityBonus}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function computeWarehouseHullCapabilities(stats: Record<string, number>): {
|
|
137
|
+
hullmass: number
|
|
138
|
+
capacity: number
|
|
139
|
+
} {
|
|
140
|
+
const density = stats.density ?? 500
|
|
141
|
+
const strength = stats.strength ?? 500
|
|
142
|
+
const hardness = stats.hardness ?? 500
|
|
143
|
+
const saturation = stats.saturation ?? 500
|
|
144
|
+
|
|
145
|
+
const hullmass = 25000 + 75 * density
|
|
146
|
+
const statSum = strength + hardness + saturation
|
|
147
|
+
const exponent = statSum / 2997.0
|
|
148
|
+
const capacity = Math.floor(20000000 * 10 ** exponent)
|
|
149
|
+
|
|
150
|
+
return {hullmass, capacity}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export interface ShipCapabilities {
|
|
154
|
+
engines?: {thrust: number; drain: number}
|
|
155
|
+
generator?: {capacity: number; recharge: number}
|
|
156
|
+
gatherer?: {yield: number; drain: number; depth: number; speed: number}
|
|
157
|
+
hauler?: {capacity: number; efficiency: number; drain: number}
|
|
158
|
+
loaders?: {mass: number; thrust: number; quantity: number}
|
|
159
|
+
crafter?: {speed: number; drain: number}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function computeShipCapabilities(
|
|
163
|
+
modules: {itemId: number; stats: bigint}[]
|
|
164
|
+
): ShipCapabilities {
|
|
165
|
+
const ship: ShipCapabilities = {}
|
|
166
|
+
|
|
167
|
+
const engineModules = modules.filter((m) => getModuleCapabilityType(m.itemId) === MODULE_ENGINE)
|
|
168
|
+
if (engineModules.length > 0) {
|
|
169
|
+
let totalThrust = 0
|
|
170
|
+
let totalDrain = 0
|
|
171
|
+
for (const m of engineModules) {
|
|
172
|
+
const caps = computeEngineCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
|
|
173
|
+
totalThrust += caps.thrust
|
|
174
|
+
totalDrain += caps.drain
|
|
175
|
+
}
|
|
176
|
+
ship.engines = {thrust: totalThrust, drain: totalDrain}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const generatorModules = modules.filter(
|
|
180
|
+
(m) => getModuleCapabilityType(m.itemId) === MODULE_GENERATOR
|
|
181
|
+
)
|
|
182
|
+
if (generatorModules.length > 0) {
|
|
183
|
+
let totalCapacity = 0
|
|
184
|
+
let totalRecharge = 0
|
|
185
|
+
for (const m of generatorModules) {
|
|
186
|
+
const caps = computeGeneratorCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
|
|
187
|
+
totalCapacity += caps.capacity
|
|
188
|
+
totalRecharge += caps.recharge
|
|
189
|
+
}
|
|
190
|
+
ship.generator = {capacity: totalCapacity, recharge: totalRecharge}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const gathererModules = modules.filter(
|
|
194
|
+
(m) => getModuleCapabilityType(m.itemId) === MODULE_GATHERER
|
|
195
|
+
)
|
|
196
|
+
if (gathererModules.length > 0) {
|
|
197
|
+
let totalYield = 0
|
|
198
|
+
let totalDrain = 0
|
|
199
|
+
let totalDepth = 0
|
|
200
|
+
let totalSpeed = 0
|
|
201
|
+
for (const m of gathererModules) {
|
|
202
|
+
const caps = computeGathererCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
|
|
203
|
+
totalYield += caps.yield
|
|
204
|
+
totalDrain += caps.drain
|
|
205
|
+
totalDepth += caps.depth
|
|
206
|
+
totalSpeed += caps.speed
|
|
207
|
+
}
|
|
208
|
+
ship.gatherer = {yield: totalYield, drain: totalDrain, depth: totalDepth, speed: totalSpeed}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const haulerModules = modules.filter((m) => getModuleCapabilityType(m.itemId) === MODULE_HAULER)
|
|
212
|
+
if (haulerModules.length > 0) {
|
|
213
|
+
let totalCapacity = 0
|
|
214
|
+
let weightedEffNum = 0
|
|
215
|
+
let totalDrain = 0
|
|
216
|
+
for (const m of haulerModules) {
|
|
217
|
+
const caps = computeHaulerCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
|
|
218
|
+
totalCapacity += caps.capacity
|
|
219
|
+
weightedEffNum += caps.efficiency * caps.capacity
|
|
220
|
+
totalDrain += caps.drain
|
|
221
|
+
}
|
|
222
|
+
ship.hauler = {
|
|
223
|
+
capacity: totalCapacity,
|
|
224
|
+
efficiency: totalCapacity > 0 ? Math.floor(weightedEffNum / totalCapacity) : 0,
|
|
225
|
+
drain: totalDrain,
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const loaderModules = modules.filter((m) => getModuleCapabilityType(m.itemId) === MODULE_LOADER)
|
|
230
|
+
if (loaderModules.length > 0) {
|
|
231
|
+
let totalMass = 0
|
|
232
|
+
let totalThrust = 0
|
|
233
|
+
let totalQuantity = 0
|
|
234
|
+
for (const m of loaderModules) {
|
|
235
|
+
const caps = computeLoaderCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
|
|
236
|
+
totalMass += caps.mass
|
|
237
|
+
totalThrust += caps.thrust
|
|
238
|
+
totalQuantity += caps.quantity
|
|
239
|
+
}
|
|
240
|
+
ship.loaders = {mass: totalMass, thrust: totalThrust, quantity: totalQuantity}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const crafterModules = modules.filter(
|
|
244
|
+
(m) => getModuleCapabilityType(m.itemId) === MODULE_CRAFTER
|
|
245
|
+
)
|
|
246
|
+
if (crafterModules.length > 0) {
|
|
247
|
+
let totalSpeed = 0
|
|
248
|
+
let totalDrain = 0
|
|
249
|
+
for (const m of crafterModules) {
|
|
250
|
+
const caps = computeCrafterCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
|
|
251
|
+
totalSpeed += caps.speed
|
|
252
|
+
totalDrain += caps.drain
|
|
253
|
+
}
|
|
254
|
+
ship.crafter = {speed: totalSpeed, drain: totalDrain}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return ship
|
|
258
|
+
}
|