-
Notifications
You must be signed in to change notification settings - Fork 57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cockroach copy target #2070
base: main
Are you sure you want to change the base?
Cockroach copy target #2070
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -86,6 +86,7 @@ import { | |
runGarboQuests, | ||
SetupTargetCopyQuest, | ||
} from "./tasks"; | ||
import { CockroachSetup } from "./tasks/cockroachPrep"; | ||
|
||
// Max price for tickets. You should rethink whether Barf is the best place if they're this expensive. | ||
const TICKET_MAX_PRICE = 500000; | ||
|
@@ -107,6 +108,15 @@ function ensureBarfAccess() { | |
} | ||
|
||
function defaultTarget() { | ||
// Can we account for re-entry if we only have certain amounts of copiers left in each of these? | ||
if ( | ||
have($skill`Just the Facts`) && | ||
have($skill`Meteor Lore`) && | ||
have($item`Powerful Glove`) && | ||
Comment on lines
+114
to
+115
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should have a function that essentially boils down to "how many monster replacers are available" |
||
(get("_prToday") || get("prAlways")) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use |
||
) { | ||
return $monster`cockroach`; | ||
} | ||
if ($skills`Curse of Weaksauce, Saucegeyser`.every((s) => have(s))) { | ||
return maxBy( | ||
$monsters.all().filter((m) => m.wishable && isFreeAndCopyable(m)), | ||
|
@@ -517,6 +527,11 @@ export function main(argString = ""): void { | |
// FIXME: Dynamically figure out pointer ring approach. | ||
withStash(stashItems, () => { | ||
withVIPClan(() => { | ||
// Prepare pirate realm if our copy target is cockroach | ||
// How do we handle if garbo was started without enough turns left without dieting to prep? | ||
if (globalOptions.target === $monster`cockroach`) { | ||
runGarboQuests([CockroachSetup]); | ||
} | ||
// 0. diet stuff. | ||
if ( | ||
globalOptions.nodiet || | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,283 @@ | ||
import { OutfitSpec, Quest } from "grimoire-kolmafia"; | ||
import { GarboTask } from "./engine"; | ||
import { | ||
$effect, | ||
$familiar, | ||
$item, | ||
$items, | ||
$location, | ||
$stat, | ||
get, | ||
have, | ||
maxBy, | ||
questStep, | ||
uneffect, | ||
} from "libram"; | ||
import { | ||
abort, | ||
adv1, | ||
mallPrice, | ||
myBuffedstat, | ||
runChoice, | ||
visitUrl, | ||
} from "kolmafia"; | ||
import { garboValue } from "../garboValue"; | ||
import { freeFightFamiliar } from "../familiar"; | ||
import { freeFightOutfit } from "../outfit"; | ||
import { GarboStrategy, Macro } from "../combat"; | ||
import { acquire } from "../acquire"; | ||
|
||
// Just checking for the gummi effects for now, maybe can check other stuff later? | ||
function checkAndFixOvercapStats(): void { | ||
if (myBuffedstat($stat`Muscle`) >= 100) { | ||
if (have($effect`Gummiheart`)) uneffect($effect`Gummiheart`); | ||
} | ||
if (myBuffedstat($stat`Mysticality`) >= 100) { | ||
if (have($effect`Gummibrain`)) uneffect($effect`Gummibrain`); | ||
} | ||
if (myBuffedstat($stat`Moxie`) >= 100) { | ||
if (have($effect`Gummiskin`)) uneffect($effect`Gummiskin`); | ||
} | ||
if ( | ||
myBuffedstat($stat`Moxie`) >= 100 || | ||
myBuffedstat($stat`Mysticality`) >= 100 || | ||
myBuffedstat($stat`Muscle`) >= 100 | ||
) { | ||
uneffect($effect`Having a Ball!`); | ||
} | ||
if ( | ||
myBuffedstat($stat`Moxie`) >= 100 || | ||
myBuffedstat($stat`Mysticality`) >= 100 || | ||
myBuffedstat($stat`Muscle`) >= 100 | ||
) { | ||
abort( | ||
"Buffed stats are too high for PirateRealm! Check for equipment or buffs that we can add to prevent in the script", | ||
); | ||
} | ||
} | ||
Comment on lines
+31
to
+57
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be streamlined through iteration |
||
|
||
function dessertIslandWorthIt(): boolean { | ||
// guesstimating value of giant crab at 3*VOA | ||
if (garboValue($item`cocoa of youth`) > 3 * get("valueOfAdventure")) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
function crewRoleValue(crewmate: string): number { | ||
// Cuisinier is highest value if cocoa of youth is more meat than expected from giant crab | ||
if (dessertIslandWorthIt() && crewmate.includes("Cuisinier")) { | ||
return 50; | ||
} | ||
// Coxswain helps save turns if we run from storms | ||
if (crewmate.includes("Coxswain")) return 40; | ||
// Harquebusier gives us extra fun from combats | ||
if (crewmate.includes("Harquebusier")) return 30; | ||
// Crypto, Cuisinier (if cocoa not worth it), and Mixologist have small bonuses we care about less | ||
return 0; | ||
} | ||
|
||
function crewAdjectiveValue(crewmate: string): number { | ||
// Wide-Eyed give us bonus fun when counting birds in smooth sailing, and we'll mostly be doing that rather than spending limited grub/grog | ||
if (crewmate.includes("Wide-Eyed")) return 5; | ||
// Gluttonous can help when running out of grub, even though we usually shouldn't? | ||
if (crewmate.includes("Gluttonous")) return 4; | ||
// Beligerent, Dipsomaniacal, and Pinch-Fisted don't make much difference | ||
return 0; | ||
} | ||
|
||
function chooseCrew(): void { | ||
const bestChoice = maxBy([1, 2, 3], (choiceOption) => { | ||
const crewmatePref = `_pirateRealmCrewmate${choiceOption}`; | ||
const crewmate = get(crewmatePref); | ||
const roleValue = crewRoleValue(crewmate); | ||
const adjectiveValue = crewAdjectiveValue(crewmate); | ||
return roleValue + adjectiveValue; | ||
}); | ||
runChoice(bestChoice); | ||
} | ||
|
||
export const CockroachSetup: Quest<GarboTask> = { | ||
name: "Setup Cockroach Target", | ||
completed: () => get("_lastPirateRealmIsland") === $location`Trash Island`, | ||
tasks: [ | ||
// Tasks to progress pirate realm up to selecting Trash Island go here | ||
// We'll have to be careful about things like max stats becoming too high (bofa is annoying for this!) | ||
// To be optimal we would progress up until we're about to fight the giant giant crab, and then after buffing and fighting it, we then select trash island. | ||
// We might need some restructuring to do this nicely? | ||
{ | ||
name: "Get PirateRealm Eyepatch", | ||
completed: () => have($item`PirateRealm eyepatch`), | ||
do: () => visitUrl("place.php?whichplace=realm_pirate&action=pr_port"), | ||
limit: { tries: 1 }, | ||
spendsTurn: false, | ||
}, | ||
{ | ||
name: "Start PirateRealm Journey", | ||
ready: () => have($item`PirateRealm eyepatch`), | ||
completed: () => questStep("_questPirateRealm") > 0, | ||
prepare: () => checkAndFixOvercapStats(), | ||
do: () => { | ||
visitUrl("place.php?whichplace=realm_pirate&action=pr_port"); | ||
runChoice(1); // Head to Groggy's | ||
chooseCrew(); // Choose our crew | ||
runChoice(4); // Choose anemometer for trash island | ||
runChoice(4); // Choose swift clipper, fastest ship | ||
runChoice(1); // Head for the sea | ||
}, | ||
outfit: { equip: $items`PirateRealm eyepatch` }, | ||
limit: { tries: 1 }, | ||
spendsTurn: false, | ||
}, | ||
{ | ||
name: "Choose First Island", | ||
ready: () => questStep("_questPirateRealm") === 1, | ||
completed: () => questStep("_questPirateRealm") > 1, | ||
prepare: () => checkAndFixOvercapStats(), | ||
do: () => adv1($location`Sailing the PirateRealm Seas`), | ||
outfit: { equip: $items`PirateRealm eyepatch` }, | ||
choices: () => ({ | ||
1352: | ||
dessertIslandWorthIt() && | ||
get("_pirateRealmCrewmate").includes("Cuisinier") | ||
? 6 | ||
: 1, | ||
}), | ||
limit: { tries: 1 }, | ||
spendsTurn: false, | ||
}, | ||
{ | ||
name: "Sail to first Island", | ||
ready: () => questStep("_questPirateRealm") === 2, | ||
completed: () => questStep("_questPirateRealm") > 2, | ||
prepare: () => checkAndFixOvercapStats(), | ||
do: () => adv1($location`Sailing the PirateRealm Seas`), | ||
outfit: { | ||
equip: $items`PirateRealm eyepatch, PirateRealm party hat, Red Roger's red right foot`, | ||
}, | ||
choices: () => ({ | ||
1365: 1, | ||
1364: 2, | ||
1361: 1, | ||
1357: get("_pirateRealmGold") >= 50 ? 3 : 4, | ||
1360: 6, // Will need to add shop handling, perhaps to choice adventure script | ||
1356: 3, | ||
1362: | ||
get("_pirateRealmShipSpeed") - get("_pirateRealmSailingTurns") >= 2 | ||
? 2 | ||
: 1, | ||
1363: 2, | ||
1359: 1, // Emergency grog adventure, choice one seems more consistent? | ||
1358: 1, // Emergency grub adventure, choice one seems more consistent? | ||
1367: 1, // Wrecked ship, this uses glue, need a pref for glue to make this not break if we don't have glue | ||
}), | ||
limit: { tries: 8 }, | ||
spendsTurn: true, | ||
}, | ||
{ | ||
name: "Land Ho (First Island)", | ||
ready: () => questStep("_questPirateRealm") === 3, | ||
completed: () => questStep("_questPirateRealm") > 3, | ||
prepare: () => checkAndFixOvercapStats(), | ||
do: () => { | ||
// Should give us the Land-Ho adventure | ||
if (visitUrl("adventure.php?snarfblat=530").includes("Land Ho!")) { | ||
runChoice(1); | ||
} else { | ||
abort("Expected Land Ho! but didn't get it!"); | ||
} | ||
}, | ||
outfit: { | ||
equip: $items`PirateRealm eyepatch`, | ||
}, | ||
limit: { tries: 1 }, | ||
spendsTurn: false, | ||
}, | ||
{ | ||
name: "Standard Island Combats (Island 1)", | ||
ready: () => questStep("_questPirateRealm") === 4, | ||
completed: () => questStep("_questPirateRealm") > 4, | ||
prepare: () => { | ||
checkAndFixOvercapStats(); | ||
if ( | ||
mallPrice($item`windicle`) < 3 * get("valueOfAdventure") && | ||
!get("_pirateRealmWindicleUsed") | ||
) { | ||
acquire(1, $item`windicle`, 3 * get("valueOfAdventure"), true); | ||
} | ||
}, | ||
do: () => adv1(get("_lastPirateRealmIsland", $location`none`)), | ||
outfit: () => | ||
freeFightOutfit({ | ||
equip: $items`PirateRealm eyepatch, PirateRealm party hat, carnivorous potted plant, Red Roger's red left foot, Space Trip safety headphones`, | ||
familiar: freeFightFamiliar({ | ||
canChooseMacro: false, | ||
location: get("_lastPirateRealmIsland", $location`none`), | ||
allowAttackFamiliars: true, | ||
mode: "free", | ||
}), | ||
}), | ||
combat: new GarboStrategy(() => | ||
Macro.externalIf( | ||
mallPrice($item`windicle`) < 3 * get("valueOfAdventure") && | ||
!get("_pirateRealmWindicleUsed") && | ||
get("_pirateRealmIslandMonstersDefeated") <= 1, | ||
Macro.item($item`windicle`), | ||
).basicCombat(), | ||
), | ||
limit: { tries: 8 }, | ||
spendsTurn: true, | ||
}, | ||
{ | ||
name: "Final Island Encounter (Island 1)", // Ideally we delay this to do it before our copy target fights for meat but here for now | ||
ready: () => questStep("_questPirateRealm") === 5, | ||
completed: () => questStep("_questPirateRealm") > 5, | ||
prepare: () => { | ||
checkAndFixOvercapStats(); | ||
}, | ||
do: () => { | ||
if (get("_lastPirateRealmIsland") === $location`Dessert Island`) { | ||
// Should give us cocoa of youth | ||
if ( | ||
visitUrl("adventure.php?snarfblat=531").includes( | ||
"Chocolate Fountain of Youth", | ||
) | ||
) { | ||
runChoice(1); | ||
} else { | ||
abort("Expected cocoa of youth but got something else!"); | ||
} | ||
} else { | ||
adv1($location`Crab Island`); | ||
} | ||
}, | ||
outfit: () => { | ||
if (get("_lastPirateRealmIsland") === $location`Crab Island`) { | ||
const spec: OutfitSpec = { | ||
modifier: ["meat"], | ||
equip: $items`PirateRealm eyepatch`, | ||
avoid: $items`cursed pirate cutlass`, // Gives +25 muscle which often overcaps | ||
familiar: $familiar`Hobo Monkey`, // We haven't done familiar prep yet so robort isn't fed yet, something to fix later | ||
}; | ||
return spec; | ||
} | ||
return { equip: $items`PirateRealm eyepatch` }; | ||
}, | ||
choices: { 1385: 1, 1368: 1 }, // Take cocoa of youth, fight crab | ||
combat: new GarboStrategy(() => Macro.delevel().meatKill()), | ||
limit: { tries: 1 }, | ||
spendsTurn: true, | ||
}, | ||
{ | ||
name: "Choose Trash Island", | ||
ready: () => questStep("_questPirateRealm") === 6, | ||
completed: () => questStep("_questPirateRealm") > 6, | ||
prepare: () => checkAndFixOvercapStats(), | ||
do: () => adv1($location`Sailing the PirateRealm Seas`), | ||
outfit: { equip: $items`PirateRealm eyepatch` }, | ||
choices: { 1353: 5 }, // Trash Island | ||
limit: { tries: 1 }, | ||
spendsTurn: false, | ||
}, | ||
], | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should have a function that boils down to "do we expect to fight gregs today"