@shipload/sdk 2.0.0-rc5 → 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 +376 -1008
- package/lib/shipload.js +712 -1948
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +694 -1924
- 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 +103 -220
- package/src/data/items.json +15 -15
- package/src/data/recipes.ts +129 -0
- package/src/derivation/crafting.ts +120 -0
- package/src/derivation/index.ts +1 -0
- package/src/derivation/stats.ts +91 -15
- package/src/derivation/stratum.ts +2 -2
- 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 +34 -41
- 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 +0 -1
- 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 +5 -25
- package/src/utils/system.ts +5 -4
- package/src/managers/trades.ts +0 -119
- package/src/market/market.ts +0 -195
- 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,17 +1,17 @@
|
|
|
1
1
|
[
|
|
2
|
-
{"id": 26, "name": "Iron", "description": "A versatile metal used in hulls and structures.", "
|
|
3
|
-
{"id": 13, "name": "Aluminum", "description": "A lightweight metal for structural components.", "
|
|
4
|
-
{"id": 24, "name": "Chromium", "description": "A hard, corrosion-resistant alloy metal.", "
|
|
5
|
-
{"id": 29, "name": "Copper", "description": "A conductive metal for electronics and wiring.", "
|
|
6
|
-
{"id": 47, "name": "Silver", "description": "A high-conductivity metal for precision components.", "
|
|
7
|
-
{"id": 79, "name": "Gold", "description": "A corrosion-proof metal for advanced electronics.", "
|
|
8
|
-
{"id": 1, "name": "Hydrogen", "description": "A lightweight gas used for fuel cells and propulsion.", "
|
|
9
|
-
{"id": 2, "name": "Helium", "description": "An inert noble gas used in energy systems.", "
|
|
10
|
-
{"id": 18, "name": "Argon", "description": "A noble gas used in industrial and energy applications.", "
|
|
11
|
-
{"id": 14, "name": "Silicon", "description": "A semiconductor used in sensors and computing.", "
|
|
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": 6, "name": "Carbon", "description": "A versatile element for life support and coatings.", "
|
|
15
|
-
{"id": 1003, "name": "Biomass", "description": "Organic matter for life support systems.", "
|
|
16
|
-
{"id": 1002, "name": "Resin", "description": "A fossilized organic compound for coatings and sealants.", "
|
|
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"}
|
|
17
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
package/src/derivation/stats.ts
CHANGED
|
@@ -9,33 +9,109 @@ export interface StatDefinition {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
const METAL_STATS: StatDefinition[] = [
|
|
12
|
-
{
|
|
13
|
-
|
|
14
|
-
|
|
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
|
+
},
|
|
15
31
|
]
|
|
16
32
|
|
|
17
33
|
const PRECIOUS_STATS: StatDefinition[] = [
|
|
18
|
-
{
|
|
19
|
-
|
|
20
|
-
|
|
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
|
+
},
|
|
21
52
|
]
|
|
22
53
|
|
|
23
54
|
const GAS_STATS: StatDefinition[] = [
|
|
24
|
-
{
|
|
25
|
-
|
|
26
|
-
|
|
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
|
+
},
|
|
27
73
|
]
|
|
28
74
|
|
|
29
75
|
const MINERAL_STATS: StatDefinition[] = [
|
|
30
|
-
{
|
|
31
|
-
|
|
32
|
-
|
|
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
|
+
},
|
|
33
94
|
]
|
|
34
95
|
|
|
35
96
|
const ORGANIC_STATS: StatDefinition[] = [
|
|
36
|
-
{
|
|
37
|
-
|
|
38
|
-
|
|
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
|
+
},
|
|
39
115
|
]
|
|
40
116
|
|
|
41
117
|
const STAT_MAP: Record<ResourceCategory, StatDefinition[]> = {
|
|
@@ -85,7 +85,7 @@ export function deriveStratum(
|
|
|
85
85
|
|
|
86
86
|
let depthBonus = 0
|
|
87
87
|
if (stratum > 1) {
|
|
88
|
-
depthBonus = 50 * Math.log(stratum) / Math.log(65535)
|
|
88
|
+
depthBonus = (50 * Math.log(stratum)) / Math.log(65535)
|
|
89
89
|
}
|
|
90
90
|
const richness = Math.min(Math.floor(baseRichness + depthBonus), 1000)
|
|
91
91
|
|
|
@@ -95,7 +95,7 @@ export function deriveStratum(
|
|
|
95
95
|
export function deriveResourceStats(seed: bigint): ResourceStats {
|
|
96
96
|
const seedBytes = new Uint8Array(8)
|
|
97
97
|
for (let i = 7; i >= 0; i--) {
|
|
98
|
-
seedBytes[i] = Number(seed &
|
|
98
|
+
seedBytes[i] = Number(seed & 0xffn)
|
|
99
99
|
seed >>= 8n
|
|
100
100
|
}
|
|
101
101
|
const hashResult = Checksum256.hash(Bytes.from(seedBytes))
|
|
@@ -12,12 +12,6 @@ export function totalCargoMass(cargo: EntityInventory[]): UInt64 {
|
|
|
12
12
|
}, UInt64.from(0))
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export function cargoValue(cargo: EntityInventory[]): UInt64 {
|
|
16
|
-
return cargo.reduce((sum, c) => {
|
|
17
|
-
return sum.adding(c.totalCost)
|
|
18
|
-
}, UInt64.from(0))
|
|
19
|
-
}
|
|
20
|
-
|
|
21
15
|
export function getCargoForItem(
|
|
22
16
|
cargo: EntityInventory[],
|
|
23
17
|
goodId: UInt64Type
|
|
@@ -47,84 +41,33 @@ export function isFull(currentMass: UInt64, maxCapacity: UInt64): boolean {
|
|
|
47
41
|
return currentMass.gte(maxCapacity)
|
|
48
42
|
}
|
|
49
43
|
|
|
50
|
-
export
|
|
51
|
-
revenue: UInt64
|
|
52
|
-
profit: UInt64
|
|
53
|
-
cost: UInt64
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function calculateSaleValue(
|
|
57
|
-
cargo: ServerContract.Types.cargo_item[],
|
|
58
|
-
prices: Map<number, UInt64>
|
|
59
|
-
): SaleValue {
|
|
60
|
-
if (cargo.length === 0) {
|
|
61
|
-
return {revenue: UInt64.from(0), profit: UInt64.from(0), cost: UInt64.from(0)}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
let revenue = UInt64.from(0)
|
|
65
|
-
let cost = UInt64.from(0)
|
|
66
|
-
|
|
67
|
-
for (const item of cargo) {
|
|
68
|
-
if (UInt32.from(item.quantity).equals(UInt32.from(0))) continue
|
|
69
|
-
|
|
70
|
-
const goodId = Number(item.item_id)
|
|
71
|
-
const salePrice = prices.get(goodId)
|
|
72
|
-
|
|
73
|
-
if (salePrice) {
|
|
74
|
-
revenue = revenue.adding(salePrice.multiplying(item.quantity))
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
cost = cost.adding(item.unit_cost.multiplying(item.quantity))
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const profit = revenue.gte(cost) ? revenue.subtracting(cost) : UInt64.from(0)
|
|
81
|
-
|
|
82
|
-
return {
|
|
83
|
-
revenue,
|
|
84
|
-
profit,
|
|
85
|
-
cost,
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function calculateSaleValueFromArray(
|
|
90
|
-
cargo: ServerContract.Types.cargo_item[],
|
|
91
|
-
prices: UInt64[]
|
|
92
|
-
): SaleValue {
|
|
93
|
-
const priceMap = new Map<number, UInt64>()
|
|
94
|
-
prices.forEach((price, index) => {
|
|
95
|
-
priceMap.set(index, price)
|
|
96
|
-
})
|
|
97
|
-
return calculateSaleValue(cargo, priceMap)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function afterSellItems(
|
|
44
|
+
export function afterRemoveItems(
|
|
101
45
|
cargo: ServerContract.Types.cargo_item[],
|
|
102
|
-
|
|
46
|
+
goodsToRemove: Array<{goodId: number; quantity: number}>
|
|
103
47
|
): EntityInventory[] {
|
|
104
48
|
if (cargo.length === 0) {
|
|
105
49
|
return []
|
|
106
50
|
}
|
|
107
51
|
|
|
108
52
|
return cargo.map((item) => {
|
|
109
|
-
const
|
|
110
|
-
if (!
|
|
53
|
+
const removeItem = goodsToRemove.find((s) => Number(item.item_id) === s.goodId)
|
|
54
|
+
if (!removeItem) {
|
|
111
55
|
return new EntityInventory(item)
|
|
112
56
|
}
|
|
113
57
|
|
|
114
58
|
const currentQty = Number(item.quantity)
|
|
115
|
-
const newQty = Math.max(0, currentQty -
|
|
59
|
+
const newQty = Math.max(0, currentQty - removeItem.quantity)
|
|
116
60
|
|
|
117
61
|
return new EntityInventory(
|
|
118
62
|
ServerContract.Types.cargo_item.from({
|
|
119
63
|
item_id: item.item_id,
|
|
120
64
|
quantity: UInt32.from(newQty),
|
|
121
|
-
unit_cost: item.unit_cost,
|
|
122
65
|
})
|
|
123
66
|
)
|
|
124
67
|
})
|
|
125
68
|
}
|
|
126
69
|
|
|
127
|
-
export function
|
|
70
|
+
export function afterRemoveAllItems(cargo: ServerContract.Types.cargo_item[]): EntityInventory[] {
|
|
128
71
|
if (cargo.length === 0) {
|
|
129
72
|
return []
|
|
130
73
|
}
|
|
@@ -135,7 +78,6 @@ export function afterSellAllItems(cargo: ServerContract.Types.cargo_item[]): Ent
|
|
|
135
78
|
ServerContract.Types.cargo_item.from({
|
|
136
79
|
item_id: item.item_id,
|
|
137
80
|
quantity: UInt32.from(0),
|
|
138
|
-
unit_cost: item.unit_cost,
|
|
139
81
|
})
|
|
140
82
|
)
|
|
141
83
|
)
|
|
@@ -68,3 +68,21 @@ export class Container extends ServerContract.Types.entity_info {
|
|
|
68
68
|
return this.coordinates.z?.toNumber() || 0
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
+
|
|
72
|
+
export function computeContainerCapabilities(stats: Record<string, number>): {
|
|
73
|
+
hullmass: number
|
|
74
|
+
capacity: number
|
|
75
|
+
} {
|
|
76
|
+
const density = stats['density'] ?? 500
|
|
77
|
+
const strength = stats['strength'] ?? 500
|
|
78
|
+
const ductility = stats['ductility'] ?? 500
|
|
79
|
+
const purity = stats['purity'] ?? 500
|
|
80
|
+
|
|
81
|
+
const hullmass = 25000 + 75 * density
|
|
82
|
+
|
|
83
|
+
const statSum = strength + ductility + purity
|
|
84
|
+
const exponent = statSum / 2997
|
|
85
|
+
const capacity = Math.floor(1000000 * Math.pow(10, exponent))
|
|
86
|
+
|
|
87
|
+
return {hullmass, capacity}
|
|
88
|
+
}
|
|
@@ -29,10 +29,6 @@ export class EntityInventory extends ServerContract.Types.cargo_item {
|
|
|
29
29
|
return UInt64.from(this.unitMass).multiplying(this.quantity)
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
get totalCost(): UInt64 {
|
|
33
|
-
return this.unit_cost.multiplying(this.quantity)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
32
|
get hasCargo(): boolean {
|
|
37
33
|
return UInt32.from(this.quantity).gt(UInt32.from(0))
|
|
38
34
|
}
|
|
@@ -20,10 +20,6 @@ export class InventoryAccessor {
|
|
|
20
20
|
return this.items.reduce((sum, c) => sum.adding(c.totalMass), UInt64.from(0))
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
get totalValue(): UInt64 {
|
|
24
|
-
return this.items.reduce((sum, c) => sum.adding(c.totalCost), UInt64.from(0))
|
|
25
|
-
}
|
|
26
|
-
|
|
27
23
|
forItem(goodId: UInt64Type): EntityInventory | undefined {
|
|
28
24
|
return this.items.find((c) => c.item_id.equals(goodId))
|
|
29
25
|
}
|