@shipload/sdk 1.0.0-next.65 → 1.0.0-next.67
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 +786 -321
- package/lib/shipload.js +2731 -1305
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +2644 -1286
- package/lib/shipload.m.js.map +1 -1
- package/lib/testing.d.ts +223 -146
- package/lib/testing.js +907 -563
- package/lib/testing.js.map +1 -1
- package/lib/testing.m.js +907 -563
- package/lib/testing.m.js.map +1 -1
- package/package.json +1 -1
- package/src/capabilities/crafting.test.ts +51 -6
- package/src/capabilities/crafting.ts +19 -4
- package/src/capabilities/gathering.ts +2 -2
- package/src/capabilities/storage.test.ts +26 -0
- package/src/capabilities/storage.ts +6 -6
- package/src/contracts/fund.ts +79 -2
- package/src/contracts/platform.ts +10 -1
- package/src/contracts/server.ts +521 -330
- package/src/data/charters.json +325 -127
- package/src/data/entities.json +29 -24
- package/src/data/items.json +114 -114
- package/src/data/kind-registry.json +3 -3
- package/src/data/kind-registry.ts +5 -0
- package/src/data/metadata.ts +9 -2
- package/src/data/recipes.json +56 -95
- package/src/derivation/build-methods.test.ts +59 -1
- package/src/derivation/build-methods.ts +2 -0
- package/src/derivation/capabilities.ts +7 -7
- package/src/derivation/strata.ts +5 -3
- package/src/derivation/tiers.ts +5 -5
- package/src/entity/hydrate.test.ts +156 -0
- package/src/entity/hydrate.ts +86 -0
- package/src/errors.ts +9 -0
- package/src/format.ts +15 -13
- package/src/index-module.ts +32 -5
- package/src/influence/ballot.test.ts +103 -0
- package/src/influence/ballot.ts +98 -0
- package/src/influence/charter-metadata.test.ts +136 -0
- package/src/influence/charter-metadata.ts +236 -0
- package/src/influence/charters.test.ts +51 -65
- package/src/influence/charters.ts +70 -40
- package/src/influence/civic-shuttle.test.ts +159 -0
- package/src/influence/civic-shuttle.ts +115 -0
- package/src/influence/civic.test.ts +287 -0
- package/src/influence/civic.ts +269 -0
- package/src/influence/constants.ts +30 -9
- package/src/influence/decomp.ts +15 -15
- package/src/influence/duration.ts +42 -6
- package/src/influence/index.ts +59 -6
- package/src/influence/influence.test.ts +76 -9
- package/src/influence/valuation.ts +17 -17
- package/src/managers/actions.ts +99 -37
- package/src/managers/base.ts +4 -0
- package/src/managers/context.ts +3 -1
- package/src/managers/epochs.ts +32 -1
- package/src/managers/index.ts +4 -2
- package/src/managers/influence.test.ts +18 -20
- package/src/managers/influence.ts +137 -84
- package/src/managers/jobs.test.ts +2 -2
- package/src/managers/jobs.ts +4 -4
- package/src/nft/description.ts +10 -11
- package/src/planner/index.ts +54 -0
- package/src/planner/planner.test.ts +127 -2
- package/src/resolution/display-name.ts +1 -1
- package/src/scheduling/availability.test.ts +113 -0
- package/src/scheduling/availability.ts +48 -8
- package/src/scheduling/epoch.ts +1 -0
- package/src/scheduling/jobs.ts +1 -1
- package/src/scheduling/projection.ts +2 -16
- package/src/scheduling/task-cargo.test.ts +62 -1
- package/src/scheduling/task-cargo.ts +25 -10
- package/src/scheduling/task-effects.test.ts +35 -0
- package/src/scheduling/task-effects.ts +34 -0
- package/src/scheduling/unwrap.test.ts +3 -3
- package/src/scheduling/unwrap.ts +3 -3
- package/src/shipload.ts +22 -3
- package/src/travel/reach.test.ts +86 -0
- package/src/travel/reach.ts +23 -10
- package/src/travel/route-build.test.ts +63 -0
- package/src/travel/route-build.ts +60 -0
- package/src/travel/travel.ts +2 -1
- package/src/types.ts +6 -2
- package/src/utils/hash.ts +18 -2
- package/src/utils/system.ts +6 -1
package/src/influence/decomp.ts
CHANGED
|
@@ -4,40 +4,40 @@ import recipes from '../data/recipes.json'
|
|
|
4
4
|
import {
|
|
5
5
|
DECOMP_MAX_BUCKETS,
|
|
6
6
|
DECOMP_MAX_DEPTH,
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
DECOMP_PROCESSED_MASS_MAX,
|
|
8
|
+
DECOMP_RAW_MASS_MAX,
|
|
9
9
|
} from './constants'
|
|
10
10
|
import {categoryIndex} from './categories'
|
|
11
11
|
|
|
12
12
|
export interface DecompBucket {
|
|
13
13
|
category: number
|
|
14
14
|
tier: number
|
|
15
|
-
|
|
15
|
+
rawMass: number
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export interface DecompEntry {
|
|
19
19
|
itemId: number
|
|
20
|
-
|
|
20
|
+
processedMass: number
|
|
21
21
|
buckets: DecompBucket[]
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
interface Accumulator {
|
|
25
25
|
buckets: DecompBucket[]
|
|
26
|
-
|
|
26
|
+
processedMass: number
|
|
27
27
|
overflow: boolean
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
function add(acc: Accumulator, category: number, tier: number,
|
|
30
|
+
function add(acc: Accumulator, category: number, tier: number, mass: number): void {
|
|
31
31
|
const existing = acc.buckets.find((b) => b.category === category && b.tier === tier)
|
|
32
32
|
if (existing) {
|
|
33
|
-
existing.
|
|
33
|
+
existing.rawMass += mass
|
|
34
34
|
return
|
|
35
35
|
}
|
|
36
36
|
if (acc.buckets.length >= DECOMP_MAX_BUCKETS) {
|
|
37
37
|
acc.overflow = true
|
|
38
38
|
return
|
|
39
39
|
}
|
|
40
|
-
acc.buckets.push({category, tier,
|
|
40
|
+
acc.buckets.push({category, tier, rawMass: mass})
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
function walk(itemId: number, multiplier: number, acc: Accumulator, depth: number): void {
|
|
@@ -51,14 +51,14 @@ function walk(itemId: number, multiplier: number, acc: Accumulator, depth: numbe
|
|
|
51
51
|
for (const input of recipe.inputs) {
|
|
52
52
|
const def = tryGetItem(input.itemId)
|
|
53
53
|
const units = multiplier * input.quantity
|
|
54
|
-
const
|
|
55
|
-
acc.
|
|
54
|
+
const mass = units * (def ? def.mass : 0)
|
|
55
|
+
acc.processedMass += mass
|
|
56
56
|
if (getRecipe(input.itemId)) {
|
|
57
57
|
walk(input.itemId, units, acc, depth + 1)
|
|
58
58
|
} else if (def?.type !== 'resource' || def.category === undefined) {
|
|
59
59
|
acc.overflow = true
|
|
60
60
|
} else {
|
|
61
|
-
add(acc, categoryIndex(def.category), def.tier,
|
|
61
|
+
add(acc, categoryIndex(def.category), def.tier, mass)
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
}
|
|
@@ -66,18 +66,18 @@ function walk(itemId: number, multiplier: number, acc: Accumulator, depth: numbe
|
|
|
66
66
|
function buildRegistry(): DecompEntry[] {
|
|
67
67
|
const out: DecompEntry[] = []
|
|
68
68
|
for (const recipe of recipes as {outputItemId: number}[]) {
|
|
69
|
-
const acc: Accumulator = {buckets: [],
|
|
69
|
+
const acc: Accumulator = {buckets: [], processedMass: 0, overflow: false}
|
|
70
70
|
walk(recipe.outputItemId, 1, acc, 0)
|
|
71
71
|
acc.buckets.sort((a, b) => a.category - b.category || a.tier - b.tier)
|
|
72
72
|
|
|
73
|
-
const rawTotal = acc.buckets.reduce((sum, b) => sum + b.
|
|
74
|
-
if (rawTotal >
|
|
73
|
+
const rawTotal = acc.buckets.reduce((sum, b) => sum + b.rawMass, 0)
|
|
74
|
+
if (rawTotal > DECOMP_RAW_MASS_MAX || acc.processedMass > DECOMP_PROCESSED_MASS_MAX) {
|
|
75
75
|
acc.overflow = true
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
out.push({
|
|
79
79
|
itemId: recipe.outputItemId,
|
|
80
|
-
|
|
80
|
+
processedMass: acc.overflow ? 0 : acc.processedMass,
|
|
81
81
|
buckets: acc.buckets,
|
|
82
82
|
})
|
|
83
83
|
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
import type {NameType} from '@wharfkit/antelope'
|
|
2
|
+
import type {ServerContract} from '../contracts'
|
|
3
|
+
import {getEntityClass} from '../data/kind-registry'
|
|
1
4
|
import {computeLoaderMass, computeLoaderThrust} from '../nft/description'
|
|
2
|
-
import {
|
|
5
|
+
import {laneKeyForModule, resolveLaneLoader} from '../scheduling/lanes'
|
|
6
|
+
import {calc_onesided_duration} from '../travel/travel'
|
|
7
|
+
import {MASS_STAT_SCALE, MASS_UNITS_PER_TONNE, PRECISION} from '../types'
|
|
3
8
|
import {CIVIC_LOADER_STAT, CIVIC_LOADER_TIER, DEFAULT_ORBITAL_Z} from './constants'
|
|
4
9
|
|
|
5
10
|
export interface CivicLoaderStats {
|
|
@@ -16,7 +21,7 @@ export function civicLoader(): CivicLoaderStats {
|
|
|
16
21
|
|
|
17
22
|
function acceleration(thrust: number, mass: number): number {
|
|
18
23
|
if (mass <= 0) return 0
|
|
19
|
-
return (thrust / mass) * PRECISION
|
|
24
|
+
return (thrust / (mass * MASS_STAT_SCALE)) * PRECISION
|
|
20
25
|
}
|
|
21
26
|
|
|
22
27
|
function flightTime(distance: number, accel: number): number {
|
|
@@ -24,13 +29,44 @@ function flightTime(distance: number, accel: number): number {
|
|
|
24
29
|
return Math.floor(2 * Math.sqrt(distance / accel))
|
|
25
30
|
}
|
|
26
31
|
|
|
27
|
-
export function contributeDuration(
|
|
32
|
+
export function contributeDuration(totalMass: number, altitudeZ = 0): number {
|
|
28
33
|
const loader = civicLoader()
|
|
29
|
-
const
|
|
34
|
+
const mass = totalMass + loader.mass
|
|
30
35
|
const z = Math.max(altitudeZ, DEFAULT_ORBITAL_Z)
|
|
31
|
-
return flightTime(z, acceleration(loader.thrust,
|
|
36
|
+
return flightTime(z, acceleration(loader.thrust, mass))
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
export function contributeDurationForTonnes(tonnes: number, altitudeZ = 0): number {
|
|
35
|
-
return contributeDuration(Math.floor(tonnes *
|
|
40
|
+
return contributeDuration(Math.floor(tonnes * MASS_UNITS_PER_TONNE), altitudeZ)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const DEPOT_LOADER_SLOT = 0
|
|
44
|
+
|
|
45
|
+
export interface DepotTransferParams {
|
|
46
|
+
depotModules: ServerContract.Types.module_entry[]
|
|
47
|
+
depotItemId: number
|
|
48
|
+
depotKind: NameType
|
|
49
|
+
depotZ: number
|
|
50
|
+
shipKind: NameType
|
|
51
|
+
shipZ: number
|
|
52
|
+
cargoMass: number
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Mirrors depot.cpp: the depot's slot-0 loader drives the transfer; 0 when no loader is installed.
|
|
56
|
+
export function depotTransferDuration(params: DepotTransferParams): number {
|
|
57
|
+
const loader = resolveLaneLoader(
|
|
58
|
+
params.depotModules,
|
|
59
|
+
params.depotItemId,
|
|
60
|
+
laneKeyForModule(DEPOT_LOADER_SLOT)
|
|
61
|
+
)
|
|
62
|
+
if (!loader.valid) return 0
|
|
63
|
+
return calc_onesided_duration(
|
|
64
|
+
loader.thrust,
|
|
65
|
+
loader.mass,
|
|
66
|
+
params.shipZ,
|
|
67
|
+
params.depotZ,
|
|
68
|
+
getEntityClass(params.shipKind),
|
|
69
|
+
getEntityClass(params.depotKind),
|
|
70
|
+
params.cargoMass
|
|
71
|
+
)
|
|
36
72
|
}
|
package/src/influence/index.ts
CHANGED
|
@@ -38,26 +38,79 @@ export {
|
|
|
38
38
|
civicLoader,
|
|
39
39
|
contributeDuration,
|
|
40
40
|
contributeDurationForTonnes,
|
|
41
|
+
depotTransferDuration,
|
|
42
|
+
DEPOT_LOADER_SLOT,
|
|
41
43
|
type CivicLoaderStats,
|
|
44
|
+
type DepotTransferParams,
|
|
42
45
|
} from './duration'
|
|
46
|
+
export {
|
|
47
|
+
civicShuttleBays,
|
|
48
|
+
civicTransfersAtCap,
|
|
49
|
+
civicTransfersFrom,
|
|
50
|
+
pendingCivicTransfers,
|
|
51
|
+
selectCivicShuttleBay,
|
|
52
|
+
CIVIC_TRANSFER_PER_PLAYER_CAP,
|
|
53
|
+
type CivicShuttleBay,
|
|
54
|
+
type PendingCivicTransfer,
|
|
55
|
+
} from './civic-shuttle'
|
|
43
56
|
export {citizenryName, citizenryPatternCount} from './citizenry'
|
|
44
57
|
export {
|
|
45
|
-
|
|
46
|
-
charterEffectTargetPresent,
|
|
58
|
+
charterBuildingEntity,
|
|
47
59
|
charterEligible,
|
|
60
|
+
charterEligibleChained,
|
|
61
|
+
charterGateNodeFor,
|
|
48
62
|
charterIneligible,
|
|
49
63
|
charterNode,
|
|
50
64
|
charterPrereqsMet,
|
|
65
|
+
charterRungValue,
|
|
51
66
|
charterSingletonMandate,
|
|
52
|
-
|
|
67
|
+
charterModuleCount,
|
|
53
68
|
CHARTER_INELIGIBILITY_MESSAGES,
|
|
54
69
|
CHARTER_REGISTRY,
|
|
55
|
-
effectiveMandate,
|
|
56
70
|
eligibleCharters,
|
|
57
71
|
type BuiltCharter,
|
|
58
|
-
type
|
|
72
|
+
type CharterGrant,
|
|
59
73
|
type CharterIneligibility,
|
|
60
74
|
type CharterNode,
|
|
61
75
|
type CharterWorld,
|
|
62
|
-
type
|
|
76
|
+
type WorldBuilding,
|
|
63
77
|
} from './charters'
|
|
78
|
+
export {
|
|
79
|
+
projectBallot,
|
|
80
|
+
type BallotInput,
|
|
81
|
+
type BallotProjection,
|
|
82
|
+
type BallotVoter,
|
|
83
|
+
type ProjectedOption,
|
|
84
|
+
type ProjectedSeat,
|
|
85
|
+
} from './ballot'
|
|
86
|
+
export {
|
|
87
|
+
charterMetadata,
|
|
88
|
+
charterName,
|
|
89
|
+
charterSignature,
|
|
90
|
+
charterSummary,
|
|
91
|
+
civicBuildingLabel,
|
|
92
|
+
civicStatLabel,
|
|
93
|
+
romanNumeral,
|
|
94
|
+
type CharterGateSignature,
|
|
95
|
+
type CharterMeta,
|
|
96
|
+
type CharterRungSignature,
|
|
97
|
+
type CharterSignature,
|
|
98
|
+
} from './charter-metadata'
|
|
99
|
+
export {
|
|
100
|
+
CIVIC_STAT_DEFS,
|
|
101
|
+
civicBuildingFor,
|
|
102
|
+
civicBuildingKind,
|
|
103
|
+
civicHullItem,
|
|
104
|
+
civicModuleItemFor,
|
|
105
|
+
civicStatModuleType,
|
|
106
|
+
emptyCivicModules,
|
|
107
|
+
findCivicStatDef,
|
|
108
|
+
fitCivicModules,
|
|
109
|
+
isCivicEntity,
|
|
110
|
+
isCivicKind,
|
|
111
|
+
previewCharterGrants,
|
|
112
|
+
raiseCivicStat,
|
|
113
|
+
type CivicBuildingPreview,
|
|
114
|
+
type CivicStanding,
|
|
115
|
+
type CivicStatDef,
|
|
116
|
+
} from './civic'
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {describe, expect, test} from 'bun:test'
|
|
2
|
-
import {Checksum256} from '@wharfkit/antelope'
|
|
2
|
+
import {Checksum256, UInt8, UInt16, UInt64} from '@wharfkit/antelope'
|
|
3
|
+
import type {ServerContract} from '../contracts'
|
|
3
4
|
import {ITEM_ORE_T1, ITEM_PLATE, ITEM_CRYSTAL_T1} from '../data/item-ids'
|
|
4
5
|
import {categoryIndex} from './categories'
|
|
5
6
|
import {
|
|
@@ -19,7 +20,7 @@ import {
|
|
|
19
20
|
popcount5,
|
|
20
21
|
} from './demand'
|
|
21
22
|
import {findDecomp, DECOMP_REGISTRY} from './decomp'
|
|
22
|
-
import {contributeDuration} from './duration'
|
|
23
|
+
import {depotTransferDuration, contributeDuration} from './duration'
|
|
23
24
|
import {getStatCount, statsSumSq} from './quality'
|
|
24
25
|
import {pricingFromWeights, valueCargoItem, valueContribution} from './valuation'
|
|
25
26
|
import {citizenryName, citizenryPatternCount} from './citizenry'
|
|
@@ -135,7 +136,7 @@ describe('decomposition', () => {
|
|
|
135
136
|
for (let i = 1; i < DECOMP_REGISTRY.length; i++) {
|
|
136
137
|
expect(DECOMP_REGISTRY[i].itemId).toBeGreaterThan(DECOMP_REGISTRY[i - 1].itemId)
|
|
137
138
|
}
|
|
138
|
-
expect(DECOMP_REGISTRY.length).toBe(
|
|
139
|
+
expect(DECOMP_REGISTRY.length).toBe(56)
|
|
139
140
|
})
|
|
140
141
|
|
|
141
142
|
test('the retired Workshop has no decomposition entry', () => {
|
|
@@ -145,7 +146,7 @@ describe('decomposition', () => {
|
|
|
145
146
|
test('buckets are sorted and hold only raw resources', () => {
|
|
146
147
|
const entry = findDecomp(ITEM_PLATE)
|
|
147
148
|
if (!entry) throw new Error('Plate has no decomposition entry')
|
|
148
|
-
expect(entry.
|
|
149
|
+
expect(entry.processedMass).toBeGreaterThan(0)
|
|
149
150
|
for (let i = 1; i < entry.buckets.length; i++) {
|
|
150
151
|
const prev = entry.buckets[i - 1]
|
|
151
152
|
const cur = entry.buckets[i]
|
|
@@ -250,18 +251,18 @@ describe('valuation', () => {
|
|
|
250
251
|
|
|
251
252
|
describe('contribution duration', () => {
|
|
252
253
|
test('grows with mass and never falls below a single tick', () => {
|
|
253
|
-
expect(contributeDuration(
|
|
254
|
-
expect(contributeDuration(
|
|
254
|
+
expect(contributeDuration(10)).toBeGreaterThan(0)
|
|
255
|
+
expect(contributeDuration(10_000)).toBeGreaterThan(contributeDuration(10))
|
|
255
256
|
})
|
|
256
257
|
|
|
257
258
|
test('splitting a haul costs more than one trip', () => {
|
|
258
|
-
const single = contributeDuration(
|
|
259
|
-
const hundred = contributeDuration(
|
|
259
|
+
const single = contributeDuration(1_000_000)
|
|
260
|
+
const hundred = contributeDuration(10_000) * 100
|
|
260
261
|
expect(hundred).toBeGreaterThan(single)
|
|
261
262
|
})
|
|
262
263
|
|
|
263
264
|
test('ground level is floored at the base orbital climb', () => {
|
|
264
|
-
expect(contributeDuration(
|
|
265
|
+
expect(contributeDuration(10, 0)).toBe(contributeDuration(10, 800))
|
|
265
266
|
})
|
|
266
267
|
})
|
|
267
268
|
|
|
@@ -315,3 +316,69 @@ describe('scales', () => {
|
|
|
315
316
|
expect(NEED_FP_SCALE % INFLUENCE_ATOMIC_PER_POINT).toBe(0)
|
|
316
317
|
})
|
|
317
318
|
})
|
|
319
|
+
|
|
320
|
+
describe('depot transfer duration', () => {
|
|
321
|
+
type ModuleEntry = ServerContract.Types.module_entry
|
|
322
|
+
const DEPOT_ITEM = 10219
|
|
323
|
+
const LOADER_T1 = 10103
|
|
324
|
+
|
|
325
|
+
function makeModuleEntry(itemId: number, stats: bigint): ModuleEntry {
|
|
326
|
+
return {
|
|
327
|
+
type: UInt8.from(0),
|
|
328
|
+
installed: {item_id: UInt16.from(itemId), stats: UInt64.from(stats)},
|
|
329
|
+
} as unknown as ModuleEntry
|
|
330
|
+
}
|
|
331
|
+
function emptySlot(): ModuleEntry {
|
|
332
|
+
return {type: UInt8.from(0)} as unknown as ModuleEntry
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// A spawned depot: loader in slot 0, four empty bays after it.
|
|
336
|
+
const depotWithLoader = (): ModuleEntry[] => [
|
|
337
|
+
makeModuleEntry(LOADER_T1, packStats(500)),
|
|
338
|
+
emptySlot(),
|
|
339
|
+
emptySlot(),
|
|
340
|
+
emptySlot(),
|
|
341
|
+
emptySlot(),
|
|
342
|
+
]
|
|
343
|
+
const params = (overrides: Record<string, unknown> = {}) => ({
|
|
344
|
+
depotModules: depotWithLoader(),
|
|
345
|
+
depotItemId: DEPOT_ITEM,
|
|
346
|
+
depotKind: 'depot',
|
|
347
|
+
depotZ: 0,
|
|
348
|
+
shipKind: 'ship',
|
|
349
|
+
shipZ: 0,
|
|
350
|
+
cargoMass: 100,
|
|
351
|
+
...overrides,
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
test('drives off the depot loader, not the ship', () => {
|
|
355
|
+
expect(depotTransferDuration(params())).toBeGreaterThan(0)
|
|
356
|
+
})
|
|
357
|
+
|
|
358
|
+
test('grows with cargo mass', () => {
|
|
359
|
+
expect(depotTransferDuration(params({cargoMass: 10_000}))).toBeGreaterThan(
|
|
360
|
+
depotTransferDuration(params({cargoMass: 100}))
|
|
361
|
+
)
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
test('empty cargo costs nothing', () => {
|
|
365
|
+
expect(depotTransferDuration(params({cargoMass: 0}))).toBe(0)
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
test('a depot with no loader installed cannot transfer', () => {
|
|
369
|
+
expect(depotTransferDuration(params({depotModules: [emptySlot()]}))).toBe(0)
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
test('co-located transfers are floored at the orbital minimum distance', () => {
|
|
373
|
+
// max(planetary 100, orbital 200) = 200, so anything under 200 reads as 200
|
|
374
|
+
const touching = depotTransferDuration(params({shipZ: 0, depotZ: 0}))
|
|
375
|
+
const under = depotTransferDuration(params({shipZ: 150, depotZ: 0}))
|
|
376
|
+
const over = depotTransferDuration(params({shipZ: 400, depotZ: 0}))
|
|
377
|
+
expect(under).toBe(touching)
|
|
378
|
+
expect(over).toBeGreaterThan(touching)
|
|
379
|
+
})
|
|
380
|
+
|
|
381
|
+
test('never reports zero for real cargo', () => {
|
|
382
|
+
expect(depotTransferDuration(params({cargoMass: 1}))).toBeGreaterThanOrEqual(1)
|
|
383
|
+
})
|
|
384
|
+
})
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {getItem} from '../data/catalog'
|
|
2
|
+
import {MASS_UNITS_PER_TONNE} from '../types'
|
|
2
3
|
import {categoryIndex} from './categories'
|
|
3
4
|
import {
|
|
4
5
|
INFLUENCE_ATOMIC_PER_POINT,
|
|
5
|
-
|
|
6
|
+
INFLUENCE_MASS_MAX,
|
|
6
7
|
INFLUENCE_NEED_FP_MAX,
|
|
7
8
|
INFLUENCE_WEIGHT_FP_MAX,
|
|
8
|
-
MASS_KG_PER_TONNE,
|
|
9
9
|
NEED_FP_SCALE,
|
|
10
10
|
WEIGHT_FP_SCALE,
|
|
11
|
-
|
|
11
|
+
W_EFF_ATOMIC_PER_UNIT,
|
|
12
12
|
} from './constants'
|
|
13
13
|
import {findDecomp} from './decomp'
|
|
14
14
|
import type {DemandView} from './demand'
|
|
@@ -29,16 +29,16 @@ export interface ValuedItem {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export function weightedQualityAtomic(
|
|
32
|
-
|
|
32
|
+
weightedMassFp: bigint,
|
|
33
33
|
sumSq: number,
|
|
34
34
|
nStats: number,
|
|
35
35
|
d1: number,
|
|
36
36
|
needFp: bigint
|
|
37
37
|
): bigint {
|
|
38
38
|
if (nStats <= 0 || d1 <= 0) throw new Error('influence: invalid quality divisor')
|
|
39
|
-
const num =
|
|
39
|
+
const num = weightedMassFp * BigInt(sumSq) * needFp
|
|
40
40
|
const den =
|
|
41
|
-
(BigInt(
|
|
41
|
+
(BigInt(MASS_UNITS_PER_TONNE) *
|
|
42
42
|
BigInt(WEIGHT_FP_SCALE) *
|
|
43
43
|
BigInt(nStats) *
|
|
44
44
|
BigInt(d1) *
|
|
@@ -48,34 +48,34 @@ export function weightedQualityAtomic(
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
export function resourceValueAtomic(
|
|
51
|
-
|
|
51
|
+
mass: bigint,
|
|
52
52
|
weightFp: bigint,
|
|
53
53
|
sumSq: number,
|
|
54
54
|
nStats: number,
|
|
55
55
|
d1: number,
|
|
56
56
|
needFp: bigint
|
|
57
57
|
): bigint {
|
|
58
|
-
if (
|
|
58
|
+
if (mass > INFLUENCE_MASS_MAX) throw new Error('influence: mass out of range')
|
|
59
59
|
if (weightFp > INFLUENCE_WEIGHT_FP_MAX) throw new Error('influence: weight out of range')
|
|
60
60
|
if (needFp > INFLUENCE_NEED_FP_MAX) throw new Error('influence: need out of range')
|
|
61
|
-
return weightedQualityAtomic(
|
|
61
|
+
return weightedQualityAtomic(mass * weightFp, sumSq, nStats, d1, needFp)
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
export function componentBaseAtomic(
|
|
65
|
-
|
|
65
|
+
weightedRawMassFp: bigint,
|
|
66
66
|
sumSq: number,
|
|
67
67
|
nStats: number,
|
|
68
68
|
d1: number
|
|
69
69
|
): bigint {
|
|
70
|
-
return weightedQualityAtomic(
|
|
70
|
+
return weightedQualityAtomic(weightedRawMassFp, sumSq, nStats, d1, BigInt(NEED_FP_SCALE))
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
export function componentEffortAtomic(
|
|
74
|
-
const processed = BigInt(
|
|
75
|
-
if (processed >
|
|
73
|
+
export function componentEffortAtomic(processedMassPerUnit: number, quantity: number): bigint {
|
|
74
|
+
const processed = BigInt(processedMassPerUnit) * BigInt(quantity)
|
|
75
|
+
if (processed > INFLUENCE_MASS_MAX) {
|
|
76
76
|
throw new Error('influence: processed mass out of range')
|
|
77
77
|
}
|
|
78
|
-
return processed *
|
|
78
|
+
return processed * W_EFF_ATOMIC_PER_UNIT
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
export function valueCargoItem(
|
|
@@ -114,14 +114,14 @@ export function valueCargoItem(
|
|
|
114
114
|
|
|
115
115
|
let weighted = 0n
|
|
116
116
|
for (const bucket of entry.buckets) {
|
|
117
|
-
weighted += BigInt(bucket.
|
|
117
|
+
weighted += BigInt(bucket.rawMass) * pricing.weight(bucket.category, bucket.tier)
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
const perUnit = componentBaseAtomic(weighted, sumSq, nStats, pricing.d1)
|
|
121
121
|
const total = perUnit * BigInt(item.quantity)
|
|
122
122
|
if (total > UINT64_MAX) throw new Error('influence: component value overflow')
|
|
123
123
|
|
|
124
|
-
const withEffort = total + componentEffortAtomic(entry.
|
|
124
|
+
const withEffort = total + componentEffortAtomic(entry.processedMass, item.quantity)
|
|
125
125
|
if (withEffort > UINT64_MAX) throw new Error('influence: component value overflow')
|
|
126
126
|
return withEffort
|
|
127
127
|
}
|