@shipload/sdk 1.0.0-next.3 → 1.0.0-next.5
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 +203 -97
- package/lib/shipload.js +762 -419
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +752 -418
- package/lib/shipload.m.js.map +1 -1
- package/package.json +1 -1
- package/src/contracts/server.ts +54 -1
- package/src/data/capabilities.ts +5 -330
- package/src/data/capability-formulas.ts +68 -0
- package/src/data/colors.ts +1 -0
- package/src/data/items.json +245 -0
- package/src/data/metadata.ts +44 -1
- package/src/data/recipes.json +6 -16
- package/src/derivation/capability-mappings.ts +120 -0
- package/src/derivation/resources.ts +27 -19
- package/src/entities/container.ts +23 -8
- package/src/entities/ship-deploy.ts +78 -41
- package/src/entities/ship.ts +17 -0
- package/src/entities/warehouse.ts +8 -0
- package/src/index-module.ts +32 -18
- package/src/managers/actions.ts +24 -1
- package/src/nft/description.ts +25 -6
- package/src/resolution/resolve-item.ts +14 -9
- package/src/scheduling/accessor.ts +4 -0
- package/src/scheduling/projection.ts +8 -0
- package/src/scheduling/schedule.ts +15 -1
- package/src/subscriptions/manager.ts +37 -1
- package/src/travel/travel.ts +58 -1
- package/src/types.ts +3 -0
package/src/data/metadata.ts
CHANGED
|
@@ -11,7 +11,7 @@ export interface EntityMetadata {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export const itemMetadata: Record<number, ItemMetadata> = {
|
|
14
|
-
// === Resources
|
|
14
|
+
// === Resources / Ore ===
|
|
15
15
|
101: {name: 'Ore', description: 'Crude metallic ore.', color: '#C26D3F'},
|
|
16
16
|
102: {name: 'Ore', description: 'Refined metallic ore with improved purity.', color: '#C26D3F'},
|
|
17
17
|
103: {
|
|
@@ -19,6 +19,15 @@ export const itemMetadata: Record<number, ItemMetadata> = {
|
|
|
19
19
|
description: 'High-grade metallic ore with exceptional density.',
|
|
20
20
|
color: '#C26D3F',
|
|
21
21
|
},
|
|
22
|
+
104: {name: 'Ore', description: '', color: '#C26D3F'},
|
|
23
|
+
105: {name: 'Ore', description: '', color: '#C26D3F'},
|
|
24
|
+
106: {name: 'Ore', description: '', color: '#C26D3F'},
|
|
25
|
+
107: {name: 'Ore', description: '', color: '#C26D3F'},
|
|
26
|
+
108: {name: 'Ore', description: '', color: '#C26D3F'},
|
|
27
|
+
109: {name: 'Ore', description: '', color: '#C26D3F'},
|
|
28
|
+
110: {name: 'Ore', description: '', color: '#C26D3F'},
|
|
29
|
+
|
|
30
|
+
// === Resources / Crystal ===
|
|
22
31
|
201: {name: 'Crystal', description: 'Raw resonant crystal.', color: '#4ADBFF'},
|
|
23
32
|
202: {
|
|
24
33
|
name: 'Crystal',
|
|
@@ -30,6 +39,15 @@ export const itemMetadata: Record<number, ItemMetadata> = {
|
|
|
30
39
|
description: 'High-grade resonant crystal with exceptional purity.',
|
|
31
40
|
color: '#4ADBFF',
|
|
32
41
|
},
|
|
42
|
+
204: {name: 'Crystal', description: '', color: '#4ADBFF'},
|
|
43
|
+
205: {name: 'Crystal', description: '', color: '#4ADBFF'},
|
|
44
|
+
206: {name: 'Crystal', description: '', color: '#4ADBFF'},
|
|
45
|
+
207: {name: 'Crystal', description: '', color: '#4ADBFF'},
|
|
46
|
+
208: {name: 'Crystal', description: '', color: '#4ADBFF'},
|
|
47
|
+
209: {name: 'Crystal', description: '', color: '#4ADBFF'},
|
|
48
|
+
210: {name: 'Crystal', description: '', color: '#4ADBFF'},
|
|
49
|
+
|
|
50
|
+
// === Resources / Gas ===
|
|
33
51
|
301: {name: 'Gas', description: 'Raw volatile gas.', color: '#B8E4A0'},
|
|
34
52
|
302: {
|
|
35
53
|
name: 'Gas',
|
|
@@ -41,6 +59,15 @@ export const itemMetadata: Record<number, ItemMetadata> = {
|
|
|
41
59
|
description: 'High-grade volatile gas with exceptional energy density.',
|
|
42
60
|
color: '#B8E4A0',
|
|
43
61
|
},
|
|
62
|
+
304: {name: 'Gas', description: '', color: '#B8E4A0'},
|
|
63
|
+
305: {name: 'Gas', description: '', color: '#B8E4A0'},
|
|
64
|
+
306: {name: 'Gas', description: '', color: '#B8E4A0'},
|
|
65
|
+
307: {name: 'Gas', description: '', color: '#B8E4A0'},
|
|
66
|
+
308: {name: 'Gas', description: '', color: '#B8E4A0'},
|
|
67
|
+
309: {name: 'Gas', description: '', color: '#B8E4A0'},
|
|
68
|
+
310: {name: 'Gas', description: '', color: '#B8E4A0'},
|
|
69
|
+
|
|
70
|
+
// === Resources / Regolith ===
|
|
44
71
|
401: {name: 'Regolith', description: 'Crude regolith dust.', color: '#C4A57B'},
|
|
45
72
|
402: {
|
|
46
73
|
name: 'Regolith',
|
|
@@ -52,6 +79,15 @@ export const itemMetadata: Record<number, ItemMetadata> = {
|
|
|
52
79
|
description: 'High-grade regolith with exceptional uniformity.',
|
|
53
80
|
color: '#C4A57B',
|
|
54
81
|
},
|
|
82
|
+
404: {name: 'Regolith', description: '', color: '#C4A57B'},
|
|
83
|
+
405: {name: 'Regolith', description: '', color: '#C4A57B'},
|
|
84
|
+
406: {name: 'Regolith', description: '', color: '#C4A57B'},
|
|
85
|
+
407: {name: 'Regolith', description: '', color: '#C4A57B'},
|
|
86
|
+
408: {name: 'Regolith', description: '', color: '#C4A57B'},
|
|
87
|
+
409: {name: 'Regolith', description: '', color: '#C4A57B'},
|
|
88
|
+
410: {name: 'Regolith', description: '', color: '#C4A57B'},
|
|
89
|
+
|
|
90
|
+
// === Resources / Biomass ===
|
|
55
91
|
501: {name: 'Biomass', description: 'Crude organic biomass.', color: '#5A8B3E'},
|
|
56
92
|
502: {
|
|
57
93
|
name: 'Biomass',
|
|
@@ -63,6 +99,13 @@ export const itemMetadata: Record<number, ItemMetadata> = {
|
|
|
63
99
|
description: 'High-grade biomass with exceptional saturation.',
|
|
64
100
|
color: '#5A8B3E',
|
|
65
101
|
},
|
|
102
|
+
504: {name: 'Biomass', description: '', color: '#5A8B3E'},
|
|
103
|
+
505: {name: 'Biomass', description: '', color: '#5A8B3E'},
|
|
104
|
+
506: {name: 'Biomass', description: '', color: '#5A8B3E'},
|
|
105
|
+
507: {name: 'Biomass', description: '', color: '#5A8B3E'},
|
|
106
|
+
508: {name: 'Biomass', description: '', color: '#5A8B3E'},
|
|
107
|
+
509: {name: 'Biomass', description: '', color: '#5A8B3E'},
|
|
108
|
+
510: {name: 'Biomass', description: '', color: '#5A8B3E'},
|
|
66
109
|
|
|
67
110
|
// === Components (T1) ===
|
|
68
111
|
10001: {
|
package/src/data/recipes.json
CHANGED
|
@@ -393,12 +393,7 @@
|
|
|
393
393
|
]
|
|
394
394
|
},
|
|
395
395
|
{
|
|
396
|
-
"sources": [
|
|
397
|
-
{
|
|
398
|
-
"inputIndex": 1,
|
|
399
|
-
"statIndex": 1
|
|
400
|
-
}
|
|
401
|
-
]
|
|
396
|
+
"sources": []
|
|
402
397
|
},
|
|
403
398
|
{
|
|
404
399
|
"sources": [
|
|
@@ -436,8 +431,8 @@
|
|
|
436
431
|
{
|
|
437
432
|
"sources": [
|
|
438
433
|
{
|
|
439
|
-
"inputIndex":
|
|
440
|
-
"statIndex":
|
|
434
|
+
"inputIndex": 1,
|
|
435
|
+
"statIndex": 1
|
|
441
436
|
}
|
|
442
437
|
]
|
|
443
438
|
},
|
|
@@ -552,7 +547,7 @@
|
|
|
552
547
|
"sources": [
|
|
553
548
|
{
|
|
554
549
|
"inputIndex": 0,
|
|
555
|
-
"statIndex":
|
|
550
|
+
"statIndex": 1
|
|
556
551
|
}
|
|
557
552
|
]
|
|
558
553
|
},
|
|
@@ -568,17 +563,12 @@
|
|
|
568
563
|
"sources": [
|
|
569
564
|
{
|
|
570
565
|
"inputIndex": 0,
|
|
571
|
-
"statIndex":
|
|
566
|
+
"statIndex": 0
|
|
572
567
|
}
|
|
573
568
|
]
|
|
574
569
|
},
|
|
575
570
|
{
|
|
576
|
-
"sources": [
|
|
577
|
-
{
|
|
578
|
-
"inputIndex": 1,
|
|
579
|
-
"statIndex": 1
|
|
580
|
-
}
|
|
581
|
-
]
|
|
571
|
+
"sources": []
|
|
582
572
|
}
|
|
583
573
|
],
|
|
584
574
|
"blendWeights": []
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import {SLOT_FORMULAS, type SlotConsumerKind} from '../data/capability-formulas'
|
|
2
|
+
import {getStatDefinitions, type StatDefinition} from './stats'
|
|
3
|
+
import {
|
|
4
|
+
getRecipe,
|
|
5
|
+
type Recipe,
|
|
6
|
+
type RecipeInput,
|
|
7
|
+
type RecipeInputCategory,
|
|
8
|
+
} from '../data/recipes-runtime'
|
|
9
|
+
import {
|
|
10
|
+
ITEM_ENGINE_T1,
|
|
11
|
+
ITEM_GENERATOR_T1,
|
|
12
|
+
ITEM_GATHERER_T1,
|
|
13
|
+
ITEM_LOADER_T1,
|
|
14
|
+
ITEM_CRAFTER_T1,
|
|
15
|
+
ITEM_STORAGE_T1,
|
|
16
|
+
ITEM_HAULER_T1,
|
|
17
|
+
ITEM_WARP_T1,
|
|
18
|
+
ITEM_SHIP_T1_PACKED,
|
|
19
|
+
ITEM_CONTAINER_T1_PACKED,
|
|
20
|
+
ITEM_WAREHOUSE_T1_PACKED,
|
|
21
|
+
ITEM_CONTAINER_T2_PACKED,
|
|
22
|
+
} from '../data/item-ids'
|
|
23
|
+
import type {StatMapping} from '../data/capabilities'
|
|
24
|
+
|
|
25
|
+
export const KIND_TO_ITEM_ID: Record<SlotConsumerKind, number> = {
|
|
26
|
+
engine: ITEM_ENGINE_T1,
|
|
27
|
+
generator: ITEM_GENERATOR_T1,
|
|
28
|
+
gatherer: ITEM_GATHERER_T1,
|
|
29
|
+
loader: ITEM_LOADER_T1,
|
|
30
|
+
crafter: ITEM_CRAFTER_T1,
|
|
31
|
+
storage: ITEM_STORAGE_T1,
|
|
32
|
+
hauler: ITEM_HAULER_T1,
|
|
33
|
+
warp: ITEM_WARP_T1,
|
|
34
|
+
'ship-t1': ITEM_SHIP_T1_PACKED,
|
|
35
|
+
'container-t1': ITEM_CONTAINER_T1_PACKED,
|
|
36
|
+
'warehouse-t1': ITEM_WAREHOUSE_T1_PACKED,
|
|
37
|
+
'container-t2': ITEM_CONTAINER_T2_PACKED,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function isCategoryInput(input: RecipeInput): input is RecipeInputCategory {
|
|
41
|
+
return 'category' in input
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Walk a recipe's slot source down to the raw category stat that ultimately
|
|
46
|
+
* lands in that slot. Returns the StatDefinition or undefined if the trace
|
|
47
|
+
* dead-ends (unknown sub-component, missing slot, etc.).
|
|
48
|
+
*
|
|
49
|
+
* Multi-source sub-slots collapse to `sources[0]`; top-level multi-source slots
|
|
50
|
+
* are expanded by the caller (`deriveStatMappings`).
|
|
51
|
+
*/
|
|
52
|
+
function traceToRawCategoryStat(
|
|
53
|
+
recipe: Recipe,
|
|
54
|
+
source: {inputIndex: number; statIndex: number},
|
|
55
|
+
visited: Set<number> = new Set()
|
|
56
|
+
): StatDefinition | undefined {
|
|
57
|
+
const input = recipe.inputs[source.inputIndex]
|
|
58
|
+
if (!input) return undefined
|
|
59
|
+
if (isCategoryInput(input)) {
|
|
60
|
+
const defs = getStatDefinitions(input.category)
|
|
61
|
+
return defs[source.statIndex]
|
|
62
|
+
}
|
|
63
|
+
if (visited.has(input.itemId)) return undefined
|
|
64
|
+
const subRecipe = getRecipe(input.itemId)
|
|
65
|
+
if (!subRecipe) return undefined
|
|
66
|
+
const subSlot = subRecipe.statSlots[source.statIndex]
|
|
67
|
+
if (!subSlot) return undefined
|
|
68
|
+
const subSource = subSlot.sources[0]
|
|
69
|
+
if (!subSource) return undefined
|
|
70
|
+
const nextVisited = new Set(visited)
|
|
71
|
+
nextVisited.add(input.itemId)
|
|
72
|
+
return traceToRawCategoryStat(subRecipe, subSource, nextVisited)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let cached: StatMapping[] | undefined
|
|
76
|
+
|
|
77
|
+
export function deriveStatMappings(): StatMapping[] {
|
|
78
|
+
if (cached) return cached
|
|
79
|
+
const out: StatMapping[] = []
|
|
80
|
+
const seen = new Set<string>()
|
|
81
|
+
for (const [kind, slots] of Object.entries(SLOT_FORMULAS) as [
|
|
82
|
+
SlotConsumerKind,
|
|
83
|
+
Record<number, {capability: string; attribute: string}>,
|
|
84
|
+
][]) {
|
|
85
|
+
const itemId = KIND_TO_ITEM_ID[kind]
|
|
86
|
+
const recipe = getRecipe(itemId)
|
|
87
|
+
if (!recipe) continue
|
|
88
|
+
for (const [slotIdxStr, consumer] of Object.entries(slots)) {
|
|
89
|
+
const slotIdx = Number(slotIdxStr)
|
|
90
|
+
const slot = recipe.statSlots[slotIdx]
|
|
91
|
+
if (!slot) continue
|
|
92
|
+
for (const source of slot.sources) {
|
|
93
|
+
const stat = traceToRawCategoryStat(recipe, source)
|
|
94
|
+
if (!stat) continue
|
|
95
|
+
const key = `${stat.label}|${consumer.capability}|${consumer.attribute}`
|
|
96
|
+
if (seen.has(key)) continue
|
|
97
|
+
seen.add(key)
|
|
98
|
+
out.push({
|
|
99
|
+
stat: stat.label,
|
|
100
|
+
capability: consumer.capability,
|
|
101
|
+
attribute: consumer.attribute,
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
cached = out
|
|
107
|
+
return out
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function getStatMappings(): StatMapping[] {
|
|
111
|
+
return deriveStatMappings()
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function getStatMappingsForStat(stat: string): StatMapping[] {
|
|
115
|
+
return deriveStatMappings().filter((m) => m.stat === stat)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function getStatMappingsForCapability(capability: string): StatMapping[] {
|
|
119
|
+
return deriveStatMappings().filter((m) => m.capability === capability)
|
|
120
|
+
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import {getItem} from '../data/catalog'
|
|
2
2
|
|
|
3
3
|
export const DEPTH_THRESHOLD_T1 = 0
|
|
4
|
-
export const DEPTH_THRESHOLD_T2 =
|
|
5
|
-
export const DEPTH_THRESHOLD_T3 =
|
|
6
|
-
export const DEPTH_THRESHOLD_T4 =
|
|
7
|
-
export const DEPTH_THRESHOLD_T5 =
|
|
4
|
+
export const DEPTH_THRESHOLD_T2 = 1500
|
|
5
|
+
export const DEPTH_THRESHOLD_T3 = 5000
|
|
6
|
+
export const DEPTH_THRESHOLD_T4 = 12000
|
|
7
|
+
export const DEPTH_THRESHOLD_T5 = 22000
|
|
8
|
+
export const DEPTH_THRESHOLD_T6 = 32000
|
|
9
|
+
export const DEPTH_THRESHOLD_T7 = 42000
|
|
10
|
+
export const DEPTH_THRESHOLD_T8 = 50000
|
|
11
|
+
export const DEPTH_THRESHOLD_T9 = 57000
|
|
12
|
+
export const DEPTH_THRESHOLD_T10 = 63000
|
|
8
13
|
|
|
9
14
|
export const LOCATION_MIN_DEPTH = 500
|
|
10
15
|
export const LOCATION_MAX_DEPTH = 65535
|
|
@@ -18,19 +23,22 @@ export const PLANET_SUBTYPE_ICY = 3
|
|
|
18
23
|
export const PLANET_SUBTYPE_OCEAN = 4
|
|
19
24
|
export const PLANET_SUBTYPE_INDUSTRIAL = 5
|
|
20
25
|
|
|
26
|
+
const DEPTH_THRESHOLD_TABLE = [
|
|
27
|
+
DEPTH_THRESHOLD_T1,
|
|
28
|
+
DEPTH_THRESHOLD_T2,
|
|
29
|
+
DEPTH_THRESHOLD_T3,
|
|
30
|
+
DEPTH_THRESHOLD_T4,
|
|
31
|
+
DEPTH_THRESHOLD_T5,
|
|
32
|
+
DEPTH_THRESHOLD_T6,
|
|
33
|
+
DEPTH_THRESHOLD_T7,
|
|
34
|
+
DEPTH_THRESHOLD_T8,
|
|
35
|
+
DEPTH_THRESHOLD_T9,
|
|
36
|
+
DEPTH_THRESHOLD_T10,
|
|
37
|
+
]
|
|
38
|
+
|
|
21
39
|
export function getDepthThreshold(tier: number): number {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return DEPTH_THRESHOLD_T1
|
|
25
|
-
case 2:
|
|
26
|
-
return DEPTH_THRESHOLD_T2
|
|
27
|
-
case 3:
|
|
28
|
-
return DEPTH_THRESHOLD_T3
|
|
29
|
-
case 4:
|
|
30
|
-
return DEPTH_THRESHOLD_T4
|
|
31
|
-
default:
|
|
32
|
-
return DEPTH_THRESHOLD_T5
|
|
33
|
-
}
|
|
40
|
+
if (tier < 1 || tier > 10) return 65535
|
|
41
|
+
return DEPTH_THRESHOLD_TABLE[tier - 1]
|
|
34
42
|
}
|
|
35
43
|
|
|
36
44
|
export function getResourceTier(itemId: number): number {
|
|
@@ -46,9 +54,9 @@ export function getResourceWeight(itemId: number, stratum: number): number {
|
|
|
46
54
|
|
|
47
55
|
switch (tier) {
|
|
48
56
|
case 1:
|
|
49
|
-
if (stratum <
|
|
50
|
-
if (stratum <
|
|
51
|
-
if (stratum <
|
|
57
|
+
if (stratum < DEPTH_THRESHOLD_T2) return 100
|
|
58
|
+
if (stratum < DEPTH_THRESHOLD_T3) return 80
|
|
59
|
+
if (stratum < DEPTH_THRESHOLD_T4) return 50
|
|
52
60
|
return 30
|
|
53
61
|
case 2:
|
|
54
62
|
if (depthAbove < 3000) return 40
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {UInt64, type UInt64Type} from '@wharfkit/antelope'
|
|
2
2
|
import {ServerContract} from '../contracts'
|
|
3
3
|
import type {CoordinatesType} from '../types'
|
|
4
|
+
import {type FloatPosition, getInterpolatedPosition} from '../travel/travel'
|
|
4
5
|
import {Location} from './location'
|
|
5
6
|
import {ScheduleAccessor} from '../scheduling/accessor'
|
|
6
7
|
import * as schedule from '../scheduling/schedule'
|
|
@@ -24,6 +25,14 @@ export class Container extends ServerContract.Types.entity_info {
|
|
|
24
25
|
return this.entity_name
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
get entityClass(): 'mobile' {
|
|
29
|
+
return 'mobile'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get canUndeploy(): boolean {
|
|
33
|
+
return true
|
|
34
|
+
}
|
|
35
|
+
|
|
27
36
|
get sched(): ScheduleAccessor {
|
|
28
37
|
this._sched ??= new ScheduleAccessor(this)
|
|
29
38
|
return this._sched
|
|
@@ -33,6 +42,12 @@ export class Container extends ServerContract.Types.entity_info {
|
|
|
33
42
|
return this.is_idle
|
|
34
43
|
}
|
|
35
44
|
|
|
45
|
+
interpolatedPositionAt(now: Date): FloatPosition {
|
|
46
|
+
const taskIndex = this.sched.currentTaskIndex(now)
|
|
47
|
+
const progress = this.sched.currentTaskProgressFloat(now)
|
|
48
|
+
return getInterpolatedPosition(this, taskIndex, progress)
|
|
49
|
+
}
|
|
50
|
+
|
|
36
51
|
isLoading(now: Date): boolean {
|
|
37
52
|
return schedule.isLoading(this, now)
|
|
38
53
|
}
|
|
@@ -75,10 +90,10 @@ export function computeContainerCapabilities(stats: Record<string, number>): {
|
|
|
75
90
|
hullmass: number
|
|
76
91
|
capacity: number
|
|
77
92
|
} {
|
|
78
|
-
const density = stats
|
|
79
|
-
const strength = stats
|
|
80
|
-
const hardness = stats
|
|
81
|
-
const saturation = stats
|
|
93
|
+
const density = stats.density
|
|
94
|
+
const strength = stats.strength
|
|
95
|
+
const hardness = stats.hardness
|
|
96
|
+
const saturation = stats.saturation
|
|
82
97
|
|
|
83
98
|
const hullmass = 25000 + 75 * density
|
|
84
99
|
|
|
@@ -93,10 +108,10 @@ export function computeContainerT2Capabilities(stats: Record<string, number>): {
|
|
|
93
108
|
hullmass: number
|
|
94
109
|
capacity: number
|
|
95
110
|
} {
|
|
96
|
-
const strength = stats
|
|
97
|
-
const density = stats
|
|
98
|
-
const hardness = stats
|
|
99
|
-
const saturation = stats
|
|
111
|
+
const strength = stats.strength
|
|
112
|
+
const density = stats.density
|
|
113
|
+
const hardness = stats.hardness
|
|
114
|
+
const saturation = stats.saturation
|
|
100
115
|
|
|
101
116
|
const hullmass = 20000 + 50 * density
|
|
102
117
|
|
|
@@ -8,15 +8,16 @@ import {
|
|
|
8
8
|
MODULE_HAULER,
|
|
9
9
|
MODULE_LOADER,
|
|
10
10
|
} from '../capabilities/modules'
|
|
11
|
+
import {getItem} from '../data/catalog'
|
|
11
12
|
|
|
12
13
|
export function computeShipHullCapabilities(stats: Record<string, number>): {
|
|
13
14
|
hullmass: number
|
|
14
15
|
capacity: number
|
|
15
16
|
} {
|
|
16
|
-
const density = stats.density
|
|
17
|
-
const strength = stats.strength
|
|
18
|
-
const hardness = stats.hardness
|
|
19
|
-
const saturation = stats.saturation
|
|
17
|
+
const density = stats.density
|
|
18
|
+
const strength = stats.strength
|
|
19
|
+
const hardness = stats.hardness
|
|
20
|
+
const saturation = stats.saturation
|
|
20
21
|
|
|
21
22
|
const hullmass = 25000 + 75 * density
|
|
22
23
|
const statSum = strength + hardness + saturation
|
|
@@ -30,8 +31,8 @@ export function computeEngineCapabilities(stats: Record<string, number>): {
|
|
|
30
31
|
thrust: number
|
|
31
32
|
drain: number
|
|
32
33
|
} {
|
|
33
|
-
const vol = stats.volatility
|
|
34
|
-
const thm = stats.thermal
|
|
34
|
+
const vol = stats.volatility
|
|
35
|
+
const thm = stats.thermal
|
|
35
36
|
|
|
36
37
|
return {
|
|
37
38
|
thrust: 400 + Math.floor((vol * 3) / 4),
|
|
@@ -43,30 +44,61 @@ export function computeGeneratorCapabilities(stats: Record<string, number>): {
|
|
|
43
44
|
capacity: number
|
|
44
45
|
recharge: number
|
|
45
46
|
} {
|
|
46
|
-
const
|
|
47
|
-
const
|
|
47
|
+
const com = stats.composition
|
|
48
|
+
const fin = stats.fineness
|
|
48
49
|
|
|
49
50
|
return {
|
|
50
|
-
capacity: 300 + Math.floor(
|
|
51
|
-
recharge: 1 + Math.floor((
|
|
51
|
+
capacity: 300 + Math.floor(com / 6),
|
|
52
|
+
recharge: 1 + Math.floor((fin * 3) / 1000),
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
export
|
|
56
|
+
export interface GathererDepthParams {
|
|
57
|
+
readonly floor: number
|
|
58
|
+
readonly slope: number
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const GATHERER_DEPTH_TABLE: readonly GathererDepthParams[] = [
|
|
62
|
+
{floor: 500, slope: 5},
|
|
63
|
+
{floor: 2000, slope: 11},
|
|
64
|
+
{floor: 7000, slope: 16},
|
|
65
|
+
{floor: 15000, slope: 18},
|
|
66
|
+
{floor: 25000, slope: 19},
|
|
67
|
+
{floor: 35000, slope: 16},
|
|
68
|
+
{floor: 46000, slope: 12},
|
|
69
|
+
{floor: 53500, slope: 10},
|
|
70
|
+
{floor: 60000, slope: 5},
|
|
71
|
+
{floor: 63500, slope: 2},
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
export const GATHERER_DEPTH_MAX_TIER = 10
|
|
75
|
+
|
|
76
|
+
export function gathererDepthForTier(tol: number, tier: number): number {
|
|
77
|
+
if (tier < 1 || tier > GATHERER_DEPTH_MAX_TIER) {
|
|
78
|
+
throw new Error(`gatherer tier out of range: ${tier}`)
|
|
79
|
+
}
|
|
80
|
+
const p = GATHERER_DEPTH_TABLE[tier - 1]
|
|
81
|
+
return p.floor + tol * p.slope
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function computeGathererCapabilities(
|
|
85
|
+
stats: Record<string, number>,
|
|
86
|
+
tier: number
|
|
87
|
+
): {
|
|
56
88
|
yield: number
|
|
57
89
|
drain: number
|
|
58
90
|
depth: number
|
|
59
91
|
speed: number
|
|
60
92
|
} {
|
|
61
|
-
const str = stats.strength
|
|
62
|
-
const con = stats.conductivity
|
|
63
|
-
const ref = stats.reflectivity
|
|
64
|
-
const tol = stats.tolerance
|
|
93
|
+
const str = stats.strength
|
|
94
|
+
const con = stats.conductivity
|
|
95
|
+
const ref = stats.reflectivity
|
|
96
|
+
const tol = stats.tolerance
|
|
65
97
|
|
|
66
98
|
return {
|
|
67
99
|
yield: 200 + str,
|
|
68
100
|
drain: Math.max(250, 1250 - Math.floor((con * 25) / 20)),
|
|
69
|
-
depth:
|
|
101
|
+
depth: gathererDepthForTier(tol, tier),
|
|
70
102
|
speed: 100 + Math.floor((ref * 4) / 5),
|
|
71
103
|
}
|
|
72
104
|
}
|
|
@@ -76,12 +108,12 @@ export function computeLoaderCapabilities(stats: Record<string, number>): {
|
|
|
76
108
|
thrust: number
|
|
77
109
|
quantity: number
|
|
78
110
|
} {
|
|
79
|
-
const
|
|
80
|
-
const
|
|
111
|
+
const insulation = stats.insulation
|
|
112
|
+
const plasticity = stats.plasticity
|
|
81
113
|
|
|
82
114
|
return {
|
|
83
|
-
mass: Math.max(200, 2000 - Math.floor(
|
|
84
|
-
thrust: 1 + Math.floor(
|
|
115
|
+
mass: Math.max(200, 2000 - Math.floor(insulation * 2)),
|
|
116
|
+
thrust: 1 + Math.floor(plasticity / 500),
|
|
85
117
|
quantity: 1,
|
|
86
118
|
}
|
|
87
119
|
}
|
|
@@ -90,12 +122,12 @@ export function computeCrafterCapabilities(stats: Record<string, number>): {
|
|
|
90
122
|
speed: number
|
|
91
123
|
drain: number
|
|
92
124
|
} {
|
|
93
|
-
const rea = stats.reactivity
|
|
94
|
-
const
|
|
125
|
+
const rea = stats.reactivity
|
|
126
|
+
const fin = stats.fineness
|
|
95
127
|
|
|
96
128
|
return {
|
|
97
129
|
speed: 100 + Math.floor((rea * 4) / 5),
|
|
98
|
-
drain: Math.max(5, 30 - Math.floor(
|
|
130
|
+
drain: Math.max(5, 30 - Math.floor(fin / 33)),
|
|
99
131
|
}
|
|
100
132
|
}
|
|
101
133
|
|
|
@@ -104,14 +136,14 @@ export function computeHaulerCapabilities(stats: Record<string, number>): {
|
|
|
104
136
|
efficiency: number
|
|
105
137
|
drain: number
|
|
106
138
|
} {
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
const
|
|
139
|
+
const fineness = stats.fineness
|
|
140
|
+
const conductivity = stats.conductivity
|
|
141
|
+
const composition = stats.composition
|
|
110
142
|
|
|
111
143
|
return {
|
|
112
|
-
capacity: Math.max(1, 1 + Math.floor(
|
|
113
|
-
efficiency: 2000 +
|
|
114
|
-
drain: Math.max(3, 15 - Math.floor(
|
|
144
|
+
capacity: Math.max(1, 1 + Math.floor(fineness / 400)),
|
|
145
|
+
efficiency: 2000 + conductivity * 6,
|
|
146
|
+
drain: Math.max(3, 15 - Math.floor(composition / 80)),
|
|
115
147
|
}
|
|
116
148
|
}
|
|
117
149
|
|
|
@@ -121,11 +153,12 @@ export function computeStorageCapabilities(
|
|
|
121
153
|
): {
|
|
122
154
|
capacityBonus: number
|
|
123
155
|
} {
|
|
124
|
-
const strength = stats.strength
|
|
125
|
-
const
|
|
126
|
-
const
|
|
156
|
+
const strength = stats.strength
|
|
157
|
+
const density = stats.density
|
|
158
|
+
const hardness = stats.hardness
|
|
159
|
+
const saturation = stats.saturation
|
|
127
160
|
|
|
128
|
-
const statSum = strength + hardness + saturation
|
|
161
|
+
const statSum = strength + density + hardness + saturation
|
|
129
162
|
const capacityBonus = Math.floor(
|
|
130
163
|
(baseCapacity * (10 + Math.floor((statSum * 10) / 2997))) / 100
|
|
131
164
|
)
|
|
@@ -137,10 +170,10 @@ export function computeWarehouseHullCapabilities(stats: Record<string, number>):
|
|
|
137
170
|
hullmass: number
|
|
138
171
|
capacity: number
|
|
139
172
|
} {
|
|
140
|
-
const density = stats.density
|
|
141
|
-
const strength = stats.strength
|
|
142
|
-
const hardness = stats.hardness
|
|
143
|
-
const saturation = stats.saturation
|
|
173
|
+
const density = stats.density
|
|
174
|
+
const strength = stats.strength
|
|
175
|
+
const hardness = stats.hardness
|
|
176
|
+
const saturation = stats.saturation
|
|
144
177
|
|
|
145
178
|
const hullmass = 25000 + 75 * density
|
|
146
179
|
const statSum = strength + hardness + saturation
|
|
@@ -196,16 +229,20 @@ export function computeShipCapabilities(
|
|
|
196
229
|
if (gathererModules.length > 0) {
|
|
197
230
|
let totalYield = 0
|
|
198
231
|
let totalDrain = 0
|
|
199
|
-
let
|
|
232
|
+
let maxDepth = 0
|
|
200
233
|
let totalSpeed = 0
|
|
201
234
|
for (const m of gathererModules) {
|
|
202
|
-
const
|
|
235
|
+
const tier = getItem(m.itemId).tier
|
|
236
|
+
const caps = computeGathererCapabilities(
|
|
237
|
+
decodeCraftedItemStats(m.itemId, m.stats),
|
|
238
|
+
tier
|
|
239
|
+
)
|
|
203
240
|
totalYield += caps.yield
|
|
204
241
|
totalDrain += caps.drain
|
|
205
|
-
|
|
242
|
+
if (caps.depth > maxDepth) maxDepth = caps.depth
|
|
206
243
|
totalSpeed += caps.speed
|
|
207
244
|
}
|
|
208
|
-
ship.gatherer = {yield: totalYield, drain: totalDrain, depth:
|
|
245
|
+
ship.gatherer = {yield: totalYield, drain: totalDrain, depth: maxDepth, speed: totalSpeed}
|
|
209
246
|
}
|
|
210
247
|
|
|
211
248
|
const haulerModules = modules.filter((m) => getModuleCapabilityType(m.itemId) === MODULE_HAULER)
|
package/src/entities/ship.ts
CHANGED
|
@@ -2,7 +2,9 @@ import {type UInt16, type UInt16Type, UInt32, UInt64, type UInt64Type} from '@wh
|
|
|
2
2
|
import {ServerContract} from '../contracts'
|
|
3
3
|
import {Coordinates, type CoordinatesType} from '../types'
|
|
4
4
|
import {
|
|
5
|
+
type FloatPosition,
|
|
5
6
|
getDestinationLocation,
|
|
7
|
+
getInterpolatedPosition,
|
|
6
8
|
getPositionAt,
|
|
7
9
|
getFlightOrigin as travelGetFlightOrigin,
|
|
8
10
|
} from '../travel/travel'
|
|
@@ -55,6 +57,14 @@ export class Ship extends ServerContract.Types.entity_info {
|
|
|
55
57
|
return this.entity_name
|
|
56
58
|
}
|
|
57
59
|
|
|
60
|
+
get entityClass(): 'mobile' {
|
|
61
|
+
return 'mobile'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get canUndeploy(): boolean {
|
|
65
|
+
return true
|
|
66
|
+
}
|
|
67
|
+
|
|
58
68
|
get inv(): InventoryAccessor {
|
|
59
69
|
this._inv ??= new InventoryAccessor(this)
|
|
60
70
|
return this._inv
|
|
@@ -87,12 +97,19 @@ export class Ship extends ServerContract.Types.entity_info {
|
|
|
87
97
|
return dest ? Coordinates.from(dest) : undefined
|
|
88
98
|
}
|
|
89
99
|
|
|
100
|
+
/** Chain-tile coordinates at `now`. For smooth visual position use interpolatedPositionAt. */
|
|
90
101
|
positionAt(now: Date): Coordinates {
|
|
91
102
|
const taskIndex = this.sched.currentTaskIndex(now)
|
|
92
103
|
const progress = this.sched.currentTaskProgress(now)
|
|
93
104
|
return Coordinates.from(getPositionAt(this, taskIndex, progress))
|
|
94
105
|
}
|
|
95
106
|
|
|
107
|
+
interpolatedPositionAt(now: Date): FloatPosition {
|
|
108
|
+
const taskIndex = this.sched.currentTaskIndex(now)
|
|
109
|
+
const progress = this.sched.currentTaskProgressFloat(now)
|
|
110
|
+
return getInterpolatedPosition(this, taskIndex, progress)
|
|
111
|
+
}
|
|
112
|
+
|
|
96
113
|
isInFlight(now: Date): boolean {
|
|
97
114
|
return schedule.isInFlight(this, now)
|
|
98
115
|
}
|
|
@@ -31,6 +31,14 @@ export class Warehouse extends ServerContract.Types.entity_info {
|
|
|
31
31
|
return this.entity_name
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
get entityClass(): 'building' {
|
|
35
|
+
return 'building'
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get canDemolish(): boolean {
|
|
39
|
+
return true
|
|
40
|
+
}
|
|
41
|
+
|
|
34
42
|
get inv(): InventoryAccessor {
|
|
35
43
|
this._inv ??= new InventoryAccessor(this)
|
|
36
44
|
return this._inv
|