@shipload/sdk 1.0.0-next.64 → 1.0.0-next.66

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.
Files changed (79) hide show
  1. package/lib/shipload.d.ts +754 -316
  2. package/lib/shipload.js +2650 -1336
  3. package/lib/shipload.js.map +1 -1
  4. package/lib/shipload.m.js +2564 -1308
  5. package/lib/shipload.m.js.map +1 -1
  6. package/lib/testing.d.ts +219 -145
  7. package/lib/testing.js +910 -557
  8. package/lib/testing.js.map +1 -1
  9. package/lib/testing.m.js +910 -557
  10. package/lib/testing.m.js.map +1 -1
  11. package/package.json +1 -1
  12. package/src/capabilities/crafting.test.ts +51 -6
  13. package/src/capabilities/crafting.ts +19 -4
  14. package/src/capabilities/gathering.ts +2 -2
  15. package/src/capabilities/storage.test.ts +26 -0
  16. package/src/capabilities/storage.ts +6 -6
  17. package/src/contracts/fund.ts +110 -5
  18. package/src/contracts/platform.ts +10 -1
  19. package/src/contracts/server.ts +498 -331
  20. package/src/data/charters.json +325 -127
  21. package/src/data/entities.json +24 -24
  22. package/src/data/items.json +114 -114
  23. package/src/data/kind-registry.json +3 -3
  24. package/src/data/kind-registry.ts +5 -0
  25. package/src/data/recipes.json +56 -95
  26. package/src/derivation/build-methods.test.ts +59 -1
  27. package/src/derivation/build-methods.ts +2 -0
  28. package/src/derivation/capabilities.ts +7 -7
  29. package/src/derivation/strata.ts +5 -3
  30. package/src/derivation/tiers.ts +5 -5
  31. package/src/entity/hydrate.test.ts +156 -0
  32. package/src/entity/hydrate.ts +86 -0
  33. package/src/errors.ts +9 -0
  34. package/src/format.ts +15 -13
  35. package/src/index-module.ts +33 -5
  36. package/src/influence/ballot.test.ts +103 -0
  37. package/src/influence/ballot.ts +98 -0
  38. package/src/influence/charter-metadata.test.ts +136 -0
  39. package/src/influence/charter-metadata.ts +236 -0
  40. package/src/influence/charters.test.ts +51 -65
  41. package/src/influence/charters.ts +70 -40
  42. package/src/influence/civic.test.ts +278 -0
  43. package/src/influence/civic.ts +269 -0
  44. package/src/influence/constants.ts +30 -9
  45. package/src/influence/decomp.ts +15 -15
  46. package/src/influence/duration.ts +42 -6
  47. package/src/influence/index.ts +49 -6
  48. package/src/influence/influence.test.ts +76 -9
  49. package/src/influence/valuation.ts +17 -17
  50. package/src/managers/actions.ts +65 -37
  51. package/src/managers/base.ts +4 -0
  52. package/src/managers/context.ts +3 -1
  53. package/src/managers/index.ts +5 -2
  54. package/src/managers/influence.test.ts +18 -20
  55. package/src/managers/influence.ts +162 -84
  56. package/src/managers/jobs.test.ts +2 -2
  57. package/src/managers/jobs.ts +4 -4
  58. package/src/nft/description.ts +10 -11
  59. package/src/planner/index.ts +54 -0
  60. package/src/planner/planner.test.ts +127 -2
  61. package/src/resolution/display-name.ts +1 -1
  62. package/src/scheduling/availability.test.ts +113 -0
  63. package/src/scheduling/availability.ts +48 -8
  64. package/src/scheduling/jobs.ts +1 -1
  65. package/src/scheduling/projection.ts +2 -16
  66. package/src/scheduling/task-cargo.test.ts +62 -1
  67. package/src/scheduling/task-cargo.ts +25 -10
  68. package/src/scheduling/task-effects.test.ts +35 -0
  69. package/src/scheduling/task-effects.ts +34 -0
  70. package/src/scheduling/unwrap.test.ts +3 -3
  71. package/src/scheduling/unwrap.ts +3 -3
  72. package/src/shipload.ts +23 -4
  73. package/src/travel/reach.test.ts +86 -0
  74. package/src/travel/reach.ts +23 -10
  75. package/src/travel/route-build.test.ts +63 -0
  76. package/src/travel/route-build.ts +60 -0
  77. package/src/travel/travel.ts +2 -1
  78. package/src/types.ts +6 -2
  79. package/src/utils/hash.ts +18 -2
@@ -0,0 +1,269 @@
1
+ import {Name, type NameType, UInt16, UInt64, UInt8} from '@wharfkit/antelope'
2
+ import {
3
+ getModuleCapabilityType,
4
+ MODULE_BUILDER,
5
+ MODULE_CRAFTER,
6
+ MODULE_LOADER,
7
+ MODULE_STORAGE,
8
+ moduleSlotTypeToCode,
9
+ } from '../capabilities/modules'
10
+ import {ServerContract} from '../contracts'
11
+ import {
12
+ ITEM_BUILDER_T1,
13
+ ITEM_CONSTRUCTION_DOCK_T1_PACKED,
14
+ ITEM_CRAFTER_T1,
15
+ ITEM_DEPOT_T1_PACKED,
16
+ ITEM_LOADER_T1,
17
+ ITEM_NEXUS_T1_PACKED,
18
+ ITEM_STORAGE_T1,
19
+ ITEM_WORKSHOP_T1_PACKED,
20
+ } from '../data/item-ids'
21
+ import {
22
+ ENTITY_CONSTRUCTION_DOCK,
23
+ ENTITY_DEPOT,
24
+ ENTITY_NEXUS,
25
+ ENTITY_WORKSHOP,
26
+ } from '../data/kind-registry'
27
+ import {getEntityLayout} from '../data/recipes-runtime'
28
+ import {decodeStat, encodeStats} from '../derivation/crafting'
29
+ import type {CharterGrant, CharterNode} from './charters'
30
+ import {
31
+ CIVIC_DEPOT,
32
+ CIVIC_DOCK,
33
+ CIVIC_GRANT_LEVEL_GATE,
34
+ CIVIC_GRANT_MODULE,
35
+ CIVIC_GRANT_RUNG,
36
+ CIVIC_NEXUS,
37
+ CIVIC_SPEED_CAP,
38
+ CIVIC_STAT_BUILD_SPEED,
39
+ CIVIC_STAT_CRAFT_SPEED,
40
+ CIVIC_STAT_STORAGE_CAPACITY,
41
+ CIVIC_STAT_TRANSFER_SPEED,
42
+ CIVIC_WORKSHOP,
43
+ } from './constants'
44
+ import {getStatCount} from './quality'
45
+
46
+ type ModuleEntry = ServerContract.Types.module_entry
47
+
48
+ export interface CivicStatDef {
49
+ building: number
50
+ stat: number
51
+ base: number
52
+ cap: number
53
+ }
54
+
55
+ export const CIVIC_STAT_DEFS: readonly CivicStatDef[] = [
56
+ {building: CIVIC_WORKSHOP, stat: CIVIC_STAT_CRAFT_SPEED, base: 0, cap: CIVIC_SPEED_CAP},
57
+ {building: CIVIC_DOCK, stat: CIVIC_STAT_BUILD_SPEED, base: 0, cap: CIVIC_SPEED_CAP},
58
+ {building: CIVIC_DEPOT, stat: CIVIC_STAT_TRANSFER_SPEED, base: 0, cap: CIVIC_SPEED_CAP},
59
+ {building: CIVIC_DEPOT, stat: CIVIC_STAT_STORAGE_CAPACITY, base: 0, cap: CIVIC_SPEED_CAP},
60
+ ]
61
+
62
+ export function findCivicStatDef(building: number, stat: number): CivicStatDef | undefined {
63
+ return CIVIC_STAT_DEFS.find((def) => def.building === building && def.stat === stat)
64
+ }
65
+
66
+ export function civicHullItem(building: number): number {
67
+ switch (building) {
68
+ case CIVIC_WORKSHOP:
69
+ return ITEM_WORKSHOP_T1_PACKED
70
+ case CIVIC_NEXUS:
71
+ return ITEM_NEXUS_T1_PACKED
72
+ case CIVIC_DOCK:
73
+ return ITEM_CONSTRUCTION_DOCK_T1_PACKED
74
+ case CIVIC_DEPOT:
75
+ return ITEM_DEPOT_T1_PACKED
76
+ default:
77
+ return 0
78
+ }
79
+ }
80
+
81
+ export function civicModuleItemFor(moduleType: number): number {
82
+ switch (moduleType) {
83
+ case MODULE_CRAFTER:
84
+ return ITEM_CRAFTER_T1
85
+ case MODULE_BUILDER:
86
+ return ITEM_BUILDER_T1
87
+ case MODULE_LOADER:
88
+ return ITEM_LOADER_T1
89
+ case MODULE_STORAGE:
90
+ return ITEM_STORAGE_T1
91
+ default:
92
+ return 0
93
+ }
94
+ }
95
+
96
+ export function civicStatModuleType(stat: number): number {
97
+ switch (stat) {
98
+ case CIVIC_STAT_CRAFT_SPEED:
99
+ return MODULE_CRAFTER
100
+ case CIVIC_STAT_BUILD_SPEED:
101
+ return MODULE_BUILDER
102
+ case CIVIC_STAT_TRANSFER_SPEED:
103
+ return MODULE_LOADER
104
+ case CIVIC_STAT_STORAGE_CAPACITY:
105
+ return MODULE_STORAGE
106
+ default:
107
+ return 0
108
+ }
109
+ }
110
+
111
+ export function civicBuildingKind(building: number): Name | undefined {
112
+ switch (building) {
113
+ case CIVIC_WORKSHOP:
114
+ return ENTITY_WORKSHOP
115
+ case CIVIC_NEXUS:
116
+ return ENTITY_NEXUS
117
+ case CIVIC_DOCK:
118
+ return ENTITY_CONSTRUCTION_DOCK
119
+ case CIVIC_DEPOT:
120
+ return ENTITY_DEPOT
121
+ default:
122
+ return undefined
123
+ }
124
+ }
125
+
126
+ export function civicBuildingFor(kind: NameType): number | undefined {
127
+ const name = Name.from(kind)
128
+ if (name.equals(ENTITY_WORKSHOP)) return CIVIC_WORKSHOP
129
+ if (name.equals(ENTITY_NEXUS)) return CIVIC_NEXUS
130
+ if (name.equals(ENTITY_CONSTRUCTION_DOCK)) return CIVIC_DOCK
131
+ if (name.equals(ENTITY_DEPOT)) return CIVIC_DEPOT
132
+ return undefined
133
+ }
134
+
135
+ export function isCivicKind(kind: NameType): boolean {
136
+ return civicBuildingFor(kind) !== undefined
137
+ }
138
+
139
+ export function isCivicEntity(entity: {owner: NameType}, civicOwner: NameType): boolean {
140
+ return Name.from(entity.owner).equals(Name.from(civicOwner))
141
+ }
142
+
143
+ function moduleItemId(module: ServerContract.Types.packed_module): number {
144
+ return Number(module.item_id.value ?? module.item_id)
145
+ }
146
+
147
+ function moduleStats(module: ServerContract.Types.packed_module): bigint {
148
+ return BigInt(module.stats.toString())
149
+ }
150
+
151
+ function packedModule(itemId: number, stats: bigint): ServerContract.Types.packed_module {
152
+ return ServerContract.Types.packed_module.from({
153
+ item_id: UInt16.from(itemId),
154
+ stats: UInt64.from(stats.toString()),
155
+ })
156
+ }
157
+
158
+ function cloneEntry(
159
+ entry: ModuleEntry,
160
+ installed?: ServerContract.Types.packed_module
161
+ ): ModuleEntry {
162
+ return ServerContract.Types.module_entry.from({
163
+ type: UInt8.from(Number(entry.type.value ?? entry.type)),
164
+ installed:
165
+ installed ??
166
+ (entry.installed
167
+ ? packedModule(moduleItemId(entry.installed), moduleStats(entry.installed))
168
+ : undefined),
169
+ })
170
+ }
171
+
172
+ export function emptyCivicModules(building: number): ModuleEntry[] {
173
+ const layout = getEntityLayout(civicHullItem(building))?.slots ?? []
174
+ return layout.map((slot) =>
175
+ ServerContract.Types.module_entry.from({type: UInt8.from(moduleSlotTypeToCode(slot.type))})
176
+ )
177
+ }
178
+
179
+ export function fitCivicModules(modules: ModuleEntry[], count: number): ModuleEntry[] {
180
+ const next = modules.map((entry) => cloneEntry(entry))
181
+ let remaining = count
182
+ for (let i = 0; i < next.length && remaining > 0; i++) {
183
+ if (next[i].installed) continue
184
+ const type = Number(next[i].type.value ?? next[i].type)
185
+ const item = civicModuleItemFor(type)
186
+ if (item === 0) throw new Error('charter grant found no open slot of its module type')
187
+ let stats = encodeStats(new Array(getStatCount(item)).fill(0))
188
+ for (const sibling of next) {
189
+ if (
190
+ sibling.installed &&
191
+ getModuleCapabilityType(moduleItemId(sibling.installed)) === type
192
+ ) {
193
+ stats = moduleStats(sibling.installed)
194
+ break
195
+ }
196
+ }
197
+ next[i] = cloneEntry(next[i], packedModule(item, stats))
198
+ remaining--
199
+ }
200
+ if (remaining > 0) throw new Error('charter grant found no open slot of its module type')
201
+ return next
202
+ }
203
+
204
+ export function raiseCivicStat(modules: ModuleEntry[], grant: CharterGrant): ModuleEntry[] {
205
+ const def = findCivicStatDef(grant.building, grant.stat)
206
+ if (!def) throw new Error(`no civic stat ${grant.stat} on building ${grant.building}`)
207
+ const type = civicStatModuleType(grant.stat)
208
+ let raised = 0
209
+ const next = modules.map((entry) => {
210
+ if (!entry.installed || getModuleCapabilityType(moduleItemId(entry.installed)) !== type) {
211
+ return cloneEntry(entry)
212
+ }
213
+ const item = moduleItemId(entry.installed)
214
+ const stats = moduleStats(entry.installed)
215
+ const values: number[] = []
216
+ for (let c = 0; c < getStatCount(item); c++) {
217
+ values.push(Math.min(decodeStat(stats, c) + grant.value, def.cap))
218
+ }
219
+ raised++
220
+ return cloneEntry(entry, packedModule(item, encodeStats(values)))
221
+ })
222
+ if (raised === 0) throw new Error('charter rung found no module carrying its stat')
223
+ return next
224
+ }
225
+
226
+ export interface CivicBuildingPreview {
227
+ building: number
228
+ itemId: number
229
+ spawned: boolean
230
+ modules: ModuleEntry[]
231
+ }
232
+
233
+ export type CivicStanding = Partial<Record<number, ModuleEntry[]>>
234
+
235
+ export function previewCharterGrants(
236
+ node: CharterNode,
237
+ standing: CivicStanding = {}
238
+ ): CivicBuildingPreview[] {
239
+ const previews = new Map<number, CivicBuildingPreview>()
240
+ const target = (building: number, spawn: boolean): CivicBuildingPreview => {
241
+ let preview = previews.get(building)
242
+ if (preview) return preview
243
+ const current = standing[building]
244
+ if (!current && !spawn)
245
+ throw new Error('charter grant targets a building that is not standing')
246
+ preview = {
247
+ building,
248
+ itemId: civicHullItem(building),
249
+ spawned: !current,
250
+ modules: current
251
+ ? current.map((entry) => cloneEntry(entry))
252
+ : emptyCivicModules(building),
253
+ }
254
+ previews.set(building, preview)
255
+ return preview
256
+ }
257
+ for (const grant of node.grants) {
258
+ if (grant.kind === CIVIC_GRANT_LEVEL_GATE) {
259
+ target(grant.building, true)
260
+ } else if (grant.kind === CIVIC_GRANT_MODULE) {
261
+ const preview = target(grant.building, false)
262
+ preview.modules = fitCivicModules(preview.modules, grant.value)
263
+ } else if (grant.kind === CIVIC_GRANT_RUNG) {
264
+ const preview = target(grant.building, false)
265
+ preview.modules = raiseCivicStat(preview.modules, grant)
266
+ }
267
+ }
268
+ return [...previews.values()]
269
+ }
@@ -1,22 +1,23 @@
1
+ import {MASS_UNITS_PER_TONNE} from '../types'
2
+
1
3
  export const INFLUENCE_ATOMIC_PER_POINT = 10_000
2
4
  export const NEED_FP_SCALE = 10_000
3
5
  export const WEIGHT_FP_SCALE = 10_000
4
- export const MASS_KG_PER_TONNE = 1_000
5
6
 
6
7
  export const INFLUENCE_WEIGHT_FP_MAX = 1_000_000n
7
8
  export const INFLUENCE_NEED_FP_MAX = 1_000_000n
8
- export const INFLUENCE_MASS_KG_MAX = 1n << 48n
9
+ export const INFLUENCE_MASS_MAX = 1n << 48n
9
10
 
10
11
  export const DECAY_RETAIN_NUM = 19n
11
12
  export const DECAY_RETAIN_DEN = 20n
12
13
  export const DECAY_MAX_ITERATIONS = 1_024
13
14
 
14
- export const W_EFF_ATOMIC_PER_KG = BigInt(INFLUENCE_ATOMIC_PER_POINT / MASS_KG_PER_TONNE)
15
+ export const W_EFF_ATOMIC_PER_UNIT = BigInt(INFLUENCE_ATOMIC_PER_POINT / MASS_UNITS_PER_TONNE)
15
16
 
16
17
  export const DECOMP_MAX_BUCKETS = 12
17
18
  export const DECOMP_MAX_DEPTH = 8
18
- export const DECOMP_RAW_KG_MAX = 100_000_000
19
- export const DECOMP_PROCESSED_KG_MAX = 100_000_000
19
+ export const DECOMP_RAW_MASS_MAX = 1_000_000
20
+ export const DECOMP_PROCESSED_MASS_MAX = 1_000_000
20
21
 
21
22
  export const RESOURCE_ORE = 0
22
23
  export const RESOURCE_GAS = 1
@@ -39,13 +40,33 @@ export const BAR_STEP_UP = BAR_STEP_UNIT * 9
39
40
  export const BAR_STEP_DOWN = BAR_STEP_UNIT
40
41
 
41
42
  export const CHARTER_NONE = 0
42
- export const CHARTER_WORKSHOP = 1
43
- export const CHARTER_EFFECT_SPAWN_ENTITY = 1
44
- export const CHARTER_EFFECT_REFIT_MODULES = 2
43
+ export const CHARTER_WORKSHOP = 10001
44
+ export const CIVIC_GRANT_NONE = 0
45
+ export const CIVIC_GRANT_MODULE = 1
46
+ export const CIVIC_GRANT_RUNG = 2
47
+ export const CIVIC_GRANT_LEVEL_GATE = 3
48
+ export const CIVIC_GRANT_ADVANCEMENT = 4
49
+
50
+ export const CIVIC_WORLD = 0
51
+ export const CIVIC_WORKSHOP = 1
52
+ export const CIVIC_NEXUS = 2
53
+ export const CIVIC_DOCK = 3
54
+ export const CIVIC_DEPOT = 4
55
+
56
+ export const CIVIC_STAT_NONE = 0
57
+ export const CIVIC_STAT_CRAFT_SPEED = 1
58
+ export const CIVIC_STAT_BUILD_SPEED = 2
59
+ export const CIVIC_STAT_TRANSFER_SPEED = 3
60
+ export const CIVIC_STAT_STORAGE_CAPACITY = 4
45
61
  export const VOTE_READY_DEFAULT_MAX = 20
62
+ export const BALLOT_CHARTER = 1
63
+ export const BALLOT_MAX_PICKS = 8
46
64
  export const CHARTER_WORKSHOP_COST = 2_000_000_000n
47
- export const CHARTER_BASELINE_STAT = 213
65
+ export const CHARTER_BASELINE_STAT = 200
66
+ export const CHARTER_RUNG_STEP = 50
67
+ export const CIVIC_SPEED_CAP = 999
48
68
  export const CHARTER_MAX_PREREQS = 4
69
+ export const CHARTER_MAX_GRANTS = 4
49
70
 
50
71
  export const INFLUENCE_D1_SEED = 45_369
51
72
  export const DEMAND_TRIPLE_SEED = {peak: 20_000n, base: 10_000n, floor: 1_000n}
@@ -4,40 +4,40 @@ import recipes from '../data/recipes.json'
4
4
  import {
5
5
  DECOMP_MAX_BUCKETS,
6
6
  DECOMP_MAX_DEPTH,
7
- DECOMP_PROCESSED_KG_MAX,
8
- DECOMP_RAW_KG_MAX,
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
- rawKg: number
15
+ rawMass: number
16
16
  }
17
17
 
18
18
  export interface DecompEntry {
19
19
  itemId: number
20
- processedKg: number
20
+ processedMass: number
21
21
  buckets: DecompBucket[]
22
22
  }
23
23
 
24
24
  interface Accumulator {
25
25
  buckets: DecompBucket[]
26
- processedKg: number
26
+ processedMass: number
27
27
  overflow: boolean
28
28
  }
29
29
 
30
- function add(acc: Accumulator, category: number, tier: number, kg: number): void {
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.rawKg += kg
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, rawKg: kg})
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 kg = units * (def ? def.mass : 0)
55
- acc.processedKg += kg
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, kg)
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: [], processedKg: 0, overflow: false}
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.rawKg, 0)
74
- if (rawTotal > DECOMP_RAW_KG_MAX || acc.processedKg > DECOMP_PROCESSED_KG_MAX) {
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
- processedKg: acc.overflow ? 0 : acc.processedKg,
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 {PRECISION} from '../types'
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(totalMassKg: number, altitudeZ = 0): number {
32
+ export function contributeDuration(totalMass: number, altitudeZ = 0): number {
28
33
  const loader = civicLoader()
29
- const totalMass = totalMassKg + loader.mass
34
+ const mass = totalMass + loader.mass
30
35
  const z = Math.max(altitudeZ, DEFAULT_ORBITAL_Z)
31
- return flightTime(z, acceleration(loader.thrust, totalMass))
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 * 1000), altitudeZ)
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
  }
@@ -38,26 +38,69 @@ 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'
43
46
  export {citizenryName, citizenryPatternCount} from './citizenry'
44
47
  export {
45
- charterEffectTargetEntity,
46
- charterEffectTargetPresent,
48
+ charterBuildingEntity,
47
49
  charterEligible,
50
+ charterEligibleChained,
51
+ charterGateNodeFor,
48
52
  charterIneligible,
49
53
  charterNode,
50
54
  charterPrereqsMet,
55
+ charterRungValue,
51
56
  charterSingletonMandate,
52
- charterSpawnNodeFor,
57
+ charterModuleCount,
53
58
  CHARTER_INELIGIBILITY_MESSAGES,
54
59
  CHARTER_REGISTRY,
55
- effectiveMandate,
56
60
  eligibleCharters,
57
61
  type BuiltCharter,
58
- type CharterEffect,
62
+ type CharterGrant,
59
63
  type CharterIneligibility,
60
64
  type CharterNode,
61
65
  type CharterWorld,
62
- type ChosenCharter,
66
+ type WorldBuilding,
63
67
  } from './charters'
68
+ export {
69
+ projectBallot,
70
+ type BallotInput,
71
+ type BallotProjection,
72
+ type BallotVoter,
73
+ type ProjectedOption,
74
+ type ProjectedSeat,
75
+ } from './ballot'
76
+ export {
77
+ charterMetadata,
78
+ charterName,
79
+ charterSignature,
80
+ charterSummary,
81
+ civicBuildingLabel,
82
+ civicStatLabel,
83
+ romanNumeral,
84
+ type CharterGateSignature,
85
+ type CharterMeta,
86
+ type CharterRungSignature,
87
+ type CharterSignature,
88
+ } from './charter-metadata'
89
+ export {
90
+ CIVIC_STAT_DEFS,
91
+ civicBuildingFor,
92
+ civicBuildingKind,
93
+ civicHullItem,
94
+ civicModuleItemFor,
95
+ civicStatModuleType,
96
+ emptyCivicModules,
97
+ findCivicStatDef,
98
+ fitCivicModules,
99
+ isCivicEntity,
100
+ isCivicKind,
101
+ previewCharterGrants,
102
+ raiseCivicStat,
103
+ type CivicBuildingPreview,
104
+ type CivicStanding,
105
+ type CivicStatDef,
106
+ } from './civic'