@eveshipfit/dogma-engine 11.1.0 → 11.2.0

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/README.md CHANGED
@@ -199,15 +199,24 @@ The WebAssembly variant is published on npm as
199
199
  [`@eveshipfit/dogma-engine`](https://www.npmjs.com/package/@eveshipfit/dogma-engine).
200
200
  [`@eveshipfit/sde`](https://www.npmjs.com/package/@eveshipfit/sde) ships `sde.dat` in its `dist` folder; serve or bundle that file.
201
201
 
202
+ The package is built for bundlers (Vite, webpack 5, ...), which load the `.wasm` themselves.
203
+
202
204
  ```bash
203
205
  npm install @eveshipfit/dogma-engine @eveshipfit/sde
204
206
  ```
205
207
 
206
208
  ```js
207
- import init, { init as initPanicHook, load_sde, load_eft, calculate, validate, beacon } from "@eveshipfit/dogma-engine";
208
-
209
- await init();
210
- initPanicHook();
209
+ import {
210
+ init,
211
+ load_sde,
212
+ load_eft,
213
+ save_eft,
214
+ calculate,
215
+ validate,
216
+ beacon,
217
+ } from "@eveshipfit/dogma-engine";
218
+
219
+ init();
211
220
 
212
221
  const sde = await fetch("/sde.dat").then((response) => response.arrayBuffer());
213
222
  const buildNumber = load_sde(new Uint8Array(sde));
@@ -224,6 +233,8 @@ const withSources = calculate(fit, { sources: true });
224
233
  const withBeacon = calculate({ ...fit, incoming: beacon(beaconTypeId) });
225
234
  /* Or if you have an EFT, the text format EVE copies a fit to the clipboard in: */
226
235
  const imported = calculate(load_eft("[Rifter, My Rifter]\n200mm AutoCannon I"));
236
+ /* And to write a fit back out as EFT: */
237
+ const eft = save_eft(fit);
227
238
 
228
239
  /* What EVE would not let you fly; it calculates the fit itself: */
229
240
  const violations = validate(fit);
@@ -265,6 +276,8 @@ with_sources = dogma.calculate(fit, {"sources": True})
265
276
  with_beacon = dogma.calculate({**fit, "incoming": dogma.beacon(beacon_type_id)})
266
277
  # Or if you have an EFT, the text format EVE copies a fit to the clipboard in:
267
278
  imported = dogma.calculate(dogma.load_eft("[Rifter, My Rifter]\n200mm AutoCannon I"))
279
+ # And to write a fit back out as EFT:
280
+ eft = dogma.save_eft(fit)
268
281
 
269
282
  # What EVE would not let you fly; it calculates the fit itself:
270
283
  violations = dogma.validate(fit)
@@ -307,6 +320,13 @@ printf '[Nergal, Spool]\nLight Entropic Disintegrator II, Occult S\n' \
307
320
 
308
321
  It prints a table on a terminal and JSON otherwise; see `--help` for the rest.
309
322
 
323
+ Or to write the fit back out as EFT, instead of calculating it:
324
+
325
+ ```bash
326
+ printf '[Nergal, Spool]\nLight Entropic Disintegrator II, Occult S\n' \
327
+ | cargo run --release -p esf-cli -- --eft
328
+ ```
329
+
310
330
  The regression suite reads the same paths; set `ESF_SDE` and `ESF_NAMES` to point it elsewhere.
311
331
 
312
332
  ## Regression
@@ -1,23 +1,448 @@
1
1
  /* tslint:disable */
2
2
  /* eslint-disable */
3
+ /**
4
+ * A charge loaded in a module.
5
+ */
6
+ export interface Charge {
7
+ /**
8
+ * The type id of the charge.
9
+ */
10
+ type_id: number;
11
+ }
12
+
13
+ /**
14
+ * A module, drone, fighter squadron, implant, booster or item in cargo.
15
+ */
16
+ export interface FitItem {
17
+ /**
18
+ * The type id of the item.
19
+ */
20
+ type_id: number;
21
+ /**
22
+ * Where the item is.
23
+ */
24
+ slot: Slot;
25
+ /**
26
+ * 1 for modules. Stack size for drones, fighters and cargo; for fighters
27
+ * in a tube, the size of the squadron.
28
+ */
29
+ quantity?: number;
30
+ /**
31
+ * The state asked for; the calculation lowers it when the item cannot
32
+ * reach it.
33
+ */
34
+ state: State;
35
+ /**
36
+ * The charge loaded in the module, if any.
37
+ */
38
+ charge?: Charge;
39
+ /**
40
+ * Only for mutated modules and drones.
41
+ */
42
+ mutation?: Mutation;
43
+ /**
44
+ * Only for fighters: the abilities used, by effect id. `None` uses the
45
+ * abilities the fighter uses by default.
46
+ */
47
+ fighter_abilities?: number[];
48
+ /**
49
+ * Only for boosters: the side effects rolled, by effect id. Empty means
50
+ * none.
51
+ */
52
+ booster_side_effects?: number[];
53
+ /**
54
+ * How far a module whose bonus grows every cycle has spooled.
55
+ * Only per-second stats use it; volley is always unspooled.
56
+ * `None` is fully spooled.
57
+ */
58
+ spool?: Spool;
59
+ }
60
+
61
+ /**
62
+ * A projected buff (like command bursts).
63
+ */
64
+ export interface ProjectedBuff {
65
+ /**
66
+ * Which buff, as `dbuffCollections` in the SDE numbers them.
67
+ */
68
+ id: number;
69
+ /**
70
+ * How strong it is, in whatever the buff's operation reads.
71
+ */
72
+ value: number;
73
+ }
74
+
75
+ /**
76
+ * A projected dogma effect.
77
+ */
78
+ export interface ProjectedEffect {
79
+ /**
80
+ * The type the effect belongs to.
81
+ */
82
+ type_id: number;
83
+ /**
84
+ * Which effect, as `dogmaEffects` in the SDE numbers them.
85
+ */
86
+ effect_id: number;
87
+ /**
88
+ * Every attribute and its value the effect reads.
89
+ */
90
+ attributes?: Map<number, number> | Record<number, number>;
91
+ }
92
+
93
+ /**
94
+ * A rack of slots, or a hardpoint a weapon needs.
95
+ */
96
+ export type SlotKind = "high" | "medium" | "low" | "rig" | "subsystem" | "service" | "turret" | "launcher";
97
+
98
+ /**
99
+ * A resource the ship only has so much of.
100
+ */
101
+ export type Resource = "cpu" | "powergrid" | "calibration" | "drone_bay" | "drone_bandwidth" | "launched_drones" | "fighter_bay" | "fighter_tubes" | "light_fighter_tubes" | "support_fighter_tubes" | "heavy_fighter_tubes" | "cargo_bay" | "charge_capacity";
102
+
103
+ /**
104
+ * A rule of EVE's, and the values that failed it.
105
+ *
106
+ * What an item would accept instead is not repeated here; it is on the item
107
+ * itself, as `chargeGroup1`, `canFitShipType1` and the like.
108
+ */
109
+ export type Rule = { type: "resource"; resource: Resource; used: number; available: number } | { type: "slots"; slot: SlotKind; used: number; available: number } | { type: "wrong_slot"; expected: SlotKind } | { type: "slot_taken" } | { type: "wrong_slot_index"; expected: number } | { type: "subsystem_taken" } | { type: "skill"; type_id: number; required: number; level: number } | { type: "rig_size"; ship: number; item: number } | { type: "ship_restricted" } | { type: "capital_item" } | { type: "structure_item" } | { type: "ship_item" } | { type: "max_group"; group_id: number; limit: GroupLimit; used: number; allowed: number } | { type: "max_type"; type_id: number; used: number; allowed: number } | { type: "charge_group" } | { type: "charge_size"; module: number; charge: number };
110
+
111
+ /**
112
+ * A ship, what is fitted to it, and the character flying it.
113
+ */
114
+ export interface Fit {
115
+ /**
116
+ * Only for display; the calculation does not use it.
117
+ */
118
+ name?: string;
119
+ /**
120
+ * The ship.
121
+ */
122
+ ship: Ship;
123
+ /**
124
+ * Modules, drones, fighters, implants, boosters and cargo.
125
+ */
126
+ items: FitItem[];
127
+ /**
128
+ * The character flying the ship.
129
+ */
130
+ character?: Character;
131
+ /**
132
+ * Where the ship is.
133
+ */
134
+ environment?: Environment;
135
+ /**
136
+ * What projections to apply on this fit.
137
+ */
138
+ incoming?: Projection;
139
+ }
140
+
141
+ /**
142
+ * How a modifier changes an attribute. They are applied in this order.
143
+ */
144
+ export type EffectOperator = "pre_assign" | "pre_mul" | "pre_div" | "mod_add" | "mod_sub" | "post_mul" | "post_div" | "post_percent" | "post_assign";
145
+
146
+ /**
147
+ * How a module or drone was mutated.
148
+ */
149
+ export interface Mutation {
150
+ /**
151
+ * The type id of the item before it was mutated.
152
+ */
153
+ base: number;
154
+ /**
155
+ * The rolled value of each mutated attribute, by attribute id.
156
+ */
157
+ attributes?: Map<number, number> | Record<number, number>;
158
+ }
159
+
160
+ /**
161
+ * How far a module has spooled.
162
+ */
163
+ export type Spool = { multiplier_bonus: number };
164
+
165
+ /**
166
+ * How incoming damage is split over the four damage types. Only the ratio
167
+ * matters; the calculation scales the four to add up to one.
168
+ */
169
+ export interface DamageProfile {
170
+ /**
171
+ * EM damage.
172
+ */
173
+ em?: number;
174
+ /**
175
+ * Explosive damage.
176
+ */
177
+ explosive?: number;
178
+ /**
179
+ * Kinetic damage.
180
+ */
181
+ kinetic?: number;
182
+ /**
183
+ * Thermal damage.
184
+ */
185
+ thermal?: number;
186
+ }
187
+
188
+ /**
189
+ * One attribute, before and after the effects on it.
190
+ */
191
+ export interface AttributeValue {
192
+ /**
193
+ * The value from the SDE.
194
+ */
195
+ base: number;
196
+ /**
197
+ * The value after every effect is applied.
198
+ */
199
+ value: number;
200
+ /**
201
+ * In the order pass 3 applied them; empty unless `Options::sources` is set.
202
+ */
203
+ sources?: Source[];
204
+ }
205
+
206
+ /**
207
+ * One modifier on an attribute, and where it came from.
208
+ */
209
+ export interface Source {
210
+ /**
211
+ * The object the effect belongs to.
212
+ */
213
+ from: SourceRef;
214
+ /**
215
+ * The effect that holds the modifier; `None` for a buff, which has none.
216
+ */
217
+ effect_id: number | undefined;
218
+ /**
219
+ * The attribute on the source that holds `value`; `None` for a buff,
220
+ * which carries its own strength.
221
+ */
222
+ source_attribute_id: number | undefined;
223
+ /**
224
+ * How `value` changes the attribute.
225
+ */
226
+ operator: EffectOperator;
227
+ /**
228
+ * The source attribute's value, as pass 3 used it.
229
+ */
230
+ value: number;
231
+ /**
232
+ * A penalised stack is split into one entry per item, as each gets its own penalty.
233
+ */
234
+ quantity: number;
235
+ /**
236
+ * The stacking penalty factor applied; `None` when not penalised.
237
+ */
238
+ penalty: number | undefined;
239
+ /**
240
+ * False when the source's state is too low for the effect.
241
+ */
242
+ applied: boolean;
243
+ }
244
+
245
+ /**
246
+ * One rule the fit breaks, and what breaks it.
247
+ */
248
+ export interface Violation {
249
+ /**
250
+ * What the rule is about.
251
+ */
252
+ target: Target;
253
+ /**
254
+ * The rule, and the values that failed it.
255
+ */
256
+ rule: Rule;
257
+ }
258
+
259
+ /**
260
+ * The calculated attributes of the ship, its mode, the character, or one item.
261
+ */
262
+ export interface ItemResult {
263
+ /**
264
+ * Every attribute, by attribute id.
265
+ */
266
+ attributes: Map<number, AttributeValue>;
267
+ /**
268
+ * The state actually reached, which may be below what was requested.
269
+ */
270
+ state: State;
271
+ /**
272
+ * The highest state the item can reach.
273
+ */
274
+ max_state: State;
275
+ /**
276
+ * The charge loaded in the module, if any.
277
+ */
278
+ charge: ItemResult | undefined;
279
+ }
280
+
281
+ /**
282
+ * The character flying the ship.
283
+ */
284
+ export interface Character {
285
+ /**
286
+ * The level of each skill, by type id. A skill that is not listed is not
287
+ * trained, and gives no bonus.
288
+ */
289
+ skills?: Map<number, number> | Record<number, number>;
290
+ /**
291
+ * -10.0 to 5.0. Only a few ships have a bonus that scales with it.
292
+ */
293
+ security_status?: number;
294
+ }
295
+
296
+ /**
297
+ * The external buffs and effects that can be applied to another ship.
298
+ *
299
+ * A calculation reports what the fit hands out; put that in
300
+ * [`Fit::incoming`](crate::Fit::incoming) of another fit to have it applied.
301
+ */
302
+ export interface Projection {
303
+ /**
304
+ * Buffs, like command burst hands.
305
+ */
306
+ buffs?: ProjectedBuff[];
307
+ /**
308
+ * Effects, like webifiers, remote reps, etc.
309
+ */
310
+ effects?: ProjectedEffect[];
311
+ }
312
+
313
+ /**
314
+ * The result of [`calculate()`](crate::calculate).
315
+ */
316
+ export interface Calculation {
317
+ /**
318
+ * The ship.
319
+ */
320
+ ship: ItemResult;
321
+ /**
322
+ * The active mode, if the ship has one.
323
+ */
324
+ mode?: ItemResult;
325
+ /**
326
+ * Index-parallel to `Fit::items`: same length, same order.
327
+ */
328
+ items: ItemResult[];
329
+ /**
330
+ * The character.
331
+ */
332
+ character: ItemResult;
333
+ /**
334
+ * The buffs that landed, ordered by id: those of `Fit::incoming`, plus
335
+ * the ones the fit's own bursts hand out. What is missing lost to another
336
+ * source of the same buff, or the SDE has no such buff.
337
+ */
338
+ buffs?: ProjectedBuff[];
339
+ /**
340
+ * All outgoing projections (effects and buffs).
341
+ */
342
+ outgoing?: Projection;
343
+ }
344
+
345
+ /**
346
+ * The security of a solar system.
347
+ */
348
+ export type Security = "high_sec" | "low_sec" | "null_sec" | "wormhole";
349
+
350
+ /**
351
+ * The ship of a fit.
352
+ */
353
+ export interface Ship {
354
+ /**
355
+ * The type id of the ship.
356
+ */
357
+ type_id: number;
358
+ /**
359
+ * The type id of the active mode, for ships that have modes.
360
+ */
361
+ mode?: number;
362
+ }
363
+
364
+ /**
365
+ * The state of an item, lowest first.
366
+ */
367
+ export type State = "offline" | "online" | "active" | "overload";
368
+
369
+ /**
370
+ * What [`calculate()`] reports on top of the values.
371
+ */
372
+ export interface Options {
373
+ /**
374
+ * Report per attribute the modifiers its value was calculated from.
375
+ */
376
+ sources?: boolean;
377
+ }
378
+
379
+ /**
380
+ * What a Reactive Armor Hardener shifts its resistances towards.
381
+ *
382
+ * EVE shows it as a plain 15/15/15/15 hardener, as the client has no damage
383
+ * to shift it against. `DoNotAdapt` reports the same, and is the default.
384
+ */
385
+ export type ReactiveArmor = "do_not_adapt" | "damage_profile" | { profile: DamageProfile };
386
+
387
+ /**
388
+ * What a [`Violation`] is about. `Item` and `Charge` index into `Fit::items`.
389
+ */
390
+ export type Target = { type: "ship" } | { type: "item"; index: number } | { type: "charge"; index: number };
391
+
392
+ /**
393
+ * Where an item is. The number is the position in its rack, starting at 0;
394
+ * for implants and boosters, the slot as EVE numbers it, starting at 1.
395
+ */
396
+ export type Slot = { type: "high"; index: number } | { type: "medium"; index: number } | { type: "low"; index: number } | { type: "rig"; index: number } | { type: "subsystem"; index: number } | { type: "service"; index: number } | { type: "fighter_tube"; index: number } | { type: "fighter_bay" } | { type: "implant"; index: number } | { type: "booster"; index: number } | { type: "drone_bay" } | { type: "cargo" };
397
+
398
+ /**
399
+ * Where the ship is.
400
+ */
401
+ export interface Environment {
402
+ /**
403
+ * The damage the ship is assumed to take, for effective hitpoints.
404
+ */
405
+ damage_profile?: DamageProfile;
406
+ /**
407
+ * The security of the solar system.
408
+ */
409
+ security?: Security;
410
+ /**
411
+ * What a Reactive Armor Hardener shifts its resistances towards.
412
+ */
413
+ reactive_armor?: ReactiveArmor;
414
+ }
415
+
416
+ /**
417
+ * Which state a group limit counts.
418
+ */
419
+ export type GroupLimit = "fitted" | "online" | "active";
420
+
421
+ /**
422
+ * `Item` and `Charge` index into `Fit::items`, `Projected` into
423
+ * `Fit::incoming.effects`. A skill and a buff are not in the result, so they
424
+ * carry their own id.
425
+ */
426
+ export type SourceRef = { type: "ship" } | { type: "mode" } | { type: "character" } | { type: "item"; index: number } | { type: "charge"; index: number } | { type: "skill"; type_id: number } | { type: "projected"; index: number } | { type: "buff"; id: number };
427
+
3
428
 
4
429
  /**
5
430
  * What a beacon in space hands to every fit in there with it. Put the result
6
431
  * in `incoming` of a fit to have it applied.
7
432
  */
8
- export function beacon(type_id: number): any;
433
+ export function beacon(type_id: number): Projection;
9
434
 
10
435
  /**
11
- * `js_options` may be left out; it then uses the defaults.
436
+ * `options` may be left out; it then uses the defaults.
12
437
  */
13
- export function calculate(js_fit: any, js_options: any): any;
438
+ export function calculate(fit: Fit, options?: Options | null): Calculation;
14
439
 
15
440
  export function init(): void;
16
441
 
17
442
  /**
18
443
  * Load a fit from EFT, the text format EVE copies a fit to the clipboard in.
19
444
  */
20
- export function load_eft(eft: string): any;
445
+ export function load_eft(eft: string): Fit;
21
446
 
22
447
  /**
23
448
  * Load `names.dat`, so an EFT written in another language than English also
@@ -32,8 +457,13 @@ export function load_names(bytes: Uint8Array): number;
32
457
  */
33
458
  export function load_sde(bytes: Uint8Array): number;
34
459
 
460
+ /**
461
+ * Write a fit as EFT, the text format EVE copies a fit to the clipboard in.
462
+ */
463
+ export function save_eft(fit: Fit): string;
464
+
35
465
  /**
36
466
  * Report the fitting rules the fit breaks. Calculates the fit itself, as
37
467
  * every rule reads the values after skills and modules changed them.
38
468
  */
39
- export function validate(js_fit: any): any;
469
+ export function validate(fit: Fit): Violation[];
@@ -5,5 +5,5 @@ import { __wbg_set_wasm } from "./esf_dogma_engine_bg.js";
5
5
  __wbg_set_wasm(wasm);
6
6
  wasm.__wbindgen_start();
7
7
  export {
8
- beacon, calculate, init, load_eft, load_names, load_sde, validate
8
+ beacon, calculate, init, load_eft, load_names, load_sde, save_eft, validate
9
9
  } from "./esf_dogma_engine_bg.js";
@@ -2,7 +2,7 @@
2
2
  * What a beacon in space hands to every fit in there with it. Put the result
3
3
  * in `incoming` of a fit to have it applied.
4
4
  * @param {number} type_id
5
- * @returns {any}
5
+ * @returns {Projection}
6
6
  */
7
7
  export function beacon(type_id) {
8
8
  const ret = wasm.beacon(type_id);
@@ -13,13 +13,13 @@ export function beacon(type_id) {
13
13
  }
14
14
 
15
15
  /**
16
- * `js_options` may be left out; it then uses the defaults.
17
- * @param {any} js_fit
18
- * @param {any} js_options
19
- * @returns {any}
16
+ * `options` may be left out; it then uses the defaults.
17
+ * @param {Fit} fit
18
+ * @param {Options | null} [options]
19
+ * @returns {Calculation}
20
20
  */
21
- export function calculate(js_fit, js_options) {
22
- const ret = wasm.calculate(js_fit, js_options);
21
+ export function calculate(fit, options) {
22
+ const ret = wasm.calculate(fit, isLikeNone(options) ? 0 : addToExternrefTable0(options));
23
23
  if (ret[2]) {
24
24
  throw takeFromExternrefTable0(ret[1]);
25
25
  }
@@ -33,7 +33,7 @@ export function init() {
33
33
  /**
34
34
  * Load a fit from EFT, the text format EVE copies a fit to the clipboard in.
35
35
  * @param {string} eft
36
- * @returns {any}
36
+ * @returns {Fit}
37
37
  */
38
38
  export function load_eft(eft) {
39
39
  const ptr0 = passStringToWasm0(eft, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
@@ -78,14 +78,38 @@ export function load_sde(bytes) {
78
78
  return ret[0];
79
79
  }
80
80
 
81
+ /**
82
+ * Write a fit as EFT, the text format EVE copies a fit to the clipboard in.
83
+ * @param {Fit} fit
84
+ * @returns {string}
85
+ */
86
+ export function save_eft(fit) {
87
+ let deferred2_0;
88
+ let deferred2_1;
89
+ try {
90
+ const ret = wasm.save_eft(fit);
91
+ var ptr1 = ret[0];
92
+ var len1 = ret[1];
93
+ if (ret[3]) {
94
+ ptr1 = 0; len1 = 0;
95
+ throw takeFromExternrefTable0(ret[2]);
96
+ }
97
+ deferred2_0 = ptr1;
98
+ deferred2_1 = len1;
99
+ return getStringFromWasm0(ptr1, len1);
100
+ } finally {
101
+ wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
102
+ }
103
+ }
104
+
81
105
  /**
82
106
  * Report the fitting rules the fit breaks. Calculates the fit itself, as
83
107
  * every rule reads the values after skills and modules changed them.
84
- * @param {any} js_fit
85
- * @returns {any}
108
+ * @param {Fit} fit
109
+ * @returns {Violation[]}
86
110
  */
87
- export function validate(js_fit) {
88
- const ret = wasm.validate(js_fit);
111
+ export function validate(fit) {
112
+ const ret = wasm.validate(fit);
89
113
  if (ret[2]) {
90
114
  throw takeFromExternrefTable0(ret[1]);
91
115
  }
@@ -136,10 +160,6 @@ export function __wbg___wbindgen_is_function_fcda5e3902d732fe(arg0) {
136
160
  const ret = typeof(arg0) === 'function';
137
161
  return ret;
138
162
  }
139
- export function __wbg___wbindgen_is_null_5160b3e381865372(arg0) {
140
- const ret = arg0 === null;
141
- return ret;
142
- }
143
163
  export function __wbg___wbindgen_is_object_edb6b15aa3afe12e(arg0) {
144
164
  const val = arg0;
145
165
  const ret = typeof(val) === 'object' && val !== null;
Binary file
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "EVEShipFit Team <info@eveship.fit>"
6
6
  ],
7
7
  "description": "Library to calculate statistics for EVE Online ship fits",
8
- "version": "11.1.0",
8
+ "version": "11.2.0",
9
9
  "license": "MIT",
10
10
  "repository": {
11
11
  "type": "git",