PokeBot/ai/strategies.lua

3031 lines
67 KiB
Lua

local strategies = {}
local combat = require "ai.combat"
local control = require "ai.control"
local battle = require "action.battle"
local shop = require "action.shop"
local textbox = require "action.textbox"
local walk = require "action.walk"
local bridge = require "util.bridge"
local input = require "util.input"
local memory = require "util.memory"
local menu = require "util.menu"
local player = require "util.player"
local utils = require "util.utils"
local inventory = require "storage.inventory"
local pokemon = require "storage.pokemon"
local areaName = "Unknown"
local splitNumber, splitTime = 0, 0
local tries = 0
local tempDir, canProgress, initialized
local level4Nidoran = true -- 57 vs 96 (d39)
local nidoAttack, nidoSpeed, nidoSpecial = 0, 0, 0
local squirtleAtt, squirtleDef, squirtleSpd, squirtleScl
local yolo, deepRun, resetting, riskGiovanni, maxEtherSkip
-- TIME CONSTRAINTS
local timeRequirements = {
bulbasaur = function()
return 2.25
end,
nidoran = function()
local timeLimit = 6.25
if pokemon.inParty("spearow") then
timeLimit = timeLimit + 0.67
end
return timeLimit
end,
mt_moon = function()
local timeLimit = 27
if nidoAttack > 15 and nidoSpeed > 14 then
timeLimit = timeLimit + 0.25
end
if pokemon.inParty("paras") then
timeLimit = timeLimit + 0.75
end
return timeLimit
end,
mankey = function()
local timeLimit = 33
if pokemon.inParty("paras") then
timeLimit = timeLimit + 0.75
end
return timeLimit
end,
goldeen = function()
local timeLimit = 38
if pokemon.inParty("paras") then
timeLimit = timeLimit + 0.75
end
return timeLimit
end,
misty = function()
local timeLimit = 40
if pokemon.inParty("paras") then
timeLimit = timeLimit + 0.75
end
return timeLimit
end,
vermilion = function()
return 44.25
end,
trash = function()
local timeLimit = 47
if nidoSpecial > 44 then
timeLimit = timeLimit + 0.25
end
if nidoAttack > 53 then
timeLimit = timeLimit + 0.25
end
if nidoAttack >= 54 and nidoSpecial >= 45 then
timeLimit = timeLimit + 0.25
end
return timeLimit
end,
safari_carbos = function()
return 70.5
end,
e4center = function()
return 102
end,
blue = function()
return 108.2
end,
}
-- RISK/RESET
local function hardReset(message, extra)
resetting = true
if strategies.seed then
if extra then
extra = extra.." | "..strategies.seed
else
extra = strategies.seed
end
end
bridge.chat(message, extra)
client.reboot_core()
return true
end
local function reset(reason, extra)
local time = utils.elapsedTime()
local resetString = "Reset"
if time then
resetString = resetString.." after "..time
end
if areaName then
resetString = " "..resetString.." at "..areaName
end
local separator
if deepRun and not yolo then
separator = " BibleThump"
else
separator = ":"
end
resetString = resetString..separator.." "..reason
return hardReset(resetString, extra)
end
strategies.reset = reset
local function resetDeath(extra)
local reason
if strategies.criticaled then
reason = "Critical'd"
elseif yolo then
reason = "Yolo strats"
else
reason = "Died"
end
return reset(reason, extra)
end
strategies.death = resetDeath
local function overMinute(min)
return utils.igt() > min * 60
end
local function resetTime(timeLimit, reason, once)
if overMinute(timeLimit) then
reason = "Took too long to "..reason
if RESET_FOR_TIME then
return reset(reason)
end
if once then
print(reason.." "..utils.elapsedTime())
end
end
end
local function getTimeRequirement(name)
return timeRequirements[name]()
end
local function setYolo(name)
if not RESET_FOR_TIME then
return false
end
local minimumTime = getTimeRequirement(name)
local shouldYolo = overMinute(minimumTime)
if yolo ~= shouldYolo then
yolo = shouldYolo
control.setYolo(shouldYolo)
local prefix
if yolo then
prefix = "en"
else
prefix = "dis"
end
if areaName then
print("YOLO "..prefix.."abled at "..areaName)
else
print("YOLO "..prefix.."abled")
end
end
return yolo
end
-- PRIVATE
local function initialize()
if not initialized then
initialized = true
return true
end
end
local function canHealFor(damage)
local curr_hp = pokemon.index(0, "hp")
local max_hp = pokemon.index(0, "max_hp")
if max_hp - curr_hp < 5 then
return nil
end
local healChecks = {
{"full_restore", 9001},
{"super_potion", 50},
{"potion", 20},
}
for idx,potion in ipairs(healChecks) do
local name = potion[1]
local result_hp = math.min(curr_hp + potion[2], max_hp)
if result_hp >= damage and inventory.contains(name) then
return name
end
end
end
local function hasHealthFor(opponent, extra)
if not extra then
extra = 0
end
return pokemon.index(0, "hp") + extra > combat.healthFor(opponent)
end
local function damaged(factor)
if not factor then
factor = 1
end
return pokemon.index(0, "hp") * factor < pokemon.index(0, "max_hp")
end
local function opponentDamaged(factor)
if not factor then
factor = 1
end
return memory.double("battle", "opponent_hp") * factor < memory.double("battle", "opponent_max_hp")
end
local function redHP()
return math.ceil(pokemon.index(0, "max_hp") * 0.2)
end
local function buffTo(buff, defLevel)
if battle.isActive() then
canProgress = true
local forced
if defLevel and memory.double("battle", "opponent_defense") > defLevel then
forced = buff
end
battle.automate(forced, true)
elseif canProgress then
return true
else
battle.automate()
end
end
local function dodgeUp(npc, sx, sy, dodge, offset)
if not battle.handleWild() then
return false
end
local px, py = player.position()
if py < sy - 1 then
return true
end
local wx, wy = px, py
if py < sy then
wy = py - 1
elseif px == sx or px == dodge then
if px - memory.raw(npc) == offset then
if px == sx then
wx = dodge
else
wx = sx
end
else
wy = py - 1
end
end
walk.step(wx, wy)
end
local function dodgeH(options)
local left = 1
if options.left then
left = -1
end
local px, py = player.position()
if px * left > options.sx * left + (options.dist or 1) * left then
return true
end
local wx, wy = px, py
if px * left > options.sx * left then
wx = px + 1 * left
elseif py == options.sy or py == options.dodge then
if py - memory.raw(options.npc) == options.offset then
if py == options.sy then
wy = options.dodge
else
wy = options.sy
end
else
wx = px + 1 * left
end
end
walk.step(wx, wy)
end
local function completedMenuFor(data)
local count = inventory.count(data.item)
if count == 0 or count + (data.amount or 1) <= tries then
return true
end
return false
end
local function closeMenuFor(data)
if (not tempDir and not data.close) or data.chain or menu.close() then
return true
end
end
local function useItem(data)
local main = memory.value("menu", "main")
if tries == 0 then
tries = inventory.count(data.item)
if tries == 0 then
if closeMenuFor(data) then
return true
end
return false
end
end
if completedMenuFor(data) then
if closeMenuFor(data) then
return true
end
else
if inventory.use(data.item, data.poke) then
tempDir = true
else
menu.pause()
end
end
end
local function completedSkillFor(data)
if data.map then
if data.map ~= memory.value("game", "map") then
return true
end
elseif data.x or data.y then
local px, py = player.position()
if data.x == px or data.y == py then
return true
end
elseif data.done then
if memory.raw(data.done) > (data.val or 0) then
return true
end
elseif tries > 0 and not menu.isOpen() then
return true
end
return false
end
local function isPrepared(...)
if tries == 0 then
tries = {}
end
for i,name in ipairs(arg) do
local currentCount = inventory.count(name)
if currentCount > 0 then
local previousCount = tries[name]
if previousCount == nil or currentCount == previousCount then
return false
end
end
end
return true
end
local function prepare(...)
if tries == 0 then
tries = {}
end
local item
for idx,name in ipairs(arg) do
local currentCount = inventory.count(name)
local needsItem = currentCount > 0
local previousCount = tries[name]
if previousCount == nil then
tries[name] = currentCount
elseif needsItem then
needsItem = currentCount == previousCount
end
if needsItem then
item = name
break
end
end
if not item then
return true
end
if battle.isActive() then
inventory.use(item, nil, true)
else
input.cancel()
end
end
-- DSum
local function nidoranDSum(disabled)
local sx, sy = player.position()
if not disabled and tries == nil then
local opponentName = battle.opponent()
local opLevel = memory.value("battle", "opponent_level")
if opponentName == "rattata" then
if opLevel == 2 then
tries = {0, 4, 12}
elseif opLevel == 3 then
tries = {0, 14, 11}
else
-- tries = {0, 0, 10} -- TODO can't escape
end
elseif opponentName == "spearow" then
if opLevel == 5 then
-- can't escape
end
elseif opponentName == "nidoran" then
tries = {0, 6, 12}
elseif opponentName == "nidoranf" then
if opLevel == 3 then
tries = {4, 6, 12}
else
tries = {5, 6, 12}
end
end
if tries then
tries.idx = 1
tries.x, tries.y = sx, sy
else
tries = 0
end
end
if not disabled and tries ~= 0 then
if tries[tries.idx] == 0 then
tries.idx = tries.idx + 1
if tries.idx > 3 then
tries = 0
end
return nidoranDSum()
end
if tries.x ~= sx or tries.y ~= sy then
tries[tries.idx] = tries[tries.idx] - 1
tries.x, tries.y = sx, sy
end
if tries.idx == 2 then
sy = 11
else
sy = 12
end
else
sy = 11
end
if sx == 33 then
sx = 32
else
sx = 33
end
walk.step(sx, sy)
end
-- Strategies
local strategyFunctions
strategyFunctions = {
a = function(data)
areaName = data.a
return true
end,
startFrames = function()
strategies.frames = 0
return true
end,
reportFrames = function()
print("FR "..strategies.frames)
local repels = memory.value("player", "repel")
if repels > 0 then
print("S "..repels)
end
strategies.frames = nil
return true
end,
tweetMisty = function()
local elt = utils.elapsedTime()
if setYolo("misty") then
print("Misty: "..elt)
else
local pbn = ""
local pbTime = getTimeRequirement("misty") - 1.75
if not overMinute(pbTime) then
pbn = " (PB pace)"
end
bridge.tweet("Got a run going, just beat Misty "..elt.." in"..pbn.." http://www.twitch.tv/thepokebot")
end
return true
end,
tweetVictoryRoad = function()
local elt = utils.elapsedTime()
local pbn = ""
if not overMinute(98) then -- TODO verify
pbn = " (PB pace)"
end
local elt = utils.elapsedTime()
bridge.tweet("Entering Victory Road at "..elt..pbn.." on our way to the Elite Four http://www.twitch.tv/thepokebot")
return true
end,
split = function(data)
bridge.split(data and data.finished)
if not INTERNAL then
splitNumber = splitNumber + 1
local timeDiff
splitTime, timeDiff = utils.timeSince(splitTime)
if timeDiff then
print(splitNumber..". "..areaName..": "..utils.elapsedTime().." ("..timeDiff..")")
end
end
return true
end,
wait = function()
print("Please save state")
input.press("Start", 9001)
end,
emuSpeed = function(data)
-- client.speedmode = data.percent
return true
end,
-- Global
interact = function(data)
if battle.handleWild() then
if battle.isActive() then
return true
end
if textbox.isActive() then
if tries > 0 then
return true
end
tries = tries - 1
input.cancel()
elseif player.interact(data.dir) then
tries = tries + 1
end
end
end,
confirm = function(data)
if battle.handleWild() then
if textbox.isActive() then
tries = tries + 1
input.cancel(data.type or "A")
else
if tries > 0 then
return true
end
player.interact(data.dir)
end
end
end,
item = function(data)
if battle.handleWild() then
if data.full and not inventory.isFull() then
if closeMenuFor(data) then
return true
end
return false
end
return useItem(data)
end
end,
potion = function(data)
local curr_hp = pokemon.index(0, "hp")
if curr_hp == 0 then
return false
end
local toHP
if yolo and data.yolo ~= nil then
toHP = data.yolo
else
toHP = data.hp
end
if type(toHP) == "string" then
toHP = combat.healthFor(toHP)
end
local toHeal = toHP - curr_hp
if toHeal > 0 then
local toPotion
if data.forced then
toPotion = inventory.contains(data.forced)
else
local p_first, p_second, p_third
if toHeal > 50 then
if data.full then
p_first = "full_restore"
else
p_first = "super_potion"
end
p_second, p_third = "super_potion", "potion"
else
if toHeal > 20 then
p_first, p_second = "super_potion", "potion"
else
p_first, p_second = "potion", "super_potion"
end
if data.full then
p_third = "full_restore"
end
end
toPotion = inventory.contains(p_first, p_second, p_third)
end
if toPotion then
if menu.pause() then
inventory.use(toPotion)
tempDir = true
end
return false
end
end
if closeMenuFor(data) then
return true
end
end,
teach = function(data)
if data.full and not inventory.isFull() then
return true
end
local itemName
if data.item then
itemName = data.item
else
itemName = data.move
end
if pokemon.hasMove(data.move) then
local main = memory.value("menu", "main")
if main == 128 then
if data.chain then
return true
end
elseif main < 3 then
return true
end
input.press("B")
else
local replacement
if data.replace then
replacement = pokemon.moveIndex(data.replace, data.poke) - 1
else
replacement = 0
end
if inventory.teach(itemName, data.poke, replacement, data.alt) then
tempDir = true
else
menu.pause()
end
end
end,
skill = function(data)
if completedSkillFor(data) then
if not textbox.isActive() then
return true
end
input.press("B")
elseif not data.dir or player.face(data.dir) then
if pokemon.use(data.move) then
tries = tries + 1
else
menu.pause()
end
end
end,
fly = function(data)
if memory.value("game", "map") == data.map then
return true
end
local cities = {
pallet = {62, "Up"},
viridian = {63, "Up"},
lavender = {66, "Down"},
celadon = {68, "Down"},
fuchsia = {69, "Down"},
cinnabar = {70, "Down"},
}
local main = memory.value("menu", "main")
if main == 228 then
local currentFly = memory.raw(0x1FEF)
local destination = cities[data.dest]
local press
if destination[1] - currentFly == 0 then
press = "A"
else
press = destination[2]
end
input.press(press)
elseif not pokemon.use("fly") then
menu.pause()
end
end,
bicycle = function()
if memory.raw(0x1700) == 1 then
if textbox.handle() then
return true
end
else
return useItem({item="bicycle"})
end
end,
fightXAccuracy = function()
return prepare("x_accuracy")
end,
waitToTalk = function()
if battle.isActive() then
canProgress = false
battle.automate()
elseif textbox.isActive() then
canProgress = true
input.cancel()
elseif canProgress then
return true
end
end,
waitToPause = function()
local main = memory.value("menu", "main")
if main == 128 then
if canProgress then
return true
end
elseif battle.isActive() then
canProgress = false
battle.automate()
elseif main == 123 then
canProgress = true
input.press("B")
elseif textbox.handle() then
input.press("Start", 2)
end
end,
waitToFight = function(data)
if battle.isActive() then
canProgress = true
battle.automate()
elseif canProgress then
return true
elseif textbox.handle() then
if data.dir then
player.interact(data.dir)
else
input.cancel()
end
end
end,
allowDeath = function(data)
control.canDie(data.on)
return true
end,
-- Route
squirtleIChooseYou = function()
if pokemon.inParty("squirtle") then
bridge.caught("squirtle")
return true
end
if player.face("Up") then
textbox.name("A")
end
end,
fightBulbasaur = function()
if tries < 9000 and pokemon.index(0, "level") == 6 then
if tries > 200 then
squirtleAtt = pokemon.index(0, "attack")
squirtleDef = pokemon.index(0, "defense")
squirtleSpd = pokemon.index(0, "speed")
squirtleScl = pokemon.index(0, "special")
if squirtleAtt < 11 and squirtleScl < 12 then
return reset("Bad Squirtle - "..squirtleAtt.." attack, "..squirtleScl.." special")
end
tries = 9001
else
tries = tries + 1
end
end
if battle.isActive() and memory.double("battle", "opponent_hp") > 0 and resetTime(getTimeRequirement("bulbasaur"), "kill Bulbasaur") then
return true
end
return buffTo("tail_whip", 6)
end,
dodgePalletBoy = function()
return dodgeUp(0x0223, 14, 14, 15, 7)
end,
shopViridianPokeballs = function()
return shop.transaction{
buy = {{name="pokeball", index=0, amount=8}}
}
end,
catchNidoran = function()
if not control.canCatch() then
return true
end
local pokeballs = inventory.count("pokeball")
local caught = memory.value("player", "party_size") - 1
if pokeballs < 5 - caught * 2 then
return reset("Ran too low on PokeBalls", pokeballs)
end
if battle.isActive() then
local isNidoran = pokemon.isOpponent("nidoran")
if isNidoran and memory.value("battle", "opponent_level") > 2 then
if initialize() then
bridge.pollForName()
end
end
tries = nil
if memory.value("menu", "text_input") == 240 then
textbox.name()
elseif memory.value("battle", "menu") == 95 then
if isNidoran then
input.press("A")
else
input.cancel()
end
elseif not control.shouldCatch() then
if control.shouldFight() then
battle.fight()
else
battle.run()
end
end
else
local noDSum
pokemon.updateParty()
local hasNidoran = pokemon.inParty("nidoran")
if hasNidoran then
if not tempDir then
bridge.caught("nidoran")
tempDir = true
end
if pokemon.getExp() > 205 then
level4Nidoran = pokemon.info("nidoran", "level") == 4
return true
end
noDSum = true
end
local timeLimit = getTimeRequirement("nidoran")
local resetMessage
if hasNidoran then
resetMessage = "get an experience kill before Brock"
else
resetMessage = "find a Nidoran"
end
if resetTime(timeLimit, resetMessage) then
return true
end
if not noDSum and overMinute(timeLimit - 0.25) then
noDSum = true
end
nidoranDSum(noDSum)
end
end,
-- 1: NIDORAN
dodgeViridianOldMan = function()
return dodgeUp(0x0273, 18, 6, 17, 9)
end,
grabTreePotion = function()
if initialize() then
if pokemon.info("squirtle", "hp") > 15 or pokemon.info("spearow", "level") == 3 then
return true
end
end
if inventory.contains("potion") then
return true
end
local px, py = player.position()
if px > 15 then
walk.step(15, 4)
else
player.interact("Left")
end
end,
grabAntidote = function()
local px, py = player.position()
if py < 11 then
return true
end
if pokemon.info("spearow", "level") == 3 then
if px < 26 then
px = 26
else
py = 10
end
elseif inventory.contains("antidote") then
py = 10
else
player.interact("Up")
end
walk.step(px, py)
end,
grabForestPotion = function()
if battle.handleWild() then
if inventory.contains("potion") then
local healthNeeded = (pokemon.info("spearow", "level") == 3) and 8 or 15
if pokemon.info("squirtle", "hp") <= healthNeeded then
if menu.pause() then
inventory.use("potion", "squirtle")
end
else
return true
end
elseif menu.close() then
player.interact("Up")
end
end
end,
fightWeedle = function()
if battle.isTrainer() then
canProgress = true
local squirtleOut = pokemon.isDeployed("squirtle")
if squirtleOut and memory.value("battle", "our_status") > 0 and not inventory.contains("antidote") then
return reset("Poisoned, but we skipped the antidote")
end
local sidx = pokemon.indexOf("spearow")
if sidx ~= -1 and pokemon.index(sidx, "level") > 3 then
sidx = -1
end
if sidx == -1 then
return buffTo("tail_whip", 5)
end
if pokemon.index(sidx, "hp") < 1 then
local battleMenu = memory.value("battle", "menu")
if utils.onPokemonSelect(battleMenu) then
menu.select(pokemon.indexOf("squirtle"), true)
elseif battleMenu == 95 then
input.press("A")
elseif squirtleOut then
battle.automate()
else
input.cancel()
end
elseif squirtleOut then
battle.swap("spearow")
else
local peck = combat.bestMove()
local forced
if peck and peck.damage and peck.damage + 1 >= memory.double("battle", "opponent_hp") then
forced = "growl"
end
battle.fight(forced)
end
elseif canProgress then
return true
end
end,
equipForBrock = function(data)
if initialize() then
if pokemon.info("squirtle", "level") < 8 then
return reset("Not level 8 before Brock", pokemon.getExp())
end
if data.anti then
local poisoned = pokemon.info("squirtle", "status") > 0
if not poisoned then
return true
end
if not inventory.contains("antidote") then
return reset("Poisoned, but we skipped the antidote")
end
local curr_hp = pokemon.info("squirtle", "hp")
if inventory.contains("potion") and curr_hp > 8 and curr_hp < 18 then
return true
end
end
end
local main = memory.value("menu", "main")
local nidoranIndex = pokemon.indexOf("nidoran")
if nidoranIndex == 0 then
if menu.close() then
return true
end
elseif menu.pause() then
local column = menu.getCol()
if pokemon.info("squirtle", "status") > 0 then
inventory.use("antidote", "squirtle")
elseif inventory.contains("potion") and pokemon.info("squirtle", "hp") < 15 then
inventory.use("potion", "squirtle")
else
if main == 128 then
if column == 11 then
menu.select(1, true)
elseif column == 12 then
menu.select(1, true)
else
input.press("B")
end
elseif main == 103 then
if memory.value("menu", "selection_mode") == 1 then
menu.select(nidoranIndex, true)
else
menu.select(0, true)
end
else
input.press("B")
end
end
end
end,
fightBrock = function()
local squirtleHP = pokemon.info("squirtle", "hp")
if squirtleHP == 0 then
return resetDeath()
end
if battle.isActive() then
if tries < 1 then
tries = 1
end
local bubble, turnsToKill, turnsToDie = combat.bestMove()
if not pokemon.isDeployed("squirtle") then
battle.swap("squirtle")
elseif turnsToDie and turnsToDie < 2 and inventory.contains("potion") then
inventory.use("potion", "squirtle", true)
else
local battleMenu = memory.value("battle", "menu")
local bideTurns = memory.value("battle", "opponent_bide")
if battleMenu == 95 and menu.getCol() == 1 then
input.press("A")
elseif bideTurns > 0 then
local onixHP = memory.double("battle", "opponent_hp")
if not canProgress then
canProgress = onixHP
tempDir = bideTurns
end
if turnsToKill then
local forced
if turnsToDie < 2 or turnsToKill < 2 or tempDir - bideTurns > 1 then
-- elseif turnsToKill < 3 and tempDir == bideTurns then
elseif onixHP == canProgress then
forced = "tail_whip"
end
battle.fight(forced)
else
input.cancel()
end
elseif utils.onPokemonSelect(battleMenu) then
menu.select(pokemon.indexOf("nidoran"), true)
else
canProgress = false
battle.fight()
end
if tries < 9000 then
local nidx = pokemon.indexOf("nidoran")
if pokemon.index(nidx, "level") == 8 then
local att = pokemon.index(nidx, "attack")
local def = pokemon.index(nidx, "defense")
local spd = pokemon.index(nidx, "speed")
local scl = pokemon.index(nidx, "special")
bridge.stats(att.." "..def.." "..spd.." "..scl)
nidoAttack = att
nidoSpeed = spd
nidoSpecial = scl
if tries > 300 then
local statDiff = (16 - att) + (15 - spd) + (13 - scl)
if not level4Nidoran then
statDiff = statDiff + 1
end
local resets = att < 15 or spd < 14 or scl < 12 or (att == 15 and spd == 14)
local nStatus = "Att: "..att..", Def: "..def..", Speed: "..spd..", Special: "..scl
if resets then
return reset("Bad Nidoran - "..nStatus)
end
tries = 9001
if def < 12 then
statDiff = statDiff + 1
end
local superlative
local exclaim = "!"
if statDiff == 0 then
if def == 14 then
superlative = " god"
exclaim = "! Kreygasm"
else
superlative = " perfect"
end
elseif att == 16 and spd == 15 then
if statDiff == 1 then
superlative = " great"
elseif statDiff == 2 then
superlative = " good"
else
superlative = " okay"
end
elseif statDiff == 1 then
superlative = " good"
elseif statDiff <= 3 then
superlative = "n okay"
exclaim = "."
else
superlative = " min stat"
exclaim = "."
end
nStatus = "Beat Brock with a"..superlative.." Nidoran"..exclaim.." "..nStatus..", caught at level "..(level4Nidoran and "4" or "3").."."
bridge.chat(nStatus)
else
tries = tries + 1
end
end
end
end
elseif tries > 0 then
return true
elseif textbox.handle() then
player.interact("Up")
end
end,
-- 2: BROCK
shopPewterMart = function()
return shop.transaction{
buy = {{name="potion", index=1, amount=9}}
}
end,
battleModeSet = function()
if memory.value("setting", "battle_style") == 10 then
if menu.close() then
return true
end
elseif menu.pause() then
local main = memory.value("menu", "main")
if main == 128 then
if menu.getCol() ~= 11 then
input.press("B")
else
menu.select(5, true)
end
elseif main == 228 then
menu.setOption("battle_style", 8, 10)
else
input.press("B")
end
end
end,
leer = function(data)
local bm = combat.bestMove()
if not bm or bm.minTurns < 3 then
if battle.isActive() then
canProgress = true
elseif canProgress then
return true
end
battle.automate()
return false
end
local opp = battle.opponent()
local defLimit = 9001
for i,poke in ipairs(data) do
if opp == poke[1] then
local minimumAttack = poke[3]
if not minimumAttack or nidoAttack > minimumAttack then
defLimit = poke[2]
end
break
end
end
return buffTo("leer", defLimit)
end,
bugCatcher = function()
if battle.isActive() then
canProgress = true
local isWeedle = pokemon.isOpponent("weedle")
if isWeedle and not tempDir then
tempDir = true
end
secondCaterpie = tempDir
if not isWeedle and secondCaterpie then
if level4Nidoran and nidoSpeed >= 14 and pokemon.index(0, "attack") >= 19 then
-- print("IA "..pokemon.index(0, "attack"))
battle.automate()
return
end
end
strategyFunctions.leer({{"caterpie",8}, {"weedle",7}})
elseif canProgress then
return true
else
battle.automate()
end
end,
shortsKid = function()
local fightingEkans = pokemon.isOpponent("ekans")
if fightingEkans then
local wrapping = memory.value("battle", "turns") > 0
if wrapping then
local curr_hp = memory.double("battle", "our_hp")
if not tempDir then
tempDir = curr_hp
end
local wrapDamage = tempDir - curr_hp
if wrapDamage > 0 and wrapDamage < 7 and curr_hp < 14 and not opponentDamaged() then
inventory.use("potion", nil, true)
return false
end
elseif tempDir then
tempDir = nil
end
end
control.battlePotion(fightingEkans or damaged(2))
return strategyFunctions.leer({{"rattata",9}, {"ekans",10}})
end,
potionBeforeCocoons = function()
if nidoSpeed >= 15 then
return true
end
return strategyFunctions.potion({hp=6, yolo=3})
end,
swapHornAttack = function()
if pokemon.battleMove("horn_attack") == 1 then
return true
end
battle.swapMove(1, 3)
end,
fightMetapod = function()
if battle.isActive() then
canProgress = true
if memory.double("battle", "opponent_hp") > 0 and pokemon.isOpponent("metapod") then
return true
end
battle.automate()
elseif canProgress then
return true
else
battle.automate()
end
end,
catchFlierBackup = function()
if initialize() then
control.canDie(true)
end
if not control.canCatch() then
return true
end
local caught = pokemon.inParty("pidgey", "spearow")
if battle.isActive() then
if memory.double("battle", "our_hp") == 0 then
if pokemon.info("squirtle", "hp") == 0 then
control.canDie(false)
elseif utils.onPokemonSelect(memory.value("battle", "menu")) then
menu.select(pokemon.indexOf("squirtle"), true)
else
input.press("A")
end
elseif not control.shouldCatch() then
battle.run()
end
else
local birdPath
local px, py = player.position()
if caught then
if px > 33 then
return true
end
local startY = 9
if px > 28 then
startY = py
end
birdPath = {{32,startY}, {32,11}, {34,11}}
elseif px == 37 then
if py == 10 then
py = 11
else
py = 10
end
walk.step(px, py)
else
birdPath = {{32,10}, {32,11}, {34,11}, {34,10}, {37,10}}
end
if birdPath then
walk.custom(birdPath)
end
end
end,
-- 3: ROUTE 3
startMtMoon = function()
strategies.moonEncounters = 0
control.canDie(false)
return true
end,
evolveNidorino = function()
if pokemon.inParty("nidorino") then
bridge.caught("nidorino")
return true
end
if battle.isActive() then
tries = 0
canProgress = true
if memory.double("battle", "opponent_hp") == 0 then
input.press("A")
else
battle.automate()
end
elseif tries > 3600 then
print("Broke from Nidorino on tries")
return true
else
if canProgress then
tries = tries + 1
end
input.press("A")
end
end,
evolveNidoking = function()
if battle.handleWild() then
if not inventory.contains("moon_stone") then
if initialize() then
bridge.caught("nidoking")
end
if menu.close() then
return true
end
elseif not inventory.use("moon_stone") then
menu.pause()
end
end
end,
helix = function()
if battle.handleWild() then
if inventory.contains("helix_fossil") then
return true
end
player.interact("Up")
end
end,
reportMtMoon = function()
if battle.pp("horn_attack") == 0 then
print("ERR: Ran out of Horn Attacks")
end
if strategies.moonEncounters then
local parasStatus
local conjunction = "but"
local goodEncounters = strategies.moonEncounters < 10
local parasCatch
if pokemon.inParty("paras") then
parasCatch = "paras"
if goodEncounters then
conjunction = "and"
end
parasStatus = "we found a Paras!"
else
parasCatch = "no_paras"
if not goodEncounters then
conjunction = "and"
end
parasStatus = "we didn't find a Paras :("
end
bridge.caught(parasCatch)
bridge.chat(strategies.moonEncounters.." Moon encounters, "..conjunction.." "..parasStatus)
strategies.moonEncounters = nil
end
local timeLimit = getTimeRequirement("mt_moon")
resetTime(timeLimit, "complete Mt. Moon", true)
return true
end,
-- 4: MT. MOON
dodgeCerulean = function()
return dodgeH{
npc = 0x0242,
sx = 14, sy = 18,
dodge = 19,
offset = 10,
dist = 4
}
end,
dodgeCeruleanLeft = function()
return dodgeH{
npc = 0x0242,
sx = 16, sy = 18,
dodge = 17,
offset = 10,
dist = -7,
left = true
}
end,
rivalSandAttack = function(data)
if battle.isActive() then
if battle.redeployNidoking() then
return false
end
local opponent = battle.opponent()
if memory.value("battle", "accuracy") < 7 then
local sacrifice
if opponent == "pidgeotto" then
local __, turns = combat.bestMove()
if turns == 1 then
sacrifice = pokemon.getSacrifice("pidgey", "spearow", "paras", "oddish", "squirtle")
end
elseif opponent == "raticate" then
sacrifice = pokemon.getSacrifice("pidgey", "spearow", "oddish")
end
if battle.sacrifice(sacrifice) then
return false
end
end
if opponent == "pidgeotto" then
combat.disableThrash = true
elseif opponent == "raticate" then
combat.disableThrash = opponentDamaged() or (not yolo and pokemon.index(0, "hp") < 32) -- RISK
elseif opponent == "ivysaur" then
if not yolo and damaged(5) and inventory.contains("super_potion") then
inventory.use("super_potion", nil, true)
return false
end
combat.disableThrash = opponentDamaged()
else
combat.disableThrash = false
end
battle.automate()
canProgress = true
elseif canProgress then
combat.disableThrash = false
return true
else
textbox.handle()
end
end,
teachThrash = function()
if initialize() then
if pokemon.hasMove("thrash") or pokemon.info("nidoking", "level") < 21 then
return true
end
end
if strategyFunctions.teach({move="thrash",item="rare_candy",replace="leer"}) then
if menu.close() then
local att = pokemon.index(0, "attack")
local def = pokemon.index(0, "defense")
local spd = pokemon.index(0, "speed")
local scl = pokemon.index(0, "special")
local statDesc = att.." "..def.." "..spd.." "..scl
nidoAttack = att
nidoSpeed = spd
nidoSpecial = scl
bridge.stats(statDesc)
print(statDesc)
return true
end
end
end,
potionForMankey = function()
if initialize() then
if pokemon.info("nidoking", "level") > 20 then
return true
end
end
return strategyFunctions.potion({hp=18, yolo=8})
end,
redbarMankey = function()
if not setYolo("mankey") then
return true
end
local curr_hp, red_hp = pokemon.index(0, "hp"), redHP()
if curr_hp <= red_hp then
return true
end
if initialize() then
if pokemon.info("nidoking", "level") < 23 or inventory.count("potion") < 3 then -- RISK
return true
end
bridge.chat("Using Poison Sting to attempt to redbar off Mankey")
end
if battle.isActive() then
canProgress = true
local enemyMove, enemyTurns = combat.enemyAttack()
if enemyTurns then
if enemyTurns < 2 then
return true
end
local scratchDmg = enemyMove.damage
if curr_hp - red_hp > scratchDmg then
return true
end
end
battle.automate("poison_sting")
elseif canProgress then
return true
else
textbox.handle()
end
end,
thrashGeodude = function()
if battle.isActive() then
canProgress = true
if pokemon.isOpponent("geodude") and pokemon.isDeployed("nidoking") then
if battle.sacrifice("squirtle") then
return false
end
end
battle.automate()
elseif canProgress then
return true
else
textbox.handle()
end
end,
potionBeforeGoldeen = function()
if initialize() then
if setYolo("goldeen") or pokemon.index(0, "hp") > 7 then
return true
end
end
return strategyFunctions.potion({hp=64, chain=true})
end,
potionBeforeMisty = function()
local healAmount = 70
if yolo then
if nidoAttack > 53 and nidoSpeed > 50 then
healAmount = 45
elseif nidoAttack > 53 then
healAmount = 65
end
else
if nidoAttack > 53 and nidoSpeed > 51 then -- RISK
healAmount = 45
elseif nidoAttack > 53 and nidoSpeed > 50 then
healAmount = 65
end
end
if initialize() then
if healAmount < 60 then
bridge.chat("Limiting heals to attempt to get closer to red-bar off Misty", inventory.count("potion"))
end
end
return strategyFunctions.potion({hp=healAmount})
end,
fightMisty = function()
if battle.isActive() then
canProgress = true
if battle.redeployNidoking() then
if tempDir == false then
tempDir = true
end
return false
end
local swappedOut = tempDir
if not swappedOut and combat.isConfused() then
tempDir = false
if battle.sacrifice("pidgey", "spearow", "paras") then
return false
end
end
battle.automate()
elseif canProgress then
return true
else
textbox.handle()
end
end,
-- 6: MISTY
potionBeforeRocket = function()
local minAttack = 55 -- RISK
if yolo then
minAttack = minAttack - 1
end
if nidoAttack >= minAttack then
return true
end
return strategyFunctions.potion({hp=10})
end,
jingleSkip = function()
if canProgress then
local px, py = player.position()
if px < 4 then
return true
end
input.press("Left", 0)
else
input.press("A", 0)
canProgress = true
end
end,
catchOddish = function()
if not control.canCatch() then
return true
end
local caught = pokemon.inParty("oddish", "paras")
local battleValue = memory.value("game", "battle")
local px, py = player.position()
if battleValue > 0 then
if battleValue == 2 then
tries = 2
battle.automate()
else
if tries == 0 and py == 31 then
tries = 1
end
if not control.shouldCatch() then
battle.run()
end
end
elseif tries == 1 and py == 31 then
player.interact("Left")
else
local path
if caught then
if not tempDir then
bridge.caught(pokemon.inParty("oddish"))
tempDir = true
end
if py < 21 then
py = 21
elseif py < 24 then
if px < 16 then
px = 17
else
py = 24
end
elseif py < 25 then
py = 25
elseif px > 15 then
px = 15
elseif py < 28 then
py = 28
elseif py > 29 then
py = 29
elseif px ~= 11 then
px = 11
elseif py ~= 29 then
py = 29
else
return true
end
walk.step(px, py)
elseif px == 12 then
local dy
if py == 30 then
dy = 31
else
dy = 30
end
walk.step(px, dy)
else
local path = {{15,19}, {15,25}, {15,25}, {15,27}, {14,27}, {14,30}, {12,30}}
walk.custom(path)
end
end
end,
shopVermilionMart = function()
if initialize() then
setYolo("vermilion")
end
local buyArray, sellArray
if not inventory.contains("pokeball") or (not yolo and nidoAttack < 53) then
sellArray = {{name="pokeball"}, {name="antidote"}, {name="tm34"}, {name="nugget"}}
buyArray = {{name="super_potion",index=1,amount=3}, {name="paralyze_heal",index=4,amount=2}, {name="repel",index=5,amount=3}}
else
sellArray = {{name="antidote"}, {name="tm34"}, {name="nugget"}}
buyArray = {{name="super_potion",index=1,amount=3}, {name="repel",index=5,amount=3}}
end
return shop.transaction{
sell = sellArray,
buy = buyArray
}
end,
-- rivalSandAttack
trashcans = function()
local progress = memory.value("progress", "trashcans")
if textbox.isActive() then
if not canProgress then
if progress < 2 then
tries = tries + 1
end
canProgress = true
end
input.cancel()
else
if progress == 3 then
local px, py = player.position()
if px == 4 and py == 6 then
tries = tries + 1
local timeLimit = getTimeRequirement("trash") + 1
if resetTime(timeLimit, "complete Trashcans ("..tries.." tries)") then
return true
end
setYolo("trash")
local prefix
local suffix = "!"
if tries < 2 then
prefix = "PERFECT"
elseif tries < 4 then
prefix = "Amazing"
elseif tries < 7 then
prefix = "Great"
elseif tries < 10 then
prefix = "Good"
elseif tries < 24 then
prefix = "Ugh"
suffix = "."
else -- TODO trashcans WR
prefix = "Reset me now"
suffix = " BibleThump"
end
bridge.chat(prefix..", "..tries.." try Trashcans"..suffix, utils.elapsedTime())
return true
end
local completePath = {
Down = {{2,11}, {8,7}},
Right = {{2,12}, {3,12}, {2,6}, {3,6}},
Left = {{9,8}, {8,8}, {7,8}, {6,8}, {5,8}, {9,10}, {8,10}, {7,10}, {6,10}, {5,10}, {}, {}, {}, {}, {}, {}},
}
local walkIn = "Up"
for dir,tileset in pairs(completePath) do
for i,tile in ipairs(tileset) do
if px == tile[1] and py == tile[2] then
walkIn = dir
break
end
end
end
input.press(walkIn, 0)
elseif progress == 2 then
if canProgress then
canProgress = false
walk.invertCustom()
end
local inverse = {
Up = "Down",
Right = "Left",
Down = "Up",
Left = "Right"
}
player.interact(inverse[tempDir])
else
local trashPath = {{2,11},{"Left"},{2,11}, {2,12},{4,12},{4,11},{"Right"},{4,11}, {4,9},{"Left"},{4,9}, {4,7},{"Right"},{4,7}, {4,6},{2,6},{2,7},{"Left"},{2,7}, {2,6},{4,6},{4,8},{9,8},{"Up"},{9,8}, {8,8},{8,9},{"Left"},{8,9}, {8,10},{9,10},{"Down"},{9,10},{8,10}}
if tempDir and type(tempDir) == "number" then
local px, py = player.position()
local dx, dy = px, py
if py < 12 then
dy = 12
elseif tempDir == 1 then
dx = 2
else
dx = 8
end
if px ~= dx or py ~= dy then
walk.step(dx, dy)
return
end
tempDir = nil
end
tempDir = walk.custom(trashPath, canProgress)
canProgress = false
end
end
end,
fightSurge = function()
if battle.isActive() then
canProgress = true
local forced
if pokemon.isOpponent("voltorb") then
combat.disableThrash = true
local __, enemyTurns = combat.enemyAttack()
if not enemyTurns or enemyTurns > 2 then
forced = "bubblebeam"
elseif enemyTurns == 2 and not opponentDamaged() then
local curr_hp, red_hp = pokemon.index(0, "hp"), redHP()
local afterHit = curr_hp - 20
if afterHit > 5 and afterHit <= red_hp then
forced = "bubblebeam"
end
end
else
combat.disableThrash = false
end
battle.automate(forced)
elseif canProgress then
return true
else
textbox.handle()
end
end,
-- 7: SURGE
procureBicycle = function()
if inventory.contains("bicycle") then
if not textbox.isActive() then
return true
end
input.cancel()
elseif textbox.handle() then
player.interact("Right")
end
end,
swapBicycle = function()
local bicycleIdx = inventory.indexOf("bicycle")
if bicycleIdx < 3 then
return true
end
local main = memory.value("menu", "main")
if main == 128 then
if menu.getCol() ~= 5 then
menu.select(2, true)
else
local selection = memory.value("menu", "selection_mode")
if selection == 0 then
if menu.select(0, "accelerate", true, nil, true) then
input.press("Select")
end
else
if menu.select(bicycleIdx, "accelerate", true, nil, true) then
input.press("Select")
end
end
end
else
menu.pause()
end
end,
redbarCubone = function()
if battle.isActive() then
local forced
canProgress = true
if pokemon.isOpponent("cubone") then
local enemyMove, enemyTurns = combat.enemyAttack()
if enemyTurns then
local curr_hp, red_hp = pokemon.index(0, "hp"), redHP()
local clubDmg = enemyMove.damage
local afterHit = curr_hp - clubDmg
red_hp = red_hp - 2
if afterHit > -2 and afterHit < red_hp then
forced = "thunderbolt"
else
afterHit = afterHit - clubDmg
if afterHit > 1 and afterHit < red_hp then
forced = "thunderbolt"
end
end
if forced and initialize() then
bridge.chat("Using Thunderbolt to attempt to redbar off Cubone")
end
end
end
battle.automate(forced)
elseif canProgress then
return true
else
battle.automate()
end
end,
shopTM07 = function()
return shop.transaction{
direction = "Up",
buy = {{name="horn_drill", index=3}}
}
end,
shopRepels = function()
return shop.transaction{
direction = "Up",
buy = {{name="super_repel", index=3, amount=9}}
}
end,
shopPokeDoll = function()
return shop.transaction{
direction = "Down",
buy = {{name="pokedoll", index=0}}
}
end,
shopVending = function()
return shop.vend{
direction = "Up",
buy = {{name="fresh_water", index=0}, {name="soda_pop", index=1}}
}
end,
giveWater = function()
if not inventory.contains("fresh_water", "soda_pop") then
return true
end
if textbox.isActive() then
input.cancel("A")
else
local cx, cy = memory.raw(0x0223) - 3, memory.raw(0x0222) - 3
local px, py = player.position()
if utils.dist(cx, cy, px, py) == 1 then
player.interact(walk.dir(px, py, cx, cy))
else
walk.step(cx, cy)
end
end
end,
shopExtraWater = function()
return shop.vend{
direction = "Up",
buy = {{name="fresh_water", index=0}}
}
end,
shopBuffs = function()
if initialize() then
local minSpecial = 45
if yolo then
minSpecial = minSpecial - 1
end
if nidoAttack >= 54 and nidoSpecial >= minSpecial then
riskGiovanni = true
print("Giovanni skip strats!")
end
end
local xspecAmt = 4
if riskGiovanni then
xspecAmt = xspecAmt + 1
elseif nidoSpecial < 46 then
-- xspecAmt = xspecAmt - 1
end
return shop.transaction{
direction = "Up",
buy = {{name="x_accuracy", index=0, amount=10}, {name="x_speed", index=5, amount=4}, {name="x_special", index=6, amount=xspecAmt}}
}
end,
deptElevator = function()
if textbox.isActive() then
canProgress = true
menu.select(0, false)
else
if canProgress then
return true
end
player.interact("Up")
end
end,
swapRepels = function()
local repelIdx = inventory.indexOf("super_repel")
if repelIdx < 3 then
return true
end
local main = memory.value("menu", "main")
if main == 128 then
if menu.getCol() ~= 5 then
menu.select(2, true)
else
local selection = memory.value("menu", "selection_mode")
if selection == 0 then
if menu.select(1, "accelerate", true, nil, true) then
input.press("Select")
end
else
if menu.select(repelIdx, "accelerate", true, nil, true) then
input.press("Select")
end
end
end
else
menu.pause()
end
end,
-- 8: FLY
lavenderRival = function()
if battle.isActive() then
canProgress = true
local forced
if nidoSpecial > 44 then -- RISK
local __, enemyTurns = combat.enemyAttack()
if enemyTurns and enemyTurns < 2 and pokemon.isOpponent("pidgeotto", "gyarados") then
battle.automate()
return false
end
end
if pokemon.isOpponent("gyarados") or prepare("x_accuracy") then
battle.automate()
end
elseif canProgress then
return true
else
input.cancel()
end
end,
pokeDoll = function()
if battle.isActive() then
canProgress = true
inventory.use("pokedoll", nil, true)
elseif canProgress then
return true
else
input.cancel()
end
end,
digFight = function()
if battle.isActive() then
canProgress = true
local currentlyDead = memory.double("battle", "our_hp") == 0
if currentlyDead then
local backupPokemon = pokemon.getSacrifice("paras", "squirtle")
if not backupPokemon then
return resetDeath()
end
if utils.onPokemonSelect(memory.value("battle", "menu")) then
menu.select(pokemon.indexOf(backupPokemon), true)
else
input.press("A")
end
else
battle.automate()
end
elseif canProgress then
return true
else
textbox.handle()
end
end,
thunderboltFirst = function()
local forced
if pokemon.isOpponent("zubat") then
canProgress = true
forced = "thunderbolt"
elseif canProgress then
return true
end
battle.automate(forced)
end,
-- 8: POKÉFLUTE
playPokeflute = function()
if battle.isActive() then
return true
end
if memory.value("battle", "menu") == 95 then
input.press("A")
elseif menu.pause() then
inventory.use("pokeflute")
end
end,
drivebyRareCandy = function()
if textbox.isActive() then
canProgress = true
input.cancel()
elseif canProgress then
return true
else
local px, py = player.position()
if py < 13 then
tries = 0
return
end
if py == 13 and tries % 2 == 0 then
input.press("A", 2)
else
input.press("Up")
tries = 0
end
tries = tries + 1
end
end,
safariCarbos = function()
if initialize() then
setYolo("safari_carbos")
end
local minSpeed = 50
if yolo then
minSpeed = minSpeed - 1
end
if nidoSpeed >= minSpeed then
return true
end
if inventory.contains("carbos") then
if walk.step(20, 20) then
return true
end
else
local px, py = player.position()
if px < 21 then
walk.step(21, py)
elseif px == 21 and py == 13 then
player.interact("Left")
else
walk.step(21, 13)
end
end
end,
centerSkipFullRestore = function()
if initialize() then
if yolo or inventory.contains("full_restore") then
return true
end
bridge.chat("We need to grab the backup Full Restore here.")
end
local px, py = player.position()
if px < 21 then
px = 21
elseif py < 9 then
py = 9
else
return strategyFunctions.interact({dir="Down"})
end
walk.step(px, py)
end,
silphElevator = function()
if textbox.isActive() then
canProgress = true
menu.select(9, false, true)
else
if canProgress then
return true
end
player.interact("Up")
end
end,
fightSilphMachoke = function()
if battle.isActive() then
canProgress = true
if nidoSpecial > 44 then
return prepare("x_accuracy")
end
battle.automate("thrash")
elseif canProgress then
return true
else
textbox.handle()
end
end,
silphCarbos = function()
if nidoSpeed > 50 then
return true
end
return strategyFunctions.interact({dir="Left"})
end,
silphRival = function()
if battle.isActive() then
if initialize() then
tempDir = combat.healthFor("RivalGyarados")
canProgress = true
end
local gyaradosDamage = tempDir
local forced
local readyToAttack = false
local opponentName = battle.opponent()
if opponentName == "gyarados" then
readyToAttack = true
local hp, red_hp = pokemon.index(0, "hp"), redHP()
if hp > gyaradosDamage * 0.98 and hp - gyaradosDamage * 0.975 < red_hp then --TODO
if prepare("x_special") then
forced = "ice_beam"
else
readyToAttack = false
end
elseif isPrepared("x_special") then
local canPotion
if inventory.contains("potion") and hp + 20 > gyaradosDamage and hp + 20 - gyaradosDamage < red_hp then
canPotion = "potion"
elseif inventory.contains("super_potion") and hp + 50 > gyaradosDamage and hp + 50 - gyaradosDamage < red_hp then
canPotion = "super_potion"
end
if canPotion then
inventory.use(canPotion, nil, true)
readyToAttack = false
end
end
elseif prepare("x_accuracy", "x_speed") then
if opName == "pidgeot" then
if nidoSpecial < 45 or hasHealthFor("KogaWeezing", 10) then --TODO remove for red bar
forced = "thunderbolt"
end
elseif opponentName == "alakazam" or opponentName == "growlithe" then
forced = "earthquake"
end
readyToAttack = true
end
if readyToAttack then
battle.automate(forced)
end
elseif canProgress then
return true
else
textbox.handle()
end
end,
potionBeforeGiovanni = function()
-- TODO verify newly leveled
-- local curr_hp = pokemon.index(0, "hp")
-- if curr_hp < 16 and pokemon.index(0, "level") == 37 then
-- local rareCandyCount = inventory.count("rare_candy")
-- if rareCandyCount > 2 then
-- if menu.pause() then
-- inventory.use("rare_candy", nil, false)
-- end
-- return false
-- end
-- end
return strategyFunctions.potion({hp=16, yolo=12, close=true})
end,
fightSilphGiovanni = function()
if battle.isActive() then
canProgress = true
local forced
local opponentName = battle.opponent()
if opponentName == "nidorino" then
if battle.pp("horn_drill") > 2 then
forced = "horn_drill"
else
forced = "earthquake"
end
elseif opponentName == "rhyhorn" then
forced = "ice_beam"
elseif opponentName == "kangaskhan" or opponentName == "nidoqueen" then
forced = "horn_drill"
end
battle.automate(forced)
elseif canProgress then
return true
else
textbox.handle()
end
end,
-- 9: SILPH CO.
potionBeforeHypno = function()
local curr_hp, red_hp = pokemon.index(0, "hp"), redHP()
local healthUnderRedBar = red_hp - curr_hp
local yoloHP = combat.healthFor("HypnoHeadbutt") * 0.9
local useRareCandy = inventory.count("rare_candy") > 2
local healTarget
if healthUnderRedBar >= 0 then
healTarget = "HypnoHeadbutt"
if useRareCandy then
useRareCandy = healthUnderRedBar > 2
end
else
healTarget = "HypnoConfusion"
if useRareCandy then
useRareCandy = false --TODO
-- useRareCandy = curr_hp < combat.healthFor("KogaWeezing") * 0.85
end
end
if useRareCandy then
if menu.pause() then
inventory.use("rare_candy", nil, false)
end
return false
end
return strategyFunctions.potion({hp=healTarget, yolo=yoloHP, close=true})
end,
fightHypno = function()
if battle.isActive() then
local forced
if pokemon.isOpponent("hypno") then
if pokemon.info("nidoking", "hp") > combat.healthFor("KogaWeezing") * 0.9 then
if combat.isDisabled(85) then
forced = "ice_beam"
else
forced = "thunderbolt"
end
end
end
battle.automate(forced)
canProgress = true
elseif canProgress then
return true
else
textbox.handle()
end
end,
fightKoga = function() --TODO x-accuracy?
if battle.isActive() then
local forced
if pokemon.isOpponent("weezing") then
if opponentDamaged(2) then
inventory.use("pokeflute", nil, true)
return false
end
if combat.isDisabled(85) then
forced = "ice_beam"
else
forced = "thunderbolt"
end
control.canDie(true)
end
battle.automate(forced)
canProgress = true
elseif canProgress then
deepRun = true
return true
else
textbox.handle()
end
end,
-- 10: KOGA
dodgeGirl = function()
local gx, gy = memory.raw(0x0223) - 5, memory.raw(0x0222)
local px, py = player.position()
if py > gy then
if px > 3 then
px = 3
else
return true
end
elseif gy - py ~= 1 or px ~= gx then
py = py + 1
elseif px == 3 then
px = 2
else
px = 3
end
walk.step(px, py)
end,
cinnabarCarbos = function()
local px, py = player.position()
if px == 21 then
return true
end
local minSpeed = 51
if yolo then
minSpeed = minSpeed - 1
end
if nidoSpeed > minSpeed then -- TODO >=
walk.step(21, 20)
else
if py == 20 then
py = 21
elseif px == 17 and not inventory.contains("carbos") then
player.interact("Right")
return false
else
px = 21
end
walk.step(px, py)
end
end,
fightErika = function()
if battle.isActive() then
canProgress = true
local forced
local curr_hp, red_hp = pokemon.index(0, "hp"), redHP()
local razorDamage = 34
if curr_hp > razorDamage and curr_hp - razorDamage < red_hp then
if opponentDamaged() then
forced = "thunderbolt"
elseif nidoSpecial < 45 then
forced = "ice_beam"
else
forced = "thunderbolt"
end
elseif riskGiovanni then
forced = "ice_beam"
end
battle.automate(forced)
elseif canProgress then
return true
else
textbox.handle()
end
end,
-- 11: ERIKA
waitToReceive = function()
local main = memory.value("menu", "main")
if main == 128 then
if canProgress then
return true
end
elseif main == 32 or main == 123 then
canProgress = true
input.cancel()
else
input.press("Start", 2)
end
end,
-- 14: SABRINA
earthquakeElixer = function(data)
if battle.pp("earthquake") >= data.min then
if closeMenuFor(data) then
return true
end
return false
end
if initialize() then
if areaName then
print("EQ Elixer: "..areaName)
end
end
return useItem({item="elixer", poke="nidoking", chain=data.chain, close=data.close})
end,
fightGiovanniMachoke = function()
if initialize() then
if nidoAttack >= 55 then
local eqPpRequired = nidoSpecial >= 47 and 7 or 8
if battle.pp("earthquake") >= eqPpRequired then
bridge.chat("Using Earthquake strats on the Machokes")
return true
end
end
end
return prepare("x_special")
end,
checkGiovanni = function()
local ryhornDamage = math.floor(combat.healthFor("GiovanniRhyhorn") * 0.95) --RISK
if initialize() then
local earthquakePP = battle.pp("earthquake")
if earthquakePP >= 2 then
if riskGiovanni then
if earthquakePP >= 5 then
bridge.chat("Saved enough Earthquake PP for safe strats on Giovanni")
elseif earthquakePP >= 3 and battle.pp("horn_drill") >= 5 and (yolo or pokemon.info("nidoking", "hp") >= ryhornDamage) then -- RISK
bridge.chat("Using risky strats on Giovanni to skip the extra Max Ether...")
else
riskGiovanni = false
end
end
return true
end
local message = "Ran out of Earthquake PP :( "
if yolo then
message = message.."Risking on Giovanni."
else
message = message.."Time for standard strats."
end
bridge.chat(message)
riskGiovanni = false
end
return strategyFunctions.potion({hp=50, yolo=ryhornDamage})
end,
fightGiovanni = function()
if battle.isActive() then
if initialize() then
tempDir = battle.pp("earthquake")
canProgress = true
end
local forced, needsXSpecial
local startEqPP = tempDir
if riskGiovanni then
if startEqPP < 5 then
needsXSpecial = true
end
if needsXSpecial or battle.pp("earthquake") < 4 then
forced = "ice_beam"
end
else
needsXSpecial = startEqPP < 2
if pokemon.isOpponent("rhydon") then
forced = "ice_beam"
end
end
if needsXSpecial and not prepare("x_special") then
return false
end
battle.automate(forced)
elseif canProgress then
return true
else
textbox.handle()
end
end,
-- 15: GIOVANNI
viridianRival = function()
if battle.isActive() then
if not canProgress then
if riskGiovanni or nidoSpecial < 45 or pokemon.index(0, "speed") < 134 then
tempDir = "x_special"
else
print("Skip X Special strats!")
end
canProgress = true
end
if prepare("x_accuracy", tempDir) then
local forced
if pokemon.isOpponent("pidgeot") then
forced = "thunderbolt"
elseif riskGiovanni then
if pokemon.isOpponent("rhyhorn") or opponentDamaged() then
forced = "ice_beam"
elseif pokemon.isOpponent("gyarados") then
forced = "thunderbolt"
elseif pokemon.isOpponent("growlithe", "alakazam") then
forced = "earthquake"
end
end
battle.automate(forced)
end
elseif canProgress then
return true
else
textbox.handle()
end
end,
ether = function(data)
local main = memory.value("menu", "main")
data.item = tempDir
if tempDir and completedMenuFor(data) then
if closeMenuFor(data) then
return true
end
else
if not tempDir then
if data.max then
-- TODO don't skip center if not in redbar
maxEtherSkip = nidoAttack > 53 and battle.pp("earthquake") > 0 and battle.pp("horn_drill") > 3
if maxEtherSkip then
return true
end
bridge.chat("Grabbing the Max Ether to skip the Elite 4 Center")
end
tempDir = inventory.contains("ether", "max_ether")
if not tempDir then
return true
end
tries = inventory.count(tempDir) --TODO remove?
end
if memory.value("menu", "main") == 144 and menu.getCol() == 5 then
if memory.value("battle", "menu") ~= 95 then
menu.select(pokemon.battleMove("horn_drill"), true)
else
input.cancel()
end
elseif menu.pause() then
inventory.use(tempDir, "nidoking")
end
end
end,
pickMaxEther = function()
if not canProgress then
if maxEtherSkip then
return true
end
if memory.value("player", "moving") == 0 then
if player.isFacing("Right") then
canProgress = true
end
tries = not tries
if tries then
input.press("Right", 1)
end
end
return false
end
if inventory.contains("max_ether") then
return true
end
player.interact("Right")
end,
push = function(data)
local pos
if data.dir == "Up" or data.dir == "Down" then
pos = data.y
else
pos = data.x
end
local newP = memory.raw(pos)
if tries == 0 then
tries = {start=newP}
elseif tries.start ~= newP then
return true
end
input.press(data.dir, 0)
end,
potionBeforeLorelei = function()
if initialize() then
local canPotion
if inventory.contains("potion") and hasHealthFor("LoreleiDewgong", 20) then
canPotion = true
elseif inventory.contains("super_potion") and hasHealthFor("LoreleiDewgong", 50) then
canPotion = true
end
if not canPotion then
return true
end
bridge.chat("Healing before Lorelei to skip the Elite 4 Center...")
end
return strategyFunctions.potion({hp=combat.healthFor("LoreleiDewgong")})
end,
depositPokemon = function()
local toSize
if hasHealthFor("LoreleiDewgong") then
toSize = 1
else
toSize = 2
end
if memory.value("player", "party_size") == toSize then
if menu.close() then
return true
end
else
if not textbox.isActive() then
player.interact("Up")
else
local pc = memory.value("menu", "size")
if memory.value("battle", "menu") ~= 95 and (pc == 2 or pc == 4) then
local menuColumn = menu.getCol()
if menuColumn == 10 then
input.press("A")
elseif menuColumn == 5 then
local depositIndex = 1
if pokemon.indexOf("pidgey", "spearow") == 1 then
depositIndex = 2
end
menu.select(depositIndex)
else
menu.select(1)
end
else
input.press("A")
end
end
end
end,
centerSkip = function()
setYolo("e4center")
local message = "Skipping the Center and attempting to redbar "
if hasHealthFor("LoreleiDewgong") then
message = message.."off Lorelei..."
else
message = message.."the Elite 4!"
end
bridge.chat(message)
return true
end,
lorelei = function()
if battle.isActive() then
canProgress = true
if battle.redeployNidoking() then
return false
end
local forced
local opponentName = battle.opponent()
if opponentName == "dewgong" then
if battle.sacrifice("pidgey", "spearow", "squirtle", "paras", "oddish") then
return false
end
elseif opponentName == "jinx" then
if battle.pp("horn_drill") < 2 then
forced = "earthquake"
end
end
if prepare("x_accuracy") then
battle.automate(forced)
end
elseif canProgress then
return true
else
textbox.handle()
end
end,
-- 16: LORELEI
bruno = function()
if battle.isActive() then
canProgress = true
local forced
if pokemon.isOpponent("onix") then
forced = "ice_beam"
-- local curr_hp, red_hp = pokemon.info("nidoking", "hp"), redHP()
-- if curr_hp > red_hp then
-- local enemyMove, enemyTurns = combat.enemyAttack()
-- if enemyTurns and enemyTurns > 1 then
-- local rockDmg = enemyMove.damage
-- if curr_hp - rockDmg <= red_hp then
-- forced = "thunderbolt"
-- end
-- end
-- end
end
if prepare("x_accuracy") then
battle.automate(forced)
end
elseif canProgress then
return true
else
textbox.handle()
end
end,
agatha = function() --TODO test without x acc
if battle.isActive() then
canProgress = true
if combat.isSleeping() then
inventory.use("pokeflute", nil, true)
return false
end
if pokemon.isOpponent("gengar") then
local currentHP = pokemon.info("nidoking", "hp")
-- if not yolo and currentHP <= 56 and not isPrepared("x_speed") then
if not yolo and currentHP <= 56 and not isPrepared("x_accuracy", "x_speed") then
local toPotion = inventory.contains("full_restore", "super_potion")
if toPotion then
inventory.use(toPotion, nil, true)
return false
end
end
-- if not prepare("x_speed") then
if not prepare("x_accuracy", "x_speed") then
return false
end
end
battle.automate()
elseif canProgress then
return true
else
textbox.handle()
end
end,
prepareForLance = function()
local enableFull
if hasHealthFor("LanceGyarados", 100) then
enableFull = inventory.count("super_potion") < 2
elseif hasHealthFor("LanceGyarados", 50) then
enableFull = not inventory.contains("super_potion")
else
enableFull = true
end
local min_recovery = combat.healthFor("LanceGyarados")
return strategyFunctions.potion({hp=min_recovery, full=enableFull, chain=true})
end,
lance = function()
if battle.isActive() then
canProgress = true
local xItem
if pokemon.isOpponent("dragonair") then
xItem = "x_speed"
else
xItem = "x_special"
end
if prepare(xItem) then
battle.automate()
end
elseif canProgress then
return true
else
textbox.handle()
end
end,
prepareForBlue = function()
if initialize() then
setYolo("blue")
end
local skyDmg = combat.healthFor("BlueSky") * 0.9
local wingDmg = combat.healthFor("BluePidgeot")
return strategyFunctions.potion({hp=skyDmg-50, yolo=wingDmg, full=true})
end,
blue = function()
if battle.isActive() then
if not canProgress then
canProgress = true
if nidoSpecial >= 45 and pokemon.index(0, "speed") >= 52 and inventory.contains("x_special") then
tempDir = "x_special"
else
tempDir = "x_speed"
end
if not STREAMING_MODE then
tempDir = "x_speed"
end
end
local boostFirst = pokemon.index(0, "hp") < 55
local firstItem, secondItem
if boostFirst then
firstItem = tempDir
secondItem = "x_accuracy"
else
firstItem = "x_accuracy"
secondItem = tempDir
end
local forced = "horn_drill"
if memory.value("battle", "turns") > 0 then
local skyDamage = combat.healthFor("BlueSky")
local healCutoff = skyDamage * 0.825
if initialize() then
if not isPrepared("x_accuracy", tempDir) then
local msg = "Uh oh... First-turn Sky Attack could end the run here, "
if pokemon.index(0, "hp") > skyDamage then
msg = msg.."no criticals pls D:"
elseif canHealFor(healCutoff) then
msg = msg.."attempting to heal for it"
if not canHealFor(skyDamage) then
msg = msg.." (damage range)"
end
msg = msg.."."
else
msg = msg.."and nothing left to heal with BibleThump"
end
bridge.chat(msg)
end
end
if prepare(firstItem) then
if not isPrepared(secondItem) then
local toPotion = canHealFor(healCutoff)
if toPotion then
inventory.use(toPotion, nil, true)
return false
end
end
if prepare("x_accuracy", tempDir) then
battle.automate(forced)
end
end
else
if prepare(firstItem, secondItem) then
if pokemon.isOpponent("alakazam") then
if tempDir == "x_speed" then
forced = "earthquake"
end
elseif pokemon.isOpponent("rhydon") then
if tempDir == "x_special" then
forced = "ice_beam"
end
end
battle.automate(forced)
end
end
elseif canProgress then
return true
else
textbox.handle()
end
end,
champion = function()
if canProgress then
if tries > 1500 then
return hardReset("Beat the game in "..canProgress.." !")
end
if tries == 0 then
bridge.tweet("Beat Pokemon Red in "..canProgress.."!")
if strategies.seed then
print(utils.frames().." frames, with seed "..strategies.seed)
print("Please save this seed number to share, if you would like proof of your run!")
end
end
tries = tries + 1
elseif memory.value("menu", "shop_current") == 252 then
strategyFunctions.split({finished=true})
canProgress = utils.elapsedTime()
else
input.cancel()
end
end
}
function strategies.execute(data)
if strategyFunctions[data.s](data) then
tries = 0
canProgress = false
initialized = false
tempDir = nil
if resetting then
return nil
end
return true
end
return false
end
function strategies.init(midGame)
if not STREAMING_MODE then
-- setYolo(0)
nidoAttack = 55
nidoSpeed = 50
nidoSpecial = 45
riskGiovanni = true
splitTime = utils.timeSince(0)
print(nidoAttack.." x "..nidoSpeed.." "..nidoSpecial)
end
if midGame then
combat.factorPP(true)
end
end
function strategies.softReset()
canProgress = false
initialized = false
maxEtherSkip = false
tempDir = nil
strategies.moonEncounters = nil
tries = 0
splitNumber, splitTime = 0, 0
deepRun = false
resetting = nil
yolo = false
end
return strategies