First Commit

Files are not even cleaned, its the really first commit, not done yet,
beta or even alpha version.
This commit is contained in:
bouletmarc 2015-05-24 18:39:11 -04:00
parent add481dc9a
commit 1df131ce79
24 changed files with 7038 additions and 0 deletions

45
Readme.md Normal file
View File

@ -0,0 +1,45 @@
# PokéBot Generation II
An automated computer program that speedruns Pokémon Cristal (gold/silver not made yet).
### Run the bot locally
Running the PokéBot on your own machine is easy. You will need a Windows environment (it runs great in VMs on Mac/Linux too).
1. First, clone this repository (or download and unzip it) to your computer.
2. Download the [BizHawk 1.6.1](http://sourceforge.net/projects/bizhawk/files/BizHawk/BizHawk-1.6.1.zip/download) emulator and extract the ZIP file anywhere you like to “install” it.
**Note:** BizHawk v1.6.1 (Windows only) is the only version known to work. Later versions like v1.7.2a do not seem to work, due to differences with reading bytes from memory.
3. Run [the BizHawk prerequisites installer](http://sourceforge.net/projects/bizhawk/files/Prerequisites/bizhawk_prereqs_v1.1.zip/download), which should update a C++ distributable needed by BizHawk.
4. Procure a ROM file of Pokémon Crystal (not yet for gold/silver) english version (japan might not work and you should personally own the game).
5. Open the ROM file with BizHawk (drag the `.gb` file onto EmuHawk), and Pokémon should start up. Otherwise select Open ROM in EmuHawk.
6. The colors may look weird. To fix this, go to _GB__Palette Editor_, and then find the `POKEMON ***.pal` file which should be under _Gameboy__Palettes_ in the directory where BizHawk was extracted.
7. If you want to test the full run, set [`RESET_FOR_TIME` in `main.lua`](https://github.com/kylecoburn/PokeBot/blob/0fd1258ca17f7d74edbac72fa0afc2b5c6d58bb3/main.lua#L3) to `false` instead of `true`.
8. 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](Seeds.md). 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
Michael Jondahl: Combat algorithm, Java bridge for connecting the bot to Twitch chat, LiveSplit, Twitter, etc.
Marc-Andre Boulet(Bouletmarc): PokeBot GenerationII
### 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.

296
action/battle.lua Normal file
View File

@ -0,0 +1,296 @@
local Battle = {}
local Textbox = require "action.textbox"
local Combat = require "ai.combat"
local Control = require "ai.control"
local Memory = require "util.memory"
local Menu = require "util.menu"
local Input = require "util.input"
local Utils = require "util.utils"
local Inventory = require "storage.inventory"
local Pokemon = require "storage.pokemon"
-- HELPERS
local function potionsForHit(potion, curr_hp, max_hp)
if not potion then
return
end
local ours, killAmount = Combat.inKillRange()
if ours then
return Utils.canPotionWith(potion, killAmount, curr_hp, max_hp)
end
end
local function recover()
if Control.canRecover() then
local currentHP = Pokemon.index(0, "hp")
if currentHP > 0 then
local maxHP = Pokemon.index(0, "max_hp")
if currentHP < maxHP then
local first, second
if 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
if Memory.value("battle", "text") == 3 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
if battleMenu == 106 then
return true
--elseif battleMenu == 94 then
elseif battleMenu == 186 then
local rowSelected = Memory.value("battle", "menuY")
local columnSelected = Memory.value("battle", "menuX")
if columnSelected == 0 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)
Menu.select(attackIndex, true, false, false, 3)
end
end
function movePP(name)
local midx = Pokemon.battleMove(name)
if not midx then
return 0
end
--return Memory.raw(0x102C + midx)
return Memory.raw(0x0634 + midx)
end
Battle.pp = movePP
-- UTILS
--[[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.handle()
else
Textbox.handle()
end
end
function Battle.opponent()
return Pokemon.getName(Memory.value("battle", "opponent_id"))
end
-- HANDLE
function Battle.run()
if Memory.double("battle", "opponent_hp") < 1 then
Input.cancel()
--elseif Memory.value("battle", "menu") ~= 94 then
elseif Memory.value("battle", "menu") ~= 186 then
--if Memory.value("menu", "text_length") == 127 then
-- Input.press("B")
--else
Input.press("B", 2)
--Input.cancel()
--end
elseif Memory.value("battle", "menu") == 186 then
--elseif Textbox.handle() then
--local rowSelected = Memory.value("battle", "menuY")
--local columnSelected = Memory.value("battle", "menuX")
--local selected = Memory.value("menu", "selection")
--if selected == 239 then
--if rowSelected == 2 and columnSelected == 2 then
-- Input.press("A", 2)
--else
Input.escape()
--end
end
end
function Battle.handle()
--if not Control.shouldCatch() then
--if Control.shouldFight() then
-- Battle.fight()
--else
Battle.run()
--end
--end
end
function Battle.handleWild()
if Memory.value("game", "battle") ~= 1 then
return true
end
Battle.handle()
end
function Battle.fight(move, skipBuffs)
if move then
if type(move) ~= "number" then
move = Pokemon.battleMove(move)
end
attack(move)
else
move = Combat.bestMove()
if move then
--Battle.accurateAttack = move.accuracy == 100
attack(move.midx)
--elseif Memory.value("menu", "text_length") == 127 then
-- Input.press("B")
else
Input.cancel()
end
end
end
--[[function Battle.swap(target)
local battleMenu = Memory.value("battle", "menu")
if 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 Battle.automate(moveName, skipBuffs)
--if not recover() then
local state = Memory.value("game", "battle")
if state == 0 then
Input.cancel()
else
if moveName and movePP(moveName) == 0 then
moveName = nil
end
if state == 1 then
--if Control.shouldFight() then
-- Battle.fight(moveName, skipBuffs)
--else
Battle.run()
--end
elseif state == 2 then
Battle.fight(moveName, skipBuffs)
end
end
--end
end
-- SACRIFICE
--[[function Battle.sacrifice(...)
local sacrifice = Pokemon.getSacrifice(...)
if sacrifice then
Battle.swap(sacrifice)
return true
end
return false
end
function Battle.redeployNidoking()
if Pokemon.isDeployed("nidoking") then
return false
end
local battleMenu = Memory.value("battle", "menu")
if Utils.onPokemonSelect(battleMenu) then
Menu.select(0, true)
elseif battleMenu == 95 and Menu.getCol() == 1 then
Input.press("A")
else
local __, turns = Combat.bestMove()
if turns == 1 then
if Pokemon.isDeployed("spearow") then
forced = "growl"
else
forced = "sand_attack"
end
end
Battle.automate(forced)
end
return true
end]]
return Battle

109
action/shop.lua Normal file
View File

@ -0,0 +1,109 @@
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"
--local yellow = YELLOW
--[[function Shop.transaction(options)
local item, itemMenu, menuIdx, quantityMenu
if options.sell then
menuIdx = 1
itemMenu = yellow and 28 or 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 = yellow and 122 or 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
local mainMenu = yellow and 245 or 32
if Menu.isCurrently(mainMenu, "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

231
action/textbox.lua Normal file
View File

@ -0,0 +1,231 @@
local Textbox = {}
local Input = require "util.input"
local Memory = require "util.memory"
local Menu = require "util.menu"
--local alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ *():;[]ab-?!mf/.,"
local alphabet_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ -?1/., "
local alphabet_lower = "abcdefghijklmnopqrstuvw<yz x():;[]{}"
-- < = special X
-- { = Pk
-- } = mon
local nidoName = "A"
local nidoIdx = 1
local TableNumber = 1
local ActualUpper = 1
--[[local function getLetterAt(index)
return alphabet:sub(index, index)
end]]
local function getIndexForLetter(letter, Caps)
if Caps then
return alphabet_upper:find(letter, 1, true)
else
return alphabet_lower:find(letter, 1, true)
end
end
function Textbox.name(letter, randomize)
local inputting = false
--if letter ~= TOTODILE_NAME and Memory.value("menu", "current") == 231 then --french
if letter ~= TOTODILE_NAME and Memory.value("menu", "current") == 232 then --english
inputting = true
elseif letter == TOTODILE_NAME and Memory.value("menu", "option_current") == 17 then
inputting = true
end
if inputting then
-- Values
local lidx
local crow
local drow
local ccol
local dcol
local NameTable = {}
local Waiting
--if letter then
--RUNNING4NEWGAME = false --make sure it's not running if we begin a game
local StringLenght = string.len(letter)
letter:gsub(".",function(letter2)
table.insert(NameTable,letter2)
if NameTable[TableNumber] then
local GetUpper = true
--Set Special Chars & Get UpperCase
--[[if NameTable[TableNumber] == "<" then
GetUpper = false
lidx = 28
elseif NameTable[TableNumber] == "{" then
GetUpper = false
lidx = 35
elseif NameTable[TableNumber] == "}" then
GetUpper = false
lidx = 36
else]]
--its a letter
if string.match(NameTable[TableNumber], '%a') then
if string.match(NameTable[TableNumber], '%u') then
--the letter was uppercase
GetUpper = true
elseif string.match(NameTable[TableNumber], '%l') then
--the letter was lowercase
GetUpper = false
end
--its anything but not a letter
else
if string.find(alphabet_upper, NameTable[TableNumber]) ~= nil then
GetUpper = true
elseif string.find(alphabet_lower, NameTable[TableNumber]) ~= nil then
GetUpper = false
end
end
lidx = getIndexForLetter(NameTable[TableNumber], GetUpper)
--lidx = getIndexForLetter(string.upper(NameTable[TableNumber]))
--end
--Check For Waiting
local Waiting = Input.isWaiting()
--Proceed
if not Waiting then
--Get/set Lower/Upper
if GetUpper == false and ActualUpper == 1 or GetUpper ~= false and ActualUpper == 0 then
crow = Memory.value("text_inputing", "row")
if crow ~= 4 then
Input.press("Down", 2)
elseif crow == 4 then
ccol = Memory.value("text_inputing", "column")
if ccol <= 2 then
Input.press("A", 2)
if ActualUpper == 1 then
ActualUpper = 0
elseif ActualUpper == 0 then
ActualUpper = 1
end
elseif ccol > 2 then
Input.press("Left", 2)
end
end
--Get/Set Letter
else
--local LineIndex = math.ceil(lidx/9)
crow = Memory.value("text_inputing", "row")
drow = math.ceil(lidx/9)-1
if crow < drow then
Input.press("Down", 2)
elseif crow > drow then
Input.press("Up", 2)
elseif crow == drow then
ccol = Memory.value("text_inputing", "column")
dcol = math.fmod(lidx - 1, 9)
if ccol < dcol then
Input.press("Right", 2)
elseif ccol > dcol then
Input.press("Left", 2)
elseif ccol == dcol then
--if Menu.sidle(ccol, dcol, 8, true) then
Input.press("A", 2)
if Memory.value("menu", "text_length") == TableNumber then
TableNumber = TableNumber + 1
end
end
end
end
end
end
end)
local Waiting = Input.isWaiting()
if TableNumber > StringLenght and not Waiting then
if Memory.value("menu", "text_length") > 0 then
--get column/row
crow = Memory.value("text_inputing", "row")
ccol = Memory.value("text_inputing", "column")
if crow ~= 4 then
Input.press("Start", 2)
elseif ccol < 6 then
Input.press("Start", 2)
elseif crow == 4 and ccol >= 6 then
Input.press("A", 2)
TableNumber = 1
ActualUpper = 1
NameTable = {}
return true
end
end
end
--[[else
if Memory.value("menu", "text_length") > 0 then
Input.press("Start")
return true
end
lidx = nidoIdx
crow = Memory.value("menu", "input_row")
drow = math.ceil(lidx / 9)
if Menu.balance(crow, drow, true, 6, true) then
ccol = math.floor(Memory.value("menu", "column") / 2)
dcol = math.fmod(lidx - 1, 9)
if Menu.sidle(ccol, dcol, 9, true) then
Input.press("A")
end
end]]
--end
else
--Reset Values
TableNumber = 1
ActualUpper = 1
NameTable = {}
--if Memory.raw(0x10B7) == 3 then
-- Input.press("A", 2)
--elseif randomize then
if randomize then
Input.press("A", math.random(1, 5))
else
Input.press("A", 2)
--Input.cancel()
end
end
end
--[[function Textbox.getName()
if nidoName == "a" then
return ""
end
if nidoName == "b" then
return ""
end
if nidoName == "m" then
return ""
end
if nidoName == "f" then
return ""
end
return nidoName
end]]
--[[function Textbox.setName(index)
if index >= 0 and index < #alphabet then
nidoIdx = index + 1
nidoName = getLetterAt(index)
end
end]]
function Textbox.isActive()
if Memory.value("game", "textbox") == 65 then
return true
elseif Memory.value("game", "textbox") == 1 then
return false
end
end
function Textbox.handle()
if not Textbox.isActive() then
return true
end
Input.cancel()
end
return Textbox

193
action/walk.lua Normal file
View File

@ -0,0 +1,193 @@
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 Pokemon = require "storage.pokemon"
local path, stepIdx, currentMap, currentMap2
local pathIdx = 0
local customIdx = 1
local customDir = 1
--local custom_done = false
-- Private functions
local function setPath(index, region, region2)
--if PRINT_PATH then
-- print("Path Idx : "..index.." *******")
--end
pathIdx = index
--stepIdx = 2
stepIdx = 3
currentMap = region
currentMap2 = region2
path = Paths[index]
end
--[[local function setPathCustom(index, region, Idx)
if PRINT_PATH then
print("Path Idx : "..index.." *******")
end
if PRINT_STEP then
print("Step Idx : "..stepIdx)
end
pathIdx = index
stepIdx = Idx
currentMap = region
path = Paths[index]
custom_done = true
end]]
local function completeStep(region, region2)
--if PRINT_STEP then
-- print("Step Idx : "..stepIdx)
--end
stepIdx = stepIdx + 1
return Walk.traverse(region, region2)
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, hold)
local px, py = Player.position()
if px == dx and py == dy then
return true
end
Input.press(dir(px, py, dx, dy), 0, hold)
end
Walk.step = step
-- Table functions
function Walk.reset()
path = nil
pathIdx = 0
customIdx = 1
customDir = 1
currentMap = nil
currentMap2 = nil
Walk.strategy = nil
end
function Walk.init()
local region = Memory.value("game", "map")
local region2 = Memory.value("game", "map2")
local px, py = Player.position()
if region == 0 or region2 == 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 and p[2] == region2 then
local origin = p[3]
if tries == 2 or (origin[1] == px and origin[2] == py) then
setPath(i, region, region2)
return tries == 1
end
end
end
end
end
function Walk.traverse(region, region2)
local newIndex
if not path or currentMap ~= region or currentMap2 ~= region2 then
Walk.strategy = nil
customIdx = 1
customDir = 1
--if PATH_IDX ~= 0 and STEP_IDX ~= 0 and not custom_done then
-- setPathCustom(PATH_IDX, region, STEP_IDX)
-- newIndex = pathIdx
--else
setPath(pathIdx + 1, region, region2)
newIndex = pathIdx
--end
elseif stepIdx > #path then
return
end
local tile = path[stepIdx]
if tile.c then
Control.set(tile)
return completeStep(region, region2)
end
if tile.s then
if Walk.strategy then
Walk.strategy = nil
return completeStep(region, region2)
end
Walk.strategy = tile
elseif step(tile[1], tile[2]) then
Pokemon.updateParty()
return completeStep(region, region2)
end
return newIndex
end
function Walk.canMove()
--return Memory.value("player", "moving") == 0 and Memory.value("player", "fighting") == 0
--return Memory.value("player", "moving") == 1 and Memory.value("game", "battle") == 0
return Memory.value("player", "moving") == 1
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

419
ai/combat.lua Normal file
View File

@ -0,0 +1,419 @@
local Combat = {}
local Movelist = require "data.movelist"
local Opponents = require "data.opponents"
local Utils = require "util.utils"
local Memory = require "util.memory"
local Pokemon = require "storage.pokemon"
local damageMultiplier = { -- http://bulbapedia.bulbagarden.net/wiki/Type_chart#Generation_II
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, steel=0.5, fire=1.0, water=1.0, grass=1.0, electric=1.0, psychic=1.0, ice=1.0, dragon=1.0, dark=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, steel=2.0, fire=1.0, water=1.0, grass=1.0, electric=1.0, psychic=0.5, ice=2.0, dragon=1.0, dark=2.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, steel=0.5, fire=1.0, water=1.0, grass=2.0, electric=0.5, psychic=1.0, ice=1.0, dragon=1.0, dark=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, steel=0.0, fire=1.0, water=1.0, grass=2.0, electric=1.0, psychic=1.0, ice=1.0, dragon=1.0, dark=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, steel=2.0, fire=2.0, water=1.0, grass=0.5, electric=2.0, psychic=1.0, ice=1.0, dragon=1.0, dark=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, steel=0.5, fire=2.0, water=1.0, grass=1.0, electric=1.0, psychic=1.0, ice=2.0, dragon=1.0, dark=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, steel=0.5, fire=0.5, water=1.0, grass=2.0, electric=1.0, psychic=2.0, ice=1.0, dragon=1.0, dark=2.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, steel=1.0, fire=1.0, water=1.0, grass=1.0, electric=1.0, psychic=0.0, ice=1.0, dragon=1.0, dark=0.5, },
steel = {normal=1.0, fighting=1.0, flying=1.0, poison=1.0, ground=1.0, rock=2.0, bug=1.0, ghost=1.0, steel=0.5, fire=0.5, water=0.5, grass=1.0, electric=0.5, psychic=1.0, ice=2.0, dragon=1.0, dark=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, steel=2.0, fire=0.5, water=0.5, grass=2.0, electric=1.0, psychic=1.0, ice=2.0, dragon=0.5, dark=1.0, },
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, steel=1.0, fire=2.0, water=0.5, grass=0.5, electric=1.0, psychic=1.0, ice=1.0, dragon=0.5, dark=1.0, },
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, steel=0.5, fire=0.5, water=2.0, grass=0.5, electric=1.0, psychic=1.0, ice=1.0, dragon=0.5, dark=1.0, },
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, steel=1.0, fire=1.0, water=2.0, grass=0.5, electric=0.5, psychic=1.0, ice=1.0, dragon=0.5, dark=1.0, },
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, steel=0.5, fire=1.0, water=1.0, grass=1.0, electric=1.0, psychic=0.5, ice=1.0, dragon=1.0, dark=0.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, steel=0.5, fire=1.0, water=0.5, grass=2.0, electric=1.0, psychic=1.0, ice=0.5, dragon=2.0, dark=1.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, steel=0.5, fire=1.0, water=1.0, grass=1.0, electric=1.0, psychic=1.0, ice=1.0, dragon=2.0, dark=1.0, },
dark = {normal=1.0, fighting=0.5, flying=1.0, poison=1.0, ground=1.0, rock=1.0, bug=1.0, ghost=2.0, steel=1.0, fire=1.0, water=1.0, grass=1.0, electric=1.0, psychic=2.0, ice=1.0, dragon=1.0, dark=0.5, },
}
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[9] = "steel"
types[20] = "fire"
types[21] = "water"
types[22] = "grass"
types[23] = "electric"
types[24] = "psychic"
types[25] = "ice"
types[26] = "dragon"
types[27] = "dark"
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
if Memory.value("battle", "x_accuracy") == 1 and defender.speed < attacker.speed then
return 9001, 9001
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
attFactor, defFactor = attacker.spec_att, defender.spec_def
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 move.multiple then
damage = damage * move.multiple
end
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 = Memory.value("battle", "opponent_move_id")
else
base = Memory.value("battle", "our_move_id")
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.value("battle", "our_move_pp")
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 = 9001, 9001
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 = 9001, 9001
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
Utils.drawText(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"),
spec_att = Memory.double("battle", "our_special_attack"),
spec_def = Memory.double("battle", "our_special_defense"),
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 currSpec = ours.spec_att
local booster = toBoost.mp
if (currSpec < 140) == (booster > 1) then
--ours.spec = math.floor(currSpec * booster)
ours.spec_att = 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", "our_special"),
spec_att = Memory.double("battle", "our_special_attack"),
spec_def = Memory.double("battle", "our_special_defense"),
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(0x116F) > 1
return Memory.value("battle", "our_status") == 14 --######################
end
Combat.isSleeping = isSleeping
local function isConfused()
--return Memory.raw(0x106B) > 0
return Memory.value("battle", "our_status") == 15 --######################
end
Combat.isConfused = isConfused
-- HP
function Combat.hp()
return Pokemon.index(0, "hp")
end
function Combat.redHP()
return math.ceil(Pokemon.index(0, "max_hp") * 0.2)
end
function Combat.inRedBar()
return Combat.hp() <= Combat.redHP()
end
-- Combat AI
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
Utils.drawText(0, 21, ours.speed.." "..enemy.speed)
Utils.drawText(0, 28, turnsToDie.." "..ours.hp.." | "..turnsToKill.." "..enemy.hp)
end
local hpReq = enemyAttack.damage
local isConfused = isConfused()
if isConfused then
hpReq = hpReq + math.floor(ours.hp * 0.2)
end
if ours.hp <= hpReq then
local outsped = enemyAttack.outspeed
if outsped and outsped ~= true then
outsped = Memory.value("battle", "attack_turns") > 0
end
if outsped 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.975
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

300
ai/control.lua Normal file
View File

@ -0,0 +1,300 @@
local Control = {}
local Battle
local Strategies
local Combat = require "ai.combat"
local Bridge = require "util.bridge"
local Memory = require "util.memory"
local Paint = require "util.paint"
local Utils = require "util.utils"
local Inventory = require "storage.inventory"
local Pokemon = require "storage.pokemon"
local potionInBattle = true
local encounters = 0
local canDie, shouldFight, minExp
local shouldCatch, attackIdx
local extraEncounter, maxEncounters
local battleYolo
Control.areaName = "Unknown"
Control.moonEncounters = nil
Control.getMoonExp = true
Control.yolo = false
--[[local function withinOneKill(forExp)
return Pokemon.getExp() + 80 > forExp
end]]
local controlFunctions = {
a = function(data)
Control.areaName = data.a
return true
end,
potion = function(data)
if data.b ~= nil then
Control.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,
-- EXP
--[[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,
-- CATCH
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,]]
}
-- COMBAT
function Control.battlePotion(enable)
potionInBattle = enable
end
function Control.canDie(enabled)
if enabled == nil then
return canDie
end
canDie = enabled
end
local function isNewFight()
if Memory.double("battle", "opponent_hp") == Memory.double("battle", "opponent_max_hp") then
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
local minimumCount = 1
if pokeballs < minimumCount then
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
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)
else
Inventory.use("pokeball", nil, true)
end
return true
end
end
end
end]]
-- Items
function Control.canRecover()
return potionInBattle and (not battleYolo or not Control.yolo)
end
function Control.set(data)
controlFunctions[data.c](data)
end
function Control.setYolo(enabled)
Control.yolo = enabled
end
function Control.setPotion(enabled)
potionInBattle = enabled
end
function Control.encounters()
return encounters
end
function Control.encounter(battleState)
if battleState > 0 then
local wildBattle = false
if battleState == 1 then
wildBattle = true
end
--[[local isCritical
local battleMenu = Memory.value("battle", "menu")
if battleMenu == 94 then
isCritical = false
Control.missed = false
elseif Memory.double("battle", "our_hp") == 0 then
if Memory.value("battle", "critical") == 1 then
isCritical = true
end
elseif not Control.missed then
local turnMarker = Memory.value("battle", "our_turn")
if turnMarker == 100 or turnMarker == 128 then
local isMiss = Memory.value("battle", "miss") == 1
if isMiss then
if not Control.ignoreMiss and Battle.accurateAttack and Memory.value("battle", "accuracy") == 7 then
Bridge.chat("gen 1 missed :( (1 in 256 chance)")
end
Control.missed = true
end
end
end
if isCritical ~= nil and isCritical ~= Control.criticaled then
Control.criticaled = isCritical
end]]
if wildBattle then
local opponentHP = Memory.double("battle", "opponent_hp")
if not Control.inBattle then
Control.escaped = false
if opponentHP > 0 then
Control.killedCatch = false
Control.inBattle = true
encounters = encounters + 1
Paint.wildEncounters(encounters)
Bridge.encounter()
--if Control.moonEncounters then
-- Control.moonEncounters = Control.moonEncounters + 1
--end
end
else
--if opponentHP == 0 and shouldCatch and not Control.killedCatch then
--if opponentHP == 0 and shouldCatch then
--local gottaCatchEm = {"pidgey", "spearow", "paras", "oddish"}
--local opponent = Battle.opponent()
--for i,catch in ipairs(gottaCatchEm) do
-- if opponent == catch then
-- if not Pokemon.inParty(catch) then
-- Bridge.chat("accidentally killed "..Utils.capitalize(catch).." with a "..(isCritical and "critical" or "high damage range").." :(")
-- Control.killedCatch = true
-- end
-- break
-- end
--end
--end
end
end
elseif Control.inBattle then
if Memory.value("battle", "battle_turns") == 0 then
Control.escaped = true
end
Control.inBattle = false
end
end
function Control.reset()
canDie = false
oneHits = false
--shouldCatch = nil
--shouldFight = nil
extraEncounter = nil
potionInBattle = true
encounters = 0
battleYolo = false
maxEncounters = nil
Control.yolo = false
Control.inBattle = false
end
function Control.init()
local LowerGameName = string.lower(GAME_NAME)
Battle = require "action.battle"
Strategies = require("ai."..LowerGameName..".strategies")
end
return Control

979
ai/crystal/strategies.lua Normal file
View File

@ -0,0 +1,979 @@
local Combat = require "ai.combat"
local Control = require "ai.control"
local Strategies = require "ai.strategies"
local Battle = require "action.battle"
local Shop = require "action.shop"
local Textbox = require "action.textbox"
local Walk = require "action.walk"
local Bridge = require "util.bridge"
local Input = require "util.input"
local Memory = require "util.memory"
local Menu = require "util.menu"
local Player = require "util.player"
local Utils = require "util.utils"
local Inventory = require "storage.inventory"
local Pokemon = require "storage.pokemon"
local status = Strategies.status
local strategyFunctions = Strategies.functions
--local bulbasaurScl
--local UsingSTRATS = ""
-- TIME CONSTRAINTS
Strategies.timeRequirements = {
--[[charmander = function()
return 2.39
end,
pidgey = function()
local timeLimit = 7.55
return timeLimit
end,
glitch = function()
local timeLimit = 10.15
if Pokemon.inParty("pidgey") then
timeLimit = timeLimit + 0.67
end
return timeLimit
end,]]
}
-- HELPERS
--[[local function pidgeyDSum()
local sx, sy = Player.position()
if status.tries == nil then
if status.tries then
status.tries.idx = 1
status.tries.x, status.tries.y = sx, sy
else
status.tries = 0
end
end
if status.tries ~= 0 and Control.escaped then
if status.tries[status.tries.idx] == 0 then
tries.idx = tries.idx + 1
if tries.idx > 3 then
tries = 0
end
return pidgeyDSum()
end
if status.tries.x ~= sx or status.tries.y ~= sy then
status.tries[status.tries.idx] = status.tries[status.tries.idx] - 1
status.tries.x, status.tries.y = sx, sy
end
sy = 47
else
sy = 48
end
if sx == 8 then
sx = 9
else
sx = 8
end
Walk.step(sx, sy)
end
local function tackleDSum()
local sx, sy = Player.position()
if status.tries == nil then
if status.tries then
status.tries.idx = 1
status.tries.x, status.tries.y = sx, sy
else
status.tries = 0
end
end
if status.tries ~= 0 and Control.escaped then
if status.tries[status.tries.idx] == 0 then
tries.idx = tries.idx + 1
if tries.idx > 3 then
tries = 0
end
return tackleDSum()
end
if status.tries.x ~= sx or status.tries.y ~= sy then
status.tries[status.tries.idx] = status.tries[status.tries.idx] - 1
status.tries.x, status.tries.y = sx, sy
end
--sx = 1
--else
--sx = 2
end
if sy == 6 then
sy = 8
else
sy = 6
end
Walk.step(sx, sy)
end]]
-- STRATEGIES
local strategyFunctions = Strategies.functions
--[[strategyFunctions.grabPCPotion = function()
if Inventory.contains("potion") then
return true
end
Player.interact("Up")
end
strategyFunctions.checkStrats = function()
UsingSTRATS = STRATS
return true
end]]
strategyFunctions.talk_mom = function()
if Strategies.initialize() then
status.tempDir = false
end
local Direction = Memory.value("player", "facing")
if Direction == 8 then
Input.press("Down", 2)
else
local CurrentMenu = Memory.value("menu", "current")
if CurrentMenu == 32 and not status.tempDir then
Input.press("A", 2)
elseif CurrentMenu == 32 and status.tempDir then
return true
--elseif CurrentMenu == 79 then --french
elseif CurrentMenu == 110 then --english
local OptionMenu = Memory.value("menu", "option_current")
local DaysRow = Memory.value("menu", "days_row")
if OptionMenu == 0 or OptionMenu == 11 then
Input.press("A", 2)
elseif OptionMenu == 17 then
status.tempDir = true
--set days
if DaysRow < GAME_DAY then
Input.press("Up", 2)
elseif DaysRow > GAME_DAY then
Input.press("Down", 2)
else
Input.press("A", 2)
end
end
end
end
end
--strategyFunctions.bulbasaurIChooseYou = function()
strategyFunctions.totodileIChooseYou = function()
if Strategies.initialize() then
status.tempDir = false
end
--if Pokemon.inParty("bulbasaur") then
--if Pokemon.inParty("totodile") then
--Bridge.caught("bulbasaur")
-- Bridge.caught("totodile")
-- return true
--end
if Player.face("Up") then
--if Textbox.isActive() then
if Textbox.name(TOTODILE_NAME) then
-- status.tempDir = true
--else
-- if status.tempDir then
-- status.tempDir = false
return true
-- else
-- Input.press("A", 2)
-- end
end
--Textbox.name(BULBASAUR_NAME)
--Textbox.name(TOTODILE_NAME)
end
end
--[[strategyFunctions.fightCharmander = function()
if status.tries < 9000 and Pokemon.index(0, "level") == 6 then
if status.tries > 200 then
bulbasaurScl = Pokemon.index(0, "special")
if bulbasaurScl < 12 then
if UsingSTRATS == "Pidgey" then
return Strategies.reset("Bad Bulbasaur for pidgey strats - "..bulbasaurScl.." special")
else
UsingSTRATS = "PP"
end
end
status.tries = 9001
return true
else
status.tries = status.tries + 1
end
end
if Battle.isActive() and Memory.double("battle", "opponent_hp") > 0 and Strategies.resetTime(Strategies.getTimeRequirement("charmander"), "kill Charmander") then
return true
end
Battle.automate()
end
strategyFunctions.dodgePalletBoy = function()
return Strategies.dodgeUp(0x0223, 14, 14, 15, 7)
end
strategyFunctions.shopViridian = function()
if Strategies.initialize() then
status.tempDir = 5
end
bulbasaurScl = Pokemon.index(0, "special")
if bulbasaurScl == 16 then
if UsingSTRATS == "Pidgey" then
return Strategies.reset("We are already at 16special, we got no chance for Weedle")
else
UsingSTRATS = "PP"
end
end
if UsingSTRATS == "PP" then
status.tempDir = 1
end
return Shop.transaction{
buy = {{name="pokeball", index=0, amount=status.tempDir}, {name="paralyze_heal", index=2, amount=1}, {name="burn_heal", index=3, amount=1}}
}
end
strategyFunctions.dodgeViridianOldMan = function()
if UsingSTRATS == "PP" then
local bidx = Pokemon.indexOf("bulbasaur")
if Memory.raw(0x101E) ~= 73 then
if Pokemon.index(bidx, "level") >= 7 then
return Strategies.reset("We need leech seed for the brock skip glitch")
end
end
end
return Strategies.dodgeUp(0x0273, 18, 6, 17, 9)
end
strategyFunctions.healTreePotion = function()
if Battle.handleWild() then
if Inventory.contains("potion") then
if Pokemon.info("bulbasaur", "hp") <= 12 then
if Menu.pause() then
Inventory.use("potion", "bulbasaur")
end
else
return true
end
elseif Menu.close() then
return true
end
end
end
strategyFunctions.catchPidgey = function()
if UsingSTRATS == "PP" then
local px, py = Player.position()
if px < 10 and py < 46 then
px = 10
elseif px == 10 and py < 46 then
py = 46
elseif px > 8 and py == 46 then
px = 8
elseif px == 8 and py == 46 then
return true
end
Walk.step(px, py)
else
if Strategies.initialize() then
status.tempDir = false
status.tries = nil
local bidx = Pokemon.indexOf("bulbasaur")
local scl = Pokemon.index(bidx, "special")
if scl == 16 then
if UsingSTRATS == "" then
UsingSTRATS = "PP"
return true
else
return Strategies.reset("We are already at 16special, we got no chance for Weedle")
end
end
end
if Battle.isActive() then
local isPidgey = Pokemon.isOpponent("pidgey")
status.tries = nil
if isPidgey then
local pidgeyHP = Memory.raw(0xCFE7)
gui.text(100, 134, pidgeyHP.."HP")
if Memory.value("menu", "text_input") == 240 then
Textbox.name(PIDGEY_NAME, true)
elseif Memory.value("battle", "menu") == 95 then
Input.press("A")
elseif status.tempDir then
local pokeballs = Inventory.count("pokeball")
if pokeballs < 2 then
if Memory.value("menu", "selection") == 233 then
Input.press("Right", 2)
elseif Memory.value("menu", "selection") == 239 then
Input.press("A", 2)
end
--Battle.run()
elseif not Control.shouldCatch(3) then
Battle.run()
end
else
local pidgeyHPtable = {17, 16, 15, 13, 10, 8}
if Utils.match(pidgeyHP, pidgeyHPtable) then
status.tempDir = true
elseif not Utils.match(pidgeyHP, pidgeyHPtable) and pidgeyHP > 8 then
Battle.fight("tackle", false, true) --perform tackle
else
Battle.run()
end
end
else
if Memory.value("battle", "menu") == 95 then
Input.cancel()
elseif not Control.shouldCatch() then
if Control.shouldFight() then
Battle.fight()
else
Battle.run()
end
end
end
else
local hasPidgey = Pokemon.inParty("pidgey")
Pokemon.updateParty()
if hasPidgey then
if status.tempDir then
Bridge.caught("pidgey")
status.tempDir = false
end
return true
end
local pokeballs = Inventory.count("pokeball")
if pokeballs < 2 then
if not hasPidgey then
if UsingSTRATS == "Pidgey" then
return Strategies.reset("Ran too low on PokeBalls", pokeballs)
else
UsingSTRATS = "PP"
print("Ran too low on PokeBalls, going to PP-Strats")
return true
end
end
else
local timeLimit = Strategies.getTimeRequirement("pidgey")
local resetMessage = "find a Pidgey"
if Strategies.resetTime(timeLimit, resetMessage, false, true) then
return true
end
pidgeyDSum()
end
end
end
end
strategyFunctions.grabAntidote = function()
local px, py = Player.position()
if py < 11 then
return true
end
if Inventory.contains("antidote") then
py = 10
else
Player.interact("Up")
end
Walk.step(px, py)
end
strategyFunctions.grabForestPotion = function()
if Strategies.initialize() then
status.tempDir = false
end
if Battle.handleWild() then
if not Textbox.isActive() and not status.tempDir then
Input.press("A", 2)
elseif Textbox.isActive() and not status.tempDir then
Input.press("A", 2)
status.tempDir = true
elseif not Textbox.isActive() and status.tempDir then
return true
end
end
end
strategyFunctions.fightWeedle = function()
if Battle.isTrainer() then
status.canProgress = true
return Strategies.buffTo("growl", 0, 39) --Peform 1x Growl
elseif status.canProgress then
return true
end
end
strategyFunctions.checkSpec = function()
if Strategies.initialize() then
local WillReset
if not Inventory.contains("potion") then WillReset = true end
if not Inventory.contains("pokeball") then WillReset = true end
if not Inventory.contains("antidote") then WillReset = true end
if not Inventory.contains("paralyze_heal") then WillReset = true end
if not Inventory.contains("burn_heal") then WillReset = true end
if WillReset then
return Strategies.reset("We need 5 items for the brock skip glitch")
end
end
if UsingSTRATS == "" then
local bidx = Pokemon.indexOf("bulbasaur")
local scl = Pokemon.index(bidx, "special")
local hasPidgey = Pokemon.inParty("pidgey")
if hasPidgey then
if scl == 16 then
UsingSTRATS = "Pidgey"
print("Performing Pidgey Strats")
return true
else
UsingSTRATS = "PP"
end
else
UsingSTRATS = "PP"
end
elseif UsingSTRATS == "Pidgey" then
local bidx = Pokemon.indexOf("bulbasaur")
local scl = Pokemon.index(bidx, "special")
if scl == 16 then
print("Performing Pidgey Strats")
return true
else
return Strategies.reset("We need 16special on Bulbasaur for the brock skip glitch")
end
elseif UsingSTRATS == "PP" then
local bidx = Pokemon.indexOf("bulbasaur")
if Memory.raw(0x101E) ~= 73 then
if Pokemon.index(bidx, "level") >= 7 then
return Strategies.reset("We need leech seed for the brock skip glitch")
end
end
print("Performing PP Strats")
return true
end
end
strategyFunctions.equipForGlitch = function()
if UsingSTRATS == "Pidgey" then
return true
else
if Strategies.initialize() then
status.tempDir = false
end
local TacklePP = Memory.raw(0x102D)
local GrowlPP = Memory.raw(0x102E)
local bidx = Pokemon.indexOf("bulbasaur")
--in Battle
if Battle.isActive() then
status.tries = nil
if Memory.value("battle", "menu") == 95 then
Input.press("A")
else
TacklePP = Memory.raw(0x102D)
if not status.tempDir then
if GrowlPP > 36 then
Battle.fight("growl", false, true) --perform 3x Growl
else
if TacklePP > 16 then --perform tackle until 16pp
Battle.fight()
elseif TacklePP == 16 then
if Memory.raw(0x101E) ~= 73 then
return Strategies.reset("We need leech seed for the brock skip glitch")
else
status.tempDir = true
end
end
end
end
if status.tempDir then
if Pokemon.battleMove("tackle") == 1 then
Battle.swapMove(1, 3)
elseif Pokemon.battleMove("tackle") == 3 then
Battle.swapMove(3, 2)
elseif Pokemon.battleMove("tackle") == 2 then
if Memory.value("battle", "menu") == 106 then
Input.press("B")
else
if Pokemon.index(bidx, "level") ~= 8 then
status.tempDir = false
return Strategies.reset("Can't be Lvl"..Pokemon.index(bidx, "level").." for the brock skip glitch with the PP Strats")
else
Battle.run()
end
end
end
end
end
else --out battle
TacklePP = Memory.raw(0x102D)
if not status.tempDir then
if TacklePP == 16 then
if Pokemon.index(bidx, "level") ~= 8 then
return Strategies.reset("Can't be Lvl"..Pokemon.index(bidx, "level").." for the brock skip glitch with the PP Strats")
end
if Memory.raw(0x101E) ~= 73 then
return Strategies.reset("We need leech seed for the brock skip glitch")
end
status.tempDir = true
elseif TacklePP < 16 then
return Strategies.reset("Ran too low on Tackle for the PP Strats "..TacklePP.."PP available")
end
end
if status.tempDir then
if Pokemon.battleMove("tackle") == 2 then
status.tempDir = false
return true
end
end
local timeLimit = Strategies.getTimeRequirement("glitch")
local resetMessage = "perform enough Tackle for the PP Strats glitch"
if Strategies.resetTime(timeLimit, resetMessage) then
return true
end
tackleDSum()
end
end
end
strategyFunctions.checkInventory = function()
if Strategies.initialize() then
local WillReset
if not Inventory.contains("potion") then WillReset = true end
if not Inventory.contains("pokeball") then WillReset = true end
if not Inventory.contains("antidote") then WillReset = true end
if not Inventory.contains("paralyze_heal") then WillReset = true end
if not Inventory.contains("burn_heal") then WillReset = true end
if WillReset then
return Strategies.reset("We need 5 items for the brock skip glitch")
else
return true
end
end
end
strategyFunctions.checkForPidgey = function()
if UsingSTRATS == "Pidgey" then
return true
else
if Strategies.initialize() then
status.tempDir = false
local hasPidgey = Pokemon.inParty("pidgey")
if not hasPidgey then
return true
end
end
local map = Memory.value("game", "map")
local px, py = Player.position()
if not status.tempDir then --go to pc to depose
if map == 2 then
if px > 13 then
px = 13
else
if py > 25 then
py = 25
end
end
elseif map == 58 then
if py > 5 then
py = 5
else
if px < 13 then
px = 13
else
if py > 4 then
py = 4
else -- deposit pidgey
if Memory.value("player", "party_size") == 1 then
if Menu.close() then
status.tempDir = true
end
else
if not Textbox.isActive() then
Player.interact("Up")
else
local pc = Memory.value("menu", "size")
if Memory.value("battle", "menu") ~= 95 and (pc == 2 or pc == 4) then
local menuColumn = Menu.getCol()
if menuColumn == 10 then
Input.press("A")
elseif menuColumn == 5 then
Menu.select(1) -- select pidgey
else
Menu.select(1) -- select deposit box
end
else
Input.press("A")
end
end
end
end
end
end
end
else --get back to the spot
if map == 58 then
if px > 4 then
px = 4
else
if py < 8 then
py = 8
end
end
elseif map == 2 then
if px < 18 then
px = 18
else
return true
end
end
end
Walk.step(px, py, true)
end
end
strategyFunctions.prepareSave = function()
local main = Memory.value("menu", "main")
local row = Memory.value("menu", "row")
if main == 128 then
if row == 4 then
Input.press("B")
else
Input.press("Down")
end
else
if row == 4 then
return true
end
Input.press("Start")
end
end
strategyFunctions.performSkip = function()
local current = Memory.value("menu", "current")
local selection = Memory.value("menu", "selection")
local skip = Memory.value("menu", "pokemon")
if current == 15 then
if Memory.value("menu", "pokemon") ~= 0 then
Input.press("Start", 0)
else
Player.disinteract("left")
end
else
if selection == 115 then
Input.press("A")
elseif selection == 65 then
if skip == 207 then
return true
else
Input.press("A")
end
else
Input.press("Start", 0)
end
end
end
strategyFunctions.performReset = function()
local skip = Memory.value("menu", "pokemon")
if skip == 197 or skip == 204 then
return Strategies.SkipReset()
else
Input.press("A")
end
end
strategyFunctions.openPokemonMenu = function()
if UsingSTRATS == "Pidgey" then
if Textbox.isActive() then
return true
else
Input.press("Start")
end
else
if Strategies.initialize() then
status.tempDir = false
end
local main = Memory.value("menu", "main")
local row = Memory.value("menu", "row")
if main == 128 then
if status.tempDir then
Input.press("B")
else
if row == 0 then
Input.press("Down")
else
Input.press("A")
end
end
elseif main == 103 then
status.tempDir = true
Input.press("B")
elseif main == 8 then
status.tempDir = false
return true
else
if status.tempDir then
Input.press("B")
else
Input.press("Start")
end
end
end
end
strategyFunctions.speakToGlithGuy = function()
local main = Memory.value("menu", "main")
if not Textbox.isActive() then
Player.interact("Left")
else
if main == 167 then
return true
else
Input.press("A")
end
end
end
strategyFunctions.leaveGlitchGuy = function()
local map = Memory.value("game", "map")
local px, py = Player.position()
if map == 2 then --Pewter City
if py == 16 then
px = 40
end
elseif map == 14 then --Route3
if px < 17 then
px = 17
else
if py > 7 then
py = 7
else
if px < 60 then
px = 60
else
if py > -1 then
py = -1
end
end
end
end
elseif map == 15 then --Center Route
if px < 90 then
px = 90
end
elseif map == 3 then --Cerulean City
if px < 8 then
px = 8
else
if py < 36 then
py = 36
end
end
elseif map == 16 then --Out of Cerulean
if py < 36 then
py = 36
end
elseif map == 10 then --Saffron City
if py < 29 then
py = 29
else
if px < 9 then
px = 9
end
end
elseif map == 182 then --Saffron City Poke Center
return true
end
Walk.step(px, py, true)
end
strategyFunctions.checkPidgeyHP = function()
if UsingSTRATS == "PP" then
return true
else
if Strategies.initialize() then
status.tempDir = false
status.canProgress = true
end
local pidx = Pokemon.indexOf("pidgey")
local hp = Pokemon.index(pidx, "hp")
if hp ~= 16 and status.canProgress then
return true
else
status.canProgress = false
local px, py = Player.position()
if px < 13 then
Walk.step(13, py)
else
if Memory.value("player", "party_size") == 2 then --Depose Pidgey
if not Textbox.isActive() then
Player.interact("Up")
else
local pc = Memory.value("menu", "size")
if Memory.value("battle", "menu") ~= 95 and (pc == 2 or pc == 4) then
local menuColumn = Menu.getCol()
if menuColumn == 10 then
Input.press("A")
elseif menuColumn == 5 then
Menu.select(1) --select pidgey
else
Menu.select(1) --select deposit box
end
else
Input.press("A")
end
end
else
if not status.tempDir then --swap box for saving
if Memory.value("menu", "shop_current") == 20 or Memory.value("menu", "shop_current") == 73 then
if Memory.value("menu", "column") == 1 then
if Memory.value("menu", "row") ~= 3 then
Input.press("Down")
else
Input.press("A", 2)
end
elseif Memory.value("menu", "column") == 15 then --select yes to save
Input.press("A", 2)
elseif Memory.value("menu", "column") == 12 then --select box
if Memory.value("menu", "row") ~= 1 then
--Menu.select(1) --select box2
Input.press("Down")
else
Input.press("A")
status.tempDir = true
end
end
else
Input.press("A")
end
else --Resetting
if Memory.value("menu", "selection") == 65 then
return Strategies.SkipReset()
else
Input.press("A")
end
end
end
end
end
end
end
strategyFunctions.walkBack = function()
local px, py = Player.position()
if px > 3 then
Walk.step(3, py)
else
return true
end
end
strategyFunctions.getAbra = function()
local party_size = Memory.value("player", "party_size")
local text_input = Memory.value("menu", "text_input")
local textbox_active = Memory.value("game", "textbox")
local hasAbra = Pokemon.inParty("abra")
if textbox_active == 1 then
if party_size == 1 then
Input.press("A")
else
if text_input == 240 then
Textbox.name(ABRA_NAME, true)
else
Input.press("A")
end
end
else
if hasAbra then
return true
else
Input.press("A")
end
end
end
strategyFunctions.performTeleportGlitch = function()
if Strategies.initialize() then
status.tempDir = false
end
local map = Memory.value("game", "map")
local main = Memory.value("menu", "main")
local px, py = Player.position()
if not status.tempDir then
if px == 5 then
status.tempDir = true
Walk.step(4, py, true)
end
else
if main ~= 128 then
Input.press("Start", 0)
else
status.tempDir = false
return true
end
end
end
strategyFunctions.fightGymGuy = function()
local abraHP = Pokemon.info("abra", "hp")
if abraHP == 0 then
return true
end
if Battle.isTrainer() then
status.canProgress = true
return Strategies.buffTo("teleport", 0, 1) --Perform teleport
end
end
strategyFunctions.closingAutomation = function()
if Memory.value("menu", "shop_current") == 0 then
return Strategies.reset("We need need to encounter a MissingNo, Not a Trainer")
else
if Memory.value("menu", "main") == 123 then
Input.press("B")
elseif Memory.value("menu", "main") == 32 then
return true
end
end
end
strategyFunctions.battleMissingNo = function()
if Battle.isActive() then
Battle.run()
else
if Textbox.isActive() then
Input.press("A")
else
local px, py = Player.position()
if py < 1 then
py = 1
else
return true
end
Walk.step(px, py, true)
end
end
end]]
-- PROCESS
function Strategies.completeGameStrategy()
status = Strategies.status
end
function Strategies.resetGame()
--maxEtherSkip = false
status = Strategies.status
stats = Strategies.stats
end
return Strategies

546
ai/strategies.lua Normal file
View File

@ -0,0 +1,546 @@
local Strategies = {}
local Combat = require "ai.combat"
local Control = require "ai.control"
local Battle = require "action.battle"
local Textbox = require "action.textbox"
local Walk = require "action.walk"
local Bridge = require "util.bridge"
local Input = require "util.input"
local Memory = require "util.memory"
local Menu = require "util.menu"
local Player = require "util.player"
local Utils = require "util.utils"
local Inventory = require "storage.inventory"
local Pokemon = require "storage.pokemon"
--local yellow = YELLOW
local splitNumber, splitTime = 0, 0
local resetting, itemPos1, itemPos2, itemNumber
local status = {tries = 0, canProgress = nil, initialized = false, tempDir = false}
Strategies.status = status
local strategyFunctions
-- RISK/RESET
function Strategies.getTimeRequirement(name)
return Strategies.timeRequirements[name]()
end
-- RISK/RESET
function Strategies.hardReset(message, extra, wait)
resetting = true
if Strategies.seed then
if extra then
extra = extra.." | "..Strategies.seed
else
extra = Strategies.seed
end
end
--Reset values
--RUNNING4CONTINUE = false
--RUNNING4NEWGAME = true
Bridge.chat(message, extra)
if wait and INTERNAL and not STREAMING_MODE then
strategyFunctions.wait()
else
client.reboot_core()
end
return true
end
function Strategies.reset(reason, extra, wait)
local time = Utils.elapsedTime()
local resetMessage = "reset"
if time then
resetMessage = resetMessage.." after "..time
end
resetMessage = resetMessage.." at "..Control.areaName
local separator
if Strategies.deepRun and not Control.yolo then
separator = " BibleThump"
else
separator = ":"
end
resetMessage = resetMessage..separator.." "..reason
if status.tweeted then
Strategies.tweetProgress(resetMessage)
end
return Strategies.hardReset(resetMessage, extra, wait)
end
-- RESET TO CONTINUE
--[[function Strategies.SkipReset(message)
RUNNING4CONTINUE = true
EXTERNALDONE = false
client.reboot_core()
return true
end]]
function Strategies.death(extra)
local reason = "Died"
--[[local reason
if Control.missed then
reason = "Missed"
elseif Control.criticaled then
reason = "Critical'd"
elseif Control.yolo then
reason = "Yolo strats"
else
reason = "Died"
end]]
return Strategies.reset(reason, extra)
end
function Strategies.overMinute(min)
if type(min) == "string" then
min = Strategies.getTimeRequirement(min)
end
return Utils.igt() > (min * 60)
end
function Strategies.resetTime(timeLimit, reason, once)
if Strategies.overMinute(timeLimit) then
reason = "Took too long to "..reason
if RESET_FOR_TIME then
return Strategies.reset(reason)
end
if once then
print(reason.." "..Utils.elapsedTime())
end
end
end
-- HELPERS
function Strategies.initialize()
if not status.initialized then
status.initialized = true
return true
end
end
--[[function Strategies.buffTo(buff, defLevel, usePPAmount, oneHit)
if Battle.isActive() then
status.canProgress = true
local forced
if not usePPAmount then
if defLevel and Memory.double("battle", "opponent_defense") > defLevel then
forced = buff
end
else
local AvailablePP = Battle.pp(buff)
if not oneHit then
if AvailablePP > usePPAmount then
forced = buff
end
else
if Strategies.initialize() then
status.tempDir = AvailablePP
end
if AvailablePP > status.tempDir-1 then
forced = buff
end
end
end
Battle.automate(forced, true)
elseif status.canProgress then
return true
else
Battle.automate()
end
end
function Strategies.dodgeUp(npc, sx, sy, dodge, offset)
if not Battle.handleWild() then
return false
end
local px, py = Player.position()
if py < sy - 1 then
return true
end
local wx, wy = px, py
if py < sy then
wy = py - 1
elseif px == sx or px == dodge then
if px - Memory.raw(npc) == offset then
if px == sx then
wx = dodge
else
wx = sx
end
else
wy = py - 1
end
end
Walk.step(wx, wy)
end
local function dodgeH(options)
local left = 1
if options.left then
left = -1
end
local px, py = Player.position()
if px * left > options.sx * left + (options.dist or 1) * left then
return true
end
local wx, wy = px, py
if px * left > options.sx * left then
wx = px + 1 * left
elseif py == options.sy or py == options.dodge then
if py - Memory.raw(options.npc) == options.offset then
if py == options.sy then
wy = options.dodge
else
wy = options.sy
end
else
wx = px + 1 * left
end
end
Walk.step(wx, wy)
end]]
-- GENERALIZED STRATEGIES
Strategies.functions = {
split = function(data)
Bridge.split(data and data.finished)
if not INTERNAL then
splitNumber = splitNumber + 1
local timeDiff
splitTime, timeDiff = Utils.timeSince(splitTime)
if timeDiff then
print(splitNumber..". "..Control.areaName..": "..Utils.elapsedTime().." ("..timeDiff..")")
end
end
return true
end,
interact = function(data)
if Battle.handleWild() then
if Battle.isActive() then
return true
end
if Textbox.isActive() then
if status.tries > 0 then
return true
end
status.tries = status.tries - 1
Input.cancel()
elseif Player.interact(data.dir) then
status.tries = status.tries + 1
end
end
end,
confirm = function(data)
if Battle.handleWild() then
if Textbox.isActive() then
status.tries = status.tries + 1
Input.cancel(data.type or "A")
else
if status.tries > 0 then
return true
end
Player.interact(data.dir)
end
end
end,
--[[teleport = function(data)
if Memory.value("game", "map") == data.map then
return true
end
if not Pokemon.use("teleport") then
Menu.pause()
end
end,]]
speak = function()
if Strategies.initialize() then
status.tempDir = false
end
if Textbox.isActive() then
Input.press("A", 2)
status.tempDir = true
else
if status.tempDir then
status.tempDir = false
return true
else
Input.press("A", 2)
end
end
end,
--[[deposeAll = function(data)
if Memory.value("player", "party_size") == 1 then
if Menu.close() then
return true
end
else
if not Textbox.isActive() then
Player.interact("Up")
else
local pc = Memory.value("menu", "size")
if Memory.value("battle", "menu") ~= 95 and (pc == 2 or pc == 4) then
local menuColumn = Menu.getCol()
if menuColumn == 10 then
Input.press("A")
elseif menuColumn == 5 then
local depositIndex = 0
if Pokemon.indexOf(data.keep) == 0 then
depositIndex = 1
end
Menu.select(depositIndex)
else
Menu.select(1)
end
else
Input.press("A")
end
end
end
end,
swapItem = function(data)
if Strategies.initialize() then
status.tempDir = false
end
if not data.pos2 then -- 1x position mode + item name
itemPos1 = Inventory.indexOf(data.item)
itemPos2 = data.pos1-1
else -- 2x position mode
itemPos1 = data.pos1-1
itemPos2 = data.pos2-1
end
local main = Memory.value("menu", "main")
local selection = Memory.value("menu", "selection_mode")
if status.tempDir and selection == 0 then
return true
end
if main == 128 then
if Menu.getCol() ~= 5 then
Menu.select(2, true)
else
if selection == 0 then
if Menu.select(itemPos1, 1, true, nil, true) then
Input.press("Select")
end
else
if Menu.select(itemPos2, 1, true, nil, true) then
Input.press("Select")
status.tempDir = true
end
end
end
else
Menu.pause()
end
end,
tossItem = function(data)
if Strategies.initialize() then
status.canProgress = false
if not data.item then
itemPos1 = data.pos-1
status.tempDir = Memory.raw(0x131E+itemPos1*2+1)
else
status.tempDir = Inventory.count(data.item)
end
if data.amount then
itemNumber = data.amount
else
itemNumber = status.tempDir
end
end
if not data.pos then --tossing by item name
itemPos1 = Inventory.indexOf(data.item)
else --tossing by item position
itemPos1 = data.pos-1
end
local main = Memory.value("menu", "main")
if main == 60 and Memory.value("menu", "shop_current") == 20 and status.canProgress then
return true
end
if main == 128 or Menu.getCol() == 14 and main ~= 209 then
if Menu.getCol() ~= 5 and Menu.getCol() ~= 14 then
Menu.select(2, true)
else
if Memory.value("menu", "text_input") == 146 then
if Memory.value("menu", "row") == 0 then
Menu.select(1, true)
else
if Memory.value("menu", "shop_current") ~= 248 then
Input.press("A")
else
local currAmount = Memory.value("shop", "transaction_amount")
if Menu.balance(currAmount, itemNumber, false, 99, true) then
Input.press("A")
status.canProgress = true
end
end
end
else
if Menu.select(itemPos1, 1, true, nil, true) then
Input.press("A")
end
end
end
elseif main == 209 then
Input.press("A")
else
Menu.pause()
end
end,
tossTM = function(data)
if Strategies.initialize() then
status.canProgress = false
status.tries = 0
if data.amount then
itemNumber = data.amount
else
itemPos1 = data.pos-1
status.tempDir = Memory.raw(0x131E+itemPos1*2+1)
itemNumber = status.tempDir
end
end
if not data.pos then --tossing by item name
itemPos1 = Inventory.indexOf(data.item)
else --tossing by item position
itemPos1 = data.pos-1
end
local main = Memory.value("menu", "main")
if main == 60 and Memory.value("menu", "shop_current") == 20 and status.canProgress then
return true
end
if status.tries == 0 then
if main == 128 or Menu.getCol() == 14 and main ~= 209 then
if Menu.getCol() ~= 5 and Menu.getCol() ~= 14 then
Menu.select(2, true)
else
if Memory.value("menu", "text_input") == 146 then
if Memory.value("menu", "row") == 0 then
Menu.select(1, true, false, nil, true)
else
if main == 128 then
Input.press("A")
else
status.tries = 1
end
end
else
if Menu.select(itemPos1, 1, true, nil, true) then
Input.press("A")
end
end
end
end
else
if main == 128 or Menu.getCol() == 14 and main ~= 209 then
local currAmount = Memory.value("shop", "transaction_amount")
if Menu.balance(currAmount, itemNumber, false, 99, true) then
Input.press("A")
status.canProgress = true
end
else
Input.press("A")
end
end
end,]]
openMenu = function()
if Textbox.isActive() then
return true
else
Input.press("Start", 2)
end
end,
closeMenu = function()
if not Textbox.isActive() then
return true
else
Input.press("B")
end
end,
allowDeath = function(data)
Control.canDie(data.on)
return true
end,
--[[champion = function()
if status.canProgress then
if status.tries > 1500 then
return Strategies.hardReset("Beat the game in "..status.canProgress.." !")
end
if status.tries == 0 then
Bridge.tweet("Beat Pokemon "..GAME_NAME.." in "..status.canProgress.."!")
if Strategies.seed then
print(Utils.frames().." frames, with seed "..Strategies.seed)
print("Please save this seed number to share, if you would like proof of your run!")
end
end
status.tries = status.tries + 1
elseif Memory.value("menu", "shop_current") == 252 then
Strategies.functions.split({finished=true})
status.canProgress = Utils.elapsedTime()
else
Input.cancel()
end
end]]
}
strategyFunctions = Strategies.functions
function Strategies.execute(data)
if strategyFunctions[data.s](data) then
status = {tries=0}
Strategies.status = status
Strategies.completeGameStrategy()
-- print(data.s)
if resetting then
return nil
end
return true
end
return false
end
function Strategies.init(midGame)
if not STREAMING_MODE then
splitTime = Utils.timeSince(0)
end
if midGame then
Combat.factorPP(true)
end
end
function Strategies.softReset()
status = {tries=0}
Strategies.status = status
stats = {}
Strategies.stats = stats
Strategies.updates = {}
splitNumber, splitTime = 0, 0
resetting = nil
Strategies.deepRun = false
Strategies.resetGame()
end
return Strategies

1522
data/movelist.lua Normal file

File diff suppressed because it is too large Load Diff

225
data/opponents.lua Normal file
View File

@ -0,0 +1,225 @@
local Opponents = {
--[[RivalGyarados = {
type1 = "water",
type2 = "flying",
def = 71,
id = 22,
spec = 87,
hp = 126,
speed = 72,
level = 38,
att = 106,
moves = {
{
name = "Hydro-Pump",
accuracy = 80,
power = 120,
id = 56,
special = true,
max_pp = 5,
move_type = "water",
}
},
boost = {
stat = "spec",
mp = 1.5
}
},
HypnoHeadbutt = {
type1 = "psychic",
type2 = "psychic",
def = 58,
id = 129,
spec = 88,
hp = 107,
speed = 56,
level = 34,
att = 60,
moves = {
{
name = "Headbutt",
accuracy = 100,
power = 70,
id = 29,
special = false,
max_pp = 15,
move_type = "normal",
}
}
},
HypnoConfusion = {
type1 = "psychic",
type2 = "psychic",
def = 58,
id = 129,
spec = 88,
hp = 107,
speed = 56,
level = 34,
att = 60,
moves = {
{
name = "Confusion",
accuracy = 100,
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 = {
{
name = "Self-Destruct",
accuracy = 100,
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 = {
{
name = "Stomp",
move_type = "normal",
accuracy = 100,
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 = {
{
name = "Aurora-Beam",
accuracy = 100,
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 = {
{
name = "Hydro-Pump",
accuracy = 80,
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 = {
{
name = "Wing-Attack",
accuracy = 100,
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 = {
{
name = "Sky-Attack",
accuracy = 90,
power = 140,
id = 143,
special = false,
max_pp = 5,
move_type = "flying",
}
}
},]]
}
return Opponents

24
data/paths.lua Normal file
View File

@ -0,0 +1,24 @@
local paths = {
-- Bed room
{7, 24, {3,3}, {7,3}, {7,0}},
-- Mom house
{6, 24, {9,1}, {9,3}, {7,3}, {s="talk_mom"}, {7,3}, {8,3}, {8,7}, {7,7}, {7,8}},
-- Go to lab
{4, 24, {13,6}, {6,6}, {6,3}},
-- Choose your character!
{5, 24, {4,4}, {c="a",a="Prof Orm Lab"}, {s="speak"}, {s="speak"}, {s="speak"}, {s="speak"}, {4,4}, {7,4}, {s="totodileIChooseYou"}, {5,3}, {5,8}, {s="speak"}, {5,12}},
-- 1: GO TAKE THE EGG
-- Go to route 29
{4, 24, {6,4}, {6,7}, {2,7}, {2,9}, {-1,9}},
-- Route 29
{3, 24, {59,9}, {44,9}, {44,15}, {39,15}, {39,16}, {31,16}, {31,10} , {36,10}, {36,7}, {23,7}, {23,5}, {21,5}, {21,3}, {16,3}, {16,7}, {14,7}, {14,8}, {4,8}, {4,7}, {-1,7}},
-- CherryGrove City
{3, 26, {39,7}, {28,7}, {28,5}, {17,5}, {17,-1}},
-- Route 30
{1, 26, {7,53}, {7,48}, {12,48}, {12,35}, {9,35}, {s="interact",dir="Left"}, {9,35}, {9,31}},
}
return paths

324
main.lua Normal file
View File

@ -0,0 +1,324 @@
--##################################################
--############# ############
--############# SETTING ############
--############# ############
--##################################################
--Reset Settings
RESET_FOR_TIME = false -- Set to false if you just want to see the bot finish a run without reset for time
RESET_FOR_ENCOUNTERS = false -- Set to false if you just want to see the bot finish a run without reset for encounters
--Game Settings
GAME_NAME = "Crystal" -- Set to Gold/Silver or Crystal
GAME_RUN = "" -- Set to "" or "" for the run you want
GAME_HOURS = 17 -- Set the internal game hour (0-23h)
GAME_MINUTES = 35 -- Set the internal game minutes (0-59min)
GAME_DAY = 3 -- Set the internal game day (0-6 // sunday-saturday)
GAME_GENDER = 1 -- Set the player gender (1-2 // boy-girl)
GAME_TEXT_SPEED = 128 -- Set the Text Speed (247-249 // slow-fast)
GAME_BATTLE_ANIMATION = 133 -- Set the battle animation (141-142 // no-yes)
GAME_BATTLE_STYLE = 132 -- Set the battle style (130-131 // choice-set)
GAME_SOUND_STYLE = 145 -- Set the sound style (132 or 141 // stereo-mono)
GAME_PRINT_STYLE = 127 -- Set the print style (64=normal // 96=dark // 127=dark+ // 0=clear+ // 32=clear)
GAME_ACCOUNT_STYLE = 0 -- Set the account style (0-1 // no-yes)
GAME_WINDOWS_STYLE = 3 -- Set the windows style (0-7)
--GAME_TEXT_SPEED = 249 -- Set the Text Speed (247-249 // slow-fast)
--GAME_BATTLE_ANIMATION = 141 -- Set the battle animation (141-142 // no-yes)
--GAME_BATTLE_STYLE = 131 -- Set the battle style (130-131 // choice-set)
--GAME_SOUND_STYLE = 132 -- Set the sound style (132 or 141 // stereo-mono)
--GAME_PRINT_STYLE = 127 -- Set the print style (64=normal // 96=dark // 127=dark+ // 0=clear+ // 32=clear)
--GAME_ACCOUNT_STYLE = 0 -- Set the account style (0-1 // no-yes)
--GAME_WINDOWS_STYLE = 3 -- Set the windows style (0-7)
--Connection Settings
INTERNAL = false -- Allow connection with LiveSplit ?
STREAMING_MODE = false -- Enable Streaming mode
--Script Settings
CUSTOM_SEED = nil -- Set to a known seed to replay it, or leave nil for random runs
PAINT_ON = true -- Display contextual information while the bot runs
--Names Settings
PLAYER_NAME = "TeSt" -- Player name
RIVAL_NAME = "URRival" -- Rival name
TOTODILE_NAME = "ToTo" -- Set Totodile name
--Advanced area Settings
PATH_IDX = 0 -- Start the bot to the specified path idx
STEP_IDX = 0 -- Start the bot to the specified step idx
PRINT_PATH = false -- Print the current path in the console.
PRINT_STEP = false -- Print the current step in the console.
--NAMES SETTINGS TIPS :
-- - Can use up to 7 letter ingame
-- - Upper and Lower case allowed
-- - Specials Characters : <=Special X, {=Pk, }=Mn
--#####################################################################################
--#####################################################################################
--########### ###############
--########### PLEASE DON'T EDIT ANYTHING BELLOW, IT'S AT YOUR RISK ###############
--########### START CODE (hard hats on) ###############
--########### ###############
--#####################################################################################
--#####################################################################################
-- SET VALUES
local VERSION = "1.0"
--YELLOW = memory.getcurrentmemorydomainsize() > 30000
local START_WAIT = 99
local hasAlreadyStartedPlaying = false
local oldSeconds
local running = true
local lastHP
--FirstSpawnDone = false
--FirstSpawnDone2 = false
--RUNNING4CONTINUE = false --used to continue a game
--RUNNING4NEWGAME = true --used to make a new game (remove last save also)
--EXTERNALDONE = false --used when the above settings are done externally
--local InternalDone = false --used when the above settings are done internally
--local UsingCustomPath = false --used when we set a custom path
-- SET DIR
--[[local lowerGameRun = string.lower(GAME_RUN)
local lowerGameName = string.lower(GAME_NAME)
local secondStratDir = ""
local secondPaintDir = ""
if lowerGameRun == "no save corruption" then
if lowerGameName == "red" or lowerGameName == "blue" then
secondStratDir = ".red-blue"
secondPaintDir = secondStratDir
end
else
secondStratDir = YELLOW and ".yellow" or ".red-blue"
end]]
-- LOAD DIR
local LowerGameName = string.lower(GAME_NAME)
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."..LowerGameName..".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"
-- GLOBAL
function p(...) --print
local string
if #arg == 0 then
string = arg[0]
else
string = ""
for i,str in ipairs(arg) do
if str == true then
string = string.."\n"
else
string = string..str.." "
end
end
end
print(string)
end
-- RESET
local function resetAll()
Strategies.softReset()
Combat.reset()
Control.reset()
Walk.reset()
Paint.reset()
Bridge.reset()
oldSeconds = 0
running = false
--FirstSpawnDone = false
--FirstSpawnDone2 = false
-- client.speedmode = 200
if CUSTOM_SEED then
Strategies.seed = CUSTOM_SEED
p("RUNNING WITH A FIXED SEED ("..Strategies.seed.."), every run will play out identically!", true)
else
Strategies.seed = os.time()
p("Starting a new run with seed "..Strategies.seed, true)
end
math.randomseed(Strategies.seed)
end
-- EXECUTE
local OWNER = "Bouletmarc"
p("Welcome to PokeBot Version "..VERSION, true)
p("Actually running Pokemon "..GAME_NAME.." Speedruns by "..OWNER, true)
Control.init()
--STREAMING_MODE = not walk.init()
--if INTERNAL and STREAMING_MODE then
-- RESET_FOR_TIME = true
--end
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
p("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.", true)
end
--if STREAMING_MODE then
-- Bridge.init()
--else
Input.setDebug(true)
--end
--if PATH_IDX ~= 0 and STEP_IDX ~= 0 then
-- UsingCustomPath = true
--end
-- MAIN LOOP
local previousMap
local previousMap2
local RebootDone = false
while true do
local currentMap = Memory.value("game", "map")
local currentMap2 = Memory.value("game", "map2")
if currentMap ~= previousMap or currentMap2 ~= previousMap2 then
Input.clear()
previousMap = currentMap
previousMap2 = currentMap2
end
if Strategies.frames then
if Memory.value("game", "battle") == 0 then
Strategies.frames = Strategies.frames + 1
end
Utils.drawText(0, 80, Strategies.frames)
end
--if Bridge.polling then
-- Settings.pollForResponse()
--end
if not Input.update() then
--if not Utils.ingame() then
if not Utils.ingame() and currentMap == 0 then
--if not Utils.ingame() and currentMap == 0 and currentMap2 == 0 then
--if currentMap == 0 then
if running then
if not hasAlreadyStartedPlaying then
client.reboot_core() --remove this for the new bizhawk
hasAlreadyStartedPlaying = true
else
--if not RUNNING4CONTINUE then
resetAll() --reset if not running to continue
--RUNNING4NEWGAME = true --set back on in case we done a reboot
--else
-- running = false --continue adventure
--end
end
else
--if UsingCustomPath then
-- if not EXTERNALDONE then --continue adventure
-- RUNNING4CONTINUE, RUNNING4NEWGAME = true, false
-- elseif EXTERNALDONE and InternalDone then
-- RUNNING4NEWGAME = true --set back to new game
-- end
--end
Settings.startNewAdventure(START_WAIT) --start/continue adventure
end
--else
--if not running then
-- Bridge.liveSplit()
-- running = true
--end
--Settings.choosePlayerNames() --set names
--end
else
if not running then
--Bridge.liveSplit()
running = true
end
--open and close menu for first spawn
--if Settings.FirstSpawn() then
--[[local Done = false
local MenuValue = Memory.value("menu", "main")
if MenuValue ~= 121 and Textbox.isActive() then
Input.press("Start", 2)
elseif MenuValue == 121 then
Input.press("B", 2)
Done = true
elseif MenuValue == 2 and not Textbox.isActive() and Done then
FirstSpawn = false
end
end]]
--else
--if RUNNING4NEWGAME then --remove last save game
-- Settings.RemoveLastAdventure(START_WAIT)
--elseif RUNNING4CONTINUE then --continue the last adventure
-- EXTERNALDONE = true
-- InternalDone = true
-- Settings.ContinueAdventure()
--else
local battleState = Memory.value("game", "battle")
Control.encounter(battleState)
--local curr_hp = Pokemon.index(0, "hp")
--if curr_hp == 0 and not Control.canDie() and Pokemon.index(0) > 0 then
-- Strategies.death(currentMap, currentMap2)
--elseif Walk.strategy then
if Walk.strategy then
if Strategies.execute(Walk.strategy) then
Walk.traverse(currentMap, currentMap2)
end
elseif battleState > 0 then
--if not Control.shouldCatch(partySize) then
Battle.automate()
--end
elseif Textbox.handle() then
Walk.traverse(currentMap, currentMap2)
end
--end
end
end
--[[if STREAMING_MODE then
local newSeconds = Memory.value("time", "seconds")
if newSeconds ~= oldSeconds and (newSeconds > 0 or Memory.value("time", "frames") > 0) then
Bridge.time(Utils.elapsedTime())
oldSeconds = newSeconds
end
elseif PAINT_ON then]]
if PAINT_ON then
Paint.draw(currentMap, currentMap2)
end
Input.advance()
emu.frameadvance()
end
Bridge.close()

273
storage/inventory.lua Normal file
View File

@ -0,0 +1,273 @@
local Inventory = {}
local Input = require "util.input"
local Memory = require "util.memory"
local Menu = require "util.menu"
local Utils = require "util.utils"
local Pokemon = require "storage.pokemon"
local ItemList = require "storage.itemlist"
--[[local items = {
pokeball = 4,
bicycle = 6,
moon_stone = 10,
antidote = 11,
burn_heal = 12,
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,
coin_case = 69,
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 = Memory.value("inventory", "item_base")
-- Data
function Inventory.indexOf(name)
--local searchID = items[name]
local searchID = ItemList.items[name]
for i=0,19 do
--local iidx = ITEM_BASE + i * 2
local SubIndex = i * 2
local iidx = ITEM_BASE + SubIndex
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
local SubIndex = index * 2
return Memory.raw(ITEM_BASE + SubIndex + 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 == Menu.pokemon 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.value("inventory", "item_count") == 20
end
function Inventory.use(item, poke, midfight, BagMenu)
if midfight then
local battleMenu = Memory.value("battle", "menu")
--if battleMenu == 94 then
--open bag menu
if battleMenu == 186 then
local rowSelected = Memory.value("battle", "menuY")
local ColumnSelected = Memory.value("battle", "menuX")
if ColumnSelected == 1 then
if rowSelected == 1 then
Input.press("Down")
else
--select bag
Input.press("A")
end
else
Input.press("Left")
end
--elseif battleMenu == 233 then
--inside bag menu
elseif battleMenu == 128 then
--if its not done
if not give_done then
if column ~= BagMenu then
--select proper bag menu
Menu.setCol(BagMenu)
else
if Memory.value("menu", "shop_current") ~= 70 then
--select the item
Menu.select(Inventory.indexOf(item)+1, "accelerate", "input")
else
--accept the use
Menu.select(1, true, "input")
end
end
--if its done
else
Menu.close()
end
elseif Utils.onPokemonSelect(battleMenu) then
if poke then
--if type(poke) == "string" then
-- poke = Pokemon.indexOf(poke)
--end
Menu.select(poke, true, "input")
else
Input.press("A")
end
else
Input.press("B")
end
return
end
local main = Memory.value("menu", "main")
local column = Menu.getCol()
local give_done = false
--select item menu
if main == 121 then
Menu.select(3, true)
--inside bag menu
elseif main == 50 then
--if its not done
if not give_done then
if column ~= BagMenu then
--select proper bag menu
Menu.setCol(BagMenu)
else
if Memory.value("menu", "shop_current") ~= 66 then
--select the item
Menu.select(Inventory.indexOf(item)+1, "accelerate", "input")
else
--accept the use
Menu.select(1, true, "input")
end
end
--if its done
else
Menu.close()
end
--inside pokemon menu
elseif main == 127 then
local idx = 1
if poke then
idx = poke
end
if Memory.value("menu", "input_row") ~= idx then
Menu.select(idx, true, "input")
else
Input.press("A", 1)
give_done = true
end
else
return false
end
--####################################
--[[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 == Menu.pokemon then
Input.press("B")
else
return false
end]]
return true
end
return Inventory

62
storage/itemlist.lua Normal file
View File

@ -0,0 +1,62 @@
ItemList = {}
ItemList.items = {
masterball = 1,
ultraball = 2,
bright_powder = 3,
greatball = 4,
pokeball = 5,
--teru_sama = 6,
bicycle = 7,
moon_stone = 8,
antidote = 9,
burn_heal = 10,
ice_heal = 11,
awakening = 12,
paralyze_heal = 13,
full_restore = 14,
max_potion = 15,
hyper_potion = 16,
super_potion = 17,
potion = 18,
escape_rope = 19,
repel = 20,
--carbos = 38,
rare_candy = 32,
--helix_fossil = 42,
--nugget = 49,
--pokedoll = 51,
super_repel = 42,
--fresh_water = 60,
--soda_pop = 61,
coin_case = 54,
--pokeflute = 73,
--ether = 80,
--max_ether = 81,
--elixer = 82,
--x_accuracy = 46,
--x_speed = 67,
--x_special = 68,
--horn_drill = 207,
--bubblebeam = 211,
--water_gun = 212,
--ice_beam = 213,
--thunderbolt = 224,
--earthquake = 226,
--dig = 228,
--tm34 = 234,
--rock_slide = 248,
}
ItemList.moves = {
cut = 15,
fly = 19,
surf = 57,
strength = 70,
teleport = 100,
watefall = 127,
whirlpool = 250,
}
--return ItemList

325
storage/pokemon.lua Normal file
View File

@ -0,0 +1,325 @@
local Pokemon = {}
local Bridge = require "util.bridge"
local Input = require "util.input"
local Memory = require "util.memory"
local Menu = require "util.menu"
local pokeIDs = {
pidgey = 16,
spearow = 21,
rattata = 19,
nidoranF = 29,
nidoranM = 32,
chikorita = 152,
bayleef = 153,
meganium = 154,
cyndaquil = 155,
quilava = 156,
typhlosion = 157,
totodile = 158,
croconaw = 159,
feraligatr = 160,
sentret = 161,
furret = 162,
hoothoot = 163,
marill = 183,
azumarill = 184,
sudowoodo = 185,
politoed = 186,
hoppip = 187,
}
local moveList = {
cut = 15,
fly = 19,
surf = 57,
strength = 70,
teleport = 100,
watefall = 127,
whirlpool = 250,
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,
ice_beam = 58,
bubblebeam = 61,
leech_seed = 73,
thunderbolt = 85,
earthquake = 89,
dig = 91,
rock_slide = 157,
}
--[[local data = {
hp = {1, true},
status = {4},
moves = {8},
pp = {28},
level = {33},
max_hp = {34, true},
attack = {36, true},
defense = {38, true},
speed = {40, true},
special = {42, true},
}]]
local previousPartySize
--[[local function getAddress(index)
return 0x116B + index * 0x2C
end]]
--local function index(index, offset)
--[[local function index(index)
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 address = getAddress(index)
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=0,3 do
--if mid == Memory.raw(0x101B + i) then
if mid == Memory.raw(0x062E + 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)
--[[function Pokemon.info(name)
--return index(indexOf(name), offset)
return index(indexOf(name))
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.getSacrifice(...)
for i,name in ipairs(arg) do
local pokemonIndex = indexOf(name)
if pokemonIndex ~= -1 and index(pokemonIndex, "hp") > 0 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("tododile", "paras", "spearow", "pidgey", "nidoran", "squirtle")
--local poke = Pokemon.inParty("tododile")
--if poke then
-- Bridge.caught(poke)
-- previousPartySize = partySize
--end
end
end
--[[function Pokemon.pp(index, move)
local midx = Pokemon.battleMove(move)
return Memory.raw(getAddress(index) + 28 + midx)
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(...)
local deployedID = Memory.value("battle", "our_id")
for i,name in ipairs(arg) do
if deployedID == pokeIDs[name] then
return name
end
end
end
function Pokemon.isEvolving()
return false
--return Memory.value("menu", "pokemon") == 144
end
--[[function Pokemon.getExp()
return Memory.raw(0x117A) * 256 + Memory.raw(0x117B)
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 battlemenu = Memory.value("battle", "menu")
local pokeName = Pokemon.forMove(move)
local column = Memory.value("battle", "menuX")
local row = Memory.value("battle", "menuY")
if battlemenu == 186 then
if column == 2 then
Input.press("Left", 1)
else
if row == 2 then
Input.press("Up", 1)
else
--select move menu
Input.press("A", 1)
end
end
elseif battlemenu == 106 then
local midx = 1
if move then
midx = move
end
Menu.select(midx, true, "input")
else
return false
end
--[[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 == Menu.pokemon then
Menu.select(pokeName, true)
elseif main == 228 then
Input.press("B")
else
return false
end]]
return true
end
--[[function Pokemon.getDVs(name)
local index = Pokemon.indexOf(name)
local baseAddress = getAddress(index)
local attackDefense = Memory.raw(baseAddress + 0x1B)
local speedSpecial = Memory.raw(baseAddress + 0x1C)
return bit.rshift(attackDefense, 4), bit.band(attackDefense, 15), bit.rshift(speedSpecial, 4), bit.band(speedSpecial, 15)
end]]
return Pokemon

148
util/bridge.lua Normal file
View File

@ -0,0 +1,148 @@
local Bridge = {}
--local socket
--if INTERNAL then
-- socket = require("socket")
--end
local utils = require("util.utils")
local client = nil
local timeStopped = true
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()
if socket then
-- io.popen("java -jar Main.jar")
client = socket.connect("127.0.0.1", 13378)
if client then
client:settimeout(0.005)
client:setoption("keepalive", true)
print("Connected to Java!");
return true
else
print("Error connecting to Java!");
end
end
end]]
--[[function Bridge.tweet(message)
if INTERNAL and STREAMING_MODE then
print("tweet::"..message)
return send("tweet", message)
end
end
function Bridge.pollForName()
Bridge.polling = true
send("poll_name")
end
function Bridge.chat(message, extra, newLine)
if extra then
p(message.." || "..extra, newLine)
else
p(message, newLine)
end
return send("msg", "/me "..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(finished)
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

166
util/input.lua Normal file
View File

@ -0,0 +1,166 @@
local Input = {}
local Bridge = require "util.bridge"
local Memory = require "util.memory"
local Utils = require "util.utils"
local lastSend
local currentButton, remainingFrames, setForFrame
local debug
local bCancel = true
local Waiting = false
local function bridgeButton(btn)
if btn ~= lastSend then
lastSend = btn
Bridge.input(btn)
end
end
--local function sendButton(button, ab, hold, newgame)
local function sendButton(button, ab, hold)
local inputTable = {}
if hold then
inputTable = {[button]=true, B=true}
else
--if not newgame then
inputTable = {[button]=true}
--else
-- inputTable = {Up=true, B=true, Select=true}
--end
end
joypad.set(inputTable)
if debug then
if hold then
gui.text(0, 7, button.."+B")
else
--if not newgame then
gui.text(0, 7, button.." "..remainingFrames)
--else
-- gui.text(0, 7, "Up+B+Select")
--end
end
end
if ab then
buttonbutton = "A,B"
end
bridgeButton(button)
setForFrame = button
end
function Input.isWaiting()
if setForFrame and not Waiting then
Waiting = true
elseif not setForFrame and Waiting then
Waiting = false
end
return Waiting
end
--function Input.press(button, frames, hold, newgame)
function Input.press(button, frames, hold)
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, false, hold)
--sendButton(button, false, hold, newgame)
end
--function Input.cancel(accept)
function Input.cancel()
--if accept and Memory.value("menu", "shop_current") == 20 then
--if accept and Memory.value("menu", "shop_current") == 30 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 rowSelected = Memory.value("battle", "menuY")
local columnSelected = Memory.value("battle", "menuX")
if not Input.isWaiting() then
if rowSelected == 1 then
Input.press("Down", 2)
else
if columnSelected == 1 then
Input.press("Right", 2)
else
Input.press("A", 2)
end
end
end
--local inputTable = {Right=true, Down=true}
--joypad.set(inputTable)
--bridgeButton("D,R")
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")
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

188
util/memory.lua Normal file
View File

@ -0,0 +1,188 @@
local Memory = {}
local memoryNames = {
setting = {
text_speed = 0x04E8, --247=1 // 248=2 // 249=3 ###### 139-136-128
battle_animation = 0x0510, --141=no // 142=yes ###### 141=on 133=off
battle_style = 0x0538, --130=shift // 131=set ##### 135=shift 132=set
sound_style = 0x0562, --132=stereo // 141=mono ##### 142=mono 145=stereo
print_style = 0x0FD0, --64=normal // 96=darker // 127=darkest // 0=lightest // 32=lighter
account_style = 0x0FD1, --0=no // 1=yes
windows_style = 0x0FCE, --0 to 7
},
text_inputing = {
column = 0x0330,
row = 0x0331,
},
inventory = {
item_count = 0x1892,
item_base = 0x1893,
},
menu = {
row = 0x0F88,
input_row = 0x0FA9,
settings_row = 0x0F63,
hours_row = 0x061C, --(0-23)
minutes_row = 0x0626, --(0-59)
days_row = 0x1002, --(0-6)
column = 0x0F65,
current = 0x00DF, --32=off 79=on instead of 20=on
size = 0x0FA3,
option_current = 0x0F84,--DONE used while settings options (5=startmenu, 7=optionmenu)
shop_current = 0x0F87, --DONE 95=main 94=buy 80=ammount 30=accept 74=sell instead of 32=main 158/161=amount 20=buy/accept 248=sell
selection = 0x0F78, --DONE ?? going like 1 or 2 or 4 etc...
text_input = 0x0F69, --DONE 65=inputing
text_length = 0x06D2, --DONE
main = 0x04AA, --DONE 121=open
--pokemon = 0x0C51, --TO DO, USED WHILE EVOLVING
--selection_mode = 0x0C35, --TO DO, USED WHEN SWAPING MOVE
--transaction_current = 0x0F8B,--TODO, USED FOR SHOPPING
--################################################################
--main_current = 0x0C27, --NOT USED??
--scroll_offset = 0x0C36, --NOT USED
--################################################################
},
player = {
name = 0x147D,
name2 = 0x1493,
moving = 0x14E1, --if not 1 then moving
x = 0x1CB8,
y = 0x1CB7,
facing = 0x14DE, --0=S // 4=N // 8=W // 12=E instead of 4=S // 8=N // 2=W // 1=E
repel = 0x1CA1,
party_size = 0x1CD7,
},
game = {
map = 0x1CB6,
map2 = 0x1CB5,
battle = 0x122D, --1=wild 2=trainer
battle_type = 0x1230, --ex:7=shiny/cant escape
ingame = 0x02CE,
textbox = 0x10ED, --1=false 65=On
--textbox = 0x0FA4, --or 0x0FAA
--inside_area = 0x02D0, --can be used while inside a area we can use escape_rope?
},
time = {
hours = 0x14C4, --DONE or 0xD4C5
minutes = 0x14C6, --DONE
seconds = 0x14C7, --DONE
frames = 0x14C8, --DONE
},
shop = {
transaction_amount = 0x110C,--DONE
},
battle = {
text = 0x0FCF, --DONE 1=11(texting) // 3=1(not)
menu = 0x0FB6, --DONE 106=106(att) // 186=94(free) // 128=233(item) // 145=224(pkmon)
menuX = 0x0FAA, --DONE used for battle Row-X
menuY = 0x0FA9, --DONE used for battle Row-Y
battle_turns = 0x06DD, --DONE?? USED FOR DSUM ESCAPE??
opponent_id = 0x1206, --DONE or 0x1204
opponent_level = 0x1213, --DONE
opponent_type1 = 0x1224, --DONE
opponent_type2 = 0x1225, --DONE
opponent_move_id = 0x1208, --DONE used to get opponent moves ID
opponent_move_pp = 0x120E, --DONE used to get opponent moves PP
our_id = 0x1205, --DONE old=1014
our_status = 0x063A, --DONE
our_level = 0x0639, --DONE
our_type1 = 0x064A, --DONE
our_type2 = 0x064B, --DONE
our_move_id = 0x062E, --DONE used to get our moves ID
our_move_pp = 0x0634, --DONE used to get our moves PP
--attack_turns = 0x06DC, --DONE?? NOT USED??
--accuracy = 0x0D1E,
--x_accuracy = 0x1063,
--disabled = 0x0CEE,
--paralyzed = 0x1018,
--critical = 0x105E,
--miss = 0x105F,
--our_turn = 0x1FF1,
--TO DO GET SLEEPING STATUS
--TO DO GET CONFUSED STATUS
--TO DO GET PARALIZED STATUS ?
--TO DO GET CRITICAL'D STATUS ?
--opponent_next_move = 0x0CDD, --C6E4 ?? NOT USED?
--opponent_last_move = 0x0FCC,
--opponent_bide = 0x106F,
},
--[[pokemon = {
exp1 = 0x1179,
exp2 = 0x117A, --NOT USED, WAS ONLY FOR REMOVE LAST ADVENTURE
exp3 = 0x117B,
},]]
}
local doubleNames = {
battle = {
opponent_hp = 0x1216, --DONE 10FF index +278? //
opponent_max_hp = 0x1218, --DONE
opponent_attack = 0x121A, --DONE
opponent_defense = 0x121C, --DONE
opponent_speed = 0x121E, --DONE
opponent_special_attack = 0x1220,--DONE
opponent_special_defense = 0x1222,--DONE
our_hp = 0x063C, --DONE
our_max_hp = 0x063E, --DONE
our_attack = 0x0640, --DONE
our_defense = 0x0642, --DONE
our_speed = 0x0644, --DONE
our_special_attack = 0x0646, --DONE
our_special_defense = 0x0648, --DONE
},
--[[pokemon = {
attack = 0x117E,
defense = 0x1181, --NOT USED AT ALL??
speed = 0x1183,
special = 0x1185,
},]]
}
--local yellow = YELLOW
local function raw(address)
--local function raw(address, forYellow)
--if yellow and not forYellow and address > 0x0F12 and address < 0x1F00 then
-- address = address - 1
--end
return memory.readbyte(address)
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, forYellow)
local memoryAddress = memoryNames[section]
if key then
memoryAddress = memoryAddress[key]
end
return raw(memoryAddress, forYellow)
end
return Memory

228
util/menu.lua Normal file
View File

@ -0,0 +1,228 @@
local Menu = {}
local Input = require "util.input"
local Memory = require "util.memory"
--local yellow = GAME_NAME == "yellow"
local sliding = false
--Menu.pokemon = yellow and 51 or 103
--Menu.pokemon = 103 --NOT USED?
-- Private functions
--local function getRow(menuType, scrolls)
local function getRow(menuType)
if menuType 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 function setRow(desired, throttle, menuType, loop)
--local currentRow = getRow(menuType, scrolls)
local currentRow = getRow(menuType)
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
--if menuType ~= "hours" or menuType ~= "minutes" then
return Menu.balance(currentRow, desired, true, loop, throttle)
--else
-- return Menu.balance(currentRow, desired, false, loop, throttle)
--end
end
local function isCurrently(desired, menuType)
if menuType then
if menuType ~= "main" then
menuType = menuType.."_current"
end
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, menuType)
return false
end
--function Menu.select(option, throttle, scrolls, menuType, dontPress, loop)
function Menu.select(option, throttle, menuType, dontPress, loop)
--Reset MenuType
local menuTypeSent
if menuType == "option" then
menuTypeSent = nil
else
menuTypeSent = menuType
end
--if setRow(option, throttle, scrolls, menuType, loop) then
if setRow(option, throttle, menuTypeSent, loop) then
local delay = 1
if throttle or menuType == "option" 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
local goUp
if inverted then
if desired < current then
goUp = true
else
goUp = false
end
else
goUp = false
end
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, looping, throttle)
return Menu.sidle(Menu.getCol(), desired, looping, throttle)
end
-- Options
function Menu.setOption(name, desired)
local rowFor = {
text_speed = 0,
battle_animation = 1,
battle_style = 2,
sound_style = 3,
print_style = 4,
account_style = 5,
windows_style = 6
}
if Memory.value("setting", name) == desired then
return true
end
--if setRow(rowFor[name], true, false, "settings") then
if setRow(rowFor[name], 2, "settings") then
Menu.setCol(desired, false, 2)
end
return false
end
-- Pause menu
function Menu.isOpen()
return Memory.value("game", "textbox") == 1 or Memory.value("menu", "current") == 79
--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
if Memory.value("game", "textbox") == 0 and Memory.value("menu", "main") == 0 then
return true
end
Input.press("B")
end
function Menu.pause()
if Memory.value("game", "textbox") == 1 then
--if Memory.value("battle", "menu") == 95 then
if Memory.value("battle", "text") == 3 then
Input.cancel()
--[[else
local main = Memory.value("menu", "main")
if main > 2 and main ~= 64 then
return true
end
Input.press("B")]]
elseif Memory.value("battle", "text") == 11 then
return true
else
Input.press("B")
end
else
Input.press("Start", 2)
end
end
return Menu

56
util/paint.lua Normal file
View File

@ -0,0 +1,56 @@
local Paint = {}
local Memory = require "util.memory"
local Player = require "util.player"
local Utils = require "util.utils"
local Pokemon = require "storage.pokemon"
local encounters = 0
local elapsedTime = Utils.elapsedTime
local drawText = Utils.drawText
function Paint.draw(currentMap, currentMap2)
local px, py = Player.position()
drawText(0, 14, currentMap..","..currentMap2.." : "..px.." "..py)
drawText(0, 0, elapsedTime())
--[[if Memory.value("game", "battle") > 0 then
local curr_hp = Pokemon.index(0, "hp")
local hpStatus
if curr_hp == 0 then
hpStatus = "DEAD"
elseif curr_hp <= math.ceil(Pokemon.index(0, "max_hp") * 0.2) then
hpStatus = "RED"
end
if hpStatus then
drawText(120, 7, hpStatus)
end
end
local tidx = Pokemon.indexOf("totodile")
if tidx ~= -1 then
local attack = Pokemon.index(tidx, "attack")
local defense = Pokemon.index(tidx, "defense")
local speed = Pokemon.index(tidx, "speed")
local scl_att = Pokemon.index(tidx, "special_attack")
local scl_def = Pokemon.index(tidx, "special_defense")
drawText(0, 134, attack.." Att/"..defense.." Def/"..speed.." Spd/"..scl_att.." Scl_Att/"..scl_def.." Scl_Def")
end]]
local enc = " encounter"
if encounters > 1 then
enc = enc.."s"
end
drawText(0, 90, encounters..enc)
return true
end
function Paint.wildEncounters(count)
encounters = count
end
function Paint.reset()
encounters = 0
end
return Paint

43
util/player.lua Normal file
View File

@ -0,0 +1,43 @@
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}
local facingDirections = {Up=4, Right=12, Left=8, Down=0}
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, opposite)
if Player.face(direction) then
if not opposite then
Input.press("A", 2)
else
Input.press("B", 2)
end
return true
end
end
function Player.isMoving()
return Memory.value("player", "moving") ~= 1
end
function Player.position()
return Memory.value("player", "x"), Memory.value("player", "y")
end
return Player

215
util/settings.lua Normal file
View File

@ -0,0 +1,215 @@
local Settings = {}
local Textbox = require "action.textbox"
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 START_WAIT = 99
--local tempDir
local settings_menu = 7
local settings_done = false
local Setting_done = false
local desired = {}
desired.text_speed = GAME_TEXT_SPEED
desired.battle_animation = GAME_BATTLE_ANIMATION
desired.battle_style = GAME_BATTLE_STYLE
desired.sound_style = GAME_SOUND_STYLE
desired.print_style = GAME_PRINT_STYLE
desired.account_style = GAME_ACCOUNT_STYLE
desired.windows_style = GAME_WINDOWS_STYLE
local function isEnabled(name)
return Memory.value("setting", name) == desired[name]
end
-- PUBLIC
function Settings.set(...)
if not settings_done then
for i,name in ipairs(arg) do
if not isEnabled(name) then
if Menu.open(settings_menu, 2, "option") then
Menu.setOption(name, desired[name])
end
return false
end
end
--setting done
settings_done = true
end
--close option menu
local OptionValue = Memory.value("menu", "option_current")
if OptionValue ~= 5 then
Input.press("B", 1)
end
if OptionValue == 5 then
settings_done = false
return true
end
end
function Settings.startNewAdventure(startWait)
local startMenu = Memory.value("menu", "main")
local MenuCurrent = Memory.value("menu", "current")
local ShopCurrent = Memory.value("menu", "shop_current")
local InputRow = Memory.value("menu", "input_row")
local HoursRow = Memory.value("menu", "hours_row")
local MinutesRow = Memory.value("menu", "minutes_row")
--set settings
if startMenu == 122 then
if not Setting_done then
if Settings.set("text_speed", "battle_animation", "battle_style", "sound_style", "print_style", "account_style", "windows_style") then
Setting_done = true
end
else
Input.press("A", 2)
end
--press A or Start
elseif startMenu == 127 then
--if MenuCurrent == 59 then --french
if MenuCurrent == 104 then --english
Input.press("A", 2)
else
if not Setting_done and math.random(0, startWait) == 0 then
Input.press("Start")
end
end
else
--Set Name
--if MenuCurrent == 79 then --french
if MenuCurrent == 110 then --english
if InputRow == 1 and GAME_GENDER == 2 then
Input.press("Down", 2)
elseif InputRow == 2 and GAME_GENDER == 1 then
Input.press("Up", 2)
else
Input.press("A", 2)
end
--Set hours/minutes/name
elseif MenuCurrent == 32 or MenuCurrent == 107 then
--if ShopCurrent == 77 then --french
if ShopCurrent == 78 then --english
--set hours
if HoursRow < GAME_HOURS then
Input.press("Up", 1)
elseif HoursRow > GAME_HOURS then
Input.press("Down", 1)
elseif HoursRow == GAME_HOURS then
Input.press("A", 1)
end
elseif ShopCurrent == 30 then
--set minutes
if MinutesRow < GAME_MINUTES then
Input.press("Up", 1)
elseif MinutesRow > GAME_MINUTES then
Input.press("Down", 1)
elseif MinutesRow == GAME_MINUTES then
Input.press("A", 1)
end
end
--elseif MenuCurrent == 231 then --french
elseif MenuCurrent == 232 then --english
--remake setting not done
Setting_done = false
--set our name
Textbox.name(PLAYER_NAME, true)
else
Input.press("A")
end
end
end
--[[function Settings.FirstSpawn()
if not FirstSpawnDone then
local MenuValue = Memory.value("menu", "main")
if MenuValue == 121 then
Input.press("B", 2)
FirstSpawnDone2 = true
elseif MenuValue == 0 then
if Textbox.isActive() then
Input.press("Start", 2)
elseif not Textbox.isActive() and FirstSpawnDone2 then
FirstSpawnDone = true
return true
end
end
else
return true
end
end]]
--[[function Settings.RemoveLastAdventure(startWait)
if not tempDir then
if Memory.value("menu", "size") ~= 2 and math.random(0, startWait) == 0 then
Input.press("Start")
elseif Memory.value("menu", "size") == 2 then
Input.press("B")
tempDir = true
end
else
if Utils.ingame() then
if Memory.value("menu", "pokemon") ~= 0 then
Input.press("B")
elseif Memory.value("menu", "pokemon") == 0 then
if Memory.value("menu", "size") == 2 then
Input.press("", 0, false, true)
else
if Memory.value("menu", "row") == 1 then
Input.press("A")
else
Input.press("Down")
end
end
end
else
tempDir = false
RUNNING4NEWGAME = false --stop the function after removed
end
end
end]]
--[[function Settings.ContinueAdventure()
local current = Memory.value("menu", "current")
local row = Memory.value("menu", "row")
if row == 0 then
if current == 32 then
RUNNING4CONTINUE = false --stop ContinueAdventure
elseif current ~= 55 then
Input.press("A")
end
else
Input.press("Up")
end
end]]
--[[function Settings.choosePlayerNames()
local name = PLAYER_NAME
if dirText ~= "glitch" then
if (Memory.value("player", "name") ~= 141) or (Memory.value("player", "name2") ~= 136) then
name = RIVAL_NAME
end
else
if (Memory.value("player", "name") ~= 141) or (Memory.value("player", "name2") ~= 136) then
name = "> "
end
end
Textbox.name(name, true)
end
function Settings.pollForResponse()
local response = Bridge.process()
if response then
Bridge.polling = false
Textbox.setName(tonumber(response))
end
end]]
return Settings

121
util/utils.lua Normal file
View File

@ -0,0 +1,121 @@
local Utils = {}
local Memory = require "util.memory"
local EMP = 1
-- GENERAL
function Utils.dist(x1, y1, x2, y2)
return math.sqrt(math.pow(x2 - x1, 2) + math.pow(y2 - y1, 2))
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
return false
end
function Utils.key(needle, haystack)
for key,val in pairs(haystack) do
if needle == val then
return key
end
end
return nil
end
function Utils.capitalize(string)
return string:sub(1, 1):upper()..string:sub(2)
end
-- GAME
function Utils.canPotionWith(potion, forDamage, curr_hp, max_hp)
local potion_hp
if potion == "full_restore" then
potion_hp = 9001
elseif potion == "super_potion" then
potion_hp = 50
else
potion_hp = 20
end
return math.min(curr_hp + potion_hp, max_hp) >= forDamage - 1
end
function Utils.ingame()
return Memory.value("game", "ingame") > 0
end
function Utils.onPokemonSelect(battleMenu)
--return battleMenu == 8 or battleMenu == 48 or battleMenu == 184 or battleMenu == 224
return battleMenu == 145
end
function Utils.drawText(x, y, message)
gui.text(x * EMP, y * EMP, message)
end
-- TIME
function Utils.igt()
local hours = Memory.value("time", "hours")
local mins = Memory.value("time", "minutes")
local secs = Memory.value("time", "seconds")
return (hours * 60 + mins) * 60 + secs
end
local function clockSegment(unit)
if unit < 10 then
unit = "0"..unit
end
return unit
end
function Utils.timeSince(prevTime)
local currTime = Utils.igt()
local diff = currTime - prevTime
local timeString
if diff > 0 then
local secs = diff % 60
local mins = math.floor(diff / 60)
timeString = clockSegment(mins)..":"..clockSegment(secs)
end
return currTime, timeString
end
function Utils.elapsedTime()
local secs = Memory.value("time", "seconds")
local mins = Memory.value("time", "minutes")
local hours = Memory.value("time", "hours")
return hours..":"..clockSegment(mins)..":"..clockSegment(secs)
end
function Utils.frames()
local totalFrames = Memory.value("time", "hours") * 60
totalFrames = (totalFrames + Memory.value("time", "minutes")) * 60
totalFrames = (totalFrames + Memory.value("time", "seconds")) * 60
totalFrames = totalFrames + Memory.value("time", "frames")
return totalFrames
end
return Utils