libram 0.4.6 → 0.5.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/dist/combat.d.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  * @category Combat
5
5
  * @returns {number} The macro ID.
6
6
  */
7
- export declare function getMacroId(): number;
7
+ export declare function getMacroId(name?: string): number;
8
8
  declare type ItemOrName = Item | string;
9
9
  declare type SkillOrName = Skill | string;
10
10
  declare type Constructor<T> = {
@@ -21,13 +21,20 @@ export declare class InvalidMacroError extends Error {
21
21
  */
22
22
  export declare class Macro {
23
23
  static SAVED_MACRO_PROPERTY: string;
24
- static cachedMacroId: number | null;
25
- static cachedAutoAttack: string | null;
24
+ static cachedMacroIds: Map<string, number>;
25
+ static cachedAutoAttacks: Map<string, string>;
26
26
  components: string[];
27
+ name: string;
27
28
  /**
28
29
  * Convert macro to string.
29
30
  */
30
31
  toString(): string;
32
+ /**
33
+ * Gives your macro a new name to be used when saving an autoattack.
34
+ * @param name The name to be used when saving as an autoattack.
35
+ * @returns The previous name assigned to this macro.
36
+ */
37
+ rename(name: string): string;
31
38
  /**
32
39
  * Save a macro to a Mafia property for use in a consult script.
33
40
  */
@@ -60,6 +67,15 @@ export declare class Macro {
60
67
  * Set this macro as a KoL native autoattack.
61
68
  */
62
69
  setAutoAttack(): void;
70
+ /**
71
+ * Renames the macro, then sets it as an autoattack.
72
+ * @param name The name to save the macro under as an autoattack.
73
+ */
74
+ setAutoAttackAs(name: string): void;
75
+ /**
76
+ * Clear all cached autoattacks, and delete all stored macros server-side.
77
+ */
78
+ static clearAutoAttackMacros(): void;
63
79
  /**
64
80
  * Add an "abort" step to this macro.
65
81
  * @returns {Macro} This object itself.
package/dist/combat.js CHANGED
@@ -9,11 +9,11 @@ const MACRO_NAME = "Script Autoattack Macro";
9
9
  * @category Combat
10
10
  * @returns {number} The macro ID.
11
11
  */
12
- export function getMacroId() {
13
- const macroMatches = xpath(visitUrl("account_combatmacros.php"), `//select[@name="macroid"]/option[text()="${MACRO_NAME}"]/@value`);
12
+ export function getMacroId(name = MACRO_NAME) {
13
+ const macroMatches = xpath(visitUrl("account_combatmacros.php"), `//select[@name="macroid"]/option[text()="${name}"]/@value`);
14
14
  if (macroMatches.length === 0) {
15
15
  visitUrl("account_combatmacros.php?action=new");
16
- const newMacroText = visitUrl(`account_combatmacros.php?macroid=0&name=${MACRO_NAME}&macrotext=abort&action=save`);
16
+ const newMacroText = visitUrl(`account_combatmacros.php?macroid=0&name=${name}&macrotext=abort&action=save`);
17
17
  return parseInt(xpath(newMacroText, "//input[@name=macroid]/@value")[0], 10);
18
18
  }
19
19
  else {
@@ -70,15 +70,26 @@ export class InvalidMacroError extends Error {
70
70
  */
71
71
  export class Macro {
72
72
  static SAVED_MACRO_PROPERTY = "libram_savedMacro";
73
- static cachedMacroId = null;
74
- static cachedAutoAttack = null;
73
+ static cachedMacroIds = new Map();
74
+ static cachedAutoAttacks = new Map();
75
75
  components = [];
76
+ name = MACRO_NAME;
76
77
  /**
77
78
  * Convert macro to string.
78
79
  */
79
80
  toString() {
80
81
  return this.components.join(";");
81
82
  }
83
+ /**
84
+ * Gives your macro a new name to be used when saving an autoattack.
85
+ * @param name The name to be used when saving as an autoattack.
86
+ * @returns The previous name assigned to this macro.
87
+ */
88
+ rename(name) {
89
+ const returnValue = this.name;
90
+ this.name = name;
91
+ return returnValue;
92
+ }
82
93
  /**
83
94
  * Save a macro to a Mafia property for use in a consult script.
84
95
  */
@@ -129,16 +140,36 @@ export class Macro {
129
140
  * Set this macro as a KoL native autoattack.
130
141
  */
131
142
  setAutoAttack() {
132
- if (Macro.cachedMacroId === null)
133
- Macro.cachedMacroId = getMacroId();
134
- if (getAutoAttack() === 99000000 + Macro.cachedMacroId &&
135
- this.toString() === Macro.cachedAutoAttack) {
143
+ let id = Macro.cachedMacroIds.get(this.name);
144
+ if (id === undefined)
145
+ Macro.cachedMacroIds.set(this.name, getMacroId(this.name));
146
+ id = getMacroId(this.name);
147
+ if (getAutoAttack() === 99000000 + id &&
148
+ this.toString() === Macro.cachedAutoAttacks.get(this.name)) {
136
149
  // This macro is already set. Don"t make the server request.
137
150
  return;
138
151
  }
139
- visitUrl(`account_combatmacros.php?macroid=${Macro.cachedMacroId}&name=${urlEncode(MACRO_NAME)}&macrotext=${urlEncode(this.toString())}&action=save`, true, true);
140
- visitUrl(`account.php?am=1&action=autoattack&value=${99000000 + Macro.cachedMacroId}&ajax=1`);
141
- Macro.cachedAutoAttack = this.toString();
152
+ visitUrl(`account_combatmacros.php?macroid=${id}&name=${urlEncode(this.name)}&macrotext=${urlEncode(this.toString())}&action=save`, true, true);
153
+ visitUrl(`account.php?am=1&action=autoattack&value=${99000000 + id}&ajax=1`);
154
+ Macro.cachedAutoAttacks.set(this.name, this.toString());
155
+ }
156
+ /**
157
+ * Renames the macro, then sets it as an autoattack.
158
+ * @param name The name to save the macro under as an autoattack.
159
+ */
160
+ setAutoAttackAs(name) {
161
+ this.name = name;
162
+ this.setAutoAttack();
163
+ }
164
+ /**
165
+ * Clear all cached autoattacks, and delete all stored macros server-side.
166
+ */
167
+ static clearAutoAttackMacros() {
168
+ for (const name of Macro.cachedAutoAttacks.keys()) {
169
+ const id = Macro.cachedMacroIds.get(name) ?? getMacroId(name);
170
+ visitUrl(`account_combatmacros.php?macroid=${id}&action=edit&what=Delete&confirm=1`);
171
+ Macro.cachedAutoAttacks.delete(name);
172
+ }
142
173
  }
143
174
  /**
144
175
  * Add an "abort" step to this macro.
@@ -1,18 +1,26 @@
1
- declare type MenuItemOptions = {
1
+ declare type RawDietEntry<T> = [MenuItem<T>[], number];
2
+ declare type RawDiet<T> = RawDietEntry<T>[];
3
+ declare type MenuItemOptions<T> = {
2
4
  organ?: Organ;
3
5
  size?: number;
4
6
  maximum?: number | "auto";
5
7
  additionalValue?: number;
6
- wishEffect?: Effect;
8
+ effect?: Effect;
9
+ priceOverride?: number;
10
+ mayo?: Item;
11
+ data?: T;
7
12
  };
8
- export declare class MenuItem {
13
+ export declare class MenuItem<T> {
9
14
  item: Item;
10
15
  organ?: Organ;
11
16
  size: number;
12
17
  maximum?: number;
13
18
  additionalValue?: number;
14
- wishEffect?: Effect;
15
- static defaultOptions: Map<Item, MenuItemOptions>;
19
+ effect?: Effect;
20
+ priceOverride?: number;
21
+ mayo?: Item;
22
+ data?: T;
23
+ static defaultOptions<T>(): Map<Item, MenuItemOptions<T>>;
16
24
  /**
17
25
  * Construct a new menu item, possibly with extra properties. Items in MenuItem.defaultOptions have intelligent defaults.
18
26
  * @param item Item to add to menu.
@@ -20,21 +28,48 @@ export declare class MenuItem {
20
28
  * @param options.size Override item organ size. Necessary for any non-food/booze/spleen item.
21
29
  * @param options.maximum Maximum uses remaining today, or "auto" to check dailyusesleft Mafia property.
22
30
  * @param options.additionalValue Additional value (positive) or cost (negative) to consider with item, e.g. from buffs.
23
- * @param options.wishEffect If item is a pocket wish, effect to wish for.
31
+ * @param options.effect Effect associated with this menu item (pocket wish effect, sweet synthesis effect, pill keeper potion extension)
32
+ * @param options.mayo Which mayo to use before item (ignored if mayo clinic is not installed or item is not a food)
33
+ * @param options.note Any note to track information about item, to be used later
24
34
  */
25
- constructor(item: Item, options?: MenuItemOptions);
26
- equals(other: MenuItem): boolean;
35
+ constructor(item: Item, options?: MenuItemOptions<T>);
36
+ equals(other: MenuItem<T>): boolean;
27
37
  toString(): string;
28
38
  price(): number;
29
39
  }
30
40
  declare const organs: readonly ["food", "booze", "spleen item"];
31
41
  declare type Organ = typeof organs[number];
42
+ declare class DietEntry<T> {
43
+ readonly menuItems: MenuItem<T>[];
44
+ quantity: number;
45
+ constructor(menuItems: MenuItem<T>[], quantity: number);
46
+ target(): MenuItem<T>;
47
+ helpers(): MenuItem<T>[];
48
+ expectedAdventures(diet: Diet<T>): number;
49
+ expectedValue(mpa: number, diet: Diet<T>, method?: "gross" | "net"): number;
50
+ expectedPrice(): number;
51
+ }
52
+ interface OrganCapacity {
53
+ food?: number | "auto";
54
+ booze?: number | "auto";
55
+ spleen?: number | "auto";
56
+ }
32
57
  /**
33
- * Plan out an optimal diet using a knapsack algorithm.
34
- * @param mpa Meat per adventure value.
35
- * @param menu Array of MenuItems to consider for diet purposes.
36
- * @param organCapacities Optional override of each organ's capacity.
37
- * @returns Array of [menu item and helpers, count].
58
+ * A representation of a potential diet
38
59
  */
39
- export declare function planDiet(mpa: number, menu: MenuItem[], organCapacities?: [Organ, number | null][]): [MenuItem[], number][];
60
+ export declare class Diet<T> {
61
+ entries: DietEntry<T>[];
62
+ constructor(entries?: DietEntry<T>[]);
63
+ get refinedPalate(): boolean;
64
+ get garish(): boolean;
65
+ get saucemaven(): boolean;
66
+ get tuxedoShirt(): boolean;
67
+ get pinkyRing(): boolean;
68
+ expectedAdventures(): number;
69
+ expectedValue(mpa: number, method?: "gross" | "net"): number;
70
+ expectedPrice(): number;
71
+ copy(): Diet<T>;
72
+ static from<T>(rawDiet: RawDiet<T>): Diet<T>;
73
+ static plan<T>(mpa: number, menu: MenuItem<T>[], organCapacities?: OrganCapacity): Diet<T>;
74
+ }
40
75
  export {};
@@ -1,10 +1,11 @@
1
- import { canEquip, fullnessLimit, getWorkshed, inebrietyLimit, itemType, mallPrice, mallPrices, myFullness, myInebriety, myLevel, myPrimestat, mySpleenUse, npcPrice, spleenLimit, } from "kolmafia";
1
+ import { canEquip, fullnessLimit, inebrietyLimit, itemType, mallPrice, mallPrices, myFullness, myInebriety, myLevel, myPrimestat, mySpleenUse, npcPrice, spleenLimit, } from "kolmafia";
2
2
  import { knapsack } from "./knapsack";
3
3
  import { have } from "../lib";
4
4
  import { get as getModifier } from "../modifier";
5
5
  import { get } from "../property";
6
6
  import { $effect, $item, $items, $skill, $stat } from "../template-string";
7
- import { sum } from "../utils";
7
+ import { sum, sumNumbers } from "../utils";
8
+ import { Mayo, installed as mayoInstalled } from "../resources/2015/MayoClinic";
8
9
  function isMonday() {
9
10
  // Checking Tuesday's ruby is a hack to see if it's Monday in Arizona.
10
11
  return getModifier("Muscle Percent", $item `Tuesday's ruby`) > 0;
@@ -55,59 +56,64 @@ export class MenuItem {
55
56
  size;
56
57
  maximum;
57
58
  additionalValue;
58
- wishEffect;
59
- static defaultOptions = new Map([
60
- [
61
- $item `distention pill`,
62
- {
63
- organ: "food",
64
- maximum: !have($item `distention pill`) || get("_distentionPillUsed") ? 0 : 1,
65
- size: -1,
66
- },
67
- ],
68
- [
69
- $item `synthetic dog hair pill`,
70
- {
71
- organ: "booze",
72
- maximum: !have($item `synthetic dog hair pill`) ||
73
- get("_syntheticDogHairPillUsed")
74
- ? 0
75
- : 1,
76
- size: -1,
77
- },
78
- ],
79
- [
80
- $item `cuppa Voraci tea`,
81
- { organ: "food", maximum: get("_voraciTeaUsed") ? 0 : 1, size: -1 },
82
- ],
83
- [
84
- $item `cuppa Sobrie tea`,
85
- { organ: "booze", maximum: get("_sobrieTeaUsed") ? 0 : 1, size: -1 },
86
- ],
87
- [
88
- $item `mojo filter`,
89
- {
90
- organ: "spleen item",
91
- maximum: 3 - get("currentMojoFilters"),
92
- size: -1,
93
- },
94
- ],
95
- [$item `spice melange`, { maximum: get("spiceMelangeUsed") ? 0 : 1 }],
96
- [
97
- $item `Ultra Mega Sour Ball`,
98
- { maximum: get("_ultraMegaSourBallUsed") ? 0 : 1 },
99
- ],
100
- [
101
- $item `The Plumber's mushroom stew`,
102
- { maximum: get("_plumbersMushroomStewEaten") ? 0 : 1 },
103
- ],
104
- [$item `The Mad Liquor`, { maximum: get("_madLiquorDrunk") ? 0 : 1 }],
105
- [
106
- $item `Doc Clock's thyme cocktail`,
107
- { maximum: get("_docClocksThymeCocktailDrunk") ? 0 : 1 },
108
- ],
109
- [$item `Mr. Burnsger`, { maximum: get("_mrBurnsgerEaten") ? 0 : 1 }],
110
- ]);
59
+ effect;
60
+ priceOverride;
61
+ mayo;
62
+ data;
63
+ static defaultOptions() {
64
+ return new Map([
65
+ [
66
+ $item `distention pill`,
67
+ {
68
+ organ: "food",
69
+ maximum: !have($item `distention pill`) || get("_distentionPillUsed") ? 0 : 1,
70
+ size: -1,
71
+ },
72
+ ],
73
+ [
74
+ $item `synthetic dog hair pill`,
75
+ {
76
+ organ: "booze",
77
+ maximum: !have($item `synthetic dog hair pill`) ||
78
+ get("_syntheticDogHairPillUsed")
79
+ ? 0
80
+ : 1,
81
+ size: -1,
82
+ },
83
+ ],
84
+ [
85
+ $item `cuppa Voraci tea`,
86
+ { organ: "food", maximum: get("_voraciTeaUsed") ? 0 : 1, size: -1 },
87
+ ],
88
+ [
89
+ $item `cuppa Sobrie tea`,
90
+ { organ: "booze", maximum: get("_sobrieTeaUsed") ? 0 : 1, size: -1 },
91
+ ],
92
+ [
93
+ $item `mojo filter`,
94
+ {
95
+ organ: "spleen item",
96
+ maximum: 3 - get("currentMojoFilters"),
97
+ size: -1,
98
+ },
99
+ ],
100
+ [$item `spice melange`, { maximum: get("spiceMelangeUsed") ? 0 : 1 }],
101
+ [
102
+ $item `Ultra Mega Sour Ball`,
103
+ { maximum: get("_ultraMegaSourBallUsed") ? 0 : 1 },
104
+ ],
105
+ [
106
+ $item `The Plumber's mushroom stew`,
107
+ { maximum: get("_plumbersMushroomStewEaten") ? 0 : 1 },
108
+ ],
109
+ [$item `The Mad Liquor`, { maximum: get("_madLiquorDrunk") ? 0 : 1 }],
110
+ [
111
+ $item `Doc Clock's thyme cocktail`,
112
+ { maximum: get("_docClocksThymeCocktailDrunk") ? 0 : 1 },
113
+ ],
114
+ [$item `Mr. Burnsger`, { maximum: get("_mrBurnsgerEaten") ? 0 : 1 }],
115
+ ]);
116
+ }
111
117
  /**
112
118
  * Construct a new menu item, possibly with extra properties. Items in MenuItem.defaultOptions have intelligent defaults.
113
119
  * @param item Item to add to menu.
@@ -115,17 +121,22 @@ export class MenuItem {
115
121
  * @param options.size Override item organ size. Necessary for any non-food/booze/spleen item.
116
122
  * @param options.maximum Maximum uses remaining today, or "auto" to check dailyusesleft Mafia property.
117
123
  * @param options.additionalValue Additional value (positive) or cost (negative) to consider with item, e.g. from buffs.
118
- * @param options.wishEffect If item is a pocket wish, effect to wish for.
124
+ * @param options.effect Effect associated with this menu item (pocket wish effect, sweet synthesis effect, pill keeper potion extension)
125
+ * @param options.mayo Which mayo to use before item (ignored if mayo clinic is not installed or item is not a food)
126
+ * @param options.note Any note to track information about item, to be used later
119
127
  */
120
128
  constructor(item, options = {}) {
121
- const { size, organ, maximum, additionalValue, wishEffect } = {
129
+ const { size, organ, maximum, additionalValue, effect, priceOverride, mayo, data, } = {
122
130
  ...options,
123
- ...(MenuItem.defaultOptions.get(item) ?? {}),
131
+ ...(MenuItem.defaultOptions().get(item) ?? {}),
124
132
  };
125
133
  this.item = item;
126
134
  this.maximum = maximum === "auto" ? item.dailyusesleft : maximum;
127
135
  this.additionalValue = additionalValue;
128
- this.wishEffect = wishEffect;
136
+ this.effect = effect;
137
+ this.priceOverride = priceOverride;
138
+ this.mayo = mayo;
139
+ this.data = data;
129
140
  const typ = itemType(this.item);
130
141
  this.organ = organ ?? (isOrgan(typ) ? typ : undefined);
131
142
  this.size =
@@ -139,13 +150,17 @@ export class MenuItem {
139
150
  : 0);
140
151
  }
141
152
  equals(other) {
142
- return this.item === other.item && this.wishEffect === other.wishEffect;
153
+ return this.item === other.item && this.effect === other.effect;
143
154
  }
144
155
  toString() {
156
+ if (this.effect) {
157
+ return `${this.item}:${this.effect}`;
158
+ }
145
159
  return this.item.toString();
146
160
  }
147
161
  price() {
148
- return npcPrice(this.item) > 0 ? npcPrice(this.item) : mallPrice(this.item);
162
+ return (this.priceOverride ??
163
+ (npcPrice(this.item) > 0 ? npcPrice(this.item) : mallPrice(this.item)));
149
164
  }
150
165
  }
151
166
  const organs = ["food", "booze", "spleen item"];
@@ -155,20 +170,24 @@ function isOrgan(x) {
155
170
  class DietPlanner {
156
171
  mpa;
157
172
  menu;
173
+ mayoLookup;
158
174
  fork;
159
175
  mug;
160
176
  seasoning;
161
- mayoflex;
162
177
  spleenValue = 0;
163
178
  constructor(mpa, menu) {
164
179
  this.mpa = mpa;
165
180
  this.fork = menu.find((item) => item.item === $item `Ol' Scratch's salad fork`);
166
181
  this.mug = menu.find((item) => item.item === $item `Frosty's frosty mug`);
167
182
  this.seasoning = menu.find((item) => item.item === $item `Special Seasoning`);
168
- this.mayoflex =
169
- getWorkshed() === $item `portable Mayo Clinic`
170
- ? menu.find((item) => item.item === $item `Mayoflex`)
171
- : undefined;
183
+ this.mayoLookup = new Map();
184
+ if (mayoInstalled()) {
185
+ for (const mayo of [Mayo.flex, Mayo.zapine]) {
186
+ const menuItem = menu.find((item) => item.item === mayo);
187
+ if (menuItem)
188
+ this.mayoLookup.set(mayo, menuItem);
189
+ }
190
+ }
172
191
  this.menu = menu.filter((item) => item.organ);
173
192
  if (menu.length > 100) {
174
193
  mallPrices("food");
@@ -203,15 +222,19 @@ class DietPlanner {
203
222
  this.mpa > mallPrice($item `Special Seasoning`)) {
204
223
  helpers.push(this.seasoning);
205
224
  }
206
- if (this.mayoflex &&
207
- itemType(menuItem.item) === "food" &&
208
- this.mpa > npcPrice($item `Mayoflex`)) {
209
- helpers.push(this.mayoflex);
225
+ if (itemType(menuItem.item) === "food" && this.mayoLookup.size) {
226
+ const mayo = menuItem.mayo
227
+ ? this.mayoLookup.get(menuItem.mayo)
228
+ : this.mayoLookup.get(Mayo.flex);
229
+ if (mayo)
230
+ helpers.push(mayo);
210
231
  }
211
232
  const defaultModifiers = {
212
233
  forkMug: false,
213
234
  seasoning: this.seasoning ? helpers.includes(this.seasoning) : false,
214
- mayoflex: this.mayoflex ? helpers.includes(this.mayoflex) : false,
235
+ mayoflex: this.mayoLookup.size
236
+ ? helpers.some((item) => item.item === Mayo.flex)
237
+ : false,
215
238
  refinedPalate: have($effect `Refined Palate`),
216
239
  garish: have($effect `Gar-ish`),
217
240
  saucemaven: have($skill `Saucemaven`),
@@ -297,10 +320,10 @@ class DietPlanner {
297
320
  }
298
321
  const organCapacitiesWith = [...organCapacitiesWithMap];
299
322
  const isRefinedPalate = (trialItem.item === $item `pocket wish` &&
300
- trialItem.wishEffect === $effect `Refined Palate`) ||
323
+ trialItem.effect === $effect `Refined Palate`) ||
301
324
  trialItem.item === $item `toasted brie`;
302
325
  const isGarish = (trialItem.item === $item `pocket wish` &&
303
- trialItem.wishEffect === $effect `Gar-ish`) ||
326
+ trialItem.effect === $effect `Gar-ish`) ||
304
327
  trialItem.item === $item `potion of the field gar`;
305
328
  const [valueWithout, planWithout] = this.planOrgansWithTrials(organCapacities, trialItems.slice(1), overrideModifiers);
306
329
  const [valueWith, planWith] = this.planOrgansWithTrials(organCapacitiesWith, trialItems.slice(1), {
@@ -374,7 +397,7 @@ const interactingItems = [
374
397
  * @param organCapacities Optional override of each organ's capacity.
375
398
  * @returns Array of [menu item and helpers, count].
376
399
  */
377
- export function planDiet(mpa, menu, organCapacities = [
400
+ function planDiet(mpa, menu, organCapacities = [
378
401
  ["food", null],
379
402
  ["booze", null],
380
403
  ["spleen item", null],
@@ -400,7 +423,7 @@ export function planDiet(mpa, menu, organCapacities = [
400
423
  .map((menuItem) => {
401
424
  const interacting = interactingItems.find(([itemOrEffect]) => menuItem.item === itemOrEffect ||
402
425
  (menuItem.item === $item `pocket wish` &&
403
- menuItem.wishEffect === itemOrEffect));
426
+ menuItem.effect === itemOrEffect));
404
427
  if (interacting) {
405
428
  const [, organSizes] = interacting;
406
429
  return [menuItem, organSizes];
@@ -434,3 +457,127 @@ export function planDiet(mpa, menu, organCapacities = [
434
457
  return planFoodBooze;
435
458
  }
436
459
  }
460
+ class DietEntry {
461
+ menuItems;
462
+ quantity;
463
+ constructor(menuItems, quantity) {
464
+ this.menuItems = menuItems;
465
+ this.quantity = quantity;
466
+ }
467
+ target() {
468
+ return this.menuItems[this.menuItems.length - 1];
469
+ }
470
+ helpers() {
471
+ if (this.menuItems.length > 1) {
472
+ return this.menuItems.slice(0, -1);
473
+ }
474
+ return [];
475
+ }
476
+ expectedAdventures(diet) {
477
+ {
478
+ if (this.menuItems.length === 0 || this.quantity === 0) {
479
+ return 0;
480
+ }
481
+ else {
482
+ const items = this.menuItems.map((m) => m.item);
483
+ const targetItem = this.menuItems[this.menuItems.length - 1].item;
484
+ const fork = itemType(targetItem) === "food" &&
485
+ items.includes($item `Ol' Scratch's salad fork`);
486
+ const mug = itemType(targetItem) === "booze" &&
487
+ items.includes($item `Frosty's frosty mug`);
488
+ return (this.quantity *
489
+ expectedAdventures(this.menuItems[this.menuItems.length - 1].item, {
490
+ forkMug: fork || mug,
491
+ seasoning: items.includes($item `Special Seasoning`),
492
+ mayoflex: items.includes(Mayo.flex),
493
+ refinedPalate: diet.refinedPalate,
494
+ garish: diet.garish,
495
+ saucemaven: diet.saucemaven,
496
+ pinkyRing: diet.pinkyRing,
497
+ tuxedoShirt: diet.tuxedoShirt,
498
+ }));
499
+ }
500
+ }
501
+ }
502
+ expectedValue(mpa, diet, method = "gross") {
503
+ const adventures = this.expectedAdventures(diet);
504
+ const gross = this.quantity *
505
+ (mpa * adventures +
506
+ sumNumbers(this.menuItems.map((menuItem) => menuItem.additionalValue ?? 0)));
507
+ if (method === "gross") {
508
+ return gross;
509
+ }
510
+ else {
511
+ return gross - this.expectedPrice();
512
+ }
513
+ }
514
+ expectedPrice() {
515
+ return (this.quantity *
516
+ sumNumbers(this.menuItems.map((menuItem) => menuItem.price())));
517
+ }
518
+ }
519
+ /**
520
+ * A representation of a potential diet
521
+ */
522
+ export class Diet {
523
+ entries;
524
+ constructor(entries = []) {
525
+ this.entries = entries;
526
+ }
527
+ get refinedPalate() {
528
+ return this.entries.some((dietEntry) => dietEntry.menuItems.some((trialItem) => (trialItem.item === $item `pocket wish` &&
529
+ trialItem.effect === $effect `Refined Palate`) ||
530
+ trialItem.item === $item `toasted brie`));
531
+ }
532
+ get garish() {
533
+ return this.entries.some((dietEntry) => dietEntry.menuItems.some((trialItem) => (trialItem.item === $item `pocket wish` &&
534
+ trialItem.effect === $effect `Gar-ish`) ||
535
+ trialItem.item === $item `potion of the field gar`));
536
+ }
537
+ get saucemaven() {
538
+ return have($skill `Saucemaven`);
539
+ }
540
+ get tuxedoShirt() {
541
+ return have($item `tuxedo shirt`) && canEquip($item `tuxedo shirt`);
542
+ }
543
+ get pinkyRing() {
544
+ return have($item `mafia pinky ring`) && canEquip($item `mafia pinky ring`);
545
+ }
546
+ expectedAdventures() {
547
+ return sumNumbers(this.entries.map((dietEntry) => dietEntry.expectedAdventures(this)));
548
+ }
549
+ expectedValue(mpa, method = "gross") {
550
+ return sumNumbers(this.entries.map((dietEntry) => dietEntry.expectedValue(mpa, this, method)));
551
+ }
552
+ expectedPrice() {
553
+ return sumNumbers(this.entries.map((dietEntry) => dietEntry.expectedPrice()));
554
+ }
555
+ copy() {
556
+ return new Diet([...this.entries]);
557
+ }
558
+ static from(rawDiet) {
559
+ const diet = rawDiet.map((item) => {
560
+ const [menuItems, quantity] = item;
561
+ return new DietEntry(menuItems, quantity);
562
+ });
563
+ return new Diet(diet);
564
+ }
565
+ static plan(mpa, menu, organCapacities = {
566
+ food: "auto",
567
+ booze: "auto",
568
+ spleen: "auto",
569
+ }) {
570
+ const { food, booze, spleen } = organCapacities;
571
+ const plannerCapacity = [];
572
+ if (food) {
573
+ plannerCapacity.push(["food", food === "auto" ? null : food]);
574
+ }
575
+ if (booze) {
576
+ plannerCapacity.push(["booze", booze === "auto" ? null : booze]);
577
+ }
578
+ if (spleen) {
579
+ plannerCapacity.push(["spleen item", spleen === "auto" ? null : spleen]);
580
+ }
581
+ return Diet.from(planDiet(mpa, menu, plannerCapacity));
582
+ }
583
+ }
package/dist/freerun.d.ts CHANGED
@@ -20,4 +20,5 @@ export declare class FreeRun {
20
20
  options?: FreeRunOptions;
21
21
  constructor(name: string, available: () => boolean, macro: Macro, options?: FreeRunOptions);
22
22
  }
23
- export declare function findFreeRun(useFamiliar?: boolean, buyStuff?: boolean): FreeRun | undefined;
23
+ export declare function tryFindFreeRun(useFamiliar?: boolean): FreeRun | null;
24
+ export declare function ensureFreeRun(useFamiliar?: boolean): FreeRun;
package/dist/freerun.js CHANGED
@@ -87,10 +87,13 @@ function cheapestRunSource() {
87
87
  }
88
88
  function cheapestItemRun() {
89
89
  const cheapestRun = cheapestRunSource();
90
- return new FreeRun("Cheap Combat Item", () => retrieveItem(cheapestRun), Macro.trySkill($skill `Asdon Martin: Spring-Loaded Front Bumper`).item(cheapestRunSource()), {
90
+ return new FreeRun("Cheap Combat Item", () => retrieveItem(cheapestRun), Macro.trySkill($skill `Asdon Martin: Spring-Loaded Front Bumper`).item(cheapestRun), {
91
91
  preparation: () => retrieveItem(cheapestRun),
92
92
  });
93
93
  }
94
- export function findFreeRun(useFamiliar = true, buyStuff = true) {
95
- return (freeRuns.find((run) => run.available() && (useFamiliar || run?.options?.familiar)) ?? (buyStuff ? cheapestItemRun() : undefined));
94
+ export function tryFindFreeRun(useFamiliar = true) {
95
+ return (freeRuns.find((run) => run.available() && (useFamiliar || run?.options?.familiar)) ?? null);
96
+ }
97
+ export function ensureFreeRun(useFamiliar = true) {
98
+ return tryFindFreeRun(useFamiliar) ?? cheapestItemRun();
96
99
  }
package/dist/lib.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  /** @module GeneralLibrary */
2
2
  import "core-js/modules/es.object.entries";
3
+ import "core-js/features/array/flat";
3
4
  /**
4
5
  * Returns the current maximum Accordion Thief songs the player can have in their head
5
6
  *