PokeBot-GenerationII/ai/strategies.lua

681 lines
15 KiB
Lua

local Strategies = {}
local Combat = require "ai.combat"
local Control = require "ai.control"
local Battle = require "action.battle"
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 yellow = YELLOW
local splitNumber, splitTime = 0, 0
local resetting, itemPos1, itemPos2, itemNumber
local status = {tries = 0, canProgress = nil, initialized = false, tempDir = false}
Strategies.status = status
local strategyFunctions
-- RISK/RESET
function Strategies.getTimeRequirement(name)
return Strategies.timeRequirements[name]()
end
-- RISK/RESET
function Strategies.hardReset(message, extra, wait)
resetting = true
if Strategies.seed then
if extra then
extra = extra.." | "..Strategies.seed
else
extra = Strategies.seed
end
end
--Reset values
--RUNNING4CONTINUE = false
--RUNNING4NEWGAME = true
Bridge.chat(message, extra)
if wait and INTERNAL and not STREAMING_MODE then
strategyFunctions.wait()
else
client.reboot_core()
end
return true
end
function Strategies.reset(reason, extra, wait)
local time = Utils.elapsedTime()
local resetMessage = "reset"
if time then
resetMessage = resetMessage.." after "..time
end
resetMessage = resetMessage.." at "..Control.areaName
local separator
if Strategies.deepRun and not Control.yolo then
separator = " BibleThump"
else
separator = ":"
end
resetMessage = resetMessage..separator.." "..reason
if status.tweeted then
Strategies.tweetProgress(resetMessage)
end
return Strategies.hardReset(resetMessage, extra, wait)
end
-- RESET TO CONTINUE
--[[function Strategies.SkipReset(message)
RUNNING4CONTINUE = true
EXTERNALDONE = false
client.reboot_core()
return true
end]]
function Strategies.death(extra)
local reason = "Died"
--[[local reason
if Control.missed then
reason = "Missed"
elseif Control.criticaled then
reason = "Critical'd"
elseif Control.yolo then
reason = "Yolo strats"
else
reason = "Died"
end]]
return Strategies.reset(reason, extra)
end
function Strategies.overMinute(min)
if type(min) == "string" then
min = Strategies.getTimeRequirement(min)
end
return Utils.igt() > (min * 60)
end
function Strategies.resetTime(timeLimit, reason, once)
if Strategies.overMinute(timeLimit) then
reason = "Took too long to "..reason
if RESET_FOR_TIME then
return Strategies.reset(reason)
end
if once then
print(reason.." "..Utils.elapsedTime())
end
end
end
-- HELPERS
function Strategies.initialize()
if not status.initialized then
status.initialized = true
return true
end
end
function Strategies.buffTo(buff, defLevel, usePPAmount, secondAttack)
if Battle.isActive() then
status.canProgress = true
local forced
--go by def level
if not usePPAmount then
if defLevel and Memory.double("battle", "opponent_defense") > defLevel then
forced = buff
end
--go by use PP amount
else
local AvailablePP = Battle.pp(buff)
if usePPAmount ~= "infinite" then
if Strategies.initialize() then
status.tempDir = AvailablePP-usePPAmount
end
if AvailablePP > status.tempDir and AvailablePP > 0 then
forced = buff
end
else
if AvailablePP > 0 then
forced = buff
end
end
end
--second attack
if forced ~= buff and secondAttack ~= nil then
local AvailablePP = Battle.pp(secondAttack)
if AvailablePP > 0 then
forced = secondAttack
end
end
Battle.automate(forced, true)
elseif status.canProgress then
return true
else
Battle.automate()
end
end
function Strategies.useItem(item, pokemon, Close, Option)
if Strategies.initialize() then
status.tempDir = false
status.canProgress = false
end
--set options
if not Option then
Option = 1 --use
status.canProgress = true
elseif Option == "give" then
--check if the pokemon held a item before give it
if not status.canProgress then
if Pokemon.index(Pokemon.indexOf(pokemon), "held") ~= 0 then
return true
elseif Pokemon.index(Pokemon.indexOf(pokemon), "held") == 0 then
status.canProgress = true
end
end
Option = 2
end
--select/give/use items
if status.canProgress then
local MainMenu = Memory.value("menu", "main")
local Row = Memory.value("menu", "row")
local ItemRow = Memory.value("menu", "input_row")
local Column = Memory.value("menu", "column")
local ShopCurrent = Memory.value("menu", "shop_current")
local MenuSize = Memory.value("menu", "size")
--open menu
if MainMenu ~= 50 and MainMenu ~= 121 and MainMenu ~= 127 and not status.tempDir then
Input.press("Start", 2)
--close menu
elseif MainMenu == 1 and status.tempDir then
return true
--item menu
elseif MainMenu == 50 then
--select bitter berry and go equip it to totodile
if not status.tempDir then
if Column ~= 0 then
Input.press("Right", 2)
else
--select item
if ShopCurrent ~= 66 then
local ItemIdx = Inventory.indexOf(item)
if ItemRow ~= ItemIdx+1 then
Input.press("Down", 2)
else
Input.press("A", 2)
end
--option menu
else
--reset option with menu size
if MenuSize == 3 and Option == 2 then
Option = 1
end
--use/give/toss
if ItemRow ~= Option then
Input.press("Down", 2)
else
Input.press("A", 2)
end
end
end
--switch to next function
else
if not Close then
return true
else
Input.press("B", 2)
end
end
--pokemon menu
elseif MainMenu == 127 then
local PokemonIdx = Pokemon.indexOf(pokemon)
if ItemRow ~= PokemonIdx+1 then
Input.press("Down", 2)
else
Input.press("A", 2)
status.tempDir = true
end
--start menu(open bag)
elseif MainMenu == 121 and not status.tempDir then
if Row < 3 then
Input.press("Down", 2)
elseif Row > 3 then
Input.press("Up", 2)
else
Input.press("A", 2)
end
--start menu(close)
elseif MainMenu == 121 and status.tempDir then
Input.press("B", 2)
end
end
end
--[[function Strategies.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]]
-- GENERALIZED STRATEGIES
Strategies.functions = {
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..". "..Control.areaName..": "..Utils.elapsedTime().." ("..timeDiff..")")
end
end
return true
end,
interact = function(data)
if Battle.handleWild() then
if Battle.isActive() then
return true
end
if Textbox.isActive() then
if status.tries > 0 then
return true
end
status.tries = status.tries - 1
Input.cancel()
elseif Player.interact(data.dir) then
status.tries = status.tries + 1
end
end
end,
setDirection = function(data)
if Player.isFacing(data.dir) then
return true
else
Input.press(data.dir, 2)
return true
end
end,
openTextbox = function()
if Textbox.isActive() then
return true
else
Input.press("A", 2)
end
end,
use = function(data)
local Option
if not data.option then
Option = false
else
Option = data.option
end
if Strategies.useItem(data.item, data.poke, data.close, Option) then
return true
end
end,
confirm = function(data)
if Battle.handleWild() then
if Textbox.isActive() then
status.tries = status.tries + 1
Input.cancel(data.type or "A")
else
if status.tries > 0 then
return true
end
Player.interact(data.dir)
end
end
end,
--[[teleport = function(data)
if Memory.value("game", "map") == data.map then
return true
end
if not Pokemon.use("teleport") then
Menu.pause()
end
end,]]
speak = function()
if Strategies.initialize() then
status.tempDir = false
end
if Textbox.isActive() then
Input.press("A", 2)
status.tempDir = true
else
if status.tempDir then
status.tempDir = false
return true
else
Input.press("A", 2)
end
end
end,
--[[deposeAll = function(data)
if Memory.value("player", "party_size") == 1 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 = 0
if Pokemon.indexOf(data.keep) == 0 then
depositIndex = 1
end
Menu.select(depositIndex)
else
Menu.select(1)
end
else
Input.press("A")
end
end
end
end,
swapItem = function(data)
if Strategies.initialize() then
status.tempDir = false
end
if not data.pos2 then -- 1x position mode + item name
itemPos1 = Inventory.indexOf(data.item)
itemPos2 = data.pos1-1
else -- 2x position mode
itemPos1 = data.pos1-1
itemPos2 = data.pos2-1
end
local main = Memory.value("menu", "main")
local selection = Memory.value("menu", "selection_mode")
if status.tempDir and selection == 0 then
return true
end
if main == 128 then
if Menu.getCol() ~= 5 then
Menu.select(2, true)
else
if selection == 0 then
if Menu.select(itemPos1, 1, true, nil, true) then
Input.press("Select")
end
else
if Menu.select(itemPos2, 1, true, nil, true) then
Input.press("Select")
status.tempDir = true
end
end
end
else
Menu.pause()
end
end,
tossItem = function(data)
if Strategies.initialize() then
status.canProgress = false
if not data.item then
itemPos1 = data.pos-1
status.tempDir = Memory.raw(0x131E+itemPos1*2+1)
else
status.tempDir = Inventory.count(data.item)
end
if data.amount then
itemNumber = data.amount
else
itemNumber = status.tempDir
end
end
if not data.pos then --tossing by item name
itemPos1 = Inventory.indexOf(data.item)
else --tossing by item position
itemPos1 = data.pos-1
end
local main = Memory.value("menu", "main")
if main == 60 and Memory.value("menu", "shop_current") == 20 and status.canProgress then
return true
end
if main == 128 or Menu.getCol() == 14 and main ~= 209 then
if Menu.getCol() ~= 5 and Menu.getCol() ~= 14 then
Menu.select(2, true)
else
if Memory.value("menu", "text_input") == 146 then
if Memory.value("menu", "row") == 0 then
Menu.select(1, true)
else
if Memory.value("menu", "shop_current") ~= 248 then
Input.press("A")
else
local currAmount = Memory.value("shop", "transaction_amount")
if Menu.balance(currAmount, itemNumber, false, 99, true) then
Input.press("A")
status.canProgress = true
end
end
end
else
if Menu.select(itemPos1, 1, true, nil, true) then
Input.press("A")
end
end
end
elseif main == 209 then
Input.press("A")
else
Menu.pause()
end
end,
tossTM = function(data)
if Strategies.initialize() then
status.canProgress = false
status.tries = 0
if data.amount then
itemNumber = data.amount
else
itemPos1 = data.pos-1
status.tempDir = Memory.raw(0x131E+itemPos1*2+1)
itemNumber = status.tempDir
end
end
if not data.pos then --tossing by item name
itemPos1 = Inventory.indexOf(data.item)
else --tossing by item position
itemPos1 = data.pos-1
end
local main = Memory.value("menu", "main")
if main == 60 and Memory.value("menu", "shop_current") == 20 and status.canProgress then
return true
end
if status.tries == 0 then
if main == 128 or Menu.getCol() == 14 and main ~= 209 then
if Menu.getCol() ~= 5 and Menu.getCol() ~= 14 then
Menu.select(2, true)
else
if Memory.value("menu", "text_input") == 146 then
if Memory.value("menu", "row") == 0 then
Menu.select(1, true, false, nil, true)
else
if main == 128 then
Input.press("A")
else
status.tries = 1
end
end
else
if Menu.select(itemPos1, 1, true, nil, true) then
Input.press("A")
end
end
end
end
else
if main == 128 or Menu.getCol() == 14 and main ~= 209 then
local currAmount = Memory.value("shop", "transaction_amount")
if Menu.balance(currAmount, itemNumber, false, 99, true) then
Input.press("A")
status.canProgress = true
end
else
Input.press("A")
end
end
end,]]
openMenu = function()
if Textbox.isActive() then
return true
else
Input.press("Start", 2)
end
end,
closeMenu = function()
if not Textbox.isActive() then
return true
else
Input.press("B")
end
end,
allowDeath = function(data)
Control.canDie(data.on)
return true
end,
--[[champion = function()
if status.canProgress then
if status.tries > 1500 then
return Strategies.hardReset("Beat the game in "..status.canProgress.." !")
end
if status.tries == 0 then
Bridge.tweet("Beat Pokemon "..GAME_NAME.." in "..status.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
status.tries = status.tries + 1
elseif Memory.value("menu", "shop_current") == 252 then
Strategies.functions.split({finished=true})
status.canProgress = Utils.elapsedTime()
else
Input.cancel()
end
end]]
}
strategyFunctions = Strategies.functions
function Strategies.execute(data)
if strategyFunctions[data.s](data) then
status = {tries=0}
Strategies.status = status
Strategies.completeGameStrategy()
-- print(data.s)
if resetting then
return nil
end
return true
end
return false
end
function Strategies.init(midGame)
if not STREAMING_MODE then
splitTime = Utils.timeSince(0)
end
if midGame then
Combat.factorPP(true)
end
end
function Strategies.softReset()
status = {tries=0}
Strategies.status = status
stats = {}
Strategies.stats = stats
Strategies.updates = {}
splitNumber, splitTime = 0, 0
resetting = nil
Strategies.deepRun = false
Strategies.resetGame()
end
return Strategies