@shipload/sdk 0.7.1 → 2.0.0-rc1
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 +1651 -226
- package/lib/shipload.js +4958 -1971
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +4787 -1940
- package/lib/shipload.m.js.map +1 -1
- package/package.json +5 -4
- package/src/contracts/server.ts +585 -203
- package/src/data/goods.json +23 -0
- package/src/data/syllables.json +1184 -0
- package/src/entities/cargo-utils.ts +47 -0
- package/src/entities/entity-inventory.ts +39 -0
- package/src/entities/gamestate.ts +152 -0
- package/src/entities/location.ts +241 -0
- package/src/entities/player.ts +287 -0
- package/src/entities/ship.ts +559 -0
- package/src/entities/warehouse.ts +205 -0
- package/src/errors.ts +46 -9
- package/src/index-module.ts +119 -7
- package/src/managers/actions.ts +168 -0
- package/src/managers/base.ts +25 -0
- package/src/managers/context.ts +104 -0
- package/src/managers/entities.ts +86 -0
- package/src/managers/epochs.ts +47 -0
- package/src/managers/index.ts +9 -0
- package/src/managers/locations.ts +103 -0
- package/src/managers/players.ts +13 -0
- package/src/managers/trades.ts +119 -0
- package/src/market/goods.ts +31 -0
- package/src/{market.ts → market/market.ts} +32 -37
- package/src/{rolls.ts → market/rolls.ts} +3 -3
- package/src/{epoch.ts → scheduling/epoch.ts} +1 -1
- package/src/scheduling/projection.ts +218 -0
- package/src/scheduling/schedule.ts +155 -0
- package/src/shipload.ts +39 -157
- package/src/trading/collect.ts +939 -0
- package/src/trading/deal.ts +208 -0
- package/src/trading/trade.ts +203 -0
- package/src/travel/travel.ts +425 -0
- package/src/types.ts +60 -25
- package/src/utils/system.ts +27 -0
- package/src/goods.ts +0 -124
- package/src/ship.ts +0 -36
- package/src/state.ts +0 -0
- package/src/syllables.ts +0 -1184
- package/src/system.ts +0 -37
- package/src/travel.ts +0 -259
- /package/src/{hash.ts → utils/hash.ts} +0 -0
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
import {Name, NameType, UInt16, UInt32, UInt64, UInt64Type} from '@wharfkit/antelope'
|
|
2
|
+
import {ServerContract} from '../contracts'
|
|
3
|
+
import {Coordinates, CoordinatesType, PRECISION, TaskType} from '../types'
|
|
4
|
+
import {distanceBetweenCoordinates, lerp} from '../travel/travel'
|
|
5
|
+
import {Location} from './location'
|
|
6
|
+
import {getGood} from '../market/goods'
|
|
7
|
+
import * as schedule from '../scheduling/schedule'
|
|
8
|
+
import {Scheduleable} from '../scheduling/schedule'
|
|
9
|
+
import {EntityInventory} from './entity-inventory'
|
|
10
|
+
import {ProjectedEntity} from '../scheduling/projection'
|
|
11
|
+
|
|
12
|
+
export interface ShipStateInput {
|
|
13
|
+
id: UInt64Type
|
|
14
|
+
owner: NameType
|
|
15
|
+
name: string
|
|
16
|
+
location: CoordinatesType | {x: number; y: number; z?: number}
|
|
17
|
+
mass: number
|
|
18
|
+
capacity: number
|
|
19
|
+
energy: number
|
|
20
|
+
engines: ServerContract.Types.movement_stats
|
|
21
|
+
generator: ServerContract.Types.energy_stats
|
|
22
|
+
loaders: ServerContract.Types.loader_stats
|
|
23
|
+
schedule?: ServerContract.Types.schedule
|
|
24
|
+
cargo?: ServerContract.Types.cargo_item[]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class Ship extends ServerContract.Types.entity_info implements Scheduleable {
|
|
28
|
+
static fromState(state: ShipStateInput): Ship {
|
|
29
|
+
const entityInfo = ServerContract.Types.entity_info.from({
|
|
30
|
+
type: Name.from('ship'),
|
|
31
|
+
id: UInt64.from(state.id),
|
|
32
|
+
owner: Name.from(state.owner),
|
|
33
|
+
entity_name: state.name,
|
|
34
|
+
location: ServerContract.Types.coordinates.from(state.location),
|
|
35
|
+
mass: UInt32.from(state.mass),
|
|
36
|
+
capacity: UInt32.from(state.capacity),
|
|
37
|
+
energy: UInt16.from(state.energy),
|
|
38
|
+
cargomass: UInt32.from(0),
|
|
39
|
+
cargo: state.cargo || [],
|
|
40
|
+
is_idle: !state.schedule,
|
|
41
|
+
current_task_elapsed: UInt32.from(0),
|
|
42
|
+
current_task_remaining: UInt32.from(0),
|
|
43
|
+
pending_tasks: [],
|
|
44
|
+
engines: state.engines,
|
|
45
|
+
generator: state.generator,
|
|
46
|
+
loaders: state.loaders,
|
|
47
|
+
schedule: state.schedule,
|
|
48
|
+
})
|
|
49
|
+
return new Ship(entityInfo)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private _location?: Location
|
|
53
|
+
private _inventory?: EntityInventory[]
|
|
54
|
+
|
|
55
|
+
get name(): string {
|
|
56
|
+
return this.entity_name
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get inventory(): EntityInventory[] {
|
|
60
|
+
if (!this._inventory) {
|
|
61
|
+
this._inventory = this.cargo.map((item) => new EntityInventory(item))
|
|
62
|
+
}
|
|
63
|
+
return this._inventory
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get maxDistance(): UInt32 {
|
|
67
|
+
if (!this.generator || !this.engines) return UInt32.from(0)
|
|
68
|
+
return UInt32.from(this.generator.capacity)
|
|
69
|
+
.dividing(this.engines.drain)
|
|
70
|
+
.multiplying(PRECISION)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get hasSchedule(): boolean {
|
|
74
|
+
return schedule.hasSchedule(this)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
get isIdle(): boolean {
|
|
78
|
+
return this.is_idle
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
get tasks(): ServerContract.Types.task[] {
|
|
82
|
+
return schedule.getTasks(this)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
scheduleDuration(): number {
|
|
86
|
+
return schedule.scheduleDuration(this)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
scheduleElapsed(now: Date): number {
|
|
90
|
+
return schedule.scheduleElapsed(this, now)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
scheduleRemaining(now: Date): number {
|
|
94
|
+
return schedule.scheduleRemaining(this, now)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
scheduleComplete(now: Date): boolean {
|
|
98
|
+
return schedule.scheduleComplete(this, now)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
currentTaskIndex(now: Date): number {
|
|
102
|
+
return schedule.currentTaskIndex(this, now)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
currentTask(now: Date): ServerContract.Types.task | undefined {
|
|
106
|
+
return schedule.currentTask(this, now)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
currentTaskType(now: Date): TaskType | undefined {
|
|
110
|
+
return schedule.currentTaskType(this, now)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
getTaskStartTime(index: number): number {
|
|
114
|
+
return schedule.getTaskStartTime(this, index)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
getTaskElapsed(index: number, now: Date): number {
|
|
118
|
+
return schedule.getTaskElapsed(this, index, now)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
getTaskRemaining(index: number, now: Date): number {
|
|
122
|
+
return schedule.getTaskRemaining(this, index, now)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
isTaskComplete(index: number, now: Date): boolean {
|
|
126
|
+
return schedule.isTaskComplete(this, index, now)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
isTaskInProgress(index: number, now: Date): boolean {
|
|
130
|
+
return schedule.isTaskInProgress(this, index, now)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
currentTaskProgress(now: Date): number {
|
|
134
|
+
return schedule.currentTaskProgress(this, now)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
scheduleProgress(now: Date): number {
|
|
138
|
+
return schedule.scheduleProgress(this, now)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
getFlightOrigin(flightTaskIndex: number): Coordinates {
|
|
142
|
+
if (!this.schedule) return this.location
|
|
143
|
+
|
|
144
|
+
let origin: Coordinates = this.location
|
|
145
|
+
for (let i = 0; i < flightTaskIndex && i < this.schedule.tasks.length; i++) {
|
|
146
|
+
const task = this.schedule.tasks[i]
|
|
147
|
+
if (task.type.equals(TaskType.FLIGHT) && task.location) {
|
|
148
|
+
origin = task.location
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return origin
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
destinationLocation(): Coordinates | undefined {
|
|
155
|
+
if (!this.schedule) return undefined
|
|
156
|
+
|
|
157
|
+
for (let i = this.schedule.tasks.length - 1; i >= 0; i--) {
|
|
158
|
+
const task = this.schedule.tasks[i]
|
|
159
|
+
if (task.type.equals(TaskType.FLIGHT) && task.location) {
|
|
160
|
+
return task.location
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return undefined
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
positionAt(now: Date): Coordinates {
|
|
167
|
+
if (!this.schedule || this.schedule.tasks.length === 0) {
|
|
168
|
+
return this.location
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const taskIndex = this.currentTaskIndex(now)
|
|
172
|
+
if (taskIndex < 0) return this.location
|
|
173
|
+
|
|
174
|
+
const task = this.schedule.tasks[taskIndex]
|
|
175
|
+
|
|
176
|
+
if (!task.type.equals(TaskType.FLIGHT) || !task.location) {
|
|
177
|
+
return this.getFlightOrigin(taskIndex)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const origin = this.getFlightOrigin(taskIndex)
|
|
181
|
+
const destination = task.location
|
|
182
|
+
const progress = this.currentTaskProgress(now)
|
|
183
|
+
|
|
184
|
+
const interpolated = lerp(origin, destination, progress)
|
|
185
|
+
return Coordinates.from({
|
|
186
|
+
x: Math.round(interpolated.x),
|
|
187
|
+
y: Math.round(interpolated.y),
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
isInFlight(now: Date): boolean {
|
|
192
|
+
const taskType = this.currentTaskType(now)
|
|
193
|
+
return taskType === TaskType.FLIGHT
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
isRecharging(now: Date): boolean {
|
|
197
|
+
const taskType = this.currentTaskType(now)
|
|
198
|
+
return taskType === TaskType.RECHARGE
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
isLoading(now: Date): boolean {
|
|
202
|
+
const taskType = this.currentTaskType(now)
|
|
203
|
+
return taskType === TaskType.LOAD
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
isUnloading(now: Date): boolean {
|
|
207
|
+
const taskType = this.currentTaskType(now)
|
|
208
|
+
return taskType === TaskType.UNLOAD
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
calcCargoMass(): UInt64 {
|
|
212
|
+
let mass = UInt64.from(0)
|
|
213
|
+
for (const item of this.cargo) {
|
|
214
|
+
const good = getGood(item.good_id)
|
|
215
|
+
mass = mass.adding(good.mass.multiplying(item.quantity))
|
|
216
|
+
}
|
|
217
|
+
return mass
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
private createProjectedEntity(cargoMass: UInt64): ProjectedEntity {
|
|
221
|
+
const shipMass = UInt32.from(this.mass)
|
|
222
|
+
const loaders = this.loaders
|
|
223
|
+
return {
|
|
224
|
+
location: Coordinates.from(this.location),
|
|
225
|
+
energy: UInt16.from(this.energy),
|
|
226
|
+
cargoMass,
|
|
227
|
+
shipMass,
|
|
228
|
+
engines: this.engines,
|
|
229
|
+
generator: this.generator,
|
|
230
|
+
loaders,
|
|
231
|
+
get totalMass() {
|
|
232
|
+
let mass = UInt64.from(this.shipMass).adding(this.cargoMass)
|
|
233
|
+
if (this.loaders) {
|
|
234
|
+
mass = mass.adding(this.loaders.mass.multiplying(this.loaders.quantity))
|
|
235
|
+
}
|
|
236
|
+
return mass
|
|
237
|
+
},
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
project(): ProjectedEntity {
|
|
242
|
+
const cargoMass = this.calcCargoMass()
|
|
243
|
+
const projected = this.createProjectedEntity(cargoMass)
|
|
244
|
+
|
|
245
|
+
if (!this.schedule) {
|
|
246
|
+
return projected
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
for (const task of this.schedule.tasks) {
|
|
250
|
+
switch (task.type.toNumber()) {
|
|
251
|
+
case TaskType.RECHARGE:
|
|
252
|
+
if (projected.generator) {
|
|
253
|
+
projected.energy = UInt16.from(projected.generator.capacity)
|
|
254
|
+
}
|
|
255
|
+
break
|
|
256
|
+
case TaskType.FLIGHT: {
|
|
257
|
+
if (task.location) {
|
|
258
|
+
const distance = distanceBetweenCoordinates(
|
|
259
|
+
projected.location,
|
|
260
|
+
task.location
|
|
261
|
+
)
|
|
262
|
+
if (projected.engines) {
|
|
263
|
+
const energyUsage = distance
|
|
264
|
+
.dividing(PRECISION)
|
|
265
|
+
.multiplying(projected.engines.drain)
|
|
266
|
+
projected.energy = projected.energy.gt(energyUsage)
|
|
267
|
+
? UInt16.from(projected.energy.subtracting(energyUsage))
|
|
268
|
+
: UInt16.from(0)
|
|
269
|
+
}
|
|
270
|
+
projected.location = Coordinates.from(task.location)
|
|
271
|
+
}
|
|
272
|
+
break
|
|
273
|
+
}
|
|
274
|
+
case TaskType.LOAD:
|
|
275
|
+
for (const item of task.cargo) {
|
|
276
|
+
const good = getGood(item.good_id)
|
|
277
|
+
projected.cargoMass = projected.cargoMass.adding(
|
|
278
|
+
good.mass.multiplying(item.quantity)
|
|
279
|
+
)
|
|
280
|
+
}
|
|
281
|
+
break
|
|
282
|
+
case TaskType.UNLOAD:
|
|
283
|
+
for (const item of task.cargo) {
|
|
284
|
+
const good = getGood(item.good_id)
|
|
285
|
+
const cargoMass = good.mass.multiplying(item.quantity)
|
|
286
|
+
projected.cargoMass = projected.cargoMass.gt(cargoMass)
|
|
287
|
+
? projected.cargoMass.subtracting(cargoMass)
|
|
288
|
+
: UInt64.from(0)
|
|
289
|
+
}
|
|
290
|
+
break
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return projected
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
projectAt(now: Date): ProjectedEntity {
|
|
298
|
+
const cargoMass = this.calcCargoMass()
|
|
299
|
+
const projected = this.createProjectedEntity(cargoMass)
|
|
300
|
+
|
|
301
|
+
if (!this.schedule || this.schedule.tasks.length === 0) {
|
|
302
|
+
return projected
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
for (let i = 0; i < this.schedule.tasks.length; i++) {
|
|
306
|
+
const task = this.schedule.tasks[i]
|
|
307
|
+
const taskComplete = this.isTaskComplete(i, now)
|
|
308
|
+
const taskInProgress = this.isTaskInProgress(i, now)
|
|
309
|
+
|
|
310
|
+
if (!taskComplete && !taskInProgress) {
|
|
311
|
+
break
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
switch (task.type.toNumber()) {
|
|
315
|
+
case TaskType.RECHARGE:
|
|
316
|
+
if (projected.generator) {
|
|
317
|
+
if (taskComplete) {
|
|
318
|
+
projected.energy = UInt16.from(projected.generator.capacity)
|
|
319
|
+
} else if (taskInProgress) {
|
|
320
|
+
const progress = this.getTaskElapsed(i, now) / task.duration.toNumber()
|
|
321
|
+
const capacity = Number(projected.generator.capacity)
|
|
322
|
+
const currentEnergy = Number(projected.energy)
|
|
323
|
+
const rechargeAmount = (capacity - currentEnergy) * progress
|
|
324
|
+
projected.energy = UInt16.from(
|
|
325
|
+
Math.min(capacity, currentEnergy + rechargeAmount)
|
|
326
|
+
)
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
break
|
|
330
|
+
case TaskType.FLIGHT: {
|
|
331
|
+
if (task.location && projected.engines) {
|
|
332
|
+
const origin = projected.location
|
|
333
|
+
const destination = Coordinates.from(task.location)
|
|
334
|
+
const distance = distanceBetweenCoordinates(origin, task.location)
|
|
335
|
+
const energyUsage = distance
|
|
336
|
+
.dividing(PRECISION)
|
|
337
|
+
.multiplying(projected.engines.drain)
|
|
338
|
+
|
|
339
|
+
if (taskComplete) {
|
|
340
|
+
projected.energy = projected.energy.gt(energyUsage)
|
|
341
|
+
? UInt16.from(projected.energy.subtracting(energyUsage))
|
|
342
|
+
: UInt16.from(0)
|
|
343
|
+
projected.location = destination
|
|
344
|
+
} else if (taskInProgress) {
|
|
345
|
+
const progress = this.getTaskElapsed(i, now) / task.duration.toNumber()
|
|
346
|
+
const interpolated = lerp(origin, destination, progress)
|
|
347
|
+
projected.location = Coordinates.from({
|
|
348
|
+
x: Math.round(interpolated.x),
|
|
349
|
+
y: Math.round(interpolated.y),
|
|
350
|
+
})
|
|
351
|
+
const partialEnergy = UInt64.from(
|
|
352
|
+
Math.floor(Number(energyUsage) * progress)
|
|
353
|
+
)
|
|
354
|
+
projected.energy = projected.energy.gt(partialEnergy)
|
|
355
|
+
? UInt16.from(projected.energy.subtracting(partialEnergy))
|
|
356
|
+
: UInt16.from(0)
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
break
|
|
360
|
+
}
|
|
361
|
+
case TaskType.LOAD:
|
|
362
|
+
if (taskComplete) {
|
|
363
|
+
for (const item of task.cargo) {
|
|
364
|
+
const good = getGood(item.good_id)
|
|
365
|
+
projected.cargoMass = projected.cargoMass.adding(
|
|
366
|
+
good.mass.multiplying(item.quantity)
|
|
367
|
+
)
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
break
|
|
371
|
+
case TaskType.UNLOAD:
|
|
372
|
+
if (taskComplete) {
|
|
373
|
+
for (const item of task.cargo) {
|
|
374
|
+
const good = getGood(item.good_id)
|
|
375
|
+
const cargoMass = good.mass.multiplying(item.quantity)
|
|
376
|
+
projected.cargoMass = projected.cargoMass.gt(cargoMass)
|
|
377
|
+
? projected.cargoMass.subtracting(cargoMass)
|
|
378
|
+
: UInt64.from(0)
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
break
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return projected
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
get currentLocation(): Coordinates {
|
|
389
|
+
return this.location
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
get totalCargoMass(): UInt64 {
|
|
393
|
+
return this.inventory.reduce((sum, c) => sum.adding(c.totalMass), UInt64.from(0))
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
get cargoValue(): UInt64 {
|
|
397
|
+
return this.inventory.reduce((sum, c) => sum.adding(c.totalCost), UInt64.from(0))
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
get totalMass(): UInt64 {
|
|
401
|
+
const cargoMass = this.totalCargoMass
|
|
402
|
+
let mass = UInt64.from(this.mass ?? 0).adding(cargoMass)
|
|
403
|
+
if (this.loaders) {
|
|
404
|
+
mass = mass.adding(UInt64.from(this.loaders.mass).multiplying(this.loaders.quantity))
|
|
405
|
+
}
|
|
406
|
+
return mass
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
get maxCapacity(): UInt64 {
|
|
410
|
+
return UInt64.from(this.capacity)
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
hasSpace(goodMass: UInt64, quantity: number): boolean {
|
|
414
|
+
const additionalMass = goodMass.multiplying(quantity)
|
|
415
|
+
const newTotal = this.totalMass.adding(additionalMass)
|
|
416
|
+
return newTotal.lte(this.maxCapacity)
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
get availableCapacity(): UInt64 {
|
|
420
|
+
if (this.totalMass.gte(this.maxCapacity)) {
|
|
421
|
+
return UInt64.from(0)
|
|
422
|
+
}
|
|
423
|
+
return this.maxCapacity.subtracting(this.totalMass)
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
get locationObject(): Location {
|
|
427
|
+
if (!this._location) {
|
|
428
|
+
this._location = Location.from(this.location)
|
|
429
|
+
}
|
|
430
|
+
return this._location
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
setLocation(location: Location): void {
|
|
434
|
+
this._location = location
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
getCargoForGood(goodId: UInt64Type): EntityInventory | undefined {
|
|
438
|
+
return this.inventory.find((c) => c.good_id.equals(goodId))
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
get sellableCargo(): EntityInventory[] {
|
|
442
|
+
return this.inventory.filter((c) => c.hasCargo)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
get hasSellableCargo(): boolean {
|
|
446
|
+
return this.inventory.some((c) => c.hasCargo)
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
get sellableGoodsCount(): number {
|
|
450
|
+
return this.inventory.filter((c) => c.hasCargo).length
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
get isFull(): boolean {
|
|
454
|
+
return this.totalMass.gte(this.maxCapacity)
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
get energyPercent(): number {
|
|
458
|
+
if (!this.generator) return 0
|
|
459
|
+
return (Number(this.energy ?? 0) / Number(this.generator.capacity)) * 100
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
get needsRecharge(): boolean {
|
|
463
|
+
if (!this.generator) return false
|
|
464
|
+
return UInt64.from(this.energy ?? 0).lt(this.generator.capacity)
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
hasEnergyFor(distance: UInt64): boolean {
|
|
468
|
+
if (!this.engines) return false
|
|
469
|
+
const energyNeeded = distance.dividing(PRECISION).multiplying(this.engines.drain)
|
|
470
|
+
return UInt64.from(this.energy ?? 0).gte(energyNeeded)
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
calculateSaleValue(prices: Map<number, UInt64>): {
|
|
474
|
+
revenue: UInt64
|
|
475
|
+
profit: UInt64
|
|
476
|
+
cost: UInt64
|
|
477
|
+
} {
|
|
478
|
+
if (this.cargo.length === 0) {
|
|
479
|
+
return {revenue: UInt64.from(0), profit: UInt64.from(0), cost: UInt64.from(0)}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
let revenue = UInt64.from(0)
|
|
483
|
+
let cost = UInt64.from(0)
|
|
484
|
+
|
|
485
|
+
for (const item of this.cargo) {
|
|
486
|
+
if (UInt32.from(item.quantity).equals(UInt32.from(0))) continue
|
|
487
|
+
|
|
488
|
+
const goodId = Number(item.good_id)
|
|
489
|
+
const salePrice = prices.get(goodId)
|
|
490
|
+
|
|
491
|
+
if (salePrice) {
|
|
492
|
+
revenue = revenue.adding(salePrice.multiplying(item.quantity))
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
cost = cost.adding(item.unit_cost.multiplying(item.quantity))
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const profit = revenue.gte(cost) ? revenue.subtracting(cost) : UInt64.from(0)
|
|
499
|
+
|
|
500
|
+
return {
|
|
501
|
+
revenue,
|
|
502
|
+
profit,
|
|
503
|
+
cost,
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
calculateSaleValueFromArray(prices: UInt64[]): {
|
|
508
|
+
revenue: UInt64
|
|
509
|
+
profit: UInt64
|
|
510
|
+
cost: UInt64
|
|
511
|
+
} {
|
|
512
|
+
const priceMap = new Map<number, UInt64>()
|
|
513
|
+
prices.forEach((price, index) => {
|
|
514
|
+
priceMap.set(index, price)
|
|
515
|
+
})
|
|
516
|
+
return this.calculateSaleValue(priceMap)
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
afterSellGoods(goodsToSell: Array<{goodId: number; quantity: number}>): EntityInventory[] {
|
|
520
|
+
if (this.cargo.length === 0) {
|
|
521
|
+
return []
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
return this.cargo.map((item) => {
|
|
525
|
+
const saleItem = goodsToSell.find((s) => Number(item.good_id) === s.goodId)
|
|
526
|
+
if (!saleItem) {
|
|
527
|
+
return new EntityInventory(item)
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const currentQty = Number(item.quantity)
|
|
531
|
+
const newQty = Math.max(0, currentQty - saleItem.quantity)
|
|
532
|
+
|
|
533
|
+
return new EntityInventory(
|
|
534
|
+
ServerContract.Types.cargo_item.from({
|
|
535
|
+
good_id: item.good_id,
|
|
536
|
+
quantity: UInt32.from(newQty),
|
|
537
|
+
unit_cost: item.unit_cost,
|
|
538
|
+
})
|
|
539
|
+
)
|
|
540
|
+
})
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
afterSellAllGoods(): EntityInventory[] {
|
|
544
|
+
if (this.cargo.length === 0) {
|
|
545
|
+
return []
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
return this.cargo.map(
|
|
549
|
+
(item) =>
|
|
550
|
+
new EntityInventory(
|
|
551
|
+
ServerContract.Types.cargo_item.from({
|
|
552
|
+
good_id: item.good_id,
|
|
553
|
+
quantity: UInt32.from(0),
|
|
554
|
+
unit_cost: item.unit_cost,
|
|
555
|
+
})
|
|
556
|
+
)
|
|
557
|
+
)
|
|
558
|
+
}
|
|
559
|
+
}
|