@shipload/sdk 2.0.0-rc2 → 2.0.0-rc21
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 +1 -349
- package/lib/shipload.d.ts +1729 -1127
- package/lib/shipload.js +7944 -3165
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +7487 -2840
- package/lib/shipload.m.js.map +1 -1
- package/package.json +6 -4
- package/src/capabilities/crafting.ts +22 -0
- package/src/capabilities/gathering.ts +36 -0
- package/src/capabilities/guards.ts +3 -8
- package/src/capabilities/hauling.ts +22 -0
- package/src/capabilities/index.ts +4 -1
- package/src/capabilities/modules.ts +86 -0
- package/src/capabilities/storage.ts +101 -9
- package/src/contracts/server.ts +785 -293
- package/src/data/capabilities.ts +408 -0
- package/src/data/categories.ts +55 -0
- package/src/data/colors.ts +71 -0
- package/src/data/entities.json +50 -0
- package/src/data/item-ids.ts +75 -0
- package/src/data/items.json +252 -0
- package/src/data/locations.ts +53 -0
- package/src/data/metadata.ts +208 -0
- package/src/data/nebula-adjectives.json +211 -0
- package/src/data/nebula-nouns.json +151 -0
- package/src/data/recipes-runtime.ts +65 -0
- package/src/data/recipes.json +878 -0
- package/src/data/syllables.json +1386 -780
- package/src/data/tiers.ts +45 -0
- package/src/derivation/crafting.ts +348 -0
- package/src/derivation/index.ts +30 -0
- package/src/derivation/location-size.ts +15 -0
- package/src/derivation/resources.ts +112 -0
- package/src/derivation/stats.ts +146 -0
- package/src/derivation/stratum.ts +134 -0
- package/src/derivation/tiers.ts +54 -0
- package/src/entities/cargo-utils.ts +10 -68
- package/src/entities/container.ts +37 -0
- package/src/entities/entity-inventory.ts +13 -13
- package/src/entities/inventory-accessor.ts +2 -6
- package/src/entities/location.ts +5 -200
- package/src/entities/makers.ts +144 -17
- package/src/entities/player.ts +1 -274
- package/src/entities/ship-deploy.ts +258 -0
- package/src/entities/ship.ts +28 -34
- package/src/entities/warehouse.ts +35 -7
- package/src/errors.ts +59 -5
- package/src/format.ts +12 -0
- package/src/index-module.ts +188 -50
- package/src/managers/actions.ts +138 -88
- package/src/managers/context.ts +19 -9
- package/src/managers/index.ts +0 -1
- package/src/managers/locations.ts +2 -85
- package/src/market/items.ts +41 -0
- package/src/nft/description.ts +176 -0
- package/src/nft/deserializers.ts +83 -0
- package/src/nft/index.ts +2 -0
- package/src/resolution/describe-module.ts +165 -0
- package/src/resolution/display-name.ts +43 -0
- package/src/resolution/resolve-item.ts +358 -0
- package/src/scheduling/projection.ts +200 -67
- package/src/scheduling/schedule.ts +2 -2
- package/src/shipload.ts +10 -5
- package/src/subscriptions/connection.ts +154 -0
- package/src/subscriptions/debug.ts +17 -0
- package/src/subscriptions/index.ts +5 -0
- package/src/subscriptions/manager.ts +240 -0
- package/src/subscriptions/mappers.ts +28 -0
- package/src/subscriptions/types.ts +143 -0
- package/src/travel/travel.ts +37 -23
- package/src/types/capabilities.ts +11 -14
- package/src/types/entity-traits.ts +3 -4
- package/src/types/entity.ts +9 -6
- package/src/types.ts +72 -72
- package/src/utils/system.ts +66 -53
- package/src/capabilities/extraction.ts +0 -37
- package/src/data/goods.json +0 -23
- package/src/managers/trades.ts +0 -119
- package/src/market/goods.ts +0 -31
- package/src/market/market.ts +0 -208
- package/src/market/rolls.ts +0 -8
- package/src/trading/collect.ts +0 -938
- package/src/trading/deal.ts +0 -207
- package/src/trading/trade.ts +0 -203
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import {Bytes, Checksum256, Checksum256Type} from '@wharfkit/antelope'
|
|
2
|
+
import {hash512} from '../utils/hash'
|
|
3
|
+
import {Coordinates, 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
|
+
}
|
|
@@ -12,17 +12,11 @@ export function totalCargoMass(cargo: EntityInventory[]): UInt64 {
|
|
|
12
12
|
}, UInt64.from(0))
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export function
|
|
16
|
-
return cargo.reduce((sum, c) => {
|
|
17
|
-
return sum.adding(c.totalCost)
|
|
18
|
-
}, UInt64.from(0))
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function getCargoForGood(
|
|
15
|
+
export function getCargoForItem(
|
|
22
16
|
cargo: EntityInventory[],
|
|
23
17
|
goodId: UInt64Type
|
|
24
18
|
): EntityInventory | undefined {
|
|
25
|
-
return cargo.find((c) => c.
|
|
19
|
+
return cargo.find((c) => c.item_id.equals(goodId))
|
|
26
20
|
}
|
|
27
21
|
|
|
28
22
|
export function hasSpace(
|
|
@@ -47,84 +41,33 @@ export function isFull(currentMass: UInt64, maxCapacity: UInt64): boolean {
|
|
|
47
41
|
return currentMass.gte(maxCapacity)
|
|
48
42
|
}
|
|
49
43
|
|
|
50
|
-
export
|
|
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(
|
|
44
|
+
export function afterRemoveItems(
|
|
101
45
|
cargo: ServerContract.Types.cargo_item[],
|
|
102
|
-
|
|
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
|
|
110
|
-
if (!
|
|
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 -
|
|
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
|
|
70
|
+
export function afterRemoveAllItems(cargo: ServerContract.Types.cargo_item[]): EntityInventory[] {
|
|
128
71
|
if (cargo.length === 0) {
|
|
129
72
|
return []
|
|
130
73
|
}
|
|
@@ -133,9 +76,8 @@ export function afterSellAllGoods(cargo: ServerContract.Types.cargo_item[]): Ent
|
|
|
133
76
|
(item) =>
|
|
134
77
|
new EntityInventory(
|
|
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
|
)
|
|
@@ -13,6 +13,7 @@ export interface ContainerStateInput {
|
|
|
13
13
|
hullmass: number
|
|
14
14
|
capacity: number
|
|
15
15
|
cargomass?: number
|
|
16
|
+
cargo?: ServerContract.Types.cargo_item[]
|
|
16
17
|
schedule?: ServerContract.Types.schedule
|
|
17
18
|
}
|
|
18
19
|
|
|
@@ -68,3 +69,39 @@ export class Container extends ServerContract.Types.entity_info {
|
|
|
68
69
|
return this.coordinates.z?.toNumber() || 0
|
|
69
70
|
}
|
|
70
71
|
}
|
|
72
|
+
|
|
73
|
+
export function computeContainerCapabilities(stats: Record<string, number>): {
|
|
74
|
+
hullmass: number
|
|
75
|
+
capacity: number
|
|
76
|
+
} {
|
|
77
|
+
const density = stats['density'] ?? 500
|
|
78
|
+
const strength = stats['strength'] ?? 500
|
|
79
|
+
const hardness = stats['hardness'] ?? 500
|
|
80
|
+
const saturation = stats['saturation'] ?? 500
|
|
81
|
+
|
|
82
|
+
const hullmass = 25000 + 75 * density
|
|
83
|
+
|
|
84
|
+
const statSum = strength + hardness + saturation
|
|
85
|
+
const exponent = statSum / 2997
|
|
86
|
+
const capacity = Math.floor(1000000 * Math.pow(10, exponent))
|
|
87
|
+
|
|
88
|
+
return {hullmass, capacity}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function computeContainerT2Capabilities(stats: Record<string, number>): {
|
|
92
|
+
hullmass: number
|
|
93
|
+
capacity: number
|
|
94
|
+
} {
|
|
95
|
+
const strength = stats['strength'] ?? 0
|
|
96
|
+
const density = stats['density'] ?? 0
|
|
97
|
+
const hardness = stats['hardness'] ?? 0
|
|
98
|
+
const saturation = stats['saturation'] ?? 0
|
|
99
|
+
|
|
100
|
+
const hullmass = 20000 + 50 * density
|
|
101
|
+
|
|
102
|
+
const statSum = strength + hardness + saturation
|
|
103
|
+
const exponent = statSum / 2500
|
|
104
|
+
const capacity = Math.floor(1500000 * Math.pow(10, exponent))
|
|
105
|
+
|
|
106
|
+
return {hullmass, capacity}
|
|
107
|
+
}
|
|
@@ -1,34 +1,34 @@
|
|
|
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 UInt32.from(this.item.mass)
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
get totalMass(): UInt64 {
|
|
25
29
|
return UInt64.from(this.unitMass).multiplying(this.quantity)
|
|
26
30
|
}
|
|
27
31
|
|
|
28
|
-
get totalCost(): UInt64 {
|
|
29
|
-
return this.unit_cost.multiplying(this.quantity)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
32
|
get hasCargo(): boolean {
|
|
33
33
|
return UInt32.from(this.quantity).gt(UInt32.from(0))
|
|
34
34
|
}
|
|
@@ -20,12 +20,8 @@ export class InventoryAccessor {
|
|
|
20
20
|
return this.items.reduce((sum, c) => sum.adding(c.totalMass), UInt64.from(0))
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
return this.items.
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
forGood(goodId: UInt64Type): EntityInventory | undefined {
|
|
28
|
-
return this.items.find((c) => c.good_id.equals(goodId))
|
|
23
|
+
forItem(goodId: UInt64Type): EntityInventory | undefined {
|
|
24
|
+
return this.items.find((c) => c.item_id.equals(goodId))
|
|
29
25
|
}
|
|
30
26
|
|
|
31
27
|
get sellable(): EntityInventory[] {
|
package/src/entities/location.ts
CHANGED
|
@@ -1,35 +1,22 @@
|
|
|
1
|
-
import {Checksum256, Checksum256Type,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {getLocationType, hasSystem, isExtractableLocation} from '../utils/system'
|
|
1
|
+
import {Checksum256, Checksum256Type, UInt16Type, UInt64} from '@wharfkit/antelope'
|
|
2
|
+
import {Coordinates, CoordinatesType, Distance, LocationType} from '../types'
|
|
3
|
+
import {getLocationType, hasSystem, isGatherableLocation} 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?: GoodPrice[]
|
|
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
|
-
|
|
51
|
-
*/
|
|
52
|
-
isExtractableAt(gameSeed: Checksum256Type): boolean {
|
|
53
|
-
return isExtractableLocation(this.getLocationTypeAt(gameSeed))
|
|
33
|
+
isGatherableAt(gameSeed: Checksum256Type): boolean {
|
|
34
|
+
return isGatherableLocation(this.getLocationTypeAt(gameSeed))
|
|
54
35
|
}
|
|
55
36
|
|
|
56
|
-
/**
|
|
57
|
-
* Set cached market prices for this location
|
|
58
|
-
*/
|
|
59
|
-
setMarketPrices(prices: GoodPrice[]): void {
|
|
60
|
-
this._marketPrices = prices
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Get cached market prices (returns undefined if not cached)
|
|
65
|
-
*/
|
|
66
|
-
get marketPrices(): GoodPrice[] | undefined {
|
|
67
|
-
return this._marketPrices
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Get price for a specific good (from cache)
|
|
72
|
-
*/
|
|
73
|
-
getPrice(goodId: UInt16Type): GoodPrice | 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.good_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 GoodPrice.from({
|
|
203
|
-
id: price.id,
|
|
204
|
-
good: price.good,
|
|
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.good_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
|
-
good_id: row.good_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
|