@shipload/sdk 0.7.1 → 1.0.0-next.0

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.
Files changed (94) hide show
  1. package/lib/shipload.d.ts +2730 -287
  2. package/lib/shipload.js +10862 -2229
  3. package/lib/shipload.js.map +1 -1
  4. package/lib/shipload.m.js +10434 -2171
  5. package/lib/shipload.m.js.map +1 -1
  6. package/package.json +11 -20
  7. package/src/capabilities/crafting.ts +22 -0
  8. package/src/capabilities/gathering.ts +36 -0
  9. package/src/capabilities/guards.ts +38 -0
  10. package/src/capabilities/hauling.ts +22 -0
  11. package/src/capabilities/index.ts +8 -0
  12. package/src/capabilities/loading.ts +8 -0
  13. package/src/capabilities/modules.ts +86 -0
  14. package/src/capabilities/movement.ts +29 -0
  15. package/src/capabilities/storage.ts +159 -0
  16. package/src/contracts/server.ts +1389 -285
  17. package/src/data/capabilities.ts +408 -0
  18. package/src/data/catalog.ts +135 -0
  19. package/src/data/categories.ts +55 -0
  20. package/src/data/colors.ts +84 -0
  21. package/src/data/entities.json +50 -0
  22. package/src/data/item-ids.ts +75 -0
  23. package/src/data/items.json +252 -0
  24. package/src/data/locations.ts +53 -0
  25. package/src/data/metadata.ts +208 -0
  26. package/src/data/nebula-adjectives.json +211 -0
  27. package/src/data/nebula-nouns.json +151 -0
  28. package/src/data/recipes-runtime.ts +65 -0
  29. package/src/data/recipes.json +878 -0
  30. package/src/data/syllables.json +1790 -0
  31. package/src/data/tiers.ts +45 -0
  32. package/src/derivation/crafting.ts +350 -0
  33. package/src/derivation/index.ts +32 -0
  34. package/src/derivation/location-size.ts +15 -0
  35. package/src/derivation/resources.ts +112 -0
  36. package/src/derivation/stats.ts +146 -0
  37. package/src/derivation/strata.ts +43 -0
  38. package/src/derivation/stratum.ts +134 -0
  39. package/src/derivation/tiers.ts +54 -0
  40. package/src/entities/cargo-utils.ts +84 -0
  41. package/src/entities/container.ts +108 -0
  42. package/src/entities/entity-inventory.ts +39 -0
  43. package/src/entities/gamestate.ts +152 -0
  44. package/src/entities/inventory-accessor.ts +42 -0
  45. package/src/entities/location.ts +60 -0
  46. package/src/entities/makers.ts +196 -0
  47. package/src/entities/player.ts +15 -0
  48. package/src/entities/ship-deploy.ts +258 -0
  49. package/src/entities/ship.ts +204 -0
  50. package/src/entities/warehouse.ts +119 -0
  51. package/src/errors.ts +100 -9
  52. package/src/format.ts +12 -0
  53. package/src/index-module.ts +317 -7
  54. package/src/managers/actions.ts +250 -0
  55. package/src/managers/base.ts +25 -0
  56. package/src/managers/context.ts +114 -0
  57. package/src/managers/entities.ts +103 -0
  58. package/src/managers/epochs.ts +47 -0
  59. package/src/managers/index.ts +9 -0
  60. package/src/managers/locations.ts +68 -0
  61. package/src/managers/players.ts +13 -0
  62. package/src/nft/description.ts +176 -0
  63. package/src/nft/deserializers.ts +83 -0
  64. package/src/nft/index.ts +2 -0
  65. package/src/resolution/describe-module.ts +166 -0
  66. package/src/resolution/display-name.ts +39 -0
  67. package/src/resolution/resolve-item.ts +358 -0
  68. package/src/scheduling/accessor.ts +82 -0
  69. package/src/{epoch.ts → scheduling/epoch.ts} +1 -1
  70. package/src/scheduling/projection.ts +463 -0
  71. package/src/scheduling/schedule.ts +179 -0
  72. package/src/shipload.ts +47 -160
  73. package/src/subscriptions/connection.ts +154 -0
  74. package/src/subscriptions/debug.ts +17 -0
  75. package/src/subscriptions/index.ts +5 -0
  76. package/src/subscriptions/manager.ts +240 -0
  77. package/src/subscriptions/mappers.ts +28 -0
  78. package/src/subscriptions/types.ts +143 -0
  79. package/src/travel/travel.ts +500 -0
  80. package/src/types/capabilities.ts +76 -0
  81. package/src/types/entity-traits.ts +69 -0
  82. package/src/types/entity.ts +39 -0
  83. package/src/types/index.ts +3 -0
  84. package/src/types.ts +140 -35
  85. package/src/{hash.ts → utils/hash.ts} +2 -2
  86. package/src/utils/system.ts +168 -0
  87. package/src/goods.ts +0 -124
  88. package/src/market.ts +0 -214
  89. package/src/rolls.ts +0 -8
  90. package/src/ship.ts +0 -36
  91. package/src/state.ts +0 -0
  92. package/src/syllables.ts +0 -1184
  93. package/src/system.ts +0 -37
  94. 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
+ }