PokeBot/action/battle.lua

298 lines
6.4 KiB
Lua

local Battle = {}
local Textbox = require "action.textbox"
local Combat = require "ai.combat"
local Control = require "ai.control"
local Memory = require "util.memory"
local Menu = require "util.menu"
local Input = require "util.input"
local Utils = require "util.utils"
local Inventory = require "storage.inventory"
local Pokemon = require "storage.pokemon"
-- HELPERS
local function potionsForHit(potion, curr_hp, max_hp)
if not potion then
return
end
local ours, killAmount = Combat.inKillRange()
if ours then
return Utils.canPotionWith(potion, killAmount, curr_hp, max_hp)
end
end
local function recover()
if Control.canRecover() then
local currentHP = Pokemon.index(0, "hp")
if currentHP > 0 then
local maxHP = Pokemon.index(0, "max_hp")
if currentHP < maxHP then
local first, second
if Control.preferredPotion == "full" then
first, second = "full_restore", "super_potion"
if maxHP - currentHP > 54 then
first = "full_restore"
second = "super_potion"
else
first = "super_potion"
second = "full_restore"
end
else
if Control.preferredPotion == "super" and maxHP - currentHP > 22 then
first = "super_potion"
second = "potion"
else
first = "potion"
second = "super_potion"
end
end
local potion = Inventory.contains(first, second)
if potionsForHit(potion, currentHP, maxHP) then
Inventory.use(potion, nil, true)
return true
end
end
end
end
if Memory.value("battle", "paralyzed") == 64 then
local heals = Inventory.contains("paralyze_heal", "full_restore")
if heals then
Inventory.use(heals, nil, true)
return true
end
end
end
local function openBattleMenu()
if Memory.value("battle", "text") == 1 then
Input.cancel()
return false
end
local battleMenu = Memory.value("battle", "menu")
local col = Menu.getCol()
if battleMenu == 106 or (battleMenu == 94 and col == 5) then
return true
elseif battleMenu == 94 then
local rowSelected = Memory.value("menu", "row")
if col == 9 then
if rowSelected == 1 then
Input.press("Up")
else
Input.press("A")
end
else
Input.press("Left")
end
else
Input.press("B")
end
end
local function attack(attackIndex)
if not Battle.opponentAlive() then
Input.cancel()
elseif openBattleMenu() then
Menu.select(attackIndex, true, false, false, false, 3)
end
end
function movePP(name)
local midx = Pokemon.battleMove(name)
if not midx then
return 0
end
return Memory.raw(0x102C + midx)
end
Battle.pp = movePP
-- UTILS
function Battle.swapMove(move, toIndex)
toIndex = toIndex + 1
if openBattleMenu() then
local moveIndex = Pokemon.battleMove(move)
if not moveIndex or moveIndex == toIndex then
return true
end
local selection = Memory.value("menu", "selection_mode")
local swapSelect
if selection == toIndex then
swapSelect = moveIndex
else
swapSelect = toIndex
end
local menuSize = Memory.raw(0x101F) == 0 and 3 or 4
if Menu.select(swapSelect, true, false, nil, true, menuSize) then
Input.press("Select")
end
end
end
function Battle.isActive()
return Memory.value("game", "battle") > 0
end
function Battle.isTrainer()
local battleType = Memory.value("game", "battle")
if battleType == 2 then
return true
end
if battleType == 1 then
Battle.handle()
else
Textbox.handle()
end
end
function Battle.opponent()
return Pokemon.getName(Memory.value("battle", "opponent_id"))
end
function Battle.opponentAlive()
return Memory.double("battle", "opponent_hp") > 0
end
-- HANDLE
function Battle.run()
if not Battle.opponentAlive() then
Input.cancel()
elseif Memory.value("battle", "menu") ~= 94 then
if Memory.value("menu", "text_length") == 127 then
Input.press("B")
else
Input.cancel()
end
elseif Textbox.handle() then
local selected = Memory.value("menu", "selection")
if selected == 239 then
Input.press("A", 2)
elseif selected == 233 then
Input.press("Right")
else
Input.escape()
end
end
end
function Battle.handle()
if not Control.shouldCatch() then
if Control.shouldFight() then
Battle.fight()
else
Battle.run()
end
end
end
function Battle.handleWild()
if Memory.value("game", "battle") ~= 1 then
return true
end
Battle.handle()
end
function Battle.fight(move, skipBuffs)
if move then
if type(move) ~= "number" then
move = Pokemon.battleMove(move)
end
attack(move)
else
move = Combat.bestMove()
if move then
Battle.accurateAttack = move.accuracy == 100
attack(move.midx)
elseif Memory.value("menu", "text_length") == 127 then
Input.press("B")
else
Input.cancel()
end
end
end
function Battle.swap(target)
local battleMenu = Memory.value("battle", "menu")
if Menu.onPokemonSelect(battleMenu) then
if Menu.getCol() == 0 then
Pokemon.select(target)
else
Input.press("A")
end
elseif battleMenu == 94 then
local selected = Memory.value("menu", "selection")
if selected == 199 then
Input.press("A", 2)
elseif Menu.getCol() == 9 then
Input.press("Right", 0)
else
Input.press("Up", 0)
end
else
Input.cancel()
end
end
function Battle.automate(moveName, skipBuffs)
if not recover() then
local state = Memory.value("game", "battle")
if state == 0 then
Input.cancel()
else
if moveName and movePP(moveName) == 0 then
moveName = nil
end
if state == 1 then
if Control.shouldFight() then
Battle.fight(moveName, skipBuffs)
else
Battle.run()
end
elseif state == 2 then
Battle.fight(moveName, skipBuffs)
end
end
end
end
-- SACRIFICE
function Battle.sacrifice(...)
local sacrifice = Pokemon.getSacrifice(...)
if sacrifice then
Battle.swap(sacrifice)
return true
end
return false
end
function Battle.redeployNidoking()
if Pokemon.isDeployed("nidoking") then
return false
end
if Menu.onPokemonSelect() then
Pokemon.select("nidoking")
elseif Menu.hasTextbox() and Menu.getCol() == 1 then
Input.press("A")
else
local __, turns = Combat.bestMove()
if turns == 1 then
if Pokemon.isDeployed("spearow") then
forced = "growl"
elseif Pokemon.isDeployed("squirtle") then
forced = "tail_whip"
else
forced = "sand_attack"
end
end
Battle.automate(forced)
end
return true
end
return Battle