@shipload/sdk 2.0.0-rc4 → 2.0.0-rc6

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 (45) hide show
  1. package/lib/shipload.d.ts +411 -1025
  2. package/lib/shipload.js +879 -2057
  3. package/lib/shipload.js.map +1 -1
  4. package/lib/shipload.m.js +852 -2028
  5. package/lib/shipload.m.js.map +1 -1
  6. package/package.json +1 -1
  7. package/src/capabilities/crafting.ts +10 -0
  8. package/src/capabilities/guards.ts +0 -5
  9. package/src/capabilities/index.ts +1 -0
  10. package/src/capabilities/storage.ts +0 -8
  11. package/src/contracts/server.ts +106 -225
  12. package/src/data/items.json +15 -14
  13. package/src/data/recipes.ts +129 -0
  14. package/src/derivation/crafting.ts +120 -0
  15. package/src/derivation/index.ts +9 -6
  16. package/src/derivation/resources.ts +54 -53
  17. package/src/derivation/stats.ts +146 -0
  18. package/src/derivation/stratum.ts +14 -14
  19. package/src/entities/cargo-utils.ts +6 -64
  20. package/src/entities/container.ts +18 -0
  21. package/src/entities/entity-inventory.ts +0 -4
  22. package/src/entities/inventory-accessor.ts +0 -4
  23. package/src/entities/location.ts +2 -197
  24. package/src/entities/player.ts +1 -274
  25. package/src/entities/ship.ts +0 -21
  26. package/src/entities/warehouse.ts +0 -4
  27. package/src/index-module.ts +43 -47
  28. package/src/managers/actions.ts +38 -90
  29. package/src/managers/context.ts +0 -9
  30. package/src/managers/index.ts +0 -1
  31. package/src/managers/locations.ts +2 -85
  32. package/src/market/items.ts +1 -2
  33. package/src/scheduling/projection.ts +0 -10
  34. package/src/shipload.ts +0 -5
  35. package/src/types/capabilities.ts +1 -9
  36. package/src/types/entity-traits.ts +3 -4
  37. package/src/types/entity.ts +0 -1
  38. package/src/types.ts +8 -28
  39. package/src/utils/system.ts +5 -4
  40. package/src/managers/trades.ts +0 -119
  41. package/src/market/market.ts +0 -208
  42. package/src/market/rolls.ts +0 -8
  43. package/src/trading/collect.ts +0 -938
  44. package/src/trading/deal.ts +0 -207
  45. package/src/trading/trade.ts +0 -203
@@ -1,16 +1,17 @@
1
1
  [
2
- {"id": 1, "name": "Hydrogen", "description": "A lightweight gas used for fuel cells and propulsion.", "base_price": 50, "mass": 15000, "category": "gas", "rarity": "common", "color": "#7EC8E3"},
3
- {"id": 2, "name": "Helium", "description": "An inert noble gas used in energy systems.", "base_price": 75, "mass": 2000, "category": "gas", "rarity": "uncommon", "color": "#F5E6CC"},
4
- {"id": 6, "name": "Carbon", "description": "A versatile element for life support and coatings.", "base_price": 100, "mass": 12000, "category": "organic", "rarity": "common", "color": "#4A4A4A"},
5
- {"id": 14, "name": "Silicon", "description": "A semiconductor used in sensors and computing.", "base_price": 150, "mass": 28000, "category": "mineral", "rarity": "common", "color": "#B8A9C9"},
6
- {"id": 18, "name": "Argon", "description": "A noble gas used in industrial and energy applications.", "base_price": 60, "mass": 8000, "category": "gas", "rarity": "rare", "color": "#9B59B6"},
7
- {"id": 22, "name": "Titanium", "description": "A strong, lightweight metal for ship construction.", "base_price": 300, "mass": 48000, "category": "metal", "rarity": "rare", "color": "#C0C0C0"},
8
- {"id": 26, "name": "Iron", "description": "A versatile metal used in hulls and structures.", "base_price": 125, "mass": 40000, "category": "metal", "rarity": "common", "color": "#B7410E"},
9
- {"id": 29, "name": "Copper", "description": "A conductive metal for electronics and wiring.", "base_price": 200, "mass": 60000, "category": "metal", "rarity": "uncommon", "color": "#B87333"},
10
- {"id": 54, "name": "Xenon", "description": "A rare noble gas for advanced propulsion systems.", "base_price": 175, "mass": 20000, "category": "gas", "rarity": "epic", "color": "#5B2C6F"},
11
- {"id": 74, "name": "Tungsten", "description": "An extremely dense metal for heavy-duty applications.", "base_price": 500, "mass": 80000, "category": "metal", "rarity": "epic", "color": "#708090"},
12
- {"id": 1000, "name": "Quartz", "description": "A crystalline mineral for sensors and optics.", "base_price": 200, "mass": 35000, "category": "mineral", "rarity": "uncommon", "color": "#E8D5B7"},
13
- {"id": 1001, "name": "Sapphire", "description": "A precious crystal for precision instruments.", "base_price": 400, "mass": 45000, "category": "mineral", "rarity": "rare", "color": "#0F52BA"},
14
- {"id": 1002, "name": "Polymers", "description": "Synthetic materials for coatings and consumables.", "base_price": 150, "mass": 25000, "category": "organic", "rarity": "rare", "color": "#2ECC71"},
15
- {"id": 1003, "name": "Biomass", "description": "Organic matter for life support systems.", "base_price": 100, "mass": 30000, "category": "organic", "rarity": "uncommon", "color": "#8B4513"}
2
+ {"id": 26, "name": "Iron", "description": "A versatile metal used in hulls and structures.", "mass": 40000, "category": "metal", "tier": "t1", "color": "#B7410E"},
3
+ {"id": 13, "name": "Aluminum", "description": "A lightweight metal for structural components.", "mass": 27000, "category": "metal", "tier": "t2", "color": "#A8B5C2"},
4
+ {"id": 24, "name": "Chromium", "description": "A hard, corrosion-resistant alloy metal.", "mass": 52000, "category": "metal", "tier": "t3", "color": "#D4E6F1"},
5
+ {"id": 29, "name": "Copper", "description": "A conductive metal for electronics and wiring.", "mass": 60000, "category": "precious", "tier": "t1", "color": "#B87333"},
6
+ {"id": 47, "name": "Silver", "description": "A high-conductivity metal for precision components.", "mass": 55000, "category": "precious", "tier": "t2", "color": "#C0C0C0"},
7
+ {"id": 79, "name": "Gold", "description": "A corrosion-proof metal for advanced electronics.", "mass": 70000, "category": "precious", "tier": "t3", "color": "#FFD700"},
8
+ {"id": 1, "name": "Hydrogen", "description": "A lightweight gas used for fuel cells and propulsion.", "mass": 15000, "category": "gas", "tier": "t1", "color": "#7EC8E3"},
9
+ {"id": 2, "name": "Helium", "description": "An inert noble gas used in energy systems.", "mass": 2000, "category": "gas", "tier": "t2", "color": "#F5E6CC"},
10
+ {"id": 18, "name": "Argon", "description": "A noble gas used in industrial and energy applications.", "mass": 8000, "category": "gas", "tier": "t3", "color": "#9B59B6"},
11
+ {"id": 14, "name": "Silicon", "description": "A semiconductor used in sensors and computing.", "mass": 28000, "category": "mineral", "tier": "t1", "color": "#B8A9C9"},
12
+ {"id": 1000, "name": "Quartz", "description": "A crystalline mineral for sensors and optics.", "mass": 35000, "category": "mineral", "tier": "t2", "color": "#E8D5B7"},
13
+ {"id": 1001, "name": "Sapphire", "description": "A precious crystal for precision instruments.", "mass": 45000, "category": "mineral", "tier": "t3", "color": "#0F52BA"},
14
+ {"id": 6, "name": "Carbon", "description": "A versatile element for life support and coatings.", "mass": 12000, "category": "organic", "tier": "t1", "color": "#4A4A4A"},
15
+ {"id": 1003, "name": "Biomass", "description": "Organic matter for life support systems.", "mass": 30000, "category": "organic", "tier": "t2", "color": "#8B4513"},
16
+ {"id": 1002, "name": "Resin", "description": "A fossilized organic compound for coatings and sealants.", "mass": 25000, "category": "organic", "tier": "t3", "color": "#DAA520"}
16
17
  ]
@@ -0,0 +1,129 @@
1
+ import type {ResourceCategory} from '../types'
2
+
3
+ export const ITEM_HULL_PLATES = 10001
4
+ export const ITEM_CARGO_LINING = 10002
5
+ export const ITEM_CONTAINER_PACKED = 10003
6
+
7
+ export interface RecipeInput {
8
+ category?: ResourceCategory
9
+ itemId?: number
10
+ quantity: number
11
+ }
12
+
13
+ export interface ComponentStat {
14
+ key: string
15
+ source: ResourceCategory
16
+ }
17
+
18
+ export interface ComponentDefinition {
19
+ id: number
20
+ name: string
21
+ description: string
22
+ color: string
23
+ mass: number
24
+ stats: ComponentStat[]
25
+ recipe: RecipeInput[]
26
+ usedIn: {type: 'entity'; name: string}[]
27
+ }
28
+
29
+ export interface EntityRecipe {
30
+ id: string
31
+ name: string
32
+ description: string
33
+ color: string
34
+ packedItemId: number
35
+ recipe: RecipeInput[]
36
+ stats: {key: string; sourceComponentId: number; sourceStatKey: string}[]
37
+ }
38
+
39
+ export interface CraftableItem {
40
+ type: 'component' | 'entity'
41
+ id: number | string
42
+ name: string
43
+ description: string
44
+ color: string
45
+ }
46
+
47
+ export const components: ComponentDefinition[] = [
48
+ {
49
+ id: ITEM_HULL_PLATES,
50
+ name: 'Hull Plates',
51
+ description: 'Structural plating formed from metal. Used in hulls, containers, and frames.',
52
+ color: '#7B8D9E',
53
+ mass: 50000,
54
+ stats: [
55
+ {key: 'strength', source: 'metal'},
56
+ {key: 'density', source: 'metal'},
57
+ ],
58
+ recipe: [{category: 'metal', quantity: 40}],
59
+ usedIn: [{type: 'entity', name: 'Container'}],
60
+ },
61
+ {
62
+ id: ITEM_CARGO_LINING,
63
+ name: 'Cargo Lining',
64
+ description:
65
+ 'Precision-formed composite lining for cargo storage. Combines precious metal shaping with organic sealing.',
66
+ color: '#D4A843',
67
+ mass: 30000,
68
+ stats: [
69
+ {key: 'ductility', source: 'precious'},
70
+ {key: 'purity', source: 'organic'},
71
+ ],
72
+ recipe: [
73
+ {category: 'precious', quantity: 10},
74
+ {category: 'organic', quantity: 20},
75
+ ],
76
+ usedIn: [{type: 'entity', name: 'Container'}],
77
+ },
78
+ ]
79
+
80
+ export const entityRecipes: EntityRecipe[] = [
81
+ {
82
+ id: 'container',
83
+ name: 'Container',
84
+ description: 'Passive floating cargo storage in space. Towed by ships.',
85
+ color: '#7B8D9E',
86
+ packedItemId: ITEM_CONTAINER_PACKED,
87
+ recipe: [
88
+ {itemId: ITEM_HULL_PLATES, quantity: 6},
89
+ {itemId: ITEM_CARGO_LINING, quantity: 2},
90
+ ],
91
+ stats: [
92
+ {key: 'strength', sourceComponentId: ITEM_HULL_PLATES, sourceStatKey: 'strength'},
93
+ {key: 'density', sourceComponentId: ITEM_HULL_PLATES, sourceStatKey: 'density'},
94
+ {key: 'ductility', sourceComponentId: ITEM_CARGO_LINING, sourceStatKey: 'ductility'},
95
+ {key: 'purity', sourceComponentId: ITEM_CARGO_LINING, sourceStatKey: 'purity'},
96
+ ],
97
+ },
98
+ ]
99
+
100
+ export function getComponentById(id: number): ComponentDefinition | undefined {
101
+ return components.find((c) => c.id === id)
102
+ }
103
+
104
+ export function getEntityRecipe(id: string): EntityRecipe | undefined {
105
+ return entityRecipes.find((r) => r.id === id)
106
+ }
107
+
108
+ export function getEntityRecipeByItemId(itemId: number): EntityRecipe | undefined {
109
+ return entityRecipes.find((r) => r.packedItemId === itemId)
110
+ }
111
+
112
+ export function getAllCraftableItems(): CraftableItem[] {
113
+ const items: CraftableItem[] = []
114
+ for (const comp of components) {
115
+ items.push({type: 'component', id: comp.id, name: comp.name, description: comp.description, color: comp.color})
116
+ }
117
+ for (const entity of entityRecipes) {
118
+ items.push({type: 'entity', id: entity.id, name: entity.name, description: entity.description, color: entity.color})
119
+ }
120
+ return items
121
+ }
122
+
123
+ export function getComponentsForCategory(category: ResourceCategory): ComponentDefinition[] {
124
+ return components.filter((c) => c.recipe.some((r) => r.category === category))
125
+ }
126
+
127
+ export function getComponentsForStat(statKey: string): ComponentDefinition[] {
128
+ return components.filter((c) => c.stats.some((s) => s.key === statKey))
129
+ }
@@ -0,0 +1,120 @@
1
+ import type {ResourceCategory} from '../types'
2
+ import {
3
+ components,
4
+ entityRecipes,
5
+ getComponentById,
6
+ getEntityRecipe,
7
+ } from '../data/recipes'
8
+
9
+ export interface StackInput {
10
+ quantity: number
11
+ stats: Record<string, number>
12
+ }
13
+
14
+ export interface CategoryStacks {
15
+ category: ResourceCategory
16
+ stacks: StackInput[]
17
+ }
18
+
19
+ export function encodeStats(values: number[]): bigint {
20
+ let seed = 0n
21
+ for (let i = 0; i < values.length && i < 6; i++) {
22
+ seed |= BigInt(values[i] & 0x3ff) << BigInt(i * 10)
23
+ }
24
+ return seed
25
+ }
26
+
27
+ export function decodeStats(seed: bigint, count: number): number[] {
28
+ const stats: number[] = []
29
+ for (let i = 0; i < count; i++) {
30
+ stats.push(Number((seed >> BigInt(i * 10)) & 0x3ffn))
31
+ }
32
+ return stats
33
+ }
34
+
35
+ export function decodeCraftedItemStats(itemId: number, seed: bigint): Record<string, number> {
36
+ const comp = getComponentById(itemId)
37
+ if (comp) {
38
+ const values = decodeStats(seed, comp.stats.length)
39
+ const result: Record<string, number> = {}
40
+ for (let i = 0; i < comp.stats.length; i++) {
41
+ result[comp.stats[i].key] = values[i]
42
+ }
43
+ return result
44
+ }
45
+
46
+ const entityRecipe = entityRecipes.find((r) => r.packedItemId === itemId)
47
+ if (entityRecipe) {
48
+ const values = decodeStats(seed, entityRecipe.stats.length)
49
+ const result: Record<string, number> = {}
50
+ for (let i = 0; i < entityRecipe.stats.length; i++) {
51
+ result[entityRecipe.stats[i].key] = values[i]
52
+ }
53
+ return result
54
+ }
55
+
56
+ return {}
57
+ }
58
+
59
+ export function blendStacks(stacks: StackInput[], statKey: string): number {
60
+ let totalQty = 0
61
+ let weightedSum = 0
62
+ for (const stack of stacks) {
63
+ const val = stack.stats[statKey] ?? 0
64
+ weightedSum += val * stack.quantity
65
+ totalQty += stack.quantity
66
+ }
67
+ if (totalQty === 0) return 0
68
+ return Math.round(weightedSum / totalQty)
69
+ }
70
+
71
+ export function computeComponentStats(
72
+ componentId: number,
73
+ categoryStacks: CategoryStacks[]
74
+ ): {key: string; value: number}[] {
75
+ const comp = getComponentById(componentId)
76
+ if (!comp) return []
77
+
78
+ return comp.stats.map((statDef) => {
79
+ const matching = categoryStacks.find((cs) => cs.category === statDef.source)
80
+ const value = matching ? blendStacks(matching.stacks, statDef.key) : 0
81
+ return {key: statDef.key, value: Math.max(1, Math.min(999, value))}
82
+ })
83
+ }
84
+
85
+ export function blendComponentStacks(
86
+ stacks: {quantity: number; stats: Record<string, number>}[]
87
+ ): Record<string, number> {
88
+ if (stacks.length === 0) return {}
89
+ const allKeys = new Set<string>()
90
+ for (const s of stacks) {
91
+ for (const k of Object.keys(s.stats)) allKeys.add(k)
92
+ }
93
+ const result: Record<string, number> = {}
94
+ for (const key of allKeys) {
95
+ result[key] = blendStacks(
96
+ stacks.map((s) => ({quantity: s.quantity, stats: s.stats})),
97
+ key
98
+ )
99
+ }
100
+ return result
101
+ }
102
+
103
+ export function computeEntityStats(
104
+ entityRecipeId: string,
105
+ componentStacks: Record<number, {quantity: number; stats: Record<string, number>}[]>
106
+ ): {key: string; value: number}[] {
107
+ const recipe = getEntityRecipe(entityRecipeId)
108
+ if (!recipe) return []
109
+
110
+ const blendedByComponent: Record<number, Record<string, number>> = {}
111
+ for (const [compId, stacks] of Object.entries(componentStacks)) {
112
+ blendedByComponent[Number(compId)] = blendComponentStacks(stacks)
113
+ }
114
+
115
+ return recipe.stats.map((stat) => {
116
+ const blended = blendedByComponent[stat.sourceComponentId] ?? {}
117
+ const value = blended[stat.sourceStatKey] ?? 0
118
+ return {key: stat.key, value: Math.max(1, Math.min(999, value))}
119
+ })
120
+ }
@@ -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,6 @@ export {
23
23
  PLANET_SUBTYPE_OCEAN,
24
24
  PLANET_SUBTYPE_INDUSTRIAL,
25
25
  } from './resources'
26
+
27
+ export * from './stats'
28
+ export * from './crafting'
@@ -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,146 @@
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
+ {
13
+ key: 'strength',
14
+ label: 'Strength',
15
+ abbreviation: 'STR',
16
+ purpose: 'Raw structural/mechanical force',
17
+ },
18
+ {
19
+ key: 'tolerance',
20
+ label: 'Tolerance',
21
+ abbreviation: 'TOL',
22
+ purpose: 'Ability to withstand heat, pressure, and stress extremes',
23
+ },
24
+ {
25
+ key: 'density',
26
+ label: 'Density',
27
+ abbreviation: 'DEN',
28
+ purpose: 'Mass per unit',
29
+ inverted: true,
30
+ },
31
+ ]
32
+
33
+ const PRECIOUS_STATS: StatDefinition[] = [
34
+ {
35
+ key: 'conductivity',
36
+ label: 'Conductivity',
37
+ abbreviation: 'CON',
38
+ purpose: 'Efficiency of energy/signal transfer',
39
+ },
40
+ {
41
+ key: 'ductility',
42
+ label: 'Ductility',
43
+ abbreviation: 'DUC',
44
+ purpose: 'Ability to be worked into fine, precise shapes',
45
+ },
46
+ {
47
+ key: 'reflectivity',
48
+ label: 'Reflectivity',
49
+ abbreviation: 'REF',
50
+ purpose: 'Surface quality for heat management and precision optics',
51
+ },
52
+ ]
53
+
54
+ const GAS_STATS: StatDefinition[] = [
55
+ {
56
+ key: 'volatility',
57
+ label: 'Volatility',
58
+ abbreviation: 'VOL',
59
+ purpose: 'Energy release potential for propulsion and force',
60
+ },
61
+ {
62
+ key: 'reactivity',
63
+ label: 'Reactivity',
64
+ abbreviation: 'REA',
65
+ purpose: 'Chemical interaction speed for processing and penetration',
66
+ },
67
+ {
68
+ key: 'thermal',
69
+ label: 'Thermal',
70
+ abbreviation: 'THM',
71
+ purpose: 'Heat capacity for thermal management',
72
+ },
73
+ ]
74
+
75
+ const MINERAL_STATS: StatDefinition[] = [
76
+ {
77
+ key: 'resonance',
78
+ label: 'Resonance',
79
+ abbreviation: 'RES',
80
+ purpose: 'Energy field interaction — storage, focusing, projection',
81
+ },
82
+ {
83
+ key: 'hardness',
84
+ label: 'Hardness',
85
+ abbreviation: 'HRD',
86
+ purpose: 'Resistance to wear — cutting surfaces, penetration',
87
+ },
88
+ {
89
+ key: 'clarity',
90
+ label: 'Clarity',
91
+ abbreviation: 'CLR',
92
+ purpose: 'Crystalline perfection — precision optics',
93
+ },
94
+ ]
95
+
96
+ const ORGANIC_STATS: StatDefinition[] = [
97
+ {
98
+ key: 'plasticity',
99
+ label: 'Plasticity',
100
+ abbreviation: 'PLA',
101
+ purpose: 'Ease of reshaping — speeds processing',
102
+ },
103
+ {
104
+ key: 'insulation',
105
+ label: 'Insulation',
106
+ abbreviation: 'INS',
107
+ purpose: 'Energy containment — reduces energy loss',
108
+ },
109
+ {
110
+ key: 'purity',
111
+ label: 'Purity',
112
+ abbreviation: 'PUR',
113
+ purpose: 'Biological cleanliness — better composites and lubricants',
114
+ },
115
+ ]
116
+
117
+ const STAT_MAP: Record<ResourceCategory, StatDefinition[]> = {
118
+ metal: METAL_STATS,
119
+ precious: PRECIOUS_STATS,
120
+ gas: GAS_STATS,
121
+ mineral: MINERAL_STATS,
122
+ organic: ORGANIC_STATS,
123
+ }
124
+
125
+ export function getStatDefinitions(category: ResourceCategory): StatDefinition[] {
126
+ return STAT_MAP[category]
127
+ }
128
+
129
+ export function getStatName(category: ResourceCategory, index: 0 | 1 | 2): StatDefinition {
130
+ return STAT_MAP[category][index]
131
+ }
132
+
133
+ export interface NamedStats {
134
+ definitions: StatDefinition[]
135
+ values: [number, number, number]
136
+ }
137
+
138
+ export function resolveStats(
139
+ category: ResourceCategory,
140
+ stats: {stat1: number; stat2: number; stat3: number}
141
+ ): NamedStats {
142
+ return {
143
+ definitions: STAT_MAP[category],
144
+ values: [stats.stat1, stats.stat2, stats.stat3],
145
+ }
146
+ }