@shipload/sdk 2.0.0-rc4 → 2.0.0-rc6

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 (45) hide show
  1. package/lib/shipload.d.ts +411 -1025
  2. package/lib/shipload.js +879 -2057
  3. package/lib/shipload.js.map +1 -1
  4. package/lib/shipload.m.js +852 -2028
  5. package/lib/shipload.m.js.map +1 -1
  6. package/package.json +1 -1
  7. package/src/capabilities/crafting.ts +10 -0
  8. package/src/capabilities/guards.ts +0 -5
  9. package/src/capabilities/index.ts +1 -0
  10. package/src/capabilities/storage.ts +0 -8
  11. package/src/contracts/server.ts +106 -225
  12. package/src/data/items.json +15 -14
  13. package/src/data/recipes.ts +129 -0
  14. package/src/derivation/crafting.ts +120 -0
  15. package/src/derivation/index.ts +9 -6
  16. package/src/derivation/resources.ts +54 -53
  17. package/src/derivation/stats.ts +146 -0
  18. package/src/derivation/stratum.ts +14 -14
  19. package/src/entities/cargo-utils.ts +6 -64
  20. package/src/entities/container.ts +18 -0
  21. package/src/entities/entity-inventory.ts +0 -4
  22. package/src/entities/inventory-accessor.ts +0 -4
  23. package/src/entities/location.ts +2 -197
  24. package/src/entities/player.ts +1 -274
  25. package/src/entities/ship.ts +0 -21
  26. package/src/entities/warehouse.ts +0 -4
  27. package/src/index-module.ts +43 -47
  28. package/src/managers/actions.ts +38 -90
  29. package/src/managers/context.ts +0 -9
  30. package/src/managers/index.ts +0 -1
  31. package/src/managers/locations.ts +2 -85
  32. package/src/market/items.ts +1 -2
  33. package/src/scheduling/projection.ts +0 -10
  34. package/src/shipload.ts +0 -5
  35. package/src/types/capabilities.ts +1 -9
  36. package/src/types/entity-traits.ts +3 -4
  37. package/src/types/entity.ts +0 -1
  38. package/src/types.ts +8 -28
  39. package/src/utils/system.ts +5 -4
  40. package/src/managers/trades.ts +0 -119
  41. package/src/market/market.ts +0 -208
  42. package/src/market/rolls.ts +0 -8
  43. package/src/trading/collect.ts +0 -938
  44. package/src/trading/deal.ts +0 -207
  45. package/src/trading/trade.ts +0 -203
@@ -16,10 +16,9 @@ export interface StratumInfo {
16
16
  }
17
17
 
18
18
  export interface ResourceStats {
19
- purity: number
20
- density: number
21
- reactivity: number
22
- resonance: number
19
+ stat1: number
20
+ stat2: number
21
+ stat3: number
23
22
  }
24
23
 
25
24
  export function deriveStratum(
@@ -86,7 +85,7 @@ export function deriveStratum(
86
85
 
87
86
  let depthBonus = 0
88
87
  if (stratum > 1) {
89
- depthBonus = 50 * Math.log(stratum) / Math.log(65535)
88
+ depthBonus = (50 * Math.log(stratum)) / Math.log(65535)
90
89
  }
91
90
  const richness = Math.min(Math.floor(baseRichness + depthBonus), 1000)
92
91
 
@@ -94,15 +93,17 @@ export function deriveStratum(
94
93
  }
95
94
 
96
95
  export function deriveResourceStats(seed: bigint): ResourceStats {
97
- const seedStr = seed.toString()
98
- const encoder = new TextEncoder()
99
- const data = encoder.encode(seedStr)
100
- const hashResult = Checksum256.hash(Bytes.from(data))
96
+ const seedBytes = new Uint8Array(8)
97
+ for (let i = 7; i >= 0; i--) {
98
+ seedBytes[i] = Number(seed & 0xffn)
99
+ seed >>= 8n
100
+ }
101
+ const hashResult = Checksum256.hash(Bytes.from(seedBytes))
101
102
  const hashBytes = hashResult.array
102
103
 
103
104
  const extractU16 = (offset: number): number => (hashBytes[offset] << 8) | hashBytes[offset + 1]
104
105
 
105
- const weibullStat = (raw: number): number => {
106
+ const weibull = (raw: number): number => {
106
107
  const u = raw / 65536
107
108
  let x = 0.27 * Math.sqrt(-Math.log(1 - u))
108
109
  if (x > 1) x = 1
@@ -110,9 +111,8 @@ export function deriveResourceStats(seed: bigint): ResourceStats {
110
111
  }
111
112
 
112
113
  return {
113
- purity: weibullStat(extractU16(0)),
114
- density: weibullStat(extractU16(2)),
115
- reactivity: weibullStat(extractU16(4)),
116
- resonance: weibullStat(extractU16(6)),
114
+ stat1: weibull(extractU16(0)),
115
+ stat2: weibull(extractU16(2)),
116
+ stat3: weibull(extractU16(4)),
117
117
  }
118
118
  }
@@ -12,12 +12,6 @@ export function totalCargoMass(cargo: EntityInventory[]): UInt64 {
12
12
  }, UInt64.from(0))
13
13
  }
14
14
 
15
- export function cargoValue(cargo: EntityInventory[]): UInt64 {
16
- return cargo.reduce((sum, c) => {
17
- return sum.adding(c.totalCost)
18
- }, UInt64.from(0))
19
- }
20
-
21
15
  export function getCargoForItem(
22
16
  cargo: EntityInventory[],
23
17
  goodId: UInt64Type
@@ -47,84 +41,33 @@ export function isFull(currentMass: UInt64, maxCapacity: UInt64): boolean {
47
41
  return currentMass.gte(maxCapacity)
48
42
  }
49
43
 
50
- export interface SaleValue {
51
- revenue: UInt64
52
- profit: UInt64
53
- cost: UInt64
54
- }
55
-
56
- export function calculateSaleValue(
57
- cargo: ServerContract.Types.cargo_item[],
58
- prices: Map<number, UInt64>
59
- ): SaleValue {
60
- if (cargo.length === 0) {
61
- return {revenue: UInt64.from(0), profit: UInt64.from(0), cost: UInt64.from(0)}
62
- }
63
-
64
- let revenue = UInt64.from(0)
65
- let cost = UInt64.from(0)
66
-
67
- for (const item of cargo) {
68
- if (UInt32.from(item.quantity).equals(UInt32.from(0))) continue
69
-
70
- const goodId = Number(item.item_id)
71
- const salePrice = prices.get(goodId)
72
-
73
- if (salePrice) {
74
- revenue = revenue.adding(salePrice.multiplying(item.quantity))
75
- }
76
-
77
- cost = cost.adding(item.unit_cost.multiplying(item.quantity))
78
- }
79
-
80
- const profit = revenue.gte(cost) ? revenue.subtracting(cost) : UInt64.from(0)
81
-
82
- return {
83
- revenue,
84
- profit,
85
- cost,
86
- }
87
- }
88
-
89
- export function calculateSaleValueFromArray(
90
- cargo: ServerContract.Types.cargo_item[],
91
- prices: UInt64[]
92
- ): SaleValue {
93
- const priceMap = new Map<number, UInt64>()
94
- prices.forEach((price, index) => {
95
- priceMap.set(index, price)
96
- })
97
- return calculateSaleValue(cargo, priceMap)
98
- }
99
-
100
- export function afterSellItems(
44
+ export function afterRemoveItems(
101
45
  cargo: ServerContract.Types.cargo_item[],
102
- goodsToSell: Array<{goodId: number; quantity: number}>
46
+ goodsToRemove: Array<{goodId: number; quantity: number}>
103
47
  ): EntityInventory[] {
104
48
  if (cargo.length === 0) {
105
49
  return []
106
50
  }
107
51
 
108
52
  return cargo.map((item) => {
109
- const saleItem = goodsToSell.find((s) => Number(item.item_id) === s.goodId)
110
- if (!saleItem) {
53
+ const removeItem = goodsToRemove.find((s) => Number(item.item_id) === s.goodId)
54
+ if (!removeItem) {
111
55
  return new EntityInventory(item)
112
56
  }
113
57
 
114
58
  const currentQty = Number(item.quantity)
115
- const newQty = Math.max(0, currentQty - saleItem.quantity)
59
+ const newQty = Math.max(0, currentQty - removeItem.quantity)
116
60
 
117
61
  return new EntityInventory(
118
62
  ServerContract.Types.cargo_item.from({
119
63
  item_id: item.item_id,
120
64
  quantity: UInt32.from(newQty),
121
- unit_cost: item.unit_cost,
122
65
  })
123
66
  )
124
67
  })
125
68
  }
126
69
 
127
- export function afterSellAllItems(cargo: ServerContract.Types.cargo_item[]): EntityInventory[] {
70
+ export function afterRemoveAllItems(cargo: ServerContract.Types.cargo_item[]): EntityInventory[] {
128
71
  if (cargo.length === 0) {
129
72
  return []
130
73
  }
@@ -135,7 +78,6 @@ export function afterSellAllItems(cargo: ServerContract.Types.cargo_item[]): Ent
135
78
  ServerContract.Types.cargo_item.from({
136
79
  item_id: item.item_id,
137
80
  quantity: UInt32.from(0),
138
- unit_cost: item.unit_cost,
139
81
  })
140
82
  )
141
83
  )
@@ -68,3 +68,21 @@ export class Container extends ServerContract.Types.entity_info {
68
68
  return this.coordinates.z?.toNumber() || 0
69
69
  }
70
70
  }
71
+
72
+ export function computeContainerCapabilities(stats: Record<string, number>): {
73
+ hullmass: number
74
+ capacity: number
75
+ } {
76
+ const density = stats['density'] ?? 500
77
+ const strength = stats['strength'] ?? 500
78
+ const ductility = stats['ductility'] ?? 500
79
+ const purity = stats['purity'] ?? 500
80
+
81
+ const hullmass = 25000 + 75 * density
82
+
83
+ const statSum = strength + ductility + purity
84
+ const exponent = statSum / 2997
85
+ const capacity = Math.floor(1000000 * Math.pow(10, exponent))
86
+
87
+ return {hullmass, capacity}
88
+ }
@@ -29,10 +29,6 @@ export class EntityInventory extends ServerContract.Types.cargo_item {
29
29
  return UInt64.from(this.unitMass).multiplying(this.quantity)
30
30
  }
31
31
 
32
- get totalCost(): UInt64 {
33
- return this.unit_cost.multiplying(this.quantity)
34
- }
35
-
36
32
  get hasCargo(): boolean {
37
33
  return UInt32.from(this.quantity).gt(UInt32.from(0))
38
34
  }
@@ -20,10 +20,6 @@ export class InventoryAccessor {
20
20
  return this.items.reduce((sum, c) => sum.adding(c.totalMass), UInt64.from(0))
21
21
  }
22
22
 
23
- get totalValue(): UInt64 {
24
- return this.items.reduce((sum, c) => sum.adding(c.totalCost), UInt64.from(0))
25
- }
26
-
27
23
  forItem(goodId: UInt64Type): EntityInventory | undefined {
28
24
  return this.items.find((c) => c.item_id.equals(goodId))
29
25
  }
@@ -1,35 +1,22 @@
1
- import {Checksum256, Checksum256Type, UInt16, UInt16Type, UInt64} from '@wharfkit/antelope'
2
- import {ServerContract} from '../contracts'
3
- import {Coordinates, CoordinatesType, Distance, ItemPrice, LocationType} from '../types'
1
+ import {Checksum256, Checksum256Type, UInt16Type, UInt64} from '@wharfkit/antelope'
2
+ import {Coordinates, CoordinatesType, Distance, LocationType} from '../types'
4
3
  import {getLocationType, hasSystem, isExtractableLocation} from '../utils/system'
5
4
  import {findNearbyPlanets} from '../travel/travel'
6
5
 
7
- /**
8
- * Location helper class for working with game coordinates.
9
- * Provides system detection, market price caching, nearby planet finding, and supply tracking.
10
- */
11
6
  export class Location {
12
7
  readonly coordinates: Coordinates
13
- private _marketPrices?: ItemPrice[]
14
8
  private _gameSeed?: Checksum256
15
9
  private _hasSystem?: boolean
16
- private _locationRows?: ServerContract.Types.supply_row[]
17
10
  private _epoch?: UInt64
18
11
 
19
12
  constructor(coordinates: CoordinatesType) {
20
13
  this.coordinates = Coordinates.from(coordinates)
21
14
  }
22
15
 
23
- /**
24
- * Create a Location from coordinates
25
- */
26
16
  static from(coordinates: CoordinatesType): Location {
27
17
  return new Location(Coordinates.from(coordinates))
28
18
  }
29
19
 
30
- /**
31
- * Check if this location has a system (planet, asteroid, or nebula)
32
- */
33
20
  hasSystemAt(gameSeed: Checksum256Type): boolean {
34
21
  const seed = Checksum256.from(gameSeed)
35
22
  if (this._hasSystem === undefined || !this._gameSeed?.equals(seed)) {
@@ -39,214 +26,32 @@ export class Location {
39
26
  return this._hasSystem
40
27
  }
41
28
 
42
- /**
43
- * Get the location type (EMPTY, PLANET, ASTEROID, or NEBULA)
44
- */
45
29
  getLocationTypeAt(gameSeed: Checksum256Type): LocationType {
46
30
  return getLocationType(gameSeed, this.coordinates)
47
31
  }
48
32
 
49
- /**
50
- * Check if this location is extractable (asteroid or nebula)
51
- */
52
33
  isExtractableAt(gameSeed: Checksum256Type): boolean {
53
34
  return isExtractableLocation(this.getLocationTypeAt(gameSeed))
54
35
  }
55
36
 
56
- /**
57
- * Set cached market prices for this location
58
- */
59
- setMarketPrices(prices: ItemPrice[]): void {
60
- this._marketPrices = prices
61
- }
62
-
63
- /**
64
- * Get cached market prices (returns undefined if not cached)
65
- */
66
- get marketPrices(): ItemPrice[] | undefined {
67
- return this._marketPrices
68
- }
69
-
70
- /**
71
- * Get price for a specific good (from cache)
72
- */
73
- getPrice(goodId: UInt16Type): ItemPrice | undefined {
74
- if (!this._marketPrices) return undefined
75
- return this._marketPrices.find((p) => p.id.equals(goodId))
76
- }
77
-
78
- /**
79
- * Find nearby planets from this location
80
- */
81
37
  findNearby(gameSeed: Checksum256Type, maxDistance: UInt16Type = 20): Distance[] {
82
38
  return findNearbyPlanets(Checksum256.from(gameSeed), this.coordinates, maxDistance)
83
39
  }
84
40
 
85
- /**
86
- * Check if this location equals another location
87
- */
88
41
  equals(other: CoordinatesType | Location): boolean {
89
42
  const otherCoords = other instanceof Location ? other.coordinates : Coordinates.from(other)
90
43
  return this.coordinates.equals(otherCoords)
91
44
  }
92
45
 
93
- /**
94
- * Set location rows (supply data) for this location
95
- */
96
- setLocationRows(rows: ServerContract.Types.supply_row[], epoch: UInt64): void {
97
- this._locationRows = rows
98
- this._epoch = epoch
99
- }
100
-
101
- /**
102
- * Get cached location rows (supply data)
103
- */
104
- get locationRows(): ServerContract.Types.supply_row[] | undefined {
105
- return this._locationRows
106
- }
107
-
108
- /**
109
- * Get supply for a specific good at this location
110
- * Returns undefined if location rows not cached or good not found
111
- */
112
- getSupply(goodId: UInt16Type): UInt16 | undefined {
113
- if (!this._locationRows) return undefined
114
- const row = this._locationRows.find(
115
- (r) => r.item_id.equals(goodId) && this._epoch && r.epoch.equals(this._epoch)
116
- )
117
- return row ? row.supply : undefined
118
- }
119
-
120
- /**
121
- * Get all available goods at this location (goods with supply > 0)
122
- * Returns undefined if location rows not cached
123
- */
124
- get availableGoods(): ServerContract.Types.supply_row[] | undefined {
125
- if (!this._locationRows) return undefined
126
- return this._locationRows.filter(
127
- (r) => this._epoch && r.epoch.equals(this._epoch) && r.supply.gt(UInt16.from(0))
128
- )
129
- }
130
-
131
- /**
132
- * Check if a specific good is available (has supply)
133
- * Returns false if location rows not cached
134
- */
135
- hasGood(goodId: UInt16Type): boolean {
136
- const supply = this.getSupply(goodId)
137
- return supply !== undefined && supply.gt(UInt16.from(0))
138
- }
139
-
140
- /**
141
- * Get the epoch for cached location data
142
- */
143
46
  get epoch(): UInt64 | undefined {
144
47
  return this._epoch
145
48
  }
146
49
 
147
- /**
148
- * Check if cached data exists
149
- */
150
- get hasCachedData(): boolean {
151
- return this._marketPrices !== undefined || this._locationRows !== undefined
152
- }
153
-
154
- /**
155
- * Check if supply data is cached
156
- */
157
- get hasSupplyData(): boolean {
158
- return this._locationRows !== undefined
159
- }
160
-
161
- /**
162
- * Clear all cached data
163
- */
164
50
  clearCache(): void {
165
- this._marketPrices = undefined
166
- this._locationRows = undefined
167
51
  this._epoch = undefined
168
52
  }
169
-
170
- /**
171
- * Create optimistic Location with updated supply after purchase/sale.
172
- * Matches contract: update_location_supply (delta can be positive or negative)
173
- * Contract reference: market.cpp:53, 123-151
174
- *
175
- * @param goodId - Good ID to update supply for
176
- * @param quantityDelta - Change in supply (negative for purchase, positive for sale)
177
- * @returns New Location with updated supply in cached data
178
- *
179
- * @example
180
- * // After buying 10 units (supply decreases)
181
- * const newLocation = location.withUpdatedSupply(1, -10)
182
- *
183
- * // After selling 5 units (supply increases)
184
- * const newLocation = location.withUpdatedSupply(1, 5)
185
- */
186
- withUpdatedSupply(goodId: UInt16Type, quantityDelta: number): Location {
187
- const newLocation = Location.from(this.coordinates)
188
-
189
- // Copy market prices if cached
190
- if (this._marketPrices) {
191
- newLocation._marketPrices = this._marketPrices.map((price) => {
192
- if (price.id.equals(goodId)) {
193
- const currentSupply = UInt16.from(price.supply)
194
- const delta = UInt16.from(Math.abs(quantityDelta))
195
- const newSupply =
196
- quantityDelta < 0
197
- ? currentSupply.gte(delta)
198
- ? currentSupply.subtracting(delta)
199
- : UInt16.from(0)
200
- : currentSupply.adding(quantityDelta)
201
-
202
- return ItemPrice.from({
203
- id: price.id,
204
- item: price.item,
205
- price: price.price,
206
- supply: newSupply,
207
- })
208
- }
209
- return price
210
- })
211
- }
212
-
213
- // Copy location rows if cached
214
- if (this._locationRows && this._epoch) {
215
- newLocation._locationRows = this._locationRows.map((row) => {
216
- if (row.item_id.equals(goodId) && row.epoch.equals(this._epoch!)) {
217
- const currentSupply = UInt16.from(row.supply)
218
- const delta = UInt16.from(Math.abs(quantityDelta))
219
- const newSupply =
220
- quantityDelta < 0
221
- ? currentSupply.gte(delta)
222
- ? currentSupply.subtracting(delta)
223
- : UInt16.from(0)
224
- : currentSupply.adding(quantityDelta)
225
-
226
- return ServerContract.Types.supply_row.from({
227
- id: row.id,
228
- coordinates: row.coordinates,
229
- epoch: row.epoch,
230
- item_id: row.item_id,
231
- supply: newSupply,
232
- })
233
- }
234
- return row
235
- })
236
- newLocation._epoch = this._epoch
237
- }
238
-
239
- // Copy other cached data
240
- newLocation._gameSeed = this._gameSeed
241
- newLocation._hasSystem = this._hasSystem
242
-
243
- return newLocation
244
- }
245
53
  }
246
54
 
247
- /**
248
- * Helper function to convert various coordinate types to Location
249
- */
250
55
  export function toLocation(coords: CoordinatesType | Location): Location {
251
56
  if (coords instanceof Location) {
252
57
  return coords