libram 0.4.5 → 0.4.9

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,56 +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: get("_distentionPillUsed") ? 0 : 1,
65
- size: -1,
66
- },
67
- ],
68
- [
69
- $item `synthetic dog hair pill`,
70
- {
71
- organ: "booze",
72
- maximum: get("_syntheticDogHairPillUsed") ? 0 : 1,
73
- size: -1,
74
- },
75
- ],
76
- [
77
- $item `cuppa Voraci tea`,
78
- { organ: "food", maximum: get("_voraciTeaUsed") ? 0 : 1, size: -1 },
79
- ],
80
- [
81
- $item `cuppa Sobrie tea`,
82
- { organ: "booze", maximum: get("_sobrieTeaUsed") ? 0 : 1, size: -1 },
83
- ],
84
- [
85
- $item `mojo filter`,
86
- {
87
- organ: "spleen item",
88
- maximum: 3 - get("currentMojoFilters"),
89
- size: -1,
90
- },
91
- ],
92
- [$item `spice melange`, { maximum: get("spiceMelangeUsed") ? 0 : 1 }],
93
- [
94
- $item `Ultra Mega Sour Ball`,
95
- { maximum: get("_ultraMegaSourBallUsed") ? 0 : 1 },
96
- ],
97
- [
98
- $item `The Plumber's mushroom stew`,
99
- { maximum: get("_plumbersMushroomStewEaten") ? 0 : 1 },
100
- ],
101
- [$item `The Mad Liquor`, { maximum: get("_madLiquorDrunk") ? 0 : 1 }],
102
- [
103
- $item `Doc Clock's thyme cocktail`,
104
- { maximum: get("_docClocksThymeCocktailDrunk") ? 0 : 1 },
105
- ],
106
- [$item `Mr. Burnsger`, { maximum: get("_mrBurnsgerEaten") ? 0 : 1 }],
107
- ]);
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
+ }
108
117
  /**
109
118
  * Construct a new menu item, possibly with extra properties. Items in MenuItem.defaultOptions have intelligent defaults.
110
119
  * @param item Item to add to menu.
@@ -112,17 +121,22 @@ export class MenuItem {
112
121
  * @param options.size Override item organ size. Necessary for any non-food/booze/spleen item.
113
122
  * @param options.maximum Maximum uses remaining today, or "auto" to check dailyusesleft Mafia property.
114
123
  * @param options.additionalValue Additional value (positive) or cost (negative) to consider with item, e.g. from buffs.
115
- * @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
116
127
  */
117
128
  constructor(item, options = {}) {
118
- const { size, organ, maximum, additionalValue, wishEffect } = {
129
+ const { size, organ, maximum, additionalValue, effect, priceOverride, mayo, data, } = {
119
130
  ...options,
120
- ...(MenuItem.defaultOptions.get(item) ?? {}),
131
+ ...(MenuItem.defaultOptions().get(item) ?? {}),
121
132
  };
122
133
  this.item = item;
123
134
  this.maximum = maximum === "auto" ? item.dailyusesleft : maximum;
124
135
  this.additionalValue = additionalValue;
125
- this.wishEffect = wishEffect;
136
+ this.effect = effect;
137
+ this.priceOverride = priceOverride;
138
+ this.mayo = mayo;
139
+ this.data = data;
126
140
  const typ = itemType(this.item);
127
141
  this.organ = organ ?? (isOrgan(typ) ? typ : undefined);
128
142
  this.size =
@@ -136,13 +150,17 @@ export class MenuItem {
136
150
  : 0);
137
151
  }
138
152
  equals(other) {
139
- return this.item === other.item && this.wishEffect === other.wishEffect;
153
+ return this.item === other.item && this.effect === other.effect;
140
154
  }
141
155
  toString() {
156
+ if (this.effect) {
157
+ return `${this.item}:${this.effect}`;
158
+ }
142
159
  return this.item.toString();
143
160
  }
144
161
  price() {
145
- 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)));
146
164
  }
147
165
  }
148
166
  const organs = ["food", "booze", "spleen item"];
@@ -152,20 +170,24 @@ function isOrgan(x) {
152
170
  class DietPlanner {
153
171
  mpa;
154
172
  menu;
173
+ mayoLookup;
155
174
  fork;
156
175
  mug;
157
176
  seasoning;
158
- mayoflex;
159
177
  spleenValue = 0;
160
178
  constructor(mpa, menu) {
161
179
  this.mpa = mpa;
162
180
  this.fork = menu.find((item) => item.item === $item `Ol' Scratch's salad fork`);
163
181
  this.mug = menu.find((item) => item.item === $item `Frosty's frosty mug`);
164
182
  this.seasoning = menu.find((item) => item.item === $item `Special Seasoning`);
165
- this.mayoflex =
166
- getWorkshed() === $item `portable Mayo Clinic`
167
- ? menu.find((item) => item.item === $item `Mayoflex`)
168
- : 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
+ }
169
191
  this.menu = menu.filter((item) => item.organ);
170
192
  if (menu.length > 100) {
171
193
  mallPrices("food");
@@ -200,15 +222,19 @@ class DietPlanner {
200
222
  this.mpa > mallPrice($item `Special Seasoning`)) {
201
223
  helpers.push(this.seasoning);
202
224
  }
203
- if (this.mayoflex &&
204
- itemType(menuItem.item) === "food" &&
205
- this.mpa > npcPrice($item `Mayoflex`)) {
206
- 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);
207
231
  }
208
232
  const defaultModifiers = {
209
233
  forkMug: false,
210
234
  seasoning: this.seasoning ? helpers.includes(this.seasoning) : false,
211
- mayoflex: this.mayoflex ? helpers.includes(this.mayoflex) : false,
235
+ mayoflex: this.mayoLookup.size
236
+ ? helpers.some((item) => item.item === Mayo.flex)
237
+ : false,
212
238
  refinedPalate: have($effect `Refined Palate`),
213
239
  garish: have($effect `Gar-ish`),
214
240
  saucemaven: have($skill `Saucemaven`),
@@ -294,10 +320,10 @@ class DietPlanner {
294
320
  }
295
321
  const organCapacitiesWith = [...organCapacitiesWithMap];
296
322
  const isRefinedPalate = (trialItem.item === $item `pocket wish` &&
297
- trialItem.wishEffect === $effect `Refined Palate`) ||
323
+ trialItem.effect === $effect `Refined Palate`) ||
298
324
  trialItem.item === $item `toasted brie`;
299
325
  const isGarish = (trialItem.item === $item `pocket wish` &&
300
- trialItem.wishEffect === $effect `Gar-ish`) ||
326
+ trialItem.effect === $effect `Gar-ish`) ||
301
327
  trialItem.item === $item `potion of the field gar`;
302
328
  const [valueWithout, planWithout] = this.planOrgansWithTrials(organCapacities, trialItems.slice(1), overrideModifiers);
303
329
  const [valueWith, planWith] = this.planOrgansWithTrials(organCapacitiesWith, trialItems.slice(1), {
@@ -371,7 +397,7 @@ const interactingItems = [
371
397
  * @param organCapacities Optional override of each organ's capacity.
372
398
  * @returns Array of [menu item and helpers, count].
373
399
  */
374
- export function planDiet(mpa, menu, organCapacities = [
400
+ function planDiet(mpa, menu, organCapacities = [
375
401
  ["food", null],
376
402
  ["booze", null],
377
403
  ["spleen item", null],
@@ -397,7 +423,7 @@ export function planDiet(mpa, menu, organCapacities = [
397
423
  .map((menuItem) => {
398
424
  const interacting = interactingItems.find(([itemOrEffect]) => menuItem.item === itemOrEffect ||
399
425
  (menuItem.item === $item `pocket wish` &&
400
- menuItem.wishEffect === itemOrEffect));
426
+ menuItem.effect === itemOrEffect));
401
427
  if (interacting) {
402
428
  const [, organSizes] = interacting;
403
429
  return [menuItem, organSizes];
@@ -431,3 +457,127 @@ export function planDiet(mpa, menu, organCapacities = [
431
457
  return planFoodBooze;
432
458
  }
433
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
  *