@shipload/sdk 0.7.1 → 2.0.0-rc1
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/lib/shipload.d.ts +1651 -226
- package/lib/shipload.js +4958 -1971
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +4787 -1940
- package/lib/shipload.m.js.map +1 -1
- package/package.json +5 -4
- package/src/contracts/server.ts +585 -203
- package/src/data/goods.json +23 -0
- package/src/data/syllables.json +1184 -0
- package/src/entities/cargo-utils.ts +47 -0
- package/src/entities/entity-inventory.ts +39 -0
- package/src/entities/gamestate.ts +152 -0
- package/src/entities/location.ts +241 -0
- package/src/entities/player.ts +287 -0
- package/src/entities/ship.ts +559 -0
- package/src/entities/warehouse.ts +205 -0
- package/src/errors.ts +46 -9
- package/src/index-module.ts +119 -7
- package/src/managers/actions.ts +168 -0
- package/src/managers/base.ts +25 -0
- package/src/managers/context.ts +104 -0
- package/src/managers/entities.ts +86 -0
- package/src/managers/epochs.ts +47 -0
- package/src/managers/index.ts +9 -0
- package/src/managers/locations.ts +103 -0
- package/src/managers/players.ts +13 -0
- package/src/managers/trades.ts +119 -0
- package/src/market/goods.ts +31 -0
- package/src/{market.ts → market/market.ts} +32 -37
- package/src/{rolls.ts → market/rolls.ts} +3 -3
- package/src/{epoch.ts → scheduling/epoch.ts} +1 -1
- package/src/scheduling/projection.ts +218 -0
- package/src/scheduling/schedule.ts +155 -0
- package/src/shipload.ts +39 -157
- package/src/trading/collect.ts +939 -0
- package/src/trading/deal.ts +208 -0
- package/src/trading/trade.ts +203 -0
- package/src/travel/travel.ts +425 -0
- package/src/types.ts +60 -25
- package/src/utils/system.ts +27 -0
- package/src/goods.ts +0 -124
- package/src/ship.ts +0 -36
- package/src/state.ts +0 -0
- package/src/syllables.ts +0 -1184
- package/src/system.ts +0 -37
- package/src/travel.ts +0 -259
- /package/src/{hash.ts → utils/hash.ts} +0 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import {Name, NameType, UInt64Type} from '@wharfkit/antelope'
|
|
2
|
+
import {BaseManager} from './base'
|
|
3
|
+
import {Ship} from '../entities/ship'
|
|
4
|
+
import {Warehouse} from '../entities/warehouse'
|
|
5
|
+
import {ServerContract} from '../contracts'
|
|
6
|
+
|
|
7
|
+
export type EntityType = 'ship' | 'warehouse'
|
|
8
|
+
|
|
9
|
+
export class EntitiesManager extends BaseManager {
|
|
10
|
+
async getEntity(type: EntityType, id: UInt64Type): Promise<Ship | Warehouse> {
|
|
11
|
+
const result = await this.server.readonly('getentity', {
|
|
12
|
+
entity_type: Name.from(type),
|
|
13
|
+
entity_id: id,
|
|
14
|
+
})
|
|
15
|
+
const entityInfo = result as ServerContract.Types.entity_info
|
|
16
|
+
return this.wrapEntity(entityInfo)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async getEntities(
|
|
20
|
+
owner: NameType | ServerContract.Types.player_row,
|
|
21
|
+
type?: EntityType
|
|
22
|
+
): Promise<(Ship | Warehouse)[]> {
|
|
23
|
+
const ownerName = this.resolveOwner(owner)
|
|
24
|
+
const result = await this.server.readonly('getentities', {
|
|
25
|
+
owner: ownerName,
|
|
26
|
+
entity_type: type ? Name.from(type) : null,
|
|
27
|
+
})
|
|
28
|
+
const entities = result as ServerContract.Types.entity_info[]
|
|
29
|
+
return entities.map((entity) => this.wrapEntity(entity))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async getSummaries(
|
|
33
|
+
owner: NameType | ServerContract.Types.player_row,
|
|
34
|
+
type?: EntityType
|
|
35
|
+
): Promise<ServerContract.Types.entity_summary[]> {
|
|
36
|
+
const ownerName = this.resolveOwner(owner)
|
|
37
|
+
const result = await this.server.readonly('getsummaries', {
|
|
38
|
+
owner: ownerName,
|
|
39
|
+
entity_type: type ? Name.from(type) : null,
|
|
40
|
+
})
|
|
41
|
+
return result as ServerContract.Types.entity_summary[]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async getShip(id: UInt64Type): Promise<Ship> {
|
|
45
|
+
return (await this.getEntity('ship', id)) as Ship
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async getWarehouse(id: UInt64Type): Promise<Warehouse> {
|
|
49
|
+
return (await this.getEntity('warehouse', id)) as Warehouse
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async getShips(owner: NameType | ServerContract.Types.player_row): Promise<Ship[]> {
|
|
53
|
+
return (await this.getEntities(owner, 'ship')) as Ship[]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async getWarehouses(owner: NameType | ServerContract.Types.player_row): Promise<Warehouse[]> {
|
|
57
|
+
return (await this.getEntities(owner, 'warehouse')) as Warehouse[]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async getShipSummaries(
|
|
61
|
+
owner: NameType | ServerContract.Types.player_row
|
|
62
|
+
): Promise<ServerContract.Types.entity_summary[]> {
|
|
63
|
+
return this.getSummaries(owner, 'ship')
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async getWarehouseSummaries(
|
|
67
|
+
owner: NameType | ServerContract.Types.player_row
|
|
68
|
+
): Promise<ServerContract.Types.entity_summary[]> {
|
|
69
|
+
return this.getSummaries(owner, 'warehouse')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private wrapEntity(entity: ServerContract.Types.entity_info): Ship | Warehouse {
|
|
73
|
+
if (entity.type.equals('ship')) {
|
|
74
|
+
return new Ship(entity)
|
|
75
|
+
} else {
|
|
76
|
+
return new Warehouse(entity)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private resolveOwner(owner: NameType | ServerContract.Types.player_row): Name {
|
|
81
|
+
if (typeof owner === 'object' && owner !== null && 'owner' in owner) {
|
|
82
|
+
return owner.owner
|
|
83
|
+
}
|
|
84
|
+
return Name.from(owner)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {UInt64, UInt64Type} from '@wharfkit/antelope'
|
|
2
|
+
import {BaseManager} from './base'
|
|
3
|
+
import {EpochInfo, getCurrentEpoch, getEpochInfo} from '../scheduling/epoch'
|
|
4
|
+
|
|
5
|
+
export class EpochsManager extends BaseManager {
|
|
6
|
+
async getCurrentHeight(): Promise<UInt64> {
|
|
7
|
+
const game = await this.getGame()
|
|
8
|
+
return getCurrentEpoch(game)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async getCurrent(): Promise<EpochInfo> {
|
|
12
|
+
const game = await this.getGame()
|
|
13
|
+
const epoch = await this.getCurrentHeight()
|
|
14
|
+
return getEpochInfo(game, epoch)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async getByHeight(height: UInt64Type): Promise<EpochInfo> {
|
|
18
|
+
const game = await this.getGame()
|
|
19
|
+
return getEpochInfo(game, UInt64.from(height))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async getTimeRemaining(): Promise<number> {
|
|
23
|
+
const epochInfo = await this.getCurrent()
|
|
24
|
+
const now = Date.now()
|
|
25
|
+
const endTime = epochInfo.end.getTime()
|
|
26
|
+
return Math.max(0, endTime - now)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async getProgress(): Promise<number> {
|
|
30
|
+
const epochInfo = await this.getCurrent()
|
|
31
|
+
const now = Date.now()
|
|
32
|
+
const startTime = epochInfo.start.getTime()
|
|
33
|
+
const endTime = epochInfo.end.getTime()
|
|
34
|
+
const duration = endTime - startTime
|
|
35
|
+
const elapsed = now - startTime
|
|
36
|
+
|
|
37
|
+
if (elapsed <= 0) return 0
|
|
38
|
+
if (elapsed >= duration) return 1
|
|
39
|
+
|
|
40
|
+
return elapsed / duration
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async fitsInCurrentEpoch(durationMs: number): Promise<boolean> {
|
|
44
|
+
const remaining = await this.getTimeRemaining()
|
|
45
|
+
return durationMs <= remaining
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export {GameContext} from './context'
|
|
2
|
+
export {BaseManager} from './base'
|
|
3
|
+
export {EntitiesManager} from './entities'
|
|
4
|
+
export type {EntityType} from './entities'
|
|
5
|
+
export {PlayersManager} from './players'
|
|
6
|
+
export {LocationsManager} from './locations'
|
|
7
|
+
export {TradesManager} from './trades'
|
|
8
|
+
export {EpochsManager} from './epochs'
|
|
9
|
+
export {ActionsManager} from './actions'
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import {Bytes, Checksum256, UInt16Type, UInt64} from '@wharfkit/antelope'
|
|
2
|
+
import {BaseManager} from './base'
|
|
3
|
+
import {CoordinatesType, Distance, GoodPrice} from '../types'
|
|
4
|
+
import {marketPrice, marketPrices} from '../market/market'
|
|
5
|
+
import {hasSystem} from '../utils/system'
|
|
6
|
+
import {findNearbyPlanets} from '../travel/travel'
|
|
7
|
+
import {Location, toLocation} from '../entities/location'
|
|
8
|
+
|
|
9
|
+
export class LocationsManager extends BaseManager {
|
|
10
|
+
async getMarketPrice(location: CoordinatesType, goodId: number): Promise<GoodPrice> {
|
|
11
|
+
const game = await this.getGame()
|
|
12
|
+
const state = await this.getState()
|
|
13
|
+
return marketPrice(location, goodId, game.config.seed, state)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async getMarketPrices(location: CoordinatesType): Promise<GoodPrice[]> {
|
|
17
|
+
const game = await this.getGame()
|
|
18
|
+
const state = await this.getState()
|
|
19
|
+
return marketPrices(location, game.config.seed, state)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async getMarketPricesWithSupply(location: CoordinatesType): Promise<GoodPrice[]> {
|
|
23
|
+
const [game, state, locationRows] = await Promise.all([
|
|
24
|
+
this.getGame(),
|
|
25
|
+
this.getState(),
|
|
26
|
+
this.getLocation(location),
|
|
27
|
+
])
|
|
28
|
+
|
|
29
|
+
const prices = marketPrices(location, game.config.seed, state)
|
|
30
|
+
|
|
31
|
+
const supplyMap = new Map<number, number>()
|
|
32
|
+
for (const row of locationRows) {
|
|
33
|
+
if (UInt64.from(row.epoch).equals(state.epoch)) {
|
|
34
|
+
supplyMap.set(Number(row.good_id), Number(row.supply))
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return prices.map((price) => {
|
|
39
|
+
const actualSupply = supplyMap.get(Number(price.id))
|
|
40
|
+
if (actualSupply !== undefined) {
|
|
41
|
+
return GoodPrice.from({
|
|
42
|
+
id: price.id,
|
|
43
|
+
good: price.good,
|
|
44
|
+
price: price.price,
|
|
45
|
+
supply: UInt64.from(actualSupply),
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
return price
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async hasSystem(location: CoordinatesType): Promise<boolean> {
|
|
53
|
+
const game = await this.getGame()
|
|
54
|
+
return hasSystem(game.config.seed, location)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async findNearbyPlanets(
|
|
58
|
+
origin: CoordinatesType,
|
|
59
|
+
maxDistance: UInt16Type = 20
|
|
60
|
+
): Promise<Distance[]> {
|
|
61
|
+
const game = await this.getGame()
|
|
62
|
+
return findNearbyPlanets(game.config.seed, origin, maxDistance)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async getLocation(location: CoordinatesType) {
|
|
66
|
+
const hash = Checksum256.hash(Bytes.from(`${location.x}-${location.y}`, 'utf8'))
|
|
67
|
+
return this.server.table('location').all({
|
|
68
|
+
index_position: 'secondary',
|
|
69
|
+
from: hash,
|
|
70
|
+
to: hash,
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async getLocationWithPrices(coords: CoordinatesType): Promise<Location> {
|
|
75
|
+
const location = toLocation(coords)
|
|
76
|
+
const prices = await this.getMarketPrices(location.coordinates)
|
|
77
|
+
location.setMarketPrices(prices)
|
|
78
|
+
return location
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async getLocationWithSupply(coords: CoordinatesType): Promise<Location> {
|
|
82
|
+
const location = toLocation(coords)
|
|
83
|
+
const [rows, state] = await Promise.all([
|
|
84
|
+
this.getLocation(location.coordinates),
|
|
85
|
+
this.getState(),
|
|
86
|
+
])
|
|
87
|
+
location.setLocationRows(rows, state.epoch)
|
|
88
|
+
return location
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async getLocationComplete(coords: CoordinatesType): Promise<Location> {
|
|
92
|
+
const location = toLocation(coords)
|
|
93
|
+
const [prices, rows, state] = await Promise.all([
|
|
94
|
+
this.getMarketPrices(location.coordinates),
|
|
95
|
+
this.getLocation(location.coordinates),
|
|
96
|
+
this.getState(),
|
|
97
|
+
])
|
|
98
|
+
|
|
99
|
+
location.setMarketPrices(prices)
|
|
100
|
+
location.setLocationRows(rows, state.epoch)
|
|
101
|
+
return location
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {Name, NameType} from '@wharfkit/antelope'
|
|
2
|
+
import {BaseManager} from './base'
|
|
3
|
+
import {Player} from '../entities/player'
|
|
4
|
+
|
|
5
|
+
export class PlayersManager extends BaseManager {
|
|
6
|
+
async getPlayer(account: NameType): Promise<Player | undefined> {
|
|
7
|
+
const playerRow = await this.server.table('player').get(Name.from(account))
|
|
8
|
+
if (!playerRow) {
|
|
9
|
+
return undefined
|
|
10
|
+
}
|
|
11
|
+
return new Player(playerRow)
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {UInt64} from '@wharfkit/antelope'
|
|
2
|
+
import {BaseManager} from './base'
|
|
3
|
+
import {Ship} from '../entities/ship'
|
|
4
|
+
import {Deal, findDealsForShip, FindDealsOptions} from '../trading/deal'
|
|
5
|
+
import {
|
|
6
|
+
analyzeCollectOptions,
|
|
7
|
+
CollectAnalysis,
|
|
8
|
+
CollectAnalysisCallbacks,
|
|
9
|
+
CollectAnalysisOptions,
|
|
10
|
+
} from '../trading/collect'
|
|
11
|
+
import {Coordinates, GoodPrice} from '../types'
|
|
12
|
+
import {Location, toLocation} from '../entities/location'
|
|
13
|
+
import {findNearbyPlanets} from '../travel/travel'
|
|
14
|
+
import {getCurrentEpoch} from '../scheduling/epoch'
|
|
15
|
+
|
|
16
|
+
export class TradesManager extends BaseManager {
|
|
17
|
+
private priceCache = new Map<string, GoodPrice[]>()
|
|
18
|
+
private priceCacheEpoch: UInt64 | undefined
|
|
19
|
+
|
|
20
|
+
private makePriceCacheKey(location: Coordinates): string {
|
|
21
|
+
return `${location.x},${location.y}`
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private async createCallbacks(): Promise<CollectAnalysisCallbacks> {
|
|
25
|
+
const game = await this.getGame()
|
|
26
|
+
const serverState = await this.getState()
|
|
27
|
+
const currentEpoch = getCurrentEpoch(game)
|
|
28
|
+
|
|
29
|
+
if (!this.priceCacheEpoch || !this.priceCacheEpoch.equals(currentEpoch)) {
|
|
30
|
+
this.priceCache.clear()
|
|
31
|
+
this.priceCacheEpoch = currentEpoch
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const getNearbyLocations = async (
|
|
35
|
+
origin: Coordinates,
|
|
36
|
+
maxDistance: number
|
|
37
|
+
): Promise<Location[]> => {
|
|
38
|
+
const nearby = findNearbyPlanets(game.config.seed, origin, maxDistance)
|
|
39
|
+
return nearby.map((d) => toLocation(d.destination))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const getMarketPrices = async (location: Coordinates): Promise<GoodPrice[]> => {
|
|
43
|
+
const cacheKey = this.makePriceCacheKey(location)
|
|
44
|
+
const cached = this.priceCache.get(cacheKey)
|
|
45
|
+
if (cached) {
|
|
46
|
+
return cached
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const locationWithSupply = await this.context.locations.getLocationComplete(location)
|
|
50
|
+
const prices = locationWithSupply.marketPrices || []
|
|
51
|
+
|
|
52
|
+
const result = prices.map((price) => {
|
|
53
|
+
const actualSupply = locationWithSupply.getSupply(price.id)
|
|
54
|
+
|
|
55
|
+
if (actualSupply !== undefined) {
|
|
56
|
+
return GoodPrice.from({
|
|
57
|
+
id: price.id,
|
|
58
|
+
good: price.good,
|
|
59
|
+
price: price.price,
|
|
60
|
+
supply: actualSupply,
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
return price
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
this.priceCache.set(cacheKey, result)
|
|
67
|
+
return result
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const getGameSeed = () => game.config.seed
|
|
71
|
+
const getState = () => serverState
|
|
72
|
+
|
|
73
|
+
return {getNearbyLocations, getMarketPrices, getGameSeed, getState}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
clearPriceCache(): void {
|
|
77
|
+
this.priceCache.clear()
|
|
78
|
+
this.priceCacheEpoch = undefined
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async findDeals(
|
|
82
|
+
ship: Ship,
|
|
83
|
+
originLocation?: Coordinates,
|
|
84
|
+
options: FindDealsOptions = {}
|
|
85
|
+
): Promise<Deal[]> {
|
|
86
|
+
const origin = originLocation || ship.currentLocation
|
|
87
|
+
const callbacks = await this.createCallbacks()
|
|
88
|
+
|
|
89
|
+
const deals = await findDealsForShip(
|
|
90
|
+
ship,
|
|
91
|
+
origin,
|
|
92
|
+
callbacks.getNearbyLocations,
|
|
93
|
+
callbacks.getMarketPrices,
|
|
94
|
+
options
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
return deals
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async findBestDeal(
|
|
101
|
+
ship: Ship,
|
|
102
|
+
originLocation?: Coordinates,
|
|
103
|
+
options: FindDealsOptions = {}
|
|
104
|
+
): Promise<Deal | undefined> {
|
|
105
|
+
const deals = await this.findDeals(ship, originLocation, {...options, maxDeals: 1})
|
|
106
|
+
return deals[0]
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async getCollectOptions(
|
|
110
|
+
ship: Ship,
|
|
111
|
+
arrivedAt?: Coordinates,
|
|
112
|
+
options: CollectAnalysisOptions = {}
|
|
113
|
+
): Promise<CollectAnalysis> {
|
|
114
|
+
const location = arrivedAt || ship.currentLocation
|
|
115
|
+
const callbacks = await this.createCallbacks()
|
|
116
|
+
|
|
117
|
+
return analyzeCollectOptions(ship, location, callbacks, options)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {UInt16, UInt16Type, UInt32} from '@wharfkit/antelope'
|
|
2
|
+
import {Good} from '../types'
|
|
3
|
+
import goodsData from '../data/goods.json'
|
|
4
|
+
|
|
5
|
+
const goods = goodsData as Array<{
|
|
6
|
+
id: number
|
|
7
|
+
name: string
|
|
8
|
+
description: string
|
|
9
|
+
base_price: number
|
|
10
|
+
mass: number
|
|
11
|
+
}>
|
|
12
|
+
|
|
13
|
+
export const goodIds = goods.map((g) => g.id)
|
|
14
|
+
|
|
15
|
+
export function getGood(goodId: UInt16Type): Good {
|
|
16
|
+
const good = goods.find((g) => UInt16.from(goodId).equals(g.id))
|
|
17
|
+
if (!good) {
|
|
18
|
+
throw new Error('Good does not exist')
|
|
19
|
+
}
|
|
20
|
+
return Good.from({
|
|
21
|
+
id: UInt16.from(good.id),
|
|
22
|
+
name: good.name,
|
|
23
|
+
description: good.description,
|
|
24
|
+
base_price: UInt32.from(good.base_price),
|
|
25
|
+
mass: UInt32.from(good.mass),
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function getGoods(): Good[] {
|
|
30
|
+
return goods.map((g) => getGood(g.id))
|
|
31
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {CoordinatesType, GoodPrice} from '../types'
|
|
2
2
|
import {getGood, getGoods} from './goods'
|
|
3
|
-
import {Checksum256Type, UInt16Type,
|
|
3
|
+
import {Checksum256Type, UInt16, UInt16Type, UInt32} from '@wharfkit/antelope'
|
|
4
4
|
import {roll} from './rolls'
|
|
5
|
-
import {ServerContract} from '
|
|
5
|
+
import {ServerContract} from '../contracts'
|
|
6
6
|
|
|
7
7
|
export enum Rarities {
|
|
8
8
|
legendary = 'LEGENDARY',
|
|
@@ -19,21 +19,13 @@ export interface Rarity {
|
|
|
19
19
|
maxMultiplier: number // The maximum multiplier for this rarity
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export function generateLocationSeed(
|
|
23
|
-
epochSeed: Checksum256Type,
|
|
24
|
-
location: Coordinates,
|
|
25
|
-
entitySalt: string
|
|
26
|
-
) {
|
|
27
|
-
return `${epochSeed}${location.x}${location.y}${entitySalt}`
|
|
28
|
-
}
|
|
29
|
-
|
|
30
22
|
export function getRarity(
|
|
31
23
|
gameSeed: Checksum256Type,
|
|
32
24
|
epochSeed: Checksum256Type,
|
|
33
|
-
location:
|
|
34
|
-
|
|
25
|
+
location: CoordinatesType,
|
|
26
|
+
goodId: UInt16Type
|
|
35
27
|
): Rarity {
|
|
36
|
-
const seed = `${epochSeed}${location.x}${location.y}${
|
|
28
|
+
const seed = `${epochSeed}${location.x}${location.y}${goodId}rarity`
|
|
37
29
|
const rarityRoll = roll(gameSeed, seed)
|
|
38
30
|
|
|
39
31
|
if (rarityRoll < 13) {
|
|
@@ -119,22 +111,22 @@ export function getRarity(
|
|
|
119
111
|
export function getRarityMultiplier(
|
|
120
112
|
gameSeed: Checksum256Type,
|
|
121
113
|
epochSeed: Checksum256Type,
|
|
122
|
-
location:
|
|
123
|
-
|
|
114
|
+
location: CoordinatesType,
|
|
115
|
+
goodId: UInt16Type
|
|
124
116
|
): number {
|
|
125
|
-
const rarity = getRarity(gameSeed, epochSeed, location,
|
|
117
|
+
const rarity = getRarity(gameSeed, epochSeed, location, goodId)
|
|
126
118
|
const range = rarity.maxMultiplier - rarity.minMultiplier
|
|
127
|
-
const seed = `${epochSeed}${location.x}${location.y}${
|
|
119
|
+
const seed = `${epochSeed}${location.x}${location.y}${goodId}raritymultiplier`
|
|
128
120
|
const r = roll(gameSeed, seed)
|
|
129
121
|
return rarity.minMultiplier + (r / 65535) * range
|
|
130
122
|
}
|
|
131
123
|
|
|
132
124
|
export function getLocationMultiplier(
|
|
133
125
|
gameSeed: Checksum256Type,
|
|
134
|
-
location:
|
|
135
|
-
|
|
126
|
+
location: CoordinatesType,
|
|
127
|
+
goodId: UInt16Type
|
|
136
128
|
): number {
|
|
137
|
-
const seed = `${location.x}${location.y}${
|
|
129
|
+
const seed = `${location.x}${location.y}${goodId}locationmultiplier`
|
|
138
130
|
const r = roll(gameSeed, seed)
|
|
139
131
|
if (r < 13) {
|
|
140
132
|
return 0.75
|
|
@@ -164,51 +156,54 @@ export function getLocationMultiplier(
|
|
|
164
156
|
export function getSupply(
|
|
165
157
|
gameSeed: Checksum256Type,
|
|
166
158
|
state: ServerContract.Types.state_row,
|
|
167
|
-
location:
|
|
168
|
-
|
|
159
|
+
location: CoordinatesType,
|
|
160
|
+
goodId: UInt16Type
|
|
169
161
|
): number {
|
|
170
|
-
const seed = `${state.seed}${location.x}${location.y}${
|
|
162
|
+
const seed = `${state.seed}${location.x}${location.y}${goodId}supply`
|
|
171
163
|
const r = roll(gameSeed, seed)
|
|
172
164
|
const percent = r / 65535
|
|
173
165
|
const epoch = 1 + Number(state.epoch) / 90
|
|
174
|
-
|
|
175
|
-
|
|
166
|
+
// NOTE: Contract has bug where 1/3 is integer division = 0, so pow(ships, 0) = 1
|
|
167
|
+
// TODO: Update this when contract is fixed to use (double)1/(double)3
|
|
168
|
+
const ship = Math.pow(Number(state.ships), 0)
|
|
169
|
+
const goodIdNum = Number(goodId)
|
|
170
|
+
return Math.floor((128 / goodIdNum) * percent * ship * epoch)
|
|
176
171
|
}
|
|
177
172
|
|
|
178
|
-
export function
|
|
173
|
+
export function marketPrice(
|
|
179
174
|
location: ServerContract.ActionParams.Type.coordinates,
|
|
180
|
-
|
|
175
|
+
goodId: UInt16Type,
|
|
181
176
|
gameSeed: Checksum256Type,
|
|
182
177
|
state: ServerContract.Types.state_row
|
|
183
178
|
): GoodPrice {
|
|
184
|
-
const good = getGood(
|
|
179
|
+
const good = getGood(goodId)
|
|
185
180
|
let price = Number(good.base_price)
|
|
186
181
|
|
|
187
182
|
// Rarity multiplier of the deal (changes with epoch)
|
|
188
183
|
// Large impact range on price, from 0.285x to 3.0x
|
|
189
|
-
const rarityMultiplier = getRarityMultiplier(gameSeed, state.seed, location,
|
|
184
|
+
const rarityMultiplier = getRarityMultiplier(gameSeed, state.seed, location, goodId)
|
|
190
185
|
price *= rarityMultiplier
|
|
191
186
|
|
|
192
187
|
// Location multiplier of the deal (static, based on game seed)
|
|
193
188
|
// Small impact range on price, from 1.0x to 1.5x
|
|
194
|
-
const locationMultiplier = getLocationMultiplier(gameSeed, location,
|
|
189
|
+
const locationMultiplier = getLocationMultiplier(gameSeed, location, goodId)
|
|
195
190
|
price *= locationMultiplier
|
|
196
191
|
|
|
197
192
|
// Determine the current supply of the good at the location
|
|
198
|
-
const supply = getSupply(gameSeed, state, location,
|
|
193
|
+
const supply = getSupply(gameSeed, state, location, goodId)
|
|
199
194
|
|
|
200
195
|
return GoodPrice.from({
|
|
201
|
-
id:
|
|
196
|
+
id: goodId,
|
|
202
197
|
good,
|
|
203
|
-
price:
|
|
204
|
-
supply:
|
|
198
|
+
price: UInt32.from(price),
|
|
199
|
+
supply: UInt16.from(supply),
|
|
205
200
|
})
|
|
206
201
|
}
|
|
207
202
|
|
|
208
|
-
export function
|
|
203
|
+
export function marketPrices(
|
|
209
204
|
location: ServerContract.ActionParams.Type.coordinates,
|
|
210
205
|
gameSeed: Checksum256Type,
|
|
211
206
|
state: ServerContract.Types.state_row
|
|
212
207
|
): GoodPrice[] {
|
|
213
|
-
return getGoods().map((good) =>
|
|
208
|
+
return getGoods().map((good) => marketPrice(location, good.id, gameSeed, state))
|
|
214
209
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {Checksum256Type} from '@wharfkit/antelope'
|
|
2
|
-
import {hash512} from '
|
|
2
|
+
import {hash512} from '../utils/hash'
|
|
3
3
|
|
|
4
|
-
export function roll(
|
|
5
|
-
const hash = hash512(
|
|
4
|
+
export function roll(gameSeed: Checksum256Type, rollSeed: string): number {
|
|
5
|
+
const hash = hash512(gameSeed, rollSeed)
|
|
6
6
|
// Combine the first two bytes to form a uint16_t value
|
|
7
7
|
return (hash.array[0] << 8) | hash.array[1]
|
|
8
8
|
}
|