@shipload/sdk 1.0.0-next.26 → 1.0.0-next.28
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 +201 -86
- package/lib/shipload.js +843 -425
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +825 -423
- package/lib/shipload.m.js.map +1 -1
- package/lib/testing.d.ts +13 -8
- package/lib/testing.js +41 -26
- package/lib/testing.js.map +1 -1
- package/lib/testing.m.js +41 -26
- package/lib/testing.m.js.map +1 -1
- package/package.json +1 -1
- package/src/capabilities/craftable.ts +51 -0
- package/src/contracts/server.ts +39 -18
- package/src/data/capabilities.ts +5 -0
- package/src/data/colors.ts +1 -1
- package/src/data/item-ids.ts +1 -1
- package/src/data/metadata.ts +3 -3
- package/src/data/recipes.json +10 -10
- package/src/derivation/capabilities.ts +11 -11
- package/src/derivation/index.ts +9 -0
- package/src/derivation/stars.test.ts +51 -0
- package/src/derivation/stars.ts +15 -0
- package/src/derivation/stats.ts +5 -4
- package/src/entities/entity.ts +1 -1
- package/src/entities/makers.ts +15 -6
- package/src/index-module.ts +33 -4
- package/src/managers/actions.ts +10 -1
- package/src/managers/construction.ts +67 -65
- package/src/nft/buildImmutableData.ts +13 -11
- package/src/nft/description.ts +2 -2
- package/src/resolution/resolve-item.ts +2 -2
- package/src/scheduling/accessor.ts +65 -23
- package/src/scheduling/availability.ts +108 -0
- package/src/scheduling/energy.ts +18 -11
- package/src/scheduling/lane-core.ts +130 -0
- package/src/scheduling/lanes.ts +60 -0
- package/src/scheduling/projection.ts +30 -54
- package/src/scheduling/schedule.ts +236 -121
- package/src/travel/travel.ts +21 -16
- package/src/types/capabilities.ts +1 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import {expect, test} from 'bun:test'
|
|
2
|
+
import {
|
|
3
|
+
MAX_STARS_PER_STAT,
|
|
4
|
+
MAX_STAR_RATING,
|
|
5
|
+
starRating,
|
|
6
|
+
starsForStat,
|
|
7
|
+
statMagnitude,
|
|
8
|
+
STAR_STEP,
|
|
9
|
+
} from './stars'
|
|
10
|
+
|
|
11
|
+
test('starsForStat bands at the 250 boundaries', () => {
|
|
12
|
+
expect(starsForStat(0)).toBe(0)
|
|
13
|
+
expect(starsForStat(1)).toBe(0)
|
|
14
|
+
expect(starsForStat(249)).toBe(0)
|
|
15
|
+
expect(starsForStat(250)).toBe(1)
|
|
16
|
+
expect(starsForStat(499)).toBe(1)
|
|
17
|
+
expect(starsForStat(500)).toBe(2)
|
|
18
|
+
expect(starsForStat(749)).toBe(2)
|
|
19
|
+
expect(starsForStat(750)).toBe(3)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('starsForStat clamps to MAX_STARS_PER_STAT', () => {
|
|
23
|
+
expect(starsForStat(999)).toBe(MAX_STARS_PER_STAT)
|
|
24
|
+
expect(starsForStat(1000)).toBe(MAX_STARS_PER_STAT)
|
|
25
|
+
expect(starsForStat(10_000)).toBe(MAX_STARS_PER_STAT)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('starsForStat never goes negative', () => {
|
|
29
|
+
expect(starsForStat(-50)).toBe(0)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('starRating sums per-stat stars to a 0-9 grade', () => {
|
|
33
|
+
expect(starRating(0, 0, 0)).toBe(0)
|
|
34
|
+
expect(starRating(250, 0, 0)).toBe(1)
|
|
35
|
+
expect(starRating(750, 750, 750)).toBe(MAX_STAR_RATING)
|
|
36
|
+
expect(starRating(999, 999, 999)).toBe(MAX_STAR_RATING)
|
|
37
|
+
expect(starRating(599, 599, 599)).toBe(6)
|
|
38
|
+
expect(starRating(251, 251, 251)).toBe(3)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('statMagnitude sums raw values for tiebreaking', () => {
|
|
42
|
+
expect(statMagnitude(599, 599, 599)).toBe(1797)
|
|
43
|
+
expect(statMagnitude(251, 251, 251)).toBe(753)
|
|
44
|
+
expect(statMagnitude(599, 599, 599)).toBeGreaterThan(statMagnitude(251, 251, 251))
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test('constants hold their documented values', () => {
|
|
48
|
+
expect(STAR_STEP).toBe(250)
|
|
49
|
+
expect(MAX_STARS_PER_STAT).toBe(3)
|
|
50
|
+
expect(MAX_STAR_RATING).toBe(9)
|
|
51
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const STAR_STEP = 250
|
|
2
|
+
export const MAX_STARS_PER_STAT = 3
|
|
3
|
+
export const MAX_STAR_RATING = MAX_STARS_PER_STAT * 3
|
|
4
|
+
|
|
5
|
+
export function starsForStat(value: number): number {
|
|
6
|
+
return Math.max(0, Math.min(MAX_STARS_PER_STAT, Math.floor(value / STAR_STEP)))
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function starRating(stat1: number, stat2: number, stat3: number): number {
|
|
10
|
+
return starsForStat(stat1) + starsForStat(stat2) + starsForStat(stat3)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function statMagnitude(stat1: number, stat2: number, stat3: number): number {
|
|
14
|
+
return stat1 + stat2 + stat3
|
|
15
|
+
}
|
package/src/derivation/stats.ts
CHANGED
|
@@ -73,10 +73,11 @@ const GAS_STATS: StatDefinition[] = [
|
|
|
73
73
|
|
|
74
74
|
const REGOLITH_STATS: StatDefinition[] = [
|
|
75
75
|
{
|
|
76
|
-
key: '
|
|
77
|
-
label: '
|
|
78
|
-
abbreviation: '
|
|
79
|
-
purpose:
|
|
76
|
+
key: 'cohesion',
|
|
77
|
+
label: 'Cohesion',
|
|
78
|
+
abbreviation: 'COH',
|
|
79
|
+
purpose:
|
|
80
|
+
'Binding strength of the loose aggregate; higher cohesion yields more rigid frames and hulls',
|
|
80
81
|
},
|
|
81
82
|
{
|
|
82
83
|
key: 'hardness',
|
package/src/entities/entity.ts
CHANGED
package/src/entities/makers.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {getItem} from '../data/catalog'
|
|
|
11
11
|
import {getModuleCapabilityType, moduleAccepts, moduleSlotTypeToCode} from '../capabilities/modules'
|
|
12
12
|
import {computeEntityCapabilities} from '../derivation/capabilities'
|
|
13
13
|
import {packedModulesToInstalled} from './slot-multiplier'
|
|
14
|
+
import {LANE_MOBILITY} from '../scheduling/schedule'
|
|
14
15
|
|
|
15
16
|
export interface PackedModuleInput {
|
|
16
17
|
itemId: number
|
|
@@ -29,6 +30,7 @@ export interface EntityStateInput {
|
|
|
29
30
|
energy?: number
|
|
30
31
|
modules?: PackedModuleInput[]
|
|
31
32
|
schedule?: ServerContract.Types.schedule
|
|
33
|
+
lanes?: ServerContract.Types.lane[]
|
|
32
34
|
cargo?: ServerContract.Types.cargo_item[]
|
|
33
35
|
}
|
|
34
36
|
|
|
@@ -73,7 +75,7 @@ const ZERO_HULL_STATS: Record<string, number> = {
|
|
|
73
75
|
density: 0,
|
|
74
76
|
strength: 0,
|
|
75
77
|
hardness: 0,
|
|
76
|
-
|
|
78
|
+
cohesion: 0,
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
export function makeEntity(packedItemId: number, state: EntityStateInput): Entity {
|
|
@@ -86,6 +88,17 @@ export function makeEntity(packedItemId: number, state: EntityStateInput): Entit
|
|
|
86
88
|
const layout = getEntityLayout(packedItemId)?.slots ?? []
|
|
87
89
|
const mods = state.modules ?? []
|
|
88
90
|
|
|
91
|
+
const lanes =
|
|
92
|
+
state.lanes ??
|
|
93
|
+
(state.schedule
|
|
94
|
+
? [
|
|
95
|
+
ServerContract.Types.lane.from({
|
|
96
|
+
lane_key: UInt8.from(LANE_MOBILITY),
|
|
97
|
+
schedule: state.schedule,
|
|
98
|
+
}),
|
|
99
|
+
]
|
|
100
|
+
: [])
|
|
101
|
+
|
|
89
102
|
const info: Record<string, unknown> = {
|
|
90
103
|
type: template.kind,
|
|
91
104
|
id: UInt64.from(state.id),
|
|
@@ -95,14 +108,10 @@ export function makeEntity(packedItemId: number, state: EntityStateInput): Entit
|
|
|
95
108
|
item_id: UInt16.from(state.itemId ?? template.itemId),
|
|
96
109
|
cargomass: UInt32.from(state.cargomass ?? 0),
|
|
97
110
|
cargo: state.cargo || [],
|
|
98
|
-
|
|
99
|
-
current_task_elapsed: UInt32.from(0),
|
|
100
|
-
current_task_remaining: UInt32.from(0),
|
|
101
|
-
pending_tasks: [],
|
|
111
|
+
lanes,
|
|
102
112
|
}
|
|
103
113
|
|
|
104
114
|
if (state.energy !== undefined) info.energy = UInt16.from(state.energy)
|
|
105
|
-
if (state.schedule) info.schedule = state.schedule
|
|
106
115
|
|
|
107
116
|
if (kind === 'container') {
|
|
108
117
|
info.modules = []
|
package/src/index-module.ts
CHANGED
|
@@ -28,6 +28,7 @@ export type movement_stats = ServerContract.Types.movement_stats
|
|
|
28
28
|
export type energy_stats = ServerContract.Types.energy_stats
|
|
29
29
|
export type loader_stats = ServerContract.Types.loader_stats
|
|
30
30
|
export type schedule = ServerContract.Types.schedule
|
|
31
|
+
export type lane = ServerContract.Types.lane
|
|
31
32
|
export type task = ServerContract.Types.task
|
|
32
33
|
export type cargo_item = ServerContract.Types.cargo_item
|
|
33
34
|
export type entity_row = ServerContract.Types.entity_row
|
|
@@ -132,6 +133,15 @@ export type {EffectiveReserveInput} from './derivation'
|
|
|
132
133
|
export {getStatDefinitions, getStatName, resolveStats} from './derivation'
|
|
133
134
|
export type {StatDefinition, NamedStats} from './derivation'
|
|
134
135
|
|
|
136
|
+
export {
|
|
137
|
+
STAR_STEP,
|
|
138
|
+
MAX_STARS_PER_STAT,
|
|
139
|
+
MAX_STAR_RATING,
|
|
140
|
+
starsForStat,
|
|
141
|
+
starRating,
|
|
142
|
+
statMagnitude,
|
|
143
|
+
} from './derivation'
|
|
144
|
+
|
|
135
145
|
export {hash, hash512} from './utils/hash'
|
|
136
146
|
|
|
137
147
|
export {
|
|
@@ -177,7 +187,19 @@ export type {
|
|
|
177
187
|
} from './travel/travel'
|
|
178
188
|
|
|
179
189
|
export * as schedule from './scheduling/schedule'
|
|
180
|
-
export
|
|
190
|
+
export {LANE_MOBILITY, LANE_BARRIER} from './scheduling/schedule'
|
|
191
|
+
export type {
|
|
192
|
+
ScheduleData,
|
|
193
|
+
LaneView,
|
|
194
|
+
OrderedTask,
|
|
195
|
+
ResolvedEvent,
|
|
196
|
+
} from './scheduling/schedule'
|
|
197
|
+
export {
|
|
198
|
+
candidateLaneCompletesAt,
|
|
199
|
+
laneKeyForModule,
|
|
200
|
+
rawScheduleEnd,
|
|
201
|
+
workerLaneKey,
|
|
202
|
+
} from './scheduling/lanes'
|
|
181
203
|
export {ScheduleAccessor, createScheduleAccessor} from './scheduling/accessor'
|
|
182
204
|
export {InventoryAccessor, createInventoryAccessor} from './entities/inventory-accessor'
|
|
183
205
|
export type {HasCargo} from './entities/inventory-accessor'
|
|
@@ -191,13 +213,11 @@ export {
|
|
|
191
213
|
createProjectedEntity,
|
|
192
214
|
projectEntity,
|
|
193
215
|
projectEntityAt,
|
|
194
|
-
|
|
195
|
-
projectFromCurrentStateAt,
|
|
216
|
+
projectRemainingAt,
|
|
196
217
|
validateSchedule,
|
|
197
218
|
} from './scheduling/projection'
|
|
198
219
|
export type {
|
|
199
220
|
Projectable,
|
|
200
|
-
ProjectableSnapshot,
|
|
201
221
|
ProjectedEntity,
|
|
202
222
|
ProjectionOptions,
|
|
203
223
|
} from './scheduling/projection'
|
|
@@ -205,6 +225,15 @@ export type {
|
|
|
205
225
|
export {taskCargoChanges} from './scheduling/task-cargo'
|
|
206
226
|
export type {TaskCargoChange, TaskCargoDirection} from './scheduling/task-cargo'
|
|
207
227
|
|
|
228
|
+
export {
|
|
229
|
+
projectedCargoAvailableAt,
|
|
230
|
+
availableForItem,
|
|
231
|
+
cargoReadyAt,
|
|
232
|
+
taskCargoEffect,
|
|
233
|
+
} from './scheduling/availability'
|
|
234
|
+
|
|
235
|
+
export {maxCraftable} from './capabilities/craftable'
|
|
236
|
+
|
|
208
237
|
export {energyAtTime} from './scheduling/energy'
|
|
209
238
|
|
|
210
239
|
export * from './types/capabilities'
|
package/src/managers/actions.ts
CHANGED
|
@@ -65,13 +65,22 @@ export class ActionsManager extends BaseManager {
|
|
|
65
65
|
return this.server.action('resolve', params)
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
cancel(entityId: UInt64Type, count: UInt64Type): Action {
|
|
68
|
+
cancel(entityId: UInt64Type, laneKey: number, count: UInt64Type): Action {
|
|
69
69
|
return this.server.action('cancel', {
|
|
70
70
|
id: UInt64.from(entityId),
|
|
71
|
+
lane_key: UInt8.from(laneKey),
|
|
71
72
|
count: UInt64.from(count),
|
|
72
73
|
})
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
retarget(sourceId: UInt64Type, taskIndex: UInt64Type, newDestId: UInt64Type): Action {
|
|
77
|
+
return this.server.action('retarget', {
|
|
78
|
+
source_id: UInt64.from(sourceId),
|
|
79
|
+
task_index: UInt64.from(taskIndex),
|
|
80
|
+
new_dest_id: UInt64.from(newDestId),
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
75
84
|
recharge(entityId: UInt64Type): Action {
|
|
76
85
|
return this.server.action('recharge', {
|
|
77
86
|
id: UInt64.from(entityId),
|
|
@@ -4,6 +4,7 @@ import type {ServerContract} from '../contracts'
|
|
|
4
4
|
import {PlotManager} from './plot'
|
|
5
5
|
import {getItem} from '../data/catalog'
|
|
6
6
|
import {calc_craft_duration} from '../capabilities/crafting'
|
|
7
|
+
import {getLanes, getTasks} from '../scheduling/schedule'
|
|
7
8
|
import {TaskType} from '../types'
|
|
8
9
|
import type {
|
|
9
10
|
BuildableTarget,
|
|
@@ -94,43 +95,43 @@ export class ConstructionManager extends BaseManager {
|
|
|
94
95
|
const buckets = new Map<string, Map<string, InboundTransfer>>()
|
|
95
96
|
const nowMs = now.getTime()
|
|
96
97
|
for (const entity of entities) {
|
|
97
|
-
const schedule = entity.schedule
|
|
98
|
-
if (!schedule) continue
|
|
99
98
|
const entityIdStr = entity.id.toString()
|
|
100
99
|
const sourceName = entity.entity_name || entityIdStr
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
existing
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
100
|
+
for (const lane of getLanes(entity)) {
|
|
101
|
+
const startedMs = lane.schedule.started.toDate().getTime()
|
|
102
|
+
let cumulativeSec = 0
|
|
103
|
+
for (const task of lane.schedule.tasks) {
|
|
104
|
+
cumulativeSec += task.duration.toNumber()
|
|
105
|
+
if (!isTransferTask(task)) continue
|
|
106
|
+
if (!task.entitytarget) continue
|
|
107
|
+
const projectedEndMs = startedMs + cumulativeSec * 1000
|
|
108
|
+
if (projectedEndMs < nowMs) continue
|
|
109
|
+
const targetIdStr = task.entitytarget.entity_id.toString()
|
|
110
|
+
const etaSeconds = Math.max(0, Math.round((projectedEndMs - nowMs) / 1000))
|
|
111
|
+
let perTarget = buckets.get(targetIdStr)
|
|
112
|
+
if (!perTarget) {
|
|
113
|
+
perTarget = new Map()
|
|
114
|
+
buckets.set(targetIdStr, perTarget)
|
|
115
|
+
}
|
|
116
|
+
for (const c of task.cargo) {
|
|
117
|
+
const itemId = c.item_id.toNumber()
|
|
118
|
+
const quantity = c.quantity.toNumber()
|
|
119
|
+
if (quantity === 0) continue
|
|
120
|
+
const key = `${entityIdStr}#${itemId}`
|
|
121
|
+
const existing = perTarget.get(key)
|
|
122
|
+
if (existing) {
|
|
123
|
+
existing.quantity += quantity
|
|
124
|
+
existing.etaSeconds = Math.min(existing.etaSeconds, etaSeconds)
|
|
125
|
+
} else {
|
|
126
|
+
perTarget.set(key, {
|
|
127
|
+
sourceEntityId: entity.id,
|
|
128
|
+
sourceEntityType: entity.type,
|
|
129
|
+
sourceName,
|
|
130
|
+
itemId,
|
|
131
|
+
quantity,
|
|
132
|
+
etaSeconds,
|
|
133
|
+
})
|
|
134
|
+
}
|
|
134
135
|
}
|
|
135
136
|
}
|
|
136
137
|
}
|
|
@@ -152,25 +153,24 @@ export class ConstructionManager extends BaseManager {
|
|
|
152
153
|
completesAt: number
|
|
153
154
|
hasStarted: boolean
|
|
154
155
|
} | null {
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
hasStarted: startsAt <= now.getTime(),
|
|
156
|
+
for (const lane of getLanes(plot)) {
|
|
157
|
+
const startedMs = lane.schedule.started.toDate().getTime()
|
|
158
|
+
let startSec = 0
|
|
159
|
+
for (const task of lane.schedule.tasks) {
|
|
160
|
+
if (task.type.toNumber() === TaskType.RESERVED) {
|
|
161
|
+
if (!task.entitytarget) return null
|
|
162
|
+
const startsAt = startedMs + startSec * 1000
|
|
163
|
+
const completesAt = startsAt + task.duration.toNumber() * 1000
|
|
164
|
+
return {
|
|
165
|
+
builderId: task.entitytarget.entity_id,
|
|
166
|
+
group: task.entitygroup ?? undefined,
|
|
167
|
+
startsAt,
|
|
168
|
+
completesAt,
|
|
169
|
+
hasStarted: startsAt <= now.getTime(),
|
|
170
|
+
}
|
|
171
171
|
}
|
|
172
|
+
startSec += task.duration.toNumber()
|
|
172
173
|
}
|
|
173
|
-
startSec += task.duration.toNumber()
|
|
174
174
|
}
|
|
175
175
|
return null
|
|
176
176
|
}
|
|
@@ -179,19 +179,22 @@ export class ConstructionManager extends BaseManager {
|
|
|
179
179
|
builder: ServerContract.Types.entity_info | undefined,
|
|
180
180
|
group: UInt64 | undefined
|
|
181
181
|
): {cancelable: boolean; blockingTaskCount: number} {
|
|
182
|
-
if (!builder
|
|
182
|
+
if (!builder || group === undefined) {
|
|
183
183
|
return {cancelable: false, blockingTaskCount: 0}
|
|
184
184
|
}
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
(
|
|
188
|
-
t
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
185
|
+
for (const lane of getLanes(builder)) {
|
|
186
|
+
const tasks = lane.schedule.tasks
|
|
187
|
+
const buildIdx = tasks.findIndex(
|
|
188
|
+
(t) =>
|
|
189
|
+
t.type.toNumber() === TaskType.BUILDPLOT &&
|
|
190
|
+
t.entitygroup !== undefined &&
|
|
191
|
+
t.entitygroup.equals(group)
|
|
192
|
+
)
|
|
193
|
+
if (buildIdx < 0) continue
|
|
194
|
+
const trailing = tasks.length - 1 - buildIdx
|
|
195
|
+
return {cancelable: trailing === 0, blockingTaskCount: trailing}
|
|
196
|
+
}
|
|
197
|
+
return {cancelable: false, blockingTaskCount: 0}
|
|
195
198
|
}
|
|
196
199
|
|
|
197
200
|
private buildFromReservation(
|
|
@@ -357,9 +360,8 @@ function isTransferTask(task: ServerContract.Types.task): boolean {
|
|
|
357
360
|
}
|
|
358
361
|
|
|
359
362
|
function reservationsOf(source: ServerContract.Types.entity_info): Reservation[] {
|
|
360
|
-
if (!source.schedule) return []
|
|
361
363
|
const out = new Map<string, Reservation>()
|
|
362
|
-
for (const task of source
|
|
364
|
+
for (const task of getTasks(source)) {
|
|
363
365
|
if (!isTransferTask(task)) continue
|
|
364
366
|
if (!task.entitytarget) continue
|
|
365
367
|
const targetType = task.entitytarget.entity_type
|
|
@@ -224,21 +224,23 @@ export function buildModuleImmutable(
|
|
|
224
224
|
}
|
|
225
225
|
case MODULE_CRAFTER: {
|
|
226
226
|
const rea = decodeStat(stats, 0)
|
|
227
|
-
const
|
|
227
|
+
const fin = decodeStat(stats, 1)
|
|
228
228
|
base.push({first: 'reactivity', second: ['uint16', rea]})
|
|
229
|
-
base.push({first: '
|
|
229
|
+
base.push({first: 'fineness', second: ['uint16', fin]})
|
|
230
230
|
base.push({first: 'speed', second: ['uint16', computeCrafterSpeed(rea)]})
|
|
231
|
-
base.push({first: 'drain', second: ['uint16', computeCrafterDrain(
|
|
231
|
+
base.push({first: 'drain', second: ['uint16', computeCrafterDrain(fin)]})
|
|
232
232
|
break
|
|
233
233
|
}
|
|
234
234
|
case MODULE_STORAGE: {
|
|
235
235
|
const str = decodeStat(stats, 0)
|
|
236
|
-
const
|
|
237
|
-
const
|
|
238
|
-
const
|
|
236
|
+
const den = decodeStat(stats, 1)
|
|
237
|
+
const hrd = decodeStat(stats, 2)
|
|
238
|
+
const com = decodeStat(stats, 3)
|
|
239
|
+
const sum = str + den + hrd + com
|
|
239
240
|
base.push({first: 'strength', second: ['uint16', str]})
|
|
240
|
-
base.push({first: '
|
|
241
|
-
base.push({first: '
|
|
241
|
+
base.push({first: 'density', second: ['uint16', den]})
|
|
242
|
+
base.push({first: 'hardness', second: ['uint16', hrd]})
|
|
243
|
+
base.push({first: 'cohesion', second: ['uint16', com]})
|
|
242
244
|
base.push({
|
|
243
245
|
first: 'capacity_bonus_pct',
|
|
244
246
|
second: ['uint16', 10 + Math.floor((sum * 10) / 2997)],
|
|
@@ -247,13 +249,13 @@ export function buildModuleImmutable(
|
|
|
247
249
|
}
|
|
248
250
|
case MODULE_HAULER: {
|
|
249
251
|
const res = decodeStat(stats, 0)
|
|
250
|
-
const
|
|
252
|
+
const pla = decodeStat(stats, 1)
|
|
251
253
|
const ref = decodeStat(stats, 2)
|
|
252
254
|
base.push({first: 'resonance', second: ['uint16', res]})
|
|
253
|
-
base.push({first: '
|
|
255
|
+
base.push({first: 'plasticity', second: ['uint16', pla]})
|
|
254
256
|
base.push({first: 'reflectivity', second: ['uint16', ref]})
|
|
255
257
|
base.push({first: 'capacity', second: ['uint8', computeHaulerCapacity(res)]})
|
|
256
|
-
base.push({first: 'efficiency', second: ['uint16', computeHaulerEfficiency(
|
|
258
|
+
base.push({first: 'efficiency', second: ['uint16', computeHaulerEfficiency(pla)]})
|
|
257
259
|
base.push({first: 'drain', second: ['uint16', computeHaulerDrain(ref)]})
|
|
258
260
|
break
|
|
259
261
|
}
|
package/src/nft/description.ts
CHANGED
|
@@ -162,9 +162,9 @@ export function formatModuleLine(slot: number, itemId: number, stats: bigint): s
|
|
|
162
162
|
}
|
|
163
163
|
case MODULE_HAULER: {
|
|
164
164
|
const res = decodeStat(stats, 0)
|
|
165
|
-
const
|
|
165
|
+
const pla = decodeStat(stats, 1)
|
|
166
166
|
const ref = decodeStat(stats, 2)
|
|
167
|
-
out += ` Capacity ${computeHaulerCapacity(res)} Efficiency ${computeHaulerEfficiency(
|
|
167
|
+
out += ` Capacity ${computeHaulerCapacity(res)} Efficiency ${computeHaulerEfficiency(pla)} Drain ${computeHaulerDrain(ref)}`
|
|
168
168
|
break
|
|
169
169
|
}
|
|
170
170
|
case MODULE_WARP: {
|
|
@@ -222,8 +222,8 @@ function computeCapabilityGroup(
|
|
|
222
222
|
const str = stats.strength
|
|
223
223
|
const den = stats.density
|
|
224
224
|
const hrd = stats.hardness
|
|
225
|
-
const
|
|
226
|
-
const statSum = str + den + hrd +
|
|
225
|
+
const com = stats.cohesion
|
|
226
|
+
const statSum = str + den + hrd + com
|
|
227
227
|
const pct = 10 + Math.floor((statSum * 10) / 2997)
|
|
228
228
|
return {capability: 'Storage', attributes: [{label: 'Capacity Bonus', value: pct}]}
|
|
229
229
|
}
|
|
@@ -1,86 +1,128 @@
|
|
|
1
1
|
import type {ServerContract} from '../contracts'
|
|
2
2
|
import type {TaskType} from '../types'
|
|
3
|
-
import
|
|
4
|
-
import
|
|
3
|
+
import * as core from './lane-core'
|
|
4
|
+
import {
|
|
5
|
+
activeTasks,
|
|
6
|
+
getLane,
|
|
7
|
+
getLanes,
|
|
8
|
+
hasSchedule,
|
|
9
|
+
isIdle,
|
|
10
|
+
LANE_MOBILITY,
|
|
11
|
+
type LaneView,
|
|
12
|
+
type ScheduleData,
|
|
13
|
+
} from './schedule'
|
|
5
14
|
|
|
6
15
|
type Task = ServerContract.Types.task
|
|
7
16
|
|
|
8
17
|
export class ScheduleAccessor {
|
|
9
|
-
|
|
18
|
+
private _laneResolved = false
|
|
19
|
+
private _lane: LaneView | undefined
|
|
20
|
+
|
|
21
|
+
constructor(
|
|
22
|
+
private entity: ScheduleData,
|
|
23
|
+
private laneKey: number = LANE_MOBILITY
|
|
24
|
+
) {}
|
|
25
|
+
|
|
26
|
+
private get lane(): LaneView | undefined {
|
|
27
|
+
if (!this._laneResolved) {
|
|
28
|
+
this._lane = getLane(this.entity, this.laneKey)
|
|
29
|
+
this._laneResolved = true
|
|
30
|
+
}
|
|
31
|
+
return this._lane
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
forLane(laneKey: number): ScheduleAccessor {
|
|
35
|
+
return new ScheduleAccessor(this.entity, laneKey)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get lanes(): LaneView[] {
|
|
39
|
+
return getLanes(this.entity)
|
|
40
|
+
}
|
|
10
41
|
|
|
11
42
|
get hasSchedule(): boolean {
|
|
12
|
-
return
|
|
43
|
+
return hasSchedule(this.entity)
|
|
13
44
|
}
|
|
14
45
|
|
|
15
46
|
get isIdle(): boolean {
|
|
16
|
-
return
|
|
47
|
+
return isIdle(this.entity)
|
|
17
48
|
}
|
|
18
49
|
|
|
19
50
|
get tasks(): Task[] {
|
|
20
|
-
return schedule.
|
|
51
|
+
return this.lane?.schedule.tasks ?? []
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
activeTasks(now: Date): Task[] {
|
|
55
|
+
return activeTasks(this.entity, now)
|
|
21
56
|
}
|
|
22
57
|
|
|
23
58
|
duration(): number {
|
|
24
|
-
return
|
|
59
|
+
return this.lane ? core.laneDuration(this.lane.schedule) : 0
|
|
25
60
|
}
|
|
26
61
|
|
|
27
62
|
elapsed(now: Date): number {
|
|
28
|
-
return
|
|
63
|
+
return this.lane ? core.laneElapsed(this.lane.schedule, now) : 0
|
|
29
64
|
}
|
|
30
65
|
|
|
31
66
|
remaining(now: Date): number {
|
|
32
|
-
return
|
|
67
|
+
return this.lane ? core.laneRemaining(this.lane.schedule, now) : 0
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
startsIn(now: Date): number {
|
|
71
|
+
return this.lane ? core.laneStartsIn(this.lane.schedule, now) : 0
|
|
33
72
|
}
|
|
34
73
|
|
|
35
74
|
complete(now: Date): boolean {
|
|
36
|
-
return
|
|
75
|
+
return this.lane ? core.laneComplete(this.lane.schedule, now) : false
|
|
37
76
|
}
|
|
38
77
|
|
|
39
78
|
currentTaskIndex(now: Date): number {
|
|
40
|
-
return
|
|
79
|
+
return this.lane ? core.currentTaskIndexForLane(this.lane.schedule, now) : -1
|
|
41
80
|
}
|
|
42
81
|
|
|
43
82
|
currentTask(now: Date): Task | undefined {
|
|
44
|
-
return
|
|
83
|
+
return this.lane ? core.currentTask(this.lane.schedule, now) : undefined
|
|
45
84
|
}
|
|
46
85
|
|
|
47
86
|
currentTaskType(now: Date): TaskType | undefined {
|
|
48
|
-
return
|
|
87
|
+
return this.lane ? core.currentTaskType(this.lane.schedule, now) : undefined
|
|
49
88
|
}
|
|
50
89
|
|
|
51
90
|
taskStartTime(index: number): number {
|
|
52
|
-
return
|
|
91
|
+
return this.lane ? core.laneTaskStartTime(this.lane.schedule, index) : 0
|
|
53
92
|
}
|
|
54
93
|
|
|
55
94
|
taskElapsed(index: number, now: Date): number {
|
|
56
|
-
return
|
|
95
|
+
return this.lane ? core.laneTaskElapsed(this.lane.schedule, index, now) : 0
|
|
57
96
|
}
|
|
58
97
|
|
|
59
98
|
taskRemaining(index: number, now: Date): number {
|
|
60
|
-
return
|
|
99
|
+
return this.lane ? core.laneTaskRemaining(this.lane.schedule, index, now) : 0
|
|
61
100
|
}
|
|
62
101
|
|
|
63
102
|
taskComplete(index: number, now: Date): boolean {
|
|
64
|
-
return
|
|
103
|
+
return this.lane ? core.laneTaskComplete(this.lane.schedule, index, now) : false
|
|
65
104
|
}
|
|
66
105
|
|
|
67
106
|
taskInProgress(index: number, now: Date): boolean {
|
|
68
|
-
return
|
|
107
|
+
return this.lane ? core.laneTaskInProgress(this.lane.schedule, index, now) : false
|
|
69
108
|
}
|
|
70
109
|
|
|
71
110
|
currentTaskProgress(now: Date): number {
|
|
72
|
-
return
|
|
111
|
+
return this.lane ? core.currentTaskProgress(this.lane.schedule, now) : 0
|
|
73
112
|
}
|
|
74
113
|
|
|
75
114
|
currentTaskProgressFloat(now: Date): number {
|
|
76
|
-
return
|
|
115
|
+
return this.lane ? core.currentTaskProgressFloatForLane(this.lane.schedule, now) : 0
|
|
77
116
|
}
|
|
78
117
|
|
|
79
118
|
progress(now: Date): number {
|
|
80
|
-
return
|
|
119
|
+
return this.lane ? core.laneProgress(this.lane.schedule, now) : 0
|
|
81
120
|
}
|
|
82
121
|
}
|
|
83
122
|
|
|
84
|
-
export function createScheduleAccessor(
|
|
85
|
-
|
|
123
|
+
export function createScheduleAccessor(
|
|
124
|
+
entity: ScheduleData,
|
|
125
|
+
laneKey: number = LANE_MOBILITY
|
|
126
|
+
): ScheduleAccessor {
|
|
127
|
+
return new ScheduleAccessor(entity, laneKey)
|
|
86
128
|
}
|