@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.
- package/lib/shipload.d.ts +411 -1025
- package/lib/shipload.js +879 -2057
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +852 -2028
- package/lib/shipload.m.js.map +1 -1
- package/package.json +1 -1
- package/src/capabilities/crafting.ts +10 -0
- package/src/capabilities/guards.ts +0 -5
- package/src/capabilities/index.ts +1 -0
- package/src/capabilities/storage.ts +0 -8
- package/src/contracts/server.ts +106 -225
- package/src/data/items.json +15 -14
- package/src/data/recipes.ts +129 -0
- package/src/derivation/crafting.ts +120 -0
- package/src/derivation/index.ts +9 -6
- package/src/derivation/resources.ts +54 -53
- package/src/derivation/stats.ts +146 -0
- package/src/derivation/stratum.ts +14 -14
- package/src/entities/cargo-utils.ts +6 -64
- package/src/entities/container.ts +18 -0
- package/src/entities/entity-inventory.ts +0 -4
- package/src/entities/inventory-accessor.ts +0 -4
- package/src/entities/location.ts +2 -197
- package/src/entities/player.ts +1 -274
- package/src/entities/ship.ts +0 -21
- package/src/entities/warehouse.ts +0 -4
- package/src/index-module.ts +43 -47
- package/src/managers/actions.ts +38 -90
- package/src/managers/context.ts +0 -9
- package/src/managers/index.ts +0 -1
- package/src/managers/locations.ts +2 -85
- package/src/market/items.ts +1 -2
- package/src/scheduling/projection.ts +0 -10
- package/src/shipload.ts +0 -5
- package/src/types/capabilities.ts +1 -9
- package/src/types/entity-traits.ts +3 -4
- package/src/types/entity.ts +0 -1
- package/src/types.ts +8 -28
- package/src/utils/system.ts +5 -4
- package/src/managers/trades.ts +0 -119
- package/src/market/market.ts +0 -208
- package/src/market/rolls.ts +0 -8
- package/src/trading/collect.ts +0 -938
- package/src/trading/deal.ts +0 -207
- package/src/trading/trade.ts +0 -203
package/src/data/items.json
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
[
|
|
2
|
-
{"id":
|
|
3
|
-
{"id":
|
|
4
|
-
{"id":
|
|
5
|
-
{"id":
|
|
6
|
-
{"id":
|
|
7
|
-
{"id":
|
|
8
|
-
{"id":
|
|
9
|
-
{"id":
|
|
10
|
-
{"id":
|
|
11
|
-
{"id":
|
|
12
|
-
{"id": 1000, "name": "Quartz", "description": "A crystalline mineral for sensors and optics.", "
|
|
13
|
-
{"id": 1001, "name": "Sapphire", "description": "A precious crystal for precision instruments.", "
|
|
14
|
-
{"id":
|
|
15
|
-
{"id": 1003, "name": "Biomass", "description": "Organic matter for life support systems.", "
|
|
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
|
+
}
|
package/src/derivation/index.ts
CHANGED
|
@@ -6,13 +6,13 @@ export {
|
|
|
6
6
|
getResourceWeight,
|
|
7
7
|
getLocationCandidates,
|
|
8
8
|
getDepthThreshold,
|
|
9
|
-
|
|
9
|
+
getResourceTier,
|
|
10
10
|
depthScaleFactor,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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 {
|
|
1
|
+
import {ResourceTier} from '../types'
|
|
2
2
|
|
|
3
|
-
export const
|
|
4
|
-
export const
|
|
5
|
-
export const
|
|
6
|
-
export const
|
|
7
|
-
export const
|
|
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
|
-
|
|
23
|
+
tier: ResourceTier
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
const RESOURCE_CATALOG: ResourceEntry[] = [
|
|
27
|
-
{id: 26,
|
|
28
|
-
{id:
|
|
29
|
-
{id:
|
|
30
|
-
{id:
|
|
31
|
-
{id:
|
|
32
|
-
{id:
|
|
33
|
-
{id:
|
|
34
|
-
{id:
|
|
35
|
-
{id:
|
|
36
|
-
{id:
|
|
37
|
-
{id:
|
|
38
|
-
{id:
|
|
39
|
-
{id:
|
|
40
|
-
{id:
|
|
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(
|
|
44
|
-
switch (
|
|
45
|
-
case '
|
|
46
|
-
return
|
|
47
|
-
case '
|
|
48
|
-
return
|
|
49
|
-
case '
|
|
50
|
-
return
|
|
51
|
-
case '
|
|
52
|
-
return
|
|
53
|
-
case '
|
|
54
|
-
return
|
|
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
|
|
59
|
+
export function getResourceTier(itemId: number): ResourceTier {
|
|
59
60
|
const entry = RESOURCE_CATALOG.find((r) => r.id === itemId)
|
|
60
|
-
return entry ? entry.
|
|
61
|
+
return entry ? entry.tier : 't5'
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
export function getResourceWeight(itemId: number, stratum: number): number {
|
|
64
|
-
const
|
|
65
|
-
const threshold = getDepthThreshold(
|
|
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 (
|
|
71
|
-
case '
|
|
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 '
|
|
77
|
+
case 't2':
|
|
77
78
|
if (depthAbove < 3000) return 40
|
|
78
79
|
if (depthAbove < 8000) return 60
|
|
79
80
|
return 50
|
|
80
|
-
case '
|
|
81
|
+
case 't3':
|
|
81
82
|
if (depthAbove < 5000) return 20
|
|
82
83
|
if (depthAbove < 15000) return 35
|
|
83
84
|
return 40
|
|
84
|
-
case '
|
|
85
|
+
case 't4':
|
|
85
86
|
if (depthAbove < 10000) return 10
|
|
86
87
|
if (depthAbove < 25000) return 20
|
|
87
88
|
return 30
|
|
88
|
-
case '
|
|
89
|
+
case 't5':
|
|
89
90
|
return 10
|
|
90
91
|
}
|
|
91
92
|
}
|
|
92
93
|
|
|
93
|
-
const ASTEROID_RESOURCES = [26,
|
|
94
|
-
const NEBULA_RESOURCES = [1, 2, 18
|
|
95
|
-
const GAS_GIANT_RESOURCES = [1, 2, 18,
|
|
96
|
-
const ROCKY_RESOURCES = [26,
|
|
97
|
-
const TERRESTRIAL_RESOURCES = [6, 1003, 1002
|
|
98
|
-
const ICY_RESOURCES = [
|
|
99
|
-
const OCEAN_RESOURCES = [1,
|
|
100
|
-
const INDUSTRIAL_RESOURCES = [26,
|
|
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
|
|
132
|
-
const threshold = getDepthThreshold(
|
|
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
|
+
}
|