@shipload/sdk 1.0.0-next.3 → 1.0.0-next.31

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 (99) hide show
  1. package/lib/shipload.d.ts +1849 -961
  2. package/lib/shipload.js +9089 -4854
  3. package/lib/shipload.js.map +1 -1
  4. package/lib/shipload.m.js +8958 -4805
  5. package/lib/shipload.m.js.map +1 -1
  6. package/lib/testing.d.ts +856 -0
  7. package/lib/testing.js +3739 -0
  8. package/lib/testing.js.map +1 -0
  9. package/lib/testing.m.js +3733 -0
  10. package/lib/testing.m.js.map +1 -0
  11. package/package.json +15 -2
  12. package/src/capabilities/craftable.ts +51 -0
  13. package/src/capabilities/crafting.test.ts +7 -0
  14. package/src/capabilities/crafting.ts +3 -3
  15. package/src/capabilities/gathering.ts +17 -7
  16. package/src/capabilities/index.ts +0 -1
  17. package/src/capabilities/modules.ts +6 -0
  18. package/src/capabilities/storage.ts +16 -1
  19. package/src/contracts/platform.ts +231 -3
  20. package/src/contracts/server.ts +816 -471
  21. package/src/data/capabilities.ts +14 -329
  22. package/src/data/capability-formulas.ts +76 -0
  23. package/src/data/catalog.ts +0 -5
  24. package/src/data/colors.ts +14 -47
  25. package/src/data/entities.json +46 -10
  26. package/src/data/item-ids.ts +15 -12
  27. package/src/data/items.json +302 -38
  28. package/src/data/kind-registry.json +85 -0
  29. package/src/data/kind-registry.ts +150 -0
  30. package/src/data/metadata.ts +100 -31
  31. package/src/data/recipes-runtime.ts +3 -23
  32. package/src/data/recipes.json +250 -113
  33. package/src/derivation/build-methods.ts +45 -0
  34. package/src/derivation/capabilities.ts +415 -0
  35. package/src/derivation/capability-mappings.ts +117 -0
  36. package/src/derivation/crafting.ts +23 -24
  37. package/src/derivation/index.ts +17 -2
  38. package/src/derivation/reserve-regen.ts +34 -0
  39. package/src/derivation/resources.ts +125 -38
  40. package/src/derivation/stars.test.ts +51 -0
  41. package/src/derivation/stars.ts +15 -0
  42. package/src/derivation/stats.ts +6 -6
  43. package/src/derivation/stratum.ts +15 -19
  44. package/src/derivation/tiers.ts +28 -7
  45. package/src/entities/entity.ts +98 -0
  46. package/src/entities/gamestate.ts +3 -28
  47. package/src/entities/makers.ts +91 -136
  48. package/src/entities/slot-multiplier.ts +39 -0
  49. package/src/errors.ts +10 -15
  50. package/src/format.ts +26 -4
  51. package/src/index-module.ts +189 -47
  52. package/src/managers/actions.ts +252 -83
  53. package/src/managers/base.ts +6 -2
  54. package/src/managers/construction-types.ts +79 -0
  55. package/src/managers/construction.ts +396 -0
  56. package/src/managers/context.ts +11 -1
  57. package/src/managers/entities.ts +18 -66
  58. package/src/managers/epochs.ts +40 -0
  59. package/src/managers/index.ts +17 -1
  60. package/src/managers/locations.ts +25 -29
  61. package/src/managers/nft.ts +28 -0
  62. package/src/managers/plot.ts +127 -0
  63. package/src/nft/atomicassets.abi.json +1342 -0
  64. package/src/nft/atomicassets.ts +237 -0
  65. package/src/nft/atomicdata.ts +130 -0
  66. package/src/nft/buildImmutableData.ts +321 -0
  67. package/src/nft/description.ts +37 -15
  68. package/src/nft/index.ts +3 -0
  69. package/src/resolution/describe-module.ts +5 -8
  70. package/src/resolution/display-name.ts +38 -10
  71. package/src/resolution/resolve-item.ts +22 -20
  72. package/src/scheduling/accessor.ts +68 -22
  73. package/src/scheduling/availability.ts +108 -0
  74. package/src/scheduling/energy.ts +48 -0
  75. package/src/scheduling/lane-core.ts +130 -0
  76. package/src/scheduling/lanes.ts +60 -0
  77. package/src/scheduling/projection.ts +121 -94
  78. package/src/scheduling/schedule.ts +237 -103
  79. package/src/scheduling/task-cargo.ts +46 -0
  80. package/src/shipload.ts +16 -1
  81. package/src/subscriptions/manager.ts +40 -6
  82. package/src/subscriptions/mappers.ts +3 -8
  83. package/src/subscriptions/types.ts +3 -2
  84. package/src/testing/catalog-hash.ts +19 -0
  85. package/src/testing/index.ts +2 -0
  86. package/src/testing/projection-parity.ts +143 -0
  87. package/src/travel/travel.ts +90 -13
  88. package/src/types/capabilities.ts +1 -0
  89. package/src/types/index.ts +0 -1
  90. package/src/types.ts +19 -12
  91. package/src/utils/cargo.ts +27 -0
  92. package/src/utils/display-name.ts +70 -0
  93. package/src/utils/system.ts +25 -24
  94. package/src/capabilities/loading.ts +0 -8
  95. package/src/entities/container.ts +0 -108
  96. package/src/entities/ship-deploy.ts +0 -258
  97. package/src/entities/ship.ts +0 -204
  98. package/src/entities/warehouse.ts +0 -119
  99. package/src/types/entity-traits.ts +0 -69
@@ -7,6 +7,7 @@ export {
7
7
  getEligibleResources,
8
8
  getResourceWeight,
9
9
  getLocationCandidates,
10
+ getLocationProfile,
10
11
  getDepthThreshold,
11
12
  getResourceTier,
12
13
  DEPTH_THRESHOLD_T1,
@@ -16,7 +17,9 @@ export {
16
17
  DEPTH_THRESHOLD_T5,
17
18
  LOCATION_MIN_DEPTH,
18
19
  LOCATION_MAX_DEPTH,
19
- YIELD_THRESHOLD,
20
+ yieldThresholdAt,
21
+ YIELD_FRACTION_SHALLOW,
22
+ YIELD_FRACTION_DEEP,
20
23
  PLANET_SUBTYPE_GAS_GIANT,
21
24
  PLANET_SUBTYPE_ROCKY,
22
25
  PLANET_SUBTYPE_TERRESTRIAL,
@@ -25,8 +28,20 @@ export {
25
28
  PLANET_SUBTYPE_INDUSTRIAL,
26
29
  } from './resources'
27
30
 
28
- export {RESERVE_TIERS, TIER_ROLL_MAX, rollTier, rollWithinTier} from './tiers'
31
+ export {RESERVE_TIERS, TIER_ROLL_MAX, tierOfReserve, rollTier, rollWithinTier} from './tiers'
29
32
  export type {ReserveTier, TierRange} from './tiers'
30
33
 
34
+ export {getEffectiveReserve} from './reserve-regen'
35
+ export type {EffectiveReserveInput} from './reserve-regen'
36
+
31
37
  export * from './stats'
32
38
  export * from './crafting'
39
+
40
+ export {
41
+ STAR_STEP,
42
+ MAX_STARS_PER_STAT,
43
+ MAX_STAR_RATING,
44
+ starsForStat,
45
+ starRating,
46
+ statMagnitude,
47
+ } from './stars'
@@ -0,0 +1,34 @@
1
+ import type {BlockTimestamp, UInt32} from '@wharfkit/antelope'
2
+
3
+ export interface EffectiveReserveInput {
4
+ remaining: UInt32 | number
5
+ max_reserve: UInt32 | number
6
+ last_block: BlockTimestamp
7
+ }
8
+
9
+ function toNumber(value: UInt32 | number): number {
10
+ return typeof value === 'number' ? value : Number(value)
11
+ }
12
+
13
+ function slotsBetween(now: BlockTimestamp, last: BlockTimestamp): number {
14
+ const nowMs = now.toMilliseconds()
15
+ const lastMs = last.toMilliseconds()
16
+ if (nowMs <= lastMs) return 0
17
+ return Math.floor((nowMs - lastMs) / 500)
18
+ }
19
+
20
+ export function getEffectiveReserve(
21
+ row: EffectiveReserveInput,
22
+ now: BlockTimestamp,
23
+ epochSeconds: number
24
+ ): number {
25
+ const remaining = toNumber(row.remaining)
26
+ const max = toNumber(row.max_reserve)
27
+ if (remaining >= max) return max
28
+ const epochSlots = epochSeconds * 2
29
+ if (epochSlots === 0) return remaining
30
+ const elapsed = slotsBetween(now, row.last_block)
31
+ const regen = Math.floor((max * elapsed) / epochSlots)
32
+ const effective = remaining + regen
33
+ return effective >= max ? max : effective
34
+ }
@@ -1,15 +1,29 @@
1
1
  import {getItem} from '../data/catalog'
2
+ import {LocationType} from '../types'
2
3
 
3
4
  export const DEPTH_THRESHOLD_T1 = 0
4
- export const DEPTH_THRESHOLD_T2 = 2000
5
- export const DEPTH_THRESHOLD_T3 = 10000
6
- export const DEPTH_THRESHOLD_T4 = 30000
7
- export const DEPTH_THRESHOLD_T5 = 55000
5
+ export const DEPTH_THRESHOLD_T2 = 1500
6
+ export const DEPTH_THRESHOLD_T3 = 5000
7
+ export const DEPTH_THRESHOLD_T4 = 12000
8
+ export const DEPTH_THRESHOLD_T5 = 22000
9
+ export const DEPTH_THRESHOLD_T6 = 32000
10
+ export const DEPTH_THRESHOLD_T7 = 42000
11
+ export const DEPTH_THRESHOLD_T8 = 50000
12
+ export const DEPTH_THRESHOLD_T9 = 57000
13
+ export const DEPTH_THRESHOLD_T10 = 63000
8
14
 
9
15
  export const LOCATION_MIN_DEPTH = 500
10
16
  export const LOCATION_MAX_DEPTH = 65535
11
17
 
12
- export const YIELD_THRESHOLD = Math.floor(0.001 * 0xffffffff)
18
+ export const YIELD_FRACTION_SHALLOW = 0.0025
19
+ export const YIELD_FRACTION_DEEP = 0.0005
20
+
21
+ export function yieldThresholdAt(stratum: number): number {
22
+ const clamped = stratum > 65535 ? 65535 : stratum
23
+ const t = clamped / 65535
24
+ const fraction = YIELD_FRACTION_SHALLOW + (YIELD_FRACTION_DEEP - YIELD_FRACTION_SHALLOW) * t
25
+ return Math.floor(fraction * 0xffffffff)
26
+ }
13
27
 
14
28
  export const PLANET_SUBTYPE_GAS_GIANT = 0
15
29
  export const PLANET_SUBTYPE_ROCKY = 1
@@ -18,19 +32,22 @@ export const PLANET_SUBTYPE_ICY = 3
18
32
  export const PLANET_SUBTYPE_OCEAN = 4
19
33
  export const PLANET_SUBTYPE_INDUSTRIAL = 5
20
34
 
35
+ const DEPTH_THRESHOLD_TABLE = [
36
+ DEPTH_THRESHOLD_T1,
37
+ DEPTH_THRESHOLD_T2,
38
+ DEPTH_THRESHOLD_T3,
39
+ DEPTH_THRESHOLD_T4,
40
+ DEPTH_THRESHOLD_T5,
41
+ DEPTH_THRESHOLD_T6,
42
+ DEPTH_THRESHOLD_T7,
43
+ DEPTH_THRESHOLD_T8,
44
+ DEPTH_THRESHOLD_T9,
45
+ DEPTH_THRESHOLD_T10,
46
+ ]
47
+
21
48
  export function getDepthThreshold(tier: number): number {
22
- switch (tier) {
23
- case 1:
24
- return DEPTH_THRESHOLD_T1
25
- case 2:
26
- return DEPTH_THRESHOLD_T2
27
- case 3:
28
- return DEPTH_THRESHOLD_T3
29
- case 4:
30
- return DEPTH_THRESHOLD_T4
31
- default:
32
- return DEPTH_THRESHOLD_T5
33
- }
49
+ if (tier < 1 || tier > 10) return 65535
50
+ return DEPTH_THRESHOLD_TABLE[tier - 1]
34
51
  }
35
52
 
36
53
  export function getResourceTier(itemId: number): number {
@@ -46,9 +63,9 @@ export function getResourceWeight(itemId: number, stratum: number): number {
46
63
 
47
64
  switch (tier) {
48
65
  case 1:
49
- if (stratum < 2000) return 100
50
- if (stratum < 10000) return 80
51
- if (stratum < 30000) return 50
66
+ if (stratum < DEPTH_THRESHOLD_T2) return 100
67
+ if (stratum < DEPTH_THRESHOLD_T3) return 80
68
+ if (stratum < DEPTH_THRESHOLD_T4) return 50
52
69
  return 30
53
70
  case 2:
54
71
  if (depthAbove < 3000) return 40
@@ -67,37 +84,107 @@ export function getResourceWeight(itemId: number, stratum: number): number {
67
84
  }
68
85
  }
69
86
 
70
- const ASTEROID_RESOURCES = [101, 102, 103, 201, 202]
71
- const NEBULA_RESOURCES = [202, 203, 301, 302, 303]
72
- const GAS_GIANT_RESOURCES = [301, 302, 303, 401, 501]
73
- const ROCKY_RESOURCES = [101, 102, 103, 401, 402, 403, 503]
74
- const TERRESTRIAL_RESOURCES = [201, 202, 401, 402, 501, 502, 503]
75
- const ICY_RESOURCES = [101, 301, 302, 401, 403, 501, 502]
76
- const OCEAN_RESOURCES = [201, 203, 301, 303, 501, 502, 503]
77
- const INDUSTRIAL_RESOURCES = [101, 102, 103, 201, 203, 402, 403]
87
+ const RESOURCE_ORE = 0
88
+ const RESOURCE_GAS = 1
89
+ const RESOURCE_REGOLITH = 2
90
+ const RESOURCE_BIOMASS = 3
91
+ const RESOURCE_CRYSTAL = 4
78
92
 
79
- export function getLocationCandidates(locationType: number, subtype: number): number[] {
80
- if (locationType === 2) return ASTEROID_RESOURCES
81
- if (locationType === 3) return NEBULA_RESOURCES
82
- if (locationType === 1) {
93
+ interface LocationProfileEntry {
94
+ category: number
95
+ maxTier: number
96
+ }
97
+
98
+ function categoryBaseId(category: number): number {
99
+ switch (category) {
100
+ case RESOURCE_ORE:
101
+ return 100
102
+ case RESOURCE_CRYSTAL:
103
+ return 200
104
+ case RESOURCE_GAS:
105
+ return 300
106
+ case RESOURCE_REGOLITH:
107
+ return 400
108
+ case RESOURCE_BIOMASS:
109
+ return 500
110
+ default:
111
+ return 0
112
+ }
113
+ }
114
+
115
+ function resourceId(category: number, tier: number): number {
116
+ return categoryBaseId(category) + tier
117
+ }
118
+
119
+ export function getLocationProfile(locationType: number, subtype: number): LocationProfileEntry[] {
120
+ if (locationType === LocationType.ASTEROID) {
121
+ return [
122
+ {category: RESOURCE_ORE, maxTier: 5},
123
+ {category: RESOURCE_CRYSTAL, maxTier: 5},
124
+ ]
125
+ }
126
+ if (locationType === LocationType.NEBULA) {
127
+ return [
128
+ {category: RESOURCE_GAS, maxTier: 5},
129
+ {category: RESOURCE_REGOLITH, maxTier: 5},
130
+ ]
131
+ }
132
+ if (locationType === LocationType.ICE_FIELD) {
133
+ return [
134
+ {category: RESOURCE_GAS, maxTier: 5},
135
+ {category: RESOURCE_BIOMASS, maxTier: 5},
136
+ ]
137
+ }
138
+ if (locationType === LocationType.PLANET) {
83
139
  switch (subtype) {
84
140
  case PLANET_SUBTYPE_GAS_GIANT:
85
- return GAS_GIANT_RESOURCES
141
+ return [
142
+ {category: RESOURCE_GAS, maxTier: 10},
143
+ {category: RESOURCE_CRYSTAL, maxTier: 3},
144
+ ]
86
145
  case PLANET_SUBTYPE_ROCKY:
87
- return ROCKY_RESOURCES
146
+ return [
147
+ {category: RESOURCE_REGOLITH, maxTier: 10},
148
+ {category: RESOURCE_ORE, maxTier: 3},
149
+ ]
88
150
  case PLANET_SUBTYPE_TERRESTRIAL:
89
- return TERRESTRIAL_RESOURCES
151
+ return [
152
+ {category: RESOURCE_ORE, maxTier: 10},
153
+ {category: RESOURCE_BIOMASS, maxTier: 3},
154
+ ]
90
155
  case PLANET_SUBTYPE_ICY:
91
- return ICY_RESOURCES
156
+ return [
157
+ {category: RESOURCE_CRYSTAL, maxTier: 10},
158
+ {category: RESOURCE_REGOLITH, maxTier: 3},
159
+ ]
92
160
  case PLANET_SUBTYPE_OCEAN:
93
- return OCEAN_RESOURCES
161
+ return [
162
+ {category: RESOURCE_BIOMASS, maxTier: 10},
163
+ {category: RESOURCE_GAS, maxTier: 3},
164
+ ]
94
165
  case PLANET_SUBTYPE_INDUSTRIAL:
95
- return INDUSTRIAL_RESOURCES
166
+ return [
167
+ {category: RESOURCE_ORE, maxTier: 3},
168
+ {category: RESOURCE_CRYSTAL, maxTier: 3},
169
+ {category: RESOURCE_REGOLITH, maxTier: 3},
170
+ {category: RESOURCE_BIOMASS, maxTier: 3},
171
+ ]
96
172
  }
97
173
  }
98
174
  return []
99
175
  }
100
176
 
177
+ export function getLocationCandidates(locationType: number, subtype: number): number[] {
178
+ const profile = getLocationProfile(locationType, subtype)
179
+ const ids: number[] = []
180
+ for (const {category, maxTier} of profile) {
181
+ for (let tier = 1; tier <= maxTier; tier++) {
182
+ ids.push(resourceId(category, tier))
183
+ }
184
+ }
185
+ return ids
186
+ }
187
+
101
188
  export function getEligibleResources(
102
189
  locationType: number,
103
190
  subtype: number,
@@ -0,0 +1,51 @@
1
+ import {expect, test} from 'bun:test'
2
+ import {
3
+ MAX_STARS_PER_STAT,
4
+ MAX_STAR_RATING,
5
+ starRating,
6
+ starsForStat,
7
+ statMagnitude,
8
+ STAR_STEP,
9
+ } from './stars'
10
+
11
+ test('starsForStat bands at the 250 boundaries', () => {
12
+ expect(starsForStat(0)).toBe(0)
13
+ expect(starsForStat(1)).toBe(0)
14
+ expect(starsForStat(249)).toBe(0)
15
+ expect(starsForStat(250)).toBe(1)
16
+ expect(starsForStat(499)).toBe(1)
17
+ expect(starsForStat(500)).toBe(2)
18
+ expect(starsForStat(749)).toBe(2)
19
+ expect(starsForStat(750)).toBe(3)
20
+ })
21
+
22
+ test('starsForStat clamps to MAX_STARS_PER_STAT', () => {
23
+ expect(starsForStat(999)).toBe(MAX_STARS_PER_STAT)
24
+ expect(starsForStat(1000)).toBe(MAX_STARS_PER_STAT)
25
+ expect(starsForStat(10_000)).toBe(MAX_STARS_PER_STAT)
26
+ })
27
+
28
+ test('starsForStat never goes negative', () => {
29
+ expect(starsForStat(-50)).toBe(0)
30
+ })
31
+
32
+ test('starRating sums per-stat stars to a 0-9 grade', () => {
33
+ expect(starRating(0, 0, 0)).toBe(0)
34
+ expect(starRating(250, 0, 0)).toBe(1)
35
+ expect(starRating(750, 750, 750)).toBe(MAX_STAR_RATING)
36
+ expect(starRating(999, 999, 999)).toBe(MAX_STAR_RATING)
37
+ expect(starRating(599, 599, 599)).toBe(6)
38
+ expect(starRating(251, 251, 251)).toBe(3)
39
+ })
40
+
41
+ test('statMagnitude sums raw values for tiebreaking', () => {
42
+ expect(statMagnitude(599, 599, 599)).toBe(1797)
43
+ expect(statMagnitude(251, 251, 251)).toBe(753)
44
+ expect(statMagnitude(599, 599, 599)).toBeGreaterThan(statMagnitude(251, 251, 251))
45
+ })
46
+
47
+ test('constants hold their documented values', () => {
48
+ expect(STAR_STEP).toBe(250)
49
+ expect(MAX_STARS_PER_STAT).toBe(3)
50
+ expect(MAX_STAR_RATING).toBe(9)
51
+ })
@@ -0,0 +1,15 @@
1
+ export const STAR_STEP = 250
2
+ export const MAX_STARS_PER_STAT = 3
3
+ export const MAX_STAR_RATING = MAX_STARS_PER_STAT * 3
4
+
5
+ export function starsForStat(value: number): number {
6
+ return Math.max(0, Math.min(MAX_STARS_PER_STAT, Math.floor(value / STAR_STEP)))
7
+ }
8
+
9
+ export function starRating(stat1: number, stat2: number, stat3: number): number {
10
+ return starsForStat(stat1) + starsForStat(stat2) + starsForStat(stat3)
11
+ }
12
+
13
+ export function statMagnitude(stat1: number, stat2: number, stat3: number): number {
14
+ return stat1 + stat2 + stat3
15
+ }
@@ -25,8 +25,7 @@ const ORE_STATS: StatDefinition[] = [
25
25
  key: 'density',
26
26
  label: 'Density',
27
27
  abbreviation: 'DEN',
28
- purpose: 'Mass per unit',
29
- inverted: true,
28
+ purpose: 'Structural integrity — higher rolls produce lighter hulls',
30
29
  },
31
30
  ]
32
31
 
@@ -74,10 +73,11 @@ const GAS_STATS: StatDefinition[] = [
74
73
 
75
74
  const REGOLITH_STATS: StatDefinition[] = [
76
75
  {
77
- key: 'composition',
78
- label: 'Composition',
79
- abbreviation: 'COM',
80
- purpose: 'Mineral/metal mix — drives sensor, chip, and optic crafting quality',
76
+ key: 'cohesion',
77
+ label: 'Cohesion',
78
+ abbreviation: 'COH',
79
+ purpose:
80
+ 'Binding strength of the loose aggregate; higher cohesion yields more rigid frames and hulls',
81
81
  },
82
82
  {
83
83
  key: 'hardness',
@@ -1,7 +1,8 @@
1
1
  import {Bytes, Checksum256, type Checksum256Type} from '@wharfkit/antelope'
2
2
  import {hash512} from '../utils/hash'
3
3
  import {Coordinates, type CoordinatesType} from '../types'
4
- import {getEligibleResources, getResourceWeight, YIELD_THRESHOLD} from './resources'
4
+ import {getItem} from '../data/catalog'
5
+ import {getEligibleResources, getResourceWeight, yieldThresholdAt} from './resources'
5
6
  import {RESERVE_TIERS, rollTier, rollWithinTier} from './tiers'
6
7
 
7
8
  export interface StratumInfo {
@@ -33,15 +34,8 @@ export function deriveStratum(
33
34
 
34
35
  const rawReserve = ((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]) >>> 0
35
36
 
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}
37
+ if (rawReserve > yieldThresholdAt(stratum))
38
+ return {itemId: 0, seed: 0n, richness: 0, reserve: 0}
45
39
 
46
40
  const eligible = getEligibleResources(locationType, subtype, stratum)
47
41
  if (eligible.length === 0) return {itemId: 0, seed: 0n, richness: 0, reserve: 0}
@@ -66,6 +60,12 @@ export function deriveStratum(
66
60
  }
67
61
  }
68
62
 
63
+ const tierRoll = ((bytes[18] << 8) | bytes[19]) >>> 0
64
+ const withinRoll = ((bytes[20] << 8) | bytes[21]) >>> 0
65
+ const tier = rollTier(tierRoll, stratum)
66
+ const unitMass = getItem(selectedItemId).mass
67
+ const reserve = rollWithinTier(withinRoll, RESERVE_TIERS[tier], unitMass)
68
+
69
69
  const seedBigInt =
70
70
  (BigInt(bytes[8]) << 56n) |
71
71
  (BigInt(bytes[9]) << 48n) |
@@ -76,15 +76,11 @@ export function deriveStratum(
76
76
  (BigInt(bytes[14]) << 8n) |
77
77
  BigInt(bytes[15])
78
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)
79
+ let byteSum = 0
80
+ for (let i = 22; i <= 33; i++) byteSum += bytes[i]
81
+ const z = (byteSum - 1530) / 256
82
+ const roll = 500 + 100 * z
83
+ const richness = Math.max(1, Math.min(999, Math.round(roll)))
88
84
 
89
85
  return {itemId: selectedItemId, seed: seedBigInt, richness, reserve}
90
86
  }
@@ -1,3 +1,5 @@
1
+ import {getItem} from '../data/catalog'
2
+
1
3
  export type ReserveTier = 'small' | 'medium' | 'large' | 'massive' | 'motherlode'
2
4
 
3
5
  export interface TierRange {
@@ -6,11 +8,11 @@ export interface TierRange {
6
8
  }
7
9
 
8
10
  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},
11
+ small: {min: 3_600_000, max: 14_400_000},
12
+ medium: {min: 24_000_000, max: 48_000_000},
13
+ large: {min: 96_000_000, max: 168_000_000},
14
+ massive: {min: 240_000_000, max: 600_000_000},
15
+ motherlode: {min: 960_000_000, max: 2_400_000_000},
14
16
  }
15
17
 
16
18
  const SHALLOW_THRESHOLDS = {
@@ -47,8 +49,27 @@ export function rollTier(tierRoll: number, stratum: number): ReserveTier {
47
49
  return 'motherlode'
48
50
  }
49
51
 
50
- export function rollWithinTier(withinRoll: number, range: TierRange): number {
52
+ export function rollWithinTier(
53
+ withinRoll: number,
54
+ range: TierRange,
55
+ resourceUnitMass: number
56
+ ): number {
51
57
  const u = withinRoll / 65535
52
58
  const skewed = u * u
53
- return Math.floor(range.min + skewed * (range.max - range.min))
59
+ const depositMass = range.min + skewed * (range.max - range.min)
60
+ return Math.max(1, Math.floor(depositMass / resourceUnitMass))
61
+ }
62
+
63
+ const RESERVE_TIER_ENTRIES = Object.entries(RESERVE_TIERS) as Array<[ReserveTier, TierRange]>
64
+
65
+ export function tierOfReserve(reserve: number, itemId: number): ReserveTier | null {
66
+ if (reserve <= 0) return null
67
+ const unitMass = getItem(itemId).mass
68
+ if (unitMass <= 0) return null
69
+ const impliedMassLow = reserve * unitMass
70
+ const impliedMassHigh = impliedMassLow + unitMass
71
+ for (const [tier, range] of RESERVE_TIER_ENTRIES) {
72
+ if (impliedMassHigh > range.min && impliedMassLow <= range.max) return tier
73
+ }
74
+ return null
54
75
  }
@@ -0,0 +1,98 @@
1
+ import {UInt64} from '@wharfkit/antelope'
2
+ import {ServerContract} from '../contracts'
3
+ import {
4
+ CAP_DEMOLISH,
5
+ CAP_MODULES,
6
+ CAP_UNDEPLOY,
7
+ CAP_WRAP,
8
+ type EntityClass,
9
+ getEntityClass,
10
+ kindCan,
11
+ } from '../data/kind-registry'
12
+ import {InventoryAccessor} from './inventory-accessor'
13
+ import {Location} from './location'
14
+ import {ScheduleAccessor} from '../scheduling/accessor'
15
+ import * as schedule from '../scheduling/schedule'
16
+ import type {EntityInventory} from './entity-inventory'
17
+
18
+ export class Entity extends ServerContract.Types.entity_info {
19
+ private _sched?: ScheduleAccessor
20
+ private _inv?: InventoryAccessor
21
+
22
+ get name(): string {
23
+ return this.entity_name
24
+ }
25
+
26
+ get location(): Location {
27
+ return Location.from(this.coordinates)
28
+ }
29
+
30
+ get isIdle(): boolean {
31
+ return schedule.isIdle(this)
32
+ }
33
+
34
+ get sched(): ScheduleAccessor {
35
+ this._sched ??= new ScheduleAccessor(this)
36
+ return this._sched
37
+ }
38
+
39
+ get inv(): InventoryAccessor {
40
+ this._inv ??= new InventoryAccessor(this)
41
+ return this._inv
42
+ }
43
+
44
+ get inventory(): EntityInventory[] {
45
+ return this.inv.items
46
+ }
47
+
48
+ get totalCargoMass(): UInt64 {
49
+ return this.inv.totalMass
50
+ }
51
+
52
+ get maxCapacity(): UInt64 {
53
+ return UInt64.from(this.capacity ?? 0)
54
+ }
55
+
56
+ get availableCapacity(): UInt64 {
57
+ const cargo = this.totalCargoMass
58
+ const max = this.maxCapacity
59
+ return cargo.gte(max) ? UInt64.from(0) : max.subtracting(cargo)
60
+ }
61
+
62
+ get isFull(): boolean {
63
+ return this.totalCargoMass.gte(this.maxCapacity)
64
+ }
65
+
66
+ get totalMass(): UInt64 {
67
+ const hull = this.hullmass ? UInt64.from(this.hullmass) : UInt64.from(0)
68
+ return hull.adding(this.totalCargoMass)
69
+ }
70
+
71
+ get entityClass(): EntityClass {
72
+ return getEntityClass(this.type)
73
+ }
74
+
75
+ get canWrap(): boolean {
76
+ return kindCan(this.type, CAP_WRAP)
77
+ }
78
+
79
+ get canUndeploy(): boolean {
80
+ return kindCan(this.type, CAP_UNDEPLOY)
81
+ }
82
+
83
+ get canDemolish(): boolean {
84
+ return kindCan(this.type, CAP_DEMOLISH)
85
+ }
86
+
87
+ get canUseModules(): boolean {
88
+ return kindCan(this.type, CAP_MODULES)
89
+ }
90
+
91
+ isLoading(now: Date): boolean {
92
+ return schedule.isLoading(this, now)
93
+ }
94
+
95
+ isUnloading(now: Date): boolean {
96
+ return schedule.isUnloading(this, now)
97
+ }
98
+ }
@@ -1,4 +1,4 @@
1
- import {Checksum256, Int64, type UInt64} from '@wharfkit/antelope'
1
+ import {Checksum256, Int64, UInt64} from '@wharfkit/antelope'
2
2
  import {type PlatformContract, ServerContract} from '../contracts'
3
3
  import {type EpochInfo, getCurrentEpoch, getEpochInfo} from '../scheduling/epoch'
4
4
  import {hasSystem} from '../utils/system'
@@ -34,7 +34,7 @@ export class GameState extends ServerContract.Types.state_row {
34
34
  * Get the current epoch number from the state
35
35
  */
36
36
  get currentEpoch(): UInt64 {
37
- return this.epoch
37
+ return UInt64.from(this.epoch.toString())
38
38
  }
39
39
 
40
40
  /**
@@ -58,27 +58,6 @@ export class GameState extends ServerContract.Types.state_row {
58
58
  return this.enabled
59
59
  }
60
60
 
61
- /**
62
- * Get the total number of ships in the game
63
- */
64
- get shipCount(): number {
65
- return Number(this.ships)
66
- }
67
-
68
- /**
69
- * Get the current salt value (used for random number generation)
70
- */
71
- get currentSalt(): UInt64 {
72
- return this.salt
73
- }
74
-
75
- /**
76
- * Get the commit hash for the next epoch
77
- */
78
- get nextEpochCommit(): Checksum256 {
79
- return this.commit
80
- }
81
-
82
61
  /**
83
62
  * Calculate the current epoch from game config (if game is set)
84
63
  * This might differ from state.epoch if the blockchain hasn't advanced yet
@@ -97,7 +76,7 @@ export class GameState extends ServerContract.Types.state_row {
97
76
  if (!this._game) {
98
77
  return undefined
99
78
  }
100
- return getEpochInfo(this._game, this.epoch)
79
+ return getEpochInfo(this._game, this.currentEpoch)
101
80
  }
102
81
 
103
82
  /**
@@ -137,16 +116,12 @@ export class GameState extends ServerContract.Types.state_row {
137
116
  get summary(): {
138
117
  enabled: boolean
139
118
  epoch: string
140
- ships: number
141
119
  hasSeed: boolean
142
- hasCommit: boolean
143
120
  } {
144
121
  return {
145
122
  enabled: this.enabled,
146
123
  epoch: this.epoch.toString(),
147
- ships: this.shipCount,
148
124
  hasSeed: !this.seed.equals(Checksum256.from('0'.repeat(64))),
149
- hasCommit: !this.commit.equals(Checksum256.from('0'.repeat(64))),
150
125
  }
151
126
  }
152
127
  }