@shipload/sdk 2.0.0-rc1 → 2.0.0-rc2

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 (42) hide show
  1. package/README.md +349 -1
  2. package/lib/shipload.d.ts +609 -248
  3. package/lib/shipload.js +1683 -1031
  4. package/lib/shipload.js.map +1 -1
  5. package/lib/shipload.m.js +1613 -1047
  6. package/lib/shipload.m.js.map +1 -1
  7. package/package.json +1 -2
  8. package/src/capabilities/extraction.ts +37 -0
  9. package/src/capabilities/guards.ts +43 -0
  10. package/src/capabilities/index.ts +5 -0
  11. package/src/capabilities/loading.ts +8 -0
  12. package/src/capabilities/movement.ts +29 -0
  13. package/src/capabilities/storage.ts +67 -0
  14. package/src/contracts/server.ts +340 -136
  15. package/src/data/goods.json +2 -2
  16. package/src/entities/cargo-utils.ts +96 -1
  17. package/src/entities/container.ts +70 -0
  18. package/src/entities/inventory-accessor.ts +46 -0
  19. package/src/entities/location.ts +22 -8
  20. package/src/entities/makers.ts +69 -0
  21. package/src/entities/player.ts +2 -1
  22. package/src/entities/ship.ts +86 -437
  23. package/src/entities/warehouse.ts +28 -144
  24. package/src/index-module.ts +34 -1
  25. package/src/managers/actions.ts +60 -28
  26. package/src/managers/entities.ts +22 -5
  27. package/src/managers/locations.ts +28 -9
  28. package/src/managers/trades.ts +2 -2
  29. package/src/market/market.ts +3 -4
  30. package/src/scheduling/accessor.ts +82 -0
  31. package/src/scheduling/projection.ts +125 -53
  32. package/src/scheduling/schedule.ts +24 -0
  33. package/src/trading/collect.ts +0 -1
  34. package/src/trading/deal.ts +0 -1
  35. package/src/travel/travel.ts +63 -2
  36. package/src/types/capabilities.ts +79 -0
  37. package/src/types/entity-traits.ts +70 -0
  38. package/src/types/entity.ts +36 -0
  39. package/src/types/index.ts +3 -0
  40. package/src/types.ts +75 -8
  41. package/src/utils/hash.ts +1 -1
  42. package/src/utils/system.ts +132 -4
@@ -7,14 +7,14 @@
7
7
  "mass": 15000
8
8
  },
9
9
  {
10
- "id": 2,
10
+ "id": 26,
11
11
  "name": "Iron",
12
12
  "description": "A versatile metal used in construction and manufacturing.",
13
13
  "base_price": 125,
14
14
  "mass": 40000
15
15
  },
16
16
  {
17
- "id": 3,
17
+ "id": 29,
18
18
  "name": "Copper",
19
19
  "description": "A conductive metal vital for electronics and wiring.",
20
20
  "base_price": 200,
@@ -1,5 +1,6 @@
1
- import {UInt64, UInt64Type} from '@wharfkit/antelope'
1
+ import {UInt32, UInt64, UInt64Type} from '@wharfkit/antelope'
2
2
  import {EntityInventory} from './entity-inventory'
3
+ import {ServerContract} from '../contracts'
3
4
 
4
5
  export interface CargoData {
5
6
  cargo: EntityInventory[]
@@ -45,3 +46,97 @@ export function availableCapacity(currentMass: UInt64, maxCapacity: UInt64): UIn
45
46
  export function isFull(currentMass: UInt64, maxCapacity: UInt64): boolean {
46
47
  return currentMass.gte(maxCapacity)
47
48
  }
49
+
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.good_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 afterSellGoods(
101
+ cargo: ServerContract.Types.cargo_item[],
102
+ goodsToSell: Array<{goodId: number; quantity: number}>
103
+ ): EntityInventory[] {
104
+ if (cargo.length === 0) {
105
+ return []
106
+ }
107
+
108
+ return cargo.map((item) => {
109
+ const saleItem = goodsToSell.find((s) => Number(item.good_id) === s.goodId)
110
+ if (!saleItem) {
111
+ return new EntityInventory(item)
112
+ }
113
+
114
+ const currentQty = Number(item.quantity)
115
+ const newQty = Math.max(0, currentQty - saleItem.quantity)
116
+
117
+ return new EntityInventory(
118
+ ServerContract.Types.cargo_item.from({
119
+ good_id: item.good_id,
120
+ quantity: UInt32.from(newQty),
121
+ unit_cost: item.unit_cost,
122
+ })
123
+ )
124
+ })
125
+ }
126
+
127
+ export function afterSellAllGoods(cargo: ServerContract.Types.cargo_item[]): EntityInventory[] {
128
+ if (cargo.length === 0) {
129
+ return []
130
+ }
131
+
132
+ return cargo.map(
133
+ (item) =>
134
+ new EntityInventory(
135
+ ServerContract.Types.cargo_item.from({
136
+ good_id: item.good_id,
137
+ quantity: UInt32.from(0),
138
+ unit_cost: item.unit_cost,
139
+ })
140
+ )
141
+ )
142
+ }
@@ -0,0 +1,70 @@
1
+ import {UInt64, UInt64Type} from '@wharfkit/antelope'
2
+ import {ServerContract} from '../contracts'
3
+ import {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
+ schedule?: ServerContract.Types.schedule
17
+ }
18
+
19
+ export class Container extends ServerContract.Types.entity_info {
20
+ private _sched?: ScheduleAccessor
21
+
22
+ get name(): string {
23
+ return this.entity_name
24
+ }
25
+
26
+ get sched(): ScheduleAccessor {
27
+ return (this._sched ??= new ScheduleAccessor(this))
28
+ }
29
+
30
+ get isIdle(): boolean {
31
+ return this.is_idle
32
+ }
33
+
34
+ isLoading(now: Date): boolean {
35
+ return schedule.isLoading(this, now)
36
+ }
37
+
38
+ isUnloading(now: Date): boolean {
39
+ return schedule.isUnloading(this, now)
40
+ }
41
+
42
+ get location(): Location {
43
+ return Location.from(this.coordinates)
44
+ }
45
+
46
+ get totalMass(): UInt64 {
47
+ return UInt64.from(this.hullmass ?? 0).adding(this.cargomass)
48
+ }
49
+
50
+ get maxCapacity(): UInt64 {
51
+ return UInt64.from(this.capacity)
52
+ }
53
+
54
+ get availableCapacity(): UInt64 {
55
+ const cargo = UInt64.from(this.cargomass)
56
+ return cargo.gte(this.maxCapacity) ? UInt64.from(0) : this.maxCapacity.subtracting(cargo)
57
+ }
58
+
59
+ hasSpace(additionalMass: UInt64): boolean {
60
+ return UInt64.from(this.cargomass).adding(additionalMass).lte(this.maxCapacity)
61
+ }
62
+
63
+ get isFull(): boolean {
64
+ return UInt64.from(this.cargomass).gte(this.maxCapacity)
65
+ }
66
+
67
+ get orbitalAltitude(): number {
68
+ return this.coordinates.z?.toNumber() || 0
69
+ }
70
+ }
@@ -0,0 +1,46 @@
1
+ import {UInt64, UInt64Type} from '@wharfkit/antelope'
2
+ import {EntityInventory} from './entity-inventory'
3
+ import {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
+ get totalValue(): UInt64 {
24
+ return this.items.reduce((sum, c) => sum.adding(c.totalCost), UInt64.from(0))
25
+ }
26
+
27
+ forGood(goodId: UInt64Type): EntityInventory | undefined {
28
+ return this.items.find((c) => c.good_id.equals(goodId))
29
+ }
30
+
31
+ get sellable(): EntityInventory[] {
32
+ return this.items.filter((c) => c.hasCargo)
33
+ }
34
+
35
+ get hasSellable(): boolean {
36
+ return this.items.some((c) => c.hasCargo)
37
+ }
38
+
39
+ get sellableCount(): number {
40
+ return this.items.filter((c) => c.hasCargo).length
41
+ }
42
+ }
43
+
44
+ export function createInventoryAccessor(entity: HasCargo): InventoryAccessor {
45
+ return new InventoryAccessor(entity)
46
+ }
@@ -1,7 +1,7 @@
1
1
  import {Checksum256, Checksum256Type, UInt16, UInt16Type, UInt64} from '@wharfkit/antelope'
2
2
  import {ServerContract} from '../contracts'
3
- import {Coordinates, CoordinatesType, Distance, GoodPrice} from '../types'
4
- import {hasSystem} from '../utils/system'
3
+ import {Coordinates, CoordinatesType, Distance, GoodPrice, LocationType} from '../types'
4
+ import {getLocationType, hasSystem, isExtractableLocation} from '../utils/system'
5
5
  import {findNearbyPlanets} from '../travel/travel'
6
6
 
7
7
  /**
@@ -13,7 +13,7 @@ export class Location {
13
13
  private _marketPrices?: GoodPrice[]
14
14
  private _gameSeed?: Checksum256
15
15
  private _hasSystem?: boolean
16
- private _locationRows?: ServerContract.Types.location_row[]
16
+ private _locationRows?: ServerContract.Types.supply_row[]
17
17
  private _epoch?: UInt64
18
18
 
19
19
  constructor(coordinates: CoordinatesType) {
@@ -28,7 +28,7 @@ export class Location {
28
28
  }
29
29
 
30
30
  /**
31
- * Check if this location has a system (planet)
31
+ * Check if this location has a system (planet, asteroid, or nebula)
32
32
  */
33
33
  hasSystemAt(gameSeed: Checksum256Type): boolean {
34
34
  const seed = Checksum256.from(gameSeed)
@@ -39,6 +39,20 @@ export class Location {
39
39
  return this._hasSystem
40
40
  }
41
41
 
42
+ /**
43
+ * Get the location type (EMPTY, PLANET, ASTEROID, or NEBULA)
44
+ */
45
+ getLocationTypeAt(gameSeed: Checksum256Type): LocationType {
46
+ return getLocationType(gameSeed, this.coordinates)
47
+ }
48
+
49
+ /**
50
+ * Check if this location is extractable (asteroid or nebula)
51
+ */
52
+ isExtractableAt(gameSeed: Checksum256Type): boolean {
53
+ return isExtractableLocation(this.getLocationTypeAt(gameSeed))
54
+ }
55
+
42
56
  /**
43
57
  * Set cached market prices for this location
44
58
  */
@@ -79,7 +93,7 @@ export class Location {
79
93
  /**
80
94
  * Set location rows (supply data) for this location
81
95
  */
82
- setLocationRows(rows: ServerContract.Types.location_row[], epoch: UInt64): void {
96
+ setLocationRows(rows: ServerContract.Types.supply_row[], epoch: UInt64): void {
83
97
  this._locationRows = rows
84
98
  this._epoch = epoch
85
99
  }
@@ -87,7 +101,7 @@ export class Location {
87
101
  /**
88
102
  * Get cached location rows (supply data)
89
103
  */
90
- get locationRows(): ServerContract.Types.location_row[] | undefined {
104
+ get locationRows(): ServerContract.Types.supply_row[] | undefined {
91
105
  return this._locationRows
92
106
  }
93
107
 
@@ -107,7 +121,7 @@ export class Location {
107
121
  * Get all available goods at this location (goods with supply > 0)
108
122
  * Returns undefined if location rows not cached
109
123
  */
110
- get availableGoods(): ServerContract.Types.location_row[] | undefined {
124
+ get availableGoods(): ServerContract.Types.supply_row[] | undefined {
111
125
  if (!this._locationRows) return undefined
112
126
  return this._locationRows.filter(
113
127
  (r) => this._epoch && r.epoch.equals(this._epoch) && r.supply.gt(UInt16.from(0))
@@ -209,7 +223,7 @@ export class Location {
209
223
  : UInt16.from(0)
210
224
  : currentSupply.adding(quantityDelta)
211
225
 
212
- return ServerContract.Types.location_row.from({
226
+ return ServerContract.Types.supply_row.from({
213
227
  id: row.id,
214
228
  coordinates: row.coordinates,
215
229
  epoch: row.epoch,
@@ -0,0 +1,69 @@
1
+ import {Name, UInt16, UInt32, UInt64} from '@wharfkit/antelope'
2
+ import {ServerContract} from '../contracts'
3
+ import {Ship, ShipStateInput} from './ship'
4
+ import {Warehouse, WarehouseStateInput} from './warehouse'
5
+ import {Container, ContainerStateInput} from './container'
6
+
7
+ export function makeShip(state: ShipStateInput): Ship {
8
+ const entityInfo = ServerContract.Types.entity_info.from({
9
+ type: Name.from('ship'),
10
+ id: UInt64.from(state.id),
11
+ owner: Name.from(state.owner),
12
+ entity_name: state.name,
13
+ coordinates: ServerContract.Types.coordinates.from(state.coordinates),
14
+ hullmass: UInt32.from(state.hullmass),
15
+ capacity: UInt32.from(state.capacity),
16
+ energy: UInt16.from(state.energy),
17
+ cargomass: UInt32.from(0),
18
+ cargo: state.cargo || [],
19
+ is_idle: !state.schedule,
20
+ current_task_elapsed: UInt32.from(0),
21
+ current_task_remaining: UInt32.from(0),
22
+ pending_tasks: [],
23
+ engines: state.engines,
24
+ generator: state.generator,
25
+ loaders: state.loaders,
26
+ schedule: state.schedule,
27
+ })
28
+ return new Ship(entityInfo)
29
+ }
30
+
31
+ export function makeWarehouse(state: WarehouseStateInput): Warehouse {
32
+ const entityInfo = ServerContract.Types.entity_info.from({
33
+ type: Name.from('warehouse'),
34
+ id: UInt64.from(state.id),
35
+ owner: Name.from(state.owner),
36
+ entity_name: state.name,
37
+ coordinates: ServerContract.Types.coordinates.from(state.coordinates),
38
+ capacity: UInt32.from(state.capacity),
39
+ cargomass: UInt32.from(0),
40
+ cargo: state.cargo || [],
41
+ loaders: state.loaders,
42
+ is_idle: !state.schedule,
43
+ current_task_elapsed: UInt32.from(0),
44
+ current_task_remaining: UInt32.from(0),
45
+ pending_tasks: [],
46
+ schedule: state.schedule,
47
+ })
48
+ return new Warehouse(entityInfo)
49
+ }
50
+
51
+ export function makeContainer(state: ContainerStateInput): Container {
52
+ const entityInfo = ServerContract.Types.entity_info.from({
53
+ type: Name.from('container'),
54
+ id: UInt64.from(state.id),
55
+ owner: Name.from(state.owner),
56
+ entity_name: state.name,
57
+ coordinates: ServerContract.Types.coordinates.from(state.coordinates),
58
+ hullmass: UInt32.from(state.hullmass),
59
+ capacity: UInt32.from(state.capacity),
60
+ cargomass: UInt32.from(state.cargomass || 0),
61
+ cargo: [],
62
+ is_idle: !state.schedule,
63
+ current_task_elapsed: UInt32.from(0),
64
+ current_task_remaining: UInt32.from(0),
65
+ pending_tasks: [],
66
+ schedule: state.schedule,
67
+ })
68
+ return new Container(entityInfo)
69
+ }
@@ -37,7 +37,8 @@ export class Player extends ServerContract.Types.player_row {
37
37
  }
38
38
  // Constants for game rules (match smart contract)
39
39
  private static readonly MAX_LOAN = 1000000
40
- private static readonly BASE_SHIP_COST = 100 // pow(5, sequence) * 100
40
+ // Contract formula: 2500 * pow(5, sequence - 1) = 500 * pow(5, sequence)
41
+ private static readonly BASE_SHIP_COST = 500
41
42
  private static readonly SHIP_COST_MULTIPLIER = 5
42
43
 
43
44
  // Optional ship count for nextShipCost calculation