libram 0.5.3 → 0.5.7

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.
@@ -0,0 +1,116 @@
1
+ import { Macro } from "../combat";
2
+ import { Requirement } from "../maximize";
3
+ export declare type FindActionSourceConstraints = {
4
+ /**
5
+ * Function returning true if we only accept familiar-based actions.
6
+ */
7
+ requireFamiliar?: () => boolean;
8
+ /**
9
+ * Function returning true if we only accept sources that are unlimited.
10
+ */
11
+ requireUnlimited?: () => boolean;
12
+ /**
13
+ * Function returning whether to disallow actions requiring familiar change.
14
+ */
15
+ noFamiliar?: () => boolean;
16
+ /**
17
+ * Function returning whether to disallow actions requiring equipment change.
18
+ */
19
+ noRequirements?: () => boolean;
20
+ /**
21
+ * Function returning whether to disallow actions requiring preparation.
22
+ */
23
+ noPreparation?: () => boolean;
24
+ /**
25
+ * Function returning maximum cost of allowed actions. If undefined, allow
26
+ * only actions that cost nothing.
27
+ */
28
+ maximumCost?: () => number;
29
+ };
30
+ export declare type ActionConstraints = {
31
+ /**
32
+ * Equipment requirements to have this action available.
33
+ */
34
+ equipmentRequirements?: () => Requirement;
35
+ /**
36
+ * Familiar required to be in use to have this action available.
37
+ */
38
+ familiar?: () => Familiar;
39
+ /**
40
+ * Miscellaneous preparation to ensure this action is available.
41
+ */
42
+ preparation?: () => boolean;
43
+ /**
44
+ * Cost in meat per usage of this action.
45
+ */
46
+ cost?: () => number;
47
+ };
48
+ /**
49
+ * A combat-based action resource in the game (e.g. a free run or free kill).
50
+ */
51
+ export declare class ActionSource {
52
+ source: Item | Skill | Familiar;
53
+ potential: () => number;
54
+ macro: Macro;
55
+ constraints: ActionConstraints;
56
+ /**
57
+ * @param source Source of the action (e.g. item or skill needed).
58
+ * @param potential Function returning how many times this action can be used.
59
+ * @param macro Macro to execute this action in combat.
60
+ * @param constraints Constraints required for this action to be available.
61
+ */
62
+ constructor(source: Item | Skill | Familiar, potential: () => number, macro: Macro, constraints?: ActionConstraints);
63
+ /**
64
+ * @returns Name of the action source.
65
+ */
66
+ name(): string;
67
+ /**
68
+ * @returns Whether the action is available.
69
+ */
70
+ available(): boolean;
71
+ /**
72
+ * @returns Cost in meat per usage of the action.
73
+ */
74
+ cost(): number;
75
+ /**
76
+ * @returns Whether the action costs 0 meat to use.
77
+ */
78
+ isFree(): boolean;
79
+ /**
80
+ * @returns Whether unlimited uses of the action are available.
81
+ */
82
+ isUnlimited(): boolean;
83
+ /**
84
+ * Create a compound action source with merged constraints.
85
+ * @param others Other actions to have available.
86
+ * @returns Merged constraints, or null if they are inconsistent.
87
+ */
88
+ merge(...others: ActionSource[]): ActionSource | null;
89
+ /**
90
+ * Perform all preparation necessary to make this action available.
91
+ * @param otherRequirements Any other equipment requirements.
92
+ * @returns Whether preparation succeeded.
93
+ */
94
+ prepare(otherRequirements?: Requirement): boolean;
95
+ /**
96
+ * Perform all preparation necessary to make this action available.
97
+ * Throws an error if preparation fails.
98
+ * @param otherRequirements Any other equipment requirements.
99
+ */
100
+ ensure(otherRequirements?: Requirement): void;
101
+ }
102
+ /**
103
+ * Find an available action source subject to constraints.
104
+ * @param actions Action source list.
105
+ * @param constraints Preexisting constraints that restrict possible sources.
106
+ * @returns Available action source satisfying constraints, or null.
107
+ */
108
+ export declare function findActionSource(actions: ActionSource[], constraints?: FindActionSourceConstraints): ActionSource | null;
109
+ /**
110
+ * Count available action sources subject to constraints. Note that, if
111
+ * constraints.maximumCost is high enough, this will return Infinity.
112
+ * @param actions Action source list.
113
+ * @param constraints Preexisting constraints that restrict possible sources.
114
+ * @returns Count of available action sources.
115
+ */
116
+ export declare function actionSourcesAvailable(actions: ActionSource[], constraints?: FindActionSourceConstraints): number;
@@ -0,0 +1,156 @@
1
+ import { useFamiliar } from "kolmafia";
2
+ import { Macro } from "../combat";
3
+ import { Requirement } from "../maximize";
4
+ import { sum } from "../utils";
5
+ function mergeConstraints(...allConstraints) {
6
+ const familiars = allConstraints
7
+ .map((constraints) => constraints.familiar)
8
+ .filter((familiar) => familiar);
9
+ if (familiars.length > 1) {
10
+ // Inconsistent requirements.
11
+ return null;
12
+ }
13
+ return {
14
+ equipmentRequirements: () => Requirement.merge([
15
+ ...allConstraints.map((constraints) => constraints.equipmentRequirements?.() ?? new Requirement([], {})),
16
+ ]),
17
+ preparation: () => {
18
+ let success = true;
19
+ for (const constraints of allConstraints) {
20
+ success =
21
+ success && (!constraints.preparation || constraints.preparation());
22
+ }
23
+ return success;
24
+ },
25
+ familiar: familiars.find((familiar) => familiar),
26
+ cost: () => sum(allConstraints, (constraints) => constraints.cost?.() ?? 0),
27
+ };
28
+ }
29
+ /**
30
+ * A combat-based action resource in the game (e.g. a free run or free kill).
31
+ */
32
+ export class ActionSource {
33
+ source;
34
+ potential; // Infinity: unlimited
35
+ macro;
36
+ constraints;
37
+ /**
38
+ * @param source Source of the action (e.g. item or skill needed).
39
+ * @param potential Function returning how many times this action can be used.
40
+ * @param macro Macro to execute this action in combat.
41
+ * @param constraints Constraints required for this action to be available.
42
+ */
43
+ constructor(source, potential, macro, constraints = {}) {
44
+ this.source = source;
45
+ this.potential = potential;
46
+ this.macro = macro;
47
+ this.constraints = constraints;
48
+ }
49
+ /**
50
+ * @returns Name of the action source.
51
+ */
52
+ name() {
53
+ return this.source.toString();
54
+ }
55
+ /**
56
+ * @returns Whether the action is available.
57
+ */
58
+ available() {
59
+ return this.potential() > 0;
60
+ }
61
+ /**
62
+ * @returns Cost in meat per usage of the action.
63
+ */
64
+ cost() {
65
+ return this.constraints.cost ? this.constraints.cost() : 0;
66
+ }
67
+ /**
68
+ * @returns Whether the action costs 0 meat to use.
69
+ */
70
+ isFree() {
71
+ return !this.cost || this.cost() === 0;
72
+ }
73
+ /**
74
+ * @returns Whether unlimited uses of the action are available.
75
+ */
76
+ isUnlimited() {
77
+ return this.potential() === Infinity;
78
+ }
79
+ /**
80
+ * Create a compound action source with merged constraints.
81
+ * @param others Other actions to have available.
82
+ * @returns Merged constraints, or null if they are inconsistent.
83
+ */
84
+ merge(...others) {
85
+ const actions = [this, ...others];
86
+ const constraints = mergeConstraints(...actions.map((action) => action.constraints));
87
+ if (constraints === null) {
88
+ // Inconsistent constraints - no path forward here.
89
+ return null;
90
+ }
91
+ return new ActionSource(this.source, () => sum(actions, (action) => action.potential()), Macro.step(...actions.map((action) => action.macro)), constraints);
92
+ }
93
+ /**
94
+ * Perform all preparation necessary to make this action available.
95
+ * @param otherRequirements Any other equipment requirements.
96
+ * @returns Whether preparation succeeded.
97
+ */
98
+ prepare(otherRequirements) {
99
+ if (this.constraints.familiar?.()) {
100
+ if (!useFamiliar(this.constraints.familiar()))
101
+ return false;
102
+ }
103
+ if (this.constraints.equipmentRequirements) {
104
+ const requirement = otherRequirements
105
+ ? otherRequirements.merge(this.constraints.equipmentRequirements())
106
+ : this.constraints.equipmentRequirements();
107
+ if (!requirement.maximize())
108
+ return false;
109
+ }
110
+ if (this.constraints.preparation)
111
+ return this.constraints.preparation();
112
+ return true;
113
+ }
114
+ /**
115
+ * Perform all preparation necessary to make this action available.
116
+ * Throws an error if preparation fails.
117
+ * @param otherRequirements Any other equipment requirements.
118
+ */
119
+ ensure(otherRequirements) {
120
+ if (!this.prepare(otherRequirements)) {
121
+ throw new Error(`Failed to prepare action ${this.name()}.`);
122
+ }
123
+ }
124
+ }
125
+ function filterAction(action, constraints) {
126
+ return (action.available() &&
127
+ !(constraints.requireFamiliar?.() && !action.constraints.familiar) &&
128
+ !(constraints.requireUnlimited?.() && !action.isUnlimited()) &&
129
+ !(constraints.noFamiliar?.() && action.constraints.familiar) &&
130
+ !(constraints.noRequirements?.() && action.constraints.equipmentRequirements) &&
131
+ !(constraints.noPreparation?.() && action.constraints.preparation) &&
132
+ action.cost() <= (constraints.maximumCost?.() ?? 0));
133
+ }
134
+ /**
135
+ * Find an available action source subject to constraints.
136
+ * @param actions Action source list.
137
+ * @param constraints Preexisting constraints that restrict possible sources.
138
+ * @returns Available action source satisfying constraints, or null.
139
+ */
140
+ export function findActionSource(actions, constraints = {}) {
141
+ return (actions
142
+ .filter((actions) => filterAction(actions, constraints))
143
+ .sort((a, b) => a.cost() - b.cost())
144
+ .find((action) => action) ?? null);
145
+ }
146
+ /**
147
+ * Count available action sources subject to constraints. Note that, if
148
+ * constraints.maximumCost is high enough, this will return Infinity.
149
+ * @param actions Action source list.
150
+ * @param constraints Preexisting constraints that restrict possible sources.
151
+ * @returns Count of available action sources.
152
+ */
153
+ export function actionSourcesAvailable(actions, constraints = {}) {
154
+ // TODO: This will overcount if any Actions share a counter
155
+ return sum(actions.filter((action) => filterAction(action, constraints ?? {})), (action) => action.potential());
156
+ }
@@ -0,0 +1,14 @@
1
+ import { ActionSource, FindActionSourceConstraints } from "./ActionSource";
2
+ /**
3
+ * Find an available free kill source subject to constraints.
4
+ * @param constraints Preexisting constraints that restrict possible sources.
5
+ * @returns Free kill source satisfying constraints, or null.
6
+ */
7
+ export declare function tryFindFreeKill(constraints?: FindActionSourceConstraints): ActionSource | null;
8
+ /**
9
+ * Ensure an available free kill source subject to constraints.
10
+ * Throws an error if no source can be found.
11
+ * @param constraints Preexisting constraints that restrict possible sources.
12
+ * @returns Free kill source satisfying constraints.
13
+ */
14
+ export declare function ensureFreeKill(constraints?: FindActionSourceConstraints): ActionSource;
@@ -0,0 +1,88 @@
1
+ import { mallPrice, myLightning, restoreMp, retrieveItem, use } from "kolmafia";
2
+ import { Macro } from "../combat";
3
+ import { have } from "../lib";
4
+ import { Requirement } from "../maximize";
5
+ import { get } from "../property";
6
+ import { $familiar, $item, $items, $skill } from "../template-string";
7
+ import { ActionSource, findActionSource, } from "./ActionSource";
8
+ import * as AsdonMartin from "../resources/2017/AsdonMartin";
9
+ const freeKillSources = [
10
+ // Free limited sources
11
+ new ActionSource($skill `Gingerbread Mob Hit`, () => !get("_gingerbreadMobHitUsed") && have($skill `Gingerbread Mob Hit`)
12
+ ? 1
13
+ : 0, Macro.skill($skill `Gingerbread Mob Hit`), {
14
+ preparation: () => restoreMp(30),
15
+ }),
16
+ new ActionSource($skill `Shattering Punch`, () => have($skill `Shattering Punch`) ? 3 - get("_shatteringPunchUsed") : 0, Macro.skill($skill `Shattering Punch`), {
17
+ preparation: () => restoreMp(30),
18
+ }),
19
+ new ActionSource($item `replica bat-oomerang`, () => have($item `replica bat-oomerang`)
20
+ ? 3 - get("_usedReplicaBatoomerang")
21
+ : 0, Macro.item($item `replica bat-oomerang`)),
22
+ new ActionSource($item `The Jokester's gun`, () => !get("_firedJokestersGun") && have($item `The Jokester's gun`) ? 1 : 0, Macro.skill($skill `Fire the Jokester's Gun`), {
23
+ equipmentRequirements: () => new Requirement([], {
24
+ forceEquip: $items `The Jokester's gun`,
25
+ }),
26
+ }),
27
+ new ActionSource($item `Lil' Doctor™ bag`, () => (have($item `Lil' Doctor™ bag`) ? 3 - get("_chestXRayUsed") : 0), Macro.skill($skill `Chest X-Ray`), {
28
+ equipmentRequirements: () => new Requirement([], {
29
+ forceEquip: $items `Lil' Doctor™ bag`,
30
+ }),
31
+ }),
32
+ new ActionSource($skill `Asdon Martin: Missile Launcher`, () => (!get("_missileLauncherUsed") && AsdonMartin.installed() ? 1 : 0), Macro.skill($skill `Asdon Martin: Missile Launcher`), {
33
+ preparation: () => AsdonMartin.fillTo(100),
34
+ }),
35
+ // Heavy Rains
36
+ new ActionSource($skill `Lightning Strike`, () => (have($skill `Lightning Strike`) ? Math.floor(myLightning() / 20) : 0), Macro.skill($skill `Lightning Strike`)),
37
+ // Expensive limited sources
38
+ new ActionSource($item `powdered madness`, () => 5 - get("_powderedMadnessUses"), Macro.item($item `powdered madness`), {
39
+ preparation: () => retrieveItem($item `powdered madness`),
40
+ cost: () => mallPrice($item `powdered madness`),
41
+ }),
42
+ new ActionSource($familiar `Puck Man`, () => (have($familiar `Puck Man`) ? 20 - get("_powerPillUses") : 0), Macro.item($item `power pill`), {
43
+ familiar: () => $familiar `Puck Man`,
44
+ preparation: () => retrieveItem($item `power pill`),
45
+ cost: () => mallPrice($item `power pill`),
46
+ }),
47
+ new ActionSource($familiar `Ms. Puck Man`, () => (have($familiar `Ms. Puck Man`) ? 20 - get("_powerPillUses") : 0), Macro.item($item `power pill`), {
48
+ familiar: () => $familiar `Ms. Puck Man`,
49
+ preparation: () => retrieveItem($item `power pill`),
50
+ cost: () => mallPrice($item `power pill`),
51
+ }),
52
+ // Expensive unlimited sources
53
+ new ActionSource($skill `Shocking Lick`, () => Infinity, Macro.skill($skill `Shocking Lick`), {
54
+ preparation: () => {
55
+ if (get("shockingLickCharges") === 0 &&
56
+ retrieveItem($item `battery (9-Volt)`)) {
57
+ use($item `battery (9-Volt)`);
58
+ }
59
+ return get("shockingLickCharges") > 0;
60
+ },
61
+ cost: () => mallPrice($item `battery (AAA)`) * 4,
62
+ }),
63
+ ...$items `Daily Affirmation: Think Win-Lose, superduperheated metal`.map((item) => new ActionSource(item, () => Infinity, Macro.item(item), {
64
+ preparation: () => retrieveItem(item),
65
+ cost: () => mallPrice(item),
66
+ })),
67
+ ];
68
+ /**
69
+ * Find an available free kill source subject to constraints.
70
+ * @param constraints Preexisting constraints that restrict possible sources.
71
+ * @returns Free kill source satisfying constraints, or null.
72
+ */
73
+ export function tryFindFreeKill(constraints) {
74
+ return findActionSource(freeKillSources, constraints);
75
+ }
76
+ /**
77
+ * Ensure an available free kill source subject to constraints.
78
+ * Throws an error if no source can be found.
79
+ * @param constraints Preexisting constraints that restrict possible sources.
80
+ * @returns Free kill source satisfying constraints.
81
+ */
82
+ export function ensureFreeKill(constraints) {
83
+ const source = tryFindFreeKill(constraints);
84
+ if (!source) {
85
+ throw new Error("Failed to ensure Free Kill source");
86
+ }
87
+ return source;
88
+ }
@@ -0,0 +1,14 @@
1
+ import { ActionSource, FindActionSourceConstraints } from "./ActionSource";
2
+ /**
3
+ * Find an available free run source subject to constraints.
4
+ * @param constraints Preexisting constraints that restrict possible sources.
5
+ * @returns Free run source satisfying constraints, or null.
6
+ */
7
+ export declare function tryFindFreeRun(constraints?: FindActionSourceConstraints): ActionSource | null;
8
+ /**
9
+ * Ensure an available free run source subject to constraints.
10
+ * Throws an error if no source can be found.
11
+ * @param constraints Preexisting constraints that restrict possible sources.
12
+ * @returns Free run source satisfying constraints.
13
+ */
14
+ export declare function ensureFreeRun(constraints?: FindActionSourceConstraints): ActionSource;
@@ -0,0 +1,149 @@
1
+ import { cliExecute, mallPrice, myTurncount, restoreMp, retrieveItem, visitUrl, } from "kolmafia";
2
+ import { Macro } from "../combat";
3
+ import { ensureEffect, getFoldGroup, getSongCount, getSongLimit, have, } from "../lib";
4
+ import { Requirement } from "../maximize";
5
+ import { get } from "../property";
6
+ import { $effect, $item, $items, $skill } from "../template-string";
7
+ import { ActionSource, findActionSource, } from "./ActionSource";
8
+ import * as Bandersnatch from "../resources/2009/Bandersnatch";
9
+ import * as StompingBoots from "../resources/2011/StompingBoots";
10
+ import * as AsdonMartin from "../resources/2017/AsdonMartin";
11
+ // Value of _lastCombatStarted the last time we updated scrapbook charges.
12
+ let scrapbookChargesLastUpdated = get("_lastCombatStarted");
13
+ // Free unlimited source every 30 turns.
14
+ // Does not work on special monsters so needs a backup, see tryFindFreeRun.
15
+ // banishedMonsters isn't updated if the free run succeeds on an unbanishable monster
16
+ const asdonMartinSource = new ActionSource($skill `Asdon Martin: Spring-Loaded Front Bumper`, () => {
17
+ if (!AsdonMartin.installed())
18
+ return 0;
19
+ const banishes = get("banishedMonsters").split(":");
20
+ const bumperIndex = banishes
21
+ .map((string) => string.toLowerCase())
22
+ .indexOf("spring-loaded front bumper");
23
+ if (bumperIndex === -1)
24
+ return 1;
25
+ return myTurncount() - parseInt(banishes[bumperIndex + 1]) > 30 ? 1 : 0;
26
+ }, Macro.trySkill($skill `Asdon Martin: Spring-Loaded Front Bumper`), {
27
+ preparation: () => AsdonMartin.fillTo(50),
28
+ });
29
+ const freeRunSources = [
30
+ // Free limited sources
31
+ new ActionSource(Bandersnatch.familiar, () => (have($effect `Ode to Booze`) || getSongCount() < getSongLimit()) &&
32
+ Bandersnatch.couldRunaway()
33
+ ? Bandersnatch.getRemainingRunaways()
34
+ : 0, Macro.step("runaway"), {
35
+ equipmentRequirements: () => new Requirement(["Familiar Weight"], {}),
36
+ preparation: () => {
37
+ ensureEffect($effect `Ode to Booze`);
38
+ return have($effect `Ode to Booze`);
39
+ },
40
+ familiar: () => Bandersnatch.familiar,
41
+ }),
42
+ new ActionSource(StompingBoots.familiar, () => StompingBoots.couldRunaway() ? StompingBoots.getRemainingRunaways() : 0, Macro.step("runaway"), {
43
+ equipmentRequirements: () => new Requirement(["Familiar Weight"], {}),
44
+ familiar: () => StompingBoots.familiar,
45
+ }),
46
+ new ActionSource($skill `Snokebomb`, () => (have($skill `Snokebomb`) ? 3 - get("_snokebombUsed") : 0), Macro.skill($skill `Snokebomb`), {
47
+ preparation: () => restoreMp(50),
48
+ }),
49
+ new ActionSource($skill `Emotionally Chipped`, () => (have($skill `Emotionally Chipped`) ? 3 - get("_feelHatredUsed") : 0), Macro.skill($skill `Feel Hatred`)),
50
+ new ActionSource($item `Kremlin's Greatest Briefcase`, () => have($item `Kremlin's Greatest Briefcase`)
51
+ ? 3 - get("_kgbTranquilizerDartUses")
52
+ : 0, Macro.skill($skill `KGB tranquilizer dart`), {
53
+ equipmentRequirements: () => new Requirement([], {
54
+ forceEquip: $items `Kremlin's Greatest Briefcase`,
55
+ }),
56
+ }),
57
+ new ActionSource($item `latte lovers member's mug`, () => have($item `latte lovers member's mug`) && !get("_latteBanishUsed")
58
+ ? 1
59
+ : 0, Macro.skill($skill `Throw Latte on Opponent`), {
60
+ equipmentRequirements: () => new Requirement([], { forceEquip: $items `latte lovers member's mug` }),
61
+ }),
62
+ new ActionSource($item `Lil' Doctor™ bag`, () => (have($item `Lil' Doctor™ bag`) ? 3 - get("_reflexHammerUsed") : 0), Macro.skill($skill `Reflex Hammer`), {
63
+ equipmentRequirements: () => new Requirement([], { forceEquip: $items `Lil' Doctor™ bag` }),
64
+ }),
65
+ new ActionSource($item `mafia middle finger ring`, () => have($item `mafia middle finger ring`) &&
66
+ !get("_mafiaMiddleFingerRingUsed")
67
+ ? 1
68
+ : 0, Macro.skill($skill `Show them your ring`), {
69
+ equipmentRequirements: () => new Requirement([], { forceEquip: $items `mafia middle finger ring` }),
70
+ }),
71
+ new ActionSource($item `V for Vivala mask`, () => have($item `V for Vivala mask`) && !get("_vmaskBanisherUsed") ? 1 : 0, Macro.skill($skill `Creepy Grin`), {
72
+ equipmentRequirements: () => new Requirement([], { forceEquip: $items `V for Vivala mask` }),
73
+ preparation: () => restoreMp(30),
74
+ }),
75
+ new ActionSource($item `stinky cheese eye`, () => getFoldGroup($item `stinky cheese eye`).some((item) => have(item)) &&
76
+ !get("_stinkyCheeseBanisherUsed")
77
+ ? 1
78
+ : 0, Macro.skill($skill `Give Your Opponent the Stinkeye`), {
79
+ equipmentRequirements: () => new Requirement([], { forceEquip: $items `stinky cheese eye` }),
80
+ preparation: () => {
81
+ if (!have($item `stinky cheese eye`)) {
82
+ cliExecute(`fold stinky cheese eye`);
83
+ }
84
+ return have($item `stinky cheese eye`);
85
+ },
86
+ }),
87
+ new ActionSource($item `navel ring of navel gazing`, () => have($item `navel ring of navel gazing`)
88
+ ? Math.max(0, 3 - get("_navelRunaways"))
89
+ : 0, Macro.step("runaway"), {
90
+ equipmentRequirements: () => new Requirement([], { forceEquip: $items `navel ring of navel gazing` }),
91
+ }),
92
+ new ActionSource($item `Greatest American Pants`, () => have($item `Greatest American Pants`)
93
+ ? Math.max(0, 3 - get("_navelRunaways"))
94
+ : 0, Macro.step("runaway"), {
95
+ equipmentRequirements: () => new Requirement([], { forceEquip: $items `Greatest American Pants` }),
96
+ }),
97
+ new ActionSource($skill `Show your boring familiar pictures`, () => {
98
+ if (have($item `familiar scrapbook`)) {
99
+ if (scrapbookChargesLastUpdated !== get("_lastCombatStarted")) {
100
+ visitUrl("desc_item.php?whichitem=463063785");
101
+ scrapbookChargesLastUpdated = get("_lastCombatStarted");
102
+ }
103
+ return Math.floor(get("scrapbookCharges") / 100);
104
+ }
105
+ return 0;
106
+ }, Macro.skill($skill `Show your boring familiar pictures`), {
107
+ equipmentRequirements: () => new Requirement([], { forceEquip: $items `familiar scrapbook` }),
108
+ }),
109
+ new ActionSource($item `peppermint parasol`, () => Math.max(0, 3 - get("_navelRunaways")), Macro.item($item `peppermint parasol`), {
110
+ preparation: () => retrieveItem($item `peppermint parasol`),
111
+ cost: () => Math.min(mallPrice($item `peppermint sprout`) * 5, mallPrice($item `peppermint parasol`)) / 10, // Breaks after 10 successful runaways.
112
+ }),
113
+ new ActionSource($item `human musk`, () => Math.max(0, 3 - get("_humanMuskUses")), Macro.item($item `human musk`), {
114
+ preparation: () => retrieveItem($item `human musk`),
115
+ cost: () => mallPrice($item `human musk`),
116
+ }),
117
+ // Expensive unlimited sources
118
+ ...$items `Louder Than Bomb, divine champagne popper, tennis ball`.map((item) => new ActionSource(item, () => Infinity, Macro.item(item), {
119
+ preparation: () => retrieveItem(item),
120
+ cost: () => mallPrice(item),
121
+ })),
122
+ ];
123
+ /**
124
+ * Find an available free run source subject to constraints.
125
+ * @param constraints Preexisting constraints that restrict possible sources.
126
+ * @returns Free run source satisfying constraints, or null.
127
+ */
128
+ export function tryFindFreeRun(constraints) {
129
+ let source = findActionSource(freeRunSources, constraints);
130
+ // Always try to use Asdon Martin: Spring-Loaded Front Bumper first,
131
+ // but only if another source has been found.
132
+ if (source && asdonMartinSource.available()) {
133
+ source = asdonMartinSource.merge(source);
134
+ }
135
+ return source;
136
+ }
137
+ /**
138
+ * Ensure an available free run source subject to constraints.
139
+ * Throws an error if no source can be found.
140
+ * @param constraints Preexisting constraints that restrict possible sources.
141
+ * @returns Free run source satisfying constraints.
142
+ */
143
+ export function ensureFreeRun(constraints) {
144
+ const source = tryFindFreeRun(constraints);
145
+ if (!source) {
146
+ throw new Error("Failed to ensure Free Run source");
147
+ }
148
+ return source;
149
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./ActionSource";
2
+ export * from "./FreeKill";
3
+ export * from "./FreeRun";
@@ -0,0 +1,3 @@
1
+ export * from "./ActionSource";
2
+ export * from "./FreeKill";
3
+ export * from "./FreeRun";
package/dist/ascend.js CHANGED
@@ -84,7 +84,7 @@ export function ascend(path, playerClass, lifestyle, moon, consumable = $item `a
84
84
  if (moonId < 1 || moonId > 9)
85
85
  throw `Invalid moon ${moon}`;
86
86
  if (consumable &&
87
- !$items `astral six-pack, astral hot dog dinner`.includes(consumable))
87
+ !$items `astral six-pack, astral hot dog dinner, carton of astral energy drinks`.includes(consumable))
88
88
  throw `Invalid consumable ${consumable}`;
89
89
  if (pet &&
90
90
  !$items `astral bludgeon, astral shield, astral chapeau, astral bracer, astral longbow, astral shorts, astral mace, astral ring, astral statuette, astral pistol, astral mask, astral pet sweater, astral shirt, astral belt`.includes(pet))
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from "./actions";
1
2
  export * from "./ascend";
2
3
  export * from "./Clan";
3
4
  export * from "./combat";
@@ -18,7 +19,6 @@ export * from "./utils";
18
19
  export { get, PropertiesManager, set, setProperties, withProperties, withProperty, withChoices, withChoice, } from "./property";
19
20
  export { get as getModifier } from "./modifier";
20
21
  export { Modifiers } from "./modifier";
21
- export * from "./freerun";
22
22
  export * as Dreadsylvania from "./dungeons/Dreadsylvania";
23
23
  export * as Hobopolis from "./dungeons/Hobopolis";
24
24
  export * as SlimeTube from "./dungeons/SlimeTube";
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ export * from "./actions";
1
2
  export * from "./ascend";
2
3
  export * from "./Clan";
3
4
  export * from "./combat";
@@ -17,7 +18,6 @@ export * as property from "./property";
17
18
  export * from "./utils";
18
19
  export { get, PropertiesManager, set, setProperties, withProperties, withProperty, withChoices, withChoice, } from "./property";
19
20
  export { get as getModifier } from "./modifier";
20
- export * from "./freerun";
21
21
  export * as Dreadsylvania from "./dungeons/Dreadsylvania";
22
22
  export * as Hobopolis from "./dungeons/Hobopolis";
23
23
  export * as SlimeTube from "./dungeons/SlimeTube";
@@ -1,3 +1,4 @@
1
+ import "core-js/modules/es.object.values";
1
2
  import { BooleanModifier, ClassModifier, EffectModifier, MonsterModifier, NumericModifier, SkillModifier, StatModifier, StringModifier } from "./modifierTypes";
2
3
  export declare function get(name: BooleanModifier, subject?: string | Item | Effect): boolean;
3
4
  export declare function get(name: ClassModifier, subject: string | Item): Class;
package/dist/modifier.js CHANGED
@@ -1,3 +1,4 @@
1
+ import "core-js/modules/es.object.values";
1
2
  import { booleanModifier, classModifier, effectModifier, monsterModifier, numericModifier, skillModifier, statModifier, stringModifier, } from "kolmafia";
2
3
  import { arrayContains } from "./utils";
3
4
  import { booleanModifiers, classModifiers, effectModifiers, monsterModifiers, numericModifiers, skillModifiers, statModifiers, stringModifiers, } from "./modifierTypes";
package/dist/mood.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import "core-js/modules/es.object.values";
1
2
  export declare abstract class MpSource {
2
3
  usesRemaining(): number | null;
3
4
  abstract availableMpMin(): number;
package/dist/mood.js CHANGED
@@ -1,3 +1,4 @@
1
+ import "core-js/modules/es.object.values";
1
2
  import { availableAmount, buy, cliExecute, eat, effectModifier, haveEffect, haveSkill, hpCost, itemAmount, mallPrice, mpCost, myHp, myMaxmp, myMp, numericModifier, retrieveItem, toEffect, toSkill, turnsPerCast, use, useSkill, } from "kolmafia";
2
3
  import { getActiveSongs, have, isSong } from "./lib";
3
4
  import { get } from "./property";
@@ -130,13 +131,25 @@ class PotionMoodElement extends MoodElement {
130
131
  if (mallPrice(this.potion) > this.maxPricePerTurn * turnsPerUse) {
131
132
  return false;
132
133
  }
134
+ //integer part
133
135
  if (effectTurns < ensureTurns) {
134
- const uses = (ensureTurns - effectTurns) / turnsPerUse;
136
+ const uses = Math.floor((ensureTurns - effectTurns) / turnsPerUse);
135
137
  const quantityToBuy = clamp(uses - availableAmount(this.potion), 0, 100);
136
- buy(quantityToBuy, this.potion, this.maxPricePerTurn * turnsPerUse);
138
+ buy(quantityToBuy, this.potion, Math.floor(this.maxPricePerTurn * turnsPerUse));
137
139
  const quantityToUse = clamp(uses, 0, availableAmount(this.potion));
138
140
  use(quantityToUse, this.potion);
139
141
  }
142
+ //fractional part
143
+ const remainingDifference = ensureTurns - haveEffect(effect);
144
+ if (remainingDifference > 0) {
145
+ const price = this.maxPricePerTurn * remainingDifference;
146
+ if (price <= mallPrice(this.potion)) {
147
+ if (availableAmount(this.potion) ||
148
+ buy(1, this.potion, Math.floor(price))) {
149
+ use(1, this.potion);
150
+ }
151
+ }
152
+ }
140
153
  return haveEffect(effect) >= ensureTurns;
141
154
  }
142
155
  }