@shipload/sdk 2.0.0-rc3 → 2.0.0-rc5

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.
@@ -6,13 +6,13 @@ export {
6
6
  getResourceWeight,
7
7
  getLocationCandidates,
8
8
  getDepthThreshold,
9
- getResourceRarity,
9
+ getResourceTier,
10
10
  depthScaleFactor,
11
- DEPTH_THRESHOLD_COMMON,
12
- DEPTH_THRESHOLD_UNCOMMON,
13
- DEPTH_THRESHOLD_RARE,
14
- DEPTH_THRESHOLD_EPIC,
15
- DEPTH_THRESHOLD_LEGENDARY,
11
+ DEPTH_THRESHOLD_T1,
12
+ DEPTH_THRESHOLD_T2,
13
+ DEPTH_THRESHOLD_T3,
14
+ DEPTH_THRESHOLD_T4,
15
+ DEPTH_THRESHOLD_T5,
16
16
  LOCATION_MIN_DEPTH,
17
17
  LOCATION_MAX_DEPTH,
18
18
  YIELD_THRESHOLD,
@@ -23,3 +23,5 @@ export {
23
23
  PLANET_SUBTYPE_OCEAN,
24
24
  PLANET_SUBTYPE_INDUSTRIAL,
25
25
  } from './resources'
26
+
27
+ export * from './stats'
@@ -1,10 +1,10 @@
1
- import {ResourceRarity} from '../types'
1
+ import {ResourceTier} from '../types'
2
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
3
+ 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
8
8
 
9
9
  export const LOCATION_MIN_DEPTH = 500
10
10
  export const LOCATION_MAX_DEPTH = 65535
@@ -20,84 +20,85 @@ export const PLANET_SUBTYPE_INDUSTRIAL = 5
20
20
 
21
21
  interface ResourceEntry {
22
22
  id: number
23
- rarity: ResourceRarity
23
+ tier: ResourceTier
24
24
  }
25
25
 
26
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'},
27
+ {id: 26, tier: 't1'},
28
+ {id: 13, tier: 't2'},
29
+ {id: 24, tier: 't3'},
30
+ {id: 29, tier: 't1'},
31
+ {id: 47, tier: 't2'},
32
+ {id: 79, tier: 't3'},
33
+ {id: 1, tier: 't1'},
34
+ {id: 2, tier: 't2'},
35
+ {id: 18, tier: 't3'},
36
+ {id: 14, tier: 't1'},
37
+ {id: 1000, tier: 't2'},
38
+ {id: 1001, tier: 't3'},
39
+ {id: 6, tier: 't1'},
40
+ {id: 1003, tier: 't2'},
41
+ {id: 1002, tier: 't3'},
41
42
  ]
42
43
 
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
44
+ export function getDepthThreshold(tier: ResourceTier): number {
45
+ switch (tier) {
46
+ case 't1':
47
+ return DEPTH_THRESHOLD_T1
48
+ case 't2':
49
+ return DEPTH_THRESHOLD_T2
50
+ case 't3':
51
+ return DEPTH_THRESHOLD_T3
52
+ case 't4':
53
+ return DEPTH_THRESHOLD_T4
54
+ case 't5':
55
+ return DEPTH_THRESHOLD_T5
55
56
  }
56
57
  }
57
58
 
58
- export function getResourceRarity(itemId: number): ResourceRarity {
59
+ export function getResourceTier(itemId: number): ResourceTier {
59
60
  const entry = RESOURCE_CATALOG.find((r) => r.id === itemId)
60
- return entry ? entry.rarity : 'legendary'
61
+ return entry ? entry.tier : 't5'
61
62
  }
62
63
 
63
64
  export function getResourceWeight(itemId: number, stratum: number): number {
64
- const rarity = getResourceRarity(itemId)
65
- const threshold = getDepthThreshold(rarity)
65
+ const tier = getResourceTier(itemId)
66
+ const threshold = getDepthThreshold(tier)
66
67
  if (stratum < threshold) return 0
67
68
 
68
69
  const depthAbove = stratum - threshold
69
70
 
70
- switch (rarity) {
71
- case 'common':
71
+ switch (tier) {
72
+ case 't1':
72
73
  if (stratum < 2000) return 100
73
74
  if (stratum < 10000) return 80
74
75
  if (stratum < 30000) return 50
75
76
  return 30
76
- case 'uncommon':
77
+ case 't2':
77
78
  if (depthAbove < 3000) return 40
78
79
  if (depthAbove < 8000) return 60
79
80
  return 50
80
- case 'rare':
81
+ case 't3':
81
82
  if (depthAbove < 5000) return 20
82
83
  if (depthAbove < 15000) return 35
83
84
  return 40
84
- case 'epic':
85
+ case 't4':
85
86
  if (depthAbove < 10000) return 10
86
87
  if (depthAbove < 25000) return 20
87
88
  return 30
88
- case 'legendary':
89
+ case 't5':
89
90
  return 10
90
91
  }
91
92
  }
92
93
 
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]
94
+ const ASTEROID_RESOURCES = [26, 13, 24, 29, 47]
95
+ const NEBULA_RESOURCES = [47, 79, 1, 2, 18]
96
+ const GAS_GIANT_RESOURCES = [1, 2, 18, 14, 6]
97
+ const ROCKY_RESOURCES = [26, 13, 24, 14, 1000, 1001, 1002]
98
+ const TERRESTRIAL_RESOURCES = [29, 47, 14, 1000, 6, 1003, 1002]
99
+ const ICY_RESOURCES = [26, 1, 2, 14, 1001, 6, 1003]
100
+ const OCEAN_RESOURCES = [29, 79, 1, 18, 6, 1003, 1002]
101
+ const INDUSTRIAL_RESOURCES = [26, 13, 24, 29, 79, 1000, 1001]
101
102
 
102
103
  export function getLocationCandidates(locationType: number, subtype: number): number[] {
103
104
  if (locationType === 2) return ASTEROID_RESOURCES
@@ -128,8 +129,8 @@ export function getEligibleResources(
128
129
  ): number[] {
129
130
  const candidates = getLocationCandidates(locationType, subtype)
130
131
  return candidates.filter((itemId) => {
131
- const rarity = getResourceRarity(itemId)
132
- const threshold = getDepthThreshold(rarity)
132
+ const tier = getResourceTier(itemId)
133
+ const threshold = getDepthThreshold(tier)
133
134
  return stratum >= threshold
134
135
  })
135
136
  }
@@ -0,0 +1,70 @@
1
+ import type {ResourceCategory} from '../types'
2
+
3
+ export interface StatDefinition {
4
+ key: string
5
+ label: string
6
+ abbreviation: string
7
+ purpose: string
8
+ inverted?: boolean
9
+ }
10
+
11
+ const METAL_STATS: StatDefinition[] = [
12
+ {key: 'strength', label: 'Strength', abbreviation: 'STR', purpose: 'Raw structural/mechanical force'},
13
+ {key: 'tolerance', label: 'Tolerance', abbreviation: 'TOL', purpose: 'Ability to withstand heat, pressure, and stress extremes'},
14
+ {key: 'density', label: 'Density', abbreviation: 'DEN', purpose: 'Mass per unit', inverted: true},
15
+ ]
16
+
17
+ const PRECIOUS_STATS: StatDefinition[] = [
18
+ {key: 'conductivity', label: 'Conductivity', abbreviation: 'CON', purpose: 'Efficiency of energy/signal transfer'},
19
+ {key: 'ductility', label: 'Ductility', abbreviation: 'DUC', purpose: 'Ability to be worked into fine, precise shapes'},
20
+ {key: 'reflectivity', label: 'Reflectivity', abbreviation: 'REF', purpose: 'Surface quality for heat management and precision optics'},
21
+ ]
22
+
23
+ const GAS_STATS: StatDefinition[] = [
24
+ {key: 'volatility', label: 'Volatility', abbreviation: 'VOL', purpose: 'Energy release potential for propulsion and force'},
25
+ {key: 'reactivity', label: 'Reactivity', abbreviation: 'REA', purpose: 'Chemical interaction speed for processing and penetration'},
26
+ {key: 'thermal', label: 'Thermal', abbreviation: 'THM', purpose: 'Heat capacity for thermal management'},
27
+ ]
28
+
29
+ const MINERAL_STATS: StatDefinition[] = [
30
+ {key: 'resonance', label: 'Resonance', abbreviation: 'RES', purpose: 'Energy field interaction — storage, focusing, projection'},
31
+ {key: 'hardness', label: 'Hardness', abbreviation: 'HRD', purpose: 'Resistance to wear — cutting surfaces, penetration'},
32
+ {key: 'clarity', label: 'Clarity', abbreviation: 'CLR', purpose: 'Crystalline perfection — precision optics'},
33
+ ]
34
+
35
+ const ORGANIC_STATS: StatDefinition[] = [
36
+ {key: 'plasticity', label: 'Plasticity', abbreviation: 'PLA', purpose: 'Ease of reshaping — speeds processing'},
37
+ {key: 'insulation', label: 'Insulation', abbreviation: 'INS', purpose: 'Energy containment — reduces energy loss'},
38
+ {key: 'purity', label: 'Purity', abbreviation: 'PUR', purpose: 'Biological cleanliness — better composites and lubricants'},
39
+ ]
40
+
41
+ const STAT_MAP: Record<ResourceCategory, StatDefinition[]> = {
42
+ metal: METAL_STATS,
43
+ precious: PRECIOUS_STATS,
44
+ gas: GAS_STATS,
45
+ mineral: MINERAL_STATS,
46
+ organic: ORGANIC_STATS,
47
+ }
48
+
49
+ export function getStatDefinitions(category: ResourceCategory): StatDefinition[] {
50
+ return STAT_MAP[category]
51
+ }
52
+
53
+ export function getStatName(category: ResourceCategory, index: 0 | 1 | 2): StatDefinition {
54
+ return STAT_MAP[category][index]
55
+ }
56
+
57
+ export interface NamedStats {
58
+ definitions: StatDefinition[]
59
+ values: [number, number, number]
60
+ }
61
+
62
+ export function resolveStats(
63
+ category: ResourceCategory,
64
+ stats: {stat1: number; stat2: number; stat3: number}
65
+ ): NamedStats {
66
+ return {
67
+ definitions: STAT_MAP[category],
68
+ values: [stats.stat1, stats.stat2, stats.stat3],
69
+ }
70
+ }
@@ -16,10 +16,9 @@ export interface StratumInfo {
16
16
  }
17
17
 
18
18
  export interface ResourceStats {
19
- purity: number
20
- density: number
21
- reactivity: number
22
- resonance: number
19
+ stat1: number
20
+ stat2: number
21
+ stat3: number
23
22
  }
24
23
 
25
24
  export function deriveStratum(
@@ -94,23 +93,26 @@ export function deriveStratum(
94
93
  }
95
94
 
96
95
  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))
96
+ const seedBytes = new Uint8Array(8)
97
+ for (let i = 7; i >= 0; i--) {
98
+ seedBytes[i] = Number(seed & 0xFFn)
99
+ seed >>= 8n
100
+ }
101
+ const hashResult = Checksum256.hash(Bytes.from(seedBytes))
101
102
  const hashBytes = hashResult.array
102
103
 
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
104
+ const extractU16 = (offset: number): number => (hashBytes[offset] << 8) | hashBytes[offset + 1]
105
+
106
+ const weibull = (raw: number): number => {
107
+ const u = raw / 65536
108
+ let x = 0.27 * Math.sqrt(-Math.log(1 - u))
109
+ if (x > 1) x = 1
110
+ return Math.floor(x * 999) + 1
111
+ }
109
112
 
110
113
  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,
114
+ stat1: weibull(extractU16(0)),
115
+ stat2: weibull(extractU16(2)),
116
+ stat3: weibull(extractU16(4)),
115
117
  }
116
118
  }
@@ -47,7 +47,7 @@ export type {EntityRefInput} from './managers/actions'
47
47
  export {getItem, getItems, itemIds} from './market/items'
48
48
  export {getCurrentEpoch, getEpochInfo} from './scheduling/epoch'
49
49
  export type {EpochInfo} from './scheduling/epoch'
50
- export {marketPrice, marketPrices, getRarity, Rarities} from './market/market'
50
+ export {marketPrice, marketPrices, getRarity, getRarityMultiplier, Rarities} from './market/market'
51
51
  export type {Rarity} from './market/market'
52
52
  export {
53
53
  getSystemName,
@@ -67,13 +67,13 @@ export {
67
67
  getResourceWeight,
68
68
  getLocationCandidates,
69
69
  getDepthThreshold,
70
- getResourceRarity,
70
+ getResourceTier,
71
71
  depthScaleFactor,
72
- DEPTH_THRESHOLD_COMMON,
73
- DEPTH_THRESHOLD_UNCOMMON,
74
- DEPTH_THRESHOLD_RARE,
75
- DEPTH_THRESHOLD_EPIC,
76
- DEPTH_THRESHOLD_LEGENDARY,
72
+ DEPTH_THRESHOLD_T1,
73
+ DEPTH_THRESHOLD_T2,
74
+ DEPTH_THRESHOLD_T3,
75
+ DEPTH_THRESHOLD_T4,
76
+ DEPTH_THRESHOLD_T5,
77
77
  LOCATION_MIN_DEPTH,
78
78
  LOCATION_MAX_DEPTH,
79
79
  PLANET_SUBTYPE_GAS_GIANT,
@@ -86,6 +86,9 @@ export {
86
86
 
87
87
  export type {StratumInfo, ResourceStats} from './derivation'
88
88
 
89
+ export {getStatDefinitions, getStatName, resolveStats} from './derivation'
90
+ export type {StatDefinition, NamedStats} from './derivation'
91
+
89
92
  export {hash, hash512} from './utils/hash'
90
93
 
91
94
  export type {Deal, FindDealsOptions} from './trading/deal'
@@ -10,7 +10,7 @@ const items: Item[] = itemsData.map((g) =>
10
10
  base_price: g.base_price,
11
11
  mass: g.mass,
12
12
  category: g.category,
13
- rarity: g.rarity,
13
+ tier: g.tier,
14
14
  color: g.color,
15
15
  })
16
16
  )
@@ -14,9 +14,9 @@ export enum Rarities {
14
14
  }
15
15
 
16
16
  export interface Rarity {
17
- rarity: Rarities // The rarity description of this price
18
- minMultiplier: number // The minimum multiplier for this rarity
19
- maxMultiplier: number // The maximum multiplier for this rarity
17
+ rarity: Rarities
18
+ minMultiplier: number
19
+ maxMultiplier: number
20
20
  }
21
21
 
22
22
  export function getRarity(
@@ -29,77 +29,66 @@ export function getRarity(
29
29
  const rarityRoll = roll(gameSeed, seed)
30
30
 
31
31
  if (rarityRoll < 13) {
32
- // (Orange) ~0.02% chance = incredibly high value
33
32
  return {
34
33
  rarity: Rarities.legendary,
35
34
  minMultiplier: 2.25,
36
35
  maxMultiplier: 3.0,
37
36
  }
38
37
  } else if (rarityRoll < 176) {
39
- // (Purple) ~0.25% chance = super high value
40
38
  return {
41
39
  rarity: Rarities.epic,
42
40
  minMultiplier: 1.75,
43
41
  maxMultiplier: 2.25,
44
42
  }
45
43
  } else if (rarityRoll < 996) {
46
- // (Blue) ~1.25% chance = very high value
47
44
  return {
48
45
  rarity: Rarities.rare,
49
46
  minMultiplier: 1.4,
50
47
  maxMultiplier: 1.75,
51
48
  }
52
49
  } else if (rarityRoll < 2966) {
53
- // (Green) ~3.00% chance = high value
54
50
  return {
55
51
  rarity: Rarities.uncommon,
56
52
  minMultiplier: 1.225,
57
53
  maxMultiplier: 1.4,
58
54
  }
59
55
  } else if (rarityRoll < 19568) {
60
- // (White) ~25.33% chance = slightly higher value
61
56
  return {
62
57
  rarity: Rarities.common,
63
58
  minMultiplier: 1.07,
64
59
  maxMultiplier: 1.225,
65
60
  }
66
61
  } else if (rarityRoll < 45988) {
67
- // ~40.30% chance = no value change
68
62
  return {
69
63
  rarity: Rarities.trash,
70
64
  minMultiplier: 1,
71
65
  maxMultiplier: 1.07,
72
- } // Product is not available
66
+ }
73
67
  } else if (rarityRoll < 62508) {
74
- // (White) ~25.33% chance = slightly lower value
75
68
  return {
76
69
  rarity: Rarities.common,
77
70
  minMultiplier: 0.925,
78
71
  maxMultiplier: 1,
79
72
  }
80
73
  } else if (rarityRoll < 64518) {
81
- // (Green) ~3.00% chance = low value
82
74
  return {
83
75
  rarity: Rarities.uncommon,
84
76
  minMultiplier: 0.77,
85
77
  maxMultiplier: 0.925,
86
78
  }
87
79
  } else if (rarityRoll < 65437) {
88
- // (Blue) ~1.25% chance = very low value
89
80
  return {
90
81
  rarity: Rarities.rare,
91
82
  minMultiplier: 0.595,
92
83
  maxMultiplier: 0.77,
93
84
  }
94
85
  } else if (rarityRoll < 65523) {
95
- // (Purple) ~0.25% chance = super low value
96
86
  return {
97
87
  rarity: Rarities.epic,
98
88
  minMultiplier: 0.41,
99
89
  maxMultiplier: 0.595,
100
90
  }
101
91
  } else {
102
- // (Orange) ~0.02% chance = incredibly low value
103
92
  return {
104
93
  rarity: Rarities.legendary,
105
94
  minMultiplier: 0.285,
@@ -178,8 +167,6 @@ export function marketPrice(
178
167
  const item = getItem(goodId)
179
168
  let price = Number(item.base_price)
180
169
 
181
- // Rarity multiplier of the deal (changes with epoch)
182
- // Large impact range on price, from 0.285x to 3.0x
183
170
  const rarityMultiplier = getRarityMultiplier(gameSeed, state.seed, location, goodId)
184
171
  price *= rarityMultiplier
185
172
 
package/src/types.ts CHANGED
@@ -132,8 +132,8 @@ export interface Distance {
132
132
  distance: UInt16
133
133
  }
134
134
 
135
- export type ResourceCategory = 'metal' | 'gas' | 'mineral' | 'organic'
136
- export type ResourceRarity = 'common' | 'uncommon' | 'rare' | 'epic' | 'legendary'
135
+ export type ResourceCategory = 'metal' | 'precious' | 'gas' | 'mineral' | 'organic'
136
+ export type ResourceTier = 't1' | 't2' | 't3' | 't4' | 't5'
137
137
 
138
138
  @Struct.type('item')
139
139
  export class Item extends Struct {
@@ -150,7 +150,7 @@ export class Item extends Struct {
150
150
  @Struct.field('string')
151
151
  category!: ResourceCategory
152
152
  @Struct.field('string')
153
- rarity!: ResourceRarity
153
+ tier!: ResourceTier
154
154
  @Struct.field('string')
155
155
  color!: string
156
156
  }
@@ -1,8 +1,10 @@
1
- import {Checksum256, Checksum256Type, UInt8} from '@wharfkit/antelope'
1
+ import {Checksum256, Checksum256Type, Checksum512, UInt8} from '@wharfkit/antelope'
2
2
  import {hash512} from './hash'
3
3
  import {Coordinates, CoordinatesType, LocationType} from '../types'
4
4
  import {ServerContract} from '../contracts'
5
5
  import syllables from '../data/syllables.json'
6
+ import nebulaAdjectives from '../data/nebula-adjectives.json'
7
+ import nebulaNouns from '../data/nebula-nouns.json'
6
8
 
7
9
  const LOCATION_EXISTS_THRESHOLD = 0x10
8
10
  const LOCATION_ACTIVE_THRESHOLD = 0x80
@@ -31,23 +33,55 @@ export function isExtractableLocation(locationType: LocationType): boolean {
31
33
  return locationType !== LocationType.EMPTY
32
34
  }
33
35
 
34
- export function getSystemName(gameSeed: Checksum256Type, location: CoordinatesType): string {
35
- const seed = Checksum256.from(gameSeed)
36
- if (!hasSystem(seed, location)) {
37
- throw new Error("System doesn't exist at location")
38
- }
39
- const seedStr = `${location.x}${location.y}systemName`
40
- const hashResult = hash512(seed, seedStr)
41
- const syllableCount = 1 + (hashResult.array[0] % 3)
36
+ function uint16(hash: Checksum512, offset: number): number {
37
+ return (hash.array[offset] << 8) | hash.array[offset + 1]
38
+ }
39
+
40
+ function generatePlanetName(hashResult: Checksum512): string {
41
+ const syllableCount = 2 + (hashResult.array[0] % 2)
42
42
  const name: string[] = []
43
43
  for (let i = 0; i < syllableCount; i++) {
44
- const index = hashResult.array[i] % syllables.length
44
+ const index = uint16(hashResult, 1 + i * 2) % syllables.length
45
45
  const syllable = syllables[index]
46
46
  name.push(i > 0 ? syllable.toLowerCase() : syllable)
47
47
  }
48
48
  return name.join('')
49
49
  }
50
50
 
51
+ function generateAsteroidName(hashResult: Checksum512): string {
52
+ const A = 65
53
+ const letter1 = String.fromCharCode(A + (hashResult.array[0] % 26))
54
+ const letter2 = String.fromCharCode(A + (hashResult.array[1] % 26))
55
+ const num = (uint16(hashResult, 2) % 9000) + 1000
56
+ return `${letter1}${letter2}-${num}`
57
+ }
58
+
59
+ function generateNebulaName(hashResult: Checksum512): string {
60
+ const adjIdx = uint16(hashResult, 0) % nebulaAdjectives.length
61
+ const nounIdx = uint16(hashResult, 2) % nebulaNouns.length
62
+ return `${nebulaAdjectives[adjIdx]} ${nebulaNouns[nounIdx]}`
63
+ }
64
+
65
+ export function getSystemName(gameSeed: Checksum256Type, location: CoordinatesType): string {
66
+ const seed = Checksum256.from(gameSeed)
67
+ const locationType = getLocationType(seed, location)
68
+ if (locationType === LocationType.EMPTY) {
69
+ throw new Error("System doesn't exist at location")
70
+ }
71
+ const seedStr = `${location.x}-${location.y}-${locationType}-name`
72
+ const hashResult = hash512(seed, seedStr)
73
+ switch (locationType) {
74
+ case LocationType.PLANET:
75
+ return generatePlanetName(hashResult)
76
+ case LocationType.ASTEROID:
77
+ return generateAsteroidName(hashResult)
78
+ case LocationType.NEBULA:
79
+ return generateNebulaName(hashResult)
80
+ default:
81
+ return generatePlanetName(hashResult)
82
+ }
83
+ }
84
+
51
85
  export function hasSystem(gameSeed: Checksum256Type, coordinates: CoordinatesType): boolean {
52
86
  return getLocationType(gameSeed, coordinates) !== LocationType.EMPTY
53
87
  }