@shipload/sdk 1.0.0-next.2 → 1.0.0-next.20
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 +1709 -1044
- package/lib/shipload.js +6762 -4658
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +6653 -4614
- package/lib/shipload.m.js.map +1 -1
- package/lib/testing.d.ts +833 -0
- package/lib/testing.js +3647 -0
- package/lib/testing.js.map +1 -0
- package/lib/testing.m.js +3641 -0
- package/lib/testing.m.js.map +1 -0
- package/package.json +15 -2
- package/src/capabilities/gathering.ts +17 -7
- package/src/capabilities/modules.ts +9 -0
- package/src/capabilities/storage.ts +1 -1
- package/src/contracts/platform.ts +211 -3
- package/src/contracts/server.ts +723 -438
- package/src/data/capabilities.ts +9 -329
- package/src/data/capability-formulas.ts +76 -0
- package/src/data/catalog.ts +0 -5
- package/src/data/colors.ts +14 -28
- package/src/data/entities.json +46 -10
- package/src/data/item-ids.ts +17 -13
- package/src/data/items.json +308 -37
- package/src/data/kind-registry.json +85 -0
- package/src/data/kind-registry.ts +150 -0
- package/src/data/metadata.ts +99 -24
- package/src/data/recipes-runtime.ts +3 -23
- package/src/data/recipes.json +265 -96
- package/src/derivation/build-methods.ts +45 -0
- package/src/derivation/capabilities.ts +414 -0
- package/src/derivation/capability-mappings.ts +117 -0
- package/src/derivation/crafting.ts +23 -24
- package/src/derivation/index.ts +8 -2
- package/src/derivation/reserve-regen.ts +34 -0
- package/src/derivation/resources.ts +125 -38
- package/src/derivation/stats.ts +1 -2
- package/src/derivation/stratum.ts +15 -19
- package/src/derivation/tiers.ts +28 -7
- package/src/entities/entity.ts +98 -0
- package/src/entities/gamestate.ts +3 -28
- package/src/entities/makers.ts +75 -129
- package/src/entities/slot-multiplier.ts +37 -0
- package/src/errors.ts +10 -15
- package/src/format.ts +26 -4
- package/src/index-module.ts +149 -40
- package/src/managers/actions.ts +184 -82
- package/src/managers/base.ts +2 -2
- package/src/managers/construction-types.ts +47 -0
- package/src/managers/construction.ts +147 -0
- package/src/managers/context.ts +9 -0
- package/src/managers/entities.ts +18 -66
- package/src/managers/epochs.ts +40 -0
- package/src/managers/index.ts +14 -1
- package/src/managers/locations.ts +2 -20
- package/src/managers/nft.ts +28 -0
- package/src/managers/plot.ts +123 -0
- package/src/nft/atomicassets.ts +231 -0
- package/src/nft/atomicdata.ts +130 -0
- package/src/nft/buildImmutableData.ts +319 -0
- package/src/nft/description.ts +45 -13
- package/src/nft/index.ts +3 -0
- package/src/resolution/describe-module.ts +5 -8
- package/src/resolution/display-name.ts +38 -10
- package/src/resolution/resolve-item.ts +20 -12
- package/src/scheduling/accessor.ts +4 -0
- package/src/scheduling/projection.ts +79 -27
- package/src/scheduling/schedule.ts +15 -1
- package/src/scheduling/task-cargo.ts +46 -0
- package/src/shipload.ts +5 -0
- package/src/subscriptions/manager.ts +40 -6
- package/src/subscriptions/mappers.ts +3 -8
- package/src/subscriptions/types.ts +3 -2
- package/src/testing/catalog-hash.ts +19 -0
- package/src/testing/index.ts +2 -0
- package/src/testing/projection-parity.ts +143 -0
- package/src/travel/travel.ts +61 -2
- package/src/types/index.ts +0 -1
- package/src/types.ts +17 -12
- package/src/utils/cargo.ts +27 -0
- package/src/utils/system.ts +25 -24
- package/src/entities/container.ts +0 -108
- package/src/entities/ship-deploy.ts +0 -258
- package/src/entities/ship.ts +0 -204
- package/src/entities/warehouse.ts +0 -119
- package/src/types/entity-traits.ts +0 -69
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
export interface SchemaField {
|
|
2
|
+
name: string
|
|
3
|
+
type: string
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export type RawData =
|
|
7
|
+
| Uint8Array
|
|
8
|
+
| string
|
|
9
|
+
| number[]
|
|
10
|
+
| {immutable_serialized_data: Uint8Array | string | number[]}
|
|
11
|
+
|
|
12
|
+
export function deserializeAtomicData(
|
|
13
|
+
data: RawData,
|
|
14
|
+
schema: SchemaField[]
|
|
15
|
+
): Record<string, unknown> {
|
|
16
|
+
let rawData: Uint8Array | string | number[]
|
|
17
|
+
if (data && typeof data === 'object' && 'immutable_serialized_data' in (data as object)) {
|
|
18
|
+
rawData = (data as {immutable_serialized_data: Uint8Array | string | number[]})
|
|
19
|
+
.immutable_serialized_data
|
|
20
|
+
} else {
|
|
21
|
+
rawData = data as Uint8Array | string | number[]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let bytes: Uint8Array
|
|
25
|
+
if (typeof rawData === 'string') {
|
|
26
|
+
const hex = rawData
|
|
27
|
+
bytes = new Uint8Array(hex.length / 2)
|
|
28
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
29
|
+
bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16)
|
|
30
|
+
}
|
|
31
|
+
} else if (Array.isArray(rawData)) {
|
|
32
|
+
bytes = new Uint8Array(rawData)
|
|
33
|
+
} else {
|
|
34
|
+
bytes = rawData
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let offset = 0
|
|
38
|
+
|
|
39
|
+
function readVarint(): number {
|
|
40
|
+
let result = 0
|
|
41
|
+
let multiplier = 1
|
|
42
|
+
while (bytes[offset] >= 128) {
|
|
43
|
+
result += (bytes[offset] - 128) * multiplier
|
|
44
|
+
offset++
|
|
45
|
+
multiplier *= 128
|
|
46
|
+
}
|
|
47
|
+
result += bytes[offset] * multiplier
|
|
48
|
+
offset++
|
|
49
|
+
return result
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function readVarint64(): bigint {
|
|
53
|
+
let result = 0n
|
|
54
|
+
let multiplier = 1n
|
|
55
|
+
while (bytes[offset] >= 128) {
|
|
56
|
+
result += BigInt(bytes[offset] - 128) * multiplier
|
|
57
|
+
offset++
|
|
58
|
+
multiplier *= 128n
|
|
59
|
+
}
|
|
60
|
+
result += BigInt(bytes[offset]) * multiplier
|
|
61
|
+
offset++
|
|
62
|
+
return result
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function readZigzagInt64(): bigint {
|
|
66
|
+
const unsigned = readVarint64()
|
|
67
|
+
if (unsigned % 2n === 0n) {
|
|
68
|
+
return unsigned / 2n
|
|
69
|
+
} else {
|
|
70
|
+
return -(unsigned / 2n) - 1n
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function readString(): string {
|
|
75
|
+
const length = readVarint()
|
|
76
|
+
const str = new TextDecoder().decode(bytes.slice(offset, offset + length))
|
|
77
|
+
offset += length
|
|
78
|
+
return str
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const RESERVED = 4
|
|
82
|
+
const result: Record<string, unknown> = {}
|
|
83
|
+
|
|
84
|
+
while (offset < bytes.length) {
|
|
85
|
+
const fieldIndex = readVarint() - RESERVED
|
|
86
|
+
const field = schema[fieldIndex]
|
|
87
|
+
if (!field) break
|
|
88
|
+
|
|
89
|
+
switch (field.type) {
|
|
90
|
+
case 'uint16':
|
|
91
|
+
result[field.name] = readVarint()
|
|
92
|
+
break
|
|
93
|
+
case 'uint32':
|
|
94
|
+
result[field.name] = readVarint()
|
|
95
|
+
break
|
|
96
|
+
case 'uint64':
|
|
97
|
+
result[field.name] = readVarint64()
|
|
98
|
+
break
|
|
99
|
+
case 'int32':
|
|
100
|
+
result[field.name] = readZigzagInt64()
|
|
101
|
+
break
|
|
102
|
+
case 'int64':
|
|
103
|
+
result[field.name] = readZigzagInt64()
|
|
104
|
+
break
|
|
105
|
+
case 'string':
|
|
106
|
+
case 'image':
|
|
107
|
+
case 'ipfs':
|
|
108
|
+
result[field.name] = readString()
|
|
109
|
+
break
|
|
110
|
+
case 'uint16[]': {
|
|
111
|
+
const len = readVarint()
|
|
112
|
+
const arr: number[] = []
|
|
113
|
+
for (let i = 0; i < len; i++) arr.push(readVarint())
|
|
114
|
+
result[field.name] = arr
|
|
115
|
+
break
|
|
116
|
+
}
|
|
117
|
+
case 'uint64[]': {
|
|
118
|
+
const len = readVarint()
|
|
119
|
+
const arr: bigint[] = []
|
|
120
|
+
for (let i = 0; i < len; i++) arr.push(readVarint64())
|
|
121
|
+
result[field.name] = arr
|
|
122
|
+
break
|
|
123
|
+
}
|
|
124
|
+
default:
|
|
125
|
+
throw new Error(`Unknown type: ${field.type}`)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return result
|
|
130
|
+
}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import {Serializer} from '@wharfkit/antelope'
|
|
2
|
+
import {getItem} from '../data/catalog'
|
|
3
|
+
import {
|
|
4
|
+
getModuleCapabilityType,
|
|
5
|
+
MODULE_CRAFTER,
|
|
6
|
+
MODULE_ENGINE,
|
|
7
|
+
MODULE_GATHERER,
|
|
8
|
+
MODULE_GENERATOR,
|
|
9
|
+
MODULE_HAULER,
|
|
10
|
+
MODULE_LOADER,
|
|
11
|
+
MODULE_STORAGE,
|
|
12
|
+
MODULE_WARP,
|
|
13
|
+
} from '../capabilities/modules'
|
|
14
|
+
import {decodeStat, decodeCraftedItemStats} from '../derivation/crafting'
|
|
15
|
+
import {getStatDefinitions} from '../derivation/stats'
|
|
16
|
+
import type {ResourceCategory} from '../types'
|
|
17
|
+
import {Types as ServerTypes} from '../contracts/server'
|
|
18
|
+
import {
|
|
19
|
+
buildEntityDescription,
|
|
20
|
+
computeCrafterDrain,
|
|
21
|
+
computeCrafterSpeed,
|
|
22
|
+
computeEngineDrain,
|
|
23
|
+
computeEngineThrust,
|
|
24
|
+
computeGathererDepth,
|
|
25
|
+
computeGathererDrain,
|
|
26
|
+
computeGathererYield,
|
|
27
|
+
computeGeneratorCap,
|
|
28
|
+
computeGeneratorRech,
|
|
29
|
+
computeHaulerCapacity,
|
|
30
|
+
computeHaulerDrain,
|
|
31
|
+
computeHaulerEfficiency,
|
|
32
|
+
computeLoaderMass,
|
|
33
|
+
computeLoaderThrust,
|
|
34
|
+
computeWarpRange,
|
|
35
|
+
} from './description'
|
|
36
|
+
|
|
37
|
+
export type AtomicAttributeType =
|
|
38
|
+
| 'string'
|
|
39
|
+
| 'uint8'
|
|
40
|
+
| 'uint16'
|
|
41
|
+
| 'uint32'
|
|
42
|
+
| 'uint64'
|
|
43
|
+
| 'int32'
|
|
44
|
+
| 'image'
|
|
45
|
+
| 'ipfs'
|
|
46
|
+
| 'UINT16_VEC'
|
|
47
|
+
| 'UINT64_VEC'
|
|
48
|
+
|
|
49
|
+
export interface ImmutableEntry {
|
|
50
|
+
first: string
|
|
51
|
+
second: [AtomicAttributeType, unknown]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface ImmutableModuleSlot {
|
|
55
|
+
type?: number | string | bigint
|
|
56
|
+
installed?: {item_id: number | string | bigint; stats: number | string | bigint}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function moduleSlotsForImmutable(
|
|
60
|
+
modules: ServerTypes.module_entry[]
|
|
61
|
+
): ImmutableModuleSlot[] {
|
|
62
|
+
return modules.map((m) => ({
|
|
63
|
+
type: Number(m.type.toString()),
|
|
64
|
+
installed: m.installed
|
|
65
|
+
? {
|
|
66
|
+
item_id: Number(m.installed.item_id.toString()),
|
|
67
|
+
stats: BigInt(m.installed.stats.toString()),
|
|
68
|
+
}
|
|
69
|
+
: undefined,
|
|
70
|
+
}))
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const IMAGE_HOST_URL = 'https://item.shiploadgame.com/item'
|
|
74
|
+
|
|
75
|
+
function bytesToBase64Url(bytes: Uint8Array): string {
|
|
76
|
+
let binary = ''
|
|
77
|
+
for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]!)
|
|
78
|
+
const b64 = btoa(binary)
|
|
79
|
+
return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function computeNftImageUrl(
|
|
83
|
+
item: {item_id: number; stats: bigint; modules: ImmutableModuleSlot[]; quantity: number},
|
|
84
|
+
originX: number,
|
|
85
|
+
originY: number
|
|
86
|
+
): string {
|
|
87
|
+
const payload = ServerTypes.nft_item_payload.from({
|
|
88
|
+
item: {
|
|
89
|
+
item_id: item.item_id,
|
|
90
|
+
stats: String(item.stats),
|
|
91
|
+
modules: item.modules,
|
|
92
|
+
quantity: item.quantity,
|
|
93
|
+
},
|
|
94
|
+
location: {x: String(originX), y: String(originY)},
|
|
95
|
+
})
|
|
96
|
+
const bytes = Serializer.encode({object: payload}).array
|
|
97
|
+
return `${IMAGE_HOST_URL}/${bytesToBase64Url(bytes)}.png`
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function commonBaseImmutable(
|
|
101
|
+
quantity: number,
|
|
102
|
+
stats: bigint,
|
|
103
|
+
originX: number,
|
|
104
|
+
originY: number,
|
|
105
|
+
img: string
|
|
106
|
+
): ImmutableEntry[] {
|
|
107
|
+
return [
|
|
108
|
+
{first: 'quantity', second: ['uint32', quantity]},
|
|
109
|
+
{first: 'stats', second: ['uint64', String(stats)]},
|
|
110
|
+
{first: 'origin_x', second: ['int32', originX]},
|
|
111
|
+
{first: 'origin_y', second: ['int32', originY]},
|
|
112
|
+
{first: 'img', second: ['string', img]},
|
|
113
|
+
{first: 'deposit_amount', second: ['uint64', '0']},
|
|
114
|
+
{first: 'deposit_token', second: ['uint64', '0']},
|
|
115
|
+
{first: 'deposit_symbol', second: ['uint64', '0']},
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function buildResourceImmutable(
|
|
120
|
+
itemId: number,
|
|
121
|
+
quantity: number,
|
|
122
|
+
stats: bigint,
|
|
123
|
+
originX: number,
|
|
124
|
+
originY: number
|
|
125
|
+
): ImmutableEntry[] {
|
|
126
|
+
const item = getItem(itemId)
|
|
127
|
+
const cat = item.category
|
|
128
|
+
if (!cat) throw new Error(`Resource item ${itemId} has no category`)
|
|
129
|
+
const definitions = getStatDefinitions(cat as ResourceCategory)
|
|
130
|
+
const img = computeNftImageUrl(
|
|
131
|
+
{item_id: itemId, stats, modules: [], quantity},
|
|
132
|
+
originX,
|
|
133
|
+
originY
|
|
134
|
+
)
|
|
135
|
+
const base = commonBaseImmutable(quantity, stats, originX, originY, img)
|
|
136
|
+
base.push({first: definitions[0].key, second: ['uint16', decodeStat(stats, 0)]})
|
|
137
|
+
base.push({first: definitions[1].key, second: ['uint16', decodeStat(stats, 1)]})
|
|
138
|
+
base.push({first: definitions[2].key, second: ['uint16', decodeStat(stats, 2)]})
|
|
139
|
+
return base
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function buildComponentImmutable(
|
|
143
|
+
itemId: number,
|
|
144
|
+
quantity: number,
|
|
145
|
+
stats: bigint,
|
|
146
|
+
originX: number,
|
|
147
|
+
originY: number
|
|
148
|
+
): ImmutableEntry[] {
|
|
149
|
+
const img = computeNftImageUrl(
|
|
150
|
+
{item_id: itemId, stats, modules: [], quantity},
|
|
151
|
+
originX,
|
|
152
|
+
originY
|
|
153
|
+
)
|
|
154
|
+
const base = commonBaseImmutable(quantity, stats, originX, originY, img)
|
|
155
|
+
const decoded = decodeCraftedItemStats(itemId, stats)
|
|
156
|
+
for (const [key, value] of Object.entries(decoded)) {
|
|
157
|
+
base.push({first: key, second: ['uint16', value]})
|
|
158
|
+
}
|
|
159
|
+
return base
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function buildModuleImmutable(
|
|
163
|
+
itemId: number,
|
|
164
|
+
quantity: number,
|
|
165
|
+
stats: bigint,
|
|
166
|
+
originX: number,
|
|
167
|
+
originY: number
|
|
168
|
+
): ImmutableEntry[] {
|
|
169
|
+
const img = computeNftImageUrl(
|
|
170
|
+
{item_id: itemId, stats, modules: [], quantity},
|
|
171
|
+
originX,
|
|
172
|
+
originY
|
|
173
|
+
)
|
|
174
|
+
const base = commonBaseImmutable(quantity, stats, originX, originY, img)
|
|
175
|
+
const subtype = getModuleCapabilityType(itemId)
|
|
176
|
+
const item = getItem(itemId)
|
|
177
|
+
switch (subtype) {
|
|
178
|
+
case MODULE_ENGINE: {
|
|
179
|
+
const vol = decodeStat(stats, 0)
|
|
180
|
+
const thm = decodeStat(stats, 1)
|
|
181
|
+
base.push({first: 'volatility', second: ['uint16', vol]})
|
|
182
|
+
base.push({first: 'thermal', second: ['uint16', thm]})
|
|
183
|
+
base.push({first: 'thrust', second: ['uint32', computeEngineThrust(vol)]})
|
|
184
|
+
base.push({first: 'drain', second: ['uint16', computeEngineDrain(thm)]})
|
|
185
|
+
break
|
|
186
|
+
}
|
|
187
|
+
case MODULE_GENERATOR: {
|
|
188
|
+
const res = decodeStat(stats, 0)
|
|
189
|
+
const ref = decodeStat(stats, 1)
|
|
190
|
+
base.push({first: 'resonance', second: ['uint16', res]})
|
|
191
|
+
base.push({first: 'reflectivity', second: ['uint16', ref]})
|
|
192
|
+
base.push({first: 'capacity', second: ['uint16', computeGeneratorCap(res)]})
|
|
193
|
+
base.push({first: 'recharge', second: ['uint16', computeGeneratorRech(ref)]})
|
|
194
|
+
break
|
|
195
|
+
}
|
|
196
|
+
case MODULE_GATHERER: {
|
|
197
|
+
const str = decodeStat(stats, 0)
|
|
198
|
+
const tol = decodeStat(stats, 1)
|
|
199
|
+
const con = decodeStat(stats, 2)
|
|
200
|
+
const ref = decodeStat(stats, 3)
|
|
201
|
+
base.push({first: 'strength', second: ['uint16', str]})
|
|
202
|
+
base.push({first: 'tolerance', second: ['uint16', tol]})
|
|
203
|
+
base.push({first: 'conductivity', second: ['uint16', con]})
|
|
204
|
+
base.push({first: 'reflectivity', second: ['uint16', ref]})
|
|
205
|
+
base.push({first: 'yield', second: ['uint16', computeGathererYield(str)]})
|
|
206
|
+
base.push({first: 'drain', second: ['uint16', computeGathererDrain(con)]})
|
|
207
|
+
base.push({first: 'depth', second: ['uint16', computeGathererDepth(tol, item.tier)]})
|
|
208
|
+
break
|
|
209
|
+
}
|
|
210
|
+
case MODULE_LOADER: {
|
|
211
|
+
const fin = decodeStat(stats, 0)
|
|
212
|
+
const pla = decodeStat(stats, 1)
|
|
213
|
+
base.push({first: 'fineness', second: ['uint16', fin]})
|
|
214
|
+
base.push({first: 'plasticity', second: ['uint16', pla]})
|
|
215
|
+
base.push({first: 'mass', second: ['uint32', computeLoaderMass(fin)]})
|
|
216
|
+
base.push({first: 'thrust', second: ['uint16', computeLoaderThrust(pla)]})
|
|
217
|
+
break
|
|
218
|
+
}
|
|
219
|
+
case MODULE_WARP: {
|
|
220
|
+
const res = decodeStat(stats, 0)
|
|
221
|
+
base.push({first: 'resonance', second: ['uint16', res]})
|
|
222
|
+
base.push({first: 'range', second: ['uint32', computeWarpRange(res)]})
|
|
223
|
+
break
|
|
224
|
+
}
|
|
225
|
+
case MODULE_CRAFTER: {
|
|
226
|
+
const rea = decodeStat(stats, 0)
|
|
227
|
+
const com = decodeStat(stats, 1)
|
|
228
|
+
base.push({first: 'reactivity', second: ['uint16', rea]})
|
|
229
|
+
base.push({first: 'composition', second: ['uint16', com]})
|
|
230
|
+
base.push({first: 'speed', second: ['uint16', computeCrafterSpeed(rea)]})
|
|
231
|
+
base.push({first: 'drain', second: ['uint16', computeCrafterDrain(com)]})
|
|
232
|
+
break
|
|
233
|
+
}
|
|
234
|
+
case MODULE_STORAGE: {
|
|
235
|
+
const str = decodeStat(stats, 0)
|
|
236
|
+
const fin = decodeStat(stats, 1)
|
|
237
|
+
const sat = decodeStat(stats, 2)
|
|
238
|
+
const sum = str + fin + sat
|
|
239
|
+
base.push({first: 'strength', second: ['uint16', str]})
|
|
240
|
+
base.push({first: 'fineness', second: ['uint16', fin]})
|
|
241
|
+
base.push({first: 'saturation', second: ['uint16', sat]})
|
|
242
|
+
base.push({
|
|
243
|
+
first: 'capacity_bonus_pct',
|
|
244
|
+
second: ['uint16', 10 + Math.floor((sum * 10) / 2997)],
|
|
245
|
+
})
|
|
246
|
+
break
|
|
247
|
+
}
|
|
248
|
+
case MODULE_HAULER: {
|
|
249
|
+
const res = decodeStat(stats, 0)
|
|
250
|
+
const con = decodeStat(stats, 1)
|
|
251
|
+
const ref = decodeStat(stats, 2)
|
|
252
|
+
base.push({first: 'resonance', second: ['uint16', res]})
|
|
253
|
+
base.push({first: 'conductivity', second: ['uint16', con]})
|
|
254
|
+
base.push({first: 'reflectivity', second: ['uint16', ref]})
|
|
255
|
+
base.push({first: 'capacity', second: ['uint8', computeHaulerCapacity(res)]})
|
|
256
|
+
base.push({first: 'efficiency', second: ['uint16', computeHaulerEfficiency(con)]})
|
|
257
|
+
base.push({first: 'drain', second: ['uint16', computeHaulerDrain(ref)]})
|
|
258
|
+
break
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return base
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export function buildEntityImmutable(
|
|
265
|
+
itemId: number,
|
|
266
|
+
quantity: number,
|
|
267
|
+
stats: bigint,
|
|
268
|
+
originX: number,
|
|
269
|
+
originY: number,
|
|
270
|
+
modules: ImmutableModuleSlot[]
|
|
271
|
+
): ImmutableEntry[] {
|
|
272
|
+
const moduleItems: number[] = []
|
|
273
|
+
const moduleStats: string[] = []
|
|
274
|
+
for (const m of modules) {
|
|
275
|
+
if (m.installed) {
|
|
276
|
+
moduleItems.push(Number(m.installed.item_id))
|
|
277
|
+
moduleStats.push(String(m.installed.stats))
|
|
278
|
+
} else {
|
|
279
|
+
moduleItems.push(0)
|
|
280
|
+
moduleStats.push('0')
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
const img = computeNftImageUrl({item_id: itemId, stats, modules, quantity}, originX, originY)
|
|
284
|
+
const base = commonBaseImmutable(quantity, stats, originX, originY, img)
|
|
285
|
+
base.push({first: 'module_items', second: ['UINT16_VEC', moduleItems]})
|
|
286
|
+
base.push({first: 'module_stats', second: ['UINT64_VEC', moduleStats]})
|
|
287
|
+
const description = buildEntityDescription(
|
|
288
|
+
itemId,
|
|
289
|
+
stats,
|
|
290
|
+
moduleItems,
|
|
291
|
+
moduleStats.map((s) => BigInt(s))
|
|
292
|
+
)
|
|
293
|
+
base.push({first: 'description', second: ['string', description]})
|
|
294
|
+
return base
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export function buildImmutableData(
|
|
298
|
+
itemId: number,
|
|
299
|
+
quantity: number,
|
|
300
|
+
stats: bigint,
|
|
301
|
+
originX: number,
|
|
302
|
+
originY: number,
|
|
303
|
+
modules: ImmutableModuleSlot[] = []
|
|
304
|
+
): ImmutableEntry[] {
|
|
305
|
+
const item = getItem(itemId)
|
|
306
|
+
if (item.type === 'resource') {
|
|
307
|
+
return buildResourceImmutable(itemId, quantity, stats, originX, originY)
|
|
308
|
+
}
|
|
309
|
+
if (item.type === 'component') {
|
|
310
|
+
return buildComponentImmutable(itemId, quantity, stats, originX, originY)
|
|
311
|
+
}
|
|
312
|
+
if (item.type === 'module') {
|
|
313
|
+
return buildModuleImmutable(itemId, quantity, stats, originX, originY)
|
|
314
|
+
}
|
|
315
|
+
if (item.type === 'entity') {
|
|
316
|
+
return buildEntityImmutable(itemId, quantity, stats, originX, originY, modules)
|
|
317
|
+
}
|
|
318
|
+
throw new Error(`Unsupported item type for wrap: ${item.type}`)
|
|
319
|
+
}
|
package/src/nft/description.ts
CHANGED
|
@@ -4,22 +4,29 @@ import {
|
|
|
4
4
|
MODULE_ENGINE,
|
|
5
5
|
MODULE_GATHERER,
|
|
6
6
|
MODULE_GENERATOR,
|
|
7
|
+
MODULE_HAULER,
|
|
7
8
|
MODULE_LOADER,
|
|
8
9
|
MODULE_STORAGE,
|
|
10
|
+
MODULE_WARP,
|
|
9
11
|
} from '../capabilities/modules'
|
|
10
12
|
import {
|
|
11
13
|
ITEM_CONTAINER_T1_PACKED,
|
|
12
14
|
ITEM_CONTAINER_T2_PACKED,
|
|
13
15
|
ITEM_CRAFTER_T1,
|
|
14
16
|
ITEM_ENGINE_T1,
|
|
17
|
+
ITEM_EXTRACTOR_T1_PACKED,
|
|
15
18
|
ITEM_GATHERER_T1,
|
|
16
19
|
ITEM_GENERATOR_T1,
|
|
20
|
+
ITEM_HAULER_T1,
|
|
17
21
|
ITEM_LOADER_T1,
|
|
18
22
|
ITEM_SHIP_T1_PACKED,
|
|
19
23
|
ITEM_STORAGE_T1,
|
|
20
24
|
ITEM_WAREHOUSE_T1_PACKED,
|
|
25
|
+
ITEM_WARP_T1,
|
|
21
26
|
} from '../data/item-ids'
|
|
22
27
|
import {decodeStat} from '../derivation/crafting'
|
|
28
|
+
import {gathererDepthForTier} from '../derivation/capabilities'
|
|
29
|
+
import {getItem} from '../data/catalog'
|
|
23
30
|
|
|
24
31
|
function idiv(a: number, b: number): number {
|
|
25
32
|
return Math.floor(a / b)
|
|
@@ -27,32 +34,36 @@ function idiv(a: number, b: number): number {
|
|
|
27
34
|
|
|
28
35
|
export function computeBaseHullmass(stats: bigint): number {
|
|
29
36
|
const density = decodeStat(stats, 1)
|
|
30
|
-
return
|
|
37
|
+
return 100000 - 75 * density
|
|
31
38
|
}
|
|
32
39
|
|
|
33
40
|
export function computeBaseCapacityShip(stats: bigint): number {
|
|
34
41
|
const s = decodeStat(stats, 0) + decodeStat(stats, 2) + decodeStat(stats, 3)
|
|
35
|
-
return Math.floor(
|
|
42
|
+
return Math.floor(5_000_000 * 6 ** (s / 2997))
|
|
36
43
|
}
|
|
37
44
|
|
|
38
45
|
export function computeBaseCapacityWarehouse(stats: bigint): number {
|
|
39
46
|
const s = decodeStat(stats, 0) + decodeStat(stats, 2) + decodeStat(stats, 3)
|
|
40
|
-
return Math.floor(
|
|
47
|
+
return Math.floor(100_000_000 * 6 ** (s / 2997))
|
|
41
48
|
}
|
|
42
49
|
|
|
43
50
|
export const computeEngineThrust = (vol: number): number => 400 + idiv(vol * 3, 4)
|
|
44
51
|
export const computeEngineDrain = (thm: number): number => Math.max(30, 50 - idiv(thm, 70))
|
|
45
|
-
export const computeGeneratorCap = (
|
|
46
|
-
export const computeGeneratorRech = (
|
|
52
|
+
export const computeGeneratorCap = (com: number): number => 300 + idiv(com, 6)
|
|
53
|
+
export const computeGeneratorRech = (fin: number): number => 1 + idiv(fin * 3, 1000)
|
|
47
54
|
export const computeGathererYield = (str: number): number => 200 + str
|
|
48
55
|
export const computeGathererDrain = (con: number): number =>
|
|
49
56
|
Math.max(250, 1250 - idiv(con * 25, 20))
|
|
50
|
-
export const computeGathererDepth = (tol: number): number =>
|
|
51
|
-
|
|
52
|
-
export const computeLoaderMass = (
|
|
57
|
+
export const computeGathererDepth = (tol: number, tier: number): number =>
|
|
58
|
+
gathererDepthForTier(tol, tier)
|
|
59
|
+
export const computeLoaderMass = (ins: number): number => Math.max(200, 2000 - ins * 2)
|
|
53
60
|
export const computeLoaderThrust = (pla: number): number => 1 + idiv(pla, 500)
|
|
54
61
|
export const computeCrafterSpeed = (rea: number): number => 100 + idiv(rea * 4, 5)
|
|
55
|
-
export const computeCrafterDrain = (
|
|
62
|
+
export const computeCrafterDrain = (fin: number): number => Math.max(5, 30 - idiv(fin, 33))
|
|
63
|
+
export const computeHaulerCapacity = (fin: number): number => Math.max(1, 1 + idiv(fin, 400))
|
|
64
|
+
export const computeHaulerEfficiency = (con: number): number => 2000 + con * 6
|
|
65
|
+
export const computeHaulerDrain = (com: number): number => Math.max(3, 15 - idiv(com, 80))
|
|
66
|
+
export const computeWarpRange = (stat: number): number => 100 + stat * 3
|
|
56
67
|
|
|
57
68
|
export function entityDisplayName(itemId: number): string {
|
|
58
69
|
switch (itemId) {
|
|
@@ -60,6 +71,8 @@ export function entityDisplayName(itemId: number): string {
|
|
|
60
71
|
return 'Ship'
|
|
61
72
|
case ITEM_WAREHOUSE_T1_PACKED:
|
|
62
73
|
return 'Warehouse'
|
|
74
|
+
case ITEM_EXTRACTOR_T1_PACKED:
|
|
75
|
+
return 'Extractor'
|
|
63
76
|
case ITEM_CONTAINER_T1_PACKED:
|
|
64
77
|
return 'Container'
|
|
65
78
|
case ITEM_CONTAINER_T2_PACKED:
|
|
@@ -83,6 +96,10 @@ export function moduleDisplayName(itemId: number): string {
|
|
|
83
96
|
return 'Crafter'
|
|
84
97
|
case ITEM_STORAGE_T1:
|
|
85
98
|
return 'Storage'
|
|
99
|
+
case ITEM_HAULER_T1:
|
|
100
|
+
return 'Hauler'
|
|
101
|
+
case ITEM_WARP_T1:
|
|
102
|
+
return 'Warp'
|
|
86
103
|
default:
|
|
87
104
|
return 'Module'
|
|
88
105
|
}
|
|
@@ -114,11 +131,12 @@ export function formatModuleLine(slot: number, itemId: number, stats: bigint): s
|
|
|
114
131
|
case MODULE_GATHERER: {
|
|
115
132
|
const str = decodeStat(stats, 0)
|
|
116
133
|
const tol = decodeStat(stats, 1)
|
|
117
|
-
const con = decodeStat(stats,
|
|
118
|
-
const
|
|
134
|
+
const con = decodeStat(stats, 2)
|
|
135
|
+
const tier = getItem(itemId).tier
|
|
119
136
|
out += ` Yield ${computeGathererYield(str)} Depth ${computeGathererDepth(
|
|
120
|
-
tol
|
|
121
|
-
|
|
137
|
+
tol,
|
|
138
|
+
tier
|
|
139
|
+
)} Drain ${computeGathererDrain(con)}`
|
|
122
140
|
break
|
|
123
141
|
}
|
|
124
142
|
case MODULE_LOADER: {
|
|
@@ -142,6 +160,18 @@ export function formatModuleLine(slot: number, itemId: number, stats: bigint): s
|
|
|
142
160
|
out += ` +${pct}% capacity`
|
|
143
161
|
break
|
|
144
162
|
}
|
|
163
|
+
case MODULE_HAULER: {
|
|
164
|
+
const res = decodeStat(stats, 0)
|
|
165
|
+
const con = decodeStat(stats, 1)
|
|
166
|
+
const ref = decodeStat(stats, 2)
|
|
167
|
+
out += ` Capacity ${computeHaulerCapacity(res)} Efficiency ${computeHaulerEfficiency(con)} Drain ${computeHaulerDrain(ref)}`
|
|
168
|
+
break
|
|
169
|
+
}
|
|
170
|
+
case MODULE_WARP: {
|
|
171
|
+
const stat = decodeStat(stats, 0)
|
|
172
|
+
out += ` Range ${computeWarpRange(stat)}`
|
|
173
|
+
break
|
|
174
|
+
}
|
|
145
175
|
}
|
|
146
176
|
return out
|
|
147
177
|
}
|
|
@@ -158,6 +188,8 @@ export function buildEntityDescription(
|
|
|
158
188
|
baseCapacity = computeBaseCapacityShip(hullStats)
|
|
159
189
|
} else if (itemId === ITEM_WAREHOUSE_T1_PACKED) {
|
|
160
190
|
baseCapacity = computeBaseCapacityWarehouse(hullStats)
|
|
191
|
+
} else if (itemId === ITEM_EXTRACTOR_T1_PACKED) {
|
|
192
|
+
baseCapacity = computeBaseCapacityShip(hullStats)
|
|
161
193
|
}
|
|
162
194
|
|
|
163
195
|
let out = entityDisplayName(itemId)
|
package/src/nft/index.ts
CHANGED
|
@@ -53,25 +53,22 @@ const TEMPLATES: Record<string, TemplateSpec> = {
|
|
|
53
53
|
gatherer: {
|
|
54
54
|
id: 'module.gatherer.description',
|
|
55
55
|
template:
|
|
56
|
-
'mines resources at {yield}
|
|
56
|
+
'mines resources at {yield} yield to a max depth of {depth} while draining {drain} energy per second',
|
|
57
57
|
params: [
|
|
58
58
|
['yield', 'Yield'],
|
|
59
|
-
['drain', 'Drain'],
|
|
60
59
|
['depth', 'Depth'],
|
|
61
|
-
['
|
|
60
|
+
['drain', 'Drain'],
|
|
62
61
|
],
|
|
63
|
-
highlightKeys: ['yield', 'depth', '
|
|
62
|
+
highlightKeys: ['yield', 'depth', 'drain'],
|
|
64
63
|
},
|
|
65
64
|
loader: {
|
|
66
65
|
id: 'module.loader.description',
|
|
67
|
-
template:
|
|
68
|
-
'{quantity} loader that generates {thrust} thrust with a weight of {mass} per unit',
|
|
66
|
+
template: 'generates {thrust} thrust with a weight of {mass} per unit',
|
|
69
67
|
params: [
|
|
70
|
-
['quantity', 'Quantity'],
|
|
71
68
|
['thrust', 'Thrust'],
|
|
72
69
|
['mass', 'Mass'],
|
|
73
70
|
],
|
|
74
|
-
highlightKeys: ['
|
|
71
|
+
highlightKeys: ['thrust', 'mass'],
|
|
75
72
|
},
|
|
76
73
|
crafter: {
|
|
77
74
|
id: 'module.crafter.description',
|
|
@@ -1,22 +1,50 @@
|
|
|
1
1
|
import type {ResolvedItem} from './resolve-item'
|
|
2
2
|
import type {ResourceCategory} from '../types'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
CATEGORY_LABELS,
|
|
5
|
+
RESOURCE_TIER_ADJECTIVES,
|
|
6
|
+
COMPONENT_TIER_PREFIXES,
|
|
7
|
+
MODULE_TIER_PREFIXES,
|
|
8
|
+
} from '../types'
|
|
4
9
|
import {formatMass as defaultFormatMass} from '../format'
|
|
5
10
|
|
|
6
|
-
|
|
7
|
-
itemType: 'resource' | 'component' | 'module' | 'entity' | string
|
|
11
|
+
interface DisplayNameInputCommon {
|
|
8
12
|
tier: number
|
|
9
13
|
category?: ResourceCategory
|
|
10
14
|
name: string
|
|
11
15
|
}
|
|
12
16
|
|
|
13
|
-
export
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
export type DisplayNameInput =
|
|
18
|
+
| (DisplayNameInputCommon & {itemType: 'resource' | 'component' | 'module' | 'entity' | string})
|
|
19
|
+
| (DisplayNameInputCommon & {type: string})
|
|
20
|
+
|
|
21
|
+
function itemTypeOf(item: DisplayNameInput): string {
|
|
22
|
+
return 'itemType' in item ? item.itemType : item.type
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function tierPrefix(item: DisplayNameInput): string | null {
|
|
26
|
+
const t = itemTypeOf(item)
|
|
27
|
+
if (t === 'resource') return RESOURCE_TIER_ADJECTIVES[item.tier] ?? null
|
|
28
|
+
if (t === 'component') return COMPONENT_TIER_PREFIXES[item.tier] ?? null
|
|
29
|
+
if (t === 'module') return MODULE_TIER_PREFIXES[item.tier] ?? null
|
|
30
|
+
return null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function rootName(item: DisplayNameInput): string {
|
|
34
|
+
if (itemTypeOf(item) !== 'resource') return item.name
|
|
35
|
+
return item.category ? CATEGORY_LABELS[item.category] : 'Resource'
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Tier-free display name: includes the resource tier adjective / component-module
|
|
39
|
+
// prefix, but no "(T#)" suffix. Use this when the tier is shown separately.
|
|
40
|
+
export function baseName(item: DisplayNameInput): string {
|
|
41
|
+
const prefix = tierPrefix(item)
|
|
42
|
+
const root = rootName(item)
|
|
43
|
+
return prefix ? `${prefix} ${root}` : root
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function displayName(item: DisplayNameInput): string {
|
|
47
|
+
return `${baseName(item)} (T${item.tier})`
|
|
20
48
|
}
|
|
21
49
|
|
|
22
50
|
export interface DescribeOptions {
|