Initial public commit
This commit is contained in:
commit
52232581f2
|
@ -0,0 +1,20 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Kyle Coburn and Michael Jondahl
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,32 @@
|
|||
PokéBot
|
||||
=======
|
||||
An automated computer program that speedruns Pokémon.
|
||||
|
||||
Pokémon Red Any%: [1:51:11](https://www.youtube.com/watch?v=M4pOlQ-mIoc) (23 June 2014)
|
||||
|
||||
Watch Live
|
||||
==========
|
||||
### [http://www.twitch.tv/thepokebot](http://www.twitch.tv/thepokebot)
|
||||
PokéBot's official streaming channel on Twitch. Consider following there to find out when we're streaming, or follow the [Twitter feed](https://twitter.com/thepokebot) for announcements when we get personal best pace runs going.
|
||||
|
||||
Try it out
|
||||
==========
|
||||
Running the PokéBot on your own machine is easy. You will need a Windows environment (it runs great in VM's on Mac too). First, clone this repository (or download and unzip it) to your computer. Install the [BizHawk 1.6.1](http://down.emucr.com/v3/10194002) emulator, and procure a ROM file of Pokémon Red (you should personally own the game).
|
||||
|
||||
Open the ROM file with BizHawk, and Pokémon Red should start up. Then, under the 'Tools' menu, select 'Lua Console'. Click the open folder button, and navigate to the PokéBot folder you downloaded. Select 'main.lua' and press open. The bot should start running!
|
||||
|
||||
Seeds
|
||||
=====
|
||||
PokéBot comes with a built-in run recording feature that takes advantage of random number seeding to reproduce runs in their entirety. Any time the bot resets or beats the game, it will log a number to the Lua console that is the seed for the run. If you set 'CUSTOM_SEED' in main.lua to that number, the bot will reproduce your run, allowing you to share your times with others. Note that making any other modifications will prevent this from working. So if you want to make changes to the bot and share your time, be sure to fork the repo and push your changes.
|
||||
|
||||
Credits
|
||||
=======
|
||||
### Developers
|
||||
Kyle Coburn: Original concept, Red routing
|
||||
|
||||
Michael Jondahl: Combat algorithm, Java bridge for connecting the bot to Twitch chat, Livesplit, Twitter, etc
|
||||
|
||||
### Special thanks
|
||||
To Livesplit for providing custom component for integrating in-game time splits.
|
||||
|
||||
To the Pokémon speedrunning community members who inspired the idea, and shared ways to improve the bot.
|
|
@ -0,0 +1,249 @@
|
|||
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"
|
||||
|
||||
local function potionsForHit(potion, currHP, maxHP)
|
||||
if (not potion) then
|
||||
return
|
||||
end
|
||||
local ours, killAmount = combat.inKillRange()
|
||||
if (ours) then
|
||||
local potionHP
|
||||
if (potion == "full_restore") then
|
||||
potionHP = 999
|
||||
elseif (potion == "super_potion") then
|
||||
potionHP = 50
|
||||
else
|
||||
potionHP = 20
|
||||
end
|
||||
if (not currHP) then
|
||||
currHP = pokemon.index(0, "hp")
|
||||
maxHP = pokemon.index(0, "max_hp")
|
||||
end
|
||||
return math.min(currHP + potionHP, maxHP) >= killAmount - 2
|
||||
end
|
||||
end
|
||||
battle.potionsForHit = potionsForHit
|
||||
|
||||
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 (potionIn == "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 (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 (memory.double("battle", "opponent_hp") < 1) then
|
||||
input.cancel()
|
||||
elseif (openBattleMenu()) then
|
||||
menu.select(attackIndex, true, false, false, false, 3)
|
||||
end
|
||||
end
|
||||
|
||||
-- Table functions
|
||||
|
||||
function battle.swapMove(sidx, fidx)
|
||||
if (openBattleMenu()) then
|
||||
local selection = memory.value("menu", "selection_mode")
|
||||
local swapSelect
|
||||
if (selection == sidx) then
|
||||
swapSelect = fidx
|
||||
else
|
||||
swapSelect = sidx
|
||||
end
|
||||
if (menu.select(swapSelect, false, false, nil, true, 3)) 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.run()
|
||||
else
|
||||
textbox.handle()
|
||||
end
|
||||
end
|
||||
|
||||
function battle.opponent()
|
||||
return pokemon.getName(memory.value("battle", "opponent_id"))
|
||||
end
|
||||
|
||||
function battle.run()
|
||||
if (memory.double("battle", "opponent_hp") < 1) 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)
|
||||
else
|
||||
input.escape()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function battle.handleWild()
|
||||
if (memory.value("game", "battle") ~= 1) then
|
||||
return true
|
||||
end
|
||||
battle.run()
|
||||
end
|
||||
|
||||
function battle.fight(move, isNumber, skipBuffs)
|
||||
if (move) then
|
||||
if (not isNumber) then
|
||||
move = pokemon.battleMove(move)
|
||||
end
|
||||
attack(move)
|
||||
else
|
||||
move = combat.bestMove()
|
||||
if (move) then
|
||||
attack(move.midx)
|
||||
elseif (memory.value("menu", "text_length") == 127) then
|
||||
print("Faito B!")
|
||||
input.press("B")
|
||||
else
|
||||
input.cancel()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function battle.swap(target)
|
||||
local battleMenu = memory.value("battle", "menu")
|
||||
if (utils.onPokemonSelect(battleMenu)) then
|
||||
if (menu.getCol() == 0) then
|
||||
menu.select(pokemon.indexOf(target), true)
|
||||
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 movePP(name)
|
||||
local midx = pokemon.battleMove(name)
|
||||
if (not midx) then
|
||||
return 0
|
||||
end
|
||||
return memory.raw(0xD02C + midx)
|
||||
end
|
||||
battle.pp = movePP
|
||||
|
||||
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, false, skipBuffs)
|
||||
else
|
||||
battle.run()
|
||||
end
|
||||
elseif (state == 2) then
|
||||
battle.fight(moveName, false, skipBuffs)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return battle
|
|
@ -0,0 +1,105 @@
|
|||
local shop = {}
|
||||
|
||||
local textbox = require "action.textbox"
|
||||
|
||||
local input = require "util.input"
|
||||
local memory = require "util.memory"
|
||||
local menu = require "util.menu"
|
||||
local player = require "util.player"
|
||||
|
||||
local inventory = require "storage.inventory"
|
||||
|
||||
function shop.transaction(options)
|
||||
local item, itemMenu, menuIdx, quantityMenu
|
||||
if (options.sell) then
|
||||
menuIdx = 1
|
||||
itemMenu = 29
|
||||
quantityMenu = 158
|
||||
for i,sit in ipairs(options.sell) do
|
||||
local idx = inventory.indexOf(sit.name)
|
||||
if (idx ~= -1) then
|
||||
item = sit
|
||||
item.index = idx
|
||||
item.amount = inventory.count(sit.name)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if (not item and options.buy) then
|
||||
menuIdx = 0
|
||||
itemMenu = 123
|
||||
quantityMenu = 161
|
||||
for i,bit in ipairs(options.buy) do
|
||||
local needed = (bit.amount or 1) - inventory.count(bit.name)
|
||||
if (needed > 0) then
|
||||
item = bit
|
||||
item.amount = needed
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if (not item) then
|
||||
if (not textbox.isActive()) then
|
||||
return true
|
||||
end
|
||||
input.press("B")
|
||||
elseif (player.isFacing(options.direction or "Left")) then
|
||||
if (textbox.isActive()) then
|
||||
if (menu.isCurrently(32, "shop")) then
|
||||
menu.select(menuIdx, true, false, "shop")
|
||||
elseif (menu.getCol() == 15) then
|
||||
input.press("A")
|
||||
elseif (menu.isCurrently(itemMenu, "transaction")) then
|
||||
if (menu.select(item.index, "accelerate", true, "transaction", true)) then
|
||||
if (menu.isCurrently(quantityMenu, "shop")) then
|
||||
local currAmount = memory.value("shop", "transaction_amount")
|
||||
if (menu.balance(currAmount, item.amount, false, 99, true)) then
|
||||
input.press("A")
|
||||
end
|
||||
else
|
||||
input.press("A")
|
||||
end
|
||||
end
|
||||
else
|
||||
input.press("B")
|
||||
end
|
||||
else
|
||||
input.press("A", 2)
|
||||
end
|
||||
else
|
||||
player.interact(options.direction or "Left")
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function shop.vend(options)
|
||||
local item
|
||||
menuIdx = 0
|
||||
for i,bit in ipairs(options.buy) do
|
||||
local needed = (bit.amount or 1) - inventory.count(bit.name)
|
||||
if (needed > 0) then
|
||||
item = bit
|
||||
item.buy = needed
|
||||
break
|
||||
end
|
||||
end
|
||||
if (not item) then
|
||||
if (not textbox.isActive()) then
|
||||
return true
|
||||
end
|
||||
input.press("B")
|
||||
elseif (player.face(options.direction)) then
|
||||
if (textbox.isActive()) then
|
||||
if (memory.value("battle", "text") > 1 and memory.value("battle", "menu") ~= 95) then
|
||||
menu.select(item.index, true)
|
||||
else
|
||||
input.press("A")
|
||||
end
|
||||
else
|
||||
input.press("A", 2)
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
return shop
|
|
@ -0,0 +1,77 @@
|
|||
local textbox = {}
|
||||
|
||||
local input = require "util.input"
|
||||
local memory = require "util.memory"
|
||||
local menu = require "util.menu"
|
||||
local utils = require "util.utils"
|
||||
|
||||
local alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ *():;[]ポモ-?!♂♀/.,"
|
||||
|
||||
local nidoName = "A"
|
||||
local nidoIdx = 1
|
||||
|
||||
local function getLetterAt(index)
|
||||
return alphabet[index]
|
||||
end
|
||||
|
||||
local function getIndexForLetter(letter)
|
||||
return alphabet:find(letter, 1, true)
|
||||
end
|
||||
|
||||
function textbox.name(letter, randomize)
|
||||
local inputting = memory.value("menu", "text_input") == 240
|
||||
if (inputting) then
|
||||
if (memory.value("menu", "text_length") > 0) then
|
||||
input.press("Start")
|
||||
return true
|
||||
end
|
||||
local lidx
|
||||
if (letter) then
|
||||
lidx = getIndexForLetter(letter)
|
||||
else
|
||||
lidx = nidoIdx
|
||||
end
|
||||
|
||||
local crow = memory.value("menu", "input_row")
|
||||
local drow = math.ceil(lidx / 9)
|
||||
if (menu.balance(crow, drow, true, 6, true)) then
|
||||
local ccol = math.floor(memory.value("menu", "column") / 2)
|
||||
local dcol = math.fmod(lidx - 1, 9)
|
||||
if (menu.sidle(ccol, dcol, 9, true)) then
|
||||
input.press("A")
|
||||
end
|
||||
end
|
||||
else
|
||||
-- TODO cancel more when menu isn't up
|
||||
if (memory.raw(0x10B7) == 3) then
|
||||
input.press("A", 2)
|
||||
elseif (randomize) then
|
||||
input.press("A", math.random(1, 5))
|
||||
else
|
||||
input.cancel()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function textbox.getName()
|
||||
return nidoName
|
||||
end
|
||||
|
||||
function textbox.setName(index)
|
||||
nidoIdx = index + 1
|
||||
nidoName = getLetterAt(index)
|
||||
end
|
||||
|
||||
function textbox.isActive()
|
||||
return memory.value("game", "textbox") == 1
|
||||
end
|
||||
|
||||
function textbox.handle()
|
||||
if (not textbox.isActive()) then
|
||||
return true
|
||||
end
|
||||
input.cancel()
|
||||
end
|
||||
|
||||
|
||||
return textbox
|
|
@ -0,0 +1,160 @@
|
|||
local walk = {}
|
||||
|
||||
local control = require "ai.control"
|
||||
local paths = require "data.paths"
|
||||
|
||||
local input = require "util.input"
|
||||
local memory = require "util.memory"
|
||||
local player = require "util.player"
|
||||
local utils = require "util.utils"
|
||||
|
||||
local pokemon = require "storage.pokemon"
|
||||
|
||||
local path, stepIdx, currentMap
|
||||
local pathIdx = 0
|
||||
local customIdx = 1
|
||||
local customDir = 1
|
||||
|
||||
-- Private functions
|
||||
|
||||
local function setPath(index, region)
|
||||
pathIdx = index
|
||||
stepIdx = 2
|
||||
currentMap = region
|
||||
path = paths[index]
|
||||
end
|
||||
|
||||
-- Helper functions
|
||||
|
||||
function dir(px, py, dx, dy)
|
||||
local direction
|
||||
if (py > dy) then
|
||||
direction = "Up"
|
||||
elseif (py < dy) then
|
||||
direction = "Down"
|
||||
elseif (px > dx) then
|
||||
direction = "Left"
|
||||
else
|
||||
direction = "Right"
|
||||
end
|
||||
return direction
|
||||
end
|
||||
walk.dir = dir
|
||||
|
||||
function step(dx, dy)
|
||||
local px, py = player.position()
|
||||
if (px == dx and py == dy) then
|
||||
return true
|
||||
end
|
||||
input.press(dir(px, py, dx, dy), 0)
|
||||
end
|
||||
walk.step = step
|
||||
|
||||
local function completeStep(region)
|
||||
stepIdx = stepIdx + 1
|
||||
return walk.traverse(region)
|
||||
end
|
||||
|
||||
-- Table functions
|
||||
|
||||
function walk.reset()
|
||||
path = nil
|
||||
pathIdx = 0
|
||||
customIdx = 1
|
||||
customDir = 1
|
||||
currentMap = nil
|
||||
walk.strategy = nil
|
||||
end
|
||||
|
||||
function walk.init()
|
||||
local region = memory.value("game", "map")
|
||||
local px, py = player.position()
|
||||
if (region == 0 and px == 0 and py == 0) then
|
||||
return false
|
||||
end
|
||||
for tries=1,2 do
|
||||
for i,p in ipairs(paths) do
|
||||
if (i > 2 and p[1] == region) then
|
||||
local origin = p[2]
|
||||
if (tries == 2 or (origin[1] == px and origin[2] == py)) then
|
||||
setPath(i, region)
|
||||
return tries == 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function walk.traverse(region)
|
||||
local newIndex
|
||||
if (not path or currentMap ~= region) then
|
||||
walk.strategy = nil
|
||||
setPath(pathIdx + 1, region)
|
||||
newIndex = pathIdx
|
||||
customIdx = 1
|
||||
customDir = 1
|
||||
elseif stepIdx > #path then
|
||||
return
|
||||
end
|
||||
local tile = path[stepIdx]
|
||||
if (tile.c) then
|
||||
control.set(tile)
|
||||
return completeStep(region)
|
||||
end
|
||||
if (tile.s) then
|
||||
if (walk.strategy) then
|
||||
walk.strategy = nil
|
||||
return completeStep(region)
|
||||
end
|
||||
walk.strategy = tile
|
||||
elseif step(tile[1], tile[2]) then
|
||||
pokemon.updateParty()
|
||||
return completeStep(region)
|
||||
end
|
||||
return newIndex
|
||||
end
|
||||
|
||||
function walk.canMove()
|
||||
return memory.value("player", "moving") == 0 and memory.value("player", "fighting") == 0
|
||||
end
|
||||
|
||||
-- Custom path
|
||||
|
||||
function walk.invertCustom(silent)
|
||||
if (not silent) then
|
||||
customIdx = customIdx + customDir
|
||||
end
|
||||
customDir = customDir * -1
|
||||
end
|
||||
|
||||
function walk.custom(cpath, increment)
|
||||
if (not cpath) then
|
||||
customIdx = 1
|
||||
customDir = 1
|
||||
return
|
||||
end
|
||||
if (increment) then
|
||||
customIdx = customIdx + customDir
|
||||
end
|
||||
local tile = cpath[customIdx]
|
||||
if (not tile) then
|
||||
if (customIdx < 1) then
|
||||
customIdx = #cpath
|
||||
else
|
||||
customIdx = 1
|
||||
end
|
||||
return customIdx
|
||||
end
|
||||
local t1, t2 = tile[1], tile[2]
|
||||
if (t2 == nil) then
|
||||
if (player.face(t1)) then
|
||||
input.press("A", 2)
|
||||
end
|
||||
return t1
|
||||
end
|
||||
if (step(t1, t2)) then
|
||||
customIdx = customIdx + customDir
|
||||
end
|
||||
end
|
||||
|
||||
return walk
|
|
@ -0,0 +1,386 @@
|
|||
local combat = {}
|
||||
|
||||
local movelist = require "data.movelist"
|
||||
local opponents = require "data.opponents"
|
||||
|
||||
local memory = require "util.memory"
|
||||
local utils = require "util.utils"
|
||||
|
||||
local damageMultiplier = { -- http://bulbapedia.bulbagarden.net/wiki/Type_chart#Generation_I
|
||||
normal = {normal=1.0, fighting=1.0, flying=1.0, poison=1.0, ground=1.0, rock=0.5, bug=1.0, ghost=0.0, fire=1.0, water=1.0, grass=1.0, electric=1.0, psychic=1.0, ice=1.0, dragon=1.0, },
|
||||
fighting = {normal=2.0, fighting=1.0, flying=0.5, poison=0.5, ground=1.0, rock=2.0, bug=0.5, ghost=0.0, fire=1.0, water=1.0, grass=1.0, electric=1.0, psychic=0.5, ice=2.0, dragon=1.0, },
|
||||
flying = {normal=1.0, fighting=2.0, flying=1.0, poison=1.0, ground=1.0, rock=0.5, bug=2.0, ghost=1.0, fire=1.0, water=1.0, grass=2.0, electric=0.5, psychic=1.0, ice=1.0, dragon=1.0, },
|
||||
poison = {normal=1.0, fighting=1.0, flying=1.0, poison=0.5, ground=0.5, rock=0.5, bug=2.0, ghost=0.5, fire=1.0, water=1.0, grass=2.0, electric=1.0, psychic=1.0, ice=1.0, dragon=1.0, },
|
||||
ground = {normal=1.0, fighting=1.0, flying=0.0, poison=2.0, ground=1.0, rock=2.0, bug=0.5, ghost=1.0, fire=2.0, water=1.0, grass=0.5, electric=2.0, psychic=1.0, ice=1.0, dragon=1.0, },
|
||||
rock = {normal=1.0, fighting=0.5, flying=2.0, poison=1.0, ground=0.5, rock=1.0, bug=2.0, ghost=1.0, fire=2.0, water=1.0, grass=1.0, electric=1.0, psychic=1.0, ice=2.0, dragon=1.0, },
|
||||
bug = {normal=1.0, fighting=0.5, flying=0.5, poison=2.0, ground=1.0, rock=1.0, bug=1.0, ghost=0.5, fire=0.5, water=1.0, grass=2.0, electric=1.0, psychic=2.0, ice=1.0, dragon=1.0, },
|
||||
ghost = {normal=0.0, fighting=1.0, flying=1.0, poison=1.0, ground=1.0, rock=1.0, bug=1.0, ghost=2.0, fire=1.0, water=1.0, grass=1.0, electric=1.0, psychic=0.0, ice=1.0, dragon=1.0, },
|
||||
fire = {normal=1.0, fighting=1.0, flying=1.0, poison=1.0, ground=1.0, rock=0.5, bug=2.0, ghost=1.0, fire=0.5, water=0.5, grass=2.0, electric=1.0, psychic=1.0, ice=2.0, dragon=0.5, },
|
||||
water = {normal=1.0, fighting=1.0, flying=1.0, poison=1.0, ground=2.0, rock=2.0, bug=1.0, ghost=1.0, fire=2.0, water=0.5, grass=0.5, electric=1.0, psychic=1.0, ice=1.0, dragon=0.5, },
|
||||
grass = {normal=1.0, fighting=1.0, flying=0.5, poison=0.5, ground=2.0, rock=2.0, bug=0.5, ghost=1.0, fire=0.5, water=2.0, grass=0.5, electric=1.0, psychic=1.0, ice=1.0, dragon=0.5, },
|
||||
electric = {normal=1.0, fighting=1.0, flying=2.0, poison=1.0, ground=0.0, rock=1.0, bug=1.0, ghost=1.0, fire=1.0, water=2.0, grass=0.5, electric=0.5, psychic=1.0, ice=1.0, dragon=0.5, },
|
||||
psychic = {normal=1.0, fighting=2.0, flying=1.0, poison=2.0, ground=1.0, rock=1.0, bug=1.0, ghost=1.0, fire=1.0, water=1.0, grass=1.0, electric=1.0, psychic=0.5, ice=1.0, dragon=1.0, },
|
||||
ice = {normal=1.0, fighting=1.0, flying=2.0, poison=1.0, ground=2.0, rock=1.0, bug=1.0, ghost=1.0, fire=1.0, water=0.5, grass=2.0, electric=1.0, psychic=1.0, ice=0.5, dragon=2.0, },
|
||||
dragon = {normal=1.0, fighting=1.0, flying=1.0, poison=1.0, ground=1.0, rock=1.0, bug=1.0, ghost=1.0, fire=1.0, water=1.0, grass=1.0, electric=1.0, psychic=1.0, ice=1.0, dragon=2.0, },
|
||||
}
|
||||
|
||||
local types = {}
|
||||
types[0] = "normal"
|
||||
types[1] = "fighting"
|
||||
types[2] = "flying"
|
||||
types[3] = "poison"
|
||||
types[4] = "ground"
|
||||
types[5] = "rock"
|
||||
types[7] = "bug"
|
||||
types[8] = "ghost"
|
||||
types[20] = "fire"
|
||||
types[21] = "water"
|
||||
types[22] = "grass"
|
||||
types[23] = "electric"
|
||||
types[24] = "psychic"
|
||||
types[25] = "ice"
|
||||
types[26] = "dragon"
|
||||
|
||||
local savedEncounters = {}
|
||||
local enablePP = false
|
||||
|
||||
local floor = math.floor
|
||||
|
||||
local function isDisabled(mid)
|
||||
return mid == memory.value("battle", "disabled")
|
||||
end
|
||||
combat.isDisabled = isDisabled
|
||||
|
||||
local function calcDamage(move, attacker, defender, rng)
|
||||
if (move.fixed) then
|
||||
return move.fixed, move.fixed
|
||||
end
|
||||
if (move.power == 0 or isDisabled(move.id)) then
|
||||
return 0, 0
|
||||
end
|
||||
if (move.power > 9000) then
|
||||
local oid = defender.id
|
||||
if (oid ~= 14 and oid ~= 147 and oid ~= 171 and (oidd ~= 151 or memory.value("game", "map") == 120)) then -- ???
|
||||
if (memory.value("battle", "x_accuracy") == 1 and defender.speed < attacker.speed) then
|
||||
return 9001, 9001
|
||||
end
|
||||
end
|
||||
return 0, 0
|
||||
end
|
||||
if (move.name == "Thrash" and combat.disableThrash) then
|
||||
return 0, 0
|
||||
end
|
||||
|
||||
local attFactor, defFactor
|
||||
if move.special then
|
||||
attFactor, defFactor = attacker.spec, defender.spec
|
||||
else
|
||||
attFactor, defFactor = attacker.att, defender.def
|
||||
end
|
||||
local damage = floor(floor(floor(2 * attacker.level / 5 + 2) * math.max(1, attFactor) * move.power / math.max(1, defFactor)) / 50) + 2
|
||||
|
||||
if (move.move_type == attacker.type1 or move.move_type == attacker.type2) then
|
||||
damage = floor(damage * 1.5) -- STAB
|
||||
end
|
||||
|
||||
local dmp = damageMultiplier[move.move_type]
|
||||
local typeEffect1, typeEffect2 = dmp[defender.type1], dmp[defender.type2]
|
||||
if (defender.type1 == defender.type2) then
|
||||
typeEffect2 = 1
|
||||
end
|
||||
damage = floor(damage * typeEffect1 * typeEffect2)
|
||||
if (rng) then
|
||||
return damage, damage
|
||||
end
|
||||
return floor(damage * 217 / 255), damage
|
||||
end
|
||||
|
||||
local function getOpponentType(ty)
|
||||
local t1= types[memory.value("battle", "opponent_type1")]
|
||||
if ty~=0 then
|
||||
t1 = types[memory.value("battle", "opponent_type2")]
|
||||
if not t1 then
|
||||
return memory.value("battle", "opponent_type2")
|
||||
end
|
||||
end
|
||||
if t1 then
|
||||
return t1
|
||||
end
|
||||
return memory.value("battle", "opponent_type1")
|
||||
end
|
||||
combat.getOpponentType = getOpponentType
|
||||
|
||||
function getOurType(ty)
|
||||
local t1 = types[memory.value("battle", "our_type1")]
|
||||
if (ty ~= 0) then
|
||||
t1 = types[memory.value("battle", "our_type2")]
|
||||
if (not t1) then
|
||||
return memory.value("battle", "opponent_type2")
|
||||
end
|
||||
end
|
||||
if t1 then
|
||||
return t1
|
||||
end
|
||||
return memory.value("battle", "opponent_type1")
|
||||
end
|
||||
combat.getOurType = getOurType
|
||||
|
||||
local function getMoves(who)--Get the moveset of us [0] or them [1]
|
||||
local moves = {}
|
||||
local base
|
||||
if (who == 1) then
|
||||
base = 0xCFED
|
||||
else
|
||||
base = 0xD01C
|
||||
end
|
||||
for idx=0, 3 do
|
||||
local val = memory.raw(base + idx)
|
||||
if (val > 0) then
|
||||
local moveTable = movelist.get(val)
|
||||
if (who == 0) then
|
||||
moveTable.pp = memory.raw(0xD02D + idx)
|
||||
end
|
||||
moves[idx + 1] = moveTable
|
||||
end
|
||||
end
|
||||
return moves
|
||||
end
|
||||
combat.getMoves = getMoves
|
||||
|
||||
local function modPlayerStats(user, enemy, move)
|
||||
local effect = move.effects
|
||||
if (effect) then
|
||||
local diff = effect.diff
|
||||
local hitThem = diff < 0
|
||||
local stat = effect.stat
|
||||
if (hitThem) then
|
||||
enemy[stat] = math.max(2, enemy[stat] + diff)
|
||||
else
|
||||
user[stat] = user[stat] + diff
|
||||
end
|
||||
end
|
||||
return user, enemy
|
||||
end
|
||||
|
||||
local function calcBestHit(attacker, defender, ours, rng)
|
||||
local bestTurns, bestMinTurns = 9999, 9999
|
||||
local bestDmg = -1
|
||||
local ourMaxHit
|
||||
local ret = nil
|
||||
for idx,move in ipairs(attacker.moves) do
|
||||
if (not move.pp or move.pp > 0) then
|
||||
local minDmg, maxDmg = calcDamage(move, attacker, defender, rng)
|
||||
if (maxDmg) then
|
||||
local minTurns, maxTurns
|
||||
if (maxDmg <= 0) then
|
||||
minTurns, maxTurns = 9999, 9999
|
||||
else
|
||||
minTurns = math.ceil(defender.hp / maxDmg)
|
||||
maxTurns = math.ceil(defender.hp / minDmg)
|
||||
end
|
||||
if (ours) then
|
||||
local replaces
|
||||
if (not ret or minTurns < bestMinTurns or maxTurns < bestTurns) then
|
||||
replaces = true
|
||||
elseif (maxTurns == bestTurns and move.name == "Thrash") then
|
||||
replaces = defender.hp == memory.double("battle", "opponent_max_hp")
|
||||
elseif (maxTurns == bestTurns and ret.name == "Thrash") then
|
||||
replaces = defender.hp ~= memory.double("battle", "opponent_max_hp")
|
||||
elseif (move.fast and not ret.fast) then
|
||||
replaces = maxTurns <= bestTurns
|
||||
elseif (ret.fast) then
|
||||
replaces = maxTurns < bestTurns
|
||||
elseif (enablePP) then
|
||||
if (maxTurns < 2 or maxTurns == bestMaxTurns) then
|
||||
if (ret.name == "Earthquake" and (move.name == "Ice-Beam" or move.name == "Thunderbolt")) then
|
||||
replaces = true
|
||||
elseif (move.pp > ret.pp) then
|
||||
if (ret.name == "Horn-Drill") then
|
||||
replaces = true
|
||||
elseif (move.name ~= "Earthquake") then
|
||||
replaces = true
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif (minDmg > bestDmg) then
|
||||
replaces = true
|
||||
end
|
||||
if (replaces) then
|
||||
ret = move
|
||||
bestMinTurns = minTurns
|
||||
bestTurns = maxTurns
|
||||
bestDmg = minDmg
|
||||
ourMaxHit = maxDmg
|
||||
end
|
||||
elseif (maxDmg > bestDmg) then -- Opponents automatically hit max
|
||||
ret = move
|
||||
bestTurns = minTurns
|
||||
bestDmg = maxDmg
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if (ret) then
|
||||
ret.damage = bestDmg
|
||||
ret.maxDamage = ourMaxHit
|
||||
ret.minTurns = bestMinTurns
|
||||
return ret, bestTurns
|
||||
end
|
||||
end
|
||||
|
||||
local function getBestMove(ours, enemy, draw)
|
||||
if (enemy.hp < 1) then
|
||||
return
|
||||
end
|
||||
local bm, bestUs = calcBestHit(ours, enemy, true)
|
||||
local jj, bestEnemy = calcBestHit(enemy, ours, false)
|
||||
if (not bm) then
|
||||
return
|
||||
end
|
||||
if draw and bm.midx then
|
||||
gui.text(0, 35, ''..bm.midx.." "..bm.name)
|
||||
end
|
||||
return bm, bestUs, bestEnemy
|
||||
end
|
||||
|
||||
local function activePokemon(preset)
|
||||
local ours = {
|
||||
id = memory.value("battle", "our_id"),
|
||||
level = memory.value("battle", "our_level"),
|
||||
hp = memory.double("battle", "our_hp"),
|
||||
att = memory.double("battle", "our_attack"),
|
||||
def = memory.double("battle", "our_defense"),
|
||||
spec = memory.double("battle", "our_special"),
|
||||
speed = memory.double("battle", "our_speed"),
|
||||
type1 = getOurType(0),
|
||||
type2 = getOurType(1),
|
||||
moves = getMoves(0),
|
||||
}
|
||||
|
||||
local enemy
|
||||
if (preset) then
|
||||
enemy = opponents[preset]
|
||||
local toBoost = enemy.boost
|
||||
if (toBoost) then
|
||||
local currSpec = ours.spec
|
||||
local booster = toBoost.mp
|
||||
if ((currSpec < 140) == (booster > 1)) then
|
||||
ours.spec = math.floor(currSpec * booster)
|
||||
end
|
||||
end
|
||||
else
|
||||
enemy = {
|
||||
id = memory.value("battle", "opponent_id"),
|
||||
level = memory.value("battle", "opponent_level"),
|
||||
hp = memory.double("battle", "opponent_hp"),
|
||||
att = memory.double("battle", "opponent_attack"),
|
||||
def = memory.double("battle", "opponent_defense"),
|
||||
spec = memory.double("battle", "opponent_special"),
|
||||
speed = memory.double("battle", "opponent_speed"),
|
||||
type1 = getOpponentType(0),
|
||||
type2 = getOpponentType(1),
|
||||
moves = getMoves(1),
|
||||
}
|
||||
end
|
||||
return ours, enemy
|
||||
end
|
||||
combat.activePokemon = activePokemon
|
||||
|
||||
local function isSleeping()
|
||||
return memory.raw(0xD16F) > 1
|
||||
end
|
||||
combat.isSleeping = isSleeping
|
||||
|
||||
-- Combat AI functions
|
||||
|
||||
function combat.factorPP(enabled)
|
||||
enablePP = enabled
|
||||
end
|
||||
|
||||
function combat.reset()
|
||||
enablePP = false
|
||||
end
|
||||
|
||||
function combat.healthFor(opponent)
|
||||
local ours, enemy = activePokemon(opponent)
|
||||
local enemyAttack, turnsToDie = calcBestHit(enemy, ours, false)
|
||||
return enemyAttack.damage
|
||||
end
|
||||
|
||||
function combat.inKillRange(draw)
|
||||
local ours, enemy = activePokemon()
|
||||
local enemyAttack, __ = calcBestHit(enemy, ours, false)
|
||||
local __, turnsToKill = calcBestHit(ours, enemy, true)
|
||||
if (not turnsToKill or not enemyAttack) then
|
||||
return false
|
||||
end
|
||||
if (draw) then
|
||||
gui.text(0, 21, ours.speed.." "..enemy.speed)
|
||||
gui.text(0, 28, turnsToDie.." "..ours.hp.." | "..turnsToKill.." "..enemy.hp)
|
||||
end
|
||||
local hpReq = enemyAttack.damage
|
||||
local isConfused = memory.value("battle", "confused") > 0
|
||||
if (isConfused) then
|
||||
hpReq = hpReq + math.floor(ours.hp * 0.2)
|
||||
end
|
||||
if (ours.hp < hpReq) then
|
||||
local outspeed = enemyAttack.outspeed
|
||||
if (outspeed and outspeed ~= true) then
|
||||
outspeed = memory.value("battle", "turns") > 0
|
||||
end
|
||||
if (outspeed or isConfused or turnsToKill > 1 or ours.speed <= enemy.speed or isSleeping()) then
|
||||
return ours, hpReq
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function getBattlePokemon()
|
||||
local ours, enemy = activePokemon()
|
||||
if (enemy.hp == 0) then
|
||||
return
|
||||
end
|
||||
for idx=1,4 do
|
||||
local move = ours.moves[idx]
|
||||
if (move) then
|
||||
move.midx = idx
|
||||
end
|
||||
end
|
||||
return ours, enemy
|
||||
end
|
||||
|
||||
function combat.nonKill()
|
||||
local ours, enemy = getBattlePokemon()
|
||||
if (not enemy) then
|
||||
return
|
||||
end
|
||||
local bestDmg = -1
|
||||
local ret = nil
|
||||
for idx,move in ipairs(ours.moves) do
|
||||
if (not move.pp or move.pp > 0) then
|
||||
local __, maxDmg = calcDamage(move, ours, enemy, true)
|
||||
local threshold = maxDmg * 0.95
|
||||
if (threshold and threshold < enemy.hp and threshold > bestDmg) then
|
||||
ret = move
|
||||
bestDmg = threshold
|
||||
end
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
function combat.bestMove()
|
||||
local ours, enemy = getBattlePokemon()
|
||||
if (enemy) then
|
||||
return getBestMove(ours, enemy)
|
||||
end
|
||||
end
|
||||
|
||||
function combat.enemyAttack()
|
||||
local ours, enemy = activePokemon()
|
||||
if (enemy.hp == 0) then
|
||||
return
|
||||
end
|
||||
return calcBestHit(enemy, ours, false)
|
||||
end
|
||||
|
||||
return combat
|
|
@ -0,0 +1,247 @@
|
|||
local control = {}
|
||||
|
||||
local combat = require "ai.combat"
|
||||
|
||||
local bridge = require "util.bridge"
|
||||
local memory = require "util.memory"
|
||||
local menu = require "util.menu"
|
||||
local paint = require "util.paint"
|
||||
local player = require "util.player"
|
||||
local utils = require "util.utils"
|
||||
|
||||
local inventory = require "storage.inventory"
|
||||
local pokemon = require "storage.pokemon"
|
||||
|
||||
local game_controls
|
||||
|
||||
local shouldFight, minExp, skipHiker
|
||||
local shouldCatch, attackIdx
|
||||
local encounters = 0, extraEncounter
|
||||
local potionIn = true
|
||||
local fightEncounter, caveFights = 0, 0
|
||||
local maxEncounters
|
||||
local isYolo, battleYolo
|
||||
|
||||
local function battlePotion(enable)
|
||||
potionIn = enable
|
||||
end
|
||||
control.battlePotion = battlePotion
|
||||
|
||||
local controlFunctions = {
|
||||
potion = function(data)
|
||||
if (data.b ~= nil) then
|
||||
battlePotion(data.b)
|
||||
end
|
||||
battleYolo = data.yolo
|
||||
end,
|
||||
|
||||
encounters = function(data)
|
||||
if (RESET_FOR_TIME) then
|
||||
maxEncounters = data.limit
|
||||
extraEncounter = data.extra
|
||||
end
|
||||
end,
|
||||
|
||||
pp = function(data)
|
||||
combat.factorPP(data.on)
|
||||
end,
|
||||
|
||||
setThrash = function(data)
|
||||
combat.disableThrash = data.disable
|
||||
end,
|
||||
|
||||
disableCatch = function()
|
||||
shouldCatch = nil
|
||||
shouldFight = nil
|
||||
end,
|
||||
|
||||
-- RED
|
||||
|
||||
viridianExp = function()
|
||||
minExp = 210
|
||||
shouldFight = {{name="rattata",lvl={2,3}}, {name="pidgey",lvl={2}}}
|
||||
end,
|
||||
|
||||
viridianBackupExp = function()
|
||||
minExp = 210
|
||||
shouldFight = {{name="rattata",lvl={2,3}}, {name="pidgey",lvl={2,3}}}
|
||||
end,
|
||||
|
||||
nidoranBackupExp = function()
|
||||
minExp = 210
|
||||
shouldFight = {{name="rattata"}, {name="pidgey"}, {name="nidoran"}, {name="nidoranf",lvl={2}}}
|
||||
end,
|
||||
|
||||
moon1Exp = function()
|
||||
if (skipHiker) then
|
||||
minExp = 2704
|
||||
shouldFight = {{name="zubat",lvl={9,10}}, {name="geodude"}}
|
||||
oneHits = true
|
||||
end
|
||||
end,
|
||||
|
||||
moon2Exp = function()
|
||||
if (skipHiker) then
|
||||
minExp = 3011
|
||||
shouldFight = {{name="zubat"}, {name="geodude"}, {name="paras"}}
|
||||
end
|
||||
end,
|
||||
|
||||
moon3Exp = function()
|
||||
if (skipHiker) then
|
||||
minExp = 3798
|
||||
end
|
||||
end,
|
||||
|
||||
catchNidoran = function()
|
||||
shouldCatch = {{name="nidoran",lvl={3,4}}, {name="spearow"}}
|
||||
end,
|
||||
|
||||
catchFlier = function()
|
||||
shouldCatch = {{name="spearow",alt="pidgey",hp=15}, {name="pidgey",alt="spearow",hp=15}}
|
||||
end,
|
||||
|
||||
catchParas = function()
|
||||
shouldCatch = {{name="paras",hp=16}}
|
||||
end,
|
||||
|
||||
catchOddish = function()
|
||||
shouldCatch = {{name="oddish",alt="paras",hp=26}}
|
||||
end,
|
||||
|
||||
}
|
||||
|
||||
-- Combat
|
||||
|
||||
local function isNewFight()
|
||||
if (fightEncounter < encounters and memory.double("battle", "opponent_hp") == memory.double("battle", "opponent_max_hp")) then
|
||||
fightEncounter = encounters
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function control.shouldFight()
|
||||
if (not shouldFight) then
|
||||
return false
|
||||
end
|
||||
local expTotal = pokemon.getExp()
|
||||
if (expTotal < minExp) then
|
||||
local oid = memory.value("battle", "opponent_id")
|
||||
local olvl = memory.value("battle", "opponent_level")
|
||||
for i,p in ipairs(shouldFight) do
|
||||
if (oid == pokemon.getID(p.name) and (not p.lvl or utils.match(olvl, p.lvl))) then
|
||||
if (oneHits) then
|
||||
local move = combat.bestMove()
|
||||
if (move and move.maxDamage * 0.925 < memory.double("battle", "opponent_hp")) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function control.canCatch(partySize)
|
||||
if (not partySize) then
|
||||
partySize = memory.value("player", "party_size")
|
||||
end
|
||||
local pokeballs = inventory.count("pokeball")
|
||||
local minimumCount = 4 - partySize
|
||||
if (pokeballs < minimumCount) then
|
||||
require("ai.strategies").reset("Not enough PokeBalls", pokeballs)
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function control.shouldCatch(partySize)
|
||||
if (maxEncounters and encounters > maxEncounters) then
|
||||
local extraCount = extraEncounter and pokemon.inParty(extraEncounter)
|
||||
if (not extraCount or encounters > maxEncounters + 1) then
|
||||
require("ai.strategies").reset("Too many encounters", encounters)
|
||||
return false
|
||||
end
|
||||
end
|
||||
if (not shouldCatch) then
|
||||
return false
|
||||
end
|
||||
if (not partySize) then
|
||||
partySize = memory.value("player", "party_size")
|
||||
end
|
||||
if (partySize == 4) then
|
||||
shouldCatch = nil
|
||||
return false
|
||||
end
|
||||
if (not control.canCatch(partySize)) then
|
||||
return true
|
||||
end
|
||||
local oid = memory.value("battle", "opponent_id")
|
||||
for i,poke in ipairs(shouldCatch) do
|
||||
if (oid == pokemon.getID(poke.name) and not pokemon.inParty(poke.name, poke.alt)) then
|
||||
if (not poke.lvl or utils.match(memory.value("battle", "opponent_level"), poke.lvl)) then
|
||||
local penultimate = poke.hp and memory.double("battle", "opponent_hp") > poke.hp
|
||||
if (penultimate) then
|
||||
penultimate = combat.nonKill()
|
||||
end
|
||||
if (penultimate) then
|
||||
require("action.battle").fight(penultimate.midx, true)
|
||||
else
|
||||
inventory.use("pokeball", nil, true)
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Items
|
||||
|
||||
function control.canRecover()
|
||||
return potionIn and (not battleYolo or not isYolo)
|
||||
end
|
||||
|
||||
function control.set(data)
|
||||
controlFunctions[data.c](data)
|
||||
end
|
||||
|
||||
function control.setYolo(enabled)
|
||||
isYolo = enabled
|
||||
end
|
||||
|
||||
function control.setPotion(enabled)
|
||||
potionIn = enabled
|
||||
end
|
||||
|
||||
function control.encounters()
|
||||
return encounters
|
||||
end
|
||||
|
||||
function control.mtMoonExp()
|
||||
print("Skipping Hiker strats")
|
||||
skipHiker = true
|
||||
end
|
||||
|
||||
function control.wildEncounter()
|
||||
encounters = encounters + 1
|
||||
paint.wildEncounters(encounters)
|
||||
bridge.encounter()
|
||||
end
|
||||
|
||||
function control.reset()
|
||||
oneHits = false
|
||||
shouldCatch = nil
|
||||
shouldFight = nil
|
||||
extraEncounter = nil
|
||||
skipHiker = false
|
||||
potionIn = true
|
||||
encounters = 0
|
||||
fightEncounter = 0
|
||||
caveFights = 0
|
||||
battleYolo = false
|
||||
isYolo = false
|
||||
maxEncounters = nil
|
||||
end
|
||||
|
||||
return control
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,175 @@
|
|||
local opponents = {
|
||||
|
||||
KogaHypno = {
|
||||
type1 = "psychic",
|
||||
type2 = "psychic",
|
||||
def = 58,
|
||||
id = 129,
|
||||
spec = 88,
|
||||
hp = 107,
|
||||
speed = 56,
|
||||
level = 34,
|
||||
att = 60,
|
||||
moves = {
|
||||
{
|
||||
accuracy = 100,
|
||||
name = "Confusion",
|
||||
power = 50,
|
||||
id = 93,
|
||||
special = true,
|
||||
max_pp = 25,
|
||||
move_type = "psychic",
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
KogaWeezing = {
|
||||
type1 = "poison",
|
||||
type2 = "poison",
|
||||
def = 115,
|
||||
id = 143,
|
||||
spec = 84,
|
||||
hp = 115,
|
||||
speed = 63,
|
||||
level = 43,
|
||||
att = 90,
|
||||
moves = {
|
||||
{
|
||||
accuracy = 100,
|
||||
name = "Self-Destruct",
|
||||
power = 260,
|
||||
id = 120,
|
||||
special = false,
|
||||
max_pp = 5,
|
||||
move_type = "normal",
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
GiovanniRhyhorn = {
|
||||
type1 = "ground",
|
||||
type2 = "rock",
|
||||
def = 97,
|
||||
id = 18,
|
||||
spec = 39,
|
||||
hp = 134,
|
||||
speed = 34,
|
||||
level = 45,
|
||||
att = 89,
|
||||
moves = {
|
||||
{
|
||||
move_type = "normal",
|
||||
accuracy = 100,
|
||||
name = "Stomp",
|
||||
power = 65,
|
||||
id = 23,
|
||||
special = false,
|
||||
max_pp = 20,
|
||||
damage = 21,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
LoreleiDewgong = {
|
||||
type1 = "water",
|
||||
type2 = "ice",
|
||||
def = 100,
|
||||
id = 120,
|
||||
spec = 116,
|
||||
hp = 169,
|
||||
speed = 89,
|
||||
level = 54,
|
||||
att = 90,
|
||||
moves = {
|
||||
{
|
||||
accuracy = 100,
|
||||
name = "Aurora-Beam",
|
||||
power = 65,
|
||||
id = 62,
|
||||
special = true,
|
||||
max_pp = 20,
|
||||
move_type = "ice",
|
||||
}
|
||||
},
|
||||
boost = {
|
||||
stat = "spec",
|
||||
mp = 2 / 3
|
||||
}
|
||||
},
|
||||
|
||||
LanceGyarados = {
|
||||
type1 = "water",
|
||||
type2 = "flying",
|
||||
def = 105,
|
||||
id = 22,
|
||||
spec = 130,
|
||||
hp = 187,
|
||||
speed = 108,
|
||||
level = 58,
|
||||
att = 160,
|
||||
moves = {
|
||||
{
|
||||
accuracy = 80,
|
||||
name = "Hydro-Pump",
|
||||
power = 120,
|
||||
id = 56,
|
||||
special = true,
|
||||
max_pp = 5,
|
||||
move_type = "water",
|
||||
}
|
||||
},
|
||||
boost = {
|
||||
stat = "spec",
|
||||
mp = 1.5
|
||||
}
|
||||
},
|
||||
|
||||
BluePidgeot = {
|
||||
type1 = "normal",
|
||||
type2 = "flying",
|
||||
def = 106,
|
||||
id = 151,
|
||||
spec = 100,
|
||||
hp = 182,
|
||||
speed = 125,
|
||||
level = 61,
|
||||
att = 113,
|
||||
moves = {
|
||||
{
|
||||
accuracy = 100,
|
||||
name = "Wing-Attack",
|
||||
power = 35,
|
||||
id = 17,
|
||||
special = false,
|
||||
max_pp = 35,
|
||||
move_type = "flying",
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
BlueSky = {
|
||||
type1 = "normal",
|
||||
type2 = "flying",
|
||||
def = 106,
|
||||
id = 151,
|
||||
spec = 100,
|
||||
hp = 182,
|
||||
speed = 125,
|
||||
level = 61,
|
||||
att = 113,
|
||||
moves = {
|
||||
{
|
||||
accuracy = 90,
|
||||
name = "Sky-Attack",
|
||||
power = 140,
|
||||
id = 143,
|
||||
special = false,
|
||||
max_pp = 5,
|
||||
move_type = "flying",
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
return opponents
|
|
@ -0,0 +1,454 @@
|
|||
local paths = {
|
||||
-- Red's room
|
||||
{38, {3,6}, {5,6}, {5,1}, {7,1}},
|
||||
-- Red's house
|
||||
{39, {7,1}, {7,6}, {3,6}, {3,8}},
|
||||
-- Into the Wild
|
||||
{0, {5,6}, {10,6}, {10,1}},
|
||||
-- Choose your character!
|
||||
{40, {5,3}, {s="a",a="Pallet Rival"}, {5,4}, {7,4}, {s="squirtleIChooseYou"}, {5,4}, {5,6}, {s="fightBulbasaur"}, {s="split"}, {5,12}},
|
||||
|
||||
-- 1: RIVAL 1
|
||||
|
||||
-- Let's try this escape again
|
||||
{0, {12,12}, {s="a",a="Pallet Town"}, {c="viridianExp"}, {c="encounters",limit=4}, {9,12}, {9,2}, {10,2}, {10,-1}},
|
||||
-- First encounters
|
||||
{12, {10,35}, {10,30}, {8,30}, {8,24}, {12,24}, {12,20}, {9,20}, {9,14}, {14,14}, {s="dodgePalletBoy"}, {14,2}, {11,2}, {11,-1}},
|
||||
-- To the Mart
|
||||
{1, {21,35}, {21,30}, {19,30}, {19,20}, {29,20}, {29,19}},
|
||||
-- Viridian Mart
|
||||
{42, {2,5}, {3,5}, {3,8}},
|
||||
-- Backtracking
|
||||
{1, {29,20}, {c="encounters",limit=5}, {29,21}, {26,21}, {26,30}, {20,30}, {20,36}},
|
||||
-- Parkour
|
||||
{12, {10, 0}, {10,3}, {8,3}, {8,18}, {9,18}, {9,22}, {12,22}, {12,24}, {10,24}, {10,36}},
|
||||
-- To Oak's lab
|
||||
{0, {10,0}, {10,7}, {9,7}, {9,12}, {12,12}, {12,11}},
|
||||
-- Parcel delivery
|
||||
{40, {5,11}, {5,3}, {4,3}, {4,1}, {5,1}, {s="interact",dir="Down"}, {4,1}, {4,12}},
|
||||
-- Leaving home
|
||||
{0, {12,12}, {c="viridianBackupExp"}, {9,12}, {9,2}, {10,2}, {10,-1}},
|
||||
-- The grass again!?
|
||||
{12, {10,35}, {10,30}, {8,30}, {8,24}, {12,24}, {12,20}, {9,20}, {9,14}, {14,14}, {s="dodgePalletBoy"}, {14,2}, {11,2}, {11,-1}},
|
||||
-- Back to the Mart
|
||||
{1, {21,35}, {21,30}, {19,30}, {19,20}, {29,20}, {29,19}},
|
||||
-- Viridian Mart redux
|
||||
{42, {3,7}, {3,5}, {2,5}, {s="viridianBuyPokeballs"}, {3,5}, {3,8}},
|
||||
-- Sidequest
|
||||
{1, {29,20}, {15,20}, {15,17}, {-1, 17}},
|
||||
-- Nidoran
|
||||
{33, {39, 9}, {s="a",a="Nidoran grass"}, {c="nidoranBackupExp"}, {c="encounters",limit=7,extra="spearow"}, {35, 9}, {35,12}, {33,12}, {c="catchNidoran"}, {s="catchNidoran"}, {33,12}, {s="split"}, {37,12}, {37,9}, {40,9}},
|
||||
|
||||
-- 2: NIDORAN
|
||||
|
||||
-- Out of Viridian City
|
||||
{1, {0,17}, {16,17}, {16,16}, {18,16}, {18,6}, {s="dodgeViridianOldMan"}, {17, 0}, {17, -1}},
|
||||
-- To the Forest
|
||||
{13, {7,71}, {7,57}, {4,57}, {4,52}, {10,52}, {10,44}, {3,44}, {3,43}},
|
||||
-- Forest entrance
|
||||
{50, {4,7}, {s="a",a="Viridian Forest"}, {4,1}, {5,1}, {5,0}},
|
||||
-- Viridian Forest
|
||||
{51, {17,47}, {17,43}, {26,43}, {26,34}, {25,34}, {25,32}, {27,32}, {27,20}, {25,20}, {25,12}, {s="grabAntidote"}, {25,9}, {17,9}, {17,16}, {13,16}, {13,3}, {7,3}, {7,22}, {1,22}, {1,19}, {s="interact",dir="Up"}, {1,18}, {s="fightWeedle"}, {c="encounters",limit=22,extra="paras"}, {1,16}, {c="potion",b=false}, {s="equipForBrock",anti=true}, {1,5}, {s="equipForBrock"}, {1,-1}},
|
||||
-- Forest exit
|
||||
{47, {4,7}, {4,1}, {5,1}, {5,0}},
|
||||
-- Road to Pewter City
|
||||
{13, {3,11}, {s="a",a="Pewter City"}, {3,8}, {8,8}, {8,-1}},
|
||||
-- Pewter City
|
||||
{2, {18,35}, {18,22}, {19,22}, {19,13}, {10,13}, {10,18}, {16,18}, {16,17}},
|
||||
-- Brock
|
||||
{54, {4,13}, {s="a",a="Brock's Gym"}, {4,8}, {1,8}, {1,4}, {4,4}, {4,2}, {s="interact",dir="Up"}, {s="fightBrock"}, {s="split"}, {s="emuSpeed",percent=100}, {4,14}},
|
||||
|
||||
-- 3: BROCK
|
||||
|
||||
-- To Pewter Mart
|
||||
{2, {16,18}, {c="potion",b=true}, {10,18}, {10,13}, {21,13}, {21,18}, {23,18}, {23,17}},
|
||||
-- Pewter Mart
|
||||
{56, {3,7}, {3,5}, {2,5}, {s="pewterMart"}, {2,6}, {3,6}, {3,8}},
|
||||
-- Leaving Pewter City
|
||||
{2, {23,18}, {40,18}},
|
||||
-- Route 3
|
||||
{14, {0,10}, {s="a",a="Route 3"}, {c="catchFlier"}, {c="pp",on=true}, {s="battleModeSet"}, {8,10}, {8,8}, {11,8}, {11,6}, {s="leer",{"caterpie",8},{"weedle",7}}, {11,4}, {12,4}, {s="potion",hp=19}, {13,4}, {s="interact",dir="Right"}, {s="shortsKid"}, {13,5}, {s="potionBeforeCocoons"}, {18,5}, {s="interact",dir="Right"}, {s="swapHornAttack"}, {18,6}, {22,6}, {22,5}, {s="potion",hp=4}, {24,5}, {s="interact",dir="Down"}, {s="fightMetapod"}, {27,5}, {27,9}, {s="catchFlierBackup"}, {37,8}, {37,5}, {49,5}, {49,10}, {57,10}, {57,8}, {59,8}, {59,-1}},
|
||||
-- To the Center
|
||||
{15, {9,16}, {c="pp",on=false}, {12,16}, {12,6}, {11,6}, {11,5}},
|
||||
-- PP up
|
||||
{68, {3,7}, {3,3}, {s="confirm",dir="Up"}, {3,8}},
|
||||
-- Enter Mt. Moon
|
||||
{15, {11,6}, {s="a",a="Mt. Moon"}, {18,6}, {s="split"}, {18,5}},
|
||||
|
||||
-- 4: ROUTE 3
|
||||
|
||||
-- Mt. Moon F1
|
||||
{59, {14,35}, {s="startMtMoon"}, {c="catchParas"}, {14,29}, {5,29}, {5,31}, {s="interact",dir="Down"}, {5,26}, {14,26}, {14,22}, {21,22}, {21,15}, {24,15}, {24,27}, {25,27}, {25,31}, {s="interact",dir="Left"}, {25,32}, {33,32}, {33,31}, {34,31}, {s="interact",dir="Right"}, {34,7}, {30,7}, {s="evolveNidorino"}, {28,7}, {s="teachWaterGun"}, {c="moon1Exp"}, {16,7}, {16,17}, {7,17}, {7,6}, {6,6}, {s="fightHiker"}, {6,2}, {3,2}, {s="interact",dir="Left"}, {5,2}, {5,5}},
|
||||
-- Mt. Moon B2
|
||||
{60, {5,5}, {5,17}, {21,17}},
|
||||
-- Mt. Moon B3
|
||||
{61, {21,17}, {23,17}, {23,14}, {27,14}, {27,16}, {33,16}, {33,14}, {36,14}, {36,24}, {32, 24}, {32,31}, {10,31}, {10,18}, {s="evolveNidoking"}, {c="encounters",limit=nil}, {10,17}, {12,17}, {c="moon2Exp"}, {13,17}, {13,15}, {s="potion",hp=7}, {13,7}, {c="moon3Exp"}, {s="helix"}, {13,4}, {3,4}, {3,7}, {5,7}},
|
||||
-- Mt. Moon escape
|
||||
{60, {23,3}, {27,3}},
|
||||
|
||||
-- 5: MT. MOON
|
||||
|
||||
-- To Cerulean
|
||||
{15, {24,6}, {s="reportMtMoon"}, {s="split"}, {35,6}, {35,10}, {61,10}, {61,8}, {79,8}, {79,10}, {90,10}},
|
||||
-- Enter Cerulean
|
||||
{3, {0,18}, {s="a",a="Cerulean"}, {14,18}, {s="dodgeCerulean"}, {19,18}, {19,17}},
|
||||
-- Cerulean Center
|
||||
{64, {3,7}, {3,3}, {s="confirm",dir="Up"}, {3,8}},
|
||||
-- To the house
|
||||
{3, {19,18}, {16,18}, {s="dodgeCeruleanLeft"}, {8,16}, {8,12}, {9,12}, {9,11}},
|
||||
-- In the house
|
||||
{230, {2,7}, {2,0}},
|
||||
-- Outback
|
||||
{3, {9,9}, {9,8}, {14,8}, {s="interact",dir="Right"}, {9,8}, {9,10}},
|
||||
-- Out the house
|
||||
{230, {2,1}, {2,8}},
|
||||
-- Rival 2
|
||||
{3, {9,12}, {s="a",a="Cerulean Rival"}, {21,12}, {21,6}, {s="rivalSandAttack"}, {21,-1}},
|
||||
-- Nugget bridge
|
||||
{35, {11,35}, {s="a",a="Nugget Bridge"}, {11,32}, {s="interact",dir="Up"}, {10,32}, {10,29}, {s="interact",dir="Up"}, {11,29}, {11,26}, {s="interact",dir="Up"}, {10,26}, {10,24}, {s="teachThrash"}, {10,23}, {s="interact",dir="Up"}, {11,23}, {11,21}, {s="teachThrash"}, {11,20}, {s="interact",dir="Up"}, {s="redbarMankey"}, {10,20}, {10,19}, {s="teachThrash"}, {10,15}, {s="waitToFight"}, {s="teachThrash"}, {s="split"}, {10,8}, {20,8}},
|
||||
|
||||
-- 6: NUGGET BRIDGE
|
||||
|
||||
-- To Bill's
|
||||
{36, {0,8}, {9,8}, {9,7}, {11,7}, {11,9}, {14,9}, {14,6}, {15,6}, {15,4}, {17,4}, {17,7}, {18,7}, {s="interact",dir="Down"}, {20,7}, {20,8}, {22,8}, {22,6}, {35,6}, {35,4}, {36,4}, {s="interact",dir="Right"}, {36,5}, {38,5}, {38,4}, {s="interact",dir="Up"}, {45,4}, {45,3}},
|
||||
-- Save Bill
|
||||
{88, {2,7}, {2,5}, {5,5}, {s="confirm",dir="Right"}, {1,5}, {s="interact",dir="Up"}, {4,5}, {s="interact",dir="Up"}, {s="waitToTalk"}, {s="potionBeforeGoldeen"}, {s="item",item="escape_rope"}},
|
||||
-- To Misty
|
||||
{3, {19,18}, {19,20}, {30,20}, {30,19}},
|
||||
-- Misty
|
||||
{65, {4,13}, {s="a",a="Misty's Gym"}, {c="potion",b=false}, {4,8}, {2,8}, {2,5}, {7,5}, {7,3}, {6,3}, {5,3}, {s="waitToFight"}, {s="potionBeforeMisty"}, {5,2}, {s="waitToFight",dir="Left"}, {s="split"}, {s="tweetMisty"}, {5,3}, {7,3}, {7,5}, {5,5}, {5,14}},
|
||||
|
||||
-- 7: MISTY
|
||||
|
||||
-- Past the policeman
|
||||
{3, {30,20}, {c="potion",b=true,yolo=true}, {8,20}, {8,12}, {27,12}, {27,11}},
|
||||
-- Wrecked house
|
||||
{62, {2,7}, {2,2}, {3,2}, {3,0}},
|
||||
-- Cerulean Rocket
|
||||
{3, {27,9}, {28,9}, {s="potionBeforeRocket"}, {33,9}, {33,18}, {36,18}, {36,31}, {25,31}, {25,36}},
|
||||
-- Out of Cerulean
|
||||
{16, {15,0}, {15,28}, {17,28}, {17,27}},
|
||||
-- Underground entrance
|
||||
{71, {3,7}, {3,4}, {4,4}},
|
||||
-- Underground to Vermilion
|
||||
{119, {5,4}, {4,4}, {s="jingleSkip"}, {2,4}, {2,41}},
|
||||
-- Underground exit
|
||||
{74, {4,4}, {3,8}},
|
||||
-- Oddish
|
||||
-- TODO Bubblebeam split
|
||||
{17, {17,14}, {s="a",a="Vermilion City"}, {c="catchOddish"}, {17,15}, {s="potion",hp=9,yolo=5}, {17,19}, {s="catchOddish"}, {11,29}, {s="potion",hp=9,yolo=5}, {11,29}, {s="waitToFight",dir="Down"}, {10,29}, {10,30}, {s="potion",hp=9,yolo=5}, {10,31}, {9,31}, {9,32}, {s="potion",hp=20,yolo=18,chain=true}, {s="teach",move="bubblebeam",replace="water_gun"}, {9,36}},
|
||||
-- Enter Vermilion
|
||||
{5, {19,0}, {c="disableCatch"}, {19,6}, {21,6}, {21,14}, {23,14}, {23,13}},
|
||||
-- Vermilion mart
|
||||
{91, {3,7}, {3,5}, {2,5}, {s="vermilionMart"}, {3,5}, {3,8}},
|
||||
-- To S.S. Anne
|
||||
{5, {23,14}, {30,14}, {30,26}, {18,26}, {18,31}},
|
||||
-- Mew
|
||||
{94, {14,0}, {s="a",a="S.S. Anne"}, {14,3}},
|
||||
-- First deck
|
||||
{95, {27,0}, {27,1}, {26,1}, {26,7}, {2,7}, {2,6}},
|
||||
-- Rival 3
|
||||
{96, {2,4}, {2,11}, {3,11}, {3,12}, {s="potion",hp=20,yolo=16}, {37,12}, {37,8}, {s="rivalSandAttack"}, {37,5}, {36,5}, {36,4}},
|
||||
-- Old man Cut
|
||||
{101, {0,7}, {0,4}, {4,4}, {4,3}, {s="interact",dir="Up"}, {4,5}, {0,5}, {0,7}},
|
||||
-- Second deck out
|
||||
{96, {36,4}, {36,12}, {3,12}, {3,11}, {2,11}, {2,4}},
|
||||
-- First deck out
|
||||
{95, {2,6}, {2,7}, {26,7}, {26,-1}},
|
||||
-- Departure
|
||||
{94, {14,2}},
|
||||
-- To Surge
|
||||
{5, {18,29}, {18,26}, {30,26}, {30,14}, {15,14}, {15,17}, {s="potion",hp=20,yolo=5,forced="potion",chain=true}, {s="potion",hp=5,chain=true}, {s="teach",move="cut",poke="oddish",alt="paras",chain=true}, {s="skill",move="cut",done=0x0D4D}, {15,20}, {12,20}, {12,19}},
|
||||
-- Trashcans
|
||||
{92, {4,17}, {s="a",a="Surge's Gym"}, {4,16}, {2,16}, {2,11}, {s="trashcans"}, {4,6}, {4,3}, {5,3}, {5,2}, {s="interact",dir="Up"}, {s="fightSurge"}, {s="split"}, {4,2}, {4,13}, {5,13}, {5,18}},
|
||||
|
||||
-- 8: SURGE
|
||||
|
||||
-- To bicycle house
|
||||
{5, {12,20}, {s="a",a="Bicycle Shop"}, {15,20}, {15,19}, {s="skill",move="cut",done=0x0D4D}, {15,14}, {9,14}, {9,13}},
|
||||
-- Bicycle cert
|
||||
{90, {2,7}, {2,5}, {0,5}, {0,1}, {2,1}, {s="confirm",dir="Right"}, {s="teach",move="dig",poke="paras",alt="squirtle",chain=true}, {s="skill",move="dig",map=90}},
|
||||
-- Cerulean warp
|
||||
{3, {19,18}, {19,23}, {16,23}, {16,26}, {13,26}, {13,25}},
|
||||
-- Bicycle shop
|
||||
{66, {2,7}, {2,5}, {4,5}, {s="dodgeBicycleGirlRight"}, {6,4}, {6,4}, {s="procureBicycle"}, {s="dodgeBicycleGirlLeft"}, {3,8}},
|
||||
-- Bicycle out of Cerulean
|
||||
{3, {13,26}, {s="swapBicycle"}, {s="teach",move="thunderbolt",replace="horn_attack",chain=true}, {s="bicycle"}, {19,26}, {19,27}, {s="skill",move="cut",done=0x0D4D}, {19,29}, {36,29}, {36,16}, {40,16}},
|
||||
-- TPP's Bane
|
||||
{20, {0,8}, {s="a",a="Route 9"}, {4,8}, {s="skill",move="cut",done=0x0C17,val=2}, {13,8}, {13,9}, {s="interact",dir="Down"}, {12,9}, {12,12}, {23,12}, {23,11}, {29,11}, {29,12}, {41,12}, {41,10}, {40,10}, {40,9}, {s="interact",dir="Up"}, {41,9}, {41,6}, {39,6}, {39,4}, {45,4}, {45,3}, {51,3}, {51,8}, {60,8}},
|
||||
-- To the cave
|
||||
{21, {0,8}, {3,8}, {3,10}, {13,10}, {13,15}, {14,15}, {14,26}, {3,26}, {3,18}, {8,18}, {8,17}},
|
||||
-- Rock tunnel
|
||||
{82, {15,3}, {s="a",a="Rock Tunnel"}, {c="potion",b=false}, {s="item",item="repel"}, {15,6}, {23,6}, {23,7}, {s="interact",dir="Down"}, {s="redbarCubone"}, {22,7}, {22,10}, {37,10}, {37,3}},
|
||||
-- B1
|
||||
{232, {33,25}, {33,30}, {27,30}, {s="interact",dir="Left"}, {27,31}, {14,31}, {14,29}, {s="interact",dir="Up"}, {17,29}, {17,24}, {25,24}, {25,16}, {37,16}, {37,11}, {s="item",item="repel"}, {37,3}, {27,3}},
|
||||
-- B2
|
||||
{82, {5,3}, {5,9}, {11,9}, {11,14}, {17,14}, {17,11}},
|
||||
-- B1
|
||||
{232, {23,11}, {14,11}, {14,17}, {8,17}, {8,10}, {7,10}, {s="interact",dir="Left"}, {7,11}, {5,11}, {s="item",item="repel"}, {5,3}, {3,3}},
|
||||
-- Out of the Tunnel
|
||||
{82, {37,17}, {32,17}, {32,23}, {37,23}, {37,28}, {28,28}, {26,24}, {23,24}, {s="interact",dir="Left"}, {23,27}, {15,27}, {15,33}},
|
||||
-- To Lavender Town
|
||||
{21, {8,54}, {s="a",a="Lavender Town"}, {15,54}, {15,65}, {11,65}, {11,69}, {6,69}, {6,72}},
|
||||
-- Through Lavender
|
||||
{4, {6,0}, {6,6}, {0,6}, {0,8}, {-1,8}},
|
||||
-- Leave Lavender
|
||||
{19, {59,8}, {52,8}, {52,13}, {47,13}, {s="interact",dir="Left"}, {47,14}, {42,14}, {42,7}, {40,7}, {40,6}, {29,6}, {29,7}, {23,7}, {23,12}, {14,12}, {14,4}, {13,4}, {13,3}},
|
||||
-- Underground entrance
|
||||
{80, {3,7}, {3,6}, {4,6}, {4,4}},
|
||||
-- Underground
|
||||
{121, {47,2}, {s="bicycle"}, {47,5}, {22,5}, {s="interact",dir="Left"}, {2,5}},
|
||||
-- Underground exit
|
||||
{77, {4,4}, {4,8}},
|
||||
-- To Celadon
|
||||
{18, {5,14}, {s="bicycle"}, {8,14}, {8,8}, {4,8}, {4,3}, {-1,3}},
|
||||
-- Celadon
|
||||
{6, {49,11}, {s="a",a="Celadon Mart"}, {14,11}, {14,14}, {10,14}, {10,13}},
|
||||
-- Department store
|
||||
{122, {16,7}, {c="potion",b=true,yolo=true}, {c="pp",on=true}, {16,3}, {12,3}, {12,1}},
|
||||
-- F2
|
||||
{123, {12,2}, {16,2}, {16,1}},
|
||||
-- F3
|
||||
{124, {16,2}, {12,2}, {12,1}},
|
||||
-- F4: Poke Doll
|
||||
{125, {12,2}, {10,2}, {10,5}, {5,5}, {s="shopPokeDoll"}, {11,5}, {11,2}, {16,2}, {16,1}},
|
||||
-- F5: Buffs
|
||||
{136, {16,2}, {8,2}, {8,5}, {5,5}, {s="shopBuffs"}, {9,5}, {9,2}, {12,2}, {12,1}},
|
||||
-- Roof
|
||||
{126, {15,3}, {12,3}, {s="shopVending"}, {6,3}, {6,4}, {s="giveWater"}, {6,4}, {7,3}, {12,3}, {s="shopExtraWater"}, {15,3}, {15,2}},
|
||||
-- F5
|
||||
{136, {12,2}, {16,2}, {16,1}},
|
||||
-- F4
|
||||
{125, {16,2}, {12,2}, {12,1}},
|
||||
-- F3
|
||||
{124, {12,2}, {16,2}, {16,1}},
|
||||
-- F2: TM and repel
|
||||
{123, {16,2}, {8,2}, {8,5}, {6,5}, {s="shopTM07"}, {5,5}, {s="shopRepels"}, {9,5}, {9,2}, {12,2}, {12,1}},
|
||||
-- Exit department store
|
||||
{122, {12, 2}, {12,6}, {16,6}, {16,8}},
|
||||
-- Leave Celadon
|
||||
{6, {10,14}, {s="bicycle"}, {10,15}, {2,15}, {2,18}, {-1,18}},
|
||||
-- Cut out of Celadon
|
||||
{27, {39,10}, {34,10}, {s="teach",move="horn_drill",replace="bubblebeam",full=true,chain=true}, {s="skill",move="cut",dir="Up",done=0x0D4D}, {34,6}, {27,6}, {27,4}, {23,4}},
|
||||
-- Old man's hall
|
||||
{186, {7,2}, {-1,2}},
|
||||
-- To the Fly house
|
||||
{27, {17,4}, {s="a",a="HM02 Fly"}, {10,4}, {10,6}, {7,6}, {7,5}},
|
||||
-- Fly house
|
||||
{188, {2,7}, {2,4}, {s="interact",dir="Up"}, {2,5}, {s="split"}, {2,8}},
|
||||
|
||||
-- 9: FLY
|
||||
|
||||
-- Fly to Lavender
|
||||
{27, {7,6}, {s="swapRepels"}, {s="teach",move="fly",poke="spearow",alt="pidgey",chain=true}, {s="teach",move="horn_drill",replace="bubblebeam",chain=true}, {s="item",item="super_repel",chain=true}, {s="potion",hp=6,chain=true}, {s="teach",move="rock_slide",replace="poison_sting",chain=true}, {s="fly",dest="lavender",map=4}},
|
||||
-- To the tower
|
||||
{4, {3,6}, {s="a",a="Pokemon Tower"}, {14,6}, {14,5}},
|
||||
-- Pokemon Tower
|
||||
{142, {10,17}, {10,10}, {18,10}, {18,9}},
|
||||
-- F2: Rival
|
||||
{143, {18,9}, {c="setThrash",disable=true}, {18,7}, {16,7}, {16,5}, {15,5}, {s="lavenderRival"}, {5,5}, {5,8}, {3,8}, {3,9}},
|
||||
-- F3
|
||||
{144, {3,9}, {3,10}, {6,10}, {6,13}, {8,13}, {8,6}, {17,6}, {17,9}, {18,9}},
|
||||
-- F4
|
||||
{145, {18,9}, {s="allowDeath",on=true}, {c="potion",b=false}, {18,7}, {16,7}, {s="interact",dir="Left"}, {s="digFight"}, {16,9}, {c="potion",b=true,yolo=true}, {14,9}, {14,10}, {13,10}, {s="interact",dir="Left"}, {14,10}, {14,8}, {11,8}, {11,9}, {10,9}, {10,12}, {7,12}, {7,11}, {4,11}, {4,10}, {3,10}, {3,9}},
|
||||
-- F5
|
||||
{146, {3,9}, {4,9}, {4,11}, {s="interact",dir="Down"}, {4,6}, {13,6}, {13,9}, {9,9}, {9,12}, {14,12}, {14,10}, {18,10}, {s="allowDeath",on=false}, {18,9}},
|
||||
-- F6
|
||||
{147, {18,9}, {18,7}, {15,7}, {15,3}, {11,3}, {11,5}, {10,5}, {s="interact",dir="Left"}, {10,6}, {6,6}, {6,7}, {s="interact",dir="Down"}, {6,14}, {10,14}, {10,16}, {s="pokeDoll"}, {9,16}},
|
||||
-- F7: Top
|
||||
{148, {9,16}, {10,16}, {10,9}, {s="fightXAccuracy"}, {c="setThrash",disable=false}, {10,7}, {s="thunderboltFirst"}, {10,4}, {s="interact",dir="Up"}},
|
||||
-- Old man's house
|
||||
{149, {3,7}, {3,6}, {2,6}, {2,1}, {s="interact",dir="Right"}, {2,8}},
|
||||
|
||||
-- 10: POKéFLUTE
|
||||
|
||||
-- Lavender -> Celadon
|
||||
{4, {7,10}, {s="split"}, {s="fly",dest="celadon",map=6}},
|
||||
-- To Celadon Center
|
||||
{6, {41,10}, {41,9}},
|
||||
-- Celadon Center
|
||||
{133, {3,7}, {3,3}, {s="confirm",dir="Up"}, {3,8}},
|
||||
-- Leave Celadon
|
||||
{6, {41,10}, {s="a",a="Snorlax"}, {s="item",item="super_repel",chain=true}, {s="bicycle"}, {41,11}, {14,11}, {14,14}, {2,14}, {2,18}, {-1,18}},
|
||||
-- トトロだ!
|
||||
{27, {39,10}, {27,10}, {s="playPokeflute"}, {23,10}},
|
||||
-- Snorlax pass
|
||||
{186, {7,8}, {-1,8}},
|
||||
-- Bicycle road
|
||||
{27, {17,10}, {s="a",a="Bicycle Road"}, {12,10}, {12,13}, {11,13}, {11,18}},
|
||||
-- Forced down inputs
|
||||
{28, {11,0}, {11,5}, {15,5}, {15,12}, {s="drivebyRareCandy"}, {15,14}, {18,14}, {18,122}, {13,122}, {13,143}},
|
||||
-- Cycling road exit
|
||||
{29, {13,0}, {13,8}, {34,8}},
|
||||
-- Exit building
|
||||
{190, {0,4}, {8,4}},
|
||||
-- Enter Safari City
|
||||
{29, {40,8}, {s="item",item="super_repel",chain=true}, {s="teach",move="ice_beam",replace="rock_slide",chain=true}, {s="bicycle"}, {50,8}},
|
||||
-- Safari City
|
||||
{7, {0,16}, {s="a",a="Safari Zone"}, {3,16}, {3,20}, {23,20}, {23,14}, {29,14}, {29,15}, {35,15}, {35,8}, {37,8}, {37,2}, {22,2}, {22,4}, {18,4}, {18,3}},
|
||||
-- Safari entrance
|
||||
{156, {3,5}, {3,2}, {4,2}, {s="confirm",dir="Right"}},
|
||||
-- Safari 1
|
||||
{220, {15,25}, {s="bicycle"}, {15,16}, {28,16}, {28,11}, {30,11}},
|
||||
-- Safari 2
|
||||
{217, {0,23}, {4,23}, {4,24}, {20,24}, {20,20}, {s="safariCarbos"}, {12,20}, {12,22}, {11,22}, {10,22}, {s="item",item="super_repel",chain=true}, {s="item",item="carbos",poke="nidoking",close=true}, {9,22}, {9,8}, {12,8}, {12,6}, {17,6}, {17,8}, {20,8}, {s="centerSkipFullRestore"}, {20,3}, {7,3}, {7,5}, {-1,5}},
|
||||
-- Safari 3
|
||||
{218, {39,31}, {22,31}, {22,22}, {16,22}, {16,28}, {13,28}, {13,9}, {28,9}, {28,3}, {3,3}, {3,36}},
|
||||
-- Safari 4
|
||||
{219, {21,0}, {21,5}, {19,5}, {19,6}, {s="interact",dir="Down"}, {19,5}, {7,5}, {7,6}, {3,6}, {3,3}},
|
||||
-- Warden
|
||||
{222, {2,7}, {2,6}, {3,6}, {3,4}, {s="interact",dir="Up"}, {3,8}},
|
||||
-- Safari Warp
|
||||
{219, {3,4}, {s="skill",move="dig",map=219}},
|
||||
-- Celadon again
|
||||
{6, {41,10}, {s="bicycle"}, {41,11}, {50,11}},
|
||||
-- To Saffron
|
||||
{18, {0,3}, {s="a",a="Saffron City"}, {4,3}, {4,9}, {10,9}, {10,10}, {12,10}},
|
||||
-- Thirsty guard
|
||||
{76, {0,4}, {6,4}},
|
||||
-- Saffron entry
|
||||
{18, {18,10}, {s="bicycle"}, {20,10}},
|
||||
-- Saffron City
|
||||
{10, {0,18}, {3,18}, {3,22}, {18,22}, {18,21}},
|
||||
-- Silph Co
|
||||
{181, {10,17}, {s="a",a="Silph Co."}, {10,9}, {8,9}, {8,1}, {20,1}, {20,0}},
|
||||
-- Elivator
|
||||
{236, {1,3}, {1,2}, {3,2}, {3,1}, {s="silphElevator"}, {2,1}, {2,4}},
|
||||
-- F10
|
||||
{234, {12,1}, {12,3}, {4,3}, {4,9}, {s="fightSilphMachoke"}, {6,9}, {6,11}, {s="silphCarbos"}, {6,16}, {3,16}, {3,14}, {s="interact",dir="Right"}, {3,15}, {1,15}, {s="item",item="carbos",poke="nidoking",full=true}, {1,12}, {s="interact",dir="Right"}, {1,16}, {3,16}, {s="teach",move="surf",poke="squirtle",replace="tail_whip",chain=true}, {s="teach",move="earthquake",replace="thrash",chain=true}, {s="item",item="carbos",poke="nidoking",close=true}, {6,16}, {6,9}, {4,9}, {4,1}, {8,1}, {8,0}},
|
||||
-- F9
|
||||
{233, {14,1}, {14,3}, {24,3}, {24,16}, {17,16}, {17,15}},
|
||||
-- Warped
|
||||
{210, {9,15}, {9,16}, {20,16}, {s="interact",dir="Right"}, {9,16}, {9,15}},
|
||||
-- Warp back
|
||||
{233, {17,15}, {17,14}, {17,15}},
|
||||
-- First card
|
||||
{210, {9,15}, {9,13}, {8,13}, {s="interact",dir="Left"}, {6,13}, {6,16}, {3,16}, {3,15}},
|
||||
-- Warp down
|
||||
{208, {3,15}, {3,14}, {18,14}, {18,9}, {s="interact",dir="Left"}, {14,9}, {14,11}, {11,11}},
|
||||
-- Rival 5
|
||||
{212, {5,3}, {s="a",a="Silph Rival"}, {4,3}, {4,2}, {3,2}, {c="potion",b=false}, {s="silphRival"}, {3,7}, {c="potion",b=true,yolo=true}, {5,7}},
|
||||
-- Giovanni
|
||||
{235, {3,2}, {s="a",a="Silph Giovanni"}, {3,11}, {2,11}, {2,16}, {s="interact",dir="Right"}, {2,15}, {5,15}, {s="potion",hp=16,yolo=12}, {6,15}, {6,14}, {s="interact",dir="Up"}, {6,13}, {s="fightXAccuracy"}, {s="fightSilphGiovanni"}, {s="split"}, {s="waitToPause"}, {s="skill",move="dig",map=235}},
|
||||
|
||||
-- 11: SILPH CO.
|
||||
|
||||
-- Fly to Fuschia
|
||||
{6, {41,10}, {s="fly",dest="fuchsia",map=7}},
|
||||
-- To Koga
|
||||
{7, {19,28}, {s="a",a="Koga's Gym"}, {5,28}, {5,27}},
|
||||
-- Koga
|
||||
-- TODO save turn frames?
|
||||
{157, {4,17}, {9,17}, {9,9}, {7,9}, {s="interact",dir="Up"}, {9,9}, {9,1}, {1,1}, {1,2}, {s="potion",hp="KogaHypno",yolo=18,chain=true}, {s="earthquakeElixer",min=2,close=true}, {1,3}, {2,3}, {2,5}, {1,5}, {c="potion",b=false}, {1,7}, {s="fightHypno"}, {1,9}, {2,9}, {s="earthquakeElixer",min=4}, {4,9}, {s="interact",dir="Down"}, {s="fightKoga"}, {s="split"}, {1,9}, {1,5}, {2,5}, {2,3}, {1,3}, {1,1}, {9,1}, {9,16}, {5,16}, {5,18}},
|
||||
|
||||
-- 12: KOGA
|
||||
|
||||
-- To the Warden
|
||||
{7, {5,28}, {s="bicycle"}, {6,28}, {6,30}, {24,30}, {30,30}, {30,28}, {27,28}, {27,27}},
|
||||
-- HM04 Strength
|
||||
{155, {4,7}, {4,6}, {2,6}, {2,4}, {s="interact",dir="Up"}, {4,4}, {4,8}},
|
||||
-- Fly home
|
||||
{7, {27,28}, {s="fly",dest="pallet",map=0}},
|
||||
-- Pallet to Cinnabar
|
||||
{0, {5,6}, {s="item",item="super_repel",chain=true}, {s="item",item="rare_candy",amount=3,poke="nidoking",chain=true}, {s="bicycle"}, {s="allowDeath",on=false}, {3,6}, {s="dodgeGirl"}, {3,17}, {s="skill",move="surf",dir="Right",x=4}, {4,18}},
|
||||
-- To Cinnabar
|
||||
{32, {4,0}, {4,14}, {3,14}, {3,90}},
|
||||
-- Enter Cinnabar Mansion
|
||||
{8, {3,0}, {s="a",a="Cinnabar Mansion"}, {3,4}, {6,4}, {6,3}},
|
||||
-- F1
|
||||
{165, {5,27}, {5,10}},
|
||||
-- F2
|
||||
{214, {5,11}, {10,11}, {10,5}, {6,5}, {6,1}},
|
||||
-- F3
|
||||
{215, {6,2}, {11,2}, {11,6}, {10,6}, {s="confirm",dir="Up"}, {14,6}, {14,11}, {16,11}, {16,14}},
|
||||
-- F1 drop
|
||||
{165, {16,14}, {16,15}, {13,15}, {13,20}, {s="cinnabarCarbos"}, {21,23}},
|
||||
-- B1
|
||||
{216, {23,22}, {23,15}, {21,15}, {s="item",item="super_repel",chain=true}, {s="item",item="carbos",poke="nidoking",close=true}, {17,15}, {17,19}, {18,19}, {18,23}, {17,23}, {17,26}, {18,26}, {s="confirm",dir="Up"}, {14,26}, {14,22}, {12,22}, {12,15}, {24,15}, {24,18}, {26,18}, {26,6}, {24,6}, {24,4}, {20,4}, {s="confirm",dir="Up"}, {24,4}, {24,6}, {12,6}, {12,2}, {11,2}, {s="interact",dir="Left"}, {12,2}, {12,7}, {4,7}, {4,9}, {2,9}, {s="interact",dir="Left"}, {5,9}, {5,10}, {s="teach",move="strength",poke="squirtle",replace="tackle",chain=true}, {s="item",item="rare_candy",amount=2,poke="nidoking",close=true}, {5,12}, {s="interact",dir="Down"}, {5,12}, {s="skill",move="dig",map=216}},
|
||||
-- Celadon once again
|
||||
{6, {41,10}, {s="bicycle"}, {41,13}, {36,13}, {36,23}, {25,23}, {25,30}, {35,30}, {35,31}, {s="skill",move="cut",dir="Down",done=0x0D4D}, {35,34}, {5,34}, {5,29}, {12,29}, {12,27}},
|
||||
-- Erika
|
||||
{134, {4,17}, {s="a",a="Erika's Gym"}, {4,16}, {1,16}, {1,9}, {0,9}, {0,4}, {1,4}, {s="skill",move="cut",done=0x0D4D}, {4,4}, {s="interact",dir="Up"}, {s="fightErika"}, {s="split"}, {4,5}, {5,5}, {5,6}, {s="skill",move="cut",dir="Down",done=0x0D4D}, {5,18}},
|
||||
|
||||
-- 13: ERIKA
|
||||
|
||||
-- Fly to Cinnabar
|
||||
{6, {12,28}, {s="fly",dest="cinnabar",map=8}},
|
||||
-- Cinnabar
|
||||
{8, {11,12}, {s="earthquakeElixer",min=4,chain=true}, {s="bicycle"}, {18,12}, {18,3}},
|
||||
-- Cinnabar Gym
|
||||
{166, {16,17}, {s="a",a="Blaine's Gym"}, {16,14}, {18,14}, {18,10}, {15,10}, {15,8}, {s="confirm",dir="Up"}, {16,8}, {16,7}, {18,7}, {18,1}, {12,1}, {12,2}, {10,2}, {s="confirm",dir="Up",type="B"}, {12,2}, {12,7}, {10,7}, {10,8}, {9,8}, {s="confirm",dir="Up",type="B"}, {9,11}, {12,11}, {12,13}, {10,13}, {10,14}, {9,14}, {s="confirm",dir="Up",type="B"}, {9,16}, {1,16}, {1,14}, {s="confirm",dir="Up"}, {2,14}, {2,13}, {4,13}, {4,9}, {1,9}, {1,8}, {s="confirm",dir="Up",type="B"}, {2,8}, {2,7}, {4,7}, {4,5}, {3,5}, {3,4}, {c="potion",b=false}, {s="waitToFight",dir="Up"}, {s="split"}, {s="waitToReceive"}, {s="skill",move="dig",map=166}},
|
||||
|
||||
-- 14: BLAINE
|
||||
|
||||
-- Celadon too many times
|
||||
{6, {41,10}, {c="potion",b=true,yolo=true}, {s="bicycle"}, {41,11}, {50,11}},
|
||||
-- Exit Celadon
|
||||
{18, {0,3}, {4,3}, {4,9}, {10,9}, {10,10}, {12,10}},
|
||||
-- Saffron gate
|
||||
{76, {0,4}, {s="a",a="Saffron City"}, {6,4}},
|
||||
-- Saffron edge
|
||||
{18, {18,10}, {s="earthquakeElixer",min=4,chain=true}, {s="bicycle"}, {20,10}},
|
||||
-- Saffron again
|
||||
{10, {0,18}, {3,18}, {3,6}, {31,6}, {31,4}, {34,4}, {34,3}},
|
||||
-- Sabrina
|
||||
{178, {8,17}, {s="a",a="Sabrina's Gym"}, {8,16}, {11,16}, {11,15}, {16,17}, {16,15}, {15,15}, {18,3}, {18,5}, {15,5}, {1,5}, {11,11}, {11,8}, {10,8}, {s="waitToFight",dir="Left"}, {s="split"}, {11,8}, {11,11}, {s="earthquakeElixer",min=4,chain=true}, {s="skill",move="dig",map=178}},
|
||||
|
||||
-- 15: SABRINA
|
||||
|
||||
-- Celadon
|
||||
{6, {41,10}, {s="fly",dest="viridian",map=1}},
|
||||
-- Viridian again
|
||||
{1, {23,26}, {s="bicycle"}, {19,26}, {19,4}, {27,4}, {27,3}, {34,3}, {34,8}, {32,8}, {32,7}},
|
||||
-- Giovanni Gym
|
||||
{45, {16,17}, {c="potion",b=false}, {s="a",a="Giovanni's Gym"}, {16,16}, {14,16}, {14,9}, {13,9}, {13,7}, {15,7}, {15,4}, {12,4}, {12,5}, {10,5}, {s="a",a="Machoke"}, {10,4}, {s="fightGiovanniMachoke"}, {10,5}, {s="a",a="Giovanni's Gym"}, {13,5}, {13,4}, {15,4}, {15,7}, {13,7}, {13,11}, {14,11}, {14,16}, {16,16}, {16,18}},
|
||||
-- Reset Gym
|
||||
{1, {32,8}, {32,7}},
|
||||
-- Giovanni
|
||||
{45, {16,17}, {c="potion",b=false}, {16,16}, {14,16}, {14,9}, {13,9}, {13,7}, {15,7}, {15,4}, {12,4}, {12,5}, {10,5}, {10,2}, {7,2}, {7,4}, {2,4}, {s="checkGiovanni"}, {2,2}, {s="interact",dir="Up"}, {s="fightGiovanni"}, {s="split"}, {2,4}, {7,4}, {7,2}, {10,2}, {10,5}, {12,5}, {12,4}, {15,4}, {15,7}, {13,7}, {13,11}, {14,11}, {14,16}, {16,16}, {16,18}},
|
||||
|
||||
-- 16: GIOVANNI
|
||||
|
||||
-- Leave Viridian
|
||||
{1, {32,8}, {s="bicycle"}, {32,12}, {17,12}, {17,16}, {16,16}, {16,17}, {-1,17}},
|
||||
-- To Pokemon League
|
||||
{33, {39,9}, {s="a",a="Viridian Rival"}, {35,9}, {35,12}, {31,12}, {31,5}, {29,5}, {s="viridianRival"}, {16,5}, {16,12}, {5,12}, {5,10}, {11,10}, {11,6}, {8,6}, {8,5}},
|
||||
-- Pokemon League 1
|
||||
{193, {4,7}, {4,0}},
|
||||
-- PL 2
|
||||
{34, {7,139}, {s="ether",max=true,chain=true}, {s="bicycle"}, {7,132}, {14,132}, {14,124}, {9,124}, {9,116}, {10,116}, {10,104}, {s="skill",move="surf",y=103}, {10,92}, {7,92}, {7,90}, {s="pickMaxEther"}, {7,72}, {8,72}, {8,71}, {s="item",item="super_repel",chain=true}, {s="bicycle"}, {8,66}, {10,66}, {10,57}, {12,57}, {12,48}, {6,48}, {6,32}, {4,32}, {4,31}},
|
||||
-- Victory Road
|
||||
{108, {8,17}, {s="a",a="Victory Road"}, {s="tweetVictoryRoad"}, {8,16}, {4,16}, {4,14}, {5,14}, {s="skill",move="strength"}, {5,15}, {4,15}, {4,16}, {7,16}, {s="push",dir="Right",x=0x0255,y=0x0254}, {7,17}, {9,17}, {9,15}, {8,15}, {8,14}, {15,14}, {15,15}, {16,15}, {16,14}, {s="push",dir="Up",x=0x0255,y=0x0254}, {14,14}, {14,12}, {16,12}, {16,11}, {17,11}, {17,12}, {14,12}, {14,14}, {8,14}, {8,16}, {5,16}, {5,12}, {11,12}, {11,6}, {7,6}, {7,8}, {3,8}, {3,5}, {2,5}, {2,1}, {1,1}},
|
||||
-- F2
|
||||
{194, {0,8}, {0,9}, {3,9}, {3,13}, {5,13}, {5,14}, {s="item",item="super_repel",chain=true}, {s="skill",move="strength"}, {4,14}, {4,13}, {3,13}, {3,15}, {4,15}, {4,16}, {3,16}, {s="push",dir="Left",x=0x02B5,y=0x02B4}, {3,11}, {5,11}, {5,8}, {14,8}, {14,14}, {21,14}, {21,16}, {28,16}, {28,11}, {23,11}, {23,7}},
|
||||
-- F3
|
||||
{198, {23,7}, {23,6}, {22,6}, {22,4}, {s="skill",move="strength"}, {22,2}, {23,2}, {23,1}, {7,1}, {7,0}, {6,0}, {6,1}, {7,1}, {7,2}, {3,2}, {3,1}, {2,1}, {2,4}, {1,4}, {1,5}, {2,5}, {2,4}, {4,4}, {4,2}, {7,2}, {7,1}, {20,1}, {20,6}, {17,6}, {17,4}, {9,4}, {9,10}, {5,10}, {5,8}, {1,8}, {1,15}, {11,15}, {11,16}, {20,16}, {20,15}, {23,15}},
|
||||
-- F2
|
||||
{194, {22,16}, {s="healBeforeLorelei"}, {s="item",item="super_repel",chain=true}, {s="skill",move="strength"}, {s="bicycle"}, {22,17}, {24,17}, {24,16}, {11,16}, {s="push",dir="Left",x=0x02D5,y=0x02D4}, {21,16}, {21,14}, {25,14}},
|
||||
-- F3
|
||||
{198, {27,15}, {27,8}, {26,8}},
|
||||
-- F2 Exit
|
||||
{194, {27,7}, {30,7}},
|
||||
-- Victory end
|
||||
{34, {14,32}, {18,32}, {18,20}, {14,20}, {14,10}, {13,10}, {13,6}, {10,6}, {10,-1}},
|
||||
-- Elite Four entrance
|
||||
{9, {10,17}, {s="a",a="Elite Four"}, {10,5}},
|
||||
-- Last Center
|
||||
{174, {7,11}, {15,9}, {15,8}, {s="depositPokemon"}, {7,8}, {7,7}, {s="centerSkip"}, {4,7}, {3,7}, {3,2}, {8,2}, {8,0}},
|
||||
|
||||
-- 17: LORELEI
|
||||
|
||||
{245, {4,5}, {s="a",a="Lorelei"}, {c="potion",b=false}, {4,2}, {s="interact",dir="Right"}, {s="lorelei"}, {s="split"}, {4,0}},
|
||||
|
||||
{246, {4,5}, {s="a",a="Bruno"}, {s="item",item="elixer",poke="nidoking"}, {4,2}, {s="interact",dir="Right"}, {s="bruno"}, {s="split"}, {4,0}},
|
||||
|
||||
{247, {4,5}, {s="a",a="Agatha"}, {s="potion",hp=113,full=true}, {4,2}, {s="interact",dir="Right"}, {s="agatha"}, {s="split"}, {4,1}, {s="prepareForLance"}, {s="ether"}, {4,0}},
|
||||
|
||||
{113, {6,11}, {s="a",a="Lance"}, {6,2}, {s="lance"}, {s="waitToFight"}, {s="split"}, {5,2}, {5,1}, {s="prepareForBlue"}, {5,-1}},
|
||||
|
||||
{120, {4,3}, {s="a",a="Blue"}, {s="blue"}, {3,0}},
|
||||
|
||||
{118, {4,2}, {s="champion"}}
|
||||
}
|
||||
|
||||
return paths
|
|
@ -0,0 +1,198 @@
|
|||
-- Customization settings
|
||||
|
||||
GAME_NAME = "red" -- Only currently supported option
|
||||
RESET_FOR_TIME = true -- Set to false if you just want to see the bot finish a run
|
||||
|
||||
local CUSTOM_SEED = nil -- Set to a known seed to replay it, or leave nil for random ones
|
||||
local PAINT_ON = true -- Displays contextual information while the bot runs
|
||||
|
||||
-- Start code (hard hats on)
|
||||
|
||||
local START_WAIT = 99
|
||||
local VERSION = "1.0"
|
||||
|
||||
local battle = require "action.battle"
|
||||
local textbox = require "action.textbox"
|
||||
local walk = require "action.walk"
|
||||
|
||||
local combat = require "ai.combat"
|
||||
local control = require "ai.control"
|
||||
local strategies = require "ai.strategies"
|
||||
|
||||
local bridge = require "util.bridge"
|
||||
local input = require "util.input"
|
||||
local memory = require "util.memory"
|
||||
local menu = require "util.menu"
|
||||
local paint = require "util.paint"
|
||||
local utils = require "util.utils"
|
||||
local settings = require "util.settings"
|
||||
|
||||
local pokemon = require "storage.pokemon"
|
||||
|
||||
local YELLOW = GAME_NAME == "yellow"
|
||||
|
||||
local hasAlreadyStartedPlaying = false
|
||||
local inBattle, oldSecs
|
||||
local running = true
|
||||
local previousPartySize = 0
|
||||
local lastHP
|
||||
local criticaled = false
|
||||
|
||||
local function startNewAdventure()
|
||||
local startMenu, withBattleStyle
|
||||
if (YELLOW) then
|
||||
startMenu = memory.raw(0x0F95) == 0
|
||||
withBattleStyle = "battle_style"
|
||||
else
|
||||
startMenu = memory.value("player", "name") ~= 0
|
||||
end
|
||||
if (startMenu and menu.getCol() ~= 0) then
|
||||
if (settings.set("text_speed", "battle_animation", withBattleStyle)) then
|
||||
menu.select(0)
|
||||
end
|
||||
elseif (math.random(0, START_WAIT) == 0) then
|
||||
input.press("Start")
|
||||
end
|
||||
end
|
||||
|
||||
local function choosePlayerNames()
|
||||
local name
|
||||
if (memory.value("player", "name2") == 80) then
|
||||
name = "W"
|
||||
else
|
||||
name = "B"
|
||||
end
|
||||
textbox.name(name, true)
|
||||
end
|
||||
|
||||
local function pollForResponse()
|
||||
local response = bridge.process()
|
||||
if (response) then
|
||||
bridge.polling = false
|
||||
textbox.setName(tonumber(response))
|
||||
end
|
||||
end
|
||||
|
||||
local function resetAll()
|
||||
strategies.softReset()
|
||||
combat.reset()
|
||||
control.reset()
|
||||
walk.reset()
|
||||
paint.reset()
|
||||
bridge.reset()
|
||||
oldSecs = 0
|
||||
running = false
|
||||
previousPartySize = 0
|
||||
-- client.speedmode = 200
|
||||
if (CUSTOM_SEED) then
|
||||
strategies.seed = CUSTOM_SEED
|
||||
print("RUNNING WITH A FIXED SEED ("..strategies.seed.."), every run will play out identically!")
|
||||
else
|
||||
strategies.seed = os.time()
|
||||
end
|
||||
math.randomseed(strategies.seed)
|
||||
end
|
||||
|
||||
-- Execute
|
||||
|
||||
print("Welcome to PokeBot "..GAME_NAME.." version "..VERSION)
|
||||
local productionMode = not walk.init() and false
|
||||
if (CUSTOM_SEED) then
|
||||
client.reboot_core()
|
||||
else
|
||||
hasAlreadyStartedPlaying = utils.ingame()
|
||||
end
|
||||
|
||||
strategies.init(hasAlreadyStartedPlaying)
|
||||
if (RESET_FOR_TIME and hasAlreadyStartedPlaying) then
|
||||
RESET_FOR_TIME = false
|
||||
print("Disabling time-limit resets as the game is already running. Please reset the emulator and restart the script if you'd like to go for a fast time.")
|
||||
end
|
||||
if (productionMode) then
|
||||
bridge.init()
|
||||
else
|
||||
input.setDebug(true)
|
||||
end
|
||||
|
||||
local previousMap
|
||||
|
||||
while true do
|
||||
local currentMap = memory.value("game", "map")
|
||||
if (currentMap ~= previousMap) then
|
||||
input.clear()
|
||||
previousMap = currentMap
|
||||
end
|
||||
if (not input.update()) then
|
||||
if (not utils.ingame()) then
|
||||
if (currentMap == 0) then
|
||||
if (running) then
|
||||
if (not hasAlreadyStartedPlaying) then
|
||||
client.reboot_core()
|
||||
hasAlreadyStartedPlaying = true
|
||||
else
|
||||
resetAll()
|
||||
end
|
||||
else
|
||||
startNewAdventure()
|
||||
end
|
||||
else
|
||||
if (not running) then
|
||||
bridge.liveSplit()
|
||||
running = true
|
||||
end
|
||||
choosePlayerNames()
|
||||
end
|
||||
else
|
||||
local battleState = memory.value("game", "battle")
|
||||
if (battleState > 0) then
|
||||
if (battleState == 1) then
|
||||
if (not inBattle) then
|
||||
control.wildEncounter()
|
||||
if (strategies.moonEncounters) then
|
||||
strategies.moonEncounters = strategies.moonEncounters + 1
|
||||
end
|
||||
inBattle = true
|
||||
end
|
||||
end
|
||||
local isCritical
|
||||
local battleMenu = memory.value("battle", "menu")
|
||||
if (battleMenu == 94) then
|
||||
isCritical = false
|
||||
elseif (memory.double("battle", "our_hp") == 0) then
|
||||
if (memory.value("battle", "critical") == 1) then
|
||||
isCritical = true
|
||||
end
|
||||
end
|
||||
if (isCritical ~= nil and isCritical ~= criticaled) then
|
||||
criticaled = isCritical
|
||||
strategies.criticaled = criticaled
|
||||
end
|
||||
else
|
||||
inBattle = false
|
||||
end
|
||||
local currentHP = pokemon.index(0, "hp")
|
||||
if (currentHP == 0 and not strategies.canDie and pokemon.index(0) > 0) then
|
||||
strategies.death(currentMap)
|
||||
elseif (walk.strategy) then
|
||||
if (strategies.execute(walk.strategy)) then
|
||||
walk.traverse(currentMap)
|
||||
end
|
||||
elseif (battleState > 0) then
|
||||
if (not control.shouldCatch(partySize)) then
|
||||
battle.automate()
|
||||
end
|
||||
elseif (textbox.handle()) then
|
||||
walk.traverse(currentMap)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (PAINT_ON) then
|
||||
paint.draw(currentMap)
|
||||
end
|
||||
|
||||
input.advance()
|
||||
emu.frameadvance()
|
||||
end
|
||||
|
||||
bridge.close()
|
|
@ -0,0 +1,200 @@
|
|||
local inventory = {}
|
||||
|
||||
local pokemon = require "storage.pokemon"
|
||||
|
||||
local input = require "util.input"
|
||||
local memory = require "util.memory"
|
||||
local menu = require "util.menu"
|
||||
local utils = require "util.utils"
|
||||
|
||||
local items = {
|
||||
pokeball = 4,
|
||||
bicycle = 6,
|
||||
moon_stone = 10,
|
||||
antidote = 11,
|
||||
paralyze_heal = 15,
|
||||
full_restore = 16,
|
||||
super_potion = 19,
|
||||
potion = 20,
|
||||
escape_rope = 29,
|
||||
carbos = 38,
|
||||
repel = 30,
|
||||
|
||||
rare_candy = 40,
|
||||
helix_fossil = 42,
|
||||
nugget = 49,
|
||||
pokedoll = 51,
|
||||
super_repel = 56,
|
||||
fresh_water = 60,
|
||||
soda_pop = 61,
|
||||
pokeflute = 73,
|
||||
ether = 80,
|
||||
max_ether = 81,
|
||||
elixer = 82,
|
||||
|
||||
x_accuracy = 46,
|
||||
x_speed = 67,
|
||||
x_special = 68,
|
||||
|
||||
cut = 196,
|
||||
fly = 197,
|
||||
surf = 198,
|
||||
strength = 199,
|
||||
|
||||
horn_drill = 207,
|
||||
bubblebeam = 211,
|
||||
water_gun = 212,
|
||||
ice_beam = 213,
|
||||
thunderbolt = 224,
|
||||
earthquake = 226,
|
||||
dig = 228,
|
||||
tm34 = 234,
|
||||
rock_slide = 248,
|
||||
}
|
||||
|
||||
local ITEM_BASE = 0xD31E
|
||||
|
||||
-- Data
|
||||
|
||||
function inventory.indexOf(name)
|
||||
local searchID = items[name]
|
||||
for i=0,19 do
|
||||
local iidx = ITEM_BASE + i * 2
|
||||
if (memory.raw(iidx) == searchID) then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return -1
|
||||
end
|
||||
|
||||
function inventory.count(name)
|
||||
local index = inventory.indexOf(name)
|
||||
if (index ~= -1) then
|
||||
return memory.raw(ITEM_BASE + index * 2 + 1)
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function inventory.contains(...)
|
||||
for i,name in ipairs(arg) do
|
||||
if (inventory.count(name) > 0) then
|
||||
return name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Actions
|
||||
|
||||
function inventory.teach(item, poke, replaceIdx, altPoke)
|
||||
local main = memory.value("menu", "main")
|
||||
local column = menu.getCol()
|
||||
if (main == 144) then
|
||||
if (column == 5) then
|
||||
menu.select(replaceIdx, true)
|
||||
else
|
||||
input.press("A")
|
||||
end
|
||||
elseif (main == 128) then
|
||||
if (column == 5) then
|
||||
menu.select(inventory.indexOf(item), "accelerate", true)
|
||||
elseif (column == 11) then
|
||||
menu.select(2, true)
|
||||
elseif (column == 14) then
|
||||
menu.select(0, true)
|
||||
end
|
||||
elseif (main == 103) then
|
||||
input.press("B")
|
||||
elseif (main == 64 or main == 96 or main == 192) then
|
||||
if (column == 5) then
|
||||
menu.select(replaceIdx, true)
|
||||
elseif (column == 14) then
|
||||
input.press("A")
|
||||
elseif (column == 15) then
|
||||
menu.select(0, true)
|
||||
else
|
||||
local idx = 0
|
||||
if (poke) then
|
||||
idx = pokemon.indexOf(poke, altPoke)
|
||||
end
|
||||
menu.select(idx, true)
|
||||
end
|
||||
else
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function inventory.isFull()
|
||||
return memory.raw(0xD345) > 0
|
||||
end
|
||||
|
||||
function inventory.use(item, poke, midfight)
|
||||
if (midfight) then
|
||||
local battleMenu = memory.value("battle", "menu")
|
||||
if (battleMenu == 94) then
|
||||
local rowSelected = memory.value("menu", "row")
|
||||
if (menu.getCol() == 9) then
|
||||
if (rowSelected == 0) then
|
||||
input.press("Down")
|
||||
else
|
||||
input.press("A")
|
||||
end
|
||||
else
|
||||
input.press("Left")
|
||||
end
|
||||
elseif (battleMenu == 233) then
|
||||
menu.select(inventory.indexOf(item), "accelerate", true)
|
||||
elseif (utils.onPokemonSelect(battleMenu)) then
|
||||
if (poke) then
|
||||
if (type(poke) == "string") then
|
||||
poke = pokemon.indexOf(poke)
|
||||
end
|
||||
menu.select(poke, true)
|
||||
else
|
||||
input.press("A")
|
||||
end
|
||||
else
|
||||
input.press("B")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local main = memory.value("menu", "main")
|
||||
local column = menu.getCol()
|
||||
if (main == 144) then
|
||||
if (memory.value("battle", "menu") == 95) then
|
||||
input.press("B")
|
||||
else
|
||||
local idx = 0
|
||||
if (poke) then
|
||||
idx = pokemon.indexOf(poke)
|
||||
end
|
||||
menu.select(idx, true)
|
||||
end
|
||||
elseif (main == 128 or main == 60) then
|
||||
if (column == 5) then
|
||||
menu.select(inventory.indexOf(item), "accelerate", true)
|
||||
elseif (column == 11) then
|
||||
menu.select(2, true)
|
||||
elseif (column == 14) then
|
||||
menu.select(0, true)
|
||||
else
|
||||
local index = 0
|
||||
if (poke) then
|
||||
index = pokemon.indexOf(poke)
|
||||
end
|
||||
menu.select(index, true)
|
||||
end
|
||||
elseif (main == 228) then
|
||||
if (column == 14 and memory.value("battle", "menu") == 95) then
|
||||
input.press("B")
|
||||
end
|
||||
elseif (main == 103) then
|
||||
input.press("B")
|
||||
else
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
return inventory
|
|
@ -0,0 +1,274 @@
|
|||
local pokemon = {}
|
||||
|
||||
local bridge = require "util.bridge"
|
||||
local input = require "util.input"
|
||||
local memory = require "util.memory"
|
||||
local menu = require "util.menu"
|
||||
local utils = require "util.utils"
|
||||
|
||||
local pokeIDs = {
|
||||
rhydon = 1,
|
||||
kangaskhan = 2,
|
||||
nidoran = 3,
|
||||
spearow = 5,
|
||||
voltorb = 6,
|
||||
nidoking = 7,
|
||||
ivysaur = 9,
|
||||
gengar = 14,
|
||||
nidoranf = 15,
|
||||
nidoqueen = 16,
|
||||
cubone = 17,
|
||||
rhyhorn = 18,
|
||||
gyarados = 22,
|
||||
growlithe = 33,
|
||||
onix = 34,
|
||||
pidgey = 36,
|
||||
jinx = 72,
|
||||
meowth = 77,
|
||||
pikachu = 84,
|
||||
zubat = 107,
|
||||
ekans = 108,
|
||||
paras = 109,
|
||||
weedle = 112,
|
||||
kakuna = 113,
|
||||
dewgong = 120,
|
||||
caterpie = 123,
|
||||
metapod = 124,
|
||||
hypno = 129,
|
||||
weezing = 143,
|
||||
alakazam = 149,
|
||||
pidgeotto = 150,
|
||||
pidgeot = 151,
|
||||
rattata = 165,
|
||||
raticate = 166,
|
||||
nidorino = 167,
|
||||
geodude = 169,
|
||||
squirtle = 177,
|
||||
oddish = 185,
|
||||
}
|
||||
|
||||
local moveList = {
|
||||
cut = 15,
|
||||
fly = 19,
|
||||
sand_attack = 28,
|
||||
horn_attack = 30,
|
||||
horn_drill = 32,
|
||||
tackle = 33,
|
||||
thrash = 37,
|
||||
tail_whip = 39,
|
||||
poison_sting = 40,
|
||||
leer = 43,
|
||||
growl = 45,
|
||||
water_gun = 55,
|
||||
surf = 57,
|
||||
ice_beam = 58,
|
||||
bubblebeam = 61,
|
||||
strength = 70,
|
||||
thunderbolt = 85,
|
||||
earthquake = 89,
|
||||
dig = 91,
|
||||
rock_slide = 157,
|
||||
}
|
||||
|
||||
local data = {
|
||||
hp = {1, true},
|
||||
status = {4},
|
||||
moves = {8},
|
||||
level = {33},
|
||||
max_hp = {34, true},
|
||||
|
||||
attack = {36, true},
|
||||
defense = {38, true},
|
||||
speed = {40, true},
|
||||
special = {42, true},
|
||||
}
|
||||
|
||||
local function getAddress(index)
|
||||
return 0xD16B + index * 0x2C
|
||||
end
|
||||
|
||||
local function index(index, offset)
|
||||
local double
|
||||
if (not offset) then
|
||||
offset = 0
|
||||
else
|
||||
local dataTable = data[offset]
|
||||
offset = dataTable[1]
|
||||
double = dataTable[2]
|
||||
end
|
||||
local address = getAddress(index) + offset
|
||||
local value = memory.raw(address)
|
||||
if (double) then
|
||||
value = value + memory.raw(address + 1)
|
||||
end
|
||||
return value
|
||||
end
|
||||
pokemon.index = index
|
||||
|
||||
local function indexOf(...)
|
||||
for ni,name in ipairs(arg) do
|
||||
local pid = pokeIDs[name]
|
||||
for i=0,5 do
|
||||
local atIdx = index(i)
|
||||
if (atIdx == pid) then
|
||||
return i
|
||||
end
|
||||
end
|
||||
end
|
||||
return -1
|
||||
end
|
||||
pokemon.indexOf = indexOf
|
||||
|
||||
-- Table functions
|
||||
|
||||
function pokemon.battleMove(name)
|
||||
local mid = moveList[name]
|
||||
for i=1,4 do
|
||||
if (mid == memory.raw(0xD01B + i)) then
|
||||
return i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pokemon.moveIndex(move, pokemon)
|
||||
local pokemonIdx
|
||||
if (pokemon) then
|
||||
pokemonIdx = indexOf(pokemon)
|
||||
else
|
||||
pokemonIdx = 0
|
||||
end
|
||||
local address = getAddress(pokemonIdx) + 7
|
||||
local mid = moveList[move]
|
||||
for i=1,4 do
|
||||
if (mid == memory.raw(address + i)) then
|
||||
return i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pokemon.info(name, offset)
|
||||
return index(indexOf(name), offset)
|
||||
end
|
||||
|
||||
function pokemon.getID(name)
|
||||
return pokeIDs[name]
|
||||
end
|
||||
|
||||
function pokemon.getName(id)
|
||||
for name,pid in pairs(pokeIDs) do
|
||||
if (pid == id) then
|
||||
return name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pokemon.inParty(...)
|
||||
for i,name in ipairs(arg) do
|
||||
if (indexOf(name) ~= -1) then
|
||||
return name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pokemon.forMove(move)
|
||||
local moveID = moveList[move]
|
||||
for i=0,5 do
|
||||
local address = getAddress(i)
|
||||
for j=8,11 do
|
||||
if (memory.raw(address + j) == moveID) then
|
||||
return i
|
||||
end
|
||||
end
|
||||
end
|
||||
return -1
|
||||
end
|
||||
|
||||
function pokemon.hasMove(move)
|
||||
return pokemon.forMove(move) ~= -1
|
||||
end
|
||||
|
||||
function pokemon.updateParty()
|
||||
local partySize = memory.value("player", "party_size")
|
||||
if (partySize ~= previousPartySize) then
|
||||
local poke = pokemon.inParty("oddish", "paras", "spearow", "pidgey", "nidoran", "squirtle")
|
||||
if (poke) then
|
||||
bridge.caught(poke)
|
||||
previousPartySize = partySize
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- General
|
||||
|
||||
function pokemon.isOpponent(...)
|
||||
local oid = memory.value("battle", "opponent_id")
|
||||
for i,name in ipairs(arg) do
|
||||
if (oid == pokeIDs[name]) then
|
||||
return name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pokemon.isDeployed(...)
|
||||
for i,name in ipairs(arg) do
|
||||
if (memory.value("battle", "our_id") == pokeIDs[name]) then
|
||||
return name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pokemon.isEvolving()
|
||||
return memory.value("menu", "pokemon") == 144
|
||||
end
|
||||
|
||||
function pokemon.getExp()
|
||||
return memory.raw(0xD17A) * 256 + memory.raw(0xD17B)
|
||||
end
|
||||
|
||||
function pokemon.inRedBar()
|
||||
local curr_hp, max_hp = index(0, "hp"), index(0, "max_hp")
|
||||
return curr_hp / max_hp <= 0.2
|
||||
end
|
||||
|
||||
function pokemon.use(move)
|
||||
local main = memory.value("menu", "main")
|
||||
local pokeName = pokemon.forMove(move)
|
||||
if (main == 141) then
|
||||
input.press("A")
|
||||
elseif (main == 128) then
|
||||
local column = menu.getCol()
|
||||
if (column == 11) then
|
||||
menu.select(1, true)
|
||||
elseif (column == 10 or column == 12) then
|
||||
local midx = 0
|
||||
local menuSize = memory.value("menu", "size")
|
||||
if (menuSize == 4) then
|
||||
if (move == "dig") then
|
||||
midx = 1
|
||||
elseif (move == "surf") then
|
||||
if (pokemon.inParty("paras")) then
|
||||
midx = 1
|
||||
end
|
||||
end
|
||||
elseif (menuSize == 5) then
|
||||
if (move == "dig") then
|
||||
midx = 2
|
||||
elseif (move == "surf") then
|
||||
midx = 1
|
||||
end
|
||||
end
|
||||
menu.select(midx, true)
|
||||
else
|
||||
input.press("B")
|
||||
end
|
||||
elseif (main == 103) then
|
||||
menu.select(pokeName, true)
|
||||
elseif (main == 228) then
|
||||
input.press("B")
|
||||
else
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
return pokemon
|
|
@ -0,0 +1,132 @@
|
|||
local bridge = {}
|
||||
|
||||
local utils = require("util.utils")
|
||||
|
||||
local client = nil
|
||||
local timeStopped = false
|
||||
|
||||
local function send(prefix, body)
|
||||
if (client) then
|
||||
local message = prefix
|
||||
if (body) then
|
||||
message = message..","..body
|
||||
end
|
||||
client:send(message..'\n')
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local function readln()
|
||||
if (client) then
|
||||
local s, status, partial = client:receive('*l')
|
||||
if status == "closed" then
|
||||
client = nil
|
||||
return nil
|
||||
end
|
||||
if s and s ~= '' then
|
||||
return s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Wrapper functions
|
||||
|
||||
function bridge.init()
|
||||
end
|
||||
|
||||
function bridge.tweet(message) -- Two of the same tweet in a row will only send one
|
||||
print('tweet::'..message)
|
||||
return send("tweet", message)
|
||||
end
|
||||
|
||||
function bridge.pollForName()
|
||||
bridge.polling = true
|
||||
send("poll_name")
|
||||
end
|
||||
|
||||
function bridge.chat(message, extra)
|
||||
if (extra) then
|
||||
print(message.." || "..extra)
|
||||
else
|
||||
print(message)
|
||||
end
|
||||
return send("msg", message)
|
||||
end
|
||||
|
||||
function bridge.time(message)
|
||||
if (not timeStopped) then
|
||||
return send("time", message)
|
||||
end
|
||||
end
|
||||
|
||||
function bridge.stats(message)
|
||||
return send("stats", message)
|
||||
end
|
||||
|
||||
function bridge.command(command)
|
||||
return send("livesplit_command", command)
|
||||
end
|
||||
|
||||
function bridge.comparisonTime()
|
||||
return send("livesplit_getcomparisontime")
|
||||
end
|
||||
|
||||
function bridge.process()
|
||||
local response = readln()
|
||||
if (response) then
|
||||
-- print('>'..response)
|
||||
if (response:find("name:")) then
|
||||
return response:gsub("name:", "")
|
||||
else
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function bridge.input(key)
|
||||
send("input", key)
|
||||
end
|
||||
|
||||
function bridge.caught(name)
|
||||
if (name) then
|
||||
send("caught", name)
|
||||
end
|
||||
end
|
||||
|
||||
function bridge.hp(curr, max)
|
||||
send("hp", curr..","..max)
|
||||
end
|
||||
|
||||
function bridge.liveSplit()
|
||||
send("start")
|
||||
timeStopped = false
|
||||
end
|
||||
|
||||
function bridge.split(encounters, finished)
|
||||
if (encounters) then
|
||||
-- database.split(utils.igt(), encounters)
|
||||
end
|
||||
if (finished) then
|
||||
timeStopped = true
|
||||
end
|
||||
send("split")
|
||||
end
|
||||
|
||||
function bridge.encounter()
|
||||
send("encounter")
|
||||
end
|
||||
|
||||
function bridge.reset()
|
||||
send("reset")
|
||||
timeStopped = false
|
||||
end
|
||||
|
||||
function bridge.close()
|
||||
if client then
|
||||
client:close()
|
||||
client = nil
|
||||
end
|
||||
print("Bridge closed")
|
||||
end
|
||||
|
||||
return bridge
|
|
@ -0,0 +1,119 @@
|
|||
local input = {}
|
||||
|
||||
local bridge = require "util.bridge"
|
||||
local memory = require "util.memory"
|
||||
|
||||
local lastSend
|
||||
local currentButton, remainingFrames, setForFrame
|
||||
local debug
|
||||
local bCancel = true
|
||||
|
||||
local function bridgeButton(btn)
|
||||
if (btn ~= lastSend) then
|
||||
lastSend = btn
|
||||
bridge.input(btn)
|
||||
end
|
||||
end
|
||||
|
||||
local function sendButton(button, ab)
|
||||
local inputTable = {[button] = true}
|
||||
joypad.set(inputTable)
|
||||
if (debug) then
|
||||
gui.text(0, 7, button.." "..remainingFrames)
|
||||
end
|
||||
if (ab) then
|
||||
button = "AB"
|
||||
end
|
||||
bridgeButton(button)
|
||||
setForFrame = button
|
||||
end
|
||||
|
||||
function input.press(button, frames)
|
||||
if (setForFrame) then
|
||||
print("ERR: Reassigning "..setForFrame.." to "..button)
|
||||
return
|
||||
end
|
||||
if (frames == nil or frames > 0) then
|
||||
if (button == currentButton) then
|
||||
return
|
||||
end
|
||||
if (not frames) then
|
||||
frames = 1
|
||||
end
|
||||
currentButton = button
|
||||
remainingFrames = frames
|
||||
else
|
||||
remainingFrames = 0
|
||||
end
|
||||
bCancel = button ~= "B"
|
||||
sendButton(button)
|
||||
end
|
||||
|
||||
function input.cancel(accept)
|
||||
if (accept and memory.value("menu", "shop_current") == 20) then
|
||||
input.press(accept)
|
||||
else
|
||||
local button
|
||||
if (bCancel) then
|
||||
button = "B"
|
||||
else
|
||||
button = "A"
|
||||
end
|
||||
remainingFrames = 0
|
||||
sendButton(button, true)
|
||||
bCancel = not bCancel
|
||||
end
|
||||
end
|
||||
|
||||
function input.escape()
|
||||
local inputTable = {Right=true, Down=true}
|
||||
joypad.set(inputTable)
|
||||
bridgeButton("Escape")
|
||||
end
|
||||
|
||||
function input.clear()
|
||||
currentButton = nil
|
||||
remainingFrames = -1
|
||||
end
|
||||
|
||||
function input.update()
|
||||
if (currentButton) then
|
||||
remainingFrames = remainingFrames - 1
|
||||
if (remainingFrames >= 0) then
|
||||
if (remainingFrames > 0) then
|
||||
sendButton(currentButton)
|
||||
return true
|
||||
end
|
||||
else
|
||||
currentButton = nil
|
||||
end
|
||||
end
|
||||
setForFrame = nil
|
||||
end
|
||||
|
||||
function input.advance()
|
||||
if (not setForFrame) then
|
||||
bridgeButton("e")
|
||||
-- print("e")
|
||||
end
|
||||
end
|
||||
|
||||
function input.setDebug(enabled)
|
||||
debug = enabled
|
||||
end
|
||||
|
||||
function input.test(fn, completes)
|
||||
while (true) do
|
||||
if (not input.update()) then
|
||||
if (fn() and completes) then
|
||||
break
|
||||
end
|
||||
end
|
||||
emu.frameadvance()
|
||||
end
|
||||
if (completes) then
|
||||
print(completes.." complete!")
|
||||
end
|
||||
end
|
||||
|
||||
return input
|
|
@ -0,0 +1,138 @@
|
|||
local memory = {}
|
||||
|
||||
local memoryNames = {
|
||||
setting = {
|
||||
text_speed = 0x0D3D,
|
||||
battle_animation = 0x0D3E,
|
||||
battle_style = 0x0D3F,
|
||||
yellow_bitmask = 0x1354,
|
||||
},
|
||||
menu = {
|
||||
settings_row = 0x0C24,
|
||||
column = 0x0C25,
|
||||
row = 0x0C26,
|
||||
current = 0x1FFC,
|
||||
main_current = 0x0C27,
|
||||
input_row = 0x0C2A,
|
||||
size = 0x0C28,
|
||||
pokemon = 0x0C51,
|
||||
shop_current = 0x0C52,
|
||||
transaction_current = 0x0F8B,
|
||||
selection = 0x0C30,
|
||||
selection_mode = 0x0C35,
|
||||
scroll_offset = 0x0C36,
|
||||
text_input = 0x04B6,
|
||||
text_length = 0x0EE9,
|
||||
main = 0x1FF5,
|
||||
},
|
||||
player = {
|
||||
name = 0xD158,
|
||||
name2 = 0xD159,
|
||||
moving = 0x1528,
|
||||
x = 0xD362,
|
||||
y = 0xD361,
|
||||
facing = 0x152A,
|
||||
repel = 0x10DB,
|
||||
party_size = 0xD163,
|
||||
},
|
||||
game = {
|
||||
map = 0xD35E,
|
||||
frames = 0xDA45,
|
||||
battle = 0xD057,
|
||||
textbox = 0x0FC4,
|
||||
},
|
||||
shop = {
|
||||
transaction_amount = 0x0F96,
|
||||
},
|
||||
progress = {
|
||||
trashcans = 0x1773,
|
||||
},
|
||||
pokemon = {
|
||||
exp1 = 0xD179,
|
||||
exp2 = 0xD17A,
|
||||
exp3 = 0xD17B,
|
||||
},
|
||||
battle = {
|
||||
confused = 0x106B,
|
||||
turns = 0x1067,
|
||||
text = 0x1125,
|
||||
menu = 0x0C50,
|
||||
accuracy = 0x0D1E,
|
||||
x_accuracy = 0x1063,
|
||||
disabled = 0x0CEE,
|
||||
paralyzed = 0x1018,
|
||||
|
||||
opponent_move = 0x0FEE,
|
||||
critical = 0x105E,
|
||||
|
||||
opponent_bide = 0x106F,
|
||||
opponent_id = 0xCFE5,
|
||||
opponent_level = 0xCFF3,
|
||||
opponent_type1 = 0xCFEA,
|
||||
opponent_type2 = 0xCFEB,
|
||||
|
||||
our_id = 0xD014,
|
||||
our_status = 0xD018,
|
||||
our_level = 0xD022,
|
||||
our_type1 = 0xD019,
|
||||
our_type2 = 0xD01A,
|
||||
},
|
||||
}
|
||||
|
||||
local doubleNames = {
|
||||
pokemon = {
|
||||
attack = 0xD17E,
|
||||
defense = 0xD181,
|
||||
speed = 0xD183,
|
||||
special = 0xD185,
|
||||
},
|
||||
battle = {
|
||||
opponent_hp = 0xCFE6,
|
||||
opponent_max_hp = 0xCFF4,
|
||||
opponent_attack = 0xCFF6,
|
||||
opponent_defense = 0xCFF8,
|
||||
opponent_speed = 0xCFFA,
|
||||
opponent_special = 0xCFFC,
|
||||
|
||||
our_hp = 0xD015,
|
||||
our_max_hp = 0xD023,
|
||||
our_attack = 0xD025,
|
||||
our_defense = 0xD027,
|
||||
our_speed = 0xD029,
|
||||
our_special = 0xD02B,
|
||||
},
|
||||
}
|
||||
|
||||
local function raw(value)
|
||||
return mainmemory.readbyte(value)
|
||||
end
|
||||
memory.raw = raw
|
||||
|
||||
function memory.string(first, last)
|
||||
local a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ():;[]abcdefghijklmnopqrstuvwxyz?????????????????????????????????????????-???!.????????*?/.?0123456789"
|
||||
local str = ""
|
||||
while first <= last do
|
||||
local v = raw(first) - 127
|
||||
if v < 1 then
|
||||
return str
|
||||
end
|
||||
str = str..string.sub(a, v, v)
|
||||
first = first + 1
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
function memory.double(section, key)
|
||||
local first = doubleNames[section][key]
|
||||
return raw(first) + raw(first + 1)
|
||||
end
|
||||
|
||||
function memory.value(section, key)
|
||||
local memoryAddress = memoryNames[section]
|
||||
if (key) then
|
||||
memoryAddress = memoryAddress[key]
|
||||
end
|
||||
return raw(memoryAddress)
|
||||
end
|
||||
|
||||
return memory
|
|
@ -0,0 +1,193 @@
|
|||
local menu = {}
|
||||
|
||||
local input = require "util.input"
|
||||
local memory = require "util.memory"
|
||||
|
||||
local YELLOW = GAME_NAME == "yellow"
|
||||
|
||||
local sliding = false
|
||||
|
||||
-- Private functions
|
||||
|
||||
local function getRow(menuType, scrolls)
|
||||
if (menuType and menuType == "settings") then
|
||||
menuType = menuType.."_row"
|
||||
else
|
||||
menuType = "row"
|
||||
end
|
||||
local row = memory.value("menu", menuType)
|
||||
if (scrolls) then
|
||||
row = row + memory.value("menu", "scroll_offset")
|
||||
end
|
||||
return row
|
||||
end
|
||||
|
||||
local function setRow(desired, throttle, scrolls, menuType, loop)
|
||||
local currentRow = getRow(menuType, scrolls)
|
||||
if (throttle == "accelerate") then
|
||||
if (sliding) then
|
||||
throttle = false
|
||||
else
|
||||
local dist = math.abs(desired - currentRow)
|
||||
if (dist < 15) then
|
||||
throttle = true
|
||||
else
|
||||
throttle = false
|
||||
sliding = true
|
||||
end
|
||||
end
|
||||
else
|
||||
sliding = false
|
||||
end
|
||||
return menu.balance(currentRow, desired, true, loop, throttle)
|
||||
end
|
||||
|
||||
local function isCurrently(desired, menuType)
|
||||
if (menuType) then
|
||||
menuType = menuType.."_current"
|
||||
else
|
||||
menuType = "current"
|
||||
end
|
||||
return memory.value("menu", menuType) == desired
|
||||
end
|
||||
menu.isCurrently = isCurrently
|
||||
|
||||
-- Menu
|
||||
|
||||
function menu.getCol()
|
||||
return memory.value("menu", "column")
|
||||
end
|
||||
|
||||
function menu.open(desired, atIndex, menuType)
|
||||
if (isCurrently(desired, menuType)) then
|
||||
return true
|
||||
end
|
||||
menu.select(atIndex, false, false, menuType)
|
||||
return false
|
||||
end
|
||||
|
||||
function menu.select(option, throttle, scrolls, menuType, dontPress, loop)
|
||||
if (setRow(option, throttle, scrolls, menuType, loop)) then
|
||||
local delay = 1
|
||||
if (throttle) then
|
||||
delay = 2
|
||||
end
|
||||
if (not dontPress) then
|
||||
input.press("A", delay)
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function menu.cancel(desired, menuType)
|
||||
if (not isCurrently(desired, menuType)) then
|
||||
return true
|
||||
end
|
||||
input.press("B")
|
||||
return false
|
||||
end
|
||||
|
||||
-- Selections
|
||||
|
||||
function menu.balance(current, desired, inverted, looping, throttle)
|
||||
if (current == desired) then
|
||||
sliding = false
|
||||
return true
|
||||
end
|
||||
if (not throttle) then
|
||||
throttle = 0
|
||||
else
|
||||
throttle = 1
|
||||
end
|
||||
local goUp = current > desired == inverted
|
||||
if (looping and math.abs(current - desired) > math.floor(looping / 2)) then
|
||||
goUp = not goUp
|
||||
end
|
||||
if (goUp) then
|
||||
input.press("Up", throttle)
|
||||
else
|
||||
input.press("Down", throttle)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function menu.sidle(current, desired, looping, throttle)
|
||||
if (current == desired) then
|
||||
return true
|
||||
end
|
||||
if (not throttle) then
|
||||
throttle = 0
|
||||
else
|
||||
throttle = 1
|
||||
end
|
||||
local goLeft = current > desired
|
||||
if (looping and math.abs(current - desired) > math.floor(looping / 2)) then
|
||||
goLeft = not goLeft
|
||||
end
|
||||
if (goLeft) then
|
||||
input.press("Left", throttle)
|
||||
else
|
||||
input.press("Right", throttle)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function menu.setCol(desired)
|
||||
return menu.sidle(menu.getCol(), desired)
|
||||
end
|
||||
|
||||
-- Options
|
||||
|
||||
function menu.setOption(name, desired)
|
||||
if (YELLOW) then
|
||||
local rowFor = {
|
||||
text_speed = 0,
|
||||
battle_animation = 1,
|
||||
battle_style = 2
|
||||
}
|
||||
local currentRow = memory.raw(0x0D3D)
|
||||
if (menu.balance(currentRow, rowFor[name], true, false, true)) then
|
||||
input.press("Left")
|
||||
end
|
||||
else
|
||||
local rowFor = {
|
||||
text_speed = 3,
|
||||
battle_animation = 8,
|
||||
battle_style = 13
|
||||
}
|
||||
if (memory.value("setting", name) == desired) then
|
||||
return true
|
||||
end
|
||||
if (setRow(rowFor[name], true, false, "settings")) then
|
||||
menu.setCol(desired)
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Pause menu
|
||||
|
||||
function menu.isOpen()
|
||||
return memory.value("game", "textbox") == 1 or memory.value("menu", "current") == 24
|
||||
end
|
||||
|
||||
function menu.close()
|
||||
if (memory.value("game", "textbox") == 0 and memory.value("menu", "main") < 8) then
|
||||
return true
|
||||
end
|
||||
input.press("B")
|
||||
end
|
||||
|
||||
function menu.pause()
|
||||
if (memory.value("game", "textbox") == 1) then
|
||||
local main = memory.value("menu", "main")
|
||||
if (main > 2 and main ~= 64) then
|
||||
return true
|
||||
end
|
||||
input.press("B")
|
||||
else
|
||||
input.press("Start", 2)
|
||||
end
|
||||
end
|
||||
|
||||
return menu
|
|
@ -0,0 +1,68 @@
|
|||
local paint = {}
|
||||
|
||||
local memory = require "util.memory"
|
||||
local player = require "util.player"
|
||||
|
||||
local inventory = require "storage.inventory"
|
||||
local pokemon = require "storage.pokemon"
|
||||
|
||||
local encounters = 0
|
||||
|
||||
function elapsedTime()
|
||||
local secs = memory.raw(0xDA44)
|
||||
if (secs < 10) then
|
||||
secs = "0"..secs
|
||||
end
|
||||
local mins = memory.raw(0xDA43)
|
||||
if (mins < 10) then
|
||||
mins = "0"..mins
|
||||
end
|
||||
return memory.raw(0xDA41)..":"..mins..":"..secs
|
||||
end
|
||||
paint.elapsedTime = elapsedTime
|
||||
|
||||
function paint.draw(currentMap)
|
||||
local px, py = player.position()
|
||||
gui.text(0, 14, currentMap..": "..px.." "..py)
|
||||
gui.text(0, 0, elapsedTime())
|
||||
|
||||
if (memory.value("battle", "our_id") > 0) then
|
||||
local hp = pokemon.index(0, "hp")
|
||||
local hpStatus
|
||||
if (hp == 0) then
|
||||
hpStatus = "DEAD"
|
||||
elseif (hp <= math.ceil(pokemon.index(0, "max_hp") * 0.2)) then
|
||||
hpStatus = "RED"
|
||||
end
|
||||
if (hpStatus) then
|
||||
gui.text(120, 7, hpStatus)
|
||||
end
|
||||
end
|
||||
|
||||
local nidx = pokemon.indexOf("nidoran", "nidorino", "nidoking")
|
||||
if (nidx ~= -1) 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")
|
||||
gui.text(100, 0, att.." "..def.." "..spd.." "..scl)
|
||||
end
|
||||
local enc = " encounter"
|
||||
if (encounters ~= 1) then
|
||||
enc = enc.."s"
|
||||
end
|
||||
gui.text(0, 116, memory.value("battle", "critical"))
|
||||
gui.text(0, 125, memory.value("player", "repel"))
|
||||
gui.text(0, 134, encounters..enc)
|
||||
return true
|
||||
end
|
||||
|
||||
function paint.wildEncounters(count)
|
||||
encounters = count
|
||||
end
|
||||
|
||||
function paint.reset()
|
||||
encounters = 0
|
||||
end
|
||||
|
||||
return paint
|
|
@ -0,0 +1,38 @@
|
|||
local player = {}
|
||||
|
||||
local textbox = require "action.textbox"
|
||||
|
||||
local input = require "util.input"
|
||||
local memory = require "util.memory"
|
||||
|
||||
local facingDirections = {Up=8, Right=1, Left=2, Down=4}
|
||||
|
||||
function player.isFacing(direction)
|
||||
return memory.value("player", "facing") == facingDirections[direction]
|
||||
end
|
||||
|
||||
function player.face(direction)
|
||||
if (player.isFacing(direction)) then
|
||||
return true
|
||||
end
|
||||
if (textbox.handle()) then
|
||||
input.press(direction, 0)
|
||||
end
|
||||
end
|
||||
|
||||
function player.interact(direction)
|
||||
if (player.face(direction)) then
|
||||
input.press("A", 2)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function player.isMoving()
|
||||
return memory.value("player", "moving") ~= 0
|
||||
end
|
||||
|
||||
function player.position()
|
||||
return memory.value("player", "x"), memory.value("player", "y")
|
||||
end
|
||||
|
||||
return player
|
|
@ -0,0 +1,52 @@
|
|||
local settings = {}
|
||||
|
||||
local memory = require "util.memory"
|
||||
local menu = require "util.menu"
|
||||
|
||||
local YELLOW = GAME_NAME == "yellow"
|
||||
|
||||
local settings_menu
|
||||
if (YELLOW) then
|
||||
settings_menu = 93
|
||||
else
|
||||
settings_menu = 94
|
||||
end
|
||||
|
||||
local desired = {}
|
||||
if (YELLOW) then
|
||||
desired.text_speed = 1
|
||||
desired.battle_animation = 128
|
||||
desired.battle_style = 64
|
||||
else
|
||||
desired.text_speed = 1
|
||||
desired.battle_animation = 10
|
||||
-- desired.battle_style =
|
||||
end
|
||||
|
||||
local function isEnabled(name)
|
||||
if (YELLOW) then
|
||||
local matching = {
|
||||
text_speed = 0xF,
|
||||
battle_animation = 128,
|
||||
battle_style = 64
|
||||
}
|
||||
local settingMask = memory.value("setting", "yellow_bitmask")
|
||||
return bit.band(settingMask, matching[name]) == desired[name]
|
||||
else
|
||||
return memory.value("setting", name) == desired[name]
|
||||
end
|
||||
end
|
||||
|
||||
function settings.set(...)
|
||||
for i,name in ipairs(arg) do
|
||||
if (not isEnabled(name)) then
|
||||
if (menu.open(settings_menu, 1)) then
|
||||
menu.setOption(name, desired[name])
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
return menu.cancel(settings_menu)
|
||||
end
|
||||
|
||||
return settings
|
|
@ -0,0 +1,57 @@
|
|||
local utils = {}
|
||||
|
||||
local memory = require "util.memory"
|
||||
|
||||
function utils.dist(x1, y1, x2, y2)
|
||||
return math.sqrt(math.pow(x2 - x1, 2) + math.pow(y2 - y1, 2))
|
||||
end
|
||||
|
||||
function utils.ingame()
|
||||
return memory.raw(0x020E) > 0
|
||||
end
|
||||
|
||||
function utils.each(table, func)
|
||||
for key,val in pairs(table) do
|
||||
func(key.." = "..tostring(val)..",")
|
||||
end
|
||||
end
|
||||
|
||||
function utils.eachi(table, func)
|
||||
for idx,val in ipairs(table) do
|
||||
if (val) then
|
||||
func(idx.." "..val)
|
||||
else
|
||||
func(idx)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function utils.match(needle, haystack)
|
||||
for i,val in ipairs(haystack) do
|
||||
if (needle == val) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function utils.key(needle, haystack)
|
||||
for key,val in pairs(haystack) do
|
||||
if (needle == val) then
|
||||
return key
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function utils.igt()
|
||||
local secs = memory.raw(0xDA44)
|
||||
local mins = memory.raw(0xDA43)
|
||||
local hours = memory.raw(0xDA41)
|
||||
return secs + mins * 60 + hours * 3600
|
||||
end
|
||||
|
||||
|
||||
function utils.onPokemonSelect(battleMenu)
|
||||
return battleMenu == 8 or battleMenu == 48 or battleMenu == 184 or battleMenu == 224
|
||||
end
|
||||
|
||||
return utils
|
Loading…
Reference in New Issue