@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.
- package/README.md +349 -1
- package/lib/shipload.d.ts +809 -314
- package/lib/shipload.js +2745 -1740
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +2158 -1154
- package/lib/shipload.m.js.map +1 -1
- package/package.json +1 -2
- package/src/capabilities/extraction.ts +30 -0
- package/src/capabilities/guards.ts +43 -0
- package/src/capabilities/index.ts +5 -0
- package/src/capabilities/loading.ts +8 -0
- package/src/capabilities/movement.ts +29 -0
- package/src/capabilities/storage.ts +67 -0
- package/src/contracts/server.ts +506 -183
- package/src/data/items.json +16 -0
- package/src/derivation/index.ts +25 -0
- package/src/derivation/location-size.ts +15 -0
- package/src/derivation/resources.ts +141 -0
- package/src/derivation/stratum.ts +116 -0
- package/src/entities/cargo-utils.ts +98 -3
- package/src/entities/container.ts +70 -0
- package/src/entities/entity-inventory.ts +13 -9
- package/src/entities/inventory-accessor.ts +46 -0
- package/src/entities/location.ts +31 -17
- package/src/entities/makers.ts +69 -0
- package/src/entities/player.ts +2 -1
- package/src/entities/ship.ts +93 -440
- package/src/entities/warehouse.ts +29 -145
- package/src/errors.ts +4 -4
- package/src/index-module.ts +62 -4
- package/src/managers/actions.ts +75 -31
- package/src/managers/entities.ts +22 -5
- package/src/managers/locations.ts +34 -15
- package/src/managers/trades.ts +7 -7
- package/src/market/items.ts +31 -0
- package/src/market/market.ts +12 -13
- package/src/scheduling/accessor.ts +82 -0
- package/src/scheduling/projection.ts +126 -54
- package/src/scheduling/schedule.ts +24 -0
- package/src/trading/collect.ts +25 -26
- package/src/trading/deal.ts +8 -9
- package/src/trading/trade.ts +9 -9
- package/src/travel/travel.ts +69 -8
- package/src/types/capabilities.ts +79 -0
- package/src/types/entity-traits.ts +70 -0
- package/src/types/entity.ts +36 -0
- package/src/types/index.ts +3 -0
- package/src/types.ts +92 -15
- package/src/utils/hash.ts +1 -1
- package/src/utils/system.ts +97 -4
- package/src/data/goods.json +0 -23
- 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
|
|
21
|
+
export function getCargoForItem(
|
|
21
22
|
cargo: EntityInventory[],
|
|
22
23
|
goodId: UInt64Type
|
|
23
24
|
): EntityInventory | undefined {
|
|
24
|
-
return cargo.find((c) => c.
|
|
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 {
|
|
4
|
-
import {
|
|
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
|
|
7
|
+
private _item?: Item
|
|
8
8
|
|
|
9
|
-
get
|
|
10
|
-
if (!this.
|
|
11
|
-
this.
|
|
9
|
+
get item(): Item {
|
|
10
|
+
if (!this._item) {
|
|
11
|
+
this._item = getItem(this.item_id)
|
|
12
12
|
}
|
|
13
|
-
return this.
|
|
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.
|
|
21
|
+
return this.item.name
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
get unitMass(): UInt32 {
|
|
21
|
-
return this.
|
|
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
|
+
}
|