@shipload/sdk 2.0.0-rc1 → 2.0.0-rc3

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 (52) hide show
  1. package/README.md +349 -1
  2. package/lib/shipload.d.ts +809 -314
  3. package/lib/shipload.js +2745 -1740
  4. package/lib/shipload.js.map +1 -1
  5. package/lib/shipload.m.js +2158 -1154
  6. package/lib/shipload.m.js.map +1 -1
  7. package/package.json +1 -2
  8. package/src/capabilities/extraction.ts +30 -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 +506 -183
  15. package/src/data/items.json +16 -0
  16. package/src/derivation/index.ts +25 -0
  17. package/src/derivation/location-size.ts +15 -0
  18. package/src/derivation/resources.ts +141 -0
  19. package/src/derivation/stratum.ts +116 -0
  20. package/src/entities/cargo-utils.ts +98 -3
  21. package/src/entities/container.ts +70 -0
  22. package/src/entities/entity-inventory.ts +13 -9
  23. package/src/entities/inventory-accessor.ts +46 -0
  24. package/src/entities/location.ts +31 -17
  25. package/src/entities/makers.ts +69 -0
  26. package/src/entities/player.ts +2 -1
  27. package/src/entities/ship.ts +93 -440
  28. package/src/entities/warehouse.ts +29 -145
  29. package/src/errors.ts +4 -4
  30. package/src/index-module.ts +62 -4
  31. package/src/managers/actions.ts +75 -31
  32. package/src/managers/entities.ts +22 -5
  33. package/src/managers/locations.ts +34 -15
  34. package/src/managers/trades.ts +7 -7
  35. package/src/market/items.ts +31 -0
  36. package/src/market/market.ts +12 -13
  37. package/src/scheduling/accessor.ts +82 -0
  38. package/src/scheduling/projection.ts +126 -54
  39. package/src/scheduling/schedule.ts +24 -0
  40. package/src/trading/collect.ts +25 -26
  41. package/src/trading/deal.ts +8 -9
  42. package/src/trading/trade.ts +9 -9
  43. package/src/travel/travel.ts +69 -8
  44. package/src/types/capabilities.ts +79 -0
  45. package/src/types/entity-traits.ts +70 -0
  46. package/src/types/entity.ts +36 -0
  47. package/src/types/index.ts +3 -0
  48. package/src/types.ts +92 -15
  49. package/src/utils/hash.ts +1 -1
  50. package/src/utils/system.ts +97 -4
  51. package/src/data/goods.json +0 -23
  52. package/src/market/goods.ts +0 -31
@@ -0,0 +1,16 @@
1
+ [
2
+ {"id": 1, "name": "Hydrogen", "description": "A lightweight gas used for fuel cells and propulsion.", "base_price": 50, "mass": 15000, "category": "gas", "rarity": "common", "color": "#7EC8E3"},
3
+ {"id": 2, "name": "Helium", "description": "An inert noble gas used in energy systems.", "base_price": 75, "mass": 2000, "category": "gas", "rarity": "uncommon", "color": "#F5E6CC"},
4
+ {"id": 6, "name": "Carbon", "description": "A versatile element for life support and coatings.", "base_price": 100, "mass": 12000, "category": "organic", "rarity": "common", "color": "#4A4A4A"},
5
+ {"id": 14, "name": "Silicon", "description": "A semiconductor used in sensors and computing.", "base_price": 150, "mass": 28000, "category": "mineral", "rarity": "common", "color": "#B8A9C9"},
6
+ {"id": 18, "name": "Argon", "description": "A noble gas used in industrial and energy applications.", "base_price": 60, "mass": 8000, "category": "gas", "rarity": "rare", "color": "#9B59B6"},
7
+ {"id": 22, "name": "Titanium", "description": "A strong, lightweight metal for ship construction.", "base_price": 300, "mass": 48000, "category": "metal", "rarity": "rare", "color": "#C0C0C0"},
8
+ {"id": 26, "name": "Iron", "description": "A versatile metal used in hulls and structures.", "base_price": 125, "mass": 40000, "category": "metal", "rarity": "common", "color": "#B7410E"},
9
+ {"id": 29, "name": "Copper", "description": "A conductive metal for electronics and wiring.", "base_price": 200, "mass": 60000, "category": "metal", "rarity": "uncommon", "color": "#B87333"},
10
+ {"id": 54, "name": "Xenon", "description": "A rare noble gas for advanced propulsion systems.", "base_price": 175, "mass": 20000, "category": "gas", "rarity": "epic", "color": "#5B2C6F"},
11
+ {"id": 74, "name": "Tungsten", "description": "An extremely dense metal for heavy-duty applications.", "base_price": 500, "mass": 80000, "category": "metal", "rarity": "epic", "color": "#708090"},
12
+ {"id": 1000, "name": "Quartz", "description": "A crystalline mineral for sensors and optics.", "base_price": 200, "mass": 35000, "category": "mineral", "rarity": "uncommon", "color": "#E8D5B7"},
13
+ {"id": 1001, "name": "Sapphire", "description": "A precious crystal for precision instruments.", "base_price": 400, "mass": 45000, "category": "mineral", "rarity": "rare", "color": "#0F52BA"},
14
+ {"id": 1002, "name": "Polymers", "description": "Synthetic materials for coatings and consumables.", "base_price": 150, "mass": 25000, "category": "organic", "rarity": "rare", "color": "#2ECC71"},
15
+ {"id": 1003, "name": "Biomass", "description": "Organic matter for life support systems.", "base_price": 100, "mass": 30000, "category": "organic", "rarity": "uncommon", "color": "#8B4513"}
16
+ ]
@@ -0,0 +1,25 @@
1
+ export {deriveStratum, deriveResourceStats} from './stratum'
2
+ export type {StratumInfo, ResourceStats} from './stratum'
3
+ export {deriveLocationSize} from './location-size'
4
+ export {
5
+ getEligibleResources,
6
+ getResourceWeight,
7
+ getLocationCandidates,
8
+ getDepthThreshold,
9
+ getResourceRarity,
10
+ depthScaleFactor,
11
+ DEPTH_THRESHOLD_COMMON,
12
+ DEPTH_THRESHOLD_UNCOMMON,
13
+ DEPTH_THRESHOLD_RARE,
14
+ DEPTH_THRESHOLD_EPIC,
15
+ DEPTH_THRESHOLD_LEGENDARY,
16
+ LOCATION_MIN_DEPTH,
17
+ LOCATION_MAX_DEPTH,
18
+ YIELD_THRESHOLD,
19
+ PLANET_SUBTYPE_GAS_GIANT,
20
+ PLANET_SUBTYPE_ROCKY,
21
+ PLANET_SUBTYPE_TERRESTRIAL,
22
+ PLANET_SUBTYPE_ICY,
23
+ PLANET_SUBTYPE_OCEAN,
24
+ PLANET_SUBTYPE_INDUSTRIAL,
25
+ } from './resources'
@@ -0,0 +1,15 @@
1
+ import {ServerContract} from '../contracts'
2
+ import {LocationType} from '../types'
3
+ import {LOCATION_MAX_DEPTH, LOCATION_MIN_DEPTH} from './resources'
4
+
5
+ export function deriveLocationSize(loc: ServerContract.Types.location_static): number {
6
+ if (loc.type.toNumber() === LocationType.EMPTY) return 0
7
+
8
+ const raw = (loc.seed0.toNumber() << 8) | loc.seed1.toNumber()
9
+ const normalized = raw / 65535
10
+
11
+ const curved = Math.pow(normalized, 3.0)
12
+
13
+ const range = LOCATION_MAX_DEPTH - LOCATION_MIN_DEPTH
14
+ return Math.floor(LOCATION_MIN_DEPTH + curved * range)
15
+ }
@@ -0,0 +1,141 @@
1
+ import {ResourceRarity} from '../types'
2
+
3
+ export const DEPTH_THRESHOLD_COMMON = 0
4
+ export const DEPTH_THRESHOLD_UNCOMMON = 2000
5
+ export const DEPTH_THRESHOLD_RARE = 10000
6
+ export const DEPTH_THRESHOLD_EPIC = 30000
7
+ export const DEPTH_THRESHOLD_LEGENDARY = 55000
8
+
9
+ export const LOCATION_MIN_DEPTH = 500
10
+ export const LOCATION_MAX_DEPTH = 65535
11
+
12
+ export const YIELD_THRESHOLD = Math.floor(0.003 * 0xffffffff)
13
+
14
+ export const PLANET_SUBTYPE_GAS_GIANT = 0
15
+ export const PLANET_SUBTYPE_ROCKY = 1
16
+ export const PLANET_SUBTYPE_TERRESTRIAL = 2
17
+ export const PLANET_SUBTYPE_ICY = 3
18
+ export const PLANET_SUBTYPE_OCEAN = 4
19
+ export const PLANET_SUBTYPE_INDUSTRIAL = 5
20
+
21
+ interface ResourceEntry {
22
+ id: number
23
+ rarity: ResourceRarity
24
+ }
25
+
26
+ const RESOURCE_CATALOG: ResourceEntry[] = [
27
+ {id: 26, rarity: 'common'},
28
+ {id: 1, rarity: 'common'},
29
+ {id: 14, rarity: 'common'},
30
+ {id: 6, rarity: 'common'},
31
+ {id: 29, rarity: 'uncommon'},
32
+ {id: 2, rarity: 'uncommon'},
33
+ {id: 1000, rarity: 'uncommon'},
34
+ {id: 1003, rarity: 'uncommon'},
35
+ {id: 22, rarity: 'rare'},
36
+ {id: 18, rarity: 'rare'},
37
+ {id: 1001, rarity: 'rare'},
38
+ {id: 1002, rarity: 'rare'},
39
+ {id: 74, rarity: 'epic'},
40
+ {id: 54, rarity: 'epic'},
41
+ ]
42
+
43
+ export function getDepthThreshold(rarity: ResourceRarity): number {
44
+ switch (rarity) {
45
+ case 'common':
46
+ return DEPTH_THRESHOLD_COMMON
47
+ case 'uncommon':
48
+ return DEPTH_THRESHOLD_UNCOMMON
49
+ case 'rare':
50
+ return DEPTH_THRESHOLD_RARE
51
+ case 'epic':
52
+ return DEPTH_THRESHOLD_EPIC
53
+ case 'legendary':
54
+ return DEPTH_THRESHOLD_LEGENDARY
55
+ }
56
+ }
57
+
58
+ export function getResourceRarity(itemId: number): ResourceRarity {
59
+ const entry = RESOURCE_CATALOG.find((r) => r.id === itemId)
60
+ return entry ? entry.rarity : 'legendary'
61
+ }
62
+
63
+ export function getResourceWeight(itemId: number, stratum: number): number {
64
+ const rarity = getResourceRarity(itemId)
65
+ const threshold = getDepthThreshold(rarity)
66
+ if (stratum < threshold) return 0
67
+
68
+ const depthAbove = stratum - threshold
69
+
70
+ switch (rarity) {
71
+ case 'common':
72
+ if (stratum < 2000) return 100
73
+ if (stratum < 10000) return 80
74
+ if (stratum < 30000) return 50
75
+ return 30
76
+ case 'uncommon':
77
+ if (depthAbove < 3000) return 40
78
+ if (depthAbove < 8000) return 60
79
+ return 50
80
+ case 'rare':
81
+ if (depthAbove < 5000) return 20
82
+ if (depthAbove < 15000) return 35
83
+ return 40
84
+ case 'epic':
85
+ if (depthAbove < 10000) return 10
86
+ if (depthAbove < 25000) return 20
87
+ return 30
88
+ case 'legendary':
89
+ return 10
90
+ }
91
+ }
92
+
93
+ const ASTEROID_RESOURCES = [26, 29, 22, 74, 14, 1000, 1001]
94
+ const NEBULA_RESOURCES = [1, 2, 18, 54]
95
+ const GAS_GIANT_RESOURCES = [1, 2, 18, 54]
96
+ const ROCKY_RESOURCES = [26, 29, 22, 74, 6, 1003, 1002]
97
+ const TERRESTRIAL_RESOURCES = [6, 1003, 1002, 1001]
98
+ const ICY_RESOURCES = [6, 14, 1000, 1001, 18]
99
+ const OCEAN_RESOURCES = [1, 2, 1003, 1002]
100
+ const INDUSTRIAL_RESOURCES = [26, 29, 22, 74, 14, 1000, 54]
101
+
102
+ export function getLocationCandidates(locationType: number, subtype: number): number[] {
103
+ if (locationType === 2) return ASTEROID_RESOURCES
104
+ if (locationType === 3) return NEBULA_RESOURCES
105
+ if (locationType === 1) {
106
+ switch (subtype) {
107
+ case PLANET_SUBTYPE_GAS_GIANT:
108
+ return GAS_GIANT_RESOURCES
109
+ case PLANET_SUBTYPE_ROCKY:
110
+ return ROCKY_RESOURCES
111
+ case PLANET_SUBTYPE_TERRESTRIAL:
112
+ return TERRESTRIAL_RESOURCES
113
+ case PLANET_SUBTYPE_ICY:
114
+ return ICY_RESOURCES
115
+ case PLANET_SUBTYPE_OCEAN:
116
+ return OCEAN_RESOURCES
117
+ case PLANET_SUBTYPE_INDUSTRIAL:
118
+ return INDUSTRIAL_RESOURCES
119
+ }
120
+ }
121
+ return []
122
+ }
123
+
124
+ export function getEligibleResources(
125
+ locationType: number,
126
+ subtype: number,
127
+ stratum: number
128
+ ): number[] {
129
+ const candidates = getLocationCandidates(locationType, subtype)
130
+ return candidates.filter((itemId) => {
131
+ const rarity = getResourceRarity(itemId)
132
+ const threshold = getDepthThreshold(rarity)
133
+ return stratum >= threshold
134
+ })
135
+ }
136
+
137
+ export function depthScaleFactor(stratum: number): number {
138
+ if (stratum <= 1) return 1.0
139
+ const logScale = Math.log(stratum) / Math.log(65535)
140
+ return 1.0 + logScale * 2.0
141
+ }
@@ -0,0 +1,116 @@
1
+ import {Bytes, Checksum256, Checksum256Type} from '@wharfkit/antelope'
2
+ import {hash512} from '../utils/hash'
3
+ import {Coordinates, CoordinatesType} from '../types'
4
+ import {
5
+ depthScaleFactor,
6
+ getEligibleResources,
7
+ getResourceWeight,
8
+ YIELD_THRESHOLD,
9
+ } from './resources'
10
+
11
+ export interface StratumInfo {
12
+ itemId: number
13
+ seed: bigint
14
+ richness: number
15
+ reserve: number
16
+ }
17
+
18
+ export interface ResourceStats {
19
+ purity: number
20
+ density: number
21
+ reactivity: number
22
+ resonance: number
23
+ }
24
+
25
+ export function deriveStratum(
26
+ epochSeed: Checksum256Type,
27
+ coords: CoordinatesType,
28
+ stratum: number,
29
+ locationType: number,
30
+ subtype: number,
31
+ _maxDepth: number
32
+ ): StratumInfo {
33
+ const seed = Checksum256.from(epochSeed)
34
+ const c = Coordinates.from(coords)
35
+ const input = `stratum-${c.x}-${c.y}-${stratum}`
36
+ const hashResult = hash512(seed, input)
37
+ const bytes = hashResult.array
38
+
39
+ const rawReserve = ((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]) >>> 0
40
+
41
+ let reserve = 0
42
+ if (rawReserve <= YIELD_THRESHOLD) {
43
+ const baseReserve = (rawReserve % 333) + 1
44
+ const scale = depthScaleFactor(stratum)
45
+ reserve = Math.floor(baseReserve * scale)
46
+ }
47
+
48
+ if (reserve === 0) return {itemId: 0, seed: 0n, richness: 0, reserve: 0}
49
+
50
+ const eligible = getEligibleResources(locationType, subtype, stratum)
51
+ if (eligible.length === 0) return {itemId: 0, seed: 0n, richness: 0, reserve: 0}
52
+
53
+ const resourceRoll = ((bytes[4] << 24) | (bytes[5] << 16) | (bytes[6] << 8) | bytes[7]) >>> 0
54
+
55
+ let totalWeight = 0
56
+ for (const id of eligible) {
57
+ totalWeight += getResourceWeight(id, stratum)
58
+ }
59
+
60
+ let selectedItemId = eligible[0]
61
+ if (totalWeight > 0) {
62
+ const roll = resourceRoll % totalWeight
63
+ let cumulative = 0
64
+ for (const id of eligible) {
65
+ cumulative += getResourceWeight(id, stratum)
66
+ if (roll < cumulative) {
67
+ selectedItemId = id
68
+ break
69
+ }
70
+ }
71
+ }
72
+
73
+ const seedBigInt =
74
+ (BigInt(bytes[8]) << 56n) |
75
+ (BigInt(bytes[9]) << 48n) |
76
+ (BigInt(bytes[10]) << 40n) |
77
+ (BigInt(bytes[11]) << 32n) |
78
+ (BigInt(bytes[12]) << 24n) |
79
+ (BigInt(bytes[13]) << 16n) |
80
+ (BigInt(bytes[14]) << 8n) |
81
+ BigInt(bytes[15])
82
+
83
+ const rawRichness = (bytes[16] << 8) | bytes[17]
84
+ const normalized = rawRichness / 65535
85
+ const baseRichness = Math.floor(normalized * normalized * 999) + 1
86
+
87
+ let depthBonus = 0
88
+ if (stratum > 1) {
89
+ depthBonus = 50 * Math.log(stratum) / Math.log(65535)
90
+ }
91
+ const richness = Math.min(Math.floor(baseRichness + depthBonus), 1000)
92
+
93
+ return {itemId: selectedItemId, seed: seedBigInt, richness, reserve}
94
+ }
95
+
96
+ 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))
101
+ const hashBytes = hashResult.array
102
+
103
+ const extractU32 = (offset: number): number =>
104
+ ((hashBytes[offset] << 24) |
105
+ (hashBytes[offset + 1] << 16) |
106
+ (hashBytes[offset + 2] << 8) |
107
+ hashBytes[offset + 3]) >>>
108
+ 0
109
+
110
+ return {
111
+ purity: (extractU32(0) % 1000) + 1,
112
+ density: (extractU32(4) % 1000) + 1,
113
+ reactivity: (extractU32(8) % 1000) + 1,
114
+ resonance: (extractU32(12) % 1000) + 1,
115
+ }
116
+ }
@@ -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[]
@@ -17,11 +18,11 @@ export function cargoValue(cargo: EntityInventory[]): UInt64 {
17
18
  }, UInt64.from(0))
18
19
  }
19
20
 
20
- export function getCargoForGood(
21
+ export function getCargoForItem(
21
22
  cargo: EntityInventory[],
22
23
  goodId: UInt64Type
23
24
  ): EntityInventory | undefined {
24
- return cargo.find((c) => c.good_id.equals(goodId))
25
+ return cargo.find((c) => c.item_id.equals(goodId))
25
26
  }
26
27
 
27
28
  export function hasSpace(
@@ -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.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(
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.item_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
+ item_id: item.item_id,
120
+ quantity: UInt32.from(newQty),
121
+ unit_cost: item.unit_cost,
122
+ })
123
+ )
124
+ })
125
+ }
126
+
127
+ export function afterSellAllItems(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
+ item_id: item.item_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
+ }
@@ -1,24 +1,28 @@
1
1
  import {UInt32, UInt64} from '@wharfkit/antelope'
2
2
  import {ServerContract} from '../contracts'
3
- import {getGood} from '../market/goods'
4
- import {Good} from '../types'
3
+ import {getItem} from '../market/items'
4
+ import {Item} from '../types'
5
5
 
6
6
  export class EntityInventory extends ServerContract.Types.cargo_item {
7
- private _good?: Good
7
+ private _item?: Item
8
8
 
9
- get good(): Good {
10
- if (!this._good) {
11
- this._good = getGood(this.good_id)
9
+ get item(): Item {
10
+ if (!this._item) {
11
+ this._item = getItem(this.item_id)
12
12
  }
13
- return this._good
13
+ return this._item
14
+ }
15
+
16
+ get good(): Item {
17
+ return this.item
14
18
  }
15
19
 
16
20
  get name(): string {
17
- return this.good.name
21
+ return this.item.name
18
22
  }
19
23
 
20
24
  get unitMass(): UInt32 {
21
- return this.good.mass
25
+ return this.item.mass
22
26
  }
23
27
 
24
28
  get totalMass(): UInt64 {
@@ -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
+ forItem(goodId: UInt64Type): EntityInventory | undefined {
28
+ return this.items.find((c) => c.item_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
+ }