@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,43 @@
|
|
|
1
|
+
import type {Checksum256Type} from '@wharfkit/antelope'
|
|
2
|
+
import type {CoordinatesType} from '../types'
|
|
3
|
+
import {deriveLocationStatic} from '../utils/system'
|
|
4
|
+
import {deriveLocationSize} from './location-size'
|
|
5
|
+
import {deriveResourceStats, deriveStratum, type ResourceStats} from './stratum'
|
|
6
|
+
|
|
7
|
+
export interface DerivedStratum {
|
|
8
|
+
index: number
|
|
9
|
+
itemId: number
|
|
10
|
+
seed: bigint
|
|
11
|
+
richness: number
|
|
12
|
+
reserve: number
|
|
13
|
+
stats: ResourceStats
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function deriveStrata(
|
|
17
|
+
coords: CoordinatesType,
|
|
18
|
+
gameSeed: Checksum256Type,
|
|
19
|
+
epochSeed: Checksum256Type
|
|
20
|
+
): DerivedStratum[] {
|
|
21
|
+
const loc = deriveLocationStatic(gameSeed, coords)
|
|
22
|
+
const locType = Number(loc.type)
|
|
23
|
+
if (locType === 0) return []
|
|
24
|
+
|
|
25
|
+
const size = deriveLocationSize(loc)
|
|
26
|
+
if (size === 0) return []
|
|
27
|
+
|
|
28
|
+
const subtype = Number(loc.subtype)
|
|
29
|
+
const out: DerivedStratum[] = []
|
|
30
|
+
for (let i = 0; i < size; i++) {
|
|
31
|
+
const s = deriveStratum(epochSeed, coords, i, locType, subtype, size)
|
|
32
|
+
if (s.reserve === 0) continue
|
|
33
|
+
out.push({
|
|
34
|
+
index: i,
|
|
35
|
+
itemId: s.itemId,
|
|
36
|
+
seed: s.seed,
|
|
37
|
+
richness: s.richness,
|
|
38
|
+
reserve: s.reserve,
|
|
39
|
+
stats: s.seed ? deriveResourceStats(s.seed) : {stat1: 0, stat2: 0, stat3: 0},
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
return out
|
|
43
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import {Bytes, Checksum256, type Checksum256Type} from '@wharfkit/antelope'
|
|
2
|
+
import {hash512} from '../utils/hash'
|
|
3
|
+
import {Coordinates, type CoordinatesType} from '../types'
|
|
4
|
+
import {getEligibleResources, getResourceWeight, YIELD_THRESHOLD} from './resources'
|
|
5
|
+
import {RESERVE_TIERS, rollTier, rollWithinTier} from './tiers'
|
|
6
|
+
|
|
7
|
+
export interface StratumInfo {
|
|
8
|
+
itemId: number
|
|
9
|
+
seed: bigint
|
|
10
|
+
richness: number
|
|
11
|
+
reserve: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ResourceStats {
|
|
15
|
+
stat1: number
|
|
16
|
+
stat2: number
|
|
17
|
+
stat3: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function deriveStratum(
|
|
21
|
+
epochSeed: Checksum256Type,
|
|
22
|
+
coords: CoordinatesType,
|
|
23
|
+
stratum: number,
|
|
24
|
+
locationType: number,
|
|
25
|
+
subtype: number,
|
|
26
|
+
_maxDepth: number
|
|
27
|
+
): StratumInfo {
|
|
28
|
+
const seed = Checksum256.from(epochSeed)
|
|
29
|
+
const c = Coordinates.from(coords)
|
|
30
|
+
const input = `stratum-${c.x}-${c.y}-${stratum}`
|
|
31
|
+
const hashResult = hash512(seed, input)
|
|
32
|
+
const bytes = hashResult.array
|
|
33
|
+
|
|
34
|
+
const rawReserve = ((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]) >>> 0
|
|
35
|
+
|
|
36
|
+
let reserve = 0
|
|
37
|
+
if (rawReserve <= YIELD_THRESHOLD) {
|
|
38
|
+
const tierRoll = ((bytes[18] << 8) | bytes[19]) >>> 0
|
|
39
|
+
const withinRoll = ((bytes[20] << 8) | bytes[21]) >>> 0
|
|
40
|
+
const tier = rollTier(tierRoll, stratum)
|
|
41
|
+
reserve = rollWithinTier(withinRoll, RESERVE_TIERS[tier])
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (reserve === 0) return {itemId: 0, seed: 0n, richness: 0, reserve: 0}
|
|
45
|
+
|
|
46
|
+
const eligible = getEligibleResources(locationType, subtype, stratum)
|
|
47
|
+
if (eligible.length === 0) return {itemId: 0, seed: 0n, richness: 0, reserve: 0}
|
|
48
|
+
|
|
49
|
+
const resourceRoll = ((bytes[4] << 24) | (bytes[5] << 16) | (bytes[6] << 8) | bytes[7]) >>> 0
|
|
50
|
+
|
|
51
|
+
let totalWeight = 0
|
|
52
|
+
for (const id of eligible) {
|
|
53
|
+
totalWeight += getResourceWeight(id, stratum)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let selectedItemId = eligible[0]
|
|
57
|
+
if (totalWeight > 0) {
|
|
58
|
+
const roll = resourceRoll % totalWeight
|
|
59
|
+
let cumulative = 0
|
|
60
|
+
for (const id of eligible) {
|
|
61
|
+
cumulative += getResourceWeight(id, stratum)
|
|
62
|
+
if (roll < cumulative) {
|
|
63
|
+
selectedItemId = id
|
|
64
|
+
break
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const seedBigInt =
|
|
70
|
+
(BigInt(bytes[8]) << 56n) |
|
|
71
|
+
(BigInt(bytes[9]) << 48n) |
|
|
72
|
+
(BigInt(bytes[10]) << 40n) |
|
|
73
|
+
(BigInt(bytes[11]) << 32n) |
|
|
74
|
+
(BigInt(bytes[12]) << 24n) |
|
|
75
|
+
(BigInt(bytes[13]) << 16n) |
|
|
76
|
+
(BigInt(bytes[14]) << 8n) |
|
|
77
|
+
BigInt(bytes[15])
|
|
78
|
+
|
|
79
|
+
const rawRichness = (bytes[16] << 8) | bytes[17]
|
|
80
|
+
const normalized = rawRichness / 65535
|
|
81
|
+
const baseRichness = Math.floor(normalized * normalized * 999) + 1
|
|
82
|
+
|
|
83
|
+
let depthBonus = 0
|
|
84
|
+
if (stratum > 1) {
|
|
85
|
+
depthBonus = (50 * Math.log(stratum)) / Math.log(65535)
|
|
86
|
+
}
|
|
87
|
+
const richness = Math.min(Math.floor(baseRichness + depthBonus), 1000)
|
|
88
|
+
|
|
89
|
+
return {itemId: selectedItemId, seed: seedBigInt, richness, reserve}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Derives the three stat values for a raw resource from a deposit's
|
|
94
|
+
* entropy seed (hash-based, weibull-transformed).
|
|
95
|
+
*
|
|
96
|
+
* **Use only on deposit seeds** — the bigint returned by `deriveStratum`
|
|
97
|
+
* or carried on a `MassDeposit`. Do NOT call this on a cargo item's
|
|
98
|
+
* `stats` field; cargo stats are bit-packed and must be read via
|
|
99
|
+
* `decodeStat` (or `decodeStackStats` for category-mapped output).
|
|
100
|
+
*
|
|
101
|
+
* Passing a cargo `stats` value here produces meaningless output
|
|
102
|
+
* (hash of the packed bits, unrelated to the actual stats).
|
|
103
|
+
*/
|
|
104
|
+
export function deriveResourceStats(seed: bigint): ResourceStats {
|
|
105
|
+
const seedBytes = new Uint8Array(8)
|
|
106
|
+
for (let i = 0; i < 8; i++) {
|
|
107
|
+
seedBytes[i] = Number(seed & 0xffn)
|
|
108
|
+
seed >>= 8n
|
|
109
|
+
}
|
|
110
|
+
const hashResult = Checksum256.hash(Bytes.from(seedBytes))
|
|
111
|
+
const hashBytes = hashResult.array
|
|
112
|
+
|
|
113
|
+
const extractU32 = (offset: number): number =>
|
|
114
|
+
(hashBytes[offset] * 0x1000000 +
|
|
115
|
+
(hashBytes[offset + 1] << 16) +
|
|
116
|
+
(hashBytes[offset + 2] << 8) +
|
|
117
|
+
hashBytes[offset + 3]) >>>
|
|
118
|
+
0
|
|
119
|
+
|
|
120
|
+
const weibull = (raw: number): number => {
|
|
121
|
+
const u = raw / 4294967296
|
|
122
|
+
let x = 0.24 * Math.sqrt(-Math.log(1 - u))
|
|
123
|
+
if (x > 1) x = 1
|
|
124
|
+
let val = Math.floor(x * 999) + 1
|
|
125
|
+
if (val > 999) val = 999
|
|
126
|
+
return val
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
stat1: weibull(extractU32(0)),
|
|
131
|
+
stat2: weibull(extractU32(4)),
|
|
132
|
+
stat3: weibull(extractU32(8)),
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export type ReserveTier = 'small' | 'medium' | 'large' | 'massive' | 'motherlode'
|
|
2
|
+
|
|
3
|
+
export interface TierRange {
|
|
4
|
+
min: number
|
|
5
|
+
max: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const RESERVE_TIERS: Record<ReserveTier, TierRange> = {
|
|
9
|
+
small: {min: 15, max: 60},
|
|
10
|
+
medium: {min: 100, max: 200},
|
|
11
|
+
large: {min: 400, max: 700},
|
|
12
|
+
massive: {min: 1000, max: 2500},
|
|
13
|
+
motherlode: {min: 4000, max: 10000},
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const SHALLOW_THRESHOLDS = {
|
|
17
|
+
small: 0.8,
|
|
18
|
+
medium: 0.991946,
|
|
19
|
+
large: 0.999946,
|
|
20
|
+
massive: 0.999996,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const DEEP_THRESHOLDS = {
|
|
24
|
+
small: 0.5,
|
|
25
|
+
medium: 0.95892,
|
|
26
|
+
large: 0.99892,
|
|
27
|
+
massive: 0.99992,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const TIER_ROLL_MAX = 0x10000 // 65536
|
|
31
|
+
|
|
32
|
+
function lerp(a: number, b: number, t: number): number {
|
|
33
|
+
return a + (b - a) * t
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function rollTier(tierRoll: number, stratum: number): ReserveTier {
|
|
37
|
+
const d = Math.min(stratum, 65535) / 65535
|
|
38
|
+
const smallMax = lerp(SHALLOW_THRESHOLDS.small, DEEP_THRESHOLDS.small, d) * TIER_ROLL_MAX
|
|
39
|
+
const mediumMax = lerp(SHALLOW_THRESHOLDS.medium, DEEP_THRESHOLDS.medium, d) * TIER_ROLL_MAX
|
|
40
|
+
const largeMax = lerp(SHALLOW_THRESHOLDS.large, DEEP_THRESHOLDS.large, d) * TIER_ROLL_MAX
|
|
41
|
+
const massiveMax = lerp(SHALLOW_THRESHOLDS.massive, DEEP_THRESHOLDS.massive, d) * TIER_ROLL_MAX
|
|
42
|
+
|
|
43
|
+
if (tierRoll < smallMax) return 'small'
|
|
44
|
+
if (tierRoll < mediumMax) return 'medium'
|
|
45
|
+
if (tierRoll < largeMax) return 'large'
|
|
46
|
+
if (tierRoll < massiveMax) return 'massive'
|
|
47
|
+
return 'motherlode'
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function rollWithinTier(withinRoll: number, range: TierRange): number {
|
|
51
|
+
const u = withinRoll / 65535
|
|
52
|
+
const skewed = u * u
|
|
53
|
+
return Math.floor(range.min + skewed * (range.max - range.min))
|
|
54
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import {UInt32, UInt64, type UInt64Type} from '@wharfkit/antelope'
|
|
2
|
+
import {EntityInventory} from './entity-inventory'
|
|
3
|
+
import {ServerContract} from '../contracts'
|
|
4
|
+
|
|
5
|
+
export interface CargoData {
|
|
6
|
+
cargo: EntityInventory[]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function totalCargoMass(cargo: EntityInventory[]): UInt64 {
|
|
10
|
+
return cargo.reduce((sum, c) => {
|
|
11
|
+
return sum.adding(c.totalMass)
|
|
12
|
+
}, UInt64.from(0))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getCargoForItem(
|
|
16
|
+
cargo: EntityInventory[],
|
|
17
|
+
goodId: UInt64Type
|
|
18
|
+
): EntityInventory | undefined {
|
|
19
|
+
return cargo.find((c) => c.item_id.equals(goodId))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function hasSpace(
|
|
23
|
+
currentMass: UInt64,
|
|
24
|
+
maxCapacity: UInt64,
|
|
25
|
+
goodMass: UInt64,
|
|
26
|
+
quantity: number
|
|
27
|
+
): boolean {
|
|
28
|
+
const additionalMass = goodMass.multiplying(quantity)
|
|
29
|
+
const totalMass = currentMass.adding(additionalMass)
|
|
30
|
+
return totalMass.lte(maxCapacity)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function availableCapacity(currentMass: UInt64, maxCapacity: UInt64): UInt64 {
|
|
34
|
+
if (currentMass.gte(maxCapacity)) {
|
|
35
|
+
return UInt64.from(0)
|
|
36
|
+
}
|
|
37
|
+
return maxCapacity.subtracting(currentMass)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function isFull(currentMass: UInt64, maxCapacity: UInt64): boolean {
|
|
41
|
+
return currentMass.gte(maxCapacity)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function afterRemoveItems(
|
|
45
|
+
cargo: ServerContract.Types.cargo_item[],
|
|
46
|
+
goodsToRemove: Array<{goodId: number; quantity: number}>
|
|
47
|
+
): EntityInventory[] {
|
|
48
|
+
if (cargo.length === 0) {
|
|
49
|
+
return []
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return cargo.map((item) => {
|
|
53
|
+
const removeItem = goodsToRemove.find((s) => Number(item.item_id) === s.goodId)
|
|
54
|
+
if (!removeItem) {
|
|
55
|
+
return new EntityInventory(item)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const currentQty = Number(item.quantity)
|
|
59
|
+
const newQty = Math.max(0, currentQty - removeItem.quantity)
|
|
60
|
+
|
|
61
|
+
return new EntityInventory(
|
|
62
|
+
ServerContract.Types.cargo_item.from({
|
|
63
|
+
item_id: item.item_id,
|
|
64
|
+
quantity: UInt32.from(newQty),
|
|
65
|
+
})
|
|
66
|
+
)
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function afterRemoveAllItems(cargo: ServerContract.Types.cargo_item[]): EntityInventory[] {
|
|
71
|
+
if (cargo.length === 0) {
|
|
72
|
+
return []
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return cargo.map(
|
|
76
|
+
(item) =>
|
|
77
|
+
new EntityInventory(
|
|
78
|
+
ServerContract.Types.cargo_item.from({
|
|
79
|
+
item_id: item.item_id,
|
|
80
|
+
quantity: UInt32.from(0),
|
|
81
|
+
})
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import {UInt64, type UInt64Type} from '@wharfkit/antelope'
|
|
2
|
+
import {ServerContract} from '../contracts'
|
|
3
|
+
import type {CoordinatesType} from '../types'
|
|
4
|
+
import {Location} from './location'
|
|
5
|
+
import {ScheduleAccessor} from '../scheduling/accessor'
|
|
6
|
+
import * as schedule from '../scheduling/schedule'
|
|
7
|
+
|
|
8
|
+
export interface ContainerStateInput {
|
|
9
|
+
id: UInt64Type
|
|
10
|
+
owner: string
|
|
11
|
+
name: string
|
|
12
|
+
coordinates: CoordinatesType | {x: number; y: number; z?: number}
|
|
13
|
+
hullmass: number
|
|
14
|
+
capacity: number
|
|
15
|
+
cargomass?: number
|
|
16
|
+
cargo?: ServerContract.Types.cargo_item[]
|
|
17
|
+
schedule?: ServerContract.Types.schedule
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class Container extends ServerContract.Types.entity_info {
|
|
21
|
+
private _sched?: ScheduleAccessor
|
|
22
|
+
|
|
23
|
+
get name(): string {
|
|
24
|
+
return this.entity_name
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get sched(): ScheduleAccessor {
|
|
28
|
+
this._sched ??= new ScheduleAccessor(this)
|
|
29
|
+
return this._sched
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get isIdle(): boolean {
|
|
33
|
+
return this.is_idle
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
isLoading(now: Date): boolean {
|
|
37
|
+
return schedule.isLoading(this, now)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
isUnloading(now: Date): boolean {
|
|
41
|
+
return schedule.isUnloading(this, now)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get location(): Location {
|
|
45
|
+
return Location.from(this.coordinates)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get totalMass(): UInt64 {
|
|
49
|
+
return UInt64.from(this.hullmass ?? 0).adding(this.cargomass)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get maxCapacity(): UInt64 {
|
|
53
|
+
return UInt64.from(this.capacity)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
get availableCapacity(): UInt64 {
|
|
57
|
+
const cargo = UInt64.from(this.cargomass)
|
|
58
|
+
return cargo.gte(this.maxCapacity) ? UInt64.from(0) : this.maxCapacity.subtracting(cargo)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
hasSpace(additionalMass: UInt64): boolean {
|
|
62
|
+
return UInt64.from(this.cargomass).adding(additionalMass).lte(this.maxCapacity)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
get isFull(): boolean {
|
|
66
|
+
return UInt64.from(this.cargomass).gte(this.maxCapacity)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get orbitalAltitude(): number {
|
|
70
|
+
return this.coordinates.z?.toNumber() || 0
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function computeContainerCapabilities(stats: Record<string, number>): {
|
|
75
|
+
hullmass: number
|
|
76
|
+
capacity: number
|
|
77
|
+
} {
|
|
78
|
+
const density = stats['density'] ?? 500
|
|
79
|
+
const strength = stats['strength'] ?? 500
|
|
80
|
+
const hardness = stats['hardness'] ?? 500
|
|
81
|
+
const saturation = stats['saturation'] ?? 500
|
|
82
|
+
|
|
83
|
+
const hullmass = 25000 + 75 * density
|
|
84
|
+
|
|
85
|
+
const statSum = strength + hardness + saturation
|
|
86
|
+
const exponent = statSum / 2997
|
|
87
|
+
const capacity = Math.floor(1000000 * 10 ** exponent)
|
|
88
|
+
|
|
89
|
+
return {hullmass, capacity}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function computeContainerT2Capabilities(stats: Record<string, number>): {
|
|
93
|
+
hullmass: number
|
|
94
|
+
capacity: number
|
|
95
|
+
} {
|
|
96
|
+
const strength = stats['strength'] ?? 0
|
|
97
|
+
const density = stats['density'] ?? 0
|
|
98
|
+
const hardness = stats['hardness'] ?? 0
|
|
99
|
+
const saturation = stats['saturation'] ?? 0
|
|
100
|
+
|
|
101
|
+
const hullmass = 20000 + 50 * density
|
|
102
|
+
|
|
103
|
+
const statSum = strength + hardness + saturation
|
|
104
|
+
const exponent = statSum / 2500
|
|
105
|
+
const capacity = Math.floor(1500000 * 10 ** exponent)
|
|
106
|
+
|
|
107
|
+
return {hullmass, capacity}
|
|
108
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {UInt32, UInt64} from '@wharfkit/antelope'
|
|
2
|
+
import {ServerContract} from '../contracts'
|
|
3
|
+
import {getItem} from '../data/catalog'
|
|
4
|
+
import type {Item} from '../types'
|
|
5
|
+
|
|
6
|
+
export class EntityInventory extends ServerContract.Types.cargo_item {
|
|
7
|
+
private _item?: Item
|
|
8
|
+
|
|
9
|
+
get item(): Item {
|
|
10
|
+
if (!this._item) {
|
|
11
|
+
this._item = getItem(this.item_id)
|
|
12
|
+
}
|
|
13
|
+
return this._item
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get good(): Item {
|
|
17
|
+
return this.item
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get name(): string {
|
|
21
|
+
return this.item.name
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get unitMass(): UInt32 {
|
|
25
|
+
return UInt32.from(this.item.mass)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get totalMass(): UInt64 {
|
|
29
|
+
return UInt64.from(this.unitMass).multiplying(this.quantity)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get hasCargo(): boolean {
|
|
33
|
+
return UInt32.from(this.quantity).gt(UInt32.from(0))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get isEmpty(): boolean {
|
|
37
|
+
return UInt32.from(this.quantity).equals(UInt32.from(0))
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import {Checksum256, Int64, type UInt64} from '@wharfkit/antelope'
|
|
2
|
+
import {type PlatformContract, ServerContract} from '../contracts'
|
|
3
|
+
import {type EpochInfo, getCurrentEpoch, getEpochInfo} from '../scheduling/epoch'
|
|
4
|
+
import {hasSystem} from '../utils/system'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* GameState class extends the state_row from the server contract
|
|
8
|
+
* with helper methods for epoch management and system generation
|
|
9
|
+
*/
|
|
10
|
+
export class GameState extends ServerContract.Types.state_row {
|
|
11
|
+
private _game?: PlatformContract.Types.game_row
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Create a GameState instance from a state_row
|
|
15
|
+
*/
|
|
16
|
+
static from(
|
|
17
|
+
state: ServerContract.Types.state_row,
|
|
18
|
+
game?: PlatformContract.Types.game_row
|
|
19
|
+
): GameState {
|
|
20
|
+
const gameState = Object.create(GameState.prototype) as GameState
|
|
21
|
+
Object.assign(gameState, state)
|
|
22
|
+
gameState._game = game
|
|
23
|
+
return gameState
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Set the game configuration (needed for epoch calculations)
|
|
28
|
+
*/
|
|
29
|
+
setGame(game: PlatformContract.Types.game_row): void {
|
|
30
|
+
this._game = game
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get the current epoch number from the state
|
|
35
|
+
*/
|
|
36
|
+
get currentEpoch(): UInt64 {
|
|
37
|
+
return this.epoch
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get the epoch seed (used for market pricing and system generation)
|
|
42
|
+
*/
|
|
43
|
+
get epochSeed(): Checksum256 {
|
|
44
|
+
return this.seed
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get the game seed (from game config, if available)
|
|
49
|
+
*/
|
|
50
|
+
get gameSeed(): Checksum256 | undefined {
|
|
51
|
+
return this._game?.config.seed
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Check if the game is currently enabled
|
|
56
|
+
*/
|
|
57
|
+
get isEnabled(): boolean {
|
|
58
|
+
return this.enabled
|
|
59
|
+
}
|
|
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
|
+
/**
|
|
69
|
+
* Get the current salt value (used for random number generation)
|
|
70
|
+
*/
|
|
71
|
+
get currentSalt(): UInt64 {
|
|
72
|
+
return this.salt
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get the commit hash for the next epoch
|
|
77
|
+
*/
|
|
78
|
+
get nextEpochCommit(): Checksum256 {
|
|
79
|
+
return this.commit
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Calculate the current epoch from game config (if game is set)
|
|
84
|
+
* This might differ from state.epoch if the blockchain hasn't advanced yet
|
|
85
|
+
*/
|
|
86
|
+
get calculatedCurrentEpoch(): UInt64 | undefined {
|
|
87
|
+
if (!this._game) {
|
|
88
|
+
return undefined
|
|
89
|
+
}
|
|
90
|
+
return getCurrentEpoch(this._game)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get epoch info (start/end times) for the current epoch
|
|
95
|
+
*/
|
|
96
|
+
get currentEpochInfo(): EpochInfo | undefined {
|
|
97
|
+
if (!this._game) {
|
|
98
|
+
return undefined
|
|
99
|
+
}
|
|
100
|
+
return getEpochInfo(this._game, this.epoch)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get epoch info for a specific epoch number
|
|
105
|
+
*/
|
|
106
|
+
getEpochInfo(epoch: UInt64): EpochInfo | undefined {
|
|
107
|
+
if (!this._game) {
|
|
108
|
+
return undefined
|
|
109
|
+
}
|
|
110
|
+
return getEpochInfo(this._game, epoch)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Check if a system exists at given coordinates
|
|
115
|
+
* Requires game seed from game config
|
|
116
|
+
*/
|
|
117
|
+
hasSystemAt(x: number, y: number): boolean {
|
|
118
|
+
if (!this._game) {
|
|
119
|
+
return false
|
|
120
|
+
}
|
|
121
|
+
return hasSystem(this._game.config.seed, {x: Int64.from(x), y: Int64.from(y)})
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Check if a system exists at coordinates object
|
|
126
|
+
*/
|
|
127
|
+
hasSystemAtCoords(coords: ServerContract.Types.coordinates): boolean {
|
|
128
|
+
if (!this._game) {
|
|
129
|
+
return false
|
|
130
|
+
}
|
|
131
|
+
return hasSystem(this._game.config.seed, coords)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Get a summary of the game state
|
|
136
|
+
*/
|
|
137
|
+
get summary(): {
|
|
138
|
+
enabled: boolean
|
|
139
|
+
epoch: string
|
|
140
|
+
ships: number
|
|
141
|
+
hasSeed: boolean
|
|
142
|
+
hasCommit: boolean
|
|
143
|
+
} {
|
|
144
|
+
return {
|
|
145
|
+
enabled: this.enabled,
|
|
146
|
+
epoch: this.epoch.toString(),
|
|
147
|
+
ships: this.shipCount,
|
|
148
|
+
hasSeed: !this.seed.equals(Checksum256.from('0'.repeat(64))),
|
|
149
|
+
hasCommit: !this.commit.equals(Checksum256.from('0'.repeat(64))),
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {UInt64, type UInt64Type} from '@wharfkit/antelope'
|
|
2
|
+
import {EntityInventory} from './entity-inventory'
|
|
3
|
+
import type {HasCargo} from '../capabilities/storage'
|
|
4
|
+
|
|
5
|
+
export type {HasCargo}
|
|
6
|
+
|
|
7
|
+
export class InventoryAccessor {
|
|
8
|
+
private _items?: EntityInventory[]
|
|
9
|
+
|
|
10
|
+
constructor(private readonly entity: HasCargo) {}
|
|
11
|
+
|
|
12
|
+
get items(): EntityInventory[] {
|
|
13
|
+
if (!this._items) {
|
|
14
|
+
this._items = this.entity.cargo.map((item) => new EntityInventory(item))
|
|
15
|
+
}
|
|
16
|
+
return this._items
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get totalMass(): UInt64 {
|
|
20
|
+
return this.items.reduce((sum, c) => sum.adding(c.totalMass), UInt64.from(0))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
forItem(goodId: UInt64Type): EntityInventory | undefined {
|
|
24
|
+
return this.items.find((c) => c.item_id.equals(goodId))
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get sellable(): EntityInventory[] {
|
|
28
|
+
return this.items.filter((c) => c.hasCargo)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get hasSellable(): boolean {
|
|
32
|
+
return this.items.some((c) => c.hasCargo)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get sellableCount(): number {
|
|
36
|
+
return this.items.filter((c) => c.hasCargo).length
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function createInventoryAccessor(entity: HasCargo): InventoryAccessor {
|
|
41
|
+
return new InventoryAccessor(entity)
|
|
42
|
+
}
|