From 9c286bb629185746392bdcffe86362b408742261 Mon Sep 17 00:00:00 2001 From: Ignose Date: Fri, 15 Nov 2024 10:02:59 -0500 Subject: [PATCH 01/24] Better stillsuit control --- package.json | 2 +- src/tasks/aftercoreleg.ts | 14 ++++++++++ src/tasks/robotrunleg.ts | 15 +++++++++++ src/tasks/utils.ts | 55 ++++++++++++++++++++++++++++++++++++++- yarn.lock | 8 +++--- 5 files changed, 88 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index fe609ea..775c5e6 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "build": "^0.1.4", "grimoire-kolmafia": "^0.3.25", "kolmafia": "^5.27815.0", - "libram": "^0.9.2", + "libram": "^0.9.17", "mafia-shared-relay": "0.0.7", "webpack": "^5.89.0" }, diff --git a/src/tasks/aftercoreleg.ts b/src/tasks/aftercoreleg.ts index 391391d..4481109 100644 --- a/src/tasks/aftercoreleg.ts +++ b/src/tasks/aftercoreleg.ts @@ -3,6 +3,8 @@ import { availableAmount, buy, cliExecute, + equip, + familiarEquippedEquipment, getCampground, getClanName, getWorkshed, @@ -52,6 +54,7 @@ import { have, Macro, set, + StillSuit, TrainSet, uneffect, } from "libram"; @@ -92,6 +95,8 @@ const stations = [ Station.CANDY_FACTORY, // candies ] as Cycle; +const bestStillsuitFamiliar = StillSuit.bestFamiliar("Item Drop"); + export function AftercoreQuest(): Quest { return { name: "Aftercore", @@ -108,6 +113,15 @@ export function AftercoreQuest(): Quest { completed: () => !args.clan || getClanName().toLowerCase() === args.clan.toLowerCase(), do: () => cliExecute(`/whitelist ${args.clan}`), }, + { + name: "Set up Sweatsuit", + ready: () => have($item`tiny stillsuit`), + completed: () => familiarEquippedEquipment(bestStillsuitFamiliar) === $item`tiny stillsuit`, + do: (): void => { + equip(bestStillsuitFamiliar, $item`tiny stillsuit`); + }, + limit: { tries: 1 }, + }, { name: "Pre-Run Photobooth", ready: () => have($item`Clan VIP Lounge key`) && get("_photoBoothEquipment", 0) === 0, diff --git a/src/tasks/robotrunleg.ts b/src/tasks/robotrunleg.ts index c3ed8bb..89b0946 100644 --- a/src/tasks/robotrunleg.ts +++ b/src/tasks/robotrunleg.ts @@ -5,6 +5,8 @@ import { cliExecute, drink, Effect, + equip, + familiarEquippedEquipment, getClanName, getWorkshed, hippyStoneBroken, @@ -49,6 +51,7 @@ import { have, Macro, set, + StillSuit, uneffect, } from "libram"; @@ -81,6 +84,8 @@ function firstWorkshed() { const sasqBonus = (0.5 * 30 * 1000) / get("valueOfAdventure"); const ratskinBonus = (0.3 * 40 * 1000) / get("valueOfAdventure"); +const bestStillsuitFamiliar = StillSuit.bestFamiliar("Item Drop"); + export function RobotQuests(): Quest[] { return [ { @@ -95,6 +100,16 @@ export function RobotQuests(): Quest[] { 1507: 1, }, }, + { + name: "Set up Sweatsuit", + ready: () => have($item`tiny stillsuit`), + completed: () => + familiarEquippedEquipment(bestStillsuitFamiliar) === $item`tiny stillsuit`, + do: (): void => { + equip(bestStillsuitFamiliar, $item`tiny stillsuit`); + }, + limit: { tries: 1 }, + }, { name: "Get Floundry item", ready: () => have($item`Clan VIP Lounge key`) && !args.carpe, diff --git a/src/tasks/utils.ts b/src/tasks/utils.ts index d6b62b9..e8d6864 100644 --- a/src/tasks/utils.ts +++ b/src/tasks/utils.ts @@ -1,4 +1,5 @@ import { + canEquip, chew, cliExecute, Effect, @@ -6,6 +7,7 @@ import { fullnessLimit, gamedayToInt, getCampground, + getOutfits, holiday, inebrietyLimit, Item, @@ -17,10 +19,13 @@ import { myFullness, myInebriety, mySpleenUse, + outfitPieces, + outfitTreats, print, putCloset, retrieveItem, spleenLimit, + toItem, urlEncode, use, visitUrl, @@ -35,8 +40,10 @@ import { get, getBanishedMonsters, have, + maxBy, set, Snapper, + sum, } from "libram"; import { args } from "../args"; @@ -289,7 +296,10 @@ export function deleteJunkKmails() { export const realMonth = gameDay().getMonth(); export const realDay = gameDay().getDate(); export const halloween = - gamedayToInt() === 79 || (realMonth === 10 && realDay === 31) || holiday().includes("halloween"); + (gamedayToInt() === 79 || + (realMonth === 10 && realDay === 31) || + holiday().includes("halloween")) && + isHalloweenWorthDoing(); export function pvpCloset(num: number) { const threshold = 10000; @@ -309,3 +319,46 @@ export function pvpCloset(num: number) { }); set(`_safetyCloset${num}`, true); } + +function treatValue(outfit: string): number { + return sum( + Object.entries(outfitTreats(outfit)), + ([candyName, probability]) => probability * garboValue(toItem(candyName)), + ); +} + +export function getTreatOutfit(): string { + let outfit = get("freecandy_treatOutfit"); + const availableOutfits = getOutfits().filter((name) => + outfitPieces(name).every((piece) => canEquip(piece)), + ); + + if (!availableOutfits.length) { + return "seal-clubbing club"; + } + + outfit = maxBy(availableOutfits, treatValue); + + return outfit; +} + +let _baseAdventureValue: number; +function baseAdventureValue(): number { + if (!_baseAdventureValue) { + const outfitCandyValue = treatValue(getTreatOutfit()); + const totOutfitCandyMultiplier = have($familiar`Trick-or-Treating Tot`) ? 1.6 : 1; + const bowlValue = (1 / 5) * garboValue($item`huge bowl of candy`); + const prunetsValue = have($familiar`Trick-or-Treating Tot`) + ? 4 * 0.2 * garboValue($item`Prunets`) + : 0; + + const outfitCandyTotal = 3 * outfitCandyValue * totOutfitCandyMultiplier; + _baseAdventureValue = (1 / 5) * (outfitCandyTotal + bowlValue + prunetsValue); + } + return _baseAdventureValue; +} + +function isHalloweenWorthDoing(): boolean { + const freeFightValue = have($familiar`Red-Nosed Snapper`) ? 2000 : 1100; + return baseAdventureValue() + freeFightValue > get("valueOfAdventure"); +} diff --git a/yarn.lock b/yarn.lock index e97cebe..0157fdb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3162,10 +3162,10 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -libram@^0.9.2: - version "0.9.2" - resolved "https://registry.yarnpkg.com/libram/-/libram-0.9.2.tgz#2cd174109226c08bb4b9982272d44bfa6eb74b22" - integrity sha512-RZiGUnvcD4L7zsRvwjtmmmGaDQvxfldXgx4Z7jP3Ly9zDufHg+mIxQIhhwfekYWLuvGrNWxnNmrIxUyIyBp31A== +libram@^0.9.17: + version "0.9.17" + resolved "https://registry.yarnpkg.com/libram/-/libram-0.9.17.tgz#5e130d6afbf1b615d9629769fc6fb26c78457ea2" + integrity sha512-p2gArNxzxWAN1p1KyfsTseN2ksMg9hBUd5Gu+5nT623mcWpPGNBacNzC9GtV53hwlRiBfjJVwVspFcfvO3xJ9A== dependencies: html-entities "^2.5.2" From 0010a12d9abcc59f77f55aa1603afd08773a3808 Mon Sep 17 00:00:00 2001 From: Ignose Date: Tue, 19 Nov 2024 09:46:16 -0500 Subject: [PATCH 02/24] Fix a thing for drunk garbo --- src/tasks/aftercoreleg.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/aftercoreleg.ts b/src/tasks/aftercoreleg.ts index 4481109..ab9aa35 100644 --- a/src/tasks/aftercoreleg.ts +++ b/src/tasks/aftercoreleg.ts @@ -474,7 +474,7 @@ export function AftercoreQuest(): Quest { ready: () => have($item`Drunkula's wineglass`), prepare: () => uneffect($effect`Beaten Up`), completed: () => myAdventures() === 0, - do: () => cliExecute("garbo ascend"), + do: () => cliExecute(`${args.garboascend}`), post: () => $effects`Power Ballad of the Arrowsmith, Stevedave's Shanty of Superiority, The Moxious Madrigal, The Magical Mojomuscular Melody, Aloysius' Antiphon of Aptitude, Ur-Kel's Aria of Annoyance` .filter((ef) => have(ef)) From 77b0df23526d86052d1b63174805779ee3de3913 Mon Sep 17 00:00:00 2001 From: Ignose Date: Wed, 20 Nov 2024 09:07:53 -0500 Subject: [PATCH 03/24] Stillsuit didn't work out I guess --- src/tasks/aftercoreleg.ts | 14 -------------- src/tasks/robotrunleg.ts | 15 --------------- 2 files changed, 29 deletions(-) diff --git a/src/tasks/aftercoreleg.ts b/src/tasks/aftercoreleg.ts index ab9aa35..64abbf5 100644 --- a/src/tasks/aftercoreleg.ts +++ b/src/tasks/aftercoreleg.ts @@ -3,8 +3,6 @@ import { availableAmount, buy, cliExecute, - equip, - familiarEquippedEquipment, getCampground, getClanName, getWorkshed, @@ -54,7 +52,6 @@ import { have, Macro, set, - StillSuit, TrainSet, uneffect, } from "libram"; @@ -95,8 +92,6 @@ const stations = [ Station.CANDY_FACTORY, // candies ] as Cycle; -const bestStillsuitFamiliar = StillSuit.bestFamiliar("Item Drop"); - export function AftercoreQuest(): Quest { return { name: "Aftercore", @@ -113,15 +108,6 @@ export function AftercoreQuest(): Quest { completed: () => !args.clan || getClanName().toLowerCase() === args.clan.toLowerCase(), do: () => cliExecute(`/whitelist ${args.clan}`), }, - { - name: "Set up Sweatsuit", - ready: () => have($item`tiny stillsuit`), - completed: () => familiarEquippedEquipment(bestStillsuitFamiliar) === $item`tiny stillsuit`, - do: (): void => { - equip(bestStillsuitFamiliar, $item`tiny stillsuit`); - }, - limit: { tries: 1 }, - }, { name: "Pre-Run Photobooth", ready: () => have($item`Clan VIP Lounge key`) && get("_photoBoothEquipment", 0) === 0, diff --git a/src/tasks/robotrunleg.ts b/src/tasks/robotrunleg.ts index 89b0946..c3ed8bb 100644 --- a/src/tasks/robotrunleg.ts +++ b/src/tasks/robotrunleg.ts @@ -5,8 +5,6 @@ import { cliExecute, drink, Effect, - equip, - familiarEquippedEquipment, getClanName, getWorkshed, hippyStoneBroken, @@ -51,7 +49,6 @@ import { have, Macro, set, - StillSuit, uneffect, } from "libram"; @@ -84,8 +81,6 @@ function firstWorkshed() { const sasqBonus = (0.5 * 30 * 1000) / get("valueOfAdventure"); const ratskinBonus = (0.3 * 40 * 1000) / get("valueOfAdventure"); -const bestStillsuitFamiliar = StillSuit.bestFamiliar("Item Drop"); - export function RobotQuests(): Quest[] { return [ { @@ -100,16 +95,6 @@ export function RobotQuests(): Quest[] { 1507: 1, }, }, - { - name: "Set up Sweatsuit", - ready: () => have($item`tiny stillsuit`), - completed: () => - familiarEquippedEquipment(bestStillsuitFamiliar) === $item`tiny stillsuit`, - do: (): void => { - equip(bestStillsuitFamiliar, $item`tiny stillsuit`); - }, - limit: { tries: 1 }, - }, { name: "Get Floundry item", ready: () => have($item`Clan VIP Lounge key`) && !args.carpe, From 052a9fb9533469c5dd07ac6c5de4bb9f4afc736f Mon Sep 17 00:00:00 2001 From: Ignose Date: Fri, 27 Dec 2024 16:18:38 -0500 Subject: [PATCH 04/24] Don't LGR anymore if we're gonna pingu --- src/tasks/aftercoreleg.ts | 20 +------------------- src/tasks/robotrunleg.ts | 25 +++++++++++++++---------- 2 files changed, 16 insertions(+), 29 deletions(-) diff --git a/src/tasks/aftercoreleg.ts b/src/tasks/aftercoreleg.ts index 64abbf5..84eba6f 100644 --- a/src/tasks/aftercoreleg.ts +++ b/src/tasks/aftercoreleg.ts @@ -60,15 +60,7 @@ import { Cycle, setConfiguration, Station } from "libram/dist/resources/2022/Tra import { args } from "../args"; import { Quest } from "./structure"; -import { - bestFam, - getGarden, - isGoodGarboScript, - maxBase, - pvpCloset, - stooperDrunk, - totallyDrunk, -} from "./utils"; +import { bestFam, getGarden, maxBase, pvpCloset, stooperDrunk, totallyDrunk } from "./utils"; const doSmol = args.smol ? true : false; const doCS = args.cs ? true : false; @@ -235,16 +227,6 @@ export function AftercoreQuest(): Quest { DNALab.hybridize(); }, }, - { - name: "LGR Seed", - ready: () => - //best guess if we're going to Dinseylandfill later in the day - isGoodGarboScript(args.garboascend) || - args.pulls.includes($item`one-day ticket to Dinseylandfill`), - completed: () => - !have($item`lucky gold ring`) || get("_stenchAirportToday") || get("stenchAirportAlways"), - do: () => use($item`one-day ticket to Dinseylandfill`), - }, { name: "June Cleaver", completed: () => diff --git a/src/tasks/robotrunleg.ts b/src/tasks/robotrunleg.ts index c3ed8bb..38a7887 100644 --- a/src/tasks/robotrunleg.ts +++ b/src/tasks/robotrunleg.ts @@ -197,15 +197,6 @@ export function RobotQuests(): Quest[] { getWorkshed() !== $item`none`, do: () => use(firstWorkshed()), }, - { - name: "Unlock Garbage Mountain", - completed: () => get("_stenchAirportToday") || get("stenchAirportAlways"), - do: (): void => { - retrieveItem($item`one-day ticket to Dinseylandfill`); - use($item`one-day ticket to Dinseylandfill`); - }, - tracking: "Garbo", - }, { name: "Wardrobe-o-matic", ready: () => myLevel() >= 15 && have($item`wardrobe-o-matic`), @@ -437,9 +428,23 @@ export function RobotQuests(): Quest[] { completed: () => myAdventures() === 0 || stooperDrunk(), do: () => GarboWeenQuest(), }, + { + name: "Emergency Drink Part 3", + ready: () => myAdventures() < 40 && myInebriety() < 11, + completed: () => myAdventures() > 40 || myInebriety() >= 11, + prepare: () => { + if (have($item`astral six-pack`)) use($item`astral six-pack`); + }, + do: () => { + while (myAdventures() < 40) { + useSkill($skill`The Ode to Booze`); + drink(1, $item`astral pilsner`); + } + }, + limit: { tries: 6 }, + }, { name: "Garbo", - ready: () => get("_stenchAirportToday") || get("stenchAirportAlways"), completed: () => myAdventures() === 0 || stooperDrunk(), prepare: () => uneffect($effect`Beaten Up`), do: () => cliExecute(`${args.garbo}`), From 362c168148efad0157c82a2d65d9b41c3fa17ce5 Mon Sep 17 00:00:00 2001 From: Ignose Date: Thu, 9 Jan 2025 12:38:17 -0500 Subject: [PATCH 05/24] Digital Realm V0.1 --- package.json | 4 +- src/tasks/aftercoreleg.ts | 82 +++++++++++++++++++++++++++++++++- src/tasks/robotrunleg.ts | 92 ++++++++++++++++++++++++++++++++++++++- src/tasks/utils.ts | 13 ++++++ yarn.lock | 16 +++---- 5 files changed, 194 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 775c5e6..7a39556 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "build": "^0.1.4", "grimoire-kolmafia": "^0.3.25", "kolmafia": "^5.27815.0", - "libram": "^0.9.17", + "libram": "^0.9.29", "mafia-shared-relay": "0.0.7", "webpack": "^5.89.0" }, @@ -37,7 +37,7 @@ "esbuild-plugin-babel": "^0.2.3", "eslint": "^8.45.0", "eslint-config-prettier": "^8.8.0", - "eslint-plugin-libram": "^0.4.15", + "eslint-plugin-libram": "^0.4.21", "eslint-plugin-prettier": "^5.0.0", "prettier": "^3.0.0", "typescript": "^5.1.6", diff --git a/src/tasks/aftercoreleg.ts b/src/tasks/aftercoreleg.ts index 84eba6f..6991d69 100644 --- a/src/tasks/aftercoreleg.ts +++ b/src/tasks/aftercoreleg.ts @@ -3,6 +3,8 @@ import { availableAmount, buy, cliExecute, + equip, + Familiar, getCampground, getClanName, getWorkshed, @@ -17,6 +19,7 @@ import { myAdventures, myClass, myDaycount, + myFamiliar, myHp, myInebriety, myLevel, @@ -28,8 +31,11 @@ import { restoreMp, retrieveItem, toBoolean, + toInt, + toSkill, use, useFamiliar, + useSkill, visitUrl, } from "kolmafia"; import { @@ -60,7 +66,7 @@ import { Cycle, setConfiguration, Station } from "libram/dist/resources/2022/Tra import { args } from "../args"; import { Quest } from "./structure"; -import { bestFam, getGarden, maxBase, pvpCloset, stooperDrunk, totallyDrunk } from "./utils"; +import { getGarden, maxBase, nextCyberZone, pvpCloset, stooperDrunk, totallyDrunk } from "./utils"; const doSmol = args.smol ? true : false; const doCS = args.cs ? true : false; @@ -84,6 +90,19 @@ const stations = [ Station.CANDY_FACTORY, // candies ] as Cycle; +const bestFam = () => + famCheck($familiar`Pocket Professor`) + ? $familiar`Pocket Professor` + : famCheck($familiar`Chest Mimic`) + ? $familiar`Chest Mimic` + : famCheck($familiar`Grey Goose`) + ? $familiar`Grey Goose` + : $familiar`Grey Goose`; + +function famCheck(fam: Familiar): boolean { + return have(fam) && fam.experience < 400; +} + export function AftercoreQuest(): Quest { return { name: "Aftercore", @@ -110,6 +129,67 @@ export function AftercoreQuest(): Quest { cliExecute("photobooth item feather boa"); }, }, + { + name: "Candy Deviler", + // eslint-disable-next-line libram/verify-constants + ready: () => have($item`candy egg deviler`), + completed: () => toInt(get("_candyEggsDeviled")) >= 3, + do: () => { + visitUrl(`inventory.php?action=eggdevil&pwd`); + visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); + visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); + visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); + }, + }, + { + name: "CyberRealm: Prepare Familiar", + completed: () => myFamiliar() === $familiar`Shorter-Order Cook`, + do: () => { + if (have($familiar`Shorter-Order Cook`)) { + cliExecute("familiar Shorter-Order Cook"); + equip($familiar`Shorter-Order Cook`, $item`blue plate`); + } + cliExecute(`familiar ${bestFam().name}`); + cliExecute("familiar Shorter-Order Cook"); + }, + }, + { + name: "Run CyberRealm", + ready: () => mallPrice($item`1`) > 1_000, + prepare: () => { + $effects`Astral Shell, Elemental Saucesphere, Scarysauce`.forEach((ef) => { + if (!have(ef)) useSkill(toSkill(ef)); + }); + }, + completed: () => nextCyberZone() === $location`none`, // $location`Cyberzone 1`.turnsSpent >= 19 * myDaycount(), + choices: { 1545: 1, 1546: 1, 1547: 1, 1548: 1, 1549: 1, 1550: 1 }, + do: nextCyberZone(), + outfit: { + hat: $item`Crown of Thrones`, + back: $item`unwrapped knock-off retro superhero cape`, + shirt: $item`zero-trust tanktop`, + weapon: $item`encrypted shuriken`, + offhand: $item`visual packet sniffer`, + pants: $item`digibritches`, + acc1: $item`retro floppy disk`, + acc2: $item`retro floppy disk`, + acc3: $item`retro floppy disk`, + famequip: $item`familiar-in-the-middle wrapper`, + modes: { retrocape: ["vampire", "hold"] }, + riders: { "crown-of-thrones": $familiar`Mini Kiwi` }, + }, + combat: new CombatStrategy().macro(() => + Macro.if_( + "!monsterphylum construct", + Macro.trySkill($skill`Sing Along`) + .trySkill($skill`Saucestorm`) + .repeat(), + ) + .skill($skill`Throw Cyber Rock`) + .repeat(), + ), + limit: { skip: 60 }, + }, { name: "PvP Closet Safety 1", ready: () => args.pvp && get("autoSatisfyWithCloset") && !args.safepvp, diff --git a/src/tasks/robotrunleg.ts b/src/tasks/robotrunleg.ts index 38a7887..ccf8bf9 100644 --- a/src/tasks/robotrunleg.ts +++ b/src/tasks/robotrunleg.ts @@ -5,14 +5,18 @@ import { cliExecute, drink, Effect, + equip, + Familiar, getClanName, getWorkshed, hippyStoneBroken, inebrietyLimit, itemAmount, + mallPrice, myAdventures, myAscensions, myDaycount, + myFamiliar, myInebriety, myLevel, myMaxhp, @@ -28,6 +32,7 @@ import { storageAmount, toBoolean, toInt, + toSkill, use, useFamiliar, useSkill, @@ -58,11 +63,11 @@ import { GarboWeenQuest } from "./Garboween"; import { getCurrentLeg, Leg, Quest } from "./structure"; import { backstageItemsDone, - bestFam, doneAdventuring, halloween, haveAll, maxBase, + nextCyberZone, pvpCloset, stooperDrunk, totallyDrunk, @@ -70,6 +75,7 @@ import { let pajamas = false; let smoke = 1; +let duffo = false; function firstWorkshed() { return ( @@ -81,6 +87,19 @@ function firstWorkshed() { const sasqBonus = (0.5 * 30 * 1000) / get("valueOfAdventure"); const ratskinBonus = (0.3 * 40 * 1000) / get("valueOfAdventure"); +const bestFam = () => + famCheck($familiar`Pocket Professor`) + ? $familiar`Pocket Professor` + : famCheck($familiar`Chest Mimic`) + ? $familiar`Chest Mimic` + : famCheck($familiar`Grey Goose`) + ? $familiar`Grey Goose` + : $familiar`Grey Goose`; + +function famCheck(fam: Familiar): boolean { + return have(fam) && fam.experience < 400; +} + export function RobotQuests(): Quest[] { return [ { @@ -95,6 +114,14 @@ export function RobotQuests(): Quest[] { 1507: 1, }, }, + { + name: "Unpack Duffel Bag", + completed: () => duffo, + do: () => { + visitUrl("inventory.php?action=skiduffel&pwd"); + duffo = true; + }, + }, { name: "Get Floundry item", ready: () => have($item`Clan VIP Lounge key`) && !args.carpe, @@ -436,13 +463,74 @@ export function RobotQuests(): Quest[] { if (have($item`astral six-pack`)) use($item`astral six-pack`); }, do: () => { - while (myAdventures() < 40) { + while (myAdventures() < 80 && have($item`astral pilsner`)) { useSkill($skill`The Ode to Booze`); drink(1, $item`astral pilsner`); } }, limit: { tries: 6 }, }, + { + name: "Candy Deviler", + // eslint-disable-next-line libram/verify-constants + ready: () => have($item`candy egg deviler`), + completed: () => toInt(get("_candyEggsDeviled")) >= 3, + do: () => { + visitUrl(`inventory.php?action=eggdevil&pwd`); + visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); + visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); + visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); + }, + }, + { + name: "CyberRealm: Prepare Familiar", + completed: () => myFamiliar() === $familiar`Shorter-Order Cook`, + do: () => { + if (have($familiar`Shorter-Order Cook`)) { + cliExecute("familiar Shorter-Order Cook"); + equip($familiar`Shorter-Order Cook`, $item`blue plate`); + } + cliExecute(`familiar ${bestFam().name}`); + cliExecute("familiar Shorter-Order Cook"); + }, + }, + { + name: "Run CyberRealm", + ready: () => mallPrice($item`1`) > 1_000, + prepare: () => { + $effects`Astral Shell, Elemental Saucesphere, Scarysauce`.forEach((ef) => { + if (!have(ef)) useSkill(toSkill(ef)); + }); + }, + completed: () => nextCyberZone() === $location`none`, // $location`Cyberzone 1`.turnsSpent >= 19 * myDaycount(), + choices: { 1545: 1, 1546: 1, 1547: 1, 1548: 1, 1549: 1, 1550: 1 }, + do: nextCyberZone(), + outfit: { + hat: $item`Crown of Thrones`, + back: $item`unwrapped knock-off retro superhero cape`, + shirt: $item`zero-trust tanktop`, + weapon: $item`encrypted shuriken`, + offhand: $item`visual packet sniffer`, + pants: $item`digibritches`, + acc1: $item`retro floppy disk`, + acc2: $item`retro floppy disk`, + acc3: $item`retro floppy disk`, + famequip: $item`familiar-in-the-middle wrapper`, + modes: { retrocape: ["vampire", "hold"] }, + riders: { "crown-of-thrones": $familiar`Mini Kiwi` }, + }, + combat: new CombatStrategy().macro(() => + Macro.if_( + "!monsterphylum construct", + Macro.trySkill($skill`Sing Along`) + .trySkill($skill`Saucestorm`) + .repeat(), + ) + .skill($skill`Throw Cyber Rock`) + .repeat(), + ), + limit: { skip: 60 }, + }, { name: "Garbo", completed: () => myAdventures() === 0 || stooperDrunk(), diff --git a/src/tasks/utils.ts b/src/tasks/utils.ts index e8d6864..970e784 100644 --- a/src/tasks/utils.ts +++ b/src/tasks/utils.ts @@ -12,9 +12,11 @@ import { inebrietyLimit, Item, itemAmount, + Location, mallPrice, Monster, myAdventures, + myDaycount, myFamiliar, myFullness, myInebriety, @@ -35,6 +37,7 @@ import { $familiars, $item, $items, + $location, $phylum, gameDay, get, @@ -151,6 +154,16 @@ export function bestFam(mob?: Monster) { return fams.find((fam) => have(fam)); } +export function nextCyberZone(): Location { + return $location`Cyberzone 1`.turnsSpent >= 19 * myDaycount() + ? $location`Cyberzone 1` + : $location`Cyberzone 2`.turnsSpent >= 19 * myDaycount() + ? $location`Cyberzone 2` + : $location`Cyberzone 3`.turnsSpent >= 19 * myDaycount() + ? $location`Cyberzone 3` + : $location`none`; +} + export function canDiet(): boolean { return ( myFullness() < fullnessLimit() || diff --git a/yarn.lock b/yarn.lock index 0157fdb..c9394db 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2357,10 +2357,10 @@ eslint-config-prettier@^8.8.0: resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz#bfda738d412adc917fd7b038857110efe98c9348" integrity sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA== -eslint-plugin-libram@^0.4.15: - version "0.4.15" - resolved "https://registry.yarnpkg.com/eslint-plugin-libram/-/eslint-plugin-libram-0.4.15.tgz#ea6cf0a1f9941cf0e16927a8d688d8daa6567433" - integrity sha512-QW+CwY9ocoSBr5xHM7KnlC/YcjmbYMUFSM33SmqoBrliheo9oJErdmO5PVYBrjZ4bNjqh2Ea4xW7OEzbNXjT7g== +eslint-plugin-libram@^0.4.21: + version "0.4.21" + resolved "https://registry.yarnpkg.com/eslint-plugin-libram/-/eslint-plugin-libram-0.4.21.tgz#0db49bedd807f256bc01496622065d735c5ffc2e" + integrity sha512-wSS2Uqty4lE+08NT4614I8j19gHN7rEs4MOn6hXRTgfB7ES0mIsf2+bLuVNNpknkryF49yvT/znzmAN2JhoAnw== dependencies: html-entities "^2.5.2" requireindex "~1.2.0" @@ -3162,10 +3162,10 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -libram@^0.9.17: - version "0.9.17" - resolved "https://registry.yarnpkg.com/libram/-/libram-0.9.17.tgz#5e130d6afbf1b615d9629769fc6fb26c78457ea2" - integrity sha512-p2gArNxzxWAN1p1KyfsTseN2ksMg9hBUd5Gu+5nT623mcWpPGNBacNzC9GtV53hwlRiBfjJVwVspFcfvO3xJ9A== +libram@^0.9.29: + version "0.9.29" + resolved "https://registry.yarnpkg.com/libram/-/libram-0.9.29.tgz#d81695cb7e9d9c463a0b5cfa7072ec2ff8375cb3" + integrity sha512-LVehF8VtyRhnnti2WW38FSbC6t26q4MXIw/tuth8tdcNXaRzC07Gif20cZMx+y0X76G4x2/LnIBhU0eKivnPSw== dependencies: html-entities "^2.5.2" From 205713be71eb1c81de1c379c27241b0b79fcefac Mon Sep 17 00:00:00 2001 From: Ignose Date: Tue, 14 Jan 2025 09:50:41 -0500 Subject: [PATCH 06/24] Massive overhaul --- package.json | 5 +- src/args.ts | 27 + src/main.ts | 32 +- src/tasks/Garboween.ts | 284 -------- src/tasks/aftercoreleg.ts | 354 +--------- src/tasks/casualrunleg.ts | 598 ----------------- src/tasks/csrunleg.ts | 431 ------------ src/tasks/{robotrunleg.ts => postrunleg.ts} | 349 ++-------- src/tasks/repeatableTasks.ts | 471 +++++++++++++ src/tasks/runleg.ts | 80 +++ src/tasks/smolrunleg.ts | 693 -------------------- src/tasks/utils.ts | 41 +- yarn.lock | 8 +- 13 files changed, 695 insertions(+), 2678 deletions(-) delete mode 100644 src/tasks/Garboween.ts delete mode 100644 src/tasks/casualrunleg.ts delete mode 100644 src/tasks/csrunleg.ts rename src/tasks/{robotrunleg.ts => postrunleg.ts} (61%) create mode 100644 src/tasks/repeatableTasks.ts create mode 100644 src/tasks/runleg.ts delete mode 100644 src/tasks/smolrunleg.ts diff --git a/package.json b/package.json index 7a39556..72f5f15 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "build": "^0.1.4", "grimoire-kolmafia": "^0.3.25", "kolmafia": "^5.27815.0", - "libram": "^0.9.29", + "libram": "^0.9.31", "mafia-shared-relay": "0.0.7", "webpack": "^5.89.0" }, @@ -51,5 +51,6 @@ "TS" ], "repository": "https://github.com/Ignose/candywrapper.git", - "private": false + "private": false, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/src/args.ts b/src/args.ts index 2d123ee..eb5ba0f 100644 --- a/src/args.ts +++ b/src/args.ts @@ -23,18 +23,37 @@ export const args = Args.create( }), cs: Args.flag({ help: "Ascend into and run CS.", + default: false, setting: "", }), smol: Args.flag({ help: "Ascend into and run smol.", + default: false, setting: "", }), casual: Args.flag({ help: "Ascend into and run casual.", + default: false, setting: "", }), robot: Args.flag({ help: "Ascend into and run You, Robot.", + default: false, + setting: "", + }), + crimbo: Args.flag({ + help: "Farm Crimbo instead of Garbo", + default: false, + setting: "", + }), + chrono: Args.flag({ + help: "Farm TTT instead of Garbo", + default: false, + setting: "", + }), + ween: Args.flag({ + help: "Run Halloween instead of Garbo", + default: false, setting: "", }), @@ -139,6 +158,14 @@ export const args = Args.create( help: `The command that will be used to diet and use all your adventures in Day 2 aftercore.`, default: "garbo ascend", }), + crimboscript: Args.string({ + help: `The command that will be used to run a crimbo farming script - note that if no script is provided, we will default to garbo`, + default: "", + }), + chronoscript: Args.string({ + help: `The command that will be used to run a TTT farming script - note that if no script is provided, we will default to garbo`, + default: "", + }), itemcleanup: Args.string({ help: `The script that will be used to mallsale items after a run`, default: "", diff --git a/src/main.ts b/src/main.ts index ebfc459..7d3e2ce 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,14 +3,11 @@ import { gamedayToInt, print } from "kolmafia"; import { args } from "./args"; import { ProfitTrackingEngine } from "./engine/engine"; -import { GarboWeenQuest } from "./tasks/Garboween"; +import { deleteJunkKmails, halloween, notifyVoters, realDay, realMonth } from "./tasks/utils"; import { AftercoreQuest } from "./tasks/aftercoreleg"; import { AscendQuest } from "./tasks/ascend"; -import { CasualQuests } from "./tasks/casualrunleg"; -import { CSQuests } from "./tasks/csrunleg"; -import { RobotQuests } from "./tasks/robotrunleg"; -import { SmolQuests } from "./tasks/smolrunleg"; -import { deleteJunkKmails, halloween, realDay, realMonth } from "./tasks/utils"; +import { PostRunQuests } from "./tasks/postrunleg"; +import { RunQuests } from "./tasks/runleg"; const version = "0.0.3"; @@ -31,29 +28,13 @@ export function main(command?: string): void { throw `Today is halloween, run CS for more organ space!`; } - /*if (args.profits) { - printProfits(this.profits.all()); - return; - };*/ - print(`Running: candyWrapper v${version}`); - const runQuest = args.cs - ? CSQuests() - : args.smol - ? SmolQuests() - : args.casual - ? CasualQuests() - : args.robot - ? RobotQuests() - : undefined; - if (runQuest === undefined) throw "Undefined runtype; please choose either cs or smol"; + if (!args.cs && !args.smol && !args.casual && !args.robot) throw "Undefined runtype; please choose an acceptable path"; - const tasks = halloween - ? getTasks([GarboWeenQuest(), AscendQuest(), ...runQuest]) - : getTasks([AftercoreQuest(), AscendQuest(), ...runQuest]); + const tasks = getTasks([AftercoreQuest(), AscendQuest(), RunQuests(), PostRunQuests()]); - if (tasks === undefined) throw "Undefined runtype; please choose either cs or smol"; + if (tasks === undefined) throw "Undefined runtype; please choose a valid run type"; if (args.abort) { const to_abort = tasks.find((task) => task.name === args.abort); @@ -75,4 +56,5 @@ export function main(command?: string): void { engine.destruct(); } deleteJunkKmails(); + notifyVoters(); } diff --git a/src/tasks/Garboween.ts b/src/tasks/Garboween.ts deleted file mode 100644 index 987496d..0000000 --- a/src/tasks/Garboween.ts +++ /dev/null @@ -1,284 +0,0 @@ -import { CombatStrategy } from "grimoire-kolmafia"; -import { - availableAmount, - buy, - cliExecute, - fullnessLimit, - getCampground, - getClanName, - getWorkshed, - guildStoreAvailable, - haveEffect, - hippyStoneBroken, - holiday, - inebrietyLimit, - itemAmount, - myAdventures, - myClass, - myFullness, - myHp, - myInebriety, - myMaxhp, - mySpleenUse, - pvpAttacksLeft, - restoreHp, - retrieveItem, - spleenLimit, - use, - useFamiliar, -} from "kolmafia"; -import { - $class, - $coinmaster, - $effect, - $familiar, - $item, - $items, - $location, - $phylum, - $skill, - AsdonMartin, - DNALab, - get, - getTodaysHolidayWanderers, - have, - Macro, - uneffect, -} from "libram"; - -import { args } from "../args"; - -import { Quest } from "./structure"; -import { bestFam, getGarden, maxBase, stooperDrunk, totallyDrunk } from "./utils"; - -let garboDone = false; - -export function GarboWeenQuest(): Quest { - return { - name: "Garboween", - completed: () => myAdventures() === 0 && totallyDrunk(), - tasks: [ - { - name: "Whitelist VIP Clan", - completed: () => !args.clan || getClanName().toLowerCase() === args.clan.toLowerCase(), - do: () => cliExecute(`/whitelist ${args.clan}`), - }, - { - name: "LGR Seed", - ready: () => have($item`lucky gold ring`) && have($item`one-day ticket to Dinseylandfill`), - completed: () => get("_stenchAirportToday") || get("stenchAirportAlways"), - do: () => use($item`one-day ticket to Dinseylandfill`), - tracking: "Garbo", - }, - { - name: "Breakfast", - completed: () => get("breakfastCompleted"), - do: () => cliExecute("breakfast"), - }, - { - name: "Harvest Garden", - completed: () => - getGarden() === $item`none` || - getGarden() === $item`packet of mushroom spores` || - getCampground()[getGarden().name] === 0, - do: () => cliExecute("garden pick"), - tracking: "Dailies", - limit: { tries: 3 }, - }, - { - name: "Plant Grass", - completed: () => - !have($item`packet of tall grass seeds`) || - getGarden() === $item`packet of tall grass seeds`, - do: () => use($item`packet of tall grass seeds`), - }, - { - name: "SIT Course", - ready: () => have($item`S.I.T. Course Completion Certificate`), - completed: () => get("_sitCourseCompleted", false), - choices: { - 1494: 2, - }, - do: () => use($item`S.I.T. Course Completion Certificate`), - }, - { - name: "Drive Observantly", - completed: () => - getWorkshed() !== $item`Asdon Martin keyfob (on ring)` || - haveEffect($effect`Driving Observantly`) >= - (totallyDrunk() || !have($item`Drunkula's wineglass`) - ? myAdventures() - : myAdventures() + 60), - do: () => - AsdonMartin.drive( - $effect`Driving Observantly`, - totallyDrunk() || !have($item`Drunkula's wineglass`) - ? myAdventures() - : myAdventures() + 60, - false, - ), - limit: { tries: 5 }, - }, - { - name: "Sample Constellation DNA", - ready: () => have($item`DNA extraction syringe`), - completed: () => - !DNALab.installed() || - DNALab.isHybridized($phylum`Constellation`) || - get("dnaSyringe") === $phylum`Constellation`, - outfit: { - familiar: bestFam(), - modifier: `${maxBase()}`, - }, - do: $location`The Hole in the Sky`, - combat: new CombatStrategy() - .macro(Macro.skill($skill`Curse of Weaksauce`), getTodaysHolidayWanderers()) - .macro(Macro.tryItem($item`DNA extraction syringe`)) - .macro( - Macro.tryItem($item`train whistle`) - .tryItem($item`porquoise-handled sixgun`) - .trySkill($skill`Sing Along`) - .attack() - .repeat(), - ), - }, - { - name: "Hybridize Constellation", - ready: () => get("dnaSyringe") === $phylum`Constellation`, - completed: () => !DNALab.installed() || DNALab.isHybridized($phylum`Constellation`), - do: () => { - DNALab.makeTonic(3); - DNALab.hybridize(); - }, - }, - { - name: "Restore HP", - completed: () => myHp() > 0.5 * myMaxhp(), - do: () => restoreHp(0.95 * myMaxhp()), - }, - { - name: "Implement Glitch", - ready: () => have($item`[glitch season reward name]`), - completed: () => get("_glitchItemImplemented"), - do: () => use($item`[glitch season reward name]`), - }, - { - name: "Buy Seal Summoning Supplies", - ready: () => myClass() === $class`Seal Clubber` && guildStoreAvailable(), - completed: () => - Math.min( - ...$items`figurine of a wretched-looking seal, seal-blubber candle`.map((it) => - availableAmount(it), - ), - ) >= 40, - acquire: $items`figurine of a wretched-looking seal, seal-blubber candle`.map((it) => ({ - item: it, - num: 500, - })), - do: () => false, - }, - { - name: "CONSUME ALL", - completed: () => - myFullness() >= fullnessLimit() && - mySpleenUse() >= spleenLimit() && - myInebriety() >= inebrietyLimit(), - do: () => cliExecute("consume ALL"), - }, - { - name: "Garbo Nobarf", - completed: () => garboDone, - do: (): void => { - cliExecute(`${args.garboascend} nodiet nobarf target="witchess knight"`); - garboDone = true; - }, - }, - { - name: "Freecandy time", - ready: () => holiday().includes("Halloween"), - completed: () => myAdventures() / 5 < 1, - prepare: () => uneffect($effect`Beaten Up`), - do: (): void => { - if (have($familiar`Trick-or-Treating Tot`)) cliExecute("familiar Trick-or-Treating Tot"); - else if (have($familiar`Red-Nosed Snapper`)) cliExecute("familiar snapper"); - cliExecute(`freecandy ${myAdventures()}`); - }, - clear: "all", - tracking: "Freecandy", - limit: { tries: 1 }, //this will run again after installing CMC, by magic - }, - { - name: "Do Pizza", - completed: () => - have($item`Pizza of Legend`) && - have($item`Deep Dish of Legend`) && - have($item`Calzone of Legend`), - do: (): void => { - !have($item`Pizza of Legend`) ? retrieveItem($item`Pizza of Legend`) : undefined; - !have($item`Deep Dish of Legend`) ? retrieveItem($item`Deep Dish of Legend`) : undefined; - !have($item`Calzone of Legend`) ? retrieveItem($item`Calzone of Legend`) : undefined; - }, - }, - { - name: "Stooper", - ready: () => - myInebriety() === inebrietyLimit() && - have($item`tiny stillsuit`) && - get("familiarSweat") >= 300, - completed: () => !have($familiar`Stooper`) || stooperDrunk(), - do: () => { - useFamiliar($familiar`Stooper`); - cliExecute("drink stillsuit distillate"); - }, - }, - { - name: "Super Nightcap", - ready: () => have($item`Drunkula's wineglass`), - completed: () => totallyDrunk(), - do: () => cliExecute(`CONSUME NIGHTCAP`), - }, - { - name: "Freecandy Drunk", - ready: () => holiday().includes("Halloween"), - completed: () => Math.floor(myAdventures() / 5) === 0, - prepare: () => uneffect($effect`Beaten Up`), - do: (): void => { - useFamiliar($familiar`Red-Nosed Snapper`); - cliExecute(`freeCandy ${myAdventures()}`); - }, - clear: "all", - tracking: "Freecandy", - limit: { tries: 1 }, //this will run again after installing CMC, by magic - }, - { - name: "Grimace Maps", - ready: () => have($item`Map to Safety Shelter Grimace Prime`) && totallyDrunk(), - completed: () => !have($item`Map to Safety Shelter Grimace Prime`) || myAdventures() === 0, - do: () => cliExecute("grimace maps"), - }, - { - name: "Comb Beach", - ready: () => have($item`Beach Comb`) && totallyDrunk(), - completed: () => myAdventures() === 0, - do: () => cliExecute(`combo ${11 - get("_freeBeachWalksUsed") + myAdventures()}`), - }, - { - name: "Turn in FunFunds", - ready: () => get("_stenchAirportToday") && itemAmount($item`FunFunds™`) >= 20, - completed: () => have($item`one-day ticket to Dinseylandfill`), - do: () => - buy($coinmaster`The Dinsey Company Store`, 1, $item`one-day ticket to Dinseylandfill`), - tracking: "Garbo", - }, - { - name: "PvP", - completed: () => pvpAttacksLeft() === 0 || !hippyStoneBroken(), - do: (): void => { - cliExecute("unequip"); - cliExecute("UberPvPOptimizer"); - cliExecute(`PVP_MAB target=${args.pvpTarget}`); - }, - }, - ], - }; -} diff --git a/src/tasks/aftercoreleg.ts b/src/tasks/aftercoreleg.ts index 6991d69..2f857b7 100644 --- a/src/tasks/aftercoreleg.ts +++ b/src/tasks/aftercoreleg.ts @@ -1,28 +1,15 @@ import { CombatStrategy } from "grimoire-kolmafia"; import { - availableAmount, buy, cliExecute, - equip, - Familiar, - getCampground, - getClanName, - getWorkshed, - guildStoreAvailable, - handlingChoice, - haveEffect, haveEquipped, hippyStoneBroken, inebrietyLimit, itemAmount, mallPrice, myAdventures, - myClass, myDaycount, - myFamiliar, - myHp, myInebriety, - myLevel, myMaxhp, myPrimestat, print, @@ -31,33 +18,21 @@ import { restoreMp, retrieveItem, toBoolean, - toInt, - toSkill, - use, useFamiliar, - useSkill, visitUrl, } from "kolmafia"; import { - $class, $coinmaster, $effect, $effects, $familiar, $item, - $items, $location, - $phylum, $skill, - $stat, - AprilingBandHelmet, - AsdonMartin, - DNALab, get, getTodaysHolidayWanderers, have, Macro, - set, TrainSet, uneffect, } from "libram"; @@ -66,7 +41,8 @@ import { Cycle, setConfiguration, Station } from "libram/dist/resources/2022/Tra import { args } from "../args"; import { Quest } from "./structure"; -import { getGarden, maxBase, nextCyberZone, pvpCloset, stooperDrunk, totallyDrunk } from "./utils"; +import { maxBase, pvpCloset, stooperDrunk, totallyDrunk } from "./utils"; +import { chrono, crimbo, garboWeen, noBarf, postRunQuests, preRunQuests } from "./repeatableTasks"; const doSmol = args.smol ? true : false; const doCS = args.cs ? true : false; @@ -90,19 +66,6 @@ const stations = [ Station.CANDY_FACTORY, // candies ] as Cycle; -const bestFam = () => - famCheck($familiar`Pocket Professor`) - ? $familiar`Pocket Professor` - : famCheck($familiar`Chest Mimic`) - ? $familiar`Chest Mimic` - : famCheck($familiar`Grey Goose`) - ? $familiar`Grey Goose` - : $familiar`Grey Goose`; - -function famCheck(fam: Familiar): boolean { - return have(fam) && fam.experience < 400; -} - export function AftercoreQuest(): Quest { return { name: "Aftercore", @@ -114,11 +77,6 @@ export function AftercoreQuest(): Quest { have($item`Calzone of Legend`)) || myDaycount() === 1, tasks: [ - { - name: "Whitelist VIP Clan", - completed: () => !args.clan || getClanName().toLowerCase() === args.clan.toLowerCase(), - do: () => cliExecute(`/whitelist ${args.clan}`), - }, { name: "Pre-Run Photobooth", ready: () => have($item`Clan VIP Lounge key`) && get("_photoBoothEquipment", 0) === 0, @@ -129,312 +87,18 @@ export function AftercoreQuest(): Quest { cliExecute("photobooth item feather boa"); }, }, - { - name: "Candy Deviler", - // eslint-disable-next-line libram/verify-constants - ready: () => have($item`candy egg deviler`), - completed: () => toInt(get("_candyEggsDeviled")) >= 3, - do: () => { - visitUrl(`inventory.php?action=eggdevil&pwd`); - visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); - visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); - visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); - }, - }, - { - name: "CyberRealm: Prepare Familiar", - completed: () => myFamiliar() === $familiar`Shorter-Order Cook`, - do: () => { - if (have($familiar`Shorter-Order Cook`)) { - cliExecute("familiar Shorter-Order Cook"); - equip($familiar`Shorter-Order Cook`, $item`blue plate`); - } - cliExecute(`familiar ${bestFam().name}`); - cliExecute("familiar Shorter-Order Cook"); - }, - }, - { - name: "Run CyberRealm", - ready: () => mallPrice($item`1`) > 1_000, - prepare: () => { - $effects`Astral Shell, Elemental Saucesphere, Scarysauce`.forEach((ef) => { - if (!have(ef)) useSkill(toSkill(ef)); - }); - }, - completed: () => nextCyberZone() === $location`none`, // $location`Cyberzone 1`.turnsSpent >= 19 * myDaycount(), - choices: { 1545: 1, 1546: 1, 1547: 1, 1548: 1, 1549: 1, 1550: 1 }, - do: nextCyberZone(), - outfit: { - hat: $item`Crown of Thrones`, - back: $item`unwrapped knock-off retro superhero cape`, - shirt: $item`zero-trust tanktop`, - weapon: $item`encrypted shuriken`, - offhand: $item`visual packet sniffer`, - pants: $item`digibritches`, - acc1: $item`retro floppy disk`, - acc2: $item`retro floppy disk`, - acc3: $item`retro floppy disk`, - famequip: $item`familiar-in-the-middle wrapper`, - modes: { retrocape: ["vampire", "hold"] }, - riders: { "crown-of-thrones": $familiar`Mini Kiwi` }, - }, - combat: new CombatStrategy().macro(() => - Macro.if_( - "!monsterphylum construct", - Macro.trySkill($skill`Sing Along`) - .trySkill($skill`Saucestorm`) - .repeat(), - ) - .skill($skill`Throw Cyber Rock`) - .repeat(), - ), - limit: { skip: 60 }, - }, { name: "PvP Closet Safety 1", ready: () => args.pvp && get("autoSatisfyWithCloset") && !args.safepvp, completed: () => toBoolean(get("_safetyCloset1")), do: () => pvpCloset(1), }, - { - name: "Get Floundry item", - ready: () => have($item`Clan VIP Lounge key`) && !args.carpe, - completed: () => get("_floundryItemCreated"), - do: (): void => { - retrieveItem($item`carpe`); - }, - limit: { tries: 1 }, - }, - { - name: "Prep Fireworks Shop", - completed: () => !have($item`Clan VIP Lounge key`) || get("_goorboFireworksPrepped", false), - do: () => { - visitUrl("clan_viplounge.php?action=fwshop&whichfloor=2"); - set("_goorboFireworksPrepped", true); - }, - }, - { - name: "Breakfast", - completed: () => get("breakfastCompleted"), - do: () => cliExecute("breakfast"), - }, - { - name: "Apriling", - ready: () => AprilingBandHelmet.canChangeSong(), - completed: () => have($effect`Apriling Band Celebration Bop`), - do: (): void => { - AprilingBandHelmet.conduct($effect`Apriling Band Celebration Bop`); - }, - limit: { tries: 1 }, - }, - { - name: "Harvest Garden", - completed: () => - getGarden() === $item`none` || - getGarden() === $item`packet of mushroom spores` || - getCampground()[getGarden().name] === 0, - do: () => cliExecute("garden pick"), - tracking: "Dailies", - limit: { tries: 3 }, - }, - { - name: "Plant Grass", - completed: () => - !have($item`packet of tall grass seeds`) || - getGarden() === $item`packet of tall grass seeds`, - do: () => use($item`packet of tall grass seeds`), - }, - { - name: "SIT Course", - // eslint-disable-next-line libram/verify-constants - ready: () => have($item`S.I.T. Course Completion Certificate`), - completed: () => get("_sitCourseCompleted", false), - choices: { - 1494: 2, - }, - do: () => - // eslint-disable-next-line libram/verify-constants - use($item`S.I.T. Course Completion Certificate`), - }, - { - name: "Drive Observantly", - completed: () => - get("dailyDungeonDone") || - getWorkshed() !== $item`Asdon Martin keyfob (on ring)` || - haveEffect($effect`Driving Observantly`) >= - (totallyDrunk() || !have($item`Drunkula's wineglass`) - ? myAdventures() - : myAdventures() + 60), - do: () => - AsdonMartin.drive( - $effect`Driving Observantly`, - totallyDrunk() || !have($item`Drunkula's wineglass`) - ? myAdventures() - : myAdventures() + 60, - false, - ), - limit: { tries: 5 }, - }, - { - name: "Sample Constellation DNA", - ready: () => have($item`DNA extraction syringe`), - completed: () => - !DNALab.installed() || - DNALab.isHybridized($phylum`Constellation`) || - get("dnaSyringe") === $phylum`Constellation`, - outfit: { - familiar: bestFam(), - modifier: `${maxBase()}`, - }, - do: $location`The Hole in the Sky`, - combat: new CombatStrategy() - .macro(Macro.skill($skill`Curse of Weaksauce`), getTodaysHolidayWanderers()) - .macro(Macro.tryItem($item`DNA extraction syringe`)) - .macro( - Macro.tryItem($item`train whistle`) - .tryItem($item`porquoise-handled sixgun`) - .trySkill($skill`Sing Along`) - .attack() - .repeat(), - ), - }, - { - name: "Hybridize Constellation", - ready: () => get("dnaSyringe") === $phylum`Constellation`, - completed: () => !DNALab.installed() || DNALab.isHybridized($phylum`Constellation`), - do: () => { - DNALab.makeTonic(3); - DNALab.hybridize(); - }, - }, - { - name: "June Cleaver", - completed: () => - !have($item`June cleaver`) || get("_juneCleaverFightsLeft") > 0 || myAdventures() === 0, - choices: { - 1467: 3, //Poetic Justice - 1468: get("_juneCleaverSkips") < 5 ? 4 : 2, //Aunts not Ants - 1469: 3, //Beware of Aligator - 1470: get("_juneCleaverSkips") < 5 ? 4 : 2, //Teacher's Pet - 1471: 1, //Lost and Found - 1472: get("_juneCleaverSkips") < 5 ? 4 : 1, //Summer Days - 1473: get("_juneCleaverSkips") < 5 ? 4 : 1, //Bath Time - 1474: get("_juneCleaverSkips") < 5 ? 4 : 2, //Delicious Sprouts - 1475: 1, //Hypnotic Master - }, - do: $location`Noob Cave`, - post: () => { - if (handlingChoice()) visitUrl("main.php"); - if (have($effect`Beaten Up`)) uneffect($effect`Beaten Up`); - }, - outfit: () => ({ equip: $items`June cleaver` }), - limit: undefined, - }, - { - name: "Restore HP", - completed: () => myHp() > 0.5 * myMaxhp(), - do: () => restoreHp(0.95 * myMaxhp()), - }, - { - name: "Implement Glitch", - ready: () => have($item`[glitch season reward name]`), - completed: () => get("_glitchItemImplemented"), - do: () => use($item`[glitch season reward name]`), - }, - { - name: "Unlock Guild", - ready: () => - myClass() === $class`Seal Clubber` && - Math.min( - ...$items`figurine of a wretched-looking seal, seal-blubber candle`.map((it) => - availableAmount(it), - ), - ) < 20 && - doSmol, - completed: () => guildStoreAvailable() || myAdventures() === 0 || stooperDrunk(), - do: () => cliExecute("guild"), - choices: { - //sleazy back alley - 108: 4, //craps: skip - 109: 1, //drunken hobo: fight - 110: 4, //entertainer: skip - 112: 2, //harold's hammer: skip - 21: 2, //under the knife: skip - //haunted pantry - 115: 1, //drunken hobo: fight - 116: 4, //singing tree: skip - 117: 1, //knob goblin chef: fight - 114: 2, //birthday cake: skip - //outskirts of cobb's knob - 113: 2, //knob goblin chef: fight - 111: 3, //chain gang: fight - 118: 2, //medicine quest: skip - }, - outfit: () => ({ - familiar: bestFam(), - modifier: `${maxBase()}, ${ - myPrimestat() === $stat`Muscle` ? "100 combat rate 20 max" : "-100 combat rate" - }, 250 bonus carnivorous potted plant`, - }), - combat: new CombatStrategy() - .macro( - () => - Macro.step("pickpocket") - .externalIf( - have($skill`Curse of Weaksauce`), - Macro.trySkill($skill`Curse of Weaksauce`), - Macro.tryItem($item`electronics kit`), - ) - .tryItem($item`porquoise-handled sixgun`) - .trySkill($skill`Sing Along`) - .attack() - .repeat(), - getTodaysHolidayWanderers(), - ) - .macro(() => - Macro.step("pickpocket") - .trySkill($skill`Sing Along`) - .tryItem($item`porquoise-handled sixgun`) - .attack() - .repeat(), - ), - }, - { - name: "Stock Up on MMJs", - ready: () => - guildStoreAvailable() && - (myClass().primestat === $stat`Mysticality` || - (myClass() === $class`Accordion Thief` && myLevel() >= 9)), - completed: () => availableAmount($item`magical mystery juice`) >= 500, - acquire: [ - { - item: $item`magical mystery juice`, - num: 500, - }, - ], - do: () => false, - }, - { - name: "Buy Seal Summoning Supplies", - ready: () => myClass() === $class`Seal Clubber` && guildStoreAvailable(), - completed: () => - Math.min( - ...$items`figurine of a wretched-looking seal, seal-blubber candle`.map((it) => - availableAmount(it), - ), - ) >= 40, - acquire: $items`figurine of a wretched-looking seal, seal-blubber candle`.map((it) => ({ - item: it, - num: 500, - })), - do: () => false, - }, - { - name: "PvP Closet Safety 2", - ready: () => args.pvp && get("autoSatisfyWithCloset") && !args.safepvp, - completed: () => toBoolean(get("_safetyCloset2")), - do: () => pvpCloset(2), - }, + ...preRunQuests(), + ...postRunQuests(), + ...noBarf(), + ...garboWeen(), + ...crimbo(), + ...chrono(), { name: "Garbo", completed: () => stooperDrunk() || myAdventures() === 0, @@ -531,7 +195,7 @@ export function AftercoreQuest(): Quest { tracking: "Garbo", }, { - name: "PvP Closet Safety 3", + name: "PvP Closet Safety 2", ready: () => args.pvp && get("autoSatisfyWithCloset") && !args.safepvp, completed: () => toBoolean(get("_safetyCloset3")), do: () => pvpCloset(3), diff --git a/src/tasks/casualrunleg.ts b/src/tasks/casualrunleg.ts deleted file mode 100644 index 58e1dbf..0000000 --- a/src/tasks/casualrunleg.ts +++ /dev/null @@ -1,598 +0,0 @@ -import { CombatStrategy, step } from "grimoire-kolmafia"; -import { - buy, - cliExecute, - drink, - Effect, - equip, - familiarWeight, - fullnessLimit, - getClanName, - getWorkshed, - hippyStoneBroken, - inebrietyLimit, - itemAmount, - myAdventures, - myAscensions, - myDaycount, - myFullness, - myInebriety, - myLevel, - myMaxhp, - mySign, - numericModifier, - print, - pvpAttacksLeft, - restoreHp, - restoreMp, - retrieveItem, - setProperty, - storageAmount, - use, - useFamiliar, - useSkill, - visitUrl, - wait, -} from "kolmafia"; -import { - $coinmaster, - $effect, - $effects, - $familiar, - $familiars, - $item, - $items, - $location, - $skill, - AprilingBandHelmet, - clamp, - get, - have, - Macro, - set, - uneffect, -} from "libram"; - -import { args } from "../args"; - -import { getCurrentLeg, Leg, Quest } from "./structure"; -import { - backstageItemsDone, - bestFam, - doneAdventuring, - haveAll, - maxBase, - stooperDrunk, - totallyDrunk, -} from "./utils"; - -let pajamas = false; -let smoke = 1; - -export function howManySausagesCouldIEat() { - if (!have($item`Kramco Sausage-o-Matic™`)) return 0; - // You may be full but you can't be overfull - if (myFullness() > fullnessLimit()) return 0; - - return clamp( - 23 - get("_sausagesEaten"), - 0, - itemAmount($item`magical sausage`) + itemAmount($item`magical sausage casing`), - ); -} - -function firstWorkshed() { - return ( - $items`model train set, Asdon Martin keyfob (on ring), cold medicine cabinet, Little Geneticist DNA-Splicing Lab, portable Mayo Clinic`.find( - (it) => have(it) || getWorkshed() === it || storageAmount(it) > 0, - ) || $item`none` - ); -} -function altWorkshed() { - const ws = getWorkshed(); - switch (ws) { - case $item`model train set`: - return ( - $items`cold medicine cabinet, Asdon Martin keyfob (on ring), Little Geneticist DNA-Splicing Lab, portable Mayo Clinic`.find( - (it) => have(it) || getWorkshed() === it || storageAmount(it) > 0, - ) || ws - ); - case $item`Asdon Martin keyfob (on ring)`: - return ( - $items`cold medicine cabinet, model train set, Little Geneticist DNA-Splicing Lab, portable Mayo Clinic`.find( - (it) => have(it) || getWorkshed() === it || storageAmount(it) > 0, - ) || ws - ); - case $item`cold medicine cabinet`: - return ( - $items`Asdon Martin keyfob (on ring), model train set, Little Geneticist DNA-Splicing Lab, portable Mayo Clinic, warbear induction oven, snow machine`.find( - (it) => have(it) || getWorkshed() === it || storageAmount(it) > 0, - ) || ws - ); - case $item`Little Geneticist DNA-Splicing Lab`: - return ( - $items`cold medicine cabinet, Asdon Martin keyfob (on ring), model train set, portable Mayo Clinic`.find( - (it) => have(it) || getWorkshed() === it || storageAmount(it) > 0, - ) || ws - ); - case $item`portable Mayo Clinic`: - return ( - $items`cold medicine cabinet, model train set, Asdon Martin keyfob (on ring), Little Geneticist DNA-Splicing Lab`.find( - (it) => have(it) || getWorkshed() === it || storageAmount(it) > 0, - ) || ws - ); - default: - return $item`none`; - } -} - -export function CasualQuests(): Quest[] { - return [ - { - name: "Casual Run", - completed: () => getCurrentLeg() !== Leg.Run || get("kingLiberated", false), - tasks: [ - { - name: "Whitelist VIP Clan", - completed: () => !args.clan || getClanName().toLowerCase() === args.clan.toLowerCase(), - do: () => cliExecute(`/whitelist ${args.clan}`), - choices: { - 1507: 1, - }, - }, - { - name: "Prep Fireworks Shop", - completed: () => - !have($item`Clan VIP Lounge key`) || get("_goorboFireworksPrepped", false), - do: () => { - visitUrl("clan_viplounge.php?action=fwshop&whichfloor=2"); - set("_goorboFireworksPrepped", true); - }, - }, - { - name: "LGR Seed", - ready: () => - have($item`lucky gold ring`) && have($item`one-day ticket to Dinseylandfill`), - completed: () => get("_stenchAirportToday") || get("stenchAirportAlways"), - do: () => use($item`one-day ticket to Dinseylandfill`), - tracking: "Garbo", - }, - { - name: "Install First Workshed", - ready: () => have(firstWorkshed()), - completed: () => - firstWorkshed() === $item`none` || - get("_workshedItemUsed") || - getWorkshed() !== $item`none`, - do: () => use(firstWorkshed()), - }, - { - name: "SIT Course", - // eslint-disable-next-line libram/verify-constants - ready: () => have($item`S.I.T. Course Completion Certificate`), - completed: () => get("_sitCourseCompleted", false), - choices: { - 1494: 2, - }, - do: () => - // eslint-disable-next-line libram/verify-constants - use($item`S.I.T. Course Completion Certificate`), - }, - { - name: "Break Stone", - completed: () => hippyStoneBroken() || !args.pvp, - do: (): void => { - visitUrl("peevpee.php?action=smashstone&pwd&confirm=on", true); - visitUrl("peevpee.php?place=fight"); - }, - }, - { - name: "Prepare Empathy", - completed: () => get("_empathyReady", false), - do: (): void => { - cliExecute("maximize MP; set _empathyReady = true"); - }, - }, - { - name: "Stillsuit Prep", - completed: () => itemAmount($item`tiny stillsuit`) === 0, - do: () => - equip( - $item`tiny stillsuit`, - get( - "stillsuitFamiliar", - $familiars`Gelatinous Cubeling, Levitating Potato, Mosquito`.find((fam) => - have(fam), - ) || $familiar`none`, - ), - ), - }, - { - name: "Run", - completed: () => step("questL13Final") > 11, - do: () => cliExecute(args.casualscript), - clear: "all", - tracking: "Run", - }, - { - name: "Free King", - ready: () => step("questL13Final") > 11, - completed: () => get("kingLiberated", false), - do: (): void => { - visitUrl("place.php?whichplace=nstower&action=ns_11_prism"); - }, - clear: "all", - }, - ], - }, - { - name: "Post-casual Aftercore", - ready: () => myDaycount() === 1 && get("kingLiberated", false), - completed: () => totallyDrunk() && pajamas, - tasks: [ - { - name: "Unlock Garbage Mountain", - completed: () => get("_stenchAirportToday") || get("stenchAirportAlways"), - do: (): void => { - retrieveItem($item`one-day ticket to Dinseylandfill`); - use($item`one-day ticket to Dinseylandfill`); - }, - tracking: "Garbo", - }, - { - name: "Wardrobe-o-matic", - ready: () => myLevel() >= 15 && have($item`wardrobe-o-matic`), - completed: () => get("_wardrobeUsed", false), - do: (): void => { - use($item`wardrobe-o-matic`); - cliExecute("set _wardrobeUsed = true"); - }, - limit: { tries: 1 }, - }, - { - name: "Apriling Part 1", - ready: () => AprilingBandHelmet.canChangeSong(), - completed: () => have($effect`Apriling Band Celebration Bop`), - do: (): void => { - AprilingBandHelmet.conduct($effect`Apriling Band Celebration Bop`); - }, - limit: { tries: 1 }, - }, - { - name: "Drink Pre-Tune", - ready: () => - mySign().toLowerCase() === "blender" && - myLevel() >= 7 && - have($item`mime army shotglass`) && - (have($item`astral pilsner`) || have($item`astral six-pack`)), - completed: () => - get("_mimeArmyShotglassUsed") || !have($item`hewn moon-rune spoon`) || get("moonTuned"), - prepare: () => { - if (have($item`astral six-pack`)) use($item`astral six-pack`); - }, - do: () => drink(1, $item`astral pilsner`), - }, - { - name: "Moon Spoon", - completed: () => - !have($item`hewn moon-rune spoon`) || - get("moonTuned") || - mySign().toLowerCase() === "wombat", - do: () => cliExecute("spoon wombat"), - }, - { - name: "Install Alternate Workshed", - ready: () => have(altWorkshed()), - completed: () => - altWorkshed() === $item`none` || - get("_workshedItemUsed") || - getWorkshed() === altWorkshed(), - do: () => use(altWorkshed()), - }, - { - name: "Gold Wedding Ring", - completed: () => - !have($skill`Comprehensive Cartography`) || - myAscensions() === get("lastCartographyBooPeak"), - choices: { 1430: 3, 606: 4, 610: 1, 1056: 1 }, - do: $location`A-Boo Peak`, - outfit: { modifier: "initiative 40 min 40 max, -tie" }, - }, - { - name: "Breakfast", - completed: () => get("breakfastCompleted"), - do: () => cliExecute("breakfast"), - }, - { - name: "Laugh Floor", - completed: () => - have($skill`Liver of Steel`) || - have($item`steel margarita`) || - have($item`Azazel's lollipop`) || - have($item`observational glasses`), - effects: () => [ - ...(have($skill`Musk of the Moose`) ? $effects`Musk of the Moose` : []), - ...(have($skill`Carlweather's Cantata of Confrontation`) - ? $effects`Carlweather's Cantata of Confrontation` - : []), - ], - prepare: (): void => { - if (!have($effect`Carlweather's Cantata of Confrontation`)) { - cliExecute("kmail to Buffy || 10 Cantata of Confrontation"); - wait(15); - cliExecute("refresh effects"); - } - $effects`Smooth Movements, The Sonata of Sneakiness, Darkened Photons, Shifted Phase`.forEach( - (ef: Effect) => cliExecute(`uneffect ${ef}`), - ); - restoreHp(0.75 * myMaxhp()); - restoreMp(20); - }, - do: $location`The Laugh Floor`, - outfit: () => ({ - familiar: bestFam(), - modifier: `${maxBase()}, 100 combat rate, 3 item, 250 bonus carnivorous potted plant`, - }), - combat: new CombatStrategy().macro( - Macro.trySkill($skill`Curse of Weaksauce`) - .tryItem($item`train whistle`) - .tryItem($item`porquoise-handled sixgun`) - .attack() - .repeat(), - ), - limit: { tries: 15 }, - }, - { - name: "Infernal Rackets Backstage", - completed: () => - have($skill`Liver of Steel`) || - have($item`steel margarita`) || - have($item`Azazel's unicorn`) || - backstageItemsDone(), - effects: () => [ - ...(have($skill`Smooth Movement`) ? $effects`Smooth Movements` : []), - ...(have($skill`The Sonata of Sneakiness`) ? $effects`The Sonata of Sneakiness` : []), - ], - prepare: (): void => { - if (!have($effect`The Sonata of Sneakiness`)) { - cliExecute("kmail to Buffy || 10 Sonata of Sneakiness"); - wait(15); - cliExecute("refresh effects"); - } - $effects`Musk of the Moose, Carlweather's Cantata of Confrontation, Hooooooooonk!`.forEach( - (ef: Effect) => cliExecute(`uneffect ${ef}`), - ); - restoreHp(0.75 * myMaxhp()); - restoreMp(20); - }, - do: $location`Infernal Rackets Backstage`, - outfit: () => ({ - familiar: bestFam(), - modifier: `${maxBase()}, -100 combat rate, 3 item, 250 bonus carnivorous potted plant`, - }), - combat: new CombatStrategy().macro( - Macro.trySkill($skill`Curse of Weaksauce`) - .tryItem($item`train whistle`) - .tryItem($item`porquoise-handled sixgun`) - .attack() - .repeat(), - ), - limit: { tries: 15 }, - }, - { - name: "Mourn", - ready: () => have($item`observational glasses`), - completed: () => - have($skill`Liver of Steel`) || - have($item`steel margarita`) || - have($item`Azazel's lollipop`), - outfit: { - equip: $items`hilarious comedy prop, observational glasses, Victor\, the Insult Comic Hellhound Puppet`, - }, - do: () => cliExecute("panda comedy insult; panda comedy observe"), - }, - { - name: "Sven Golly", - ready: () => backstageItemsDone(), - completed: () => - have($skill`Liver of Steel`) || - have($item`steel margarita`) || - have($item`Azazel's unicorn`), - do: (): void => { - cliExecute( - `panda arena Bognort ${$items`giant marshmallow, gin-soaked blotter paper`.find((a) => - have(a), - )}`, - ); - cliExecute( - `panda arena Stinkface ${$items`beer-scented teddy bear, gin-soaked blotter paper`.find( - (a) => have(a), - )}`, - ); - cliExecute( - `panda arena Flargwurm ${$items`booze-soaked cherry, sponge cake`.find((a) => - have(a), - )}`, - ); - cliExecute(`panda arena Jim ${$items`comfy pillow, sponge cake`.find((a) => have(a))}`); - }, - }, - { - name: "Moaning Panda", - ready: () => haveAll($items`Azazel's lollipop, Azazel's unicorn`), - completed: () => - have($skill`Liver of Steel`) || - have($item`steel margarita`) || - have($item`Azazel's tutu`), - acquire: () => - $items`bus pass, imp air`.map((it) => ({ - item: it, - num: 5, - price: get("valueOfAdventure"), - })), - do: () => cliExecute("panda moan"), - limit: { tries: 3 }, - }, - { - name: "Steel Margarita", - ready: () => haveAll($items`Azazel's tutu, Azazel's lollipop, Azazel's unicorn`), - completed: () => have($skill`Liver of Steel`) || have($item`steel margarita`), - do: () => cliExecute("panda temple"), - }, - { - name: "Liver of Steel", - ready: () => have($item`steel margarita`), - completed: () => have($skill`Liver of Steel`), - do: () => drink(1, $item`steel margarita`), - }, - { - name: "Garbo", - ready: () => get("_stenchAirportToday") || get("stenchAirportAlways"), - completed: () => myAdventures() === 0 || stooperDrunk(), - prepare: () => uneffect($effect`Beaten Up`), - do: () => cliExecute(args.garbo), - post: () => - $effects`Power Ballad of the Arrowsmith, Stevedave's Shanty of Superiority, The Moxious Madrigal, The Magical Mojomuscular Melody, Aloysius' Antiphon of Aptitude, Ur-Kel's Aria of Annoyance` - .filter((ef) => have(ef)) - .forEach((ef) => uneffect(ef)), - clear: "all", - tracking: "Garbo", - }, - { - name: "Turn in FunFunds", - ready: () => get("_stenchAirportToday") && itemAmount($item`FunFunds™`) >= 20, - completed: () => have($item`one-day ticket to Dinseylandfill`), - do: () => - buy($coinmaster`The Dinsey Company Store`, 1, $item`one-day ticket to Dinseylandfill`), - tracking: "Garbo", - }, - { - name: "PvP", - ready: () => doneAdventuring(), - completed: () => pvpAttacksLeft() === 0 || !hippyStoneBroken(), - do: (): void => { - cliExecute("unequip"); - cliExecute("UberPvPOptimizer"); - cliExecute(`PVP_MAB target=${args.pvpTarget}`); - }, - }, - { - name: "Stooper", - ready: () => - myInebriety() === inebrietyLimit() && - have($item`tiny stillsuit`) && - get("familiarSweat") >= 300, - completed: () => !have($familiar`Stooper`) || stooperDrunk(), - do: () => { - useFamiliar($familiar`Stooper`); - cliExecute("drink stillsuit distillate"); - }, - }, - { - name: "Nightcap", - ready: () => doneAdventuring(), - completed: () => totallyDrunk(), - do: () => cliExecute("CONSUME NIGHTCAP"), - }, - { - name: "Smoke em if you got em", - ready: () => get("getawayCampsiteUnlocked"), - completed: () => !have($item`stick of firewood`), - do: (): void => { - while (have($item`stick of firewood`)) { - setProperty( - "choiceAdventure1394", - `1&message=${smoke} Thanks Seraphiii for writing Candywrapper!`, - ); - use(1, $item`campfire smoke`); - print(`Smoked ${smoke} firewoods!`); - smoke = smoke + 1; - } - }, - }, - { - name: "Offhand Remarkable", - ready: () => have($item`august scepter`), - completed: () => - !have($skill`Aug. 13th: Left/Off Hander's Day!`) || - have($effect`Offhand Remarkable`) || - get("_aug13Cast", false), - do: () => useSkill($skill`Aug. 13th: Left/Off Hander's Day!`), - }, - { - name: "Item Cleanup", - // eslint-disable-next-line libram/verify-constants - completed: () => get("_cleanupToday", false) || args.itemcleanup === "", - do: (): void => { - cliExecute(`${args.itemcleanup}`); - cliExecute("set _cleanupToday = true"); - }, - clear: "all", - tracking: "Item Cleanup", - }, - { - name: "Apriling Part 2", - ready: () => AprilingBandHelmet.canJoinSection(), - completed: () => !AprilingBandHelmet.canPlay($item`Apriling band piccolo`), - do: (): void => { - AprilingBandHelmet.joinSection($item`Apriling band piccolo`); - if (AprilingBandHelmet.canJoinSection()) { - AprilingBandHelmet.joinSection($item`Apriling band saxophone`); - AprilingBandHelmet.play($item`Apriling band saxophone`); - } - if (have($familiar`Grey Goose`)) useFamiliar($familiar`Grey Goose`); - else if (have($familiar`Chest Mimic`)) useFamiliar($familiar`Chest Mimic`); - else if ( - have($familiar`Pocket Professor`) && - familiarWeight($familiar`Pocket Professor`) < 20 - ) - useFamiliar($familiar`Pocket Professor`); - else if (have($familiar`Comma Chameleon`)) useFamiliar($familiar`Comma Chameleon`); - while ( - $item`Apriling band piccolo`.dailyusesleft > 0 && - have($item`Apriling band piccolo`) - ) - AprilingBandHelmet.play($item`Apriling band piccolo`); - }, - limit: { tries: 1 }, - }, - { - name: "Pajamas", - completed: () => have($item`burning cape`), - acquire: [ - { item: $item`clockwork maid`, price: 7 * get("valueOfAdventure"), optional: true }, - { item: $item`burning cape` }, - ], - do: (): void => { - if (have($item`clockwork maid`)) { - use($item`clockwork maid`); - } - pajamas = true; - }, - outfit: () => ({ - familiar: - $familiars`Trick-or-Treating Tot, Left-Hand Man, Disembodied Hand, Grey Goose`.find( - (fam) => have(fam), - ), - modifier: `adventures${args.pvp ? ", 0.3 fites" : ""}`, - }), - }, - { - name: "Alert-No Nightcap", - ready: () => !doneAdventuring(), - completed: () => stooperDrunk(), - do: (): void => { - const targetAdvs = 100 - numericModifier("adventures"); - print("smol completed, but did not overdrink.", "red"); - if (targetAdvs < myAdventures() && targetAdvs > 0) - print( - `Rerun with fewer than ${targetAdvs} adventures for smol to handle your diet`, - "red", - ); - else print("Something went wrong.", "red"); - }, - }, - ], - }, - ]; -} diff --git a/src/tasks/csrunleg.ts b/src/tasks/csrunleg.ts deleted file mode 100644 index 7dee2b6..0000000 --- a/src/tasks/csrunleg.ts +++ /dev/null @@ -1,431 +0,0 @@ -import { - buy, - cliExecute, - drink, - fullnessLimit, - getClanName, - getWorkshed, - haveEffect, - hippyStoneBroken, - holiday, - inebrietyLimit, - itemAmount, - mallPrice, - myAdventures, - myAscensions, - myFullness, - myInebriety, - myLevel, - mySign, - mySpleenUse, - numericModifier, - print, - pvpAttacksLeft, - retrieveItem, - setProperty, - spleenLimit, - toBoolean, - use, - useFamiliar, - useSkill, - visitUrl, -} from "kolmafia"; -import { - $coinmaster, - $effect, - $effects, - $familiar, - $item, - $items, - $skill, - AsdonMartin, - gameDay, - get, - have, - set, - uneffect, -} from "libram"; - -import { args } from "../args"; - -import { getCurrentLeg, Leg, Quest } from "./structure"; -import { - canDiet, - doneAdventuring, - getGarden, - pvpCloset, - stooperDrunk, - totallyDrunk, -} from "./utils"; - -let pajamas = false; -let smoke = 1; -const offhandWorth = have($familiar`Left-Hand Man`); -let garboDone = false; - -export function CSQuests(): Quest[] { - return [ - { - name: "Community Service Run", - completed: () => getCurrentLeg() !== Leg.Run || get("kingLiberated"), - tasks: [ - { - name: "Whitelist VIP Clan", - completed: () => !args.clan || getClanName().toLowerCase() === args.clan.toLowerCase(), - do: () => cliExecute(`/whitelist ${args.clan}`), - }, - { - name: "Break Stone", - ready: () => !args.safepvp, - completed: () => hippyStoneBroken() || !args.pvp, - do: (): void => { - visitUrl("peevpee.php?action=smashstone&pwd&confirm=on", true); - visitUrl("peevpee.php?place=fight"); - }, - }, - { - name: "Prep Fireworks Shop", - completed: () => - !have($item`Clan VIP Lounge key`) || get("_goorboFireworksPrepped", false), - do: () => { - visitUrl("clan_viplounge.php?action=fwshop&whichfloor=2"); - set("_goorboFireworksPrepped", true); - }, - tracking: "Run", - }, - { - name: "Run", - completed: () => get("kingLiberated"), - do: () => cliExecute(args.csscript), - tracking: "Run", - }, - ], - }, - { - name: "Post-Community Service Aftercore", - ready: () => getCurrentLeg() === Leg.Run && get("kingLiberated", false), - completed: () => totallyDrunk() && pajamas, - tasks: [ - { - name: "Pull All", - completed: () => get("lastEmptiedStorage") === myAscensions(), - do: () => cliExecute("pull all; refresh all"), - }, - { - name: "PvP Closet Safety 1", - ready: () => args.pvp && get("autoSatisfyWithCloset") && !args.safepvp, - completed: () => toBoolean(get("_safetyCloset1")), - do: () => pvpCloset(1), - }, - { - name: "Ensure prefs reset", - completed: () => !get("_folgerInitialConfig", false), - do: () => cliExecute("set _folgerInitialConfig = false"), - }, - { - name: "But dad I don't want to feel lost", - completed: () => !have($effect`Feeling Lost`), - do: () => uneffect($effect`Feeling Lost`), - }, - { - name: "Clear citizen", - completed: () => get("_citizenZone", "") !== "Madness Bakery", - do: (): void => { - uneffect($effect`Citizen of a Zone`); - cliExecute(`set _citizenZone = ""`); - }, - }, - { - name: "Wardrobe-o-matic", - ready: () => myLevel() >= 15 && have($item`wardrobe-o-matic`), - completed: () => get("_wardrobeUsed", false), - do: (): void => { - use($item`wardrobe-o-matic`); - cliExecute("set _wardrobeUsed = true"); - }, - limit: { tries: 1 }, - }, - { - name: "Smoke em if you got em", - ready: () => get("getawayCampsiteUnlocked"), - completed: () => !have($item`stick of firewood`) || smoke >= 10, - do: (): void => { - if (mallPrice($item`stick of firewood`) <= 200) buy($item`stick of firewood`, 10); - while (have($item`stick of firewood`)) { - setProperty( - "choiceAdventure1394", - `1&message=${smoke} Thanks Seraphiii for writing Candywrapper!`, - ); - use(1, $item`campfire smoke`); - print(`Smoked ${smoke} firewoods!`); - smoke = smoke + 1; - } - if (mallPrice($item`stick of firewood`) <= 200) buy($item`stick of firewood`, 1); - }, - }, - { - name: "Acquire Carpe", - completed: () => !args.carpe || have($item`carpe`), - do: () => cliExecute("acquire carpe"), - }, - { - name: "Unlock Desert", - completed: () => have($item`bitchin' meatcar`), - do: () => cliExecute("acquire bitchin"), - }, - { - name: "Drink Pre-Tune", - ready: () => - mySign().toLowerCase() === "blender" && - myLevel() >= 7 && - have($item`mime army shotglass`) && - (have($item`astral pilsner`) || have($item`astral six-pack`)), - completed: () => - get("_mimeArmyShotglassUsed") || !have($item`hewn moon-rune spoon`) || get("moonTuned"), - prepare: () => { - if (have($item`astral six-pack`)) use($item`astral six-pack`); - }, - do: () => drink(1, $item`astral pilsner`), - }, - { - name: "Moon Spoon", - completed: () => - !have($item`hewn moon-rune spoon`) || - get("moonTuned") || - mySign().toLowerCase() === "wombat", - do: () => cliExecute("spoon wombat"), - }, - { - name: "Drive Observantly", - completed: () => - getWorkshed() !== $item`Asdon Martin keyfob (on ring)` || - haveEffect($effect`Driving Observantly`) >= - (totallyDrunk() || !have($item`Drunkula's wineglass`) - ? myAdventures() - : myAdventures() + 60), - do: () => - AsdonMartin.drive( - $effect`Driving Observantly`, - totallyDrunk() || !have($item`Drunkula's wineglass`) - ? myAdventures() - : myAdventures() + 60, - false, - ), - limit: { tries: 5 }, - }, - { - name: "Breakfast", - completed: () => get("breakfastCompleted"), - do: () => cliExecute("breakfast"), - }, - { - name: "Garbo", - ready: () => !holiday().includes("Halloween"), - completed: () => (myAdventures() === 0 && !canDiet()) || stooperDrunk(), - prepare: () => uneffect($effect`Beaten Up`), - do: () => cliExecute(`${args.garbo}`), - post: () => - $effects`Power Ballad of the Arrowsmith, Stevedave's Shanty of Superiority, The Moxious Madrigal, The Magical Mojomuscular Melody, Aloysius' Antiphon of Aptitude, Ur-Kel's Aria of Annoyance` - .filter((ef) => have(ef)) - .forEach((ef) => uneffect(ef)), - clear: "all", - tracking: "Garbo", - }, - { - name: "CONSUME ALL", - ready: () => holiday().includes("Halloween"), - completed: () => - myFullness() >= fullnessLimit() && - mySpleenUse() >= spleenLimit() && - myInebriety() >= inebrietyLimit(), - do: () => cliExecute("consume ALL"), - }, - { - name: "Garbo Nobarf", - ready: () => holiday().includes("Halloween"), - completed: () => garboDone, - do: (): void => { - cliExecute(`${args.garbo} nodiet nobarf target="witchess knight"`); - garboDone = true; - }, - }, - { - name: "Garboween", - ready: () => holiday().includes("Halloween"), - completed: () => Math.floor(myAdventures() / 5) < 1, - prepare: () => uneffect($effect`Beaten Up`), - do: (): void => { - if (have($familiar`Trick-or-Treating Tot`)) - cliExecute("familiar Trick-or-Treating Tot"); - else if (have($familiar`Red-Nosed Snapper`)) cliExecute("familiar snapper"); - cliExecute(`freeCandy ${myAdventures()}`); - }, - post: () => { - if (myAdventures() === 0) - $effects`Power Ballad of the Arrowsmith, Stevedave's Shanty of Superiority, The Moxious Madrigal, The Magical Mojomuscular Melody, Aloysius' Antiphon of Aptitude, Ur-Kel's Aria of Annoyance` - .filter((ef) => have(ef)) - .forEach((ef) => uneffect(ef)); - }, - clear: "all", - tracking: "Garbo", - }, - { - name: "PvP Closet Safety 2", - ready: () => args.pvp && get("autoSatisfyWithCloset") && !args.safepvp, - completed: () => toBoolean(get("_safetyCloset2")), - do: () => pvpCloset(2), - }, - { - name: "Turn in FunFunds", - ready: () => get("_stenchAirportToday") && itemAmount($item`FunFunds™`) >= 20, - completed: () => have($item`one-day ticket to Dinseylandfill`), - do: () => - buy($coinmaster`The Dinsey Company Store`, 1, $item`one-day ticket to Dinseylandfill`), - tracking: "Garbo", - }, - { - name: "PvP", - ready: () => doneAdventuring() && !args.safepvp, - completed: () => pvpAttacksLeft() === 0 || !hippyStoneBroken(), - do: (): void => { - cliExecute("unequip"); - cliExecute("UberPvPOptimizer"); - cliExecute(`PVP_MAB target=${args.pvpTarget}`); - }, - }, - { - name: "Stooper", - ready: () => - myInebriety() === inebrietyLimit() && - have($item`tiny stillsuit`) && - get("familiarSweat") >= 300, - completed: () => !have($familiar`Stooper`) || stooperDrunk(), - do: () => { - useFamiliar($familiar`Stooper`); - cliExecute("drink stillsuit distillate"); - }, - }, - { - name: "Nightcap", - ready: () => doneAdventuring(), - completed: () => totallyDrunk(), - do: () => cliExecute("CONSUME NIGHTCAP"), - }, - { - name: "Do Pizza", - ready: () => doneAdventuring(), - completed: () => - have($item`Pizza of Legend`) && - have($item`Deep Dish of Legend`) && - have($item`Calzone of Legend`), - do: (): void => { - !have($item`Pizza of Legend`) ? retrieveItem($item`Pizza of Legend`) : undefined; - !have($item`Deep Dish of Legend`) - ? retrieveItem($item`Deep Dish of Legend`) - : undefined; - !have($item`Calzone of Legend`) ? retrieveItem($item`Calzone of Legend`) : undefined; - }, - }, - { - name: "Plant Garden", - ready: () => - doneAdventuring() && - !!$items`packet of rock seeds, packet of thanksgarden seeds, Peppermint Pip Packet, packet of winter seeds, packet of beer seeds, packet of pumpkin seeds, packet of dragon's teeth`.find( - (it) => have(it), - ), - completed: () => getGarden() !== $item`packet of tall grass seeds`, - do: () => { - use( - $items`packet of rock seeds, packet of thanksgarden seeds, Peppermint Pip Packet, packet of winter seeds, packet of beer seeds, packet of pumpkin seeds, packet of dragon's teeth`.find( - (it) => have(it), - ) || $item`none`, - ); - cliExecute("garden pick"); - }, - }, - { - name: "Freecandy Drunk", - ready: () => holiday().includes("Halloween"), - completed: () => stooperDrunk() || (!canDiet() && Math.floor(myAdventures() / 5) === 0), - prepare: () => uneffect($effect`Beaten Up`), - do: (): void => { - cliExecute(`freeCandy ${myAdventures()}`); - }, - post: () => { - if (myAdventures() === 0) - $effects`Power Ballad of the Arrowsmith, Stevedave's Shanty of Superiority, The Moxious Madrigal, The Magical Mojomuscular Melody, Aloysius' Antiphon of Aptitude, Ur-Kel's Aria of Annoyance` - .filter((ef) => have(ef)) - .forEach((ef) => uneffect(ef)); - }, - clear: "all", - tracking: "Garbo", - limit: { tries: 1 }, //this will run again after installing CMC, by magic - }, - { - name: "Offhand Remarkable", - ready: () => have($item`august scepter`), - completed: () => - have($effect`Offhand Remarkable`) || - get("_aug13Cast", false) || - (get("_augSkillsCast", 0) >= 5 && gameDay().getDate() !== 13), - do: () => useSkill($skill`Aug. 13th: Left/Off Hander's Day!`), - }, - { - name: "Alternative Offhand Remarkable", - ready: () => offhandWorth, - completed: () => have($effect`Offhand Remarkable`), - do: (): void => { - retrieveItem($item`pocket wish`); - cliExecute("genie effect Aug. 13th: Left/Off Hander's Day!"); - }, - }, - { - name: "Item Cleanup", - // eslint-disable-next-line libram/verify-constants - completed: () => get("_cleanupToday", false) || args.itemcleanup === "", - do: (): void => { - cliExecute(`${args.itemcleanup}`); - cliExecute("set _cleanupToday = true"); - }, - tracking: "Item Cleanup", - }, - { - name: "PvP Closet Safety 3", - ready: () => args.pvp && get("autoSatisfyWithCloset") && !args.safepvp, - completed: () => toBoolean(get("_safetyCloset3")), - do: () => pvpCloset(3), - }, - { - name: "Pajamas", - completed: () => have($item`burning cape`), - acquire: [ - { item: $item`clockwork maid`, price: 7 * get("valueOfAdventure"), optional: true }, - { item: $item`burning cape` }, - ], - do: (): void => { - if (have($item`clockwork maid`)) { - use($item`clockwork maid`); - } - cliExecute("maximize adv, switch disembodied hand, -switch left-hand man"); - pajamas = true; - }, - }, - { - name: "Alert-No Nightcap", - ready: () => !doneAdventuring(), - completed: () => stooperDrunk(), - do: (): void => { - const targetAdvs = 100 - numericModifier("adventures"); - print("candyWrapper completed, but did not overdrink.", "red"); - if (targetAdvs < myAdventures() && targetAdvs > 0) - print( - `Rerun with fewer than ${targetAdvs} adventures for candyWrapper to handle your diet`, - "red", - ); - else print("Something went wrong.", "red"); - }, - }, - ], - }, - ]; -} diff --git a/src/tasks/robotrunleg.ts b/src/tasks/postrunleg.ts similarity index 61% rename from src/tasks/robotrunleg.ts rename to src/tasks/postrunleg.ts index ccf8bf9..e13c721 100644 --- a/src/tasks/robotrunleg.ts +++ b/src/tasks/postrunleg.ts @@ -1,198 +1,23 @@ -import { CombatStrategy, step } from "grimoire-kolmafia"; -import { - buy, - buyUsingStorage, - cliExecute, - drink, - Effect, - equip, - Familiar, - getClanName, - getWorkshed, - hippyStoneBroken, - inebrietyLimit, - itemAmount, - mallPrice, - myAdventures, - myAscensions, - myDaycount, - myFamiliar, - myInebriety, - myLevel, - myMaxhp, - mySign, - numericModifier, - print, - pullsRemaining, - pvpAttacksLeft, - restoreHp, - restoreMp, - retrieveItem, - setProperty, - storageAmount, - toBoolean, - toInt, - toSkill, - use, - useFamiliar, - useSkill, - visitUrl, - wait, -} from "kolmafia"; -import { - $coinmaster, - $effect, - $effects, - $familiar, - $familiars, - $item, - $items, - $location, - $skill, - AprilingBandHelmet, - get, - have, - Macro, - set, - uneffect, -} from "libram"; - +import { CombatStrategy } from "grimoire-kolmafia"; +import { buy, cliExecute, drink, Effect, hippyStoneBroken, inebrietyLimit, itemAmount, mallPrice, myAdventures, myAscensions, myDaycount, myInebriety, myLevel, myMaxhp, mySign, numericModifier, print, pvpAttacksLeft, restoreHp, restoreMp, retrieveItem, setProperty, toBoolean, use, useFamiliar, useSkill, wait } from "kolmafia"; +import { $coinmaster, $effect, $effects, $familiar, $familiars, $item, $items, $location, $skill, get, have, Macro, uneffect } from "libram"; import { args } from "../args"; - -import { GarboWeenQuest } from "./Garboween"; -import { getCurrentLeg, Leg, Quest } from "./structure"; -import { - backstageItemsDone, - doneAdventuring, - halloween, - haveAll, - maxBase, - nextCyberZone, - pvpCloset, - stooperDrunk, - totallyDrunk, -} from "./utils"; +import { chrono, crimbo, garboWeen, noBarf, postRunQuests } from "./repeatableTasks"; +import { Quest } from "./structure"; +import { backstageItemsDone, bestFam, doneAdventuring, haveAll, maxBase, pvpCloset, stooperDrunk, totallyDrunk } from "./utils"; let pajamas = false; -let smoke = 1; -let duffo = false; - -function firstWorkshed() { - return ( - $items`model train set, Asdon Martin keyfob (on ring), cold medicine cabinet, Little Geneticist DNA-Splicing Lab, portable Mayo Clinic`.find( - (it) => have(it) || getWorkshed() === it || storageAmount(it) > 0, - ) || $item`none` - ); -} +let smoke = 0; const sasqBonus = (0.5 * 30 * 1000) / get("valueOfAdventure"); const ratskinBonus = (0.3 * 40 * 1000) / get("valueOfAdventure"); -const bestFam = () => - famCheck($familiar`Pocket Professor`) - ? $familiar`Pocket Professor` - : famCheck($familiar`Chest Mimic`) - ? $familiar`Chest Mimic` - : famCheck($familiar`Grey Goose`) - ? $familiar`Grey Goose` - : $familiar`Grey Goose`; - -function famCheck(fam: Familiar): boolean { - return have(fam) && fam.experience < 400; -} +const checkMelange = () => + get("valueOfAdventure") * 45 > mallPrice($item`spice melange`) && + !have($item`designer sweatpants`); -export function RobotQuests(): Quest[] { - return [ - { - name: "Robot Run", - completed: () => getCurrentLeg() !== Leg.Run || get("kingLiberated", false), - tasks: [ - { - name: "Whitelist VIP Clan", - completed: () => !args.clan || getClanName().toLowerCase() === args.clan.toLowerCase(), - do: () => cliExecute(`/whitelist ${args.clan}`), - choices: { - 1507: 1, - }, - }, - { - name: "Unpack Duffel Bag", - completed: () => duffo, - do: () => { - visitUrl("inventory.php?action=skiduffel&pwd"); - duffo = true; - }, - }, - { - name: "Get Floundry item", - ready: () => have($item`Clan VIP Lounge key`) && !args.carpe, - completed: () => get("_floundryItemCreated"), - do: (): void => { - retrieveItem($item`carpe`); - }, - limit: { tries: 1 }, - }, - { - name: "Prep Fireworks Shop", - completed: () => - !have($item`Clan VIP Lounge key`) || get("_goorboFireworksPrepped", false), - do: () => { - visitUrl("clan_viplounge.php?action=fwshop&whichfloor=2"); - set("_goorboFireworksPrepped", true); - }, - }, - { - name: "Pre-Pulls", - completed: () => - pullsRemaining() === 0 || - !args.pulls.find( - (it) => !have(it) && !get("_roninStoragePulls").includes(toInt(it).toString()), - ), //can't find a pull that (we dont have and it hasn't been pulled today) - do: () => - args.pulls.forEach((it) => { - if (!have(it) && !get("_roninStoragePulls").includes(toInt(it).toString())) { - if (storageAmount(it) === 0) buyUsingStorage(it); //should respect autoBuyPriceLimit - cliExecute(`pull ${it}`); - } - }), - }, - { - name: "LGR Seed", - ready: () => - have($item`lucky gold ring`) && have($item`one-day ticket to Dinseylandfill`), - completed: () => get("_stenchAirportToday") || get("stenchAirportAlways"), - do: () => use($item`one-day ticket to Dinseylandfill`), - tracking: "Garbo", - }, - { - name: "Break Stone", - ready: () => !args.safepvp, - completed: () => hippyStoneBroken() || !args.pvp, - do: (): void => { - visitUrl("peevpee.php?action=smashstone&pwd&confirm=on", true); - visitUrl("peevpee.php?place=fight"); - }, - }, - { - name: "Run", - completed: () => step("questL13Final") > 11, - do: () => cliExecute(args.robotscript), - clear: "all", - tracking: "Run", - }, - { - name: "Free King", - ready: () => step("questL13Final") > 11, - completed: () => get("kingLiberated", false), - do: (): void => { - visitUrl("place.php?whichplace=nstower&action=ns_11_prism"); - }, - clear: "all", - tracking: "Ignore", - }, - ], - }, - { - name: "Post-Robot Aftercore", +export function PostRunQuests(): Quest { + return { + name: "Post-Run Aftercore", ready: () => myDaycount() === 1 && get("kingLiberated", false), completed: () => totallyDrunk() && pajamas, tasks: [ @@ -203,45 +28,48 @@ export function RobotQuests(): Quest[] { }, { name: "Clear citizen", - completed: () => !get("_citizenZone", "").includes("castle"), + completed: () => !get("_citizenZone", "").includes("castle") && !get("_citizenZone", "").includes("Madness Bakery"), do: (): void => { uneffect($effect`Citizen of a Zone`); cliExecute(`set _citizenZone = ""`); }, }, { - name: "PvP Closet Safety 1", - ready: () => args.pvp && get("autoSatisfyWithCloset") && !args.safepvp, - completed: () => toBoolean(get("_safetyCloset1")), - do: () => pvpCloset(1), + name: "Ensure prefs reset", + completed: () => !get("_folgerInitialConfig", false), + do: () => cliExecute("set _folgerInitialConfig = false"), }, { - name: "Install First Workshed", - ready: () => have(firstWorkshed()), - completed: () => - firstWorkshed() === $item`none` || - get("_workshedItemUsed") || - getWorkshed() !== $item`none`, - do: () => use(firstWorkshed()), + name: "But dad I don't want to feel lost", + completed: () => !have($effect`Feeling Lost`), + do: () => uneffect($effect`Feeling Lost`), }, { - name: "Wardrobe-o-matic", - ready: () => myLevel() >= 15 && have($item`wardrobe-o-matic`), - completed: () => get("_wardrobeUsed", false), + name: "Sober Up", + ready:() => args.smol, + completed: () => + (myInebriety() <= 15 || + get("_mimeArmyShotglassUsed") || + get("_sweatOutSomeBoozeUsed", 0) === 3), do: (): void => { - use($item`wardrobe-o-matic`); - cliExecute("set _wardrobeUsed = true"); + if (checkMelange()) { + cliExecute("acquire spice melange; use spice melange"); + } + while (get("_sweatOutSomeBoozeUsed", 0) < 3) { + useSkill($skill`Sweat Out Some Booze`); + } + if (!get("_sobrieTeaUsed", false)) { + retrieveItem($item`cuppa Sobrie tea`); + use($item`cuppa Sobrie tea`); + } + use($item`synthetic dog hair pill`); }, - limit: { tries: 1 }, }, { - name: "Apriling Part 1", - ready: () => AprilingBandHelmet.canChangeSong(), - completed: () => have($effect`Apriling Band Celebration Bop`), - do: (): void => { - AprilingBandHelmet.conduct($effect`Apriling Band Celebration Bop`); - }, - limit: { tries: 1 }, + name: "PvP Closet Safety 1", + ready: () => args.pvp && get("autoSatisfyWithCloset") && !args.safepvp, + completed: () => toBoolean(get("_safetyCloset1")), + do: () => pvpCloset(1), }, { name: "Drink Pre-Tune", @@ -267,18 +95,14 @@ export function RobotQuests(): Quest[] { }, { name: "Gold Wedding Ring", + ready: () => !args.cs, completed: () => - !have($skill`Comprehensive Cartography`) || - myAscensions() === get("lastCartographyBooPeak"), + (!have($skill`Comprehensive Cartography`) || + myAscensions() === get("lastCartographyBooPeak")), choices: { 1430: 3, 606: 4, 610: 1, 1056: 1 }, do: $location`A-Boo Peak`, outfit: { modifier: "initiative 40 min 40 max, -tie" }, }, - { - name: "Breakfast", - completed: () => get("breakfastCompleted"), - do: () => cliExecute("breakfast"), - }, { name: "Emergency Drink", ready: () => myAdventures() < 25, @@ -309,6 +133,7 @@ export function RobotQuests(): Quest[] { }, { name: "Laugh Floor", + ready: () => !args.cs, completed: () => have($skill`Liver of Steel`) || have($item`steel margarita`) || @@ -348,6 +173,7 @@ export function RobotQuests(): Quest[] { }, { name: "Infernal Rackets Backstage", + ready: () => !args.cs, completed: () => have($skill`Liver of Steel`) || have($item`steel margarita`) || @@ -385,7 +211,7 @@ export function RobotQuests(): Quest[] { }, { name: "Mourn", - ready: () => have($item`observational glasses`), + ready: () => have($item`observational glasses`) && !args.cs, completed: () => have($skill`Liver of Steel`) || have($item`steel margarita`) || @@ -397,7 +223,7 @@ export function RobotQuests(): Quest[] { }, { name: "Sven Golly", - ready: () => backstageItemsDone(), + ready: () => backstageItemsDone() && !args.cs, completed: () => have($skill`Liver of Steel`) || have($item`steel margarita`) || @@ -423,7 +249,7 @@ export function RobotQuests(): Quest[] { }, { name: "Moaning Panda", - ready: () => haveAll($items`Azazel's lollipop, Azazel's unicorn`), + ready: () => haveAll($items`Azazel's lollipop, Azazel's unicorn`) && !args.cs, completed: () => have($skill`Liver of Steel`) || have($item`steel margarita`) || @@ -449,12 +275,6 @@ export function RobotQuests(): Quest[] { completed: () => have($skill`Liver of Steel`), do: () => drink(1, $item`steel margarita`), }, - { - name: "GarboWeen", - ready: () => halloween, - completed: () => myAdventures() === 0 || stooperDrunk(), - do: () => GarboWeenQuest(), - }, { name: "Emergency Drink Part 3", ready: () => myAdventures() < 40 && myInebriety() < 11, @@ -470,67 +290,11 @@ export function RobotQuests(): Quest[] { }, limit: { tries: 6 }, }, - { - name: "Candy Deviler", - // eslint-disable-next-line libram/verify-constants - ready: () => have($item`candy egg deviler`), - completed: () => toInt(get("_candyEggsDeviled")) >= 3, - do: () => { - visitUrl(`inventory.php?action=eggdevil&pwd`); - visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); - visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); - visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); - }, - }, - { - name: "CyberRealm: Prepare Familiar", - completed: () => myFamiliar() === $familiar`Shorter-Order Cook`, - do: () => { - if (have($familiar`Shorter-Order Cook`)) { - cliExecute("familiar Shorter-Order Cook"); - equip($familiar`Shorter-Order Cook`, $item`blue plate`); - } - cliExecute(`familiar ${bestFam().name}`); - cliExecute("familiar Shorter-Order Cook"); - }, - }, - { - name: "Run CyberRealm", - ready: () => mallPrice($item`1`) > 1_000, - prepare: () => { - $effects`Astral Shell, Elemental Saucesphere, Scarysauce`.forEach((ef) => { - if (!have(ef)) useSkill(toSkill(ef)); - }); - }, - completed: () => nextCyberZone() === $location`none`, // $location`Cyberzone 1`.turnsSpent >= 19 * myDaycount(), - choices: { 1545: 1, 1546: 1, 1547: 1, 1548: 1, 1549: 1, 1550: 1 }, - do: nextCyberZone(), - outfit: { - hat: $item`Crown of Thrones`, - back: $item`unwrapped knock-off retro superhero cape`, - shirt: $item`zero-trust tanktop`, - weapon: $item`encrypted shuriken`, - offhand: $item`visual packet sniffer`, - pants: $item`digibritches`, - acc1: $item`retro floppy disk`, - acc2: $item`retro floppy disk`, - acc3: $item`retro floppy disk`, - famequip: $item`familiar-in-the-middle wrapper`, - modes: { retrocape: ["vampire", "hold"] }, - riders: { "crown-of-thrones": $familiar`Mini Kiwi` }, - }, - combat: new CombatStrategy().macro(() => - Macro.if_( - "!monsterphylum construct", - Macro.trySkill($skill`Sing Along`) - .trySkill($skill`Saucestorm`) - .repeat(), - ) - .skill($skill`Throw Cyber Rock`) - .repeat(), - ), - limit: { skip: 60 }, - }, + ...postRunQuests(), + ...noBarf(), + ...garboWeen(), + ...crimbo(), + ...chrono(), { name: "Garbo", completed: () => myAdventures() === 0 || stooperDrunk(), @@ -665,7 +429,6 @@ export function RobotQuests(): Quest[] { else print("Something went wrong.", "red"); }, }, - ], - }, - ]; + ] + } } diff --git a/src/tasks/repeatableTasks.ts b/src/tasks/repeatableTasks.ts new file mode 100644 index 0000000..a38a761 --- /dev/null +++ b/src/tasks/repeatableTasks.ts @@ -0,0 +1,471 @@ +import { CombatStrategy } from "grimoire-kolmafia"; +import { availableAmount, canAdventure, cliExecute, equip, Familiar, fullnessLimit, getCampground, getClanName, getWorkshed, guildStoreAvailable, handlingChoice, haveEffect, hippyStoneBroken, holiday, inebrietyLimit, mallPrice, myAdventures, myClass, myDaycount, myFamiliar, myFullness, myHp, myInebriety, myLevel, myMaxhp, myPrimestat, mySpleenUse, restoreHp, retrieveItem, spleenLimit, toInt, toSkill, use, useFamiliar, useSkill, visitUrl } from "kolmafia"; +import { $class, $effect, $effects, $familiar, $item, $items, $location, $monster, $skill, $stat, AprilingBandHelmet, AsdonMartin, get, getTodaysHolidayWanderers, have, Macro, set, uneffect } from "libram"; +import { Task } from "./structure"; +import { getGarden, maxBase, nextCyberZone, stooperDrunk, totallyDrunk } from "./utils"; +import { args } from "../args"; + +const bestFam = () => + famCheck($familiar`Pocket Professor`) + ? $familiar`Pocket Professor` + : famCheck($familiar`Chest Mimic`) + ? $familiar`Chest Mimic` + : famCheck($familiar`Grey Goose`) + ? $familiar`Grey Goose` + : $familiar`Grey Goose`; + +function famCheck(fam: Familiar): boolean { + return have(fam) && fam.experience < 400; +} + +const doSmol = args.smol ? true : false; + +export function postRunQuests(): Task[] { +return [ + { + name: "Whitelist VIP Clan", + completed: () => !args.clan || getClanName().toLowerCase() === args.clan.toLowerCase(), + do: () => cliExecute(`/whitelist ${args.clan}`), + }, + { + name: "Breakfast", + completed: () => get("breakfastCompleted"), + do: () => cliExecute("breakfast"), + }, + { + name: "June Cleaver", + ready: () => have($item`June cleaver`) && get("_juneCleaverFightsLeft") === 0, + completed: () => myAdventures() === 0, + choices: { + 1467: 3, //Poetic Justice + 1468: get("_juneCleaverSkips") < 5 ? 4 : 2, //Aunts not Ants + 1469: 3, //Beware of Aligator + 1470: get("_juneCleaverSkips") < 5 ? 4 : 2, //Teacher's Pet + 1471: 1, //Lost and Found + 1472: get("_juneCleaverSkips") < 5 ? 4 : 1, //Summer Days + 1473: get("_juneCleaverSkips") < 5 ? 4 : 1, //Bath Time + 1474: get("_juneCleaverSkips") < 5 ? 4 : 2, //Delicious Sprouts + 1475: 1, //Hypnotic Master + }, + do: $location`Noob Cave`, + post: () => { + if (handlingChoice()) visitUrl("main.php"); + if (have($effect`Beaten Up`)) uneffect($effect`Beaten Up`); + if (get("lastCopyableMonster") === $monster`crate`) cliExecute("kolfix cleaver"); + }, + outfit: () => ({ equip: $items`June cleaver, Greatest American Pants` }), + combat: new CombatStrategy().macro(Macro.runaway()), + limit: undefined, + }, + { + name: "Harvest Garden", + completed: () => + getGarden() === $item`none` || + getGarden() === $item`packet of mushroom spores` || + getCampground()[getGarden().name] === 0, + do: () => cliExecute("garden pick"), + tracking: "Dailies", + limit: { tries: 3 }, + }, + { + name: "Apriling", + ready: () => AprilingBandHelmet.canChangeSong(), + completed: () => have($effect`Apriling Band Celebration Bop`), + do: (): void => { + AprilingBandHelmet.conduct($effect`Apriling Band Celebration Bop`); + }, + limit: { tries: 1 }, + }, + { + name: "SIT Course", + // eslint-disable-next-line libram/verify-constants + ready: () => have($item`S.I.T. Course Completion Certificate`), + completed: () => get("_sitCourseCompleted", false), + choices: { + 1494: 2, + }, + do: () => + // eslint-disable-next-line libram/verify-constants + use($item`S.I.T. Course Completion Certificate`), + }, + { + name: "Drive Observantly", + completed: () => + get("dailyDungeonDone") || + getWorkshed() !== $item`Asdon Martin keyfob (on ring)` || + haveEffect($effect`Driving Observantly`) >= + (totallyDrunk() || !have($item`Drunkula's wineglass`) + ? myAdventures() + : myAdventures() + 60), + do: () => + AsdonMartin.drive( + $effect`Driving Observantly`, + totallyDrunk() || !have($item`Drunkula's wineglass`) + ? myAdventures() + : myAdventures() + 60, + false, + ), + limit: { tries: 5 }, + }, + { + name: "CyberRealm: Prepare Familiar", + ready: () => !get("_familiarPrepped").includes("true"), + completed: () => myFamiliar() === $familiar`Shorter-Order Cook`, + do: () => { + if (have($familiar`Shorter-Order Cook`)) { + cliExecute("familiar Shorter-Order Cook"); + equip($familiar`Shorter-Order Cook`, $item`blue plate`); + } + cliExecute(`familiar ${bestFam().name}`); + cliExecute("familiar Shorter-Order Cook"); + set("_familiarPrepped", true) + }, + }, + { + name: "Restore HP", + completed: () => myHp() > 0.5 * myMaxhp(), + do: () => restoreHp(0.95 * myMaxhp()), + }, + { + name: "Implement Glitch", + ready: () => have($item`[glitch season reward name]`), + completed: () => get("_glitchItemImplemented"), + do: () => use($item`[glitch season reward name]`), + }, + { + name: "Unlock Guild", + ready: () => + myClass() === $class`Seal Clubber` && + Math.min( + ...$items`figurine of a wretched-looking seal, seal-blubber candle`.map((it) => + availableAmount(it), + ), + ) < 20 && + doSmol, + completed: () => guildStoreAvailable() || myAdventures() === 0 || stooperDrunk(), + do: () => cliExecute("guild"), + choices: { + //sleazy back alley + 108: 4, //craps: skip + 109: 1, //drunken hobo: fight + 110: 4, //entertainer: skip + 112: 2, //harold's hammer: skip + 21: 2, //under the knife: skip + //haunted pantry + 115: 1, //drunken hobo: fight + 116: 4, //singing tree: skip + 117: 1, //knob goblin chef: fight + 114: 2, //birthday cake: skip + //outskirts of cobb's knob + 113: 2, //knob goblin chef: fight + 111: 3, //chain gang: fight + 118: 2, //medicine quest: skip + }, + outfit: () => ({ + familiar: bestFam(), + modifier: `${maxBase()}, ${ + myPrimestat() === $stat`Muscle` ? "100 combat rate 20 max" : "-100 combat rate" + }, 250 bonus carnivorous potted plant`, + }), + combat: new CombatStrategy() + .macro( + () => + Macro.step("pickpocket") + .externalIf( + have($skill`Curse of Weaksauce`), + Macro.trySkill($skill`Curse of Weaksauce`), + Macro.tryItem($item`electronics kit`), + ) + .tryItem($item`porquoise-handled sixgun`) + .trySkill($skill`Sing Along`) + .attack() + .repeat(), + getTodaysHolidayWanderers(), + ) + .macro(() => + Macro.step("pickpocket") + .trySkill($skill`Sing Along`) + .tryItem($item`porquoise-handled sixgun`) + .attack() + .repeat(), + ), + }, + { + name: "Stock Up on MMJs", + ready: () => + guildStoreAvailable() && + (myClass().primestat === $stat`Mysticality` || + (myClass() === $class`Accordion Thief` && myLevel() >= 9)), + completed: () => availableAmount($item`magical mystery juice`) >= 500, + acquire: [ + { + item: $item`magical mystery juice`, + num: 500, + }, + ], + do: () => false, + }, + { + name: "Buy Seal Summoning Supplies", + ready: () => myClass() === $class`Seal Clubber` && guildStoreAvailable(), + completed: () => + Math.min( + ...$items`figurine of a wretched-looking seal, seal-blubber candle`.map((it) => + availableAmount(it), + ), + ) >= 40, + acquire: $items`figurine of a wretched-looking seal, seal-blubber candle`.map((it) => ({ + item: it, + num: 500, + })), + do: () => false, + }, + { + name: "Run CyberRealm", + ready: () => mallPrice($item`1`) > 1_000, + prepare: () => { + $effects`Astral Shell, Elemental Saucesphere, Scarysauce`.forEach((ef) => { + if (!have(ef)) useSkill(toSkill(ef)); + }); + }, + completed: () => nextCyberZone() === $location`none`, // $location`Cyberzone 1`.turnsSpent >= 19 * myDaycount(), + choices: { 1545: 1, 1546: 1, 1547: 1, 1548: 1, 1549: 1, 1550: 1 }, + do: () => nextCyberZone(), + outfit: { + hat: $item`Crown of Thrones`, + back: $item`unwrapped knock-off retro superhero cape`, + shirt: $item`zero-trust tanktop`, + weapon: $item`June cleaver`, + offhand: $item`visual packet sniffer`, + pants: $item`digibritches`, + acc1: $item`retro floppy disk`, + acc2: $item`retro floppy disk`, + acc3: $item`retro floppy disk`, + famequip: $item`familiar-in-the-middle wrapper`, + modes: { retrocape: ["vampire", "hold"] }, + riders: { "crown-of-thrones": $familiar`Mini Kiwi` }, + }, + combat: new CombatStrategy().macro(() => + Macro.if_( + "!monsterphylum construct", + Macro.trySkill($skill`Sing Along`) + .trySkill($skill`Micrometeorite`) + .trySkill($skill`Saucestorm`) + .trySkill($skill`Saucestorm`) + .trySkill($skill`Saucestorm`) + .trySkill($skill`Saucestorm`) + .attack() + .repeat(), + ) + .skill($skill`Throw Cyber Rock`) + .repeat(), + ), + limit: { skip: 60 }, + }, + { + name: "Wardrobe-o-matic", + ready: () => myLevel() >= 15 && have($item`wardrobe-o-matic`), + completed: () => get("_wardrobeUsed", false), + do: (): void => { + use($item`wardrobe-o-matic`); + cliExecute("set _wardrobeUsed = true"); + }, + limit: { tries: 1 }, + }, + { + name: "Candy Deviler", + // eslint-disable-next-line libram/verify-constants + ready: () => have($item`candy egg deviler`), + completed: () => toInt(get("_candyEggsDeviled")) >= 3, + do: () => { + visitUrl(`inventory.php?action=eggdevil&pwd`); + visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); + visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); + visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); + }, + }, + ] +} + +let duffo = false; + + +export function preRunQuests(): Task[] { + return [ + { + name: "Whitelist VIP Clan", + completed: () => !args.clan || getClanName().toLowerCase() === args.clan.toLowerCase(), + do: () => cliExecute(`/whitelist ${args.clan}`), + choices: { + 1507: 1, + }, + }, + { + name: "Get Floundry item", + ready: () => have($item`Clan VIP Lounge key`) && !args.carpe, + completed: () => get("_floundryItemCreated"), + do: (): void => { + retrieveItem($item`carpe`); + }, + limit: { tries: 1 }, + }, + { + name: "Unpack Duffel Bag", + completed: () => duffo, + do: () => { + visitUrl("inventory.php?action=skiduffel&pwd"); + duffo = true; + }, + }, + { + name: "LGR Seed", + ready: () => + have($item`lucky gold ring`) && have($item`one-day ticket to Dinseylandfill`) && !args.garboascend.includes("penguin") && !args.cs, + completed: () => get("_stenchAirportToday") || get("stenchAirportAlways"), + do: () => use($item`one-day ticket to Dinseylandfill`), + tracking: "Garbo", + }, + { + name: "Break Stone", + ready: () => !args.safepvp, + completed: () => hippyStoneBroken() || !args.pvp, + do: (): void => { + visitUrl("peevpee.php?action=smashstone&pwd&confirm=on", true); + visitUrl("peevpee.php?place=fight"); + }, + }, + ] +} + +let garboDone = false; + +export function noBarf(): Task[] { + return [ + { + name: "CONSUME ALL", + ready: () => holiday().includes("Halloween") || args.crimbo || args.chrono, + completed: () => + myFullness() >= fullnessLimit() && + mySpleenUse() >= spleenLimit() && + myInebriety() >= inebrietyLimit(), + do: () => cliExecute("consume ALL"), + }, + { + name: "Garbo Nobarf", + ready: () => holiday().includes("Halloween") || args.crimbo || args.chrono, + completed: () => garboDone, + do: (): void => { + cliExecute(`${args.garboascend} nodiet nobarf target="witchess knight"`); + garboDone = true; + }, + }, + ] +} + +export function garboWeen(): Task[] { + return [ + { + name: "Freecandy time", + ready: () => holiday().includes("Halloween"), + completed: () => myAdventures() / 5 < 1, + prepare: () => uneffect($effect`Beaten Up`), + do: (): void => { + if (have($familiar`Trick-or-Treating Tot`)) cliExecute("familiar Trick-or-Treating Tot"); + else if (have($familiar`Red-Nosed Snapper`)) cliExecute("familiar snapper"); + cliExecute(`freecandy ${myAdventures()}`); + }, + clear: "all", + tracking: "Freecandy", + limit: { tries: 1 }, //this will run again after installing CMC, by magic + }, + { + name: "Super Nightcap", + ready: () => have($item`Drunkula's wineglass`) && holiday().includes("Halloween"), + completed: () => totallyDrunk(), + do: () => cliExecute(`CONSUME NIGHTCAP`), + }, + { + name: "Freecandy Drunk", + ready: () => holiday().includes("Halloween"), + completed: () => Math.floor(myAdventures() / 5) === 0, + prepare: () => uneffect($effect`Beaten Up`), + do: (): void => { + useFamiliar($familiar`Red-Nosed Snapper`); + cliExecute(`freeCandy ${myAdventures()}`); + }, + clear: "all", + tracking: "Freecandy", + limit: { tries: 1 }, //this will run again after installing CMC, by magic + }, + ] + } + +export function chrono(): Task[] { + return [ + { + name: "Chrono", + ready: () => args.chrono && canAdventure($location`The Primordial Stew`), + completed: () => myAdventures() === 0 && myInebriety() >= inebrietyLimit(), + prepare: () => uneffect($effect`Beaten Up`), + do: (): void => { + cliExecute(`${args.chronoscript}`); + }, + clear: "all", + tracking: "chrono", + limit: { tries: 1 }, //this will run again after installing CMC, by magic + }, + { + name: "Super Nightcap", + ready: () => have($item`Drunkula's wineglass`) && args.chrono && canAdventure($location`The Primordial Stew`) && myDaycount() > 1, + completed: () => totallyDrunk(), + do: () => cliExecute(`CONSUME NIGHTCAP`), + }, + { + name: "Chrono Drunk", + ready: () => args.chrono && canAdventure($location`The Primordial Stew`) && myDaycount() > 1, + completed: () => myAdventures() === 0, + prepare: () => uneffect($effect`Beaten Up`), + do: (): void => { + cliExecute(`${args.chronoscript}`); + }, + clear: "all", + tracking: "Chrono", + limit: { tries: 1 }, //this will run again after installing CMC, by magic + }, + ] + } + + export function crimbo(): Task[] { + return [ + { + name: "Crimbo Time", + ready: () => args.crimbo, + completed: () => myAdventures() === 0 && myInebriety() >= inebrietyLimit(), + prepare: () => uneffect($effect`Beaten Up`), + do: (): void => { + cliExecute(`${args.crimboscript}`); + }, + clear: "all", + tracking: "Crimbo", + limit: { tries: 1 }, //this will run again after installing CMC, by magic + }, + { + name: "Super Nightcap", + ready: () => have($item`Drunkula's wineglass`) && holiday().includes("Halloween") && myDaycount() > 1, + completed: () => totallyDrunk(), + do: () => cliExecute(`CONSUME NIGHTCAP`), + }, + { + name: "Crimbo Drunk", + ready: () => args.crimbo && myDaycount() > 1, + completed: () => myAdventures() === 0, + prepare: () => uneffect($effect`Beaten Up`), + do: (): void => { + cliExecute(`${args.crimboscript}`); + }, + clear: "all", + tracking: "Crimbo", + limit: { tries: 1 }, //this will run again after installing CMC, by magic + }, + ] + } diff --git a/src/tasks/runleg.ts b/src/tasks/runleg.ts new file mode 100644 index 0000000..2cab46e --- /dev/null +++ b/src/tasks/runleg.ts @@ -0,0 +1,80 @@ +import { $item, $skill, clamp, get, have } from "libram"; +import { getCurrentLeg, Leg, Quest } from "./structure"; +import { args } from "../args"; +import { cliExecute, drink, eat, fullnessLimit, itemAmount, mallPrice, myFullness, myInebriety, storageAmount, useSkill, visitUrl } from "kolmafia"; +import { preRunQuests } from "./repeatableTasks"; +import { step } from "grimoire-kolmafia"; + +const runType = () => args.smol ? args.smolscript : args.cs ? args.csscript : args.casual ? args.casualscript : args.robot ? args.robotscript : args.robotscript; + +const checkMelange = () => + get("valueOfAdventure") * 45 > mallPrice($item`spice melange`) && + !have($item`designer sweatpants`); + +export function howManySausagesCouldIEat() { + if (!have($item`Kramco Sausage-o-Matic™`)) return 0; + // You may be full but you can't be overfull + if (myFullness() > fullnessLimit()) return 0; + + return clamp( + 23 - get("_sausagesEaten"), + 0, + itemAmount($item`magical sausage`) + itemAmount($item`magical sausage casing`), + ); +} + + +export function RunQuests(): Quest { + return { + name: "Ascension Run", + completed: () => getCurrentLeg() !== Leg.Run || get("kingLiberated"), + tasks: [ + ...preRunQuests(), + { + name: "Run", + completed: () => (get("kingLiberated") && args.cs) || step("questL13Final") > 11, + do: () => { + if(runType() === undefined || runType() === null) throw "no runtime defined" + else cliExecute(runType()) + }, + tracking: "Run", + }, + { + name: "drink", + ready: () => + step("questL13Final") > 11 && + (have($item`designer sweatpants`) || checkMelange()) && + have($skill`Drinking to Drink`) && + storageAmount($item`synthetic dog hair pill`) >= 1 && + args.smol, + completed: () => myInebriety() >= 2, + do: (): void => { + if (have($skill`The Ode to Booze`)) useSkill($skill`The Ode to Booze`); + drink($item`astral pilsner`, 1); + }, + clear: "all", + tracking: "Run", + }, + { + name: "Sausages", + ready: () => args.smol, + completed: () => howManySausagesCouldIEat() === 0, + do: (): void => { + eat($item`magical sausage`, howManySausagesCouldIEat()); + }, + clear: "all", + tracking: "Run", + }, + { + name: "Free King", + ready: () => step("questL13Final") > 11 && !(args.cs), + completed: () => get("kingLiberated", false), + do: (): void => { + visitUrl("place.php?whichplace=nstower&action=ns_11_prism"); + }, + clear: "all", + }, + ] + } +} + diff --git a/src/tasks/smolrunleg.ts b/src/tasks/smolrunleg.ts deleted file mode 100644 index 1481c12..0000000 --- a/src/tasks/smolrunleg.ts +++ /dev/null @@ -1,693 +0,0 @@ -import { CombatStrategy, step } from "grimoire-kolmafia"; -import { - buy, - buyUsingStorage, - cliExecute, - drink, - eat, - Effect, - equip, - familiarWeight, - fullnessLimit, - getClanName, - getWorkshed, - hippyStoneBroken, - inebrietyLimit, - itemAmount, - mallPrice, - myAdventures, - myAscensions, - myDaycount, - myFullness, - myInebriety, - myLevel, - myMaxhp, - mySign, - numericModifier, - print, - pullsRemaining, - pvpAttacksLeft, - restoreHp, - restoreMp, - retrieveItem, - setProperty, - storageAmount, - toBoolean, - toInt, - use, - useFamiliar, - useSkill, - visitUrl, - wait, -} from "kolmafia"; -import { - $coinmaster, - $effect, - $effects, - $familiar, - $familiars, - $item, - $items, - $location, - $skill, - AprilingBandHelmet, - clamp, - get, - have, - Macro, - set, - uneffect, -} from "libram"; - -import { args } from "../args"; - -import { getCurrentLeg, Leg, Quest } from "./structure"; -import { - backstageItemsDone, - bestFam, - doneAdventuring, - haveAll, - maxBase, - pvpCloset, - stooperDrunk, - totallyDrunk, -} from "./utils"; - -let pajamas = false; -let smoke = 1; - -const checkMelange = () => - get("valueOfAdventure") * 45 > mallPrice($item`spice melange`) && - !have($item`designer sweatpants`); - -export function howManySausagesCouldIEat() { - if (!have($item`Kramco Sausage-o-Matic™`)) return 0; - // You may be full but you can't be overfull - if (myFullness() > fullnessLimit()) return 0; - - return clamp( - 23 - get("_sausagesEaten"), - 0, - itemAmount($item`magical sausage`) + itemAmount($item`magical sausage casing`), - ); -} - -function firstWorkshed() { - return ( - $items`model train set, Asdon Martin keyfob (on ring), cold medicine cabinet, Little Geneticist DNA-Splicing Lab, portable Mayo Clinic`.find( - (it) => have(it) || getWorkshed() === it || storageAmount(it) > 0, - ) || $item`none` - ); -} -function altWorkshed() { - const ws = getWorkshed(); - switch (ws) { - case $item`model train set`: - return ( - $items`cold medicine cabinet, Asdon Martin keyfob (on ring), Little Geneticist DNA-Splicing Lab, portable Mayo Clinic`.find( - (it) => have(it) || getWorkshed() === it || storageAmount(it) > 0, - ) || ws - ); - case $item`Asdon Martin keyfob (on ring)`: - return ( - $items`cold medicine cabinet, model train set, Little Geneticist DNA-Splicing Lab, portable Mayo Clinic`.find( - (it) => have(it) || getWorkshed() === it || storageAmount(it) > 0, - ) || ws - ); - case $item`cold medicine cabinet`: - return ( - $items`Asdon Martin keyfob (on ring), model train set, Little Geneticist DNA-Splicing Lab, portable Mayo Clinic, warbear induction oven, snow machine`.find( - (it) => have(it) || getWorkshed() === it || storageAmount(it) > 0, - ) || ws - ); - case $item`Little Geneticist DNA-Splicing Lab`: - return ( - $items`cold medicine cabinet, Asdon Martin keyfob (on ring), model train set, portable Mayo Clinic`.find( - (it) => have(it) || getWorkshed() === it || storageAmount(it) > 0, - ) || ws - ); - case $item`portable Mayo Clinic`: - return ( - $items`cold medicine cabinet, model train set, Asdon Martin keyfob (on ring), Little Geneticist DNA-Splicing Lab`.find( - (it) => have(it) || getWorkshed() === it || storageAmount(it) > 0, - ) || ws - ); - default: - return $item`none`; - } -} - -export function SmolQuests(): Quest[] { - return [ - { - name: "Smol Run", - completed: () => getCurrentLeg() !== Leg.Run || get("kingLiberated", false), - tasks: [ - { - name: "Whitelist VIP Clan", - completed: () => !args.clan || getClanName().toLowerCase() === args.clan.toLowerCase(), - do: () => cliExecute(`/whitelist ${args.clan}`), - choices: { - 1507: 1, - }, - }, - { - name: "Prep Fireworks Shop", - completed: () => - !have($item`Clan VIP Lounge key`) || get("_goorboFireworksPrepped", false), - do: () => { - visitUrl("clan_viplounge.php?action=fwshop&whichfloor=2"); - set("_goorboFireworksPrepped", true); - }, - }, - { - name: "Pre-Pulls", - completed: () => - pullsRemaining() === 0 || - !args.pulls.find( - (it) => !have(it) && !get("_roninStoragePulls").includes(toInt(it).toString()), - ), //can't find a pull that (we dont have and it hasn't been pulled today) - do: () => - args.pulls.forEach((it) => { - if (!have(it) && !get("_roninStoragePulls").includes(toInt(it).toString())) { - if (storageAmount(it) === 0) buyUsingStorage(it); //should respect autoBuyPriceLimit - cliExecute(`pull ${it}`); - } - }), - }, - { - name: "LGR Seed", - ready: () => - have($item`lucky gold ring`) && have($item`one-day ticket to Dinseylandfill`), - completed: () => get("_stenchAirportToday") || get("stenchAirportAlways"), - do: () => use($item`one-day ticket to Dinseylandfill`), - tracking: "Garbo", - }, - { - name: "Install First Workshed", - ready: () => have(firstWorkshed()), - completed: () => - firstWorkshed() === $item`none` || - get("_workshedItemUsed") || - getWorkshed() !== $item`none`, - do: () => use(firstWorkshed()), - }, - { - name: "SIT Course", - // eslint-disable-next-line libram/verify-constants - ready: () => have($item`S.I.T. Course Completion Certificate`), - completed: () => get("_sitCourseCompleted", false), - choices: { - 1494: 2, - }, - do: () => - // eslint-disable-next-line libram/verify-constants - use($item`S.I.T. Course Completion Certificate`), - }, - { - name: "Break Stone", - ready: () => !args.safepvp, - completed: () => hippyStoneBroken() || !args.pvp, - do: (): void => { - visitUrl("peevpee.php?action=smashstone&pwd&confirm=on", true); - visitUrl("peevpee.php?place=fight"); - }, - }, - { - name: "Prepare Empathy", - completed: () => get("_empathyReady", false), - do: (): void => { - cliExecute("maximize MP; set _empathyReady = true"); - }, - }, - { - name: "Stillsuit Prep", - completed: () => itemAmount($item`tiny stillsuit`) === 0, - do: () => - equip( - $item`tiny stillsuit`, - get( - "stillsuitFamiliar", - $familiars`Gelatinous Cubeling, Levitating Potato, Mosquito`.find((fam) => - have(fam), - ) || $familiar`none`, - ), - ), - }, - { - name: "Run", - completed: () => step("questL13Final") > 11, - do: () => cliExecute(args.smolscript), - clear: "all", - tracking: "Run", - }, - { - name: "drink", - ready: () => - step("questL13Final") > 11 && - (have($item`designer sweatpants`) || checkMelange()) && - have($skill`Drinking to Drink`) && - storageAmount($item`synthetic dog hair pill`) >= 1, - completed: () => myInebriety() >= 2, - do: (): void => { - if (have($skill`The Ode to Booze`)) useSkill($skill`The Ode to Booze`); - drink($item`astral pilsner`, 1); - }, - clear: "all", - tracking: "Run", - }, - { - name: "Sausages", - completed: () => howManySausagesCouldIEat() === 0, - do: (): void => { - eat($item`magical sausage`, howManySausagesCouldIEat()); - }, - clear: "all", - tracking: "Run", - }, - { - name: "Free King", - ready: () => step("questL13Final") > 11, - completed: () => get("kingLiberated", false), - do: (): void => { - visitUrl("place.php?whichplace=nstower&action=ns_11_prism"); - }, - clear: "all", - tracking: "Ignore", - }, - ], - }, - { - name: "Post-SMOL Aftercore", - ready: () => myDaycount() === 1 && get("kingLiberated", false), - completed: () => totallyDrunk() && pajamas, - tasks: [ - { - name: "Pull All", - completed: () => get("lastEmptiedStorage") === myAscensions(), - do: () => cliExecute("pull all; refresh all"), - }, - { - name: "PvP Closet Safety 1", - ready: () => args.pvp && get("autoSatisfyWithCloset"), - completed: () => toBoolean(get("_safetyCloset1")), - do: () => pvpCloset(1), - }, - { - name: "Unlock Garbage Mountain", - completed: () => get("_stenchAirportToday") || get("stenchAirportAlways"), - do: (): void => { - retrieveItem($item`one-day ticket to Dinseylandfill`); - use($item`one-day ticket to Dinseylandfill`); - }, - tracking: "Garbo", - }, - { - name: "Sober Up", - completed: () => - myInebriety() <= 15 || - get("_mimeArmyShotglassUsed") || - get("_sweatOutSomeBoozeUsed", 0) === 3, - do: (): void => { - if (checkMelange()) { - cliExecute("acquire spice melange; use spice melange"); - } - while (get("_sweatOutSomeBoozeUsed", 0) < 3) { - useSkill($skill`Sweat Out Some Booze`); - } - if (!get("_sobrieTeaUsed", false)) { - retrieveItem($item`cuppa Sobrie tea`); - use($item`cuppa Sobrie tea`); - } - use($item`synthetic dog hair pill`); - }, - }, - { - name: "Wardrobe-o-matic", - ready: () => myLevel() >= 15 && have($item`wardrobe-o-matic`), - completed: () => get("_wardrobeUsed", false), - do: (): void => { - use($item`wardrobe-o-matic`); - cliExecute("set _wardrobeUsed = true"); - }, - limit: { tries: 1 }, - }, - { - name: "Apriling Part 1", - ready: () => AprilingBandHelmet.canChangeSong(), - completed: () => have($effect`Apriling Band Celebration Bop`), - do: (): void => { - AprilingBandHelmet.conduct($effect`Apriling Band Celebration Bop`); - }, - limit: { tries: 1 }, - }, - { - name: "Drink Pre-Tune", - ready: () => - mySign().toLowerCase() === "blender" && - myLevel() >= 7 && - have($item`mime army shotglass`) && - (have($item`astral pilsner`) || have($item`astral six-pack`)), - completed: () => - get("_mimeArmyShotglassUsed") || !have($item`hewn moon-rune spoon`) || get("moonTuned"), - prepare: () => { - if (have($item`astral six-pack`)) use($item`astral six-pack`); - }, - do: () => drink(1, $item`astral pilsner`), - }, - { - name: "Moon Spoon", - completed: () => - !have($item`hewn moon-rune spoon`) || - get("moonTuned") || - mySign().toLowerCase() === "wombat", - do: () => cliExecute("spoon wombat"), - }, - { - name: "Install Alternate Workshed", - ready: () => have(altWorkshed()), - completed: () => - altWorkshed() === $item`none` || - get("_workshedItemUsed") || - getWorkshed() === altWorkshed(), - do: () => use(altWorkshed()), - }, - { - name: "Gold Wedding Ring", - completed: () => - !have($skill`Comprehensive Cartography`) || - myAscensions() === get("lastCartographyBooPeak"), - choices: { 1430: 3, 606: 4, 610: 1, 1056: 1 }, - do: $location`A-Boo Peak`, - outfit: { modifier: "initiative 40 min 40 max, -tie" }, - }, - { - name: "Breakfast", - completed: () => get("breakfastCompleted"), - do: () => cliExecute("breakfast"), - }, - { - name: "Laugh Floor", - completed: () => - have($skill`Liver of Steel`) || - have($item`steel margarita`) || - have($item`Azazel's lollipop`) || - have($item`observational glasses`), - effects: () => [ - ...(have($skill`Musk of the Moose`) ? $effects`Musk of the Moose` : []), - ...(have($skill`Carlweather's Cantata of Confrontation`) - ? $effects`Carlweather's Cantata of Confrontation` - : []), - ], - prepare: (): void => { - if (!have($effect`Carlweather's Cantata of Confrontation`)) { - cliExecute("kmail to Buffy || 10 Cantata of Confrontation"); - wait(15); - cliExecute("refresh effects"); - } - $effects`Smooth Movements, The Sonata of Sneakiness, Darkened Photons, Shifted Phase`.forEach( - (ef: Effect) => cliExecute(`uneffect ${ef}`), - ); - restoreHp(0.75 * myMaxhp()); - restoreMp(20); - }, - do: $location`The Laugh Floor`, - outfit: () => ({ - familiar: bestFam(), - modifier: `${maxBase()}, 100 combat rate, 3 item, 250 bonus carnivorous potted plant`, - }), - combat: new CombatStrategy().macro( - Macro.trySkill($skill`Curse of Weaksauce`) - .tryItem($item`train whistle`) - .tryItem($item`porquoise-handled sixgun`) - .attack() - .repeat(), - ), - limit: { tries: 15 }, - }, - { - name: "Infernal Rackets Backstage", - completed: () => - have($skill`Liver of Steel`) || - have($item`steel margarita`) || - have($item`Azazel's unicorn`) || - backstageItemsDone(), - effects: () => [ - ...(have($skill`Smooth Movement`) ? $effects`Smooth Movements` : []), - ...(have($skill`The Sonata of Sneakiness`) ? $effects`The Sonata of Sneakiness` : []), - ], - prepare: (): void => { - if (!have($effect`The Sonata of Sneakiness`)) { - cliExecute("kmail to Buffy || 10 Sonata of Sneakiness"); - wait(15); - cliExecute("refresh effects"); - } - $effects`Musk of the Moose, Carlweather's Cantata of Confrontation, Hooooooooonk!`.forEach( - (ef: Effect) => cliExecute(`uneffect ${ef}`), - ); - restoreHp(0.75 * myMaxhp()); - restoreMp(20); - }, - do: $location`Infernal Rackets Backstage`, - outfit: () => ({ - familiar: bestFam(), - modifier: `${maxBase()}, -100 combat rate, 3 item, 250 bonus carnivorous potted plant`, - }), - combat: new CombatStrategy().macro( - Macro.trySkill($skill`Curse of Weaksauce`) - .tryItem($item`train whistle`) - .tryItem($item`porquoise-handled sixgun`) - .attack() - .repeat(), - ), - limit: { tries: 15 }, - }, - { - name: "Mourn", - ready: () => have($item`observational glasses`), - completed: () => - have($skill`Liver of Steel`) || - have($item`steel margarita`) || - have($item`Azazel's lollipop`), - outfit: { - equip: $items`hilarious comedy prop, observational glasses, Victor\, the Insult Comic Hellhound Puppet`, - }, - do: () => cliExecute("panda comedy insult; panda comedy observe"), - }, - { - name: "Sven Golly", - ready: () => backstageItemsDone(), - completed: () => - have($skill`Liver of Steel`) || - have($item`steel margarita`) || - have($item`Azazel's unicorn`), - do: (): void => { - cliExecute( - `panda arena Bognort ${$items`giant marshmallow, gin-soaked blotter paper`.find((a) => - have(a), - )}`, - ); - cliExecute( - `panda arena Stinkface ${$items`beer-scented teddy bear, gin-soaked blotter paper`.find( - (a) => have(a), - )}`, - ); - cliExecute( - `panda arena Flargwurm ${$items`booze-soaked cherry, sponge cake`.find((a) => - have(a), - )}`, - ); - cliExecute(`panda arena Jim ${$items`comfy pillow, sponge cake`.find((a) => have(a))}`); - }, - }, - { - name: "Moaning Panda", - ready: () => haveAll($items`Azazel's lollipop, Azazel's unicorn`), - completed: () => - have($skill`Liver of Steel`) || - have($item`steel margarita`) || - have($item`Azazel's tutu`), - acquire: () => - $items`bus pass, imp air`.map((it) => ({ - item: it, - num: 5, - price: get("valueOfAdventure"), - })), - do: () => cliExecute("panda moan"), - limit: { tries: 3 }, - }, - { - name: "Steel Margarita", - ready: () => haveAll($items`Azazel's tutu, Azazel's lollipop, Azazel's unicorn`), - completed: () => have($skill`Liver of Steel`) || have($item`steel margarita`), - do: () => cliExecute("panda temple"), - }, - { - name: "Liver of Steel", - ready: () => have($item`steel margarita`), - completed: () => have($skill`Liver of Steel`), - do: () => drink(1, $item`steel margarita`), - }, - { - name: "PvP Closet Safety 2", - ready: () => args.pvp && get("autoSatisfyWithCloset"), - completed: () => toBoolean(get("_safetyCloset2")), - do: () => pvpCloset(2), - }, - { - name: "Garbo", - ready: () => get("_stenchAirportToday") || get("stenchAirportAlways"), - completed: () => myAdventures() === 0 || stooperDrunk(), - prepare: () => uneffect($effect`Beaten Up`), - do: () => cliExecute(`${args.garbo}`), - post: () => - $effects`Power Ballad of the Arrowsmith, Stevedave's Shanty of Superiority, The Moxious Madrigal, The Magical Mojomuscular Melody, Aloysius' Antiphon of Aptitude, Ur-Kel's Aria of Annoyance` - .filter((ef) => have(ef)) - .forEach((ef) => uneffect(ef)), - clear: "all", - tracking: "Garbo", - }, - { - name: "Turn in FunFunds", - ready: () => get("_stenchAirportToday") && itemAmount($item`FunFunds™`) >= 20, - completed: () => have($item`one-day ticket to Dinseylandfill`), - do: () => - buy($coinmaster`The Dinsey Company Store`, 1, $item`one-day ticket to Dinseylandfill`), - tracking: "Garbo", - }, - { - name: "PvP", - ready: () => doneAdventuring() && !args.safepvp, - completed: () => pvpAttacksLeft() === 0 || !hippyStoneBroken(), - do: (): void => { - cliExecute("unequip"); - cliExecute("UberPvPOptimizer"); - cliExecute(`PVP_MAB target=${args.pvpTarget}`); - }, - }, - { - name: "Stooper", - ready: () => - myInebriety() === inebrietyLimit() && - have($item`tiny stillsuit`) && - get("familiarSweat") >= 300, - completed: () => !have($familiar`Stooper`) || stooperDrunk(), - do: () => { - useFamiliar($familiar`Stooper`); - cliExecute("drink stillsuit distillate"); - }, - }, - { - name: "Nightcap", - ready: () => doneAdventuring(), - completed: () => totallyDrunk(), - do: () => cliExecute("CONSUME NIGHTCAP"), - }, - { - name: "Smoke em if you got em", - ready: () => get("getawayCampsiteUnlocked"), - completed: () => !have($item`stick of firewood`), - do: (): void => { - while (have($item`stick of firewood`)) { - setProperty( - "choiceAdventure1394", - `1&message=${smoke} Thanks Seraphiii for writing Candywrapper!`, - ); - use(1, $item`campfire smoke`); - print(`Smoked ${smoke} firewoods!`); - smoke = smoke + 1; - } - }, - }, - { - name: "Offhand Remarkable", - ready: () => have($item`august scepter`), - completed: () => - !have($skill`Aug. 13th: Left/Off Hander's Day!`) || - have($effect`Offhand Remarkable`) || - get("_aug13Cast", false), - do: () => useSkill($skill`Aug. 13th: Left/Off Hander's Day!`), - }, - { - name: "PvP Closet Safety 3", - ready: () => args.pvp && get("autoSatisfyWithCloset"), - completed: () => toBoolean(get("_safetyCloset3")), - do: () => pvpCloset(3), - }, - { - name: "Item Cleanup", - // eslint-disable-next-line libram/verify-constants - completed: () => get("_cleanupToday", false) || args.itemcleanup === "", - do: (): void => { - cliExecute(`${args.itemcleanup}`); - cliExecute("set _cleanupToday = true"); - }, - clear: "all", - tracking: "Item Cleanup", - }, - { - name: "Apriling Part 2", - ready: () => AprilingBandHelmet.canJoinSection(), - completed: () => !AprilingBandHelmet.canPlay($item`Apriling band piccolo`), - do: (): void => { - AprilingBandHelmet.joinSection($item`Apriling band piccolo`); - if (AprilingBandHelmet.canJoinSection()) { - AprilingBandHelmet.joinSection($item`Apriling band saxophone`); - AprilingBandHelmet.play($item`Apriling band saxophone`); - } - if (have($familiar`Grey Goose`)) useFamiliar($familiar`Grey Goose`); - else if (have($familiar`Chest Mimic`)) useFamiliar($familiar`Chest Mimic`); - else if ( - have($familiar`Pocket Professor`) && - familiarWeight($familiar`Pocket Professor`) < 20 - ) - useFamiliar($familiar`Pocket Professor`); - else if (have($familiar`Comma Chameleon`)) useFamiliar($familiar`Comma Chameleon`); - while ( - $item`Apriling band piccolo`.dailyusesleft > 0 && - have($item`Apriling band piccolo`) - ) - AprilingBandHelmet.play($item`Apriling band piccolo`); - }, - limit: { tries: 1 }, - }, - { - name: "Pajamas", - completed: () => have($item`burning cape`), - acquire: [ - { item: $item`clockwork maid`, price: 7 * get("valueOfAdventure"), optional: true }, - { item: $item`burning cape` }, - ], - do: (): void => { - if (have($item`clockwork maid`)) { - use($item`clockwork maid`); - } - pajamas = true; - }, - outfit: () => ({ - familiar: - $familiars`Trick-or-Treating Tot, Left-Hand Man, Disembodied Hand, Grey Goose`.find( - (fam) => have(fam), - ), - modifier: `adventures${args.pvp ? ", 0.3 fites" : ""}`, - }), - }, - { - name: "Alert-No Nightcap", - ready: () => !doneAdventuring(), - completed: () => stooperDrunk(), - do: (): void => { - const targetAdvs = 100 - numericModifier("adventures"); - print("smol completed, but did not overdrink.", "red"); - if (targetAdvs < myAdventures() && targetAdvs > 0) - print( - `Rerun with fewer than ${targetAdvs} adventures for smol to handle your diet`, - "red", - ); - else print("Something went wrong.", "red"); - }, - }, - ], - }, - ]; -} diff --git a/src/tasks/utils.ts b/src/tasks/utils.ts index 970e784..f568141 100644 --- a/src/tasks/utils.ts +++ b/src/tasks/utils.ts @@ -12,6 +12,7 @@ import { inebrietyLimit, Item, itemAmount, + lastChoice, Location, mallPrice, Monster, @@ -39,6 +40,7 @@ import { $items, $location, $phylum, + Kmail as _Kmail, gameDay, get, getBanishedMonsters, @@ -155,13 +157,25 @@ export function bestFam(mob?: Monster) { } export function nextCyberZone(): Location { - return $location`Cyberzone 1`.turnsSpent >= 19 * myDaycount() + + if(lastChoice() === 1546) { + set("_cyberRealm1Done", true) + } + if(lastChoice() === 1548) { + set("_cyberRealm2Done", true) + } + if(lastChoice() === 1550) { + set("_cyberRealm3Done", true) + } + const realmChoice = () => !(get("_cyberRealm1Done").includes("true")) ? $location`Cyberzone 1` - : $location`Cyberzone 2`.turnsSpent >= 19 * myDaycount() + : !get("_cyberRealm2Done").includes("true") ? $location`Cyberzone 2` - : $location`Cyberzone 3`.turnsSpent >= 19 * myDaycount() + : !get("_cyberRealm3Done").includes("true") ? $location`Cyberzone 3` : $location`none`; + print(`Choosing ${realmChoice()} because turns spent ${realmChoice().turnsSpent - 19 * (myDaycount() - 1)}`); + return realmChoice(); } export function canDiet(): boolean { @@ -251,6 +265,27 @@ export interface Kmail { delete(): boolean; } +export function notifyVoters(): void { + if (get("_kmailSentToday").includes("true")) return; + + const recipients = [ + "Datris", + "ange1ade", + "miroto1998", + "tissen", + "nannachi", + "Mandoline", + ]; + + const message = `Voter Monster Today is ${get("_voteMonster")}`; + + recipients.forEach((recipient) => { + _Kmail.send(recipient, message); + }); + + set("_kmailSentToday", true); +} + export function getKmails(caller: string = "GreyDay"): Kmail[] { const buffer = visitUrl(`api.php?pwd&what=kmail&count=100&for=${urlEncode(caller)}`); diff --git a/yarn.lock b/yarn.lock index c9394db..de9563b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3162,10 +3162,10 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -libram@^0.9.29: - version "0.9.29" - resolved "https://registry.yarnpkg.com/libram/-/libram-0.9.29.tgz#d81695cb7e9d9c463a0b5cfa7072ec2ff8375cb3" - integrity sha512-LVehF8VtyRhnnti2WW38FSbC6t26q4MXIw/tuth8tdcNXaRzC07Gif20cZMx+y0X76G4x2/LnIBhU0eKivnPSw== +libram@^0.9.31: + version "0.9.31" + resolved "https://registry.yarnpkg.com/libram/-/libram-0.9.31.tgz#49b368b285670b7512cd95be7fad8c016543ef90" + integrity sha512-ubtAkQnDctHZS1Q8cE5cHEk0Br+q3yiEIP18yXDqxsdQXWM4UI8ryMjG/VBaQSCRj+94Q6YWiT88/yzu4bUL1Q== dependencies: html-entities "^2.5.2" From 5e94f4825e24d0bc0887ee37635ca93476e4eb06 Mon Sep 17 00:00:00 2001 From: Ignose Date: Tue, 14 Jan 2025 10:12:38 -0500 Subject: [PATCH 07/24] Cleaver choice handling --- src/engine/engine.ts | 39 ++++++++++++++++++++++++++++++++++-- src/tasks/repeatableTasks.ts | 29 ++------------------------- 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/src/engine/engine.ts b/src/engine/engine.ts index 50f861c..c4d121e 100644 --- a/src/engine/engine.ts +++ b/src/engine/engine.ts @@ -1,6 +1,6 @@ import { CombatResources, CombatStrategy, Engine } from "grimoire-kolmafia"; -import { cliExecute, Location, logprint, setAutoAttack, writeCcs } from "kolmafia"; -import { clearMaximizerCache } from "libram"; +import { cliExecute, equippedAmount, Location, logprint, setAutoAttack, writeCcs } from "kolmafia"; +import { $item, clearMaximizerCache, get, JuneCleaver, PropertiesManager } from "libram"; import { getCurrentLeg, Task } from "../tasks/structure"; import { printProfits, ProfitTracker } from "./profits"; @@ -14,6 +14,20 @@ export class ProfitTrackingEngine extends Engine { this.profits = new ProfitTracker(key); } + setChoices(task: Task, manager: PropertiesManager): void { + super.setChoices(task, manager); + if (equippedAmount($item`June cleaver`) > 0) { + this.propertyManager.setChoices( + Object.fromEntries( + JuneCleaver.choices.map((choice) => [ + choice, + shouldSkip(choice) ? 4 : bestJuneCleaverOption(choice), + ]), + ), + ); + } + } + setCombat( task: Task, task_combat: CombatStrategy, @@ -80,3 +94,24 @@ export class ProfitTrackingEngine extends Engine { printProfits(this.profits.all()); } } +function shouldSkip(choice: number): boolean { + const skip = [1468, 1470, 1472, 1473, 1474] + return skip.includes(choice) && get("_juneCleaverSkips") < 5; +} + +function bestJuneCleaverOption(choice: number): number { + const choiceTable: { [key: number]: number } = { + 1467: 3, // Poetic Justice + 1468: 2, // Aunts not Ants + 1469: 3, // Beware of Alligator + 1470: 2, // Teacher's Pet + 1471: 1, // Lost and Found + 1472: 1, // Summer Days + 1473: 1, // Bath Time + 1474: 2, // Delicious Sprouts + 1475: 1, // Hypnotic Master + }; + + return choiceTable[choice] ?? 0; +} + diff --git a/src/tasks/repeatableTasks.ts b/src/tasks/repeatableTasks.ts index a38a761..0be7b55 100644 --- a/src/tasks/repeatableTasks.ts +++ b/src/tasks/repeatableTasks.ts @@ -1,6 +1,6 @@ import { CombatStrategy } from "grimoire-kolmafia"; -import { availableAmount, canAdventure, cliExecute, equip, Familiar, fullnessLimit, getCampground, getClanName, getWorkshed, guildStoreAvailable, handlingChoice, haveEffect, hippyStoneBroken, holiday, inebrietyLimit, mallPrice, myAdventures, myClass, myDaycount, myFamiliar, myFullness, myHp, myInebriety, myLevel, myMaxhp, myPrimestat, mySpleenUse, restoreHp, retrieveItem, spleenLimit, toInt, toSkill, use, useFamiliar, useSkill, visitUrl } from "kolmafia"; -import { $class, $effect, $effects, $familiar, $item, $items, $location, $monster, $skill, $stat, AprilingBandHelmet, AsdonMartin, get, getTodaysHolidayWanderers, have, Macro, set, uneffect } from "libram"; +import { availableAmount, canAdventure, cliExecute, equip, Familiar, fullnessLimit, getCampground, getClanName, getWorkshed, guildStoreAvailable, haveEffect, hippyStoneBroken, holiday, inebrietyLimit, mallPrice, myAdventures, myClass, myDaycount, myFamiliar, myFullness, myHp, myInebriety, myLevel, myMaxhp, myPrimestat, mySpleenUse, restoreHp, retrieveItem, spleenLimit, toInt, toSkill, use, useFamiliar, useSkill, visitUrl } from "kolmafia"; +import { $class, $effect, $effects, $familiar, $item, $items, $location, $skill, $stat, AprilingBandHelmet, AsdonMartin, get, getTodaysHolidayWanderers, have, Macro, set, uneffect } from "libram"; import { Task } from "./structure"; import { getGarden, maxBase, nextCyberZone, stooperDrunk, totallyDrunk } from "./utils"; import { args } from "../args"; @@ -32,31 +32,6 @@ return [ completed: () => get("breakfastCompleted"), do: () => cliExecute("breakfast"), }, - { - name: "June Cleaver", - ready: () => have($item`June cleaver`) && get("_juneCleaverFightsLeft") === 0, - completed: () => myAdventures() === 0, - choices: { - 1467: 3, //Poetic Justice - 1468: get("_juneCleaverSkips") < 5 ? 4 : 2, //Aunts not Ants - 1469: 3, //Beware of Aligator - 1470: get("_juneCleaverSkips") < 5 ? 4 : 2, //Teacher's Pet - 1471: 1, //Lost and Found - 1472: get("_juneCleaverSkips") < 5 ? 4 : 1, //Summer Days - 1473: get("_juneCleaverSkips") < 5 ? 4 : 1, //Bath Time - 1474: get("_juneCleaverSkips") < 5 ? 4 : 2, //Delicious Sprouts - 1475: 1, //Hypnotic Master - }, - do: $location`Noob Cave`, - post: () => { - if (handlingChoice()) visitUrl("main.php"); - if (have($effect`Beaten Up`)) uneffect($effect`Beaten Up`); - if (get("lastCopyableMonster") === $monster`crate`) cliExecute("kolfix cleaver"); - }, - outfit: () => ({ equip: $items`June cleaver, Greatest American Pants` }), - combat: new CombatStrategy().macro(Macro.runaway()), - limit: undefined, - }, { name: "Harvest Garden", completed: () => From a54a0d8a8786ba919de77040f05bcc9e69fb415d Mon Sep 17 00:00:00 2001 From: Ignose Date: Tue, 14 Jan 2025 13:13:39 -0500 Subject: [PATCH 08/24] Rename and reorganize --- src/main.ts | 8 +- .../{aftercoreleg.ts => 1 aftercoreleg.ts} | 0 src/tasks/{ascend.ts => 2 ascend.ts} | 0 src/tasks/{runleg.ts => 3 runleg.ts} | 0 src/tasks/{postrunleg.ts => 4 postrunleg.ts} | 0 src/tasks/repeatableTasks.ts | 624 +++++++++--------- 6 files changed, 316 insertions(+), 316 deletions(-) rename src/tasks/{aftercoreleg.ts => 1 aftercoreleg.ts} (100%) rename src/tasks/{ascend.ts => 2 ascend.ts} (100%) rename src/tasks/{runleg.ts => 3 runleg.ts} (100%) rename src/tasks/{postrunleg.ts => 4 postrunleg.ts} (100%) diff --git a/src/main.ts b/src/main.ts index 7d3e2ce..8fb6939 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,10 +4,10 @@ import { gamedayToInt, print } from "kolmafia"; import { args } from "./args"; import { ProfitTrackingEngine } from "./engine/engine"; import { deleteJunkKmails, halloween, notifyVoters, realDay, realMonth } from "./tasks/utils"; -import { AftercoreQuest } from "./tasks/aftercoreleg"; -import { AscendQuest } from "./tasks/ascend"; -import { PostRunQuests } from "./tasks/postrunleg"; -import { RunQuests } from "./tasks/runleg"; +import { AftercoreQuest } from "./tasks/1 aftercoreleg"; +import { AscendQuest } from "./tasks/2 ascend"; +import { PostRunQuests } from "./tasks/4 postrunleg"; +import { RunQuests } from "./tasks/3 runleg"; const version = "0.0.3"; diff --git a/src/tasks/aftercoreleg.ts b/src/tasks/1 aftercoreleg.ts similarity index 100% rename from src/tasks/aftercoreleg.ts rename to src/tasks/1 aftercoreleg.ts diff --git a/src/tasks/ascend.ts b/src/tasks/2 ascend.ts similarity index 100% rename from src/tasks/ascend.ts rename to src/tasks/2 ascend.ts diff --git a/src/tasks/runleg.ts b/src/tasks/3 runleg.ts similarity index 100% rename from src/tasks/runleg.ts rename to src/tasks/3 runleg.ts diff --git a/src/tasks/postrunleg.ts b/src/tasks/4 postrunleg.ts similarity index 100% rename from src/tasks/postrunleg.ts rename to src/tasks/4 postrunleg.ts diff --git a/src/tasks/repeatableTasks.ts b/src/tasks/repeatableTasks.ts index 0be7b55..5de408b 100644 --- a/src/tasks/repeatableTasks.ts +++ b/src/tasks/repeatableTasks.ts @@ -22,244 +22,244 @@ const doSmol = args.smol ? true : false; export function postRunQuests(): Task[] { return [ - { - name: "Whitelist VIP Clan", - completed: () => !args.clan || getClanName().toLowerCase() === args.clan.toLowerCase(), - do: () => cliExecute(`/whitelist ${args.clan}`), - }, - { - name: "Breakfast", - completed: () => get("breakfastCompleted"), - do: () => cliExecute("breakfast"), - }, - { - name: "Harvest Garden", - completed: () => - getGarden() === $item`none` || - getGarden() === $item`packet of mushroom spores` || - getCampground()[getGarden().name] === 0, - do: () => cliExecute("garden pick"), - tracking: "Dailies", - limit: { tries: 3 }, - }, - { - name: "Apriling", - ready: () => AprilingBandHelmet.canChangeSong(), - completed: () => have($effect`Apriling Band Celebration Bop`), - do: (): void => { - AprilingBandHelmet.conduct($effect`Apriling Band Celebration Bop`); - }, - limit: { tries: 1 }, - }, - { - name: "SIT Course", - // eslint-disable-next-line libram/verify-constants - ready: () => have($item`S.I.T. Course Completion Certificate`), - completed: () => get("_sitCourseCompleted", false), - choices: { - 1494: 2, - }, - do: () => - // eslint-disable-next-line libram/verify-constants - use($item`S.I.T. Course Completion Certificate`), - }, - { - name: "Drive Observantly", - completed: () => - get("dailyDungeonDone") || - getWorkshed() !== $item`Asdon Martin keyfob (on ring)` || - haveEffect($effect`Driving Observantly`) >= - (totallyDrunk() || !have($item`Drunkula's wineglass`) - ? myAdventures() - : myAdventures() + 60), - do: () => - AsdonMartin.drive( - $effect`Driving Observantly`, - totallyDrunk() || !have($item`Drunkula's wineglass`) - ? myAdventures() - : myAdventures() + 60, - false, - ), - limit: { tries: 5 }, - }, - { - name: "CyberRealm: Prepare Familiar", - ready: () => !get("_familiarPrepped").includes("true"), - completed: () => myFamiliar() === $familiar`Shorter-Order Cook`, - do: () => { - if (have($familiar`Shorter-Order Cook`)) { - cliExecute("familiar Shorter-Order Cook"); - equip($familiar`Shorter-Order Cook`, $item`blue plate`); - } - cliExecute(`familiar ${bestFam().name}`); - cliExecute("familiar Shorter-Order Cook"); - set("_familiarPrepped", true) - }, - }, - { - name: "Restore HP", - completed: () => myHp() > 0.5 * myMaxhp(), - do: () => restoreHp(0.95 * myMaxhp()), - }, - { - name: "Implement Glitch", - ready: () => have($item`[glitch season reward name]`), - completed: () => get("_glitchItemImplemented"), - do: () => use($item`[glitch season reward name]`), - }, - { - name: "Unlock Guild", - ready: () => - myClass() === $class`Seal Clubber` && - Math.min( - ...$items`figurine of a wretched-looking seal, seal-blubber candle`.map((it) => - availableAmount(it), - ), - ) < 20 && - doSmol, - completed: () => guildStoreAvailable() || myAdventures() === 0 || stooperDrunk(), - do: () => cliExecute("guild"), - choices: { - //sleazy back alley - 108: 4, //craps: skip - 109: 1, //drunken hobo: fight - 110: 4, //entertainer: skip - 112: 2, //harold's hammer: skip - 21: 2, //under the knife: skip - //haunted pantry - 115: 1, //drunken hobo: fight - 116: 4, //singing tree: skip - 117: 1, //knob goblin chef: fight - 114: 2, //birthday cake: skip - //outskirts of cobb's knob - 113: 2, //knob goblin chef: fight - 111: 3, //chain gang: fight - 118: 2, //medicine quest: skip - }, - outfit: () => ({ - familiar: bestFam(), - modifier: `${maxBase()}, ${ - myPrimestat() === $stat`Muscle` ? "100 combat rate 20 max" : "-100 combat rate" - }, 250 bonus carnivorous potted plant`, - }), - combat: new CombatStrategy() - .macro( - () => - Macro.step("pickpocket") - .externalIf( - have($skill`Curse of Weaksauce`), - Macro.trySkill($skill`Curse of Weaksauce`), - Macro.tryItem($item`electronics kit`), - ) - .tryItem($item`porquoise-handled sixgun`) - .trySkill($skill`Sing Along`) - .attack() - .repeat(), - getTodaysHolidayWanderers(), - ) - .macro(() => - Macro.step("pickpocket") - .trySkill($skill`Sing Along`) - .tryItem($item`porquoise-handled sixgun`) - .attack() - .repeat(), - ), - }, - { - name: "Stock Up on MMJs", - ready: () => - guildStoreAvailable() && - (myClass().primestat === $stat`Mysticality` || - (myClass() === $class`Accordion Thief` && myLevel() >= 9)), - completed: () => availableAmount($item`magical mystery juice`) >= 500, - acquire: [ - { - item: $item`magical mystery juice`, - num: 500, - }, - ], - do: () => false, - }, - { - name: "Buy Seal Summoning Supplies", - ready: () => myClass() === $class`Seal Clubber` && guildStoreAvailable(), - completed: () => - Math.min( - ...$items`figurine of a wretched-looking seal, seal-blubber candle`.map((it) => - availableAmount(it), - ), - ) >= 40, - acquire: $items`figurine of a wretched-looking seal, seal-blubber candle`.map((it) => ({ - item: it, - num: 500, - })), - do: () => false, - }, - { - name: "Run CyberRealm", - ready: () => mallPrice($item`1`) > 1_000, - prepare: () => { - $effects`Astral Shell, Elemental Saucesphere, Scarysauce`.forEach((ef) => { - if (!have(ef)) useSkill(toSkill(ef)); - }); - }, - completed: () => nextCyberZone() === $location`none`, // $location`Cyberzone 1`.turnsSpent >= 19 * myDaycount(), - choices: { 1545: 1, 1546: 1, 1547: 1, 1548: 1, 1549: 1, 1550: 1 }, - do: () => nextCyberZone(), - outfit: { - hat: $item`Crown of Thrones`, - back: $item`unwrapped knock-off retro superhero cape`, - shirt: $item`zero-trust tanktop`, - weapon: $item`June cleaver`, - offhand: $item`visual packet sniffer`, - pants: $item`digibritches`, - acc1: $item`retro floppy disk`, - acc2: $item`retro floppy disk`, - acc3: $item`retro floppy disk`, - famequip: $item`familiar-in-the-middle wrapper`, - modes: { retrocape: ["vampire", "hold"] }, - riders: { "crown-of-thrones": $familiar`Mini Kiwi` }, - }, - combat: new CombatStrategy().macro(() => - Macro.if_( - "!monsterphylum construct", - Macro.trySkill($skill`Sing Along`) - .trySkill($skill`Micrometeorite`) - .trySkill($skill`Saucestorm`) - .trySkill($skill`Saucestorm`) - .trySkill($skill`Saucestorm`) - .trySkill($skill`Saucestorm`) - .attack() - .repeat(), - ) - .skill($skill`Throw Cyber Rock`) - .repeat(), + { + name: "Whitelist VIP Clan", + completed: () => !args.clan || getClanName().toLowerCase() === args.clan.toLowerCase(), + do: () => cliExecute(`/whitelist ${args.clan}`), + }, + { + name: "Breakfast", + completed: () => get("breakfastCompleted"), + do: () => cliExecute("breakfast"), + }, + { + name: "Harvest Garden", + completed: () => + getGarden() === $item`none` || + getGarden() === $item`packet of mushroom spores` || + getCampground()[getGarden().name] === 0, + do: () => cliExecute("garden pick"), + tracking: "Dailies", + limit: { tries: 3 }, + }, + { + name: "Apriling", + ready: () => AprilingBandHelmet.canChangeSong(), + completed: () => have($effect`Apriling Band Celebration Bop`), + do: (): void => { + AprilingBandHelmet.conduct($effect`Apriling Band Celebration Bop`); + }, + limit: { tries: 1 }, + }, + { + name: "SIT Course", + // eslint-disable-next-line libram/verify-constants + ready: () => have($item`S.I.T. Course Completion Certificate`), + completed: () => get("_sitCourseCompleted", false), + choices: { + 1494: 2, + }, + do: () => + // eslint-disable-next-line libram/verify-constants + use($item`S.I.T. Course Completion Certificate`), + }, + { + name: "Drive Observantly", + completed: () => + get("dailyDungeonDone") || + getWorkshed() !== $item`Asdon Martin keyfob (on ring)` || + haveEffect($effect`Driving Observantly`) >= + (totallyDrunk() || !have($item`Drunkula's wineglass`) + ? myAdventures() + : myAdventures() + 60), + do: () => + AsdonMartin.drive( + $effect`Driving Observantly`, + totallyDrunk() || !have($item`Drunkula's wineglass`) + ? myAdventures() + : myAdventures() + 60, + false, + ), + limit: { tries: 5 }, + }, + { + name: "CyberRealm: Prepare Familiar", + ready: () => !get("_familiarPrepped").includes("true"), + completed: () => myFamiliar() === $familiar`Shorter-Order Cook`, + do: () => { + if (have($familiar`Shorter-Order Cook`)) { + cliExecute("familiar Shorter-Order Cook"); + equip($familiar`Shorter-Order Cook`, $item`blue plate`); + } + cliExecute(`familiar ${bestFam().name}`); + cliExecute("familiar Shorter-Order Cook"); + set("_familiarPrepped", true) + }, + }, + { + name: "Restore HP", + completed: () => myHp() > 0.5 * myMaxhp(), + do: () => restoreHp(0.95 * myMaxhp()), + }, + { + name: "Implement Glitch", + ready: () => have($item`[glitch season reward name]`), + completed: () => get("_glitchItemImplemented"), + do: () => use($item`[glitch season reward name]`), + }, + { + name: "Unlock Guild", + ready: () => + myClass() === $class`Seal Clubber` && + Math.min( + ...$items`figurine of a wretched-looking seal, seal-blubber candle`.map((it) => + availableAmount(it), ), - limit: { skip: 60 }, - }, - { - name: "Wardrobe-o-matic", - ready: () => myLevel() >= 15 && have($item`wardrobe-o-matic`), - completed: () => get("_wardrobeUsed", false), - do: (): void => { - use($item`wardrobe-o-matic`); - cliExecute("set _wardrobeUsed = true"); - }, - limit: { tries: 1 }, - }, + ) < 20 && + doSmol, + completed: () => guildStoreAvailable() || myAdventures() === 0 || stooperDrunk(), + do: () => cliExecute("guild"), + choices: { + //sleazy back alley + 108: 4, //craps: skip + 109: 1, //drunken hobo: fight + 110: 4, //entertainer: skip + 112: 2, //harold's hammer: skip + 21: 2, //under the knife: skip + //haunted pantry + 115: 1, //drunken hobo: fight + 116: 4, //singing tree: skip + 117: 1, //knob goblin chef: fight + 114: 2, //birthday cake: skip + //outskirts of cobb's knob + 113: 2, //knob goblin chef: fight + 111: 3, //chain gang: fight + 118: 2, //medicine quest: skip + }, + outfit: () => ({ + familiar: bestFam(), + modifier: `${maxBase()}, ${ + myPrimestat() === $stat`Muscle` ? "100 combat rate 20 max" : "-100 combat rate" + }, 250 bonus carnivorous potted plant`, + }), + combat: new CombatStrategy() + .macro( + () => + Macro.step("pickpocket") + .externalIf( + have($skill`Curse of Weaksauce`), + Macro.trySkill($skill`Curse of Weaksauce`), + Macro.tryItem($item`electronics kit`), + ) + .tryItem($item`porquoise-handled sixgun`) + .trySkill($skill`Sing Along`) + .attack() + .repeat(), + getTodaysHolidayWanderers(), + ) + .macro(() => + Macro.step("pickpocket") + .trySkill($skill`Sing Along`) + .tryItem($item`porquoise-handled sixgun`) + .attack() + .repeat(), + ), + }, + { + name: "Stock Up on MMJs", + ready: () => + guildStoreAvailable() && + (myClass().primestat === $stat`Mysticality` || + (myClass() === $class`Accordion Thief` && myLevel() >= 9)), + completed: () => availableAmount($item`magical mystery juice`) >= 500, + acquire: [ { - name: "Candy Deviler", - // eslint-disable-next-line libram/verify-constants - ready: () => have($item`candy egg deviler`), - completed: () => toInt(get("_candyEggsDeviled")) >= 3, - do: () => { - visitUrl(`inventory.php?action=eggdevil&pwd`); - visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); - visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); - visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); - }, + item: $item`magical mystery juice`, + num: 500, }, - ] + ], + do: () => false, + }, + { + name: "Buy Seal Summoning Supplies", + ready: () => myClass() === $class`Seal Clubber` && guildStoreAvailable(), + completed: () => + Math.min( + ...$items`figurine of a wretched-looking seal, seal-blubber candle`.map((it) => + availableAmount(it), + ), + ) >= 40, + acquire: $items`figurine of a wretched-looking seal, seal-blubber candle`.map((it) => ({ + item: it, + num: 500, + })), + do: () => false, + }, + { + name: "Run CyberRealm", + ready: () => mallPrice($item`1`) > 1_000, + prepare: () => { + $effects`Astral Shell, Elemental Saucesphere, Scarysauce`.forEach((ef) => { + if (!have(ef)) useSkill(toSkill(ef)); + }); + }, + completed: () => nextCyberZone() === $location`none`, // $location`Cyberzone 1`.turnsSpent >= 19 * myDaycount(), + choices: { 1545: 1, 1546: 1, 1547: 1, 1548: 1, 1549: 1, 1550: 1 }, + do: () => nextCyberZone(), + outfit: { + hat: $item`Crown of Thrones`, + back: $item`unwrapped knock-off retro superhero cape`, + shirt: $item`zero-trust tanktop`, + weapon: $item`June cleaver`, + offhand: $item`visual packet sniffer`, + pants: $item`digibritches`, + acc1: $item`retro floppy disk`, + acc2: $item`retro floppy disk`, + acc3: $item`retro floppy disk`, + famequip: $item`familiar-in-the-middle wrapper`, + modes: { retrocape: ["vampire", "hold"] }, + riders: { "crown-of-thrones": $familiar`Mini Kiwi` }, + }, + combat: new CombatStrategy().macro(() => + Macro.if_( + "!monsterphylum construct", + Macro.trySkill($skill`Sing Along`) + .trySkill($skill`Micrometeorite`) + .trySkill($skill`Saucestorm`) + .trySkill($skill`Saucestorm`) + .trySkill($skill`Saucestorm`) + .trySkill($skill`Saucestorm`) + .attack() + .repeat(), + ) + .skill($skill`Throw Cyber Rock`) + .repeat(), + ), + limit: { skip: 60 }, + }, + { + name: "Wardrobe-o-matic", + ready: () => myLevel() >= 15 && have($item`wardrobe-o-matic`), + completed: () => get("_wardrobeUsed", false), + do: (): void => { + use($item`wardrobe-o-matic`); + cliExecute("set _wardrobeUsed = true"); + }, + limit: { tries: 1 }, + }, + { + name: "Candy Deviler", + // eslint-disable-next-line libram/verify-constants + ready: () => have($item`candy egg deviler`), + completed: () => toInt(get("_candyEggsDeviled")) >= 3, + do: () => { + visitUrl(`inventory.php?action=eggdevil&pwd`); + visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); + visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); + visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); + }, + }, + ] } let duffo = false; @@ -316,24 +316,24 @@ let garboDone = false; export function noBarf(): Task[] { return [ - { - name: "CONSUME ALL", - ready: () => holiday().includes("Halloween") || args.crimbo || args.chrono, - completed: () => - myFullness() >= fullnessLimit() && - mySpleenUse() >= spleenLimit() && - myInebriety() >= inebrietyLimit(), - do: () => cliExecute("consume ALL"), - }, - { - name: "Garbo Nobarf", - ready: () => holiday().includes("Halloween") || args.crimbo || args.chrono, - completed: () => garboDone, - do: (): void => { - cliExecute(`${args.garboascend} nodiet nobarf target="witchess knight"`); - garboDone = true; - }, + { + name: "CONSUME ALL", + ready: () => holiday().includes("Halloween") || args.crimbo || args.chrono, + completed: () => + myFullness() >= fullnessLimit() && + mySpleenUse() >= spleenLimit() && + myInebriety() >= inebrietyLimit(), + do: () => cliExecute("consume ALL"), + }, + { + name: "Garbo Nobarf", + ready: () => holiday().includes("Halloween") || args.crimbo || args.chrono, + completed: () => garboDone, + do: (): void => { + cliExecute(`${args.garboascend} nodiet nobarf target="witchess knight"`); + garboDone = true; }, + }, ] } @@ -377,70 +377,70 @@ export function garboWeen(): Task[] { export function chrono(): Task[] { return [ - { - name: "Chrono", - ready: () => args.chrono && canAdventure($location`The Primordial Stew`), - completed: () => myAdventures() === 0 && myInebriety() >= inebrietyLimit(), - prepare: () => uneffect($effect`Beaten Up`), - do: (): void => { - cliExecute(`${args.chronoscript}`); - }, - clear: "all", - tracking: "chrono", - limit: { tries: 1 }, //this will run again after installing CMC, by magic - }, - { - name: "Super Nightcap", - ready: () => have($item`Drunkula's wineglass`) && args.chrono && canAdventure($location`The Primordial Stew`) && myDaycount() > 1, - completed: () => totallyDrunk(), - do: () => cliExecute(`CONSUME NIGHTCAP`), + { + name: "Chrono", + ready: () => args.chrono && canAdventure($location`The Primordial Stew`), + completed: () => myAdventures() === 0 && myInebriety() >= inebrietyLimit(), + prepare: () => uneffect($effect`Beaten Up`), + do: (): void => { + cliExecute(`${args.chronoscript}`); }, - { - name: "Chrono Drunk", - ready: () => args.chrono && canAdventure($location`The Primordial Stew`) && myDaycount() > 1, - completed: () => myAdventures() === 0, - prepare: () => uneffect($effect`Beaten Up`), - do: (): void => { - cliExecute(`${args.chronoscript}`); - }, - clear: "all", - tracking: "Chrono", - limit: { tries: 1 }, //this will run again after installing CMC, by magic + clear: "all", + tracking: "chrono", + limit: { tries: 1 }, //this will run again after installing CMC, by magic + }, + { + name: "Super Nightcap", + ready: () => have($item`Drunkula's wineglass`) && args.chrono && canAdventure($location`The Primordial Stew`) && myDaycount() > 1, + completed: () => totallyDrunk(), + do: () => cliExecute(`CONSUME NIGHTCAP`), + }, + { + name: "Chrono Drunk", + ready: () => args.chrono && canAdventure($location`The Primordial Stew`) && myDaycount() > 1, + completed: () => myAdventures() === 0, + prepare: () => uneffect($effect`Beaten Up`), + do: (): void => { + cliExecute(`${args.chronoscript}`); }, - ] - } + clear: "all", + tracking: "Chrono", + limit: { tries: 1 }, //this will run again after installing CMC, by magic + }, + ] +} export function crimbo(): Task[] { return [ - { - name: "Crimbo Time", - ready: () => args.crimbo, - completed: () => myAdventures() === 0 && myInebriety() >= inebrietyLimit(), - prepare: () => uneffect($effect`Beaten Up`), - do: (): void => { - cliExecute(`${args.crimboscript}`); - }, - clear: "all", - tracking: "Crimbo", - limit: { tries: 1 }, //this will run again after installing CMC, by magic - }, - { - name: "Super Nightcap", - ready: () => have($item`Drunkula's wineglass`) && holiday().includes("Halloween") && myDaycount() > 1, - completed: () => totallyDrunk(), - do: () => cliExecute(`CONSUME NIGHTCAP`), + { + name: "Crimbo Time", + ready: () => args.crimbo, + completed: () => myAdventures() === 0 && myInebriety() >= inebrietyLimit(), + prepare: () => uneffect($effect`Beaten Up`), + do: (): void => { + cliExecute(`${args.crimboscript}`); }, - { - name: "Crimbo Drunk", - ready: () => args.crimbo && myDaycount() > 1, - completed: () => myAdventures() === 0, - prepare: () => uneffect($effect`Beaten Up`), - do: (): void => { - cliExecute(`${args.crimboscript}`); - }, - clear: "all", - tracking: "Crimbo", - limit: { tries: 1 }, //this will run again after installing CMC, by magic + clear: "all", + tracking: "Crimbo", + limit: { tries: 1 }, //this will run again after installing CMC, by magic + }, + { + name: "Super Nightcap", + ready: () => have($item`Drunkula's wineglass`) && holiday().includes("Halloween") && myDaycount() > 1, + completed: () => totallyDrunk(), + do: () => cliExecute(`CONSUME NIGHTCAP`), + }, + { + name: "Crimbo Drunk", + ready: () => args.crimbo && myDaycount() > 1, + completed: () => myAdventures() === 0, + prepare: () => uneffect($effect`Beaten Up`), + do: (): void => { + cliExecute(`${args.crimboscript}`); }, - ] - } + clear: "all", + tracking: "Crimbo", + limit: { tries: 1 }, //this will run again after installing CMC, by magic + }, + ] +} From bfb91bd47b2b43aedbc9349838ebe75924999762 Mon Sep 17 00:00:00 2001 From: Ignose Date: Wed, 15 Jan 2025 08:33:42 -0500 Subject: [PATCH 09/24] Add tracking ignore to free king because of leaves/source/witchess --- src/tasks/3 runleg.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tasks/3 runleg.ts b/src/tasks/3 runleg.ts index 2cab46e..35cc63a 100644 --- a/src/tasks/3 runleg.ts +++ b/src/tasks/3 runleg.ts @@ -73,6 +73,7 @@ export function RunQuests(): Quest { visitUrl("place.php?whichplace=nstower&action=ns_11_prism"); }, clear: "all", + tracking: "ignore", }, ] } From ea4133b74d91279f1f2256860020eac01d913ae6 Mon Sep 17 00:00:00 2001 From: Ignose Date: Fri, 17 Jan 2025 12:25:15 -0500 Subject: [PATCH 10/24] Run Prettier, fix sobriety logic --- src/engine/engine.ts | 35 +- src/main.ts | 7 +- src/relay.ts | 11 +- src/tasks/1 aftercoreleg.ts | 2 +- src/tasks/3 runleg.ts | 123 ++--- src/tasks/4 postrunleg.ts | 848 +++++++++++++++++++---------------- src/tasks/repeatableTasks.ts | 159 +++++-- src/tasks/structure.ts | 5 +- src/tasks/utils.ts | 70 ++- 9 files changed, 705 insertions(+), 555 deletions(-) diff --git a/src/engine/engine.ts b/src/engine/engine.ts index c4d121e..13e1422 100644 --- a/src/engine/engine.ts +++ b/src/engine/engine.ts @@ -1,7 +1,9 @@ import { CombatResources, CombatStrategy, Engine } from "grimoire-kolmafia"; import { cliExecute, equippedAmount, Location, logprint, setAutoAttack, writeCcs } from "kolmafia"; import { $item, clearMaximizerCache, get, JuneCleaver, PropertiesManager } from "libram"; + import { getCurrentLeg, Task } from "../tasks/structure"; + import { printProfits, ProfitTracker } from "./profits"; const grimoireCCS = "grimoire_macro"; @@ -14,7 +16,7 @@ export class ProfitTrackingEngine extends Engine { this.profits = new ProfitTracker(key); } - setChoices(task: Task, manager: PropertiesManager): void { + setChoices(task: Task, manager: PropertiesManager): void { super.setChoices(task, manager); if (equippedAmount($item`June cleaver`) > 0) { this.propertyManager.setChoices( @@ -31,13 +33,13 @@ export class ProfitTrackingEngine extends Engine { setCombat( task: Task, task_combat: CombatStrategy, - task_resources: CombatResources + task_resources: CombatResources, ): void { // Save regular combat macro const macro = task_combat.compile( task_resources, this.options?.combat_defaults, - task.do instanceof Location ? task.do : undefined + task.do instanceof Location ? task.do : undefined, ); if (macro.toString().length > 1) { macro.save(); @@ -95,23 +97,22 @@ export class ProfitTrackingEngine extends Engine { } } function shouldSkip(choice: number): boolean { - const skip = [1468, 1470, 1472, 1473, 1474] + const skip = [1468, 1470, 1472, 1473, 1474]; return skip.includes(choice) && get("_juneCleaverSkips") < 5; } function bestJuneCleaverOption(choice: number): number { - const choiceTable: { [key: number]: number } = { - 1467: 3, // Poetic Justice - 1468: 2, // Aunts not Ants - 1469: 3, // Beware of Alligator - 1470: 2, // Teacher's Pet - 1471: 1, // Lost and Found - 1472: 1, // Summer Days - 1473: 1, // Bath Time - 1474: 2, // Delicious Sprouts - 1475: 1, // Hypnotic Master - }; + const choiceTable: { [key: number]: number } = { + 1467: 3, // Poetic Justice + 1468: 2, // Aunts not Ants + 1469: 3, // Beware of Alligator + 1470: 2, // Teacher's Pet + 1471: 1, // Lost and Found + 1472: 1, // Summer Days + 1473: 1, // Bath Time + 1474: 2, // Delicious Sprouts + 1475: 1, // Hypnotic Master + }; - return choiceTable[choice] ?? 0; + return choiceTable[choice] ?? 0; } - diff --git a/src/main.ts b/src/main.ts index 8fb6939..d903b81 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,11 +3,11 @@ import { gamedayToInt, print } from "kolmafia"; import { args } from "./args"; import { ProfitTrackingEngine } from "./engine/engine"; -import { deleteJunkKmails, halloween, notifyVoters, realDay, realMonth } from "./tasks/utils"; import { AftercoreQuest } from "./tasks/1 aftercoreleg"; import { AscendQuest } from "./tasks/2 ascend"; -import { PostRunQuests } from "./tasks/4 postrunleg"; import { RunQuests } from "./tasks/3 runleg"; +import { PostRunQuests } from "./tasks/4 postrunleg"; +import { deleteJunkKmails, halloween, notifyVoters, realDay, realMonth } from "./tasks/utils"; const version = "0.0.3"; @@ -30,7 +30,8 @@ export function main(command?: string): void { print(`Running: candyWrapper v${version}`); - if (!args.cs && !args.smol && !args.casual && !args.robot) throw "Undefined runtype; please choose an acceptable path"; + if (!args.cs && !args.smol && !args.casual && !args.robot) + throw "Undefined runtype; please choose an acceptable path"; const tasks = getTasks([AftercoreQuest(), AscendQuest(), RunQuests(), PostRunQuests()]); diff --git a/src/relay.ts b/src/relay.ts index 4c50dc8..7a279c4 100644 --- a/src/relay.ts +++ b/src/relay.ts @@ -7,6 +7,7 @@ import { handleApiRequest, RelayPage, } from "mafia-shared-relay"; + import { args } from "./args"; function convertArgsToHtml(): RelayPage[] { @@ -40,7 +41,7 @@ function convertArgsToHtml(): RelayPage[] { }, (group, name: string) => { pages.push({ page: name, components: [] }); - } + }, ); pages @@ -48,9 +49,7 @@ function convertArgsToHtml(): RelayPage[] { .forEach((p) => { const html: ComponentHtml = { type: "html", - data: `

CandyWrapper ${ - p.page - }`, + data: `

CandyWrapper ${p.page}`, }; p.components.splice(0, 0, html); }); @@ -61,7 +60,5 @@ function convertArgsToHtml(): RelayPage[] { export function main() { if (handleApiRequest()) return; - write( - generateHTML(convertArgsToHtml()) - ); + write(generateHTML(convertArgsToHtml())); } diff --git a/src/tasks/1 aftercoreleg.ts b/src/tasks/1 aftercoreleg.ts index 2f857b7..03ec660 100644 --- a/src/tasks/1 aftercoreleg.ts +++ b/src/tasks/1 aftercoreleg.ts @@ -40,9 +40,9 @@ import { Cycle, setConfiguration, Station } from "libram/dist/resources/2022/Tra import { args } from "../args"; +import { chrono, crimbo, garboWeen, noBarf, postRunQuests, preRunQuests } from "./repeatableTasks"; import { Quest } from "./structure"; import { maxBase, pvpCloset, stooperDrunk, totallyDrunk } from "./utils"; -import { chrono, crimbo, garboWeen, noBarf, postRunQuests, preRunQuests } from "./repeatableTasks"; const doSmol = args.smol ? true : false; const doCS = args.cs ? true : false; diff --git a/src/tasks/3 runleg.ts b/src/tasks/3 runleg.ts index 35cc63a..ed75fdb 100644 --- a/src/tasks/3 runleg.ts +++ b/src/tasks/3 runleg.ts @@ -1,15 +1,33 @@ +import { step } from "grimoire-kolmafia"; +import { + cliExecute, + drink, + eat, + fullnessLimit, + itemAmount, + myFullness, + myInebriety, + useSkill, + visitUrl, +} from "kolmafia"; import { $item, $skill, clamp, get, have } from "libram"; -import { getCurrentLeg, Leg, Quest } from "./structure"; + import { args } from "../args"; -import { cliExecute, drink, eat, fullnessLimit, itemAmount, mallPrice, myFullness, myInebriety, storageAmount, useSkill, visitUrl } from "kolmafia"; -import { preRunQuests } from "./repeatableTasks"; -import { step } from "grimoire-kolmafia"; -const runType = () => args.smol ? args.smolscript : args.cs ? args.csscript : args.casual ? args.casualscript : args.robot ? args.robotscript : args.robotscript; +import { preRunQuests } from "./repeatableTasks"; +import { getCurrentLeg, Leg, Quest } from "./structure"; +import { shouldWeOverdrink } from "./utils"; -const checkMelange = () => - get("valueOfAdventure") * 45 > mallPrice($item`spice melange`) && - !have($item`designer sweatpants`); +const runType = () => + args.smol + ? args.smolscript + : args.cs + ? args.csscript + : args.casual + ? args.casualscript + : args.robot + ? args.robotscript + : args.robotscript; export function howManySausagesCouldIEat() { if (!have($item`Kramco Sausage-o-Matic™`)) return 0; @@ -23,59 +41,52 @@ export function howManySausagesCouldIEat() { ); } - export function RunQuests(): Quest { return { name: "Ascension Run", - completed: () => getCurrentLeg() !== Leg.Run || get("kingLiberated"), - tasks: [ - ...preRunQuests(), - { - name: "Run", - completed: () => (get("kingLiberated") && args.cs) || step("questL13Final") > 11, - do: () => { - if(runType() === undefined || runType() === null) throw "no runtime defined" - else cliExecute(runType()) - }, - tracking: "Run", + completed: () => getCurrentLeg() !== Leg.Run || get("kingLiberated"), + tasks: [ + ...preRunQuests(), + { + name: "Run", + completed: () => (get("kingLiberated") && args.cs) || step("questL13Final") > 11, + do: () => { + if (runType() === undefined || runType() === null) throw "no runtime defined"; + else cliExecute(runType()); }, - { - name: "drink", - ready: () => - step("questL13Final") > 11 && - (have($item`designer sweatpants`) || checkMelange()) && - have($skill`Drinking to Drink`) && - storageAmount($item`synthetic dog hair pill`) >= 1 && - args.smol, - completed: () => myInebriety() >= 2, - do: (): void => { - if (have($skill`The Ode to Booze`)) useSkill($skill`The Ode to Booze`); - drink($item`astral pilsner`, 1); - }, - clear: "all", - tracking: "Run", + tracking: "Run", + }, + { + name: "drink", + ready: () => shouldWeOverdrink() && args.smol, + completed: () => myInebriety() >= 2, + do: (): void => { + if (have($skill`The Ode to Booze`)) useSkill($skill`The Ode to Booze`); + drink($item`astral pilsner`, 1); }, - { - name: "Sausages", - ready: () => args.smol, - completed: () => howManySausagesCouldIEat() === 0, - do: (): void => { - eat($item`magical sausage`, howManySausagesCouldIEat()); - }, - clear: "all", - tracking: "Run", + clear: "all", + tracking: "Run", + }, + { + name: "Sausages", + ready: () => args.smol, + completed: () => howManySausagesCouldIEat() === 0, + do: (): void => { + eat($item`magical sausage`, howManySausagesCouldIEat()); }, - { - name: "Free King", - ready: () => step("questL13Final") > 11 && !(args.cs), - completed: () => get("kingLiberated", false), - do: (): void => { - visitUrl("place.php?whichplace=nstower&action=ns_11_prism"); - }, - clear: "all", - tracking: "ignore", + clear: "all", + tracking: "Run", + }, + { + name: "Free King", + ready: () => step("questL13Final") > 11 && !args.cs, + completed: () => get("kingLiberated", false), + do: (): void => { + visitUrl("place.php?whichplace=nstower&action=ns_11_prism"); }, - ] - } + clear: "all", + tracking: "ignore", + }, + ], + }; } - diff --git a/src/tasks/4 postrunleg.ts b/src/tasks/4 postrunleg.ts index e13c721..122de51 100644 --- a/src/tasks/4 postrunleg.ts +++ b/src/tasks/4 postrunleg.ts @@ -1,10 +1,63 @@ import { CombatStrategy } from "grimoire-kolmafia"; -import { buy, cliExecute, drink, Effect, hippyStoneBroken, inebrietyLimit, itemAmount, mallPrice, myAdventures, myAscensions, myDaycount, myInebriety, myLevel, myMaxhp, mySign, numericModifier, print, pvpAttacksLeft, restoreHp, restoreMp, retrieveItem, setProperty, toBoolean, use, useFamiliar, useSkill, wait } from "kolmafia"; -import { $coinmaster, $effect, $effects, $familiar, $familiars, $item, $items, $location, $skill, get, have, Macro, uneffect } from "libram"; +import { + buy, + cliExecute, + drink, + Effect, + hippyStoneBroken, + inebrietyLimit, + itemAmount, + mallPrice, + myAdventures, + myAscensions, + myDaycount, + myInebriety, + myLevel, + myMaxhp, + mySign, + numericModifier, + print, + pvpAttacksLeft, + restoreHp, + restoreMp, + retrieveItem, + setProperty, + toBoolean, + use, + useFamiliar, + useSkill, + wait, +} from "kolmafia"; +import { + $coinmaster, + $effect, + $effects, + $familiar, + $familiars, + $item, + $items, + $location, + $skill, + get, + have, + Macro, + uneffect, +} from "libram"; + import { args } from "../args"; + import { chrono, crimbo, garboWeen, noBarf, postRunQuests } from "./repeatableTasks"; import { Quest } from "./structure"; -import { backstageItemsDone, bestFam, doneAdventuring, haveAll, maxBase, pvpCloset, stooperDrunk, totallyDrunk } from "./utils"; +import { + backstageItemsDone, + bestFam, + doneAdventuring, + haveAll, + maxBase, + pvpCloset, + stooperDrunk, + totallyDrunk, +} from "./utils"; let pajamas = false; let smoke = 0; @@ -18,417 +71,418 @@ const checkMelange = () => export function PostRunQuests(): Quest { return { name: "Post-Run Aftercore", - ready: () => myDaycount() === 1 && get("kingLiberated", false), - completed: () => totallyDrunk() && pajamas, - tasks: [ - { - name: "Pull All", - completed: () => get("lastEmptiedStorage") === myAscensions(), - do: () => cliExecute("pull all; refresh all"), - }, - { - name: "Clear citizen", - completed: () => !get("_citizenZone", "").includes("castle") && !get("_citizenZone", "").includes("Madness Bakery"), - do: (): void => { - uneffect($effect`Citizen of a Zone`); - cliExecute(`set _citizenZone = ""`); - }, - }, - { - name: "Ensure prefs reset", - completed: () => !get("_folgerInitialConfig", false), - do: () => cliExecute("set _folgerInitialConfig = false"), - }, - { - name: "But dad I don't want to feel lost", - completed: () => !have($effect`Feeling Lost`), - do: () => uneffect($effect`Feeling Lost`), - }, - { - name: "Sober Up", - ready:() => args.smol, - completed: () => - (myInebriety() <= 15 || - get("_mimeArmyShotglassUsed") || - get("_sweatOutSomeBoozeUsed", 0) === 3), - do: (): void => { - if (checkMelange()) { - cliExecute("acquire spice melange; use spice melange"); - } - while (get("_sweatOutSomeBoozeUsed", 0) < 3) { - useSkill($skill`Sweat Out Some Booze`); - } - if (!get("_sobrieTeaUsed", false)) { - retrieveItem($item`cuppa Sobrie tea`); - use($item`cuppa Sobrie tea`); - } - use($item`synthetic dog hair pill`); - }, - }, - { - name: "PvP Closet Safety 1", - ready: () => args.pvp && get("autoSatisfyWithCloset") && !args.safepvp, - completed: () => toBoolean(get("_safetyCloset1")), - do: () => pvpCloset(1), - }, - { - name: "Drink Pre-Tune", - ready: () => - mySign().toLowerCase() === "blender" && - myLevel() >= 7 && - have($item`mime army shotglass`) && - (have($item`astral pilsner`) || have($item`astral six-pack`)), - completed: () => - get("_mimeArmyShotglassUsed") || !have($item`hewn moon-rune spoon`) || get("moonTuned"), - prepare: () => { - if (have($item`astral six-pack`)) use($item`astral six-pack`); - }, - do: () => drink(1, $item`astral pilsner`), - }, - { - name: "Moon Spoon", - completed: () => - !have($item`hewn moon-rune spoon`) || - get("moonTuned") || - mySign().toLowerCase() === "wombat", - do: () => cliExecute("spoon wombat"), - }, - { - name: "Gold Wedding Ring", - ready: () => !args.cs, - completed: () => - (!have($skill`Comprehensive Cartography`) || - myAscensions() === get("lastCartographyBooPeak")), - choices: { 1430: 3, 606: 4, 610: 1, 1056: 1 }, - do: $location`A-Boo Peak`, - outfit: { modifier: "initiative 40 min 40 max, -tie" }, - }, - { - name: "Emergency Drink", - ready: () => myAdventures() < 25, - completed: () => get("_mimeArmyShotglassUsed") || !have($item`mime army shotglass`), - prepare: () => { - if (have($item`astral six-pack`)) use($item`astral six-pack`); - }, - do: () => { - while (myAdventures() < 25) { - drink(1, $item`astral pilsner`); - } - }, - }, - { - name: "Emergency Drink Part 2", - ready: () => myAdventures() === 0 && myInebriety() < 11, - completed: () => myAdventures() > 0 || myInebriety() >= 11, - prepare: () => { - if (have($item`astral six-pack`)) use($item`astral six-pack`); - }, - do: () => { - while (myAdventures() < 25) { - useSkill($skill`The Ode to Booze`); - drink(1, $item`astral pilsner`); - } - }, - limit: { tries: 6 }, + ready: () => myDaycount() === 1 && get("kingLiberated", false), + completed: () => totallyDrunk() && pajamas, + tasks: [ + { + name: "Pull All", + completed: () => get("lastEmptiedStorage") === myAscensions(), + do: () => cliExecute("pull all; refresh all"), + }, + { + name: "Clear citizen", + completed: () => + !get("_citizenZone", "").includes("castle") && + !get("_citizenZone", "").includes("Madness Bakery"), + do: (): void => { + uneffect($effect`Citizen of a Zone`); + cliExecute(`set _citizenZone = ""`); }, - { - name: "Laugh Floor", - ready: () => !args.cs, - completed: () => - have($skill`Liver of Steel`) || - have($item`steel margarita`) || - have($item`Azazel's lollipop`) || - have($item`observational glasses`), - effects: () => [ - ...(have($skill`Musk of the Moose`) ? $effects`Musk of the Moose` : []), - ...(have($skill`Carlweather's Cantata of Confrontation`) - ? $effects`Carlweather's Cantata of Confrontation` - : []), - ], - prepare: (): void => { - if (!have($effect`Carlweather's Cantata of Confrontation`)) { - cliExecute("kmail to Buffy || 10 Cantata of Confrontation"); - wait(15); - cliExecute("refresh effects"); - } - $effects`Smooth Movements, The Sonata of Sneakiness, Darkened Photons, Shifted Phase`.forEach( - (ef: Effect) => cliExecute(`uneffect ${ef}`), - ); - restoreHp(0.75 * myMaxhp()); - restoreMp(20); - }, - do: $location`The Laugh Floor`, - outfit: () => ({ - familiar: bestFam(), - modifier: `${maxBase()}, 100 combat rate, 3 item, 250 bonus carnivorous potted plant`, - }), - combat: new CombatStrategy().macro( - Macro.trySkill($skill`Curse of Weaksauce`) - .tryItem($item`train whistle`) - .tryItem($item`porquoise-handled sixgun`) - .attack() - .repeat(), - ), - limit: { tries: 15 }, + }, + { + name: "Ensure prefs reset", + completed: () => !get("_folgerInitialConfig", false), + do: () => cliExecute("set _folgerInitialConfig = false"), + }, + { + name: "But dad I don't want to feel lost", + completed: () => !have($effect`Feeling Lost`), + do: () => uneffect($effect`Feeling Lost`), + }, + { + name: "Sober Up", + ready: () => args.smol, + completed: () => myInebriety() <= inebrietyLimit() || get("_mimeArmyShotglassUsed"), + do: (): void => { + if (checkMelange()) { + cliExecute("acquire spice melange; use spice melange"); + } + while (get("_sweatOutSomeBoozeUsed", 0) < 3) { + useSkill($skill`Sweat Out Some Booze`); + } + + use($item`synthetic dog hair pill`); + + if (!get("_sobrieTeaUsed", false)) { + retrieveItem($item`cuppa Sobrie tea`); + use($item`cuppa Sobrie tea`); + } }, - { - name: "Infernal Rackets Backstage", - ready: () => !args.cs, - completed: () => - have($skill`Liver of Steel`) || - have($item`steel margarita`) || - have($item`Azazel's unicorn`) || - backstageItemsDone(), - effects: () => [ - ...(have($skill`Smooth Movement`) ? $effects`Smooth Movements` : []), - ...(have($skill`The Sonata of Sneakiness`) ? $effects`The Sonata of Sneakiness` : []), - ], - prepare: (): void => { - if (!have($effect`The Sonata of Sneakiness`)) { - cliExecute("kmail to Buffy || 10 Sonata of Sneakiness"); - wait(15); - cliExecute("refresh effects"); - } - $effects`Musk of the Moose, Carlweather's Cantata of Confrontation, Hooooooooonk!`.forEach( - (ef: Effect) => cliExecute(`uneffect ${ef}`), - ); - restoreHp(0.75 * myMaxhp()); - restoreMp(20); - }, - do: $location`Infernal Rackets Backstage`, - outfit: () => ({ - familiar: bestFam(), - modifier: `${maxBase()}, -100 combat rate, 3 item, 250 bonus carnivorous potted plant`, - }), - combat: new CombatStrategy().macro( - Macro.trySkill($skill`Curse of Weaksauce`) - .tryItem($item`train whistle`) - .tryItem($item`porquoise-handled sixgun`) - .attack() - .repeat(), - ), - limit: { tries: 15 }, + }, + { + name: "PvP Closet Safety 1", + ready: () => args.pvp && get("autoSatisfyWithCloset") && !args.safepvp, + completed: () => toBoolean(get("_safetyCloset1")), + do: () => pvpCloset(1), + }, + { + name: "Drink Pre-Tune", + ready: () => + mySign().toLowerCase() === "blender" && + myLevel() >= 7 && + have($item`mime army shotglass`) && + (have($item`astral pilsner`) || have($item`astral six-pack`)), + completed: () => + get("_mimeArmyShotglassUsed") || !have($item`hewn moon-rune spoon`) || get("moonTuned"), + prepare: () => { + if (have($item`astral six-pack`)) use($item`astral six-pack`); }, - { - name: "Mourn", - ready: () => have($item`observational glasses`) && !args.cs, - completed: () => - have($skill`Liver of Steel`) || - have($item`steel margarita`) || - have($item`Azazel's lollipop`), - outfit: { - equip: $items`hilarious comedy prop, observational glasses, Victor\, the Insult Comic Hellhound Puppet`, - }, - do: () => cliExecute("panda comedy insult; panda comedy observe"), + do: () => drink(1, $item`astral pilsner`), + }, + { + name: "Moon Spoon", + completed: () => + !have($item`hewn moon-rune spoon`) || + get("moonTuned") || + mySign().toLowerCase() === "wombat", + do: () => cliExecute("spoon wombat"), + }, + { + name: "Gold Wedding Ring", + ready: () => !args.cs, + completed: () => + !have($skill`Comprehensive Cartography`) || + myAscensions() === get("lastCartographyBooPeak"), + choices: { 1430: 3, 606: 4, 610: 1, 1056: 1 }, + do: $location`A-Boo Peak`, + outfit: { modifier: "initiative 40 min 40 max, -tie" }, + }, + { + name: "Emergency Drink", + ready: () => myAdventures() < 25, + completed: () => get("_mimeArmyShotglassUsed") || !have($item`mime army shotglass`), + prepare: () => { + if (have($item`astral six-pack`)) use($item`astral six-pack`); }, - { - name: "Sven Golly", - ready: () => backstageItemsDone() && !args.cs, - completed: () => - have($skill`Liver of Steel`) || - have($item`steel margarita`) || - have($item`Azazel's unicorn`), - do: (): void => { - cliExecute( - `panda arena Bognort ${$items`giant marshmallow, gin-soaked blotter paper`.find((a) => - have(a), - )}`, - ); - cliExecute( - `panda arena Stinkface ${$items`beer-scented teddy bear, gin-soaked blotter paper`.find( - (a) => have(a), - )}`, - ); - cliExecute( - `panda arena Flargwurm ${$items`booze-soaked cherry, sponge cake`.find((a) => - have(a), - )}`, - ); - cliExecute(`panda arena Jim ${$items`comfy pillow, sponge cake`.find((a) => have(a))}`); - }, - }, - { - name: "Moaning Panda", - ready: () => haveAll($items`Azazel's lollipop, Azazel's unicorn`) && !args.cs, - completed: () => - have($skill`Liver of Steel`) || - have($item`steel margarita`) || - have($item`Azazel's tutu`), - acquire: () => - $items`bus pass, imp air`.map((it) => ({ - item: it, - num: 5, - price: get("valueOfAdventure"), - })), - do: () => cliExecute("panda moan"), - limit: { tries: 3 }, - }, - { - name: "Steel Margarita", - ready: () => haveAll($items`Azazel's tutu, Azazel's lollipop, Azazel's unicorn`), - completed: () => have($skill`Liver of Steel`) || have($item`steel margarita`), - do: () => cliExecute("panda temple"), + do: () => { + while (myAdventures() < 25) { + drink(1, $item`astral pilsner`); + } }, - { - name: "Liver of Steel", - ready: () => have($item`steel margarita`), - completed: () => have($skill`Liver of Steel`), - do: () => drink(1, $item`steel margarita`), + }, + { + name: "Emergency Drink Part 2", + ready: () => myAdventures() === 0 && myInebriety() < 11, + completed: () => myAdventures() > 0 || myInebriety() >= 11, + prepare: () => { + if (have($item`astral six-pack`)) use($item`astral six-pack`); }, - { - name: "Emergency Drink Part 3", - ready: () => myAdventures() < 40 && myInebriety() < 11, - completed: () => myAdventures() > 40 || myInebriety() >= 11, - prepare: () => { - if (have($item`astral six-pack`)) use($item`astral six-pack`); - }, - do: () => { - while (myAdventures() < 80 && have($item`astral pilsner`)) { - useSkill($skill`The Ode to Booze`); - drink(1, $item`astral pilsner`); - } - }, - limit: { tries: 6 }, + do: () => { + while (myAdventures() < 25) { + useSkill($skill`The Ode to Booze`); + drink(1, $item`astral pilsner`); + } }, - ...postRunQuests(), - ...noBarf(), - ...garboWeen(), - ...crimbo(), - ...chrono(), - { - name: "Garbo", - completed: () => myAdventures() === 0 || stooperDrunk(), - prepare: () => uneffect($effect`Beaten Up`), - do: () => cliExecute(`${args.garbo}`), - post: () => - $effects`Power Ballad of the Arrowsmith, Stevedave's Shanty of Superiority, The Moxious Madrigal, The Magical Mojomuscular Melody, Aloysius' Antiphon of Aptitude, Ur-Kel's Aria of Annoyance` - .filter((ef) => have(ef)) - .forEach((ef) => uneffect(ef)), - clear: "all", - tracking: "Garbo", + limit: { tries: 6 }, + }, + { + name: "Laugh Floor", + ready: () => !args.cs, + completed: () => + have($skill`Liver of Steel`) || + have($item`steel margarita`) || + have($item`Azazel's lollipop`) || + have($item`observational glasses`), + effects: () => [ + ...(have($skill`Musk of the Moose`) ? $effects`Musk of the Moose` : []), + ...(have($skill`Carlweather's Cantata of Confrontation`) + ? $effects`Carlweather's Cantata of Confrontation` + : []), + ], + prepare: (): void => { + if (!have($effect`Carlweather's Cantata of Confrontation`)) { + cliExecute("kmail to Buffy || 10 Cantata of Confrontation"); + wait(15); + cliExecute("refresh effects"); + } + $effects`Smooth Movements, The Sonata of Sneakiness, Darkened Photons, Shifted Phase`.forEach( + (ef: Effect) => cliExecute(`uneffect ${ef}`), + ); + restoreHp(0.75 * myMaxhp()); + restoreMp(20); }, - { - name: "Turn in FunFunds", - ready: () => get("_stenchAirportToday") && itemAmount($item`FunFunds™`) >= 20, - completed: () => have($item`one-day ticket to Dinseylandfill`), - do: () => - buy($coinmaster`The Dinsey Company Store`, 1, $item`one-day ticket to Dinseylandfill`), - tracking: "Garbo", + do: $location`The Laugh Floor`, + outfit: () => ({ + familiar: bestFam(), + modifier: `${maxBase()}, 100 combat rate, 3 item, 250 bonus carnivorous potted plant`, + }), + combat: new CombatStrategy().macro( + Macro.trySkill($skill`Curse of Weaksauce`) + .tryItem($item`train whistle`) + .tryItem($item`porquoise-handled sixgun`) + .attack() + .repeat(), + ), + limit: { tries: 15 }, + }, + { + name: "Infernal Rackets Backstage", + ready: () => !args.cs, + completed: () => + have($skill`Liver of Steel`) || + have($item`steel margarita`) || + have($item`Azazel's unicorn`) || + backstageItemsDone(), + effects: () => [ + ...(have($skill`Smooth Movement`) ? $effects`Smooth Movements` : []), + ...(have($skill`The Sonata of Sneakiness`) ? $effects`The Sonata of Sneakiness` : []), + ], + prepare: (): void => { + if (!have($effect`The Sonata of Sneakiness`)) { + cliExecute("kmail to Buffy || 10 Sonata of Sneakiness"); + wait(15); + cliExecute("refresh effects"); + } + $effects`Musk of the Moose, Carlweather's Cantata of Confrontation, Hooooooooonk!`.forEach( + (ef: Effect) => cliExecute(`uneffect ${ef}`), + ); + restoreHp(0.75 * myMaxhp()); + restoreMp(20); }, - { - name: "PvP Closet Safety 2", - ready: () => args.pvp && get("autoSatisfyWithCloset") && !args.safepvp, - completed: () => toBoolean(get("_safetyCloset2")), - do: () => pvpCloset(2), + do: $location`Infernal Rackets Backstage`, + outfit: () => ({ + familiar: bestFam(), + modifier: `${maxBase()}, -100 combat rate, 3 item, 250 bonus carnivorous potted plant`, + }), + combat: new CombatStrategy().macro( + Macro.trySkill($skill`Curse of Weaksauce`) + .tryItem($item`train whistle`) + .tryItem($item`porquoise-handled sixgun`) + .attack() + .repeat(), + ), + limit: { tries: 15 }, + }, + { + name: "Mourn", + ready: () => have($item`observational glasses`) && !args.cs, + completed: () => + have($skill`Liver of Steel`) || + have($item`steel margarita`) || + have($item`Azazel's lollipop`), + outfit: { + equip: $items`hilarious comedy prop, observational glasses, Victor\, the Insult Comic Hellhound Puppet`, }, - { - name: "PvP", - ready: () => doneAdventuring(), - completed: () => pvpAttacksLeft() === 0 || !hippyStoneBroken(), - do: (): void => { - cliExecute("unequip"); - cliExecute("UberPvPOptimizer"); - cliExecute(`PVP_MAB target=${args.pvpTarget}`); - }, + do: () => cliExecute("panda comedy insult; panda comedy observe"), + }, + { + name: "Sven Golly", + ready: () => backstageItemsDone() && !args.cs, + completed: () => + have($skill`Liver of Steel`) || + have($item`steel margarita`) || + have($item`Azazel's unicorn`), + do: (): void => { + cliExecute( + `panda arena Bognort ${$items`giant marshmallow, gin-soaked blotter paper`.find((a) => + have(a), + )}`, + ); + cliExecute( + `panda arena Stinkface ${$items`beer-scented teddy bear, gin-soaked blotter paper`.find( + (a) => have(a), + )}`, + ); + cliExecute( + `panda arena Flargwurm ${$items`booze-soaked cherry, sponge cake`.find((a) => + have(a), + )}`, + ); + cliExecute(`panda arena Jim ${$items`comfy pillow, sponge cake`.find((a) => have(a))}`); }, - { - name: "Stooper", - ready: () => - myInebriety() === inebrietyLimit() && - have($item`tiny stillsuit`) && - get("familiarSweat") >= 300, - completed: () => !have($familiar`Stooper`) || stooperDrunk(), - do: () => { - useFamiliar($familiar`Stooper`); - cliExecute("drink stillsuit distillate"); - }, + }, + { + name: "Moaning Panda", + ready: () => haveAll($items`Azazel's lollipop, Azazel's unicorn`) && !args.cs, + completed: () => + have($skill`Liver of Steel`) || + have($item`steel margarita`) || + have($item`Azazel's tutu`), + acquire: () => + $items`bus pass, imp air`.map((it) => ({ + item: it, + num: 5, + price: get("valueOfAdventure"), + })), + do: () => cliExecute("panda moan"), + limit: { tries: 3 }, + }, + { + name: "Steel Margarita", + ready: () => haveAll($items`Azazel's tutu, Azazel's lollipop, Azazel's unicorn`), + completed: () => have($skill`Liver of Steel`) || have($item`steel margarita`), + do: () => cliExecute("panda temple"), + }, + { + name: "Liver of Steel", + ready: () => have($item`steel margarita`), + completed: () => have($skill`Liver of Steel`), + do: () => drink(1, $item`steel margarita`), + }, + { + name: "Emergency Drink Part 3", + ready: () => myAdventures() < 40 && myInebriety() < 11, + completed: () => myAdventures() > 40 || myInebriety() >= 11, + prepare: () => { + if (have($item`astral six-pack`)) use($item`astral six-pack`); }, - { - name: "Nightcap", - ready: () => doneAdventuring(), - completed: () => totallyDrunk(), - do: () => cliExecute("CONSUME NIGHTCAP"), + do: () => { + while (myAdventures() < 80 && have($item`astral pilsner`)) { + useSkill($skill`The Ode to Booze`); + drink(1, $item`astral pilsner`); + } }, - { - name: "Smoke em if you got em", - ready: () => get("getawayCampsiteUnlocked"), - completed: () => !have($item`stick of firewood`), - do: (): void => { - while (have($item`stick of firewood`)) { - setProperty( - "choiceAdventure1394", - `1&message=${smoke} Thanks Seraphiii for writing Candywrapper!`, - ); - use(1, $item`campfire smoke`); - print(`Smoked ${smoke} firewoods!`); - smoke = smoke + 1; - } - }, + limit: { tries: 6 }, + }, + ...postRunQuests(), + ...noBarf(), + ...garboWeen(), + ...crimbo(), + ...chrono(), + { + name: "Garbo", + completed: () => myAdventures() === 0 || stooperDrunk(), + prepare: () => uneffect($effect`Beaten Up`), + do: () => cliExecute(`${args.garbo}`), + post: () => + $effects`Power Ballad of the Arrowsmith, Stevedave's Shanty of Superiority, The Moxious Madrigal, The Magical Mojomuscular Melody, Aloysius' Antiphon of Aptitude, Ur-Kel's Aria of Annoyance` + .filter((ef) => have(ef)) + .forEach((ef) => uneffect(ef)), + clear: "all", + tracking: "Garbo", + }, + { + name: "Turn in FunFunds", + ready: () => get("_stenchAirportToday") && itemAmount($item`FunFunds™`) >= 20, + completed: () => have($item`one-day ticket to Dinseylandfill`), + do: () => + buy($coinmaster`The Dinsey Company Store`, 1, $item`one-day ticket to Dinseylandfill`), + tracking: "Garbo", + }, + { + name: "PvP Closet Safety 2", + ready: () => args.pvp && get("autoSatisfyWithCloset") && !args.safepvp, + completed: () => toBoolean(get("_safetyCloset2")), + do: () => pvpCloset(2), + }, + { + name: "PvP", + ready: () => doneAdventuring(), + completed: () => pvpAttacksLeft() === 0 || !hippyStoneBroken(), + do: (): void => { + cliExecute("unequip"); + cliExecute("UberPvPOptimizer"); + cliExecute(`PVP_MAB target=${args.pvpTarget}`); }, - { - name: "Offhand Remarkable", - ready: () => have($item`august scepter`), - completed: () => - !have($skill`Aug. 13th: Left/Off Hander's Day!`) || - have($effect`Offhand Remarkable`) || - get("_aug13Cast", false), - do: () => useSkill($skill`Aug. 13th: Left/Off Hander's Day!`), + }, + { + name: "Stooper", + ready: () => + myInebriety() === inebrietyLimit() && + have($item`tiny stillsuit`) && + get("familiarSweat") >= 300, + completed: () => !have($familiar`Stooper`) || stooperDrunk(), + do: () => { + useFamiliar($familiar`Stooper`); + cliExecute("drink stillsuit distillate"); }, - { - name: "PvP Closet Safety 3", - ready: () => args.pvp && get("autoSatisfyWithCloset") && !args.safepvp, - completed: () => toBoolean(get("_safetyCloset3")), - do: () => pvpCloset(3), + }, + { + name: "Nightcap", + ready: () => doneAdventuring(), + completed: () => totallyDrunk(), + do: () => cliExecute("CONSUME NIGHTCAP"), + }, + { + name: "Smoke em if you got em", + ready: () => get("getawayCampsiteUnlocked"), + completed: () => !have($item`stick of firewood`), + do: (): void => { + while (have($item`stick of firewood`)) { + setProperty( + "choiceAdventure1394", + `1&message=${smoke} Thanks Seraphiii for writing Candywrapper!`, + ); + use(1, $item`campfire smoke`); + print(`Smoked ${smoke} firewoods!`); + smoke = smoke + 1; + } }, - { - name: "Item Cleanup", - // eslint-disable-next-line libram/verify-constants - completed: () => get("_cleanupToday", false) || args.itemcleanup === "", - do: (): void => { - cliExecute(`${args.itemcleanup}`); - cliExecute("set _cleanupToday = true"); - }, - clear: "all", - tracking: "Item Cleanup", + }, + { + name: "Offhand Remarkable", + ready: () => have($item`august scepter`), + completed: () => + !have($skill`Aug. 13th: Left/Off Hander's Day!`) || + have($effect`Offhand Remarkable`) || + get("_aug13Cast", false), + do: () => useSkill($skill`Aug. 13th: Left/Off Hander's Day!`), + }, + { + name: "PvP Closet Safety 3", + ready: () => args.pvp && get("autoSatisfyWithCloset") && !args.safepvp, + completed: () => toBoolean(get("_safetyCloset3")), + do: () => pvpCloset(3), + }, + { + name: "Item Cleanup", + // eslint-disable-next-line libram/verify-constants + completed: () => get("_cleanupToday", false) || args.itemcleanup === "", + do: (): void => { + cliExecute(`${args.itemcleanup}`); + cliExecute("set _cleanupToday = true"); }, - { - name: "Pajamas", - completed: () => have($item`burning cape`), - acquire: [ - { item: $item`clockwork maid`, price: 7 * get("valueOfAdventure"), optional: true }, - { item: $item`burning cape` }, - ], - do: (): void => { - if (have($item`clockwork maid`)) { - use($item`clockwork maid`); - } - pajamas = true; - }, - outfit: () => ({ - familiar: - $familiars`Trick-or-Treating Tot, Left-Hand Man, Disembodied Hand, Grey Goose`.find( - (fam) => have(fam), - ), - modifier: `adventures ${sasqBonus} bonus Sasq™ watch, ${ratskinBonus} bonus ratskin pajama pants ${ - args.pvp ? ", 0.3 fites" : "" - }`, - }), + clear: "all", + tracking: "Item Cleanup", + }, + { + name: "Pajamas", + completed: () => have($item`burning cape`), + acquire: [ + { item: $item`clockwork maid`, price: 7 * get("valueOfAdventure"), optional: true }, + { item: $item`burning cape` }, + ], + do: (): void => { + if (have($item`clockwork maid`)) { + use($item`clockwork maid`); + } + pajamas = true; }, - { - name: "Alert-No Nightcap", - ready: () => !doneAdventuring(), - completed: () => stooperDrunk(), - do: (): void => { - const targetAdvs = 100 - numericModifier("adventures"); - print("robot completed, but did not overdrink.", "red"); - if (targetAdvs < myAdventures() && targetAdvs > 0) - print( - `Rerun with fewer than ${targetAdvs} adventures for smol to handle your diet`, - "red", - ); - else print("Something went wrong.", "red"); - }, + outfit: () => ({ + familiar: + $familiars`Trick-or-Treating Tot, Left-Hand Man, Disembodied Hand, Grey Goose`.find( + (fam) => have(fam), + ), + modifier: `adventures ${sasqBonus} bonus Sasq™ watch, ${ratskinBonus} bonus ratskin pajama pants ${ + args.pvp ? ", 0.3 fites" : "" + }`, + }), + }, + { + name: "Alert-No Nightcap", + ready: () => !doneAdventuring(), + completed: () => stooperDrunk(), + do: (): void => { + const targetAdvs = 100 - numericModifier("adventures"); + print("robot completed, but did not overdrink.", "red"); + if (targetAdvs < myAdventures() && targetAdvs > 0) + print( + `Rerun with fewer than ${targetAdvs} adventures for smol to handle your diet`, + "red", + ); + else print("Something went wrong.", "red"); }, - ] - } + }, + ], + }; } diff --git a/src/tasks/repeatableTasks.ts b/src/tasks/repeatableTasks.ts index 5de408b..1d23edd 100644 --- a/src/tasks/repeatableTasks.ts +++ b/src/tasks/repeatableTasks.ts @@ -1,9 +1,65 @@ import { CombatStrategy } from "grimoire-kolmafia"; -import { availableAmount, canAdventure, cliExecute, equip, Familiar, fullnessLimit, getCampground, getClanName, getWorkshed, guildStoreAvailable, haveEffect, hippyStoneBroken, holiday, inebrietyLimit, mallPrice, myAdventures, myClass, myDaycount, myFamiliar, myFullness, myHp, myInebriety, myLevel, myMaxhp, myPrimestat, mySpleenUse, restoreHp, retrieveItem, spleenLimit, toInt, toSkill, use, useFamiliar, useSkill, visitUrl } from "kolmafia"; -import { $class, $effect, $effects, $familiar, $item, $items, $location, $skill, $stat, AprilingBandHelmet, AsdonMartin, get, getTodaysHolidayWanderers, have, Macro, set, uneffect } from "libram"; +import { + availableAmount, + canAdventure, + cliExecute, + equip, + Familiar, + fullnessLimit, + getCampground, + getClanName, + getWorkshed, + guildStoreAvailable, + haveEffect, + hippyStoneBroken, + holiday, + inebrietyLimit, + mallPrice, + myAdventures, + myClass, + myDaycount, + myFamiliar, + myFullness, + myHp, + myInebriety, + myLevel, + myMaxhp, + myPrimestat, + mySpleenUse, + restoreHp, + retrieveItem, + spleenLimit, + toInt, + toSkill, + use, + useFamiliar, + useSkill, + visitUrl, +} from "kolmafia"; +import { + $class, + $effect, + $effects, + $familiar, + $item, + $items, + $location, + $skill, + $stat, + AprilingBandHelmet, + AsdonMartin, + get, + getTodaysHolidayWanderers, + have, + Macro, + set, + uneffect, +} from "libram"; + +import { args } from "../args"; + import { Task } from "./structure"; import { getGarden, maxBase, nextCyberZone, stooperDrunk, totallyDrunk } from "./utils"; -import { args } from "../args"; const bestFam = () => famCheck($familiar`Pocket Professor`) @@ -21,7 +77,7 @@ function famCheck(fam: Familiar): boolean { const doSmol = args.smol ? true : false; export function postRunQuests(): Task[] { -return [ + return [ { name: "Whitelist VIP Clan", completed: () => !args.clan || getClanName().toLowerCase() === args.clan.toLowerCase(), @@ -93,7 +149,7 @@ return [ } cliExecute(`familiar ${bestFam().name}`); cliExecute("familiar Shorter-Order Cook"); - set("_familiarPrepped", true) + set("_familiarPrepped", true); }, }, { @@ -259,12 +315,11 @@ return [ visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); }, }, - ] + ]; } let duffo = false; - export function preRunQuests(): Task[] { return [ { @@ -295,7 +350,10 @@ export function preRunQuests(): Task[] { { name: "LGR Seed", ready: () => - have($item`lucky gold ring`) && have($item`one-day ticket to Dinseylandfill`) && !args.garboascend.includes("penguin") && !args.cs, + have($item`lucky gold ring`) && + have($item`one-day ticket to Dinseylandfill`) && + !args.garboascend.includes("penguin") && + !args.cs, completed: () => get("_stenchAirportToday") || get("stenchAirportAlways"), do: () => use($item`one-day ticket to Dinseylandfill`), tracking: "Garbo", @@ -309,7 +367,7 @@ export function preRunQuests(): Task[] { visitUrl("peevpee.php?place=fight"); }, }, - ] + ]; } let garboDone = false; @@ -334,46 +392,46 @@ export function noBarf(): Task[] { garboDone = true; }, }, - ] + ]; } export function garboWeen(): Task[] { return [ - { - name: "Freecandy time", - ready: () => holiday().includes("Halloween"), - completed: () => myAdventures() / 5 < 1, - prepare: () => uneffect($effect`Beaten Up`), - do: (): void => { - if (have($familiar`Trick-or-Treating Tot`)) cliExecute("familiar Trick-or-Treating Tot"); - else if (have($familiar`Red-Nosed Snapper`)) cliExecute("familiar snapper"); - cliExecute(`freecandy ${myAdventures()}`); - }, - clear: "all", - tracking: "Freecandy", - limit: { tries: 1 }, //this will run again after installing CMC, by magic - }, - { - name: "Super Nightcap", - ready: () => have($item`Drunkula's wineglass`) && holiday().includes("Halloween"), - completed: () => totallyDrunk(), - do: () => cliExecute(`CONSUME NIGHTCAP`), + { + name: "Freecandy time", + ready: () => holiday().includes("Halloween"), + completed: () => myAdventures() / 5 < 1, + prepare: () => uneffect($effect`Beaten Up`), + do: (): void => { + if (have($familiar`Trick-or-Treating Tot`)) cliExecute("familiar Trick-or-Treating Tot"); + else if (have($familiar`Red-Nosed Snapper`)) cliExecute("familiar snapper"); + cliExecute(`freecandy ${myAdventures()}`); }, - { - name: "Freecandy Drunk", - ready: () => holiday().includes("Halloween"), - completed: () => Math.floor(myAdventures() / 5) === 0, - prepare: () => uneffect($effect`Beaten Up`), - do: (): void => { - useFamiliar($familiar`Red-Nosed Snapper`); - cliExecute(`freeCandy ${myAdventures()}`); - }, - clear: "all", - tracking: "Freecandy", - limit: { tries: 1 }, //this will run again after installing CMC, by magic + clear: "all", + tracking: "Freecandy", + limit: { tries: 1 }, //this will run again after installing CMC, by magic + }, + { + name: "Super Nightcap", + ready: () => have($item`Drunkula's wineglass`) && holiday().includes("Halloween"), + completed: () => totallyDrunk(), + do: () => cliExecute(`CONSUME NIGHTCAP`), + }, + { + name: "Freecandy Drunk", + ready: () => holiday().includes("Halloween"), + completed: () => Math.floor(myAdventures() / 5) === 0, + prepare: () => uneffect($effect`Beaten Up`), + do: (): void => { + useFamiliar($familiar`Red-Nosed Snapper`); + cliExecute(`freeCandy ${myAdventures()}`); }, - ] - } + clear: "all", + tracking: "Freecandy", + limit: { tries: 1 }, //this will run again after installing CMC, by magic + }, + ]; +} export function chrono(): Task[] { return [ @@ -391,7 +449,11 @@ export function chrono(): Task[] { }, { name: "Super Nightcap", - ready: () => have($item`Drunkula's wineglass`) && args.chrono && canAdventure($location`The Primordial Stew`) && myDaycount() > 1, + ready: () => + have($item`Drunkula's wineglass`) && + args.chrono && + canAdventure($location`The Primordial Stew`) && + myDaycount() > 1, completed: () => totallyDrunk(), do: () => cliExecute(`CONSUME NIGHTCAP`), }, @@ -407,10 +469,10 @@ export function chrono(): Task[] { tracking: "Chrono", limit: { tries: 1 }, //this will run again after installing CMC, by magic }, - ] + ]; } - export function crimbo(): Task[] { +export function crimbo(): Task[] { return [ { name: "Crimbo Time", @@ -426,7 +488,8 @@ export function chrono(): Task[] { }, { name: "Super Nightcap", - ready: () => have($item`Drunkula's wineglass`) && holiday().includes("Halloween") && myDaycount() > 1, + ready: () => + have($item`Drunkula's wineglass`) && holiday().includes("Halloween") && myDaycount() > 1, completed: () => totallyDrunk(), do: () => cliExecute(`CONSUME NIGHTCAP`), }, @@ -442,5 +505,5 @@ export function chrono(): Task[] { tracking: "Crimbo", limit: { tries: 1 }, //this will run again after installing CMC, by magic }, - ] + ]; } diff --git a/src/tasks/structure.ts b/src/tasks/structure.ts index f7091b4..dc032ca 100644 --- a/src/tasks/structure.ts +++ b/src/tasks/structure.ts @@ -11,11 +11,10 @@ export type Quest = BaseQuest; // eslint-disable-next-line no-restricted-syntax export enum Leg { Aftercore = 0, - Run = 1 + Run = 1, } export function getCurrentLeg(): number { - if (myDaycount() === 1) - return Leg.Run; + if (myDaycount() === 1) return Leg.Run; return Leg.Aftercore; } diff --git a/src/tasks/utils.ts b/src/tasks/utils.ts index f568141..f5f4751 100644 --- a/src/tasks/utils.ts +++ b/src/tasks/utils.ts @@ -28,6 +28,7 @@ import { putCloset, retrieveItem, spleenLimit, + storageAmount, toItem, urlEncode, use, @@ -40,6 +41,7 @@ import { $items, $location, $phylum, + $skill, Kmail as _Kmail, gameDay, get, @@ -157,24 +159,28 @@ export function bestFam(mob?: Monster) { } export function nextCyberZone(): Location { - - if(lastChoice() === 1546) { - set("_cyberRealm1Done", true) + if (lastChoice() === 1546) { + set("_cyberRealm1Done", true); } - if(lastChoice() === 1548) { - set("_cyberRealm2Done", true) + if (lastChoice() === 1548) { + set("_cyberRealm2Done", true); } - if(lastChoice() === 1550) { - set("_cyberRealm3Done", true) + if (lastChoice() === 1550) { + set("_cyberRealm3Done", true); } - const realmChoice = () => !(get("_cyberRealm1Done").includes("true")) - ? $location`Cyberzone 1` - : !get("_cyberRealm2Done").includes("true") - ? $location`Cyberzone 2` - : !get("_cyberRealm3Done").includes("true") - ? $location`Cyberzone 3` - : $location`none`; - print(`Choosing ${realmChoice()} because turns spent ${realmChoice().turnsSpent - 19 * (myDaycount() - 1)}`); + const realmChoice = () => + !get("_cyberRealm1Done").includes("true") + ? $location`Cyberzone 1` + : !get("_cyberRealm2Done").includes("true") + ? $location`Cyberzone 2` + : !get("_cyberRealm3Done").includes("true") + ? $location`Cyberzone 3` + : $location`none`; + print( + `Choosing ${realmChoice()} because turns spent ${ + realmChoice().turnsSpent - 19 * (myDaycount() - 1) + }`, + ); return realmChoice(); } @@ -268,14 +274,7 @@ export interface Kmail { export function notifyVoters(): void { if (get("_kmailSentToday").includes("true")) return; - const recipients = [ - "Datris", - "ange1ade", - "miroto1998", - "tissen", - "nannachi", - "Mandoline", - ]; + const recipients = ["Datris", "ange1ade", "miroto1998", "tissen", "nannachi", "Mandoline"]; const message = `Voter Monster Today is ${get("_voteMonster")}`; @@ -410,3 +409,28 @@ function isHalloweenWorthDoing(): boolean { const freeFightValue = have($familiar`Red-Nosed Snapper`) ? 2000 : 1100; return baseAdventureValue() + freeFightValue > get("valueOfAdventure"); } + +export function shouldWeOverdrink(): boolean { + const overdrinkValue = + get("valueOfAdventure") * (110 - 5.5 * 5) + mallPrice($item`Sacramento wine`) * 5; + const numToCleanse = 5; + const sweatpants = have($item`designer sweatpants`) ? 3 : 0; + const drinking = have($skill`Drinking to Drink`) ? 1 : 0; + const doghair = storageAmount($item`synthetic dog hair pill`) >= 1 ? 1 : 0; + const checkMelange = () => + get("valueOfAdventure") * 45 > mallPrice($item`spice melange`) && sweatpants < 3; + const melange = checkMelange() ? 3 : 0; + const melangeCost = () => (checkMelange() ? mallPrice($item`spice melange`) : 0); + + if (sweatpants + drinking + doghair + melange >= numToCleanse && overdrinkValue > 0) { + return true; + } else { + const sobrie = 1; + if ( + sweatpants + drinking + doghair + sobrie + melange >= numToCleanse && + overdrinkValue - mallPrice($item`cuppa Sobrie tea`) - melangeCost() > 0 + ) { + return true; + } else return false; + } +} From ae876f4752ca294f3ab160aef3dbf1b233d317ad Mon Sep 17 00:00:00 2001 From: Ignose Date: Mon, 20 Jan 2025 15:38:53 -0500 Subject: [PATCH 11/24] Save Cyberrealm for when we have the adventures --- src/tasks/4 postrunleg.ts | 4 ++-- src/tasks/repeatableTasks.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tasks/4 postrunleg.ts b/src/tasks/4 postrunleg.ts index 122de51..e243323 100644 --- a/src/tasks/4 postrunleg.ts +++ b/src/tasks/4 postrunleg.ts @@ -173,7 +173,7 @@ export function PostRunQuests(): Quest { { name: "Emergency Drink Part 2", ready: () => myAdventures() === 0 && myInebriety() < 11, - completed: () => myAdventures() > 0 || myInebriety() >= 11, + completed: () => myAdventures() > 0 || myInebriety() >= 11 || (!have($item`astral pilsner`) && !have($item`astral six-pack`)), prepare: () => { if (have($item`astral six-pack`)) use($item`astral six-pack`); }, @@ -332,7 +332,7 @@ export function PostRunQuests(): Quest { { name: "Emergency Drink Part 3", ready: () => myAdventures() < 40 && myInebriety() < 11, - completed: () => myAdventures() > 40 || myInebriety() >= 11, + completed: () => myAdventures() > 40 || myInebriety() >= 11|| (!have($item`astral pilsner`) && !have($item`astral six-pack`)), prepare: () => { if (have($item`astral six-pack`)) use($item`astral six-pack`); }, diff --git a/src/tasks/repeatableTasks.ts b/src/tasks/repeatableTasks.ts index 1d23edd..03ae7a4 100644 --- a/src/tasks/repeatableTasks.ts +++ b/src/tasks/repeatableTasks.ts @@ -253,7 +253,7 @@ export function postRunQuests(): Task[] { }, { name: "Run CyberRealm", - ready: () => mallPrice($item`1`) > 1_000, + ready: () => mallPrice($item`1`) > 1_000 && myAdventures() > 60, prepare: () => { $effects`Astral Shell, Elemental Saucesphere, Scarysauce`.forEach((ef) => { if (!have(ef)) useSkill(toSkill(ef)); From 5e6a790c5223ee074be4f02376ed09dccfe2e9eb Mon Sep 17 00:00:00 2001 From: Ignose Date: Wed, 22 Jan 2025 05:28:16 -0500 Subject: [PATCH 12/24] Ensure we get a meatcar --- src/tasks/1 aftercoreleg.ts | 6 +++--- src/tasks/4 postrunleg.ts | 8 +++++++- src/tasks/utils.ts | 2 ++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/tasks/1 aftercoreleg.ts b/src/tasks/1 aftercoreleg.ts index 03ec660..2f8015a 100644 --- a/src/tasks/1 aftercoreleg.ts +++ b/src/tasks/1 aftercoreleg.ts @@ -42,7 +42,7 @@ import { args } from "../args"; import { chrono, crimbo, garboWeen, noBarf, postRunQuests, preRunQuests } from "./repeatableTasks"; import { Quest } from "./structure"; -import { maxBase, pvpCloset, stooperDrunk, totallyDrunk } from "./utils"; +import { maxBase, pingu, pvpCloset, stooperDrunk, totallyDrunk } from "./utils"; const doSmol = args.smol ? true : false; const doCS = args.cs ? true : false; @@ -103,7 +103,7 @@ export function AftercoreQuest(): Quest { name: "Garbo", completed: () => stooperDrunk() || myAdventures() === 0, prepare: () => uneffect($effect`Beaten Up`), - do: () => cliExecute(`${args.garboascend}`), + do: () => cliExecute(`${args.garboascend} ${pingu()}`), post: () => { if (myAdventures() === 0) $effects`Power Ballad of the Arrowsmith, Stevedave's Shanty of Superiority, The Moxious Madrigal, The Magical Mojomuscular Melody, Aloysius' Antiphon of Aptitude, Ur-Kel's Aria of Annoyance` @@ -186,7 +186,7 @@ export function AftercoreQuest(): Quest { ready: () => have($item`Drunkula's wineglass`), prepare: () => uneffect($effect`Beaten Up`), completed: () => myAdventures() === 0, - do: () => cliExecute(`${args.garboascend}`), + do: () => cliExecute(`${args.garboascend} ${pingu()}`), post: () => $effects`Power Ballad of the Arrowsmith, Stevedave's Shanty of Superiority, The Moxious Madrigal, The Magical Mojomuscular Melody, Aloysius' Antiphon of Aptitude, Ur-Kel's Aria of Annoyance` .filter((ef) => have(ef)) diff --git a/src/tasks/4 postrunleg.ts b/src/tasks/4 postrunleg.ts index e243323..6f0a8e5 100644 --- a/src/tasks/4 postrunleg.ts +++ b/src/tasks/4 postrunleg.ts @@ -54,6 +54,7 @@ import { doneAdventuring, haveAll, maxBase, + pingu, pvpCloset, stooperDrunk, totallyDrunk, @@ -99,6 +100,11 @@ export function PostRunQuests(): Quest { completed: () => !have($effect`Feeling Lost`), do: () => uneffect($effect`Feeling Lost`), }, + { + name: "Hey kids, let's take a trip to the beach", + completed: () => have($item`bitchin' meatcar`) || have($item`Desert Bus pass`), + do: () => cliExecute("acquire bitchin"), + }, { name: "Sober Up", ready: () => args.smol, @@ -353,7 +359,7 @@ export function PostRunQuests(): Quest { name: "Garbo", completed: () => myAdventures() === 0 || stooperDrunk(), prepare: () => uneffect($effect`Beaten Up`), - do: () => cliExecute(`${args.garbo}`), + do: () => cliExecute(`${args.garbo} ${pingu()}`), post: () => $effects`Power Ballad of the Arrowsmith, Stevedave's Shanty of Superiority, The Moxious Madrigal, The Magical Mojomuscular Melody, Aloysius' Antiphon of Aptitude, Ur-Kel's Aria of Annoyance` .filter((ef) => have(ef)) diff --git a/src/tasks/utils.ts b/src/tasks/utils.ts index f5f4751..40d31ee 100644 --- a/src/tasks/utils.ts +++ b/src/tasks/utils.ts @@ -434,3 +434,5 @@ export function shouldWeOverdrink(): boolean { } else return false; } } + +export const pingu = () => args.smol || args.robot || args.casual ? "penguin" : ""; From 2b5e628c175e41e4d1cabab7494070ac062c537c Mon Sep 17 00:00:00 2001 From: Ignose Date: Wed, 22 Jan 2025 06:03:34 -0500 Subject: [PATCH 13/24] Cyberrealm tweaks --- src/tasks/repeatableTasks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/repeatableTasks.ts b/src/tasks/repeatableTasks.ts index 03ae7a4..a6cccf6 100644 --- a/src/tasks/repeatableTasks.ts +++ b/src/tasks/repeatableTasks.ts @@ -253,7 +253,7 @@ export function postRunQuests(): Task[] { }, { name: "Run CyberRealm", - ready: () => mallPrice($item`1`) > 1_000 && myAdventures() > 60, + ready: () => mallPrice($item`1`) > 2_000 && myAdventures() > 60 && myInebriety() < inebrietyLimit(), prepare: () => { $effects`Astral Shell, Elemental Saucesphere, Scarysauce`.forEach((ef) => { if (!have(ef)) useSkill(toSkill(ef)); From aba945ba3d26b45e1841f54610b079474eed18c0 Mon Sep 17 00:00:00 2001 From: Ignose Date: Wed, 29 Jan 2025 16:43:30 -0500 Subject: [PATCH 14/24] Many changes --- src/tasks/1 aftercoreleg.ts | 81 ++++++++++++++++++++++++++++++++++++ src/tasks/4 postrunleg.ts | 11 +++++ src/tasks/repeatableTasks.ts | 2 +- src/tasks/utils.ts | 3 +- 4 files changed, 95 insertions(+), 2 deletions(-) diff --git a/src/tasks/1 aftercoreleg.ts b/src/tasks/1 aftercoreleg.ts index 2f8015a..7c53590 100644 --- a/src/tasks/1 aftercoreleg.ts +++ b/src/tasks/1 aftercoreleg.ts @@ -2,9 +2,11 @@ import { CombatStrategy } from "grimoire-kolmafia"; import { buy, cliExecute, + getWorkshed, haveEquipped, hippyStoneBroken, inebrietyLimit, + Item, itemAmount, mallPrice, myAdventures, @@ -18,6 +20,7 @@ import { restoreMp, retrieveItem, toBoolean, + use, useFamiliar, visitUrl, } from "kolmafia"; @@ -33,7 +36,10 @@ import { getTodaysHolidayWanderers, have, Macro, + maxBy, + TakerSpace, TrainSet, + Tuple, uneffect, } from "libram"; import { Cycle, setConfiguration, Station } from "libram/dist/resources/2022/TrainSet"; @@ -43,6 +49,71 @@ import { args } from "../args"; import { chrono, crimbo, garboWeen, noBarf, postRunQuests, preRunQuests } from "./repeatableTasks"; import { Quest } from "./structure"; import { maxBase, pingu, pvpCloset, stooperDrunk, totallyDrunk } from "./utils"; +import { garboValue } from "../engine/profits"; + +const RESOURCES = ["Spice", "Rum", "Anchor", "Mast", "Silk", "Gold"] as const; +export type Resource = (typeof RESOURCES)[number]; + +export type Recipe = Tuple; +const RECIPES = new Map([ + [$item`deft pirate hook`, [0, 0, 1, 1, 0, 1]], + [$item`iron tricorn hat`, [0, 0, 2, 1, 0, 0]], + [$item`jolly roger flag`, [0, 1, 0, 1, 1, 0]], + [$item`sleeping profane parrot`, [15, 3, 0, 0, 2, 1]], + [$item`pirrrate's currrse`, [2, 2, 0, 0, 0, 0]], + [$item`tankard of spiced rum`, [1, 2, 0, 0, 0, 0]], + [$item`packaged luxury garment`, [0, 0, 0, 0, 3, 2]], + [$item`harpoon`, [0, 0, 0, 2, 0, 0]], + [$item`chili powder cutlass`, [5, 0, 1, 0, 0, 0]], + [$item`cursed Aztec tamale`, [2, 0, 0, 0, 0, 0]], + [$item`jolly roger tattoo kit`, [0, 6, 1, 1, 0, 6]], + [$item`golden pet rock`, [0, 0, 0, 0, 0, 7]], + [$item`groggles`, [0, 6, 0, 0, 0, 0]], + [$item`pirate dinghy`, [0, 0, 1, 1, 1, 0]], + [$item`anchor bomb`, [0, 1, 3, 1, 0, 1]], + [$item`silky pirate drawers`, [0, 0, 0, 0, 2, 0]], + [$item`spices`, [1, 0, 0, 0, 0, 0]], +]); + +/** + * @returns A copy of our map of all recipes + */ +export function allRecipes(): Map { + return new Map( + [...RECIPES.entries()].map(([item, recipe]) => [item, [...recipe]]), + ); +} + +export function affordableRecipes(): Item[] { + const recipes = allRecipes(); // Get all recipes + print(`Recipes Map: ${recipes}`); + + const result = [...recipes.keys()].filter((item) => TakerSpace.canMake(item)); + print(`Final affordable items array: ${result}`); + + return result; +} + +function takerSpaceOptimizer(): boolean { + let recipes = affordableRecipes(); + print(`Recipes after affordableRecipes: ${recipes}`); + + while (recipes.length > 0) { + print(`Current recipe list: ${recipes}`); + + // Find the best item to craft based on garboValue + const bestRecipe = maxBy(recipes, garboValue); + print(`Best recipe to craft: ${bestRecipe}`); + + // Craft the selected recipe + TakerSpace.make(bestRecipe); + + // Recompute the list of craftable recipes after crafting + recipes = affordableRecipes(); + } + + return true; +} const doSmol = args.smol ? true : false; const doCS = args.cs ? true : false; @@ -77,6 +148,16 @@ export function AftercoreQuest(): Quest { have($item`Calzone of Legend`)) || myDaycount() === 1, tasks: [ + { + name: "Takerspace", + ready: () => getWorkshed() === $item`TakerSpace letter of Marque` && !get("_workshedItemUsed"), + completed: () => getWorkshed() === $item`model train set`, + do: () => { + visitUrl("campground.php?action=workshed"); + takerSpaceOptimizer() + use($item`model train set`); + }, + }, { name: "Pre-Run Photobooth", ready: () => have($item`Clan VIP Lounge key`) && get("_photoBoothEquipment", 0) === 0, diff --git a/src/tasks/4 postrunleg.ts b/src/tasks/4 postrunleg.ts index 6f0a8e5..d1ae379 100644 --- a/src/tasks/4 postrunleg.ts +++ b/src/tasks/4 postrunleg.ts @@ -4,6 +4,7 @@ import { cliExecute, drink, Effect, + getWorkshed, hippyStoneBroken, inebrietyLimit, itemAmount, @@ -26,6 +27,7 @@ import { use, useFamiliar, useSkill, + visitUrl, wait, } from "kolmafia"; import { @@ -451,6 +453,15 @@ export function PostRunQuests(): Quest { clear: "all", tracking: "Item Cleanup", }, + { + name: "Takerspace", + ready: () => getWorkshed() === $item`model train set` && !get("_workshedItemUsed"), + completed: () => getWorkshed() === $item`TakerSpace letter of Marque`, + do: () => { + use($item`TakerSpace letter of Marque`); + visitUrl("campground.php?action=workshed"); + }, + }, { name: "Pajamas", completed: () => have($item`burning cape`), diff --git a/src/tasks/repeatableTasks.ts b/src/tasks/repeatableTasks.ts index a6cccf6..c43d7b2 100644 --- a/src/tasks/repeatableTasks.ts +++ b/src/tasks/repeatableTasks.ts @@ -253,7 +253,7 @@ export function postRunQuests(): Task[] { }, { name: "Run CyberRealm", - ready: () => mallPrice($item`1`) > 2_000 && myAdventures() > 60 && myInebriety() < inebrietyLimit(), + ready: () => mallPrice($item`1`) > 3_000 && myAdventures() > 60 && myInebriety() < inebrietyLimit(), prepare: () => { $effects`Astral Shell, Elemental Saucesphere, Scarysauce`.forEach((ef) => { if (!have(ef)) useSkill(toSkill(ef)); diff --git a/src/tasks/utils.ts b/src/tasks/utils.ts index 40d31ee..9886618 100644 --- a/src/tasks/utils.ts +++ b/src/tasks/utils.ts @@ -1,4 +1,5 @@ import { + canAdventure, canEquip, chew, cliExecute, @@ -435,4 +436,4 @@ export function shouldWeOverdrink(): boolean { } } -export const pingu = () => args.smol || args.robot || args.casual ? "penguin" : ""; +export const pingu = () => canAdventure($location`The Copperhead Club`) ? "penguin" : ""; From 25969323fb06906e92300bfc68027441901ea70e Mon Sep 17 00:00:00 2001 From: Ignose Date: Wed, 12 Feb 2025 10:51:01 -0500 Subject: [PATCH 15/24] better nobarf logic for garboween --- src/tasks/repeatableTasks.ts | 19 +------------------ src/tasks/utils.ts | 2 +- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/src/tasks/repeatableTasks.ts b/src/tasks/repeatableTasks.ts index c43d7b2..fddc6c2 100644 --- a/src/tasks/repeatableTasks.ts +++ b/src/tasks/repeatableTasks.ts @@ -3,7 +3,6 @@ import { availableAmount, canAdventure, cliExecute, - equip, Familiar, fullnessLimit, getCampground, @@ -18,7 +17,6 @@ import { myAdventures, myClass, myDaycount, - myFamiliar, myFullness, myHp, myInebriety, @@ -52,7 +50,6 @@ import { getTodaysHolidayWanderers, have, Macro, - set, uneffect, } from "libram"; @@ -138,20 +135,6 @@ export function postRunQuests(): Task[] { ), limit: { tries: 5 }, }, - { - name: "CyberRealm: Prepare Familiar", - ready: () => !get("_familiarPrepped").includes("true"), - completed: () => myFamiliar() === $familiar`Shorter-Order Cook`, - do: () => { - if (have($familiar`Shorter-Order Cook`)) { - cliExecute("familiar Shorter-Order Cook"); - equip($familiar`Shorter-Order Cook`, $item`blue plate`); - } - cliExecute(`familiar ${bestFam().name}`); - cliExecute("familiar Shorter-Order Cook"); - set("_familiarPrepped", true); - }, - }, { name: "Restore HP", completed: () => myHp() > 0.5 * myMaxhp(), @@ -388,7 +371,7 @@ export function noBarf(): Task[] { ready: () => holiday().includes("Halloween") || args.crimbo || args.chrono, completed: () => garboDone, do: (): void => { - cliExecute(`${args.garboascend} nodiet nobarf target="witchess knight"`); + cliExecute(`garbo nodiet nobarf target="witchess knight"`); garboDone = true; }, }, diff --git a/src/tasks/utils.ts b/src/tasks/utils.ts index 9886618..03ec5af 100644 --- a/src/tasks/utils.ts +++ b/src/tasks/utils.ts @@ -275,7 +275,7 @@ export interface Kmail { export function notifyVoters(): void { if (get("_kmailSentToday").includes("true")) return; - const recipients = ["Datris", "ange1ade", "miroto1998", "tissen", "nannachi", "Mandoline"]; + const recipients = ["Datris", "ange1ade", "miroto1998", "tissen", "nannachi", "Mandoline", "Lucasyeo"]; const message = `Voter Monster Today is ${get("_voteMonster")}`; From 0209c9297b3beb063758e0a6b9b6230dd9b0e8c6 Mon Sep 17 00:00:00 2001 From: Ignose Date: Wed, 12 Feb 2025 11:24:46 -0500 Subject: [PATCH 16/24] better garboween logic --- src/tasks/repeatableTasks.ts | 20 ++++++++++++++++---- src/tasks/utils.ts | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/tasks/repeatableTasks.ts b/src/tasks/repeatableTasks.ts index fddc6c2..f81916a 100644 --- a/src/tasks/repeatableTasks.ts +++ b/src/tasks/repeatableTasks.ts @@ -56,7 +56,7 @@ import { import { args } from "../args"; import { Task } from "./structure"; -import { getGarden, maxBase, nextCyberZone, stooperDrunk, totallyDrunk } from "./utils"; +import { getGarden, maxBase, nextCyberZone, pantogram, pantogramReady, stooperDrunk, totallyDrunk } from "./utils"; const bestFam = () => famCheck($familiar`Pocket Professor`) @@ -353,7 +353,8 @@ export function preRunQuests(): Task[] { ]; } -let garboDone = false; +let garboDone1 = false; +let garboDone2 = false export function noBarf(): Task[] { return [ @@ -366,13 +367,24 @@ export function noBarf(): Task[] { myInebriety() >= inebrietyLimit(), do: () => cliExecute("consume ALL"), }, + { + name: "Pantogramming", + ready: () => pantogramReady(), + completed: () => pantogram(), + do: () => pantogram(), + }, { name: "Garbo Nobarf", ready: () => holiday().includes("Halloween") || args.crimbo || args.chrono, - completed: () => garboDone, + completed: () => (myDaycount() > 1 && garboDone1) || + (myDaycount() === 1 && garboDone2), do: (): void => { cliExecute(`garbo nodiet nobarf target="witchess knight"`); - garboDone = true; + if(myDaycount() > 1) { + garboDone1 = true; + } else { + garboDone2 = true; + } }, }, ]; diff --git a/src/tasks/utils.ts b/src/tasks/utils.ts index 03ec5af..811a102 100644 --- a/src/tasks/utils.ts +++ b/src/tasks/utils.ts @@ -22,6 +22,7 @@ import { myFamiliar, myFullness, myInebriety, + myPrimestat, mySpleenUse, outfitPieces, outfitTreats, @@ -49,6 +50,7 @@ import { getBanishedMonsters, have, maxBy, + Pantogram, set, Snapper, sum, @@ -437,3 +439,35 @@ export function shouldWeOverdrink(): boolean { } export const pingu = () => canAdventure($location`The Copperhead Club`) ? "penguin" : ""; + +export function pantogramReady(): boolean { + if (!Pantogram.have() || Pantogram.havePants()) return false; + const pantogramValue = 100 * myAdventures(); + + const cloverPrice = Math.min( + ...$items`ten-leaf clover, disassembled clover`.map((item) => + mallPrice(item), + ), + ); + if (cloverPrice + mallPrice($item`porquoise`) > pantogramValue) { + return false; + } + retrieveItem($item`porquoise`, 1); + if (!have($item`porquoise`)) return false; + return true; +} + +export function pantogram(): boolean { + if (!Pantogram.have() || Pantogram.havePants()) return true; + retrieveItem($item`ten-leaf clover`); + retrieveItem($item`bubblin' crude`); + Pantogram.makePants( + myPrimestat().toString(), + "Sleaze Resistance: 2", + "MP Regen Max: 15", + "Drops Items: true", + "Meat Drop: 60", + ); + return true; +} + From b66b2659ed69b1f8eb55fa3c90f31f5334eea25c Mon Sep 17 00:00:00 2001 From: Ignose Date: Thu, 27 Feb 2025 13:56:35 -0500 Subject: [PATCH 17/24] Better profit tracking --- src/tasks/1 aftercoreleg.ts | 10 ++++++++++ src/tasks/3 runleg.ts | 2 +- src/tasks/4 postrunleg.ts | 22 ++++++++++++++++++++++ src/tasks/repeatableTasks.ts | 16 +++++++++++++++- 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/tasks/1 aftercoreleg.ts b/src/tasks/1 aftercoreleg.ts index 7c53590..43f7eac 100644 --- a/src/tasks/1 aftercoreleg.ts +++ b/src/tasks/1 aftercoreleg.ts @@ -157,6 +157,7 @@ export function AftercoreQuest(): Quest { takerSpaceOptimizer() use($item`model train set`); }, + tracking: "Workshed" }, { name: "Pre-Run Photobooth", @@ -206,6 +207,7 @@ export function AftercoreQuest(): Quest { useFamiliar($familiar`Stooper`); cliExecute("drink stillsuit distillate"); }, + tracking: "Organs", }, { name: "Barfing Drunk with Stooper", @@ -242,18 +244,21 @@ export function AftercoreQuest(): Quest { .repeat(), ), limit: { tries: 30 }, + tracking: "Garbo" }, { name: "Nightcap (Wine Glass)", ready: () => have($item`Drunkula's wineglass`), completed: () => totallyDrunk(), do: () => cliExecute(`CONSUME NIGHTCAP VALUE ${get("valueOfAdventure") - 1000}`), + tracking: "Organs" }, { name: "Nightcap (Marginal)", ready: () => have($item`Beach Comb`) || have($item`Map to Safety Shelter Grimace Prime`), completed: () => totallyDrunk(), do: () => cliExecute(`CONSUME NIGHTCAP VALUE 500`), + tracking: "Organs" }, { name: "Grimace Maps", @@ -261,6 +266,7 @@ export function AftercoreQuest(): Quest { completed: () => myAdventures() === 0 || !have($item`Map to Safety Shelter Grimace Prime`), do: () => cliExecute("grimace maps"), limit: { tries: 30 }, + tracking: "Bonus" }, { name: "Garbo (Drunk)", @@ -286,6 +292,7 @@ export function AftercoreQuest(): Quest { ready: () => have($item`Beach Comb`), completed: () => myAdventures() === 0, do: () => cliExecute(`combo ${11 - get("_freeBeachWalksUsed") + myAdventures()}`), + tracking: "Combo" }, { name: "Turn in FunFunds", @@ -329,6 +336,7 @@ export function AftercoreQuest(): Quest { "acquire Pizza of Legend; acquire Frosty's frosty mug; acquire Ol' Scratch's salad fork", ); }, + tracking: "Ascension Prep" }, { name: "Marble Soda!", @@ -342,6 +350,7 @@ export function AftercoreQuest(): Quest { skipSoda = true; } }, + tracking: "Ascension Prep" }, { name: "Prepare for LoopCS", @@ -355,6 +364,7 @@ export function AftercoreQuest(): Quest { !have($item`Calzone of Legend`) ? retrieveItem($item`Calzone of Legend`) : undefined; !have($item`borrowed time`) ? retrieveItem($item`borrowed time`) : undefined; }, + tracking: "Ascension Prep" }, { name: "Let's do the trainset again", diff --git a/src/tasks/3 runleg.ts b/src/tasks/3 runleg.ts index ed75fdb..b60f408 100644 --- a/src/tasks/3 runleg.ts +++ b/src/tasks/3 runleg.ts @@ -65,7 +65,7 @@ export function RunQuests(): Quest { drink($item`astral pilsner`, 1); }, clear: "all", - tracking: "Run", + tracking: "Organs", }, { name: "Sausages", diff --git a/src/tasks/4 postrunleg.ts b/src/tasks/4 postrunleg.ts index d1ae379..529c1b7 100644 --- a/src/tasks/4 postrunleg.ts +++ b/src/tasks/4 postrunleg.ts @@ -91,6 +91,7 @@ export function PostRunQuests(): Quest { uneffect($effect`Citizen of a Zone`); cliExecute(`set _citizenZone = ""`); }, + tracking: "Other" }, { name: "Ensure prefs reset", @@ -101,11 +102,13 @@ export function PostRunQuests(): Quest { name: "But dad I don't want to feel lost", completed: () => !have($effect`Feeling Lost`), do: () => uneffect($effect`Feeling Lost`), + tracking: "Other" }, { name: "Hey kids, let's take a trip to the beach", completed: () => have($item`bitchin' meatcar`) || have($item`Desert Bus pass`), do: () => cliExecute("acquire bitchin"), + tracking: "Other" }, { name: "Sober Up", @@ -126,6 +129,7 @@ export function PostRunQuests(): Quest { use($item`cuppa Sobrie tea`); } }, + tracking: "Organs" }, { name: "PvP Closet Safety 1", @@ -146,6 +150,7 @@ export function PostRunQuests(): Quest { if (have($item`astral six-pack`)) use($item`astral six-pack`); }, do: () => drink(1, $item`astral pilsner`), + tracking: "Organs" }, { name: "Moon Spoon", @@ -164,6 +169,7 @@ export function PostRunQuests(): Quest { choices: { 1430: 3, 606: 4, 610: 1, 1056: 1 }, do: $location`A-Boo Peak`, outfit: { modifier: "initiative 40 min 40 max, -tie" }, + tracking: "Other" }, { name: "Emergency Drink", @@ -177,6 +183,7 @@ export function PostRunQuests(): Quest { drink(1, $item`astral pilsner`); } }, + tracking: "Organs" }, { name: "Emergency Drink Part 2", @@ -192,6 +199,7 @@ export function PostRunQuests(): Quest { } }, limit: { tries: 6 }, + tracking: "Organs" }, { name: "Laugh Floor", @@ -232,6 +240,7 @@ export function PostRunQuests(): Quest { .repeat(), ), limit: { tries: 15 }, + tracking: "Steel Organ" }, { name: "Infernal Rackets Backstage", @@ -270,6 +279,7 @@ export function PostRunQuests(): Quest { .repeat(), ), limit: { tries: 15 }, + tracking: "Steel Organ" }, { name: "Mourn", @@ -282,6 +292,7 @@ export function PostRunQuests(): Quest { equip: $items`hilarious comedy prop, observational glasses, Victor\, the Insult Comic Hellhound Puppet`, }, do: () => cliExecute("panda comedy insult; panda comedy observe"), + tracking: "Steel Organ" }, { name: "Sven Golly", @@ -308,6 +319,7 @@ export function PostRunQuests(): Quest { ); cliExecute(`panda arena Jim ${$items`comfy pillow, sponge cake`.find((a) => have(a))}`); }, + tracking: "Steel Organ" }, { name: "Moaning Panda", @@ -324,18 +336,21 @@ export function PostRunQuests(): Quest { })), do: () => cliExecute("panda moan"), limit: { tries: 3 }, + tracking: "Steel Organ" }, { name: "Steel Margarita", ready: () => haveAll($items`Azazel's tutu, Azazel's lollipop, Azazel's unicorn`), completed: () => have($skill`Liver of Steel`) || have($item`steel margarita`), do: () => cliExecute("panda temple"), + tracking: "Steel Organ" }, { name: "Liver of Steel", ready: () => have($item`steel margarita`), completed: () => have($skill`Liver of Steel`), do: () => drink(1, $item`steel margarita`), + tracking: "Steel Organ" }, { name: "Emergency Drink Part 3", @@ -351,6 +366,7 @@ export function PostRunQuests(): Quest { } }, limit: { tries: 6 }, + tracking: "Organs" }, ...postRunQuests(), ...noBarf(), @@ -404,12 +420,14 @@ export function PostRunQuests(): Quest { useFamiliar($familiar`Stooper`); cliExecute("drink stillsuit distillate"); }, + tracking: "Rollover Prep" }, { name: "Nightcap", ready: () => doneAdventuring(), completed: () => totallyDrunk(), do: () => cliExecute("CONSUME NIGHTCAP"), + tracking: "Rollover Prep" }, { name: "Smoke em if you got em", @@ -426,6 +444,7 @@ export function PostRunQuests(): Quest { smoke = smoke + 1; } }, + tracking: "Community Service" }, { name: "Offhand Remarkable", @@ -435,6 +454,7 @@ export function PostRunQuests(): Quest { have($effect`Offhand Remarkable`) || get("_aug13Cast", false), do: () => useSkill($skill`Aug. 13th: Left/Off Hander's Day!`), + tracking: "Rollover Prep" }, { name: "PvP Closet Safety 3", @@ -461,6 +481,7 @@ export function PostRunQuests(): Quest { use($item`TakerSpace letter of Marque`); visitUrl("campground.php?action=workshed"); }, + tracking: "Workshed" }, { name: "Pajamas", @@ -484,6 +505,7 @@ export function PostRunQuests(): Quest { args.pvp ? ", 0.3 fites" : "" }`, }), + tracking: "Rollover Prep" }, { name: "Alert-No Nightcap", diff --git a/src/tasks/repeatableTasks.ts b/src/tasks/repeatableTasks.ts index f81916a..f08f195 100644 --- a/src/tasks/repeatableTasks.ts +++ b/src/tasks/repeatableTasks.ts @@ -84,6 +84,7 @@ export function postRunQuests(): Task[] { name: "Breakfast", completed: () => get("breakfastCompleted"), do: () => cliExecute("breakfast"), + tracking: "Breakfast" }, { name: "Harvest Garden", @@ -134,11 +135,13 @@ export function postRunQuests(): Task[] { false, ), limit: { tries: 5 }, + tracking: "Other" }, { name: "Restore HP", completed: () => myHp() > 0.5 * myMaxhp(), do: () => restoreHp(0.95 * myMaxhp()), + tracking: "Other" }, { name: "Implement Glitch", @@ -203,6 +206,7 @@ export function postRunQuests(): Task[] { .attack() .repeat(), ), + tracking: "Other" }, { name: "Stock Up on MMJs", @@ -218,6 +222,7 @@ export function postRunQuests(): Task[] { }, ], do: () => false, + tracking: "Other" }, { name: "Buy Seal Summoning Supplies", @@ -233,6 +238,7 @@ export function postRunQuests(): Task[] { num: 500, })), do: () => false, + tracking: "Other" }, { name: "Run CyberRealm", @@ -275,6 +281,7 @@ export function postRunQuests(): Task[] { .repeat(), ), limit: { skip: 60 }, + tracking: "Cyber Realm" }, { name: "Wardrobe-o-matic", @@ -297,6 +304,7 @@ export function postRunQuests(): Task[] { visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); visitUrl("choice.php?a=3054&whichchoice=1544&option=1&pwd"); }, + tracking: "Garbo" }, ]; } @@ -366,12 +374,14 @@ export function noBarf(): Task[] { mySpleenUse() >= spleenLimit() && myInebriety() >= inebrietyLimit(), do: () => cliExecute("consume ALL"), + tracking: "Organs" }, { name: "Pantogramming", ready: () => pantogramReady(), completed: () => pantogram(), do: () => pantogram(), + tracking: "Farming Prep" }, { name: "Garbo Nobarf", @@ -386,6 +396,7 @@ export function noBarf(): Task[] { garboDone2 = true; } }, + tracking: "Garbo" }, ]; } @@ -411,6 +422,7 @@ export function garboWeen(): Task[] { ready: () => have($item`Drunkula's wineglass`) && holiday().includes("Halloween"), completed: () => totallyDrunk(), do: () => cliExecute(`CONSUME NIGHTCAP`), + tracking: "Organs" }, { name: "Freecandy Drunk", @@ -439,7 +451,7 @@ export function chrono(): Task[] { cliExecute(`${args.chronoscript}`); }, clear: "all", - tracking: "chrono", + tracking: "Chrono", limit: { tries: 1 }, //this will run again after installing CMC, by magic }, { @@ -451,6 +463,7 @@ export function chrono(): Task[] { myDaycount() > 1, completed: () => totallyDrunk(), do: () => cliExecute(`CONSUME NIGHTCAP`), + tracking: "Organs" }, { name: "Chrono Drunk", @@ -487,6 +500,7 @@ export function crimbo(): Task[] { have($item`Drunkula's wineglass`) && holiday().includes("Halloween") && myDaycount() > 1, completed: () => totallyDrunk(), do: () => cliExecute(`CONSUME NIGHTCAP`), + tracking: "Organs" }, { name: "Crimbo Drunk", From ec4b76050dacea05c393e3e09101e3e54d4d9af4 Mon Sep 17 00:00:00 2001 From: Ignose Date: Fri, 7 Mar 2025 08:37:14 -0500 Subject: [PATCH 18/24] Zoot Zoot --- src/args.ts | 5 +++++ src/main.ts | 2 +- src/tasks/1 aftercoreleg.ts | 7 ++++--- src/tasks/2 ascend.ts | 4 +++- src/tasks/3 runleg.ts | 6 +++--- src/tasks/4 postrunleg.ts | 5 ++--- src/tasks/repeatableTasks.ts | 24 ++++++++++++++++++++++-- src/tasks/utils.ts | 3 --- yarn.lock | 12 ++++++------ 9 files changed, 46 insertions(+), 22 deletions(-) diff --git a/src/args.ts b/src/args.ts index eb5ba0f..147061f 100644 --- a/src/args.ts +++ b/src/args.ts @@ -41,6 +41,11 @@ export const args = Args.create( default: false, setting: "", }), + zooto: Args.flag({ + help: "Ascend into and run Zootomist", + default: false, + setting: "", + }), crimbo: Args.flag({ help: "Farm Crimbo instead of Garbo", default: false, diff --git a/src/main.ts b/src/main.ts index d903b81..6115a60 100644 --- a/src/main.ts +++ b/src/main.ts @@ -30,7 +30,7 @@ export function main(command?: string): void { print(`Running: candyWrapper v${version}`); - if (!args.cs && !args.smol && !args.casual && !args.robot) + if (!args.cs && !args.smol && !args.casual && !args.robot && !args.zooto) throw "Undefined runtype; please choose an acceptable path"; const tasks = getTasks([AftercoreQuest(), AscendQuest(), RunQuests(), PostRunQuests()]); diff --git a/src/tasks/1 aftercoreleg.ts b/src/tasks/1 aftercoreleg.ts index 43f7eac..4883c0d 100644 --- a/src/tasks/1 aftercoreleg.ts +++ b/src/tasks/1 aftercoreleg.ts @@ -48,7 +48,7 @@ import { args } from "../args"; import { chrono, crimbo, garboWeen, noBarf, postRunQuests, preRunQuests } from "./repeatableTasks"; import { Quest } from "./structure"; -import { maxBase, pingu, pvpCloset, stooperDrunk, totallyDrunk } from "./utils"; +import { maxBase, pvpCloset, stooperDrunk, totallyDrunk } from "./utils"; import { garboValue } from "../engine/profits"; const RESOURCES = ["Spice", "Rum", "Anchor", "Mast", "Silk", "Gold"] as const; @@ -140,6 +140,7 @@ const stations = [ export function AftercoreQuest(): Quest { return { name: "Aftercore", + ready: () => myDaycount() >= (args.zooto ? 3 : 2) && get("kingLiberated"), completed: () => (myAdventures() === 0 && totallyDrunk() && @@ -185,7 +186,7 @@ export function AftercoreQuest(): Quest { name: "Garbo", completed: () => stooperDrunk() || myAdventures() === 0, prepare: () => uneffect($effect`Beaten Up`), - do: () => cliExecute(`${args.garboascend} ${pingu()}`), + do: () => cliExecute(`${args.garboascend}`), post: () => { if (myAdventures() === 0) $effects`Power Ballad of the Arrowsmith, Stevedave's Shanty of Superiority, The Moxious Madrigal, The Magical Mojomuscular Melody, Aloysius' Antiphon of Aptitude, Ur-Kel's Aria of Annoyance` @@ -273,7 +274,7 @@ export function AftercoreQuest(): Quest { ready: () => have($item`Drunkula's wineglass`), prepare: () => uneffect($effect`Beaten Up`), completed: () => myAdventures() === 0, - do: () => cliExecute(`${args.garboascend} ${pingu()}`), + do: () => cliExecute(`${args.garboascend}`), post: () => $effects`Power Ballad of the Arrowsmith, Stevedave's Shanty of Superiority, The Moxious Madrigal, The Magical Mojomuscular Melody, Aloysius' Antiphon of Aptitude, Ur-Kel's Aria of Annoyance` .filter((ef) => have(ef)) diff --git a/src/tasks/2 ascend.ts b/src/tasks/2 ascend.ts index a0e4d79..28ab0b2 100644 --- a/src/tasks/2 ascend.ts +++ b/src/tasks/2 ascend.ts @@ -41,6 +41,8 @@ export function AscendQuest(): Quest { ? $path.none : args.robot ? $path`You, Robot` + : args.zooto + ? $path`Z is for Zootomist` : undefined; const lifestyle = args.casual ? 1 : 2; @@ -57,7 +59,7 @@ export function AscendQuest(): Quest { ascend({ path: path, - playerClass: myClass, + playerClass: args.zooto ? $class`Zootomist` : myClass, lifestyle: lifestyle, moon: moonsign, consumable: $item`astral six-pack`, diff --git a/src/tasks/3 runleg.ts b/src/tasks/3 runleg.ts index b60f408..d808e86 100644 --- a/src/tasks/3 runleg.ts +++ b/src/tasks/3 runleg.ts @@ -15,7 +15,7 @@ import { $item, $skill, clamp, get, have } from "libram"; import { args } from "../args"; import { preRunQuests } from "./repeatableTasks"; -import { getCurrentLeg, Leg, Quest } from "./structure"; +import { Quest } from "./structure"; import { shouldWeOverdrink } from "./utils"; const runType = () => @@ -27,7 +27,7 @@ const runType = () => ? args.casualscript : args.robot ? args.robotscript - : args.robotscript; + : "autoscend"; export function howManySausagesCouldIEat() { if (!have($item`Kramco Sausage-o-Matic™`)) return 0; @@ -44,7 +44,7 @@ export function howManySausagesCouldIEat() { export function RunQuests(): Quest { return { name: "Ascension Run", - completed: () => getCurrentLeg() !== Leg.Run || get("kingLiberated"), + completed: () => get("kingLiberated"), tasks: [ ...preRunQuests(), { diff --git a/src/tasks/4 postrunleg.ts b/src/tasks/4 postrunleg.ts index 529c1b7..6afb0c9 100644 --- a/src/tasks/4 postrunleg.ts +++ b/src/tasks/4 postrunleg.ts @@ -56,7 +56,6 @@ import { doneAdventuring, haveAll, maxBase, - pingu, pvpCloset, stooperDrunk, totallyDrunk, @@ -74,7 +73,7 @@ const checkMelange = () => export function PostRunQuests(): Quest { return { name: "Post-Run Aftercore", - ready: () => myDaycount() === 1 && get("kingLiberated", false), + ready: () => (myDaycount() === 1 || (myDaycount() === 2 && args.zooto)) && get("kingLiberated", false), completed: () => totallyDrunk() && pajamas, tasks: [ { @@ -377,7 +376,7 @@ export function PostRunQuests(): Quest { name: "Garbo", completed: () => myAdventures() === 0 || stooperDrunk(), prepare: () => uneffect($effect`Beaten Up`), - do: () => cliExecute(`${args.garbo} ${pingu()}`), + do: () => cliExecute(`${args.garbo}`), post: () => $effects`Power Ballad of the Arrowsmith, Stevedave's Shanty of Superiority, The Moxious Madrigal, The Magical Mojomuscular Melody, Aloysius' Antiphon of Aptitude, Ur-Kel's Aria of Annoyance` .filter((ef) => have(ef)) diff --git a/src/tasks/repeatableTasks.ts b/src/tasks/repeatableTasks.ts index f08f195..48e9fe2 100644 --- a/src/tasks/repeatableTasks.ts +++ b/src/tasks/repeatableTasks.ts @@ -6,6 +6,7 @@ import { Familiar, fullnessLimit, getCampground, + getClanLounge, getClanName, getWorkshed, guildStoreAvailable, @@ -46,10 +47,12 @@ import { $stat, AprilingBandHelmet, AsdonMartin, + directlyUse, get, getTodaysHolidayWanderers, have, Macro, + set, uneffect, } from "libram"; @@ -310,6 +313,9 @@ export function postRunQuests(): Task[] { } let duffo = false; +const loungeItems = getClanLounge(); +const hasClanFloundry = loungeItems["Clan Floundry"] === 1; +const hasCarpe = loungeItems["carpe"] !== undefined && loungeItems["carpe"] >= 1; export function preRunQuests(): Task[] { return [ @@ -323,13 +329,27 @@ export function preRunQuests(): Task[] { }, { name: "Get Floundry item", - ready: () => have($item`Clan VIP Lounge key`) && !args.carpe, + ready: () => have($item`Clan VIP Lounge key`) && !args.carpe && hasClanFloundry && hasCarpe, completed: () => get("_floundryItemCreated"), do: (): void => { + if(getClanLounge()) retrieveItem($item`carpe`); }, limit: { tries: 1 }, }, + { + name: "Leprecondo", + // eslint-disable-next-line libram/verify-constants + ready: () => have($item`Leprecondo`), + completed: () => get("_doCondo", false), + do: (): void => { + // eslint-disable-next-line libram/verify-constants + directlyUse($item`Leprecondo`); + visitUrl(`choice.php?pwd&option=1&whichchoice=1556&r0=21&r1=8&r2=9&r3=13`); + set("_doCondo", true); + }, + limit: { tries: 1 }, + }, { name: "Unpack Duffel Bag", completed: () => duffo, @@ -344,7 +364,7 @@ export function preRunQuests(): Task[] { have($item`lucky gold ring`) && have($item`one-day ticket to Dinseylandfill`) && !args.garboascend.includes("penguin") && - !args.cs, + !(args.cs || args.zooto), completed: () => get("_stenchAirportToday") || get("stenchAirportAlways"), do: () => use($item`one-day ticket to Dinseylandfill`), tracking: "Garbo", diff --git a/src/tasks/utils.ts b/src/tasks/utils.ts index 811a102..5338de3 100644 --- a/src/tasks/utils.ts +++ b/src/tasks/utils.ts @@ -1,5 +1,4 @@ import { - canAdventure, canEquip, chew, cliExecute, @@ -438,8 +437,6 @@ export function shouldWeOverdrink(): boolean { } } -export const pingu = () => canAdventure($location`The Copperhead Club`) ? "penguin" : ""; - export function pantogramReady(): boolean { if (!Pantogram.have() || Pantogram.havePants()) return false; const pantogramValue = 100 * myAdventures(); diff --git a/yarn.lock b/yarn.lock index de9563b..798af0c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2358,9 +2358,9 @@ eslint-config-prettier@^8.8.0: integrity sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA== eslint-plugin-libram@^0.4.21: - version "0.4.21" - resolved "https://registry.yarnpkg.com/eslint-plugin-libram/-/eslint-plugin-libram-0.4.21.tgz#0db49bedd807f256bc01496622065d735c5ffc2e" - integrity sha512-wSS2Uqty4lE+08NT4614I8j19gHN7rEs4MOn6hXRTgfB7ES0mIsf2+bLuVNNpknkryF49yvT/znzmAN2JhoAnw== + version "0.4.23" + resolved "https://registry.yarnpkg.com/eslint-plugin-libram/-/eslint-plugin-libram-0.4.23.tgz#64a5e61087f610ce319e8228ffe708535e680079" + integrity sha512-PIkxHayAcEzK8cMZmGTBOdacDjrCcKqfXUbqS8CPvrahCgG9CVRONudCM94SmApJrok/mES20AtNxEGsCQSggg== dependencies: html-entities "^2.5.2" requireindex "~1.2.0" @@ -3163,9 +3163,9 @@ levn@^0.4.1: type-check "~0.4.0" libram@^0.9.31: - version "0.9.31" - resolved "https://registry.yarnpkg.com/libram/-/libram-0.9.31.tgz#49b368b285670b7512cd95be7fad8c016543ef90" - integrity sha512-ubtAkQnDctHZS1Q8cE5cHEk0Br+q3yiEIP18yXDqxsdQXWM4UI8ryMjG/VBaQSCRj+94Q6YWiT88/yzu4bUL1Q== + version "0.9.34" + resolved "https://registry.yarnpkg.com/libram/-/libram-0.9.34.tgz#4c0df764ee0b4bbfcd9111e91f80e689d337c586" + integrity sha512-cK32LJoC5STKEu9ninf2U3zg+imNQMalPHG2AjisNhwMo0sRLwwhpWTCC5CRxaaTisDHRUNQaEKCe+r9n1Ho5Q== dependencies: html-entities "^2.5.2" From 783f686deeae6960797fe853a931a9c780712d9d Mon Sep 17 00:00:00 2001 From: Ignose Date: Mon, 10 Mar 2025 14:04:39 -0400 Subject: [PATCH 19/24] Leprecondo --- package.json | 4 +- src/engine/leprecondo.ts | 125 +++++++++++++++++++++++++++++++++++ src/tasks/repeatableTasks.ts | 17 +---- yarn.lock | 28 ++++---- 4 files changed, 143 insertions(+), 31 deletions(-) create mode 100644 src/engine/leprecondo.ts diff --git a/package.json b/package.json index 72f5f15..fb98a17 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "build": "^0.1.4", "grimoire-kolmafia": "^0.3.25", "kolmafia": "^5.27815.0", - "libram": "^0.9.31", + "libram": "^0.10.7", "mafia-shared-relay": "0.0.7", "webpack": "^5.89.0" }, @@ -37,7 +37,7 @@ "esbuild-plugin-babel": "^0.2.3", "eslint": "^8.45.0", "eslint-config-prettier": "^8.8.0", - "eslint-plugin-libram": "^0.4.21", + "eslint-plugin-libram": "^0.4.24", "eslint-plugin-prettier": "^5.0.0", "prettier": "^3.0.0", "typescript": "^5.1.6", diff --git a/src/engine/leprecondo.ts b/src/engine/leprecondo.ts new file mode 100644 index 0000000..1f83597 --- /dev/null +++ b/src/engine/leprecondo.ts @@ -0,0 +1,125 @@ +import { Effect, Item } from "kolmafia"; +import { $effect, $item, arrayEquals, get, Leprecondo, maxBy, sum, Tuple } from "libram"; +import { garboAverageValue, garboValue } from "./profits"; +import { Task } from "../tasks/structure"; + +function resultValue(result: Leprecondo.Result): number { + if (result instanceof Item) return garboValue(result); + if (Array.isArray(result)) return garboAverageValue(...result); + if (result instanceof Effect) { + if (result === $effect`Your Days Are Numbed` || result === $effect`Counter Intelligence`) return 825; + } + return 0; +} + +/* + * @returns Whether `a` is strictly better than `b` + */ +function strictlyBetterThan( + a: Leprecondo.FurnitureStat, + b: Leprecondo.FurnitureStat, +): boolean { + return Leprecondo.NEEDS.every((need) => { + const result = b[need]; + if (!result) return true; + const other = a[need]; + if (!other) return false; + if (resultValue(other) <= resultValue(result)) return false; + return true; + }); +} + +function getStat( + furniture: Leprecondo.FurniturePiece, +): Leprecondo.FurnitureStat { + return Leprecondo.Furniture[furniture]; +} + +function getCoveredNeeds( + furniture: Leprecondo.FurniturePiece, +): Leprecondo.Need[] { + return Object.keys(getStat(furniture)) as Leprecondo.Need[]; +} + +function viableFurniture(): Leprecondo.FurniturePiece[] { + const discovered = Leprecondo.discoveredFurniture(); + return [ + "empty", + ...discovered.filter( + (f, index) => + !discovered + .slice(index) + .some((futureFurniture) => + strictlyBetterThan(getStat(futureFurniture), getStat(f)), + ), + ), + ]; +} + +type Combination = Tuple; + +function valueCombination(combo: Combination): number { + const total = Leprecondo.furnitureBonuses(combo); + return sum(Leprecondo.NEEDS, (need) => + resultValue(total[need] ?? $item.none), + ); +} + +function buildCombination( + combinations: Tuple[], + furniture: Leprecondo.FurniturePiece[], +): [...Tuple, Leprecondo.FurniturePiece][] { + return combinations.flatMap((combination) => { + const coveredNeeds = new Set(combination.flatMap(getCoveredNeeds)); + const plausibleFurniture = furniture.filter((f) => + getCoveredNeeds(f).some((need) => !coveredNeeds.has(need)), + ); // Only furniture that cover at least one presently-uncovered need need apply + return ( + plausibleFurniture.length ? plausibleFurniture : (["empty"] as const) + ).map( + ( + furniture, + ): [ + ...Tuple, + Leprecondo.FurniturePiece, + ] => [...combination, furniture], + ); + }); +} + +function getViableCombinations(): Combination[] { + const furniture = viableFurniture(); + return Array(4) + .fill(null) + .reduce( + (acc) => buildCombination(acc, furniture), + [[]], + ) as Combination[]; +} + +function findBestCombination(): Combination { + return maxBy(getViableCombinations(), valueCombination); +} + +let bestCombination: Combination; +let unlocked: string; +function getBestLeprecondoCombination(): Combination { + if (unlocked !== get("leprecondoDiscovered")) { + unlocked = get("leprecondoDiscovered"); + bestCombination = findBestCombination(); + } + return bestCombination; +} + +export function leprecondoTask(): Task { + return { + name: "Configure Leprecondo", + ready: () => Leprecondo.have() && Leprecondo.rearrangesRemaining() > 0, + completed: () => + arrayEquals( + Leprecondo.installedFurniture(), + getBestLeprecondoCombination(), + ), + do: () => Leprecondo.setFurniture(...getBestLeprecondoCombination()), + }; +} diff --git a/src/tasks/repeatableTasks.ts b/src/tasks/repeatableTasks.ts index 48e9fe2..5f9892d 100644 --- a/src/tasks/repeatableTasks.ts +++ b/src/tasks/repeatableTasks.ts @@ -47,12 +47,10 @@ import { $stat, AprilingBandHelmet, AsdonMartin, - directlyUse, get, getTodaysHolidayWanderers, have, Macro, - set, uneffect, } from "libram"; @@ -60,6 +58,7 @@ import { args } from "../args"; import { Task } from "./structure"; import { getGarden, maxBase, nextCyberZone, pantogram, pantogramReady, stooperDrunk, totallyDrunk } from "./utils"; +import { leprecondoTask } from "../engine/leprecondo"; const bestFam = () => famCheck($familiar`Pocket Professor`) @@ -337,19 +336,7 @@ export function preRunQuests(): Task[] { }, limit: { tries: 1 }, }, - { - name: "Leprecondo", - // eslint-disable-next-line libram/verify-constants - ready: () => have($item`Leprecondo`), - completed: () => get("_doCondo", false), - do: (): void => { - // eslint-disable-next-line libram/verify-constants - directlyUse($item`Leprecondo`); - visitUrl(`choice.php?pwd&option=1&whichchoice=1556&r0=21&r1=8&r2=9&r3=13`); - set("_doCondo", true); - }, - limit: { tries: 1 }, - }, + leprecondoTask(), { name: "Unpack Duffel Bag", completed: () => duffo, diff --git a/yarn.lock b/yarn.lock index 798af0c..7617f7c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2107,9 +2107,9 @@ core-js@^2.4.0: integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== core-js@^3.16.4: - version "3.33.2" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.33.2.tgz#312bbf6996a3a517c04c99b9909cdd27138d1ceb" - integrity sha512-XeBzWI6QL3nJQiHmdzbAOiMYqjrb7hwU7A39Qhvd/POSa/t9E1AeZyEZx3fNvp/vtM8zXwhoL0FsiS0hD0pruQ== + version "3.41.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.41.0.tgz#57714dafb8c751a6095d028a7428f1fb5834a776" + integrity sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA== create-create-app@^7.3.0: version "7.3.0" @@ -2357,10 +2357,10 @@ eslint-config-prettier@^8.8.0: resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz#bfda738d412adc917fd7b038857110efe98c9348" integrity sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA== -eslint-plugin-libram@^0.4.21: - version "0.4.23" - resolved "https://registry.yarnpkg.com/eslint-plugin-libram/-/eslint-plugin-libram-0.4.23.tgz#64a5e61087f610ce319e8228ffe708535e680079" - integrity sha512-PIkxHayAcEzK8cMZmGTBOdacDjrCcKqfXUbqS8CPvrahCgG9CVRONudCM94SmApJrok/mES20AtNxEGsCQSggg== +eslint-plugin-libram@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/eslint-plugin-libram/-/eslint-plugin-libram-0.4.24.tgz#2c5dcd46a4e280071fc9997a71f911bc3770598c" + integrity sha512-TrlblOJjM92p0wMaAyO1P2vrG3JDi6ZZguBZvB19FhynFNyehIM9P2GPUPkKA3Lyo7l1L0j3ofOpRzHtgYK8xw== dependencies: html-entities "^2.5.2" requireindex "~1.2.0" @@ -2800,9 +2800,9 @@ graphemer@^1.4.0: integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== grimoire-kolmafia@^0.3.25: - version "0.3.25" - resolved "https://registry.yarnpkg.com/grimoire-kolmafia/-/grimoire-kolmafia-0.3.25.tgz#be46c7b8ee0e550e6346ea66d5fe171d06035889" - integrity sha512-LSnNURUnH8rHrHOgSd1RtTo1geuOU8CfM2B1fAXcHXViicQB0ZxvNYObXG+RqzjFBp+ud0fEFFI2VfRW8kyW9A== + version "0.3.29" + resolved "https://registry.yarnpkg.com/grimoire-kolmafia/-/grimoire-kolmafia-0.3.29.tgz#9f9f923b891a570365136177700f6c7614506969" + integrity sha512-EiFV38tuG7wEHhinBVepUqEDDmEyOo4vZjSRSiKEe13GV0+wgL8Q3ycArSAuE4125KPPFZ8EKXDmTU79xtPP7g== dependencies: core-js "^3.16.4" @@ -3162,10 +3162,10 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -libram@^0.9.31: - version "0.9.34" - resolved "https://registry.yarnpkg.com/libram/-/libram-0.9.34.tgz#4c0df764ee0b4bbfcd9111e91f80e689d337c586" - integrity sha512-cK32LJoC5STKEu9ninf2U3zg+imNQMalPHG2AjisNhwMo0sRLwwhpWTCC5CRxaaTisDHRUNQaEKCe+r9n1Ho5Q== +libram@^0.10.7: + version "0.10.7" + resolved "https://registry.yarnpkg.com/libram/-/libram-0.10.7.tgz#814d2f9d6c40b3ece15221b541eff63b0ee24b78" + integrity sha512-KRbeJj2O2D1D466ZItLswaXlcAJaGv52K92CKd3j3tEuIrBV4WamZD6gBW91YJVvxem4zjfJDuylVByNrc7gaQ== dependencies: html-entities "^2.5.2" From 35ecf43fa764c7149e7f5b9448fffce3c4ee73cf Mon Sep 17 00:00:00 2001 From: Ignose Date: Wed, 16 Jul 2025 11:38:33 -0400 Subject: [PATCH 20/24] Oh my god we've done so much and come so far --- package.json | 2 +- src/args.ts | 13 ++ src/beret.ts | 371 +++++++++++++++++++++++++++++++++++ src/main.ts | 7 +- src/tasks/1 aftercoreleg.ts | 19 +- src/tasks/2 ascend.ts | 9 +- src/tasks/3 runleg.ts | 44 ++++- src/tasks/4 postrunleg.ts | 3 +- src/tasks/cluedin.ts | 169 ++++++++++++++++ src/tasks/repeatableTasks.ts | 78 +++++++- yarn.lock | 14 +- 11 files changed, 711 insertions(+), 18 deletions(-) create mode 100644 src/beret.ts create mode 100644 src/tasks/cluedin.ts diff --git a/package.json b/package.json index fb98a17..8e74670 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "build": "^0.1.4", "grimoire-kolmafia": "^0.3.25", "kolmafia": "^5.27815.0", - "libram": "^0.10.7", + "libram": "^0.10.9", "mafia-shared-relay": "0.0.7", "webpack": "^5.89.0" }, diff --git a/src/args.ts b/src/args.ts index 147061f..1b3a492 100644 --- a/src/args.ts +++ b/src/args.ts @@ -46,6 +46,11 @@ export const args = Args.create( default: false, setting: "", }), + ih8u: Args.flag({ + help: "Ascend into and run 11 Things I Hate About U", + default: false, + setting: "", + }), crimbo: Args.flag({ help: "Farm Crimbo instead of Garbo", default: false, @@ -146,6 +151,10 @@ export const args = Args.create( help: "The command that will do your You, Robot run for you. Include any arguments desired.", default: "", }), + ih8uscript: Args.string({ + help: "The command that will do your You, Robot run for you. Include any arguments desired.", + default: "", + }), pulls: Args.items({ help: "A list of items to pull at the start of the smol run.", default: [ @@ -183,5 +192,9 @@ export const args = Args.create( help: "Define a specific monster for Garbo to target; note that the wrapper will find something useful for you!", default: "", }), + test: Args.flag({ + help: "test", + default: false + }) }, ); diff --git a/src/beret.ts b/src/beret.ts new file mode 100644 index 0000000..c6213f1 --- /dev/null +++ b/src/beret.ts @@ -0,0 +1,371 @@ +import { beretBuskingEffects, buy, canEquip, Effect, equip, equippedItem, getPower, Item, myFamiliar, myMeat, npcPrice, numericModifier, print, toEffect, toSlot, useFamiliar, useSkill } from "kolmafia"; +import { $effect, $familiar, $item, $skill, $slot, $slots, get, have as have_, logger, maxBy, NumericModifier, sum, unequip } from "libram"; + +// eslint-disable-next-line libram/verify-constants +const beret = $item`prismatic beret`; + +export type EffectValuer = + | Partial> + | ((effect: Effect, duration: number) => number) + | Effect[]; +const valueEffect = (effect: Effect, duration: number, valuer: EffectValuer) => + typeof valuer === "function" + ? valuer(effect, duration) + : Array.isArray(valuer) + ? Number(valuer.includes(effect)) * duration + : sum( + Object.entries(valuer), + ([modifier, weight]) => weight * numericModifier(effect, modifier), + ); + +/** + * @returns Whether or not you have the prismatic beret + */ +export function have(): boolean { + return have_(beret); +} + +function getUseableClothes(buyItem = true): { + useableHats: Item[]; + useablePants: Item[]; + useableShirts: Item[]; +} { + const availableItems = Item.all().filter( + (i) => canEquip(i) && (have_(i) || (buyItem && npcPrice(i) > 0)), + ); + const useableHats = have_($familiar`Mad Hatrack`) + ? [...availableItems.filter((i) => toSlot(i) === $slot`hat`), $item.none] + : [beret]; + const useablePants = [ + ...availableItems.filter((i) => toSlot(i) === $slot`pants`), + $item.none, + ]; + const useableShirts = [ + ...availableItems.filter((i) => toSlot(i) === $slot`shirt`), + $item.none, + ]; + return { useableHats, useablePants, useableShirts }; +} + +function availablePowersums(buyItem: boolean): number[] { + const taoMultiplier = have_($skill`Tao of the Terrapin`) ? 2 : 1; + + const { useableHats, useablePants, useableShirts } = + getUseableClothes(buyItem); + + const hatPowers = [ + ...new Set(useableHats.map((i) => taoMultiplier * getPower(i))), + ]; + const pantPowers = [ + ...new Set(useablePants.map((i) => taoMultiplier * getPower(i))), + ]; + const shirtPowers = [...new Set(useableShirts.map((i) => getPower(i)))]; + + return [ + ...new Set( + hatPowers.flatMap((hat) => + pantPowers.flatMap((pant) => + shirtPowers.flatMap((shirt) => hat + pant + shirt), + ), + ), + ), + ]; +} + +function scoreBusk( + effects: [Effect, number][], + effectValuer: EffectValuer, + uselessEffects: Set, +): number { + const usefulEffects = effects.filter( + ([effect]) => !uselessEffects.has(effect), + ); + return sum(usefulEffects, ([effect, duration]) => + valueEffect(effect, duration, effectValuer), + ); +} + +/** + * Calculate the optimal power-sum at which to busk, given a weighted set of modifiers. + * @param wantedEffects An array of Effects we care about; maximizes the number of those effects we end up with + * @param buskUses How many busks should we assume we've cast? Defaults to the current number. + * @param uselessEffects An array (defaults to empty) of effects not to consider for the purposes of busk valuation + * @param buyItem Whether or not we should consider purchasing items from NPC stores; defaults to true + * @returns The power-sum at which you'll find the optimal busk for this situation. + */ +export function findOptimalOutfitPower( + wantedEffects: Effect[], + buskUses?: number, + uselessEffects?: Effect[], + buyItem?: boolean, +): number; +/** + * Calculate the optimal power-sum at which to busk, given a weighted set of modifiers. + * @param weightedModifiers An object keyed by Numeric Modifiers, with their values representing weights + * @param buskUses How many busks should we assume we've cast? Defaults to the current number. + * @param uselessEffects An array (defaults to empty) of effects not to consider for the purposes of busk valuation + * @param buyItem Whether or not we should consider purchasing items from NPC stores; defaults to true + * @returns The power-sum at which you'll find the optimal busk for this situation. + */ +export function findOptimalOutfitPower( + weightedModifiers: Partial>, + buskUses?: number, + uselessEffects?: Effect[], + buyItem?: boolean, +): number; +/** + * Calculate the optimal power-sum at which to busk, given a weighted set of modifiers. + * @param valueFunction A function that maps effects to values + * @param buskUses How many busks should we assume we've cast? Defaults to the current number. + * @param uselessEffects An array (defaults to empty) of effects not to consider for the purposes of busk valuation + * @param buyItem Whether or not we should consider purchasing items from NPC stores; defaults to true + * @returns The power-sum at which you'll find the optimal busk for this situation. + */ +export function findOptimalOutfitPower( + valueFunction: (effect: Effect, duration: number) => number, + buskUses?: number, + uselessEffects?: Effect[], + buyItem?: boolean, +): number; +/** + * Calculate the optimal power-sum at which to busk, given a weighted set of modifiers. + * @param effectValuer Either a function that maps effect-duration pairs to values, or an object keyed by numeric modifiers with weights as values, or an array of desired effects + * @param buskUses How many busks should we assume we've cast? Defaults to the current number. + * @param uselessEffects An array (defaults to empty) of effects not to consider for the purposes of busk valuation + * @param buyItem Whether or not we should consider purchasing items from NPC stores; defaults to true + * @returns The power-sum at which you'll find the optimal busk for this situation. + */ +export function findOptimalOutfitPower( + effectValuer: EffectValuer, + buskUses?: number, + uselessEffects?: Effect[], + buyItem?: boolean, +): number; +/** + * Calculate the optimal power-sum at which to busk, given a weighted set of modifiers. + * @param effectValuer Either a function that maps effect-duration pairs to values, or an object keyed by numeric modifiers with weights as values, or an array of desired effects + * @param buskUses How many busks should we assume we've cast? Defaults to the current number. + * @param uselessEffects An array (defaults to empty) of effects not to consider for the purposes of busk valuation + * @param buyItem Whether or not we should consider purchasing items from NPC stores; defaults to true + * @returns The power-sum at which you'll find the optimal busk for this situation. + */ +export function findOptimalOutfitPower( + effectValuer: EffectValuer, + buskUses = get("_beretBuskingUses",0), + uselessEffects: Effect[] = [], + buyItem = true, +): number { + const uselessEffectSet = new Set(uselessEffects); + const powersums = availablePowersums(buyItem); + if (!powersums.length) return 0; + return maxBy(powersums, (power) => + scoreBusk( + Object.entries(beretBuskingEffects(power, buskUses)) + .map(([effect, duration]): [Effect, number] => [ + toEffect(effect), + duration, + ]) + .filter(([e]) => e !== $effect.none), + effectValuer, + uselessEffectSet, + ), + ); +} + +const populateMap = (arr: Item[], max: number, double: boolean) => { + const map = new Map(); + for (const it of arr) { + const power = getPower(it) * (double ? 2 : 1); + if (power > max) continue; + + const existing = map.get(power); + if ( + !existing || + (!have_(existing) && (have_(it) || npcPrice(it) < npcPrice(existing))) + ) { + map.set(power, it); + } + } + return map; +}; +const relevantSlots = ["hat", "pants", "shirt"] as const; + +function functionalPrice(item: Item): number { + const price = have_(item) || Item.none ? 0 : npcPrice(item); + return price; +} + +function outfitPrice(outfit: { hat: Item; pants: Item; shirt: Item }): number { + const thing = sum(relevantSlots, (slot) => functionalPrice(outfit[slot])); + return thing; +} + + +function findOutfit(power: number, buyItem: boolean) { + const { useableHats, useablePants, useableShirts } = + getUseableClothes(buyItem); + const hatPowers = populateMap( + useableHats, + power, + have_($skill`Tao of the Terrapin`), + ); + const pantsPowers = populateMap( + useablePants, + power, + have_($skill`Tao of the Terrapin`), + ); + const shirtPowers = populateMap(useableShirts, power, false); + + const outfits = [...hatPowers].flatMap(([hatPower, hat]) => + [...pantsPowers].flatMap(([pantsPower, pants]) => + [...shirtPowers].flatMap(([shirtPower, shirt]) => + hatPower + pantsPower + shirtPower === power + ? { hat, pants, shirt } + : [], + ), + ), + ); + if (!outfits.length) return null; + const outfit = maxBy(outfits, outfitPrice, true); + logger.debug(`Chose outfit ${outfit.hat} ${outfit.shirt} ${outfit.pants}`); + if (outfitPrice(outfit) > myMeat()) return null; + + for (const slot of relevantSlots) { + const item = outfit[slot]; + if (have_(item) || item === Item.none) { + print(`Have ${item}!`); + continue; + } + if (!buy(item)) { + logger.debug(`Failed to purchase ${item}`); + return null; + } + } + return outfit; +} + +/** + * Attempt to busk at a particular power + * @param power The power in question + * @param buyItem Whether to buy items from NPC shops to create an outfit + * @returns If we successfully busked at that power + */ +export function buskAt(power: number, buyItem = true): boolean { + if (!have()) return false; + const initialUses = get("_beretBuskingUses",0); + if (initialUses >= 5) return false; + const outfit = findOutfit(power, buyItem); + if (!outfit) return false; + const initialEquips = $slots`hat, shirt, pants`.map((slot) => + equippedItem(slot), + ); + const initialFamiliar = myFamiliar(); + const initialFamequip = equippedItem($slot`familiar`); + const { hat, pants, shirt } = outfit; + equip($slot`hat`, hat); + if (hat !== beret) { + useFamiliar($familiar`Mad Hatrack`); + equip($slot`familiar`, beret); + } + equip($slot`shirt`, shirt); + equip($slot`pants`, pants); + const taoMultiplier = have_($skill`Tao of the Terrapin`) ? 2 : 1; + try { + if ( + taoMultiplier * + (getPower(equippedItem($slot`hat`)) + + getPower(equippedItem($slot`pants`))) + + getPower(equippedItem($slot`shirt`)) !== + power + ) { + return false; + } + // eslint-disable-next-line libram/verify-constants + useSkill($skill`Beret Busking`); + return initialUses !== get("_beretBuskingUses",0); + } finally { + $slots`hat, shirt, pants`.forEach((slot, index) => + equip(slot, initialEquips[index]), + ); + if(initialFamiliar !== $familiar`Mad Hatrack` && myFamiliar() === $familiar`Mad Hatrack`) { + unequip($slot`familiar`); + } + useFamiliar(initialFamiliar); + equip($slot`familiar`, initialFamequip); + } +} + +export function buskFor( + weightedModifiers: Partial>, + buyItem?: boolean, + uselessEffects?: Effect[], +): boolean; +export function buskFor( + effects: Effect[], + buyItem?: boolean, + uselessEffects?: Effect[], +): boolean; +export function buskFor( + valueFunction: (effect: Effect, duration: number) => number, + buyItem?: boolean, + uselessEffects?: Effect[], +): boolean; +/** + * Calculate the best outfit-power you can achieve for a given busk valuation, and then busks. + * @param effectValuer Either a function that maps effect-duration pairs to values, or an object keyed by numeric modifiers with weights as values, or an array of desired effects + * @param buyItem Whether or not we should consider purchasing items from NPC stores; defaults to true + * @param uselessEffects An array (defaults to empty) of effects not to consider for the purposes of busk valuation + * @returns Whether we were successful in our endeavor + */ +export function buskFor( + effectValuer: EffectValuer, + buyItem = true, + uselessEffects: Effect[] = [], +): boolean { + const outfitPower = findOptimalOutfitPower( + effectValuer, + get("_beretBuskingUses",0), + uselessEffects, + buyItem, + ); + return buskAt(outfitPower, buyItem); +} + +function multipliers(): [number, number] { + const taoHatMultiplier = have_($skill`Tao of the Terrapin`) ? 2 : 1; + const taoPantsMultiplier = have_($skill`Tao of the Terrapin`) ? 1 : 0; + const hammerTimeMultiplier = have_($effect`Hammertime`) ? 3 : 0; + const totalPantsMultiplier = 1 + hammerTimeMultiplier + taoPantsMultiplier; + + return [taoHatMultiplier, totalPantsMultiplier]; +} + +export function reconstructOutfit(daRaw: number): { hat: Item, pants: Item, shirt: Item } { + const allItems = Item.all().filter((i) => have_(i) && canEquip(i)); + const shopItems = Item.all().filter((i) => npcPrice(i) > 0 && canEquip(i)); + allItems.push(...shopItems); + const allHats = () => have_($familiar`Mad Hatrack`) + ? allItems.filter((i) => toSlot(i) === $slot`hat`) + : [beret]; + const allPants = allItems.filter((i) => toSlot(i) === $slot`pants`); + const allShirts = allItems.filter((i) => toSlot(i) === $slot`shirt`); + + for (const hat of allHats()) { + const hatPower = multipliers()[0] * getPower(hat); + for (const shirt of allShirts) { + const shirtPower = getPower(shirt); + for (const pants of allPants) { + const pantsPower = multipliers()[1] * getPower(pants); + if (shirtPower + hatPower + pantsPower === daRaw) { + return { hat, pants, shirt }; + } + } + } + } + + return { + hat: new Item, + pants: new Item, + shirt: new Item +}; +} diff --git a/src/main.ts b/src/main.ts index 6115a60..570dd6c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,6 +8,7 @@ import { AscendQuest } from "./tasks/2 ascend"; import { RunQuests } from "./tasks/3 runleg"; import { PostRunQuests } from "./tasks/4 postrunleg"; import { deleteJunkKmails, halloween, notifyVoters, realDay, realMonth } from "./tasks/utils"; +import { itemPriceValue } from "./tasks/cluedin"; const version = "0.0.3"; @@ -20,6 +21,10 @@ export function main(command?: string): void { return; } + if (args.test) { + itemPriceValue(); + } + if (dontCS && args.halloween && args.cs) { throw `Tomorrow is halloween, run something that lets you get steel organs!`; } @@ -30,7 +35,7 @@ export function main(command?: string): void { print(`Running: candyWrapper v${version}`); - if (!args.cs && !args.smol && !args.casual && !args.robot && !args.zooto) + if (!args.cs && !args.smol && !args.casual && !args.robot && !args.zooto && !args.ih8u) throw "Undefined runtype; please choose an acceptable path"; const tasks = getTasks([AftercoreQuest(), AscendQuest(), RunQuests(), PostRunQuests()]); diff --git a/src/tasks/1 aftercoreleg.ts b/src/tasks/1 aftercoreleg.ts index 4883c0d..7216042 100644 --- a/src/tasks/1 aftercoreleg.ts +++ b/src/tasks/1 aftercoreleg.ts @@ -140,7 +140,7 @@ const stations = [ export function AftercoreQuest(): Quest { return { name: "Aftercore", - ready: () => myDaycount() >= (args.zooto ? 3 : 2) && get("kingLiberated"), + ready: () => myDaycount() >= 2 && get("kingLiberated"), completed: () => (myAdventures() === 0 && totallyDrunk() && @@ -355,6 +355,7 @@ export function AftercoreQuest(): Quest { }, { name: "Prepare for LoopCS", + ready: () => doCS, completed: () => have($item`Pizza of Legend`) && have($item`Deep Dish of Legend`) && @@ -367,6 +368,22 @@ export function AftercoreQuest(): Quest { }, tracking: "Ascension Prep" }, + { + name: "Prepare for IH8U", + ready: () => args.ih8u, + completed: () => + have($item`mini kiwi invisible dirigible`) && + have($item`mini kiwi digitized cookies`) && + have($item`mini kiwi intoxicating spirits`) && + have($item`incredible mini-pizza`), + do: (): void => { + retrieveItem($item`mini kiwi invisible dirigible`); + retrieveItem($item`mini kiwi digitized cookies`); + retrieveItem($item`mini kiwi intoxicating spirits`); + retrieveItem($item`incredible mini-pizza`); + }, + tracking: "Ascension Prep" + }, { name: "Let's do the trainset again", completed: () => TrainSet.cycle().toString === stations.toString, diff --git a/src/tasks/2 ascend.ts b/src/tasks/2 ascend.ts index 28ab0b2..f909c5f 100644 --- a/src/tasks/2 ascend.ts +++ b/src/tasks/2 ascend.ts @@ -14,6 +14,8 @@ import { targetPerms } from "./perm"; import { Quest } from "./structure"; import { toMoonSign, totallyDrunk } from "./utils"; +const skipPizza = args.cs || args.smol ? false : true + export function AscendQuest(): Quest { return { name: "Ascend", @@ -23,9 +25,10 @@ export function AscendQuest(): Quest { { name: "Do the Ascension", ready: () => - have($item`Pizza of Legend`) && + (have($item`Pizza of Legend`) && have($item`Deep Dish of Legend`) && - have($item`Calzone of Legend`), + have($item`Calzone of Legend`)) || + skipPizza, completed: () => myDaycount() === 1, //Change this do: (): void => { const [skills, permLifestyle] = targetPerms(); @@ -43,6 +46,8 @@ export function AscendQuest(): Quest { ? $path`You, Robot` : args.zooto ? $path`Z is for Zootomist` + : args.ih8u + ? $path`11 Things I Hate About U` : undefined; const lifestyle = args.casual ? 1 : 2; diff --git a/src/tasks/3 runleg.ts b/src/tasks/3 runleg.ts index d808e86..d871253 100644 --- a/src/tasks/3 runleg.ts +++ b/src/tasks/3 runleg.ts @@ -2,11 +2,15 @@ import { step } from "grimoire-kolmafia"; import { cliExecute, drink, + drinksilent, eat, + equip, fullnessLimit, + inebrietyLimit, itemAmount, myFullness, myInebriety, + retrieveItem, useSkill, visitUrl, } from "kolmafia"; @@ -27,6 +31,8 @@ const runType = () => ? args.casualscript : args.robot ? args.robotscript + : args.ih8u + ? args.ih8uscript : "autoscend"; export function howManySausagesCouldIEat() { @@ -41,6 +47,23 @@ export function howManySausagesCouldIEat() { ); } +function ih8uDrink(): boolean { + if(myInebriety() >= inebrietyLimit()) + return false; + if(!have($item`Kremlin's Greatest Briefcase`) || get("_kgbDispenserUses") >= 3 && !(have($item`mini kiwi`) && (have($item`bottle of vodka`) || have($item`bottle of gin`)))) + return false; + if (have($item`Kremlin's Greatest Briefcase`) && get("_kgbDispenserUses") < 3) { + cliExecute("briefcase unlock"); + cliExecute("briefcase collect"); + } + if (have($item`splendid martini`)) return true; + if(have($item`mini kiwi`) && (have($item`bottle of vodka`) || have($item`bottle of gin`))) { + retrieveItem($item`mini kiwitini`) + } + if(have($item`mini kiwitini`)) return true; + return false; +} + export function RunQuests(): Quest { return { name: "Ascension Run", @@ -54,7 +77,26 @@ export function RunQuests(): Quest { if (runType() === undefined || runType() === null) throw "no runtime defined"; else cliExecute(runType()); }, - tracking: "Run", + clear: "all", + tracking: args.cs ? "Ignore" : "Run", + }, + { + name: "drink ih8u", + ready: () => myInebriety() < inebrietyLimit() && args.ih8u, + completed: () => myInebriety() === inebrietyLimit() || !ih8uDrink(), + do: (): void => { + while(ih8uDrink()) { + equip($item`tuxedo shirt`); + useSkill($skill`The Ode to Booze`); + if(have($item`splendid martini`)) { + drinksilent($item`splendid martini`); + } else { + drinksilent($item`mini kiwitini`); + } + } + }, + clear: "all", + tracking: "Organs", }, { name: "drink", diff --git a/src/tasks/4 postrunleg.ts b/src/tasks/4 postrunleg.ts index 6afb0c9..d2f7be3 100644 --- a/src/tasks/4 postrunleg.ts +++ b/src/tasks/4 postrunleg.ts @@ -1,6 +1,7 @@ import { CombatStrategy } from "grimoire-kolmafia"; import { buy, + canAdventure, cliExecute, drink, Effect, @@ -161,7 +162,7 @@ export function PostRunQuests(): Quest { }, { name: "Gold Wedding Ring", - ready: () => !args.cs, + ready: () => canAdventure($location`A-Boo Peak`), completed: () => !have($skill`Comprehensive Cartography`) || myAscensions() === get("lastCartographyBooPeak"), diff --git a/src/tasks/cluedin.ts b/src/tasks/cluedin.ts new file mode 100644 index 0000000..815a03b --- /dev/null +++ b/src/tasks/cluedin.ts @@ -0,0 +1,169 @@ +import { Item, mallPrice, print } from "kolmafia"; +import { $item, have } from "libram"; + + +const mrStoreItems: [Item, number][] = [ + [$item`Apathargic Bandersnatch`, 0], + [$item`fairy-worn boots`, 0], + [$item`Source terminal`, 0], + [$item`Kramco Industries packing carton`, 0], + [$item`baby camelCalf`, 0], + [$item`emotion chip`, 0], + [$item`packaged backup camera`, 0], + [$item`packaged cold medicine cabinet`, 0], + [$item`packaged Jurassic Parka`, 0], + [$item`book of facts`, 0], + [$item`boxed bat wings`, 0], + [$item`Tome of Clip Art`, 1], + [$item`Pack of Every Card`, 1], + [$item`LI-11 Motor Pool voucher`, 1], + [$item`corked genie bottle`, 1], + [$item`Pocket Meteor Guide`, 1], + [$item`space planula`, 1], + [$item`January's Garbage Tote (unopened)`, 1], + [$item`Fourth of May Cosplay Saber kit`, 1], + [$item`packaged Pocket Professor`, 1], + [$item`mint-in-box Powerful Glove`, 1], + [$item`Comprehensive Cartographic Compendium`, 1], + [$item`packaged miniature crystal ball`, 1], + [$item`packaged industrial fire extinguisher`, 1], + [$item`grey gosling`, 1], + [$item`undrilled cosmic bowling ball`, 1], + [$item`boxed autumn-aton`, 1], + [$item`sleeping patriotic eagle`, 1], + [$item`closed-circuit pay phone`, 1], + [$item`cursed monkey glove`, 1], + [$item`shrink-wrapped Cincho de Mayo`, 1], + [$item`shrink-wrapped 2002 Mr. Store Catalog`, 1], + [$item`wrapped candy cane sword cane`, 1], + [$item`baby chest mimic`, 1], + [$item`in-the-box spring shoes`, 1], + [$item`packaged Everfull Dart Holster`, 1], + [$item`boxed Apriling band helmet`, 1], + [$item`packaged Roman Candelabra`, 1], + [$item`boxed Sept-Ember Censer`, 1], + [$item`McHugeLarge deluxe ski set`, 1], + [$item`Clan VIP Lounge invitation`, 2], + [$item`Schmalz's First Prize Beer`, 2], + [$item`Moping Artistic Goth Kid`, 2], + [$item`The Smith's Tome`, 2], + [$item`Granny Tood's Thanksgarden Catalog`, 2], + [$item`Witchess Set`, 2], + [$item`heart-shaped crate`, 2], + [$item`suspicious package`, 2], + [$item`xo-skeleton-in-a-box`, 2], + [$item`Neverending Party invitation envelope`, 2], + [$item`latte lovers club card`, 2], + [$item`voter registration form`, 2], + [$item`mint condition Lil' Doctor™ bag`, 2], + [$item`unopened diabolic pizza cube box`, 2], + [$item`Unopened Eight Days a Week Pill Keeper`, 2], + [$item`combat lover's locket lockbox`, 2], + [$item`packaged model train set`, 2], + [$item`boxed august scepter`, 2], + [$item`boxed Mayam Calendar`, 2], + [$item`Sealed TakerSpace letter of Marque`, 2], + [$item`Libram of Candy Heart Summoning`, 3], + [$item`Libram of BRICKOs`, 3], + [$item`Order of the Green Thumb Order Form`, 3], + [$item`bottle of lovebug pheromones`, 3], + [$item`Chateau Mantegna room key`, 3], + [$item`yellow puck`, 3], + [$item`yellow puck with a bow on it`, 3], + [$item`machine elf capsule`, 3], + [$item`disconnected intergnat`, 3], + [$item`DIY protonic accelerator kit`, 3], + [$item`li'l orphan tot`, 3], + [$item`potted tea tree`, 3], + [$item`X-32-F snowman crate`, 3], + [$item`kitten burglar`, 3], + [$item`red-spotted snapper roe`, 3], + [$item`bagged Cargo Cultist Shorts`, 3], + [$item`packaged familiar scrapbook`, 3], + [$item`power seed`, 3], + [$item`packaged Daylight Shavings Helmet`, 3], + [$item`bottled Vampire Vintner`, 3], + [$item`mint condition magnifying glass`, 3], + [$item`undamaged Unbreakable Umbrella`, 3], + [$item`packaged June cleaver`, 3], + [$item`unopened tiny stillsuit`, 3], + [$item`mummified entombed cookbookbat`, 3], + [$item`Rock Garden Guide`, 3], + [$item`peace turkey outline`, 3], + [$item`miniscule temporal rip`, 4], + [$item`Tome of Snowcone Summoning`, 4], + [$item`navel ring of navel gazing`, 4], + [$item`Libram of Divine Favors`, 4], + [$item`sane hatrack`, 4], + [$item`spooky rattling cigar box`, 4], + [$item`container of Spooky Putty`, 4], + [$item`floaty stone sphere`, 4], + [$item`Libram of Love Songs`, 4], + [$item`panicked kernel`, 4], + [$item`Crown of Thrones`, 4], + [$item`Greatest American Pants`, 4], + [$item`a cute angel`, 4], + [$item`Mint Salton Pepper's Peppermint Seed Catalog`, 4], + [$item`mysterious chest`, 4], + [$item`Camp Scout backpack`, 4], + [$item`can of Rain-Doh`, 4], + [$item`Libram of Resolutions`, 4], + [$item`Unagnimated Gnome`, 4], + [$item`avatar of the Unconscious Collective`, 4], + [$item`deanimated reanimator's coffin`, 4], + [$item`Libram of Pulled Taffy`, 4], + [$item`Buddy Bjorn`, 4], + [$item`fist turkey outline`, 4], + [$item`Little Geneticist DNA-Splicing Lab`, 4], + [$item`still grill`, 4], + [$item`shrine to the Barrel god`, 4], + [$item`haunted doghouse`, 4], + [$item`portable Mayo Clinic`, 4], + [$item`Dear Past Self Package`, 4], + [$item`LT&T telegraph office deed`, 4], + [$item`New-You Club Membership Form`, 4], + [$item`pantogram`, 4], + [$item`unpowered Robortender`, 4], + [$item`Bastille Battalion control rig crate`, 4], + [$item`Boxing Day care package`, 4], + [$item`SongBoom™ BoomBox Box`, 4], + [$item`Beach Comb Box`, 4], + [$item`rune-strewn spoon cocoon`, 4], + [$item`vampyric cloake pattern`, 4], + [$item`packaged knock-off retro superhero cape`, 4], + [$item`unopened Bird-a-Day calendar`, 4], + [$item`shortest-order cook`, 4], + [$item`designer sweatpants (new old stock)`, 4], + [$item`S.I.T. Course Completion Certificate`, 4], + [$item`Dark Jill-of-All-Trades`, 4], + [$item`Black and White Apron Enrollment Form`, 4], + [$item`CyberRealm keycode`, 4], +]; + +const tierValues = new Map([ + [0, 20], + [1, 11], + [2, 6], + [3, 3], + [4, 1], +]); + +export function itemPriceValue() { + // Filter out items the player already owns + const filteredItems = mrStoreItems + .filter(([item]) => !have(item)) + .map(([item, tier]) => { + const turnsSaved = tierValues.get(tier) ?? Infinity; + const price = mallPrice(item); + const pricePerTurn = turnsSaved !== Infinity ? price / turnsSaved : Infinity; + + return { item, tier, pricePerTurn }; + }) + .filter(({ pricePerTurn }) => pricePerTurn !== Infinity) // Remove invalid entries + .sort((a, b) => a.pricePerTurn - b.pricePerTurn); // Sort by pricePerTurn (ascending) + + // Print the sorted list + filteredItems.forEach(({ item, tier, pricePerTurn }) => { + print(`Item: ${item}, Tier: ${tier}, Price per turn saved: ${pricePerTurn.toFixed(2)}`); + }); +} diff --git a/src/tasks/repeatableTasks.ts b/src/tasks/repeatableTasks.ts index 5f9892d..fc7cfad 100644 --- a/src/tasks/repeatableTasks.ts +++ b/src/tasks/repeatableTasks.ts @@ -3,6 +3,7 @@ import { availableAmount, canAdventure, cliExecute, + equip, Familiar, fullnessLimit, getCampground, @@ -15,6 +16,7 @@ import { holiday, inebrietyLimit, mallPrice, + maximize, myAdventures, myClass, myDaycount, @@ -25,10 +27,12 @@ import { myMaxhp, myPrimestat, mySpleenUse, + print, restoreHp, retrieveItem, spleenLimit, toInt, + toItem, toSkill, use, useFamiliar, @@ -43,14 +47,18 @@ import { $item, $items, $location, + $monster, $skill, + $skills, $stat, AprilingBandHelmet, AsdonMartin, + CombatLoversLocket, get, getTodaysHolidayWanderers, have, Macro, + PocketProfessor, uneffect, } from "libram"; @@ -58,7 +66,7 @@ import { args } from "../args"; import { Task } from "./structure"; import { getGarden, maxBase, nextCyberZone, pantogram, pantogramReady, stooperDrunk, totallyDrunk } from "./utils"; -import { leprecondoTask } from "../engine/leprecondo"; +import { buskAt, findOptimalOutfitPower, reconstructOutfit } from "../beret"; const bestFam = () => famCheck($familiar`Pocket Professor`) @@ -295,6 +303,28 @@ export function postRunQuests(): Task[] { }, limit: { tries: 1 }, }, + { + name: "Beret? Beret.", + ready: () => have(toItem(11919)), + completed: () => get("_beretBuskingUses",0) >= 5, + do: () => { + const uselessEffects = $effects`How to Scam Tourists, Empathy`; + let busk = get("_beretBuskingUses",0); + print(`Busking starting at ${busk} uses.`) + for(; busk <5; busk++) { + const best = findOptimalOutfitPower( + { + "Familiar Weight": 10, + }, busk, uselessEffects, true + ); + const outfit = reconstructOutfit(best); + print(`Outfit is: ${outfit?.hat}, ${outfit?.pants}, ${outfit?.shirt}`); + print(`Busking at ${best} power.`); + buskAt(best, true); + } + }, + limit: { tries: 5 }, + }, { name: "Candy Deviler", // eslint-disable-next-line libram/verify-constants @@ -336,7 +366,6 @@ export function preRunQuests(): Task[] { }, limit: { tries: 1 }, }, - leprecondoTask(), { name: "Unpack Duffel Bag", completed: () => duffo, @@ -345,6 +374,13 @@ export function preRunQuests(): Task[] { duffo = true; }, }, + { + name: "Pantogramming", + ready: () => pantogramReady() && args.casual, + completed: () => pantogram(), + do: () => pantogram(), + tracking: "Farming Prep" + }, { name: "LGR Seed", ready: () => @@ -354,7 +390,7 @@ export function preRunQuests(): Task[] { !(args.cs || args.zooto), completed: () => get("_stenchAirportToday") || get("stenchAirportAlways"), do: () => use($item`one-day ticket to Dinseylandfill`), - tracking: "Garbo", + tracking: "Farming Prep", }, { name: "Break Stone", @@ -369,7 +405,7 @@ export function preRunQuests(): Task[] { } let garboDone1 = false; -let garboDone2 = false +let garboDone2 = false; export function noBarf(): Task[] { return [ @@ -383,6 +419,40 @@ export function noBarf(): Task[] { do: () => cliExecute("consume ALL"), tracking: "Organs" }, + { + name: "PProf Penguin Chain", + ready: () => ((args.garbo.includes("penguin") && args.garbo.includes(`target="black crayon penguin"`) && myDaycount() === 0) + || (args.garboascend.includes("penguin") && args.garboascend.includes(`target="black crayon penguin"`) && myDaycount() > 0)) && + CombatLoversLocket.canReminisce($monster`Black Crayon Flower`) && + PocketProfessor.have() && + PocketProfessor.lecturesDelivered() < 3, + prepare: () => { + if (!have($item`Pocket Professor memory chip`)) { + retrieveItem(1, $item`Pocket Professor memory chip`) + } + + useFamiliar($familiar`Pocket Professor`); + maximize(`10 familiar weight, -tie, 5.25 Meat Drop, -"equip Amulet of Perpetual Darkness", -"equip Buddy Bjorn", -"equip Roman Candelabra", -"equip Spooky Putty ball", -"equip Spooky Putty leotard", -"equip Spooky Putty mitre", -"equip Spooky Putty snake", -"equip broken champagne bottle", -"equip cheap sunglasses", -"equip dice-shaped backpack", -"equip papier-masque", -"equip papier-mitre", -"equip smoke ball", -"equip stinky fannypack", 100 "bonus pantogram pants", 124.26 "bonus June cleaver", 135 "bonus Crown of Thrones", 180 "bonus Mr. Screege's spectacles", 222.92 "bonus mafia thumb ring", 253.61 "bonus can of mixed everything", 284 "bonus lucky gold ring", 6.25 "bonus Powerful Glove", 700 "bonus mafia pointer finger ring"`, false); + $skills`Empathy of the Newt, Leash of Linguini`.forEach((sk) => useSkill(sk)); + $items`Pocket Professor memory chip, tearaway pants`.forEach((it) => equip(it)); + }, + completed: () => PocketProfessor.currentlyAvailableLectures() === 0, + do: () => CombatLoversLocket.reminisce($monster`Black Crayon Flower`,""), + combat: new CombatStrategy().macro( + Macro.externalIf(PocketProfessor.currentlyAvailableLectures() > 0, + Macro.trySkill($skill`lecture on relativity`) + .trySkill($skill`Sing Along`) + .trySkill($skill`Bowl Straight Up`) + .trySkill($skill`Tear Away your Pants!`) + .trySkillRepeat($skill`Saucestorm`), + Macro.trySkill($skill`Sing Along`) + .trySkill($skill`Bowl Straight Up`) + .trySkill($skill`Tear Away your Pants!`) + .trySkillRepeat($skill`Saucestorm`) + ) + ), + tracking: "Garbo" + }, { name: "Pantogramming", ready: () => pantogramReady(), diff --git a/yarn.lock b/yarn.lock index 7617f7c..87d868d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3145,9 +3145,9 @@ kind-of@^6.0.2: integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== kolmafia@^5.27815.0: - version "5.27815.0" - resolved "https://registry.yarnpkg.com/kolmafia/-/kolmafia-5.27815.0.tgz#d591a9a76ae9fd5ae469647e6606d55ca2ba0e95" - integrity sha512-M175RtbW2t5waudPQPXU8ypyzsme9ghi7/jY9tnCJmIrnKrvVBRcKrwCWdAs6E5WVz6DeE2YKmbTHJ2tDqqtxQ== + version "5.28554.0" + resolved "https://registry.yarnpkg.com/kolmafia/-/kolmafia-5.28554.0.tgz#a21b30452752831258534ec7de2e1da7228ab91f" + integrity sha512-I8WhVCpu11STkUswCfVmTVAARbLjVcLuQDOm8Tqw4k/fi4lrGD/IzH74y5zdd2eMw9kItyWDyYv9x3rnyiA3Wg== kuler@^2.0.0: version "2.0.0" @@ -3162,10 +3162,10 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -libram@^0.10.7: - version "0.10.7" - resolved "https://registry.yarnpkg.com/libram/-/libram-0.10.7.tgz#814d2f9d6c40b3ece15221b541eff63b0ee24b78" - integrity sha512-KRbeJj2O2D1D466ZItLswaXlcAJaGv52K92CKd3j3tEuIrBV4WamZD6gBW91YJVvxem4zjfJDuylVByNrc7gaQ== +libram@^0.10.9: + version "0.10.9" + resolved "https://registry.yarnpkg.com/libram/-/libram-0.10.9.tgz#5d0b54aaea51edf3cb7f9f47b1c006ed48d5287d" + integrity sha512-suSh6NSW2J4lf8CbL3GnU+39+i0LdLMOuEopdeT7MQ0kXW30j4kWlxr+NWky72HewNQZHmdyihXoYabsVfrbpQ== dependencies: html-entities "^2.5.2" From d706070169f7026b72196ae890e740bc596ac23a Mon Sep 17 00:00:00 2001 From: Ignose Date: Sun, 10 Aug 2025 20:22:48 -0400 Subject: [PATCH 21/24] Commit so I can work on other things --- package.json | 2 +- src/tasks/1 aftercoreleg.ts | 26 ++++++++++++++++++++++ src/tasks/repeatableTasks.ts | 42 ++++++++++++++++++++++++++++++++++++ yarn.lock | 8 +++---- 4 files changed, 73 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 8e74670..6982205 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "babel-loader": "^9.1.3", "build": "^0.1.4", "grimoire-kolmafia": "^0.3.25", - "kolmafia": "^5.27815.0", + "kolmafia": "^5.28591.0", "libram": "^0.10.9", "mafia-shared-relay": "0.0.7", "webpack": "^5.89.0" diff --git a/src/tasks/1 aftercoreleg.ts b/src/tasks/1 aftercoreleg.ts index 7216042..c1f0aa3 100644 --- a/src/tasks/1 aftercoreleg.ts +++ b/src/tasks/1 aftercoreleg.ts @@ -30,8 +30,10 @@ import { $effects, $familiar, $item, + $items, $location, $skill, + byStat, get, getTodaysHolidayWanderers, have, @@ -269,6 +271,27 @@ export function AftercoreQuest(): Quest { limit: { tries: 30 }, tracking: "Bonus" }, + { + name: "Trip Scrip", + ready: () => args.ih8u || args.smol || args.casual, + completed: () => have($item`Shore Inc. Ship Trip Scrip`) || myInebriety() > inebrietyLimit(), + do: $location`The Shore, Inc. Travel Agency`, + outfit: () => { + if (!get("candyCaneSwordShore")) return { equip: $items`candy cane sword cane` }; + else return {}; + }, + choices: () => { + const swordReady = haveEquipped($item`candy cane sword cane`) && !get("candyCaneSwordShore"); + const statChoice = byStat({ + Muscle: 1, + Mysticality: 2, + Moxie: 3, + }); + return { 793: swordReady ? 5 : statChoice }; + }, + limit: { tries: 1 }, + tracking: "Trip Scrip" + }, { name: "Garbo (Drunk)", ready: () => have($item`Drunkula's wineglass`), @@ -377,6 +400,9 @@ export function AftercoreQuest(): Quest { have($item`mini kiwi intoxicating spirits`) && have($item`incredible mini-pizza`), do: (): void => { + if(args.ih8uscript.includes("speed")) { + retrieveItem($item`Boris's key lime pie`) + } retrieveItem($item`mini kiwi invisible dirigible`); retrieveItem($item`mini kiwi digitized cookies`); retrieveItem($item`mini kiwi intoxicating spirits`); diff --git a/src/tasks/repeatableTasks.ts b/src/tasks/repeatableTasks.ts index fc7cfad..5f319a5 100644 --- a/src/tasks/repeatableTasks.ts +++ b/src/tasks/repeatableTasks.ts @@ -11,10 +11,12 @@ import { getClanName, getWorkshed, guildStoreAvailable, + handlingChoice, haveEffect, hippyStoneBroken, holiday, inebrietyLimit, + lastChoice, mallPrice, maximize, myAdventures, @@ -30,7 +32,10 @@ import { print, restoreHp, retrieveItem, + runChoice, spleenLimit, + storageAmount, + takeStorage, toInt, toItem, toSkill, @@ -59,6 +64,7 @@ import { have, Macro, PocketProfessor, + set, uneffect, } from "libram"; @@ -96,6 +102,35 @@ export function postRunQuests(): Task[] { do: () => cliExecute("breakfast"), tracking: "Breakfast" }, + { + name: "Radio", + // eslint-disable-next-line libram/verify-constants + ready: () => have($item`Allied Radio Backpack`) && get("_alliedRadioDropsUsed", 0) < 3, + // eslint-disable-next-line libram/verify-constants + completed: () => get("_alliedRadioDropsUsed", 0) >= 3, + do: () => { + const visitRadio = () => visitUrl(`inventory.php?action=requestdrop&pwd`); + visitRadio(); + if (!handlingChoice() || lastChoice() !== 1563) visitRadio(); + runChoice(1, `request=radio`); + }, + limit: { tries: 3 }, + }, + { + name: "Mobius Seed", + // eslint-disable-next-line libram/verify-constants + ready: () => have($item`Möbius ring`), + completed: () => get("_mobiusSeeded", false), + prepare: () => { + // eslint-disable-next-line libram/verify-constants + equip($item`Möbius ring`), + equip($item`Greatest American Pants`) + }, + do: () => $location`Noob Cave`, + combat: new CombatStrategy().macro(Macro.runaway()), + post: () => set("_mobiusSeeded", true), + tracking: "Farming Prep" + }, { name: "Harvest Garden", completed: () => @@ -381,6 +416,13 @@ export function preRunQuests(): Task[] { do: () => pantogram(), tracking: "Farming Prep" }, + { + name: "Trip Scrip", + ready: () => args.ih8u || args.smol || args.robot, + completed: () => get("_roninStoragePulls").includes(`${$item`Shore Inc. Ship Trip Scrip`.id}`) || storageAmount($item`Shore Inc. Ship Trip Scrip`) === 0, + do: () => takeStorage($item`Shore Inc. Ship Trip Scrip`,1), + tracking: "Farming Prep" + }, { name: "LGR Seed", ready: () => diff --git a/yarn.lock b/yarn.lock index 87d868d..a609a12 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3144,10 +3144,10 @@ kind-of@^6.0.2: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -kolmafia@^5.27815.0: - version "5.28554.0" - resolved "https://registry.yarnpkg.com/kolmafia/-/kolmafia-5.28554.0.tgz#a21b30452752831258534ec7de2e1da7228ab91f" - integrity sha512-I8WhVCpu11STkUswCfVmTVAARbLjVcLuQDOm8Tqw4k/fi4lrGD/IzH74y5zdd2eMw9kItyWDyYv9x3rnyiA3Wg== +kolmafia@^5.28591.0: + version "5.28591.0" + resolved "https://registry.yarnpkg.com/kolmafia/-/kolmafia-5.28591.0.tgz#c73ab814843a1213be4163df11eb90ed2de89620" + integrity sha512-9TGUYaipWtbuPmOYJOSG0F+7QU6dbuBGNNiNOIRaESKgpWNIAduhrJ1jLoJcbGmPRfFjalDa8nb3G83VI5mczg== kuler@^2.0.0: version "2.0.0" From 42cd28302f6421ccca97c05524ec55f1632128eb Mon Sep 17 00:00:00 2001 From: Ignose Date: Thu, 9 Oct 2025 14:50:51 -0400 Subject: [PATCH 22/24] Commit so I can work on more other things --- src/tasks/1 aftercoreleg.ts | 3 -- src/tasks/2 ascend.ts | 16 ++++++- src/tasks/repeatableTasks.ts | 43 ++--------------- src/tasks/utils.ts | 91 ++++++++++++++++++++++++++++++++++++ yarn.lock | 18 +++---- 5 files changed, 119 insertions(+), 52 deletions(-) diff --git a/src/tasks/1 aftercoreleg.ts b/src/tasks/1 aftercoreleg.ts index c1f0aa3..cf62d44 100644 --- a/src/tasks/1 aftercoreleg.ts +++ b/src/tasks/1 aftercoreleg.ts @@ -400,9 +400,6 @@ export function AftercoreQuest(): Quest { have($item`mini kiwi intoxicating spirits`) && have($item`incredible mini-pizza`), do: (): void => { - if(args.ih8uscript.includes("speed")) { - retrieveItem($item`Boris's key lime pie`) - } retrieveItem($item`mini kiwi invisible dirigible`); retrieveItem($item`mini kiwi digitized cookies`); retrieveItem($item`mini kiwi intoxicating spirits`); diff --git a/src/tasks/2 ascend.ts b/src/tasks/2 ascend.ts index f909c5f..1c9c2e6 100644 --- a/src/tasks/2 ascend.ts +++ b/src/tasks/2 ascend.ts @@ -1,18 +1,19 @@ import { cliExecute, + equip, handlingChoice, myAdventures, myDaycount, runChoice, visitUrl, } from "kolmafia"; -import { $class, $item, $path, ascend, CursedMonkeyPaw, have } from "libram"; +import { $class, $item, $path, $skill, $skills, ascend, CursedMonkeyPaw, have } from "libram"; import { args } from "../args"; import { targetPerms } from "./perm"; import { Quest } from "./structure"; -import { toMoonSign, totallyDrunk } from "./utils"; +import { availableCasts, castDownTo, toMoonSign, totallyDrunk } from "./utils"; const skipPizza = args.cs || args.smol ? false : true @@ -22,6 +23,17 @@ export function AscendQuest(): Quest { ready: () => myAdventures() === 0 && totallyDrunk(), completed: () => myDaycount() === 1, tasks: [ + { + name: "Spend them stats grrrrl", + completed: () => availableCasts($skill`BCZ: Prepare Spinal Tapas`,0) === 0 && + availableCasts($skill`BCZ: Craft a Pheromone Cocktail`,0) === 0 && + availableCasts($skill`BCZ: Create Blood Thinner`,0) === 0, + do: () => { + equip($item`blood cubic zirconia`); + $skills`BCZ: Prepare Spinal Tapas, BCZ: Craft a Pheromone Cocktail, BCZ: Create Blood Thinner`.forEach((sk) => castDownTo(sk, 0)); + }, + tracking: "Other" + }, { name: "Do the Ascension", ready: () => diff --git a/src/tasks/repeatableTasks.ts b/src/tasks/repeatableTasks.ts index 5f319a5..130439e 100644 --- a/src/tasks/repeatableTasks.ts +++ b/src/tasks/repeatableTasks.ts @@ -9,10 +9,8 @@ import { getCampground, getClanLounge, getClanName, - getWorkshed, guildStoreAvailable, handlingChoice, - haveEffect, hippyStoneBroken, holiday, inebrietyLimit, @@ -57,7 +55,6 @@ import { $skills, $stat, AprilingBandHelmet, - AsdonMartin, CombatLoversLocket, get, getTodaysHolidayWanderers, @@ -96,6 +93,11 @@ export function postRunQuests(): Task[] { completed: () => !args.clan || getClanName().toLowerCase() === args.clan.toLowerCase(), do: () => cliExecute(`/whitelist ${args.clan}`), }, + { + name: "Set Garbo Pref", + completed: () => get("_garbo_beSelfish",false), + do: () => set("_garbo_beSelfish",true), + }, { name: "Breakfast", completed: () => get("breakfastCompleted"), @@ -116,21 +118,6 @@ export function postRunQuests(): Task[] { }, limit: { tries: 3 }, }, - { - name: "Mobius Seed", - // eslint-disable-next-line libram/verify-constants - ready: () => have($item`Möbius ring`), - completed: () => get("_mobiusSeeded", false), - prepare: () => { - // eslint-disable-next-line libram/verify-constants - equip($item`Möbius ring`), - equip($item`Greatest American Pants`) - }, - do: () => $location`Noob Cave`, - combat: new CombatStrategy().macro(Macro.runaway()), - post: () => set("_mobiusSeeded", true), - tracking: "Farming Prep" - }, { name: "Harvest Garden", completed: () => @@ -162,26 +149,6 @@ export function postRunQuests(): Task[] { // eslint-disable-next-line libram/verify-constants use($item`S.I.T. Course Completion Certificate`), }, - { - name: "Drive Observantly", - completed: () => - get("dailyDungeonDone") || - getWorkshed() !== $item`Asdon Martin keyfob (on ring)` || - haveEffect($effect`Driving Observantly`) >= - (totallyDrunk() || !have($item`Drunkula's wineglass`) - ? myAdventures() - : myAdventures() + 60), - do: () => - AsdonMartin.drive( - $effect`Driving Observantly`, - totallyDrunk() || !have($item`Drunkula's wineglass`) - ? myAdventures() - : myAdventures() + 60, - false, - ), - limit: { tries: 5 }, - tracking: "Other" - }, { name: "Restore HP", completed: () => myHp() > 0.5 * myMaxhp(), diff --git a/src/tasks/utils.ts b/src/tasks/utils.ts index 5338de3..9079c44 100644 --- a/src/tasks/utils.ts +++ b/src/tasks/utils.ts @@ -17,6 +17,7 @@ import { mallPrice, Monster, myAdventures, + myBasestat, myDaycount, myFamiliar, myFullness, @@ -28,11 +29,14 @@ import { print, putCloset, retrieveItem, + Skill, spleenLimit, + Stat, storageAmount, toItem, urlEncode, use, + useSkill, visitUrl, } from "kolmafia"; import { @@ -43,6 +47,7 @@ import { $location, $phylum, $skill, + $stat, Kmail as _Kmail, gameDay, get, @@ -468,3 +473,89 @@ export function pantogram(): boolean { return true; } +/** + * @returns Whether or not you have the blood cubic zirconia + */ +export function have_(): boolean { + return have($item`blood cubic zirconia`); +} + +const BCZSKILLCOST = new Map([ + [0, 11], + [1, 23], + [2, 37], + [3, 110], + [4, 230], + [5, 370], + [6, 1100], + [7, 2300], + [8, 3700], + [9, 11000], + [10, 23000], + [11, 37000], + [12, 420000], + [13, 1100000], + [14, 2300000], + [15, 3700000], + [16, 11000000], + [17, 23000000], + [18, 37000000], + [19, 110000000], + [20, 230000000], + [21, 370000000], +]); + +export const BCZCOSTS = new Map([ + [$skill`BCZ: Blood Geyser`, $stat`SubMuscle`], + [$skill`BCZ: Refracted Gaze`, $stat`SubMysticality`], + [$skill`BCZ: Sweat Bullets`, $stat`SubMoxie`], + [$skill`BCZ: Blood Bath`, $stat`SubMuscle`], + [$skill`BCZ: Craft a Pheromone Cocktail`, $stat`SubMoxie`], + [$skill`BCZ: Create Blood Thinner`, $stat`SubMuscle`], + [$skill`BCZ: Dial it up to 11`, $stat`SubMysticality`], + [$skill`BCZ: Prepare Spinal Tapas`, $stat`SubMysticality`], + [$skill`BCZ: Sweat Equity`, $stat`SubMoxie`], +]); + +/** + * @param skill The BCZ skill to check. + * @param statFloor Minimum base stat you want to keep. + * @returns The number of casts you can achieve of the selected skill before hitting the given stat floor. + */ +export function availableCasts(skill: Skill, statFloor: number): number { + if (!have_()) return 0; + + const stat = BCZCOSTS.get(skill); + if (!stat) return 0; + + // const currentStat = myBasestat(stat); + const currentStat = myBasestat(stat); + const timesCast = skill.timescast ?? 0; + const subStatFloor = statFloor ** 2; + + let casts = 0; + let remainingStat = currentStat; + + for (let i = timesCast; i < BCZSKILLCOST.size; i++) { + const nextCost = BCZSKILLCOST.get(i); + if (nextCost === undefined) break; + if (remainingStat - nextCost < subStatFloor) break; + remainingStat -= nextCost; + casts++; + } + + return casts; +} + +/** + * @param skill The BCZ skill to cast. + * @param statFloor Minimum base stat you want to keep. + * @returns Whether you successfully cast the spell. + */ +export function castDownTo(skill: Skill, statFloor: number): boolean { + if (!have_() || !BCZCOSTS.get(skill)) return false; + const available = availableCasts(skill, statFloor); + if (available === 0) return false; + + return useSkill(skill, available); +} diff --git a/yarn.lock b/yarn.lock index a609a12..94578f3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2358,9 +2358,9 @@ eslint-config-prettier@^8.8.0: integrity sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA== eslint-plugin-libram@^0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/eslint-plugin-libram/-/eslint-plugin-libram-0.4.24.tgz#2c5dcd46a4e280071fc9997a71f911bc3770598c" - integrity sha512-TrlblOJjM92p0wMaAyO1P2vrG3JDi6ZZguBZvB19FhynFNyehIM9P2GPUPkKA3Lyo7l1L0j3ofOpRzHtgYK8xw== + version "0.4.33" + resolved "https://registry.yarnpkg.com/eslint-plugin-libram/-/eslint-plugin-libram-0.4.33.tgz#d76c0a16e5869e050e88f1c8a23f7081df49510c" + integrity sha512-QLajjkVuPNoiimfBH4iKHWXZCze8hnkUja+DHo/7ff0ddb1AQf6V2lk/nxXku/t+AtLVw2r0s1AliuZv4HV/8Q== dependencies: html-entities "^2.5.2" requireindex "~1.2.0" @@ -2850,9 +2850,9 @@ hasown@^2.0.0: function-bind "^1.1.2" html-entities@^2.5.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.2.tgz#201a3cf95d3a15be7099521620d19dfb4f65359f" - integrity sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA== + version "2.6.0" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.6.0.tgz#7c64f1ea3b36818ccae3d3fb48b6974208e984f8" + integrity sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ== human-signals@^2.1.0: version "2.1.0" @@ -3145,9 +3145,9 @@ kind-of@^6.0.2: integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== kolmafia@^5.28591.0: - version "5.28591.0" - resolved "https://registry.yarnpkg.com/kolmafia/-/kolmafia-5.28591.0.tgz#c73ab814843a1213be4163df11eb90ed2de89620" - integrity sha512-9TGUYaipWtbuPmOYJOSG0F+7QU6dbuBGNNiNOIRaESKgpWNIAduhrJ1jLoJcbGmPRfFjalDa8nb3G83VI5mczg== + version "5.28725.0" + resolved "https://registry.yarnpkg.com/kolmafia/-/kolmafia-5.28725.0.tgz#b4699c3b1bdb790d15ef0f127370041c4b8feb65" + integrity sha512-dPkZ1D0OOK20cMwsIYDKHU1/3ADy89k4rNfQRfyu3WAib3N2SWxoEA5/ohjyN1WnZ1U8aZ08GjwCr/WFL3m9dg== kuler@^2.0.0: version "2.0.0" From fcbc27025ffc63cdbf8f3c09db041f745062f7cb Mon Sep 17 00:00:00 2001 From: Ignose Date: Mon, 24 Nov 2025 10:17:13 -0500 Subject: [PATCH 23/24] Improving too many things to count --- package.json | 2 +- src/main.ts | 6 +-- src/tasks/2 ascend.ts | 1 + src/tasks/3 runleg.ts | 16 ++++-- src/tasks/4 postrunleg.ts | 2 + src/tasks/utils.ts | 100 +++++++++++++++++++++++++------------- yarn.lock | 8 +-- 7 files changed, 88 insertions(+), 47 deletions(-) diff --git a/package.json b/package.json index 6982205..cac61a0 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "build": "^0.1.4", "grimoire-kolmafia": "^0.3.25", "kolmafia": "^5.28591.0", - "libram": "^0.10.9", + "libram": "^0.11.9", "mafia-shared-relay": "0.0.7", "webpack": "^5.89.0" }, diff --git a/src/main.ts b/src/main.ts index 570dd6c..8a067bc 100644 --- a/src/main.ts +++ b/src/main.ts @@ -7,8 +7,8 @@ import { AftercoreQuest } from "./tasks/1 aftercoreleg"; import { AscendQuest } from "./tasks/2 ascend"; import { RunQuests } from "./tasks/3 runleg"; import { PostRunQuests } from "./tasks/4 postrunleg"; -import { deleteJunkKmails, halloween, notifyVoters, realDay, realMonth } from "./tasks/utils"; -import { itemPriceValue } from "./tasks/cluedin"; +import { castDownTo, deleteJunkKmails, halloween, notifyVoters, realDay, realMonth } from "./tasks/utils"; +import { $skill } from "libram"; const version = "0.0.3"; @@ -22,7 +22,7 @@ export function main(command?: string): void { } if (args.test) { - itemPriceValue(); + castDownTo($skill`BCZ: Sweat Equity`, 100); } if (dontCS && args.halloween && args.cs) { diff --git a/src/tasks/2 ascend.ts b/src/tasks/2 ascend.ts index 1c9c2e6..815116c 100644 --- a/src/tasks/2 ascend.ts +++ b/src/tasks/2 ascend.ts @@ -25,6 +25,7 @@ export function AscendQuest(): Quest { tasks: [ { name: "Spend them stats grrrrl", + ready: () => have($item`blood cubic zirconia`), completed: () => availableCasts($skill`BCZ: Prepare Spinal Tapas`,0) === 0 && availableCasts($skill`BCZ: Craft a Pheromone Cocktail`,0) === 0 && availableCasts($skill`BCZ: Create Blood Thinner`,0) === 0, diff --git a/src/tasks/3 runleg.ts b/src/tasks/3 runleg.ts index d871253..04737e9 100644 --- a/src/tasks/3 runleg.ts +++ b/src/tasks/3 runleg.ts @@ -50,13 +50,13 @@ export function howManySausagesCouldIEat() { function ih8uDrink(): boolean { if(myInebriety() >= inebrietyLimit()) return false; - if(!have($item`Kremlin's Greatest Briefcase`) || get("_kgbDispenserUses") >= 3 && !(have($item`mini kiwi`) && (have($item`bottle of vodka`) || have($item`bottle of gin`)))) + if (have($item`splendid martini`)) return true; + if(!have($item`Kremlin's Greatest Briefcase`) && !(have($item`mini kiwi`) && (have($item`bottle of vodka`) || have($item`bottle of gin`)))) return false; if (have($item`Kremlin's Greatest Briefcase`) && get("_kgbDispenserUses") < 3) { cliExecute("briefcase unlock"); cliExecute("briefcase collect"); } - if (have($item`splendid martini`)) return true; if(have($item`mini kiwi`) && (have($item`bottle of vodka`) || have($item`bottle of gin`))) { retrieveItem($item`mini kiwitini`) } @@ -85,15 +85,21 @@ export function RunQuests(): Quest { ready: () => myInebriety() < inebrietyLimit() && args.ih8u, completed: () => myInebriety() === inebrietyLimit() || !ih8uDrink(), do: (): void => { - while(ih8uDrink()) { + if (have($item`Kremlin's Greatest Briefcase`) && get("_kgbDispenserUses") < 3) { + cliExecute("briefcase unlock"); + cliExecute("briefcase collect"); + } + while(myInebriety() < inebrietyLimit() && have($item`splendid martini`)) { equip($item`tuxedo shirt`); useSkill($skill`The Ode to Booze`); if(have($item`splendid martini`)) { drinksilent($item`splendid martini`); - } else { - drinksilent($item`mini kiwitini`); } } + while(myFullness < fullnessLimit && have($item`mini kiwi`,3)) { + retrieveItem($item`mini kiwi digitized cookies`); + eat($item`mini kiwi digitized cookies`) + } }, clear: "all", tracking: "Organs", diff --git a/src/tasks/4 postrunleg.ts b/src/tasks/4 postrunleg.ts index d2f7be3..bff041e 100644 --- a/src/tasks/4 postrunleg.ts +++ b/src/tasks/4 postrunleg.ts @@ -52,8 +52,10 @@ import { args } from "../args"; import { chrono, crimbo, garboWeen, noBarf, postRunQuests } from "./repeatableTasks"; import { Quest } from "./structure"; import { + availableCasts, backstageItemsDone, bestFam, + castDownTo, doneAdventuring, haveAll, maxBase, diff --git a/src/tasks/utils.ts b/src/tasks/utils.ts index 9079c44..c5dc18d 100644 --- a/src/tasks/utils.ts +++ b/src/tasks/utils.ts @@ -480,32 +480,26 @@ export function have_(): boolean { return have($item`blood cubic zirconia`); } -const BCZSKILLCOST = new Map([ - [0, 11], - [1, 23], - [2, 37], - [3, 110], - [4, 230], - [5, 370], - [6, 1100], - [7, 2300], - [8, 3700], - [9, 11000], - [10, 23000], - [11, 37000], - [12, 420000], - [13, 1100000], - [14, 2300000], - [15, 3700000], - [16, 11000000], - [17, 23000000], - [18, 37000000], - [19, 110000000], - [20, 230000000], - [21, 370000000], -]); +/** + * @param skill The BCZ skill to check. + * @returns The subtat cost to cast the skill. + */ +export function skillCost(skill: Skill): number { + const castNumber = timesCast(skill); + if (castNumber <= 11) { + const cycle = Math.floor(castNumber / 3); + const position = castNumber % 3; + return [11, 23, 37][position] * 10 ** cycle; + } else if (castNumber === 12) { + return 420_000; + } else { + const cycle = Math.floor((castNumber - 13) / 3); + const position = (castNumber + 2) % 3; + return [11, 23, 37][position] * 10 ** (cycle + 5); + } +} -export const BCZCOSTS = new Map([ +const COSTS = new Map([ [$skill`BCZ: Blood Geyser`, $stat`SubMuscle`], [$skill`BCZ: Refracted Gaze`, $stat`SubMysticality`], [$skill`BCZ: Sweat Bullets`, $stat`SubMoxie`], @@ -517,6 +511,41 @@ export const BCZCOSTS = new Map([ [$skill`BCZ: Sweat Equity`, $stat`SubMoxie`], ]); +export const numericProperties = ["coinMasterIndex", "dailyDeedsVersion", "defaultDropdown1", "defaultDropdown2", "defaultDropdownSplit", "defaultLimit", "fixedThreadPoolSize", "itemManagerIndex", "lastBuffRequestType", "lastGlobalCounterDay", "lastImageCacheClear", "pingDefaultTestPings", "pingLoginCount", "pingLoginGoal", "pingLoginThreshold", "pingTestPings", "previousUpdateRevision", "relayDelayForSVN", "relaySkillButtonCount", "scriptButtonPosition", "statusDropdown", "svnThreadPoolSize", "toolbarPosition", "_beachTides", "_g9Effect", "8BitBonusTurns", "8BitScore", "addingScrolls", "affirmationCookiesEaten", "aminoAcidsUsed", "antagonisticSnowmanKitCost", "ascensionsToday", "asolDeferredPoints", "asolPointsPigSkinner", "asolPointsCheeseWizard", "asolPointsJazzAgent", "autoAbortThreshold", "autoAntidote", "autoBuyPriceLimit", "autopsyTweezersUsed", "autumnatonQuestTurn", "availableCandyCredits", "availableDimes", "availableFunPoints", "availableMrStore2002Credits", "availableQuarters", "availableSeptEmbers", "availableStoreCredits", "availableSwagger", "avantGuardPoints", "averageSwagger", "awolMedicine", "awolPointsBeanslinger", "awolPointsCowpuncher", "awolPointsSnakeoiler", "awolDeferredPointsBeanslinger", "awolDeferredPointsCowpuncher", "awolDeferredPointsSnakeoiler", "awolVenom", "bagOTricksCharges", "ballpitBonus", "bankedKarma", "bartenderTurnsUsed", "basementMallPrices", "basementSafetyMargin", "batmanFundsAvailable", "batmanBonusInitialFunds", "batmanTimeLeft", "bearSwagger", "beeCounter", "beGregariousCharges", "beGregariousFightsLeft", "birdformCold", "birdformHot", "birdformRoc", "birdformSleaze", "birdformSpooky", "birdformStench", "blackBartsBootyCost", "blackPuddingsDefeated", "blackForestProgress", "blankOutUsed", "bloodweiserDrunk", "bodyguardCharge", "bondPoints", "bondVillainsDefeated", "boneAbacusVictories", "bookOfFactsGummi", "bookOfFactsPinata", "bookOfIronyCost", "booPeakProgress", "borisPoints", "breakableHandling", "breakableHandling1964", "breakableHandling9691", "breakableHandling9692", "breakableHandling9699", "breathitinCharges", "brodenBacteria", "brodenSprinkles", "buffBotMessageDisposal", "buffBotPhilanthropyType", "buffJimmyIngredients", "burnoutsDefeated", "burrowgrubSummonsRemaining", "bwApronMealsEaten", "camelSpit", "camerasUsed", "campAwayDecoration", "candyWitchTurnsUsed", "candyWitchCandyTotal", "carboLoading", "catBurglarBankHeists", "cellarLayout", "charitableDonations", "chasmBridgeProgress", "chefTurnsUsed", "chessboardsCleared", "chibiAlignment", "chibiBirthday", "chibiFitness", "chibiIntelligence", "chibiLastVisit", "chibiSocialization", "chilledToTheBone", "cinchoSaltAndLime", "cinderellaMinutesToMidnight", "cinderellaScore", "cocktailSummons", "commerceGhostCombats", "cookbookbatIngredientsCharge", "controlPanelOmega", "cornucopiasOpened", "cosmicBowlingBallReturnCombats", "cozyCounter6332", "cozyCounter6333", "cozyCounter6334", "craftingClay", "craftingLeather", "craftingPlansCharges", "craftingStraw", "crimbo16BeardChakraCleanliness", "crimbo16BootsChakraCleanliness", "crimbo16BungChakraCleanliness", "crimbo16CrimboHatChakraCleanliness", "crimbo16GutsChakraCleanliness", "crimbo16HatChakraCleanliness", "crimbo16JellyChakraCleanliness", "crimbo16LiverChakraCleanliness", "crimbo16NippleChakraCleanliness", "crimbo16NoseChakraCleanliness", "crimbo16ReindeerChakraCleanliness", "crimbo16SackChakraCleanliness", "crimboTrainingSkill", "crimboTreeDays", "cubelingProgress", "cupidBowFights", "currentExtremity", "currentHedgeMazeRoom", "currentMojoFilters", "currentNunneryMeat", "currentPortalEnergy", "currentReplicaStoreYear", "cursedMagnifyingGlassCount", "cyrptAlcoveEvilness", "cyrptCrannyEvilness", "cyrptNicheEvilness", "cyrptNookEvilness", "cyrptTotalEvilness", "darkGyfftePoints", "dartsThrown", "daycareEquipment", "daycareInstructorItemQuantity", "daycareInstructors", "daycareLastScavenge", "daycareToddlers", "dbNemesisSkill1", "dbNemesisSkill2", "dbNemesisSkill3", "desertExploration", "desktopHeight", "desktopWidth", "dinseyFilthLevel", "dinseyFunProgress", "dinseyNastyBearsDefeated", "dinseySocialJusticeIProgress", "dinseySocialJusticeIIProgress", "dinseyTouristsFed", "dinseyToxicMultiplier", "doctorBagQuestLights", "doctorBagUpgrades", "dreadScroll1", "dreadScroll2", "dreadScroll3", "dreadScroll4", "dreadScroll5", "dreadScroll6", "dreadScroll7", "dreadScroll8", "dripAdventuresSinceAscension", "drippingHallAdventuresSinceAscension", "drippingTreesAdventuresSinceAscension", "drippyBatsUnlocked", "drippyJuice", "drippyOrbsClaimed", "droneSelfDestructChipsUsed", "drunkenSwagger", "edDefeatAbort", "edPoints", "eldritchTentaclesFought", "electricKoolAidEaten", "elfGratitude", "encountersUntilDMTChoice", "encountersUntilYachtzeeChoice", "encountersUntilNEPChoice", "encountersUntilSRChoice", "ensorceleeLevel", "entauntaunedColdRes", "essenceOfAnnoyanceCost", "essenceOfBearCost", "extraRolloverAdventures", "falloutShelterLevel", "familiarSweat", "fingernailsClipped", "fistSkillsKnown", "flyeredML", "fossilB", "fossilD", "fossilN", "fossilP", "fossilS", "fossilW", "fratboysDefeated", "frenchGuardTurtlesFreed", "funGuyMansionKills", "garbageChampagneCharge", "garbageFireProgress", "garbageShirtCharge", "garbageTreeCharge", "garlandUpgrades", "getsYouDrunkTurnsLeft", "ghostPepperTurnsLeft", "gingerDigCount", "gingerLawChoice", "gingerMuscleChoice", "gingerTrainScheduleStudies", "gladiatorBallMovesKnown", "gladiatorBladeMovesKnown", "gladiatorNetMovesKnown", "glitchItemCost", "glitchItemImplementationCount", "glitchItemImplementationLevel", "glitchSwagger", "gloverPoints", "gnasirProgress", "goldenMrAccessories", "gongPath", "gooseDronesRemaining", "goreCollected", "gourdItemCount", "greyYouPoints", "grimoire1Summons", "grimoire2Summons", "grimoire3Summons", "grimstoneCharge", "guardTurtlesFreed", "guideToSafariCost", "guyMadeOfBeesCount", "guzzlrBronzeDeliveries", "guzzlrDeliveryProgress", "guzzlrGoldDeliveries", "guzzlrPlatinumDeliveries", "haciendaLayout", "hallowiener8BitRealm", "hallowienerCoinspiracy", "hareMillisecondsSaved", "hareTurnsUsed", "heavyRainsStartingThunder", "heavyRainsStartingRain", "heavyRainsStartingLightning", "heroDonationBoris", "heroDonationJarlsberg", "heroDonationSneakyPete", "hiddenApartmentProgress", "hiddenBowlingAlleyProgress", "hiddenHospitalProgress", "hiddenOfficeProgress", "hiddenTavernUnlock", "highTopPumped", "hippiesDefeated", "holidayHalsBookCost", "holidaySwagger", "homemadeRobotUpgrades", "homebodylCharges", "hpAutoRecovery", "hpAutoRecoveryTarget", "iceSwagger", "ironicSwagger", "jarlsbergPoints", "juicyGarbageUsed", "jungCharge", "junglePuns", "knownAscensions", "kolhsTotalSchoolSpirited", "lassoTrainingCount", "lastAnticheeseDay", "lastArcadeAscension", "lastBadMoonReset", "lastBangPotionReset", "lastBattlefieldReset", "lastBeardBuff", "lastBreakfast", "lastCartographyBooPeak", "lastCartographyCastleTop", "lastCartographyDarkNeck", "lastCartographyDefiledNook", "lastCartographyFratHouse", "lastCartographyFratHouseVerge", "lastCartographyGuanoJunction", "lastCartographyHauntedBilliards", "lastCartographyHippyCampVerge", "lastCartographyZeppelinProtesters", "lastCastleGroundUnlock", "lastCastleTopUnlock", "lastCellarReset", "lastChanceThreshold", "lastChasmReset", "lastColosseumRoundWon", "lastCouncilVisit", "lastCounterDay", "lastDesertUnlock", "lastDispensaryOpen", "lastDMTDuplication", "lastDwarfFactoryReset", "lastEVHelmetValue", "lastEVHelmetReset", "lastEmptiedStorage", "lastFilthClearance", "lastGoofballBuy", "lastGuildStoreOpen", "lastGuyMadeOfBeesReset", "lastFratboyCall", "lastFriarCeremonyAscension", "lastFriarsElbowNC", "lastFriarsHeartNC", "lastFriarsNeckNC", "lastHippyCall", "lastIslandUnlock", "lastKeyotronUse", "lastKingLiberation", "lastLightsOutTurn", "lastMushroomPlot", "lastMiningReset", "lastNemesisReset", "lastPaperStripReset", "lastPirateEphemeraReset", "lastPirateInsultReset", "lastPlusSignUnlock", "lastQuartetAscension", "lastQuartetRequest", "lastSecondFloorUnlock", "lastShadowForgeUnlockAdventure", "lastSkateParkReset", "lastStillBeatingSpleen", "lastTavernAscension", "lastTavernSquare", "lastTelescopeReset", "lastTempleAdventures", "lastTempleButtonsUnlock", "lastTempleUnlock", "lastThingWithNoNameDefeated", "lastTowelAscension", "lastTr4pz0rQuest", "lastTrainsetConfiguration", "lastVioletFogMap", "lastVoteMonsterTurn", "lastWartDinseyDefeated", "lastWuTangDefeated", "lastYearbookCameraAscension", "lastZapperWand", "lastZapperWandExplosionDay", "lawOfAveragesCost", "legacyPoints", "leprecondoLastNeedChange", "libramSummons", "lightsOutAutomation", "louvreDesiredGoal", "louvreGoal", "lovebugsAridDesert", "lovebugsBeachBuck", "lovebugsBooze", "lovebugsChroner", "lovebugsCoinspiracy", "lovebugsCyrpt", "lovebugsFreddy", "lovebugsFunFunds", "lovebugsHoboNickel", "lovebugsItemDrop", "lovebugsMeat", "lovebugsMeatDrop", "lovebugsMoxie", "lovebugsMuscle", "lovebugsMysticality", "lovebugsOilPeak", "lovebugsOrcChasm", "lovebugsPowder", "lovebugsWalmart", "lttQuestDifficulty", "lttQuestStageCount", "manaBurnSummonThreshold", "manaBurningThreshold", "manaBurningTrigger", "manorDrawerCount", "manualOfNumberologyCost", "mapToKokomoCost", "markYourTerritoryCharges", "masksUnlocked", "maximizerMRUSize", "maximizerCombinationLimit", "maximizerEquipmentLevel", "maximizerEquipmentScope", "maximizerMaxPrice", "maximizerPriceLevel", "maxManaBurn", "mayflyExperience", "mayoLevel", "meansuckerPrice", "merkinVocabularyMastery", "miniAdvClass", "miniKiwiAiolisUsed", "miniMartinisDrunk", "mixedBerryJellyUses", "moleTunnelLevel", "momSeaMonkeeProgress", "mothershipProgress", "mpAutoRecovery", "mpAutoRecoveryTarget", "munchiesPillsUsed", "mushroomGardenCropLevel", "nanopolymerSpiderWebsUsed", "nextAprilBandTurn", "nextParanormalActivity", "nextQuantumFamiliarOwnerId", "nextQuantumFamiliarTurn", "noobPoints", "noobDeferredPoints", "noodleSummons", "nsContestants1", "nsContestants2", "nsContestants3", "nuclearAutumnPoints", "numericSwagger", "nunsVisits", "oilPeakProgress", "optimalSwagger", "optimisticCandleProgress", "palindomeDudesDefeated", "parasolUsed", "peaceTurkeyIndex", "pendingMapReflections", "phosphorTracesUses", "pingpongSkill", "pirateRealmPlasticPiratesDefeated", "pirateRealmShipsDestroyed", "pirateRealmStormsEscaped", "pirateSwagger", "plantingDay", "plumberBadgeCost", "plumberCostumeCost", "plumberPoints", "pokefamPoints", "poolSharkCount", "poolSkill", "powerPillProgress", "preworkoutPowderUses", "primaryLabGooIntensity", "prismaticSummons", "procrastinatorLanguageFluency", "promptAboutCrafting", "puzzleChampBonus", "pyramidPosition", "quantumPoints", "reagentSummons", "reanimatorArms", "reanimatorLegs", "reanimatorSkulls", "reanimatorWeirdParts", "reanimatorWings", "recentLocations", "redSnapperProgress", "relayPort", "relocatePygmyJanitor", "relocatePygmyLawyer", "rockinRobinProgress", "romanCandelabraRedCasts", "romanCandelabraBlueCasts", "romanCandelabraYellowCasts", "romanCandelabraGreenCasts", "romanCandelabraPurpleCasts", "ROMOfOptimalityCost", "rumpelstiltskinKidsRescued", "rumpelstiltskinTurnsUsed", "rwbMonsterCount", "safariSwagger", "sausageGrinderUnits", "schoolOfHardKnocksDiplomaCost", "schoolSwagger", "scrapbookCharges", "screechCombats", "scriptMRULength", "seadentConstructKills", "seadentLevel", "seaodesFound", "seaPoints", "SeasoningSwagger", "sexChanges", "shenInitiationDay", "shockingLickCharges", "singleFamiliarRun", "skillBurn3", "skillBurn90", "skillBurn153", "skillBurn154", "skillBurn155", "skillBurn236", "skillBurn237", "skillBurn1019", "skillBurn5017", "skillBurn6014", "skillBurn6015", "skillBurn6016", "skillBurn6020", "skillBurn6021", "skillBurn6022", "skillBurn6023", "skillBurn6024", "skillBurn6026", "skillBurn6028", "skillBurn7323", "skillBurn14008", "skillBurn14028", "skillBurn14038", "skillBurn15011", "skillBurn15028", "skillBurn17005", "skillBurn22034", "skillBurn22035", "skillBurn23301", "skillBurn23302", "skillBurn23303", "skillBurn23304", "skillBurn23305", "skillBurn23306", "skillLevel46", "skillLevel47", "skillLevel48", "skillLevel117", "skillLevel118", "skillLevel121", "skillLevel128", "skillLevel134", "skillLevel135", "skillLevel144", "skillLevel180", "skillLevel188", "skillLevel227", "skillLevel245", "skillLevel7254", "slimelingFullness", "slimelingStacksDropped", "slimelingStacksDue", "smoresEaten", "smutOrcNoncombatProgress", "sneakyPetePoints", "snojoMoxieWins", "snojoMuscleWins", "snojoMysticalityWins", "sourceAgentsDefeated", "sourceEnlightenment", "sourceInterval", "sourcePoints", "sourceTerminalGram", "sourceTerminalPram", "sourceTerminalSpam", "spaceBabyLanguageFluency", "spacePirateLanguageFluency", "spelunkyNextNoncombat", "spelunkySacrifices", "spelunkyWinCount", "spookyPuttyCopiesMade", "spookyVHSTapeMonsterTurn", "statbotUses", "stockCertificateTurn", "sugarCounter4178", "sugarCounter4179", "sugarCounter4180", "sugarCounter4181", "sugarCounter4182", "sugarCounter4183", "sugarCounter4191", "summonAnnoyanceCost", "sweat", "tacoDanCocktailSauce", "tacoDanFishMeat", "takerSpaceAnchor", "takerSpaceGold", "takerSpaceMast", "takerSpaceRum", "takerSpaceSilk", "takerSpaceSpice", "tavernLayout", "telescopeUpgrades", "tempuraSummons", "timeposedTopHats", "timeSpinnerMedals", "timesRested", "tomeSummons", "totalCharitableDonations", "trainsetPosition", "turtleBlessingTurns", "twinPeakProgress", "twoCRSPoints", "unicornHornInflation", "universalSeasoningCost", "usable1HWeapons", "usable1xAccs", "usable2HWeapons", "usable3HWeapons", "usableAccessories", "usableHats", "usableOffhands", "usableOther", "usablePants", "usableShirts", "valueOfAdventure", "valueOfInventory", "valueOfStill", "valueOfTome", "vintnerCharge", "vintnerWineLevel", "violetFogGoal", "walfordBucketProgress", "warehouseProgress", "welcomeBackAdv", "wereProfessorBite", "wereProfessorKick", "wereProfessorLiver", "wereProfessorPoints", "wereProfessorRend", "wereProfessorResearchPoints", "wereProfessorStomach", "wereProfessorTransformTurns", "whetstonesUsed", "wolfPigsEvicted", "wolfTurnsUsed", "writingDesksDefeated", "xoSkeleltonXProgress", "xoSkeleltonOProgress", "yearbookCameraAscensions", "yearbookCameraUpgrades", "youRobotBody", "youRobotBottom", "youRobotLeft", "youRobotPoints", "youRobotRight", "youRobotTop", "zeppelinProgress", "zeppelinProtestors", "zigguratLianas", "zombiePoints", "zootSpecimensPrepared", "zootomistPoints", "_absintheDrops", "_abstractionDropsCrown", "_aguaDrops", "_xenomorphCharge", "_alliedRadioDropsUsed", "_ancestralRecallCasts", "_antihangoverBonus", "_aprilShowerDiscoNap", "_aprilBandInstruments", "_aprilBandSaxophoneUses", "_aprilBandTomUses", "_aprilBandTubaUses", "_aprilBandStaffUses", "_aprilBandPiccoloUses", "_astralDrops", "_augSkillsCast", "_assertYourAuthorityCast", "_automatedFutureManufactures", "_autumnatonQuests", "_backUpUses", "_badlyRomanticArrows", "_badgerCharge", "_balefulHowlUses", "_banderRunaways", "_bastilleCheese", "_bastilleGames", "_bastilleGameTurn", "_bastilleLastCheese", "_batWingsCauldronUsed", "_batWingsFreeFights", "_batWingsRestUsed", "_batWingsSwoopUsed", "_bczBloodGeyserCasts", "_bczRefractedGazeCasts", "_bczSweatBulletsCasts", "_bczBloodBathCasts", "_bczDialitupCasts", "_bczSweatEquityCasts", "_bczBloodThinnerCasts", "_bczSpinalTapasCasts", "_bczPheromoneCocktailCasts", "_beanCannonUses", "_bearHugs", "_beerLensDrops", "_bellydancerPickpockets", "_benettonsCasts", "_beretBlastUses", "_beretBoastUses", "_beretBuskingUses", "_birdsSoughtToday", "_bookOfFactsWishes", "_bookOfFactsTatters", "_boomBoxFights", "_boomBoxSongsLeft", "_bootStomps", "_boxingGloveArrows", "_brickoEyeSummons", "_brickoFights", "_campAwayCloudBuffs", "_campAwaySmileBuffs", "_candyEggsDeviled", "_candySummons", "_captainHagnkUsed", "_carnieCandyDrops", "_carnivorousPottedPlantWins", "_carrotNoseDrops", "_catBurglarCharge", "_catBurglarHeistsComplete", "_cheerleaderSteam", "_chestXRayUsed", "_chibiAdventures", "_chipBags", "_chocolateCigarsUsed", "_chocolateCoveredPingPongBallsUsed", "_chocolateSculpturesUsed", "_chocolatesUsed", "_chronolithActivations", "_chronolithNextCost", "_cinchUsed", "_cinchoRests", "_circadianRhythmsAdventures", "_clanFortuneConsultUses", "_clipartSummons", "_clocksUsed", "_cloversPurchased", "_coldMedicineConsults", "_coldMedicineEquipmentTaken", "_companionshipCasts", "_concoctionDatabaseRefreshes", "_cookbookbatCrafting", "_cookbookbatCombatsUntilNewQuest", "_cosmicBowlingSkillsUsed", "_crimbo21ColdResistance", "_cyberFreeFights", "_cyberZone1Turns", "_cyberZone2Turns", "_cyberZone3Turns", "_dailySpecialPrice", "_dartsLeft", "_daycareGymScavenges", "_daycareRecruits", "_deckCardsDrawn", "_deluxeKlawSummons", "_demandSandwich", "_detectiveCasesCompleted", "_disavowed", "_dnaPotionsMade", "_donhosCasts", "_douseFoeUses", "_dreamJarDrops", "_drunkPygmyBanishes", "_durableDolphinWhistleUsed", "_edDefeats", "_edLashCount", "_eldritchTentaclesFoughtToday", "_elfGuardCookingUsed", "_elronsCasts", "_enamorangs", "_energyCollected", "_expertCornerCutterUsed", "_extraTimeUsed", "_favorRareSummons", "_feastUsed", "_feelinTheRhythm", "_feelPrideUsed", "_feelExcitementUsed", "_feelHatredUsed", "_feelLonelyUsed", "_feelNervousUsed", "_feelEnvyUsed", "_feelDisappointedUsed", "_feelSuperiorUsed", "_feelLostUsed", "_feelNostalgicUsed", "_feelPeacefulUsed", "_fingertrapArrows", "_fireExtinguisherCharge", "_fragrantHerbsUsed", "_freeBeachWalksUsed", "_frButtonsPressed", "_fudgeWaspFights", "_gapBuffs", "_garbageFireDrops", "_garbageFireDropsCrown", "_generateIronyUsed", "_genieFightsUsed", "_genieWishesUsed", "_gibbererAdv", "_gibbererCharge", "_gingerbreadCityTurns", "_glarkCableUses", "_glitchMonsterFights", "_gnomeAdv", "_godLobsterFights", "_goldenMoneyCharge", "_gongDrops", "_gothKidCharge", "_gothKidFights", "_greyYouAdventures", "_grimBrotherCharge", "_grimFairyTaleDrops", "_grimFairyTaleDropsCrown", "_grimoireConfiscatorSummons", "_grimoireGeekySummons", "_grimstoneMaskDrops", "_grimstoneMaskDropsCrown", "_grooseCharge", "_grooseDrops", "_grubbyWoolDrops", "_guzzlrDeliveries", "_guzzlrGoldDeliveries", "_guzzlrPlatinumDeliveries", "_hareAdv", "_hareCharge", "_highTopPumps", "_hipsterAdv", "_hoardedCandyDropsCrown", "_hoboUnderlingSummons", "_holidayMultitaskingUsed", "_holoWristDrops", "_holoWristProgress", "_hotAshesDrops", "_hotJellyUses", "_hotTubSoaks", "_humanMuskUses", "_iceballUses", "_inigosCasts", "_ironTricornHeadbuttUsed", "_jerksHealthMagazinesUsed", "_jiggleCheese", "_jiggleCream", "_jiggleLife", "_jiggleSteak", "_jitbCharge", "_juneCleaverAdvs", "_juneCleaverFightsLeft", "_juneCleaverEncounters", "_juneCleaverStench", "_juneCleaverSpooky", "_juneCleaverSleaze", "_juneCleaverHot", "_juneCleaverCold", "_juneCleaverSkips", "_jungDrops", "_kgbClicksUsed", "_kgbDispenserUses", "_kgbTranquilizerDartUses", "_klawSummons", "_kloopCharge", "_kloopDrops", "_kolhsAdventures", "_kolhsSavedByTheBell", "_lastDailyDungeonRoom", "_lastFitzsimmonsHatch", "_lastMobiusStripTurn", "_lastSausageMonsterTurn", "_lastZomboEye", "_latteRefillsUsed", "_lawOfAveragesUsed", "_leafblowerML", "_leafLassosCrafted", "_leafMonstersFought", "_leavesBurned", "_legionJackhammerCrafting", "_leprecondoRearrangements", "_leprecondoFurniture", "_llamaCharge", "_longConUsed", "_lovebugsBeachBuck", "_lovebugsChroner", "_lovebugsCoinspiracy", "_lovebugsFreddy", "_lovebugsFunFunds", "_lovebugsHoboNickel", "_lovebugsWalmart", "_loveChocolatesUsed", "_lynyrdSnareUses", "_machineTunnelsAdv", "_macrometeoriteUses", "_mafiaThumbRingAdvs", "_mapToACandyRichBlockDrops", "_mayamRests", "_mayflowerDrops", "_mayflySummons", "_mcHugeLargeAvalancheUses", "_mcHugeLargeSkiPlowUses", "_mcHugeLargeSlashUses", "_mediumSiphons", "_meteoriteAdesUsed", "_meteorShowerUses", "_micrometeoriteUses", "_mildEvilPerpetrated", "_mimicEggsDonated", "_mimicEggsObtained", "_miniKiwiDrops", "_miniMartiniDrops", "_mobiusStripEncounters", "_monkeyPawWishesUsed", "_monsterHabitatsFightsLeft", "_monsterHabitatsRecalled", "_monstersMapped", "_mushroomGardenFights", "_nanorhinoCharge", "_navelRunaways", "_neverendingPartyFreeTurns", "_newYouQuestSharpensDone", "_newYouQuestSharpensToDo", "_nextColdMedicineConsult", "_nextQuantumAlignment", "_nightmareFuelCharges", "_noobSkillCount", "_nuclearStockpileUsed", "_oilExtracted", "_oldSchoolCocktailCraftingUsed", "_olfactionsUsed", "_optimisticCandleDropsCrown", "_oreDropsCrown", "_otoscopeUsed", "_oysterEggsFound", "_pantsgivingBanish", "_pantsgivingCount", "_pantsgivingCrumbs", "_pantsgivingFullness", "_pasteDrops", "_perilsForeseen", "_peteJukeboxFixed", "_peteJumpedShark", "_petePeeledOut", "_photoBoothEffects", "_photoBoothEquipment", "_pieDrops", "_piePartsCount", "_pirateRealmGold", "_pirateRealmGlue", "_pirateRealmGrog", "_pirateRealmGrub", "_pirateRealmGuns", "_pirateRealmIslandMonstersDefeated", "_pirateRealmSailingTurns", "_pirateRealmShipSpeed", "_pixieCharge", "_pocketProfessorLectures", "_poisonArrows", "_pokeGrowFertilizerDrops", "_poolGames", "_powderedGoldDrops", "_powderedMadnessUses", "_powerfulGloveBatteryPowerUsed", "_powerPillDrops", "_powerPillUses", "_precisionCasts", "_questPartyFairItemsOpened", "_radlibSummons", "_raindohCopiesMade", "_rapidPrototypingUsed", "_raveStealCount", "_reflexHammerUsed", "_resolutionAdv", "_resolutionRareSummons", "_riftletAdv", "_robinEggDrops", "_roboDrops", "_rogueProgramCharge", "_romanticFightsLeft", "_saberForceMonsterCount", "_saberForceUses", "_saberMod", "_saltGrainsConsumed", "_sandwormCharge", "_saplingsPlanted", "_sausageFights", "_sausagesEaten", "_sausagesMade", "_seadentLightningUsed", "_sealFigurineUses", "_sealScreeches", "_sealsSummoned", "_shadowBricksUsed", "_shadowRiftCombats", "_shatteringPunchUsed", "_shortOrderCookCharge", "_shrubCharge", "_slimeVialsHarvested", "_sloppyDinerBeachBucks", "_smilesOfMrA", "_smithsnessSummons", "_smoochArmyHQCombats", "_snojoFreeFights", "_snojoParts", "_snokebombUsed", "_snowconeSummons", "_snowglobeDrops", "_snowmanHatPlaceUsed", "_snowSuitCount", "_sourceTerminalDigitizeMonsterCount", "_sourceTerminalDigitizeUses", "_sourceTerminalDuplicateUses", "_sourceTerminalEnhanceUses", "_sourceTerminalExtrudes", "_sourceTerminalPortscanUses", "_spaceFurDropsCrown", "_spacegatePlanetIndex", "_spacegateTurnsLeft", "_spaceJellyfishDrops", "_speakeasyDrinksDrunk", "_speakeasyFreeFights", "_spelunkerCharges", "_spelunkingTalesDrops", "_spikolodonSpikeUses", "_spiritOfTheMountainsAdvs", "_spookyJellyUses", "_stackLumpsUses", "_steamCardDrops", "_stickerSummons", "_stinkyCheeseCount", "_stressBallSqueezes", "_sugarSummons", "_summonResortPassesUsed", "_surprisinglySweetSlashUsed", "_surprisinglySweetStabUsed", "_sweatOutSomeBoozeUsed", "_taffyRareSummons", "_taffyYellowSummons", "_tearawayPantsAdvs", "_thanksgettingFoodsEaten", "_thingfinderCasts", "_thinknerdPackageDrops", "_thorsPliersCrafting", "_timeHelmetAdv", "_timeCopsFoughtToday", "_timeSpinnerMinutesUsed", "_tokenDrops", "_transponderDrops", "_turkeyBlastersUsed", "_turkeyBooze", "_turkeyMuscle", "_turkeyMyst", "_turkeyMoxie", "_unaccompaniedMinerUsed", "_unblemishedPearlAnemoneMineProgress", "_unblemishedPearlDiveBarProgress", "_unblemishedPearlMadnessReefProgress", "_unblemishedPearlMarinaraTrenchProgress", "_unblemishedPearlTheBriniestDeepestsProgress", "_unconsciousCollectiveCharge", "_universalSeasoningsUsed", "_universeCalculated", "_universeImploded", "_usedReplicaBatoomerang", "_vampyreCloakeFormUses", "_villainLairProgress", "_vitachocCapsulesUsed", "_vmaskAdv", "_voidFreeFights", "_volcanoItem1", "_volcanoItem2", "_volcanoItem3", "_volcanoItemCount1", "_volcanoItemCount2", "_volcanoItemCount3", "_voteFreeFights", "_VYKEACompanionLevel", "_warbearAutoAnvilCrafting", "_waxGlobDrops", "_whiteRiceDrops", "_witchessFights", "_xoHugsUsed", "_yellowPixelDropsCrown", "_zapCount", "_zombieSmashPocketsUsed", "lastNoncombat15", "lastNoncombat257", "lastNoncombat270", "lastNoncombat273", "lastNoncombat280", "lastNoncombat283", "lastNoncombat297", "lastNoncombat322", "lastNoncombat323", "lastNoncombat324", "lastNoncombat341", "lastNoncombat343", "lastNoncombat384", "lastNoncombat386", "lastNoncombat391", "lastNoncombat392", "lastNoncombat394", "lastNoncombat405", "lastNoncombat406", "lastNoncombat408", "lastNoncombat439", "lastNoncombat440", "lastNoncombat441", "lastNoncombat450", "lastNoncombat528", "lastNoncombat533", "lastNoncombat539", "lastNoncombat540", "lastNoncombat541", "lastNoncombat588", "lastNoncombat589", "lastNoncombat590", "lastNoncombat591", "lastNoncombat592"] as const; +export type NumericProperty = typeof numericProperties[number]; + +const PREFS = new Map([ + [$skill`BCZ: Blood Geyser`, "_bczBloodGeyserCasts"], + [$skill`BCZ: Refracted Gaze`, "_bczRefractedGazeCasts"], + [$skill`BCZ: Sweat Bullets`, "_bczSweatBulletsCasts"], + [$skill`BCZ: Blood Bath`, "_bczBloodBathCasts"], + [$skill`BCZ: Dial it up to 11`, "_bczDialitupCasts"], + [$skill`BCZ: Sweat Equity`, "_bczSweatEquityCasts"], + [$skill`BCZ: Create Blood Thinner`, "_bczBloodThinnerCasts"], + [$skill`BCZ: Prepare Spinal Tapas`, "_bczSpinalTapasCasts"], + [$skill`BCZ: Craft a Pheromone Cocktail`, "_bczPheromoneCocktailCasts"], +]); + +/** + * @param skill The BCZ skill to check. + * @returns The number of casts of the skill already used, parsing the pref. + */ +export function timesCast(skill: Skill): number { + const pref = PREFS.get(skill); + if (!pref) return 0; + return get(pref, 0); +} + +/** + * @param skill The BCZ skill to check. + * @returns The substat used to cast the skill. + */ +export function substatUsed(skill: Skill): Stat | null { + const cost = COSTS.get(skill); + if (!cost) return null; + return cost; +} + /** * @param skill The BCZ skill to check. * @param statFloor Minimum base stat you want to keep. @@ -525,20 +554,19 @@ export const BCZCOSTS = new Map([ export function availableCasts(skill: Skill, statFloor: number): number { if (!have_()) return 0; - const stat = BCZCOSTS.get(skill); + const stat = substatUsed(skill); if (!stat) return 0; // const currentStat = myBasestat(stat); const currentStat = myBasestat(stat); - const timesCast = skill.timescast ?? 0; const subStatFloor = statFloor ** 2; let casts = 0; let remainingStat = currentStat; - for (let i = timesCast; i < BCZSKILLCOST.size; i++) { - const nextCost = BCZSKILLCOST.get(i); - if (nextCost === undefined) break; + for (let i = timesCast(skill); i < 25; i++) { + // 25 is abritrary + const nextCost = skillCost(skill); if (remainingStat - nextCost < subStatFloor) break; remainingStat -= nextCost; casts++; @@ -553,9 +581,13 @@ export function availableCasts(skill: Skill, statFloor: number): number { * @returns Whether you successfully cast the spell. */ export function castDownTo(skill: Skill, statFloor: number): boolean { - if (!have_() || !BCZCOSTS.get(skill)) return false; - const available = availableCasts(skill, statFloor); - if (available === 0) return false; + if (!have_() || !COSTS.get(skill)) return false; + let casts = availableCasts(skill, statFloor); + while (casts) { + if (!useSkill(skill, casts)) return false; + casts = availableCasts(skill, statFloor); + } - return useSkill(skill, available); + return true; } + diff --git a/yarn.lock b/yarn.lock index 94578f3..ce631fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3162,10 +3162,10 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -libram@^0.10.9: - version "0.10.9" - resolved "https://registry.yarnpkg.com/libram/-/libram-0.10.9.tgz#5d0b54aaea51edf3cb7f9f47b1c006ed48d5287d" - integrity sha512-suSh6NSW2J4lf8CbL3GnU+39+i0LdLMOuEopdeT7MQ0kXW30j4kWlxr+NWky72HewNQZHmdyihXoYabsVfrbpQ== +libram@^0.11.9: + version "0.11.9" + resolved "https://registry.yarnpkg.com/libram/-/libram-0.11.9.tgz#b8aba12db14e4fec27122ac8c94d1422e93cdd53" + integrity sha512-fj4RIOc0/K/ytvp0F1bOrScstPg5Fba2bt/8eNrRbmHPRPUPILepsq+dJ9d98H3Ae/KtNupkPicNB584lg0h1w== dependencies: html-entities "^2.5.2" From 96374662c73b5612a72d0848fd9e27cb08348a12 Mon Sep 17 00:00:00 2001 From: Ignose Date: Fri, 19 Dec 2025 12:32:57 -0500 Subject: [PATCH 24/24] It ain't split-casual, but it's improvements! --- package.json | 4 ++-- src/main.ts | 38 ++++++++++++++++++++++++++++++++---- src/tasks/1 aftercoreleg.ts | 14 +++++++++++-- src/tasks/4 postrunleg.ts | 13 +++++++++--- src/tasks/repeatableTasks.ts | 21 ++++++++------------ yarn.lock | 16 +++++++-------- 6 files changed, 74 insertions(+), 32 deletions(-) diff --git a/package.json b/package.json index cac61a0..95177f1 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "build": "^0.1.4", "grimoire-kolmafia": "^0.3.25", "kolmafia": "^5.28591.0", - "libram": "^0.11.9", + "libram": "^0.11.14", "mafia-shared-relay": "0.0.7", "webpack": "^5.89.0" }, @@ -37,7 +37,7 @@ "esbuild-plugin-babel": "^0.2.3", "eslint": "^8.45.0", "eslint-config-prettier": "^8.8.0", - "eslint-plugin-libram": "^0.4.24", + "eslint-plugin-libram": "^0.4.34", "eslint-plugin-prettier": "^5.0.0", "prettier": "^3.0.0", "typescript": "^5.1.6", diff --git a/src/main.ts b/src/main.ts index 8a067bc..99b448c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,5 @@ import { Args, getTasks } from "grimoire-kolmafia"; -import { gamedayToInt, print } from "kolmafia"; +import { gamedayToInt, getRelated, Item, mallPrice, print } from "kolmafia"; import { args } from "./args"; import { ProfitTrackingEngine } from "./engine/engine"; @@ -7,8 +7,8 @@ import { AftercoreQuest } from "./tasks/1 aftercoreleg"; import { AscendQuest } from "./tasks/2 ascend"; import { RunQuests } from "./tasks/3 runleg"; import { PostRunQuests } from "./tasks/4 postrunleg"; -import { castDownTo, deleteJunkKmails, halloween, notifyVoters, realDay, realMonth } from "./tasks/utils"; -import { $skill } from "libram"; +import { deleteJunkKmails, halloween, notifyVoters, realDay, realMonth } from "./tasks/utils"; +import { $item } from "libram"; const version = "0.0.3"; @@ -22,7 +22,37 @@ export function main(command?: string): void { } if (args.test) { - castDownTo($skill`BCZ: Sweat Equity`, 100); + const coldWadPrice = mallPrice($item`stench wad`); + const twinklyWadPrice = mallPrice($item`twinkly wad`); + + const results = Item.all() + .map((item) => { + const p = getRelated(item, "pulverize"); + + const value = + // cold + (p?.["stench wad"] ?? 0) * coldWadPrice / 1_000_000 + + (p?.["stench nuggets"] ?? 0) * coldWadPrice / 5 / 1_000_000 + + (p?.["stench powder"] ?? 0) * coldWadPrice / 25 / 1_000_000 + + + // twinkly + (p?.["twinkly wad"] ?? 0) * twinklyWadPrice / 1_000_000 + + (p?.["twinkly nuggets"] ?? 0) * twinklyWadPrice / 5 / 1_000_000 + + (p?.["twinkly powder"] ?? 0) * twinklyWadPrice / 25 / 1_000_000; + + const price = mallPrice(item) > 0 ? mallPrice(item) : Infinity; + const net = value - price; + const roi = net / price; + + return { item, value, price, net, roi }; + }) + .filter(o => o.net > 0) + .sort((a, b) => b.net - a.net); + + for (const { item, net, roi } of results) { + print(`${item}: net ${net.toFixed(0)} meat (ROI ${(roi * 100).toFixed(1)}%)`); + } + } if (dontCS && args.halloween && args.cs) { diff --git a/src/tasks/1 aftercoreleg.ts b/src/tasks/1 aftercoreleg.ts index cf62d44..b6ef990 100644 --- a/src/tasks/1 aftercoreleg.ts +++ b/src/tasks/1 aftercoreleg.ts @@ -2,6 +2,7 @@ import { CombatStrategy } from "grimoire-kolmafia"; import { buy, cliExecute, + daycount, getWorkshed, haveEquipped, hippyStoneBroken, @@ -19,6 +20,7 @@ import { restoreHp, restoreMp, retrieveItem, + runChoice, toBoolean, use, useFamiliar, @@ -216,7 +218,7 @@ export function AftercoreQuest(): Quest { name: "Barfing Drunk with Stooper", ready: () => stooperDrunk() && have($familiar`Stooper`) && !have($item`Drunkula's wineglass`), - completed: () => myAdventures() === 0 || totallyDrunk(), + completed: () => myAdventures() === 0 || totallyDrunk() || daycount() === 1, acquire: [{ item: $item`seal tooth` }], outfit: () => ({ familiar: $familiar`Stooper`, @@ -253,7 +255,15 @@ export function AftercoreQuest(): Quest { name: "Nightcap (Wine Glass)", ready: () => have($item`Drunkula's wineglass`), completed: () => totallyDrunk(), - do: () => cliExecute(`CONSUME NIGHTCAP VALUE ${get("valueOfAdventure") - 1000}`), + do: () => { + if($familiar`Cooler Yeti`.experience >= 400) { + useFamiliar($familiar`Cooler Yeti`); + visitUrl("main.php?talktoyeti=1", false); + runChoice(2); + useFamiliar($familiar`Stooper`); + } + cliExecute(`CONSUME NIGHTCAP VALUE ${get("valueOfAdventure") - 1000}`); + }, tracking: "Organs" }, { diff --git a/src/tasks/4 postrunleg.ts b/src/tasks/4 postrunleg.ts index bff041e..31355c0 100644 --- a/src/tasks/4 postrunleg.ts +++ b/src/tasks/4 postrunleg.ts @@ -23,6 +23,7 @@ import { restoreHp, restoreMp, retrieveItem, + runChoice, setProperty, toBoolean, use, @@ -52,10 +53,8 @@ import { args } from "../args"; import { chrono, crimbo, garboWeen, noBarf, postRunQuests } from "./repeatableTasks"; import { Quest } from "./structure"; import { - availableCasts, backstageItemsDone, bestFam, - castDownTo, doneAdventuring, haveAll, maxBase, @@ -428,7 +427,15 @@ export function PostRunQuests(): Quest { name: "Nightcap", ready: () => doneAdventuring(), completed: () => totallyDrunk(), - do: () => cliExecute("CONSUME NIGHTCAP"), + do: () => { + if($familiar`Cooler Yeti`.experience >= 400) { + useFamiliar($familiar`Cooler Yeti`); + visitUrl("main.php?talktoyeti=1", false); + runChoice(2); + useFamiliar($familiar`Stooper`); + } + cliExecute("CONSUME NIGHTCAP"); + }, tracking: "Rollover Prep" }, { diff --git a/src/tasks/repeatableTasks.ts b/src/tasks/repeatableTasks.ts index 130439e..19efc2b 100644 --- a/src/tasks/repeatableTasks.ts +++ b/src/tasks/repeatableTasks.ts @@ -413,9 +413,6 @@ export function preRunQuests(): Task[] { ]; } -let garboDone1 = false; -let garboDone2 = false; - export function noBarf(): Task[] { return [ { @@ -472,15 +469,9 @@ export function noBarf(): Task[] { { name: "Garbo Nobarf", ready: () => holiday().includes("Halloween") || args.crimbo || args.chrono, - completed: () => (myDaycount() > 1 && garboDone1) || - (myDaycount() === 1 && garboDone2), + completed: () => ((myInebriety() > inebrietyLimit()) || (get("_monsterHabitatsRecalled") >=3 && get("_macrometeoriteUses") >= 10)), do: (): void => { - cliExecute(`garbo nodiet nobarf target="witchess knight"`); - if(myDaycount() > 1) { - garboDone1 = true; - } else { - garboDone2 = true; - } + cliExecute(`garbo nodiet nobarf target="sausage goblin"`); }, tracking: "Garbo" }, @@ -571,7 +562,11 @@ export function crimbo(): Task[] { { name: "Crimbo Time", ready: () => args.crimbo, - completed: () => myAdventures() === 0 && myInebriety() >= inebrietyLimit(), + completed: () => { + if (myDaycount() === 1) + return myAdventures() === 0 || myInebriety() > inebrietyLimit() + else return myAdventures() === 0 + }, prepare: () => uneffect($effect`Beaten Up`), do: (): void => { cliExecute(`${args.crimboscript}`); @@ -591,7 +586,7 @@ export function crimbo(): Task[] { { name: "Crimbo Drunk", ready: () => args.crimbo && myDaycount() > 1, - completed: () => myAdventures() === 0, + completed: () => myAdventures() === 0 || myDaycount() === 1, prepare: () => uneffect($effect`Beaten Up`), do: (): void => { cliExecute(`${args.crimboscript}`); diff --git a/yarn.lock b/yarn.lock index ce631fb..3c86e25 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2357,10 +2357,10 @@ eslint-config-prettier@^8.8.0: resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz#bfda738d412adc917fd7b038857110efe98c9348" integrity sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA== -eslint-plugin-libram@^0.4.24: - version "0.4.33" - resolved "https://registry.yarnpkg.com/eslint-plugin-libram/-/eslint-plugin-libram-0.4.33.tgz#d76c0a16e5869e050e88f1c8a23f7081df49510c" - integrity sha512-QLajjkVuPNoiimfBH4iKHWXZCze8hnkUja+DHo/7ff0ddb1AQf6V2lk/nxXku/t+AtLVw2r0s1AliuZv4HV/8Q== +eslint-plugin-libram@^0.4.34: + version "0.4.34" + resolved "https://registry.yarnpkg.com/eslint-plugin-libram/-/eslint-plugin-libram-0.4.34.tgz#d5be47303b16825a86eb5ec457de1c62a83bdc1a" + integrity sha512-XgGsaZvsDAl0uYvxYGa/KYYUWQbnEZoxHNZQuwc6PFMH96AR8c64lWMJnHPTrsoVInGGS24cfdPbd9ZkDAZsHg== dependencies: html-entities "^2.5.2" requireindex "~1.2.0" @@ -3162,10 +3162,10 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -libram@^0.11.9: - version "0.11.9" - resolved "https://registry.yarnpkg.com/libram/-/libram-0.11.9.tgz#b8aba12db14e4fec27122ac8c94d1422e93cdd53" - integrity sha512-fj4RIOc0/K/ytvp0F1bOrScstPg5Fba2bt/8eNrRbmHPRPUPILepsq+dJ9d98H3Ae/KtNupkPicNB584lg0h1w== +libram@^0.11.14: + version "0.11.14" + resolved "https://registry.yarnpkg.com/libram/-/libram-0.11.14.tgz#011527aba45e3225809e9f0decaf9b83fcce8325" + integrity sha512-EzuyU8fru9rYWnQkaWGcknZJ6JgwLIH+GDI8NbCKgVsA7MuSKE6CU/Do/I18vm/dmgjqSbivzE8wcjKxUDNTAQ== dependencies: html-entities "^2.5.2"