First-commit

This commit is contained in:
bouletmarc 2015-05-30 04:57:15 -04:00
parent f3347eec0a
commit 13fdfa8b5a
36 changed files with 11489 additions and 0 deletions

70
Changelog.md Normal file
View File

@ -0,0 +1,70 @@
# PokeBot Changelog
## Version 2 - Update 3 (2.3) -- 9 April 2015
* Start of the bot
* Created the No save Corruption Bot under 6days (blue version only)
* Included the Any% Speedrun Bot
* Allow Custom Name for Player/Rival/Pokemon
* Can choice Between Pidgey & PP Strats
* He remove automatically the last game save to improve speedrun time
* He can continue a game where you saved the last spot
* Updated to version 1.3 of Kylecoburn PokeBot (where the 2.3 start from)
## Version 2 - Update 4 (2.4) -- 10 April 2015
* Fixed Resetting issue
* Fixed issue while using Custom Path (when resetting to continue, he was resetting for newgame)
* Fixed Squirtle naming for the Any% run
* Fixed Strats issue, was not resetting
* Fixed Pidgey deposal if 16HP
* Fixed Forest Grabing Potion if we got an Encounter ?
* Updated the changelog
* Updated timeRequirement on the no save corruption run
* Updated the first startup "Print" text, being more comprehensible
* Added Red Routing to the No save Corruption (Abra cost higher, need to get more coins)
* Added 'PidgeyStrats.md'
* Added 'PPStrats.md'
* Added Back NoReset in Control, I miss removed it earlier
* Make the Bot reboot if not already running
* Renamed Glitchless repository to Any% which makes understanding easier, I hope
* Can use Upper&lower case letters for settings "STRING" which make less errors for beginner
* Removed Critical&Repel Painting for the No Save Corruption Run
## Version 2 - Update 5 (2.5) -- 10 April 2015
* Fixed possibly the issue that the bot stop working if we got only 1 ball and we still not caught Pidgey
* Fixed Red route for No save corruption was missing 10coins for abra
* Added printing the seed at the start of a run
* Updated 'Readme.md' file
* Updated time-related constants to `memoryNames.time`
## Version 2 - Update 6 (2.6) -- 11 April 2015
* Updated to the original PokeBot base code **v1.4**
* Updated Memory values in the glitch runs strategies file `(0xD to 0x1)`
* Updated custom path, continue game, remove game function
* Updated `readme.md`
* Fixed for good I hope the issue with Pidgey and 1 ball left in the inventory
* Added Bulbasaur Stats Checkup at Viridian Mart for the No save corruption
* Added Readme for Modified original files
* Added Git Release and/or Git tags of versions
* Cleaned control command (shouldCatch and CanCatch) no more need to use *"noReset"*
* Relocated Strategies function and `strategies.lua`
* Relocated Owners values
* Relocated Glitch Dir to No save corruption
* Removed *"local"* from any settings values, making understanding easier for beginner
* Replaced *"Running4Skip"* to *"Runing4Continue"*
* Will only buy 1 pokeball if we forced it to PP strats for the No save corruption run
## Version 2 - Update 6 (2.6.1 & 2.6.2) -- 11 April 2015
* Fixed minor issue which tempDir was missing from `strategies.lua`
* Updated `Readme_Modified_Files_List`
## Version 2 - Update 6 (2.6.3) -- 11 April 2015
* Fixed again minor issue with tempDir in `strategies.lua`
* Fixed issue with buffed attack in No save corruption run
* Fixed running4newgame issue, was not making a new game, if a save file existed and we rebooted manually
* Fixed Version values are now a string values instead of a number (can use exemple "2.6.3")
## Version 2 - Update 6 (2.6.4) -- 11 April 2015
* Fixed minor Memory value issue for the No Save Corruption run
## Version 2 - Update 6 (2.6.5) -- 11 April 2015
* Fixed minor Menu value issue for the No Save Corruption run

37
NidoranStats.md Normal file
View File

@ -0,0 +1,37 @@
# Nidoran Stat Requirements
### Overview
We catch Nidoran at level 3 or 4, west of Viridian City (level 2's are not acceptable for the route). Level 4 Nidorans have a bit more exp to start, allowing them to level up slightly earlier in a couple places - mostly helpful on Route 3.
Upon defeating Brock, our Nidoran reaches level 8 via exp sharing, and we can check his stats definitively for the first time. While possible to see them earlier by pausing, we can't tell what kind of stats they will become until we have observed a few level ups.
### Stats
Pokémon stats are described by HP, attack, defense, speed, and special. Special is used as both an attack and defense measurement for attacks designated as "special" internally.
The minimum stats at level 8 are **25** HP, **14** attack, **11** defense, **12** speed, **11** special.
The maximum stats at level 8 are **27** HP, **16** attack, **14** defense, **15** speed, **13** special.
HP and defense are almost irrelevant to the route. Having better than 11 defense is helpful for Shorts Kid's Ratatta - as we are in the process of catching up Nidoran's level, and he has high attack, this fight is quite dangerous.
### Requirements
We have stringent requirements for Nidoran's stats. High stats make the run significantly faster, and greatly increases our chances of survival.
The general requirements are **15-16** attack, **14-15** speed, **12-13** special. In addition, either attack must be 16 or speed must be 15. The reason for this is that if we reach Misty with a 15 attack, 14 speed Nidoran, our chances of survival are very bad. In this case, it is very likely that Starmie will both outspeed us, and require 3 hits from thrash.
### Probability
The following is the probability of a Nidoran stats being acceptable after defeating Brock. They are complicated slightly since we require 16 attack AND/OR 15 speed.
Equations are written as: probability of each *Attack* x *Speed* x *Special* multiplied together.
Chance of a **16 attack, 15 speed** Nidoran: 5/16 x 4/16 x 12/16 = 5.859375%
Chance of a **16 attack, 14 speed** Nidoran: 5/16 x 6/16 x 12/16 = 8.7890625%
Chance of a **15 attack, 15 speed** Nidoran: 6/16 x 4/16 x 12/16 = 7.03125%
Summed together gives us our chances of an acceptable Nidoran: 21.6796875% ≈ **22%**

39
PPStrats.md Normal file
View File

@ -0,0 +1,39 @@
# PP Stat Requirements
### Overview
We need a certain kind of PP attack available, on certain kind of attack, at certain kind of position.
Seems odd to read this like so? but you will understand my sentence below fast.
### Stats
Bulbasaur need to be at level 8 or 10 otherwise it's not working. The bot will reset if he reach the level 9.
It would take too much time to hit the level 10 with again the perfect amount of PP available.
### Requirements
About the PP we need 16PP available on Tackle, 36PP on Growl and any other attack learned.
We need to tranfert Tackle at the Position 2 and Growl at the Position 3, that's why we need a 3rd attack.
The PP amount of the 3rd attack (now in position 1) should not mind, but the bot use Leech Seed which is
sometimes learned to Bulbasaur when he reach level 7 or 8, and he dont use any PP on it so 10PP to leech seed.
### Probability
The probability of working here are extremely high, the only know issue it's if we start battling Weedle
at level 6 and we got a little exp earned, we might go directly to level 8 which makig Bulbasaur not neccesary
learning leech seed and will result into a reset because we don't have 3 attacks. Sometimes he can use too much
tackle before being in Pewter City, which also result in a reset because it would take too much time to heal and
perform the "PP strat" after the healing. After all, I could say the probability are nearly 80-90% chance of work.

33
PidgeyStrats.md Normal file
View File

@ -0,0 +1,33 @@
# Pidgey Stat Requirements
### Overview
We need to catch a Pidgey, at north of Viridian City.
But it's not that much easy since we need specials stats and requirements
### Stats
Pidgey don't need any special stats for the glitch to work. Only need some kind of HP.
But Bulbasaur need to be at 16special otherwise it'll not work.
### Requirements
Pidgey only need to have his HP (health) at the proper amount.
It will work if he got 8, 10, 13, 15, 16 or 17HP.
At 8 or 13HP we don't need to Hold B while going to Saffron City, otherwise we need to Hold B while moving.
At 16HP we need to PC Save at Saffron City (by swaping box) to continue the run.
### Probability
Catching a Pidgey with the needed amount of health it's easy and it's 100% probable to get one without reset for time.
But because Bulbasaur need to have a Special of 16 it result in a probability nearly 40%.

53
Readme.md Normal file
View File

@ -0,0 +1,53 @@
# PokéBot V2
An automated computer program that speedruns Pokémon Red, Blue, or Yellow.
Pokémon Red Any%: [1:51:11](https://www.youtube.com/watch?v=M4pOlQ-mIoc) (23 June 2014)
## Watch live
### [twitch.tv/thepokebot](http://www.twitch.tv/thepokebot)
PokéBots official streaming channel on Twitch. Consider following there to find out when were streaming, or follow the [Twitter feed](https://twitter.com/thepokebot) for announcements when we get personal best pace runs going.
### 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 Red, Blue or Yellow 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, Red routing
Michael Jondahl: Combat algorithm, Java bridge for connecting the bot to Twitch chat, LiveSplit, Twitter, etc.
Marc-Andre Boulet(Bouletmarc): PokeBot V2, create No save corruption runs, added fews options, etc.
### Special thanks
To LiveSplit for providing custom component for integrating in-game time splits.
To the Pokémon speedrunning community members who inspired the idea, and shared ways to improve the bot.

View File

@ -0,0 +1,85 @@
# Modified files from the original bot
## Not modified files :
**Action :**
`action/battle.lua`
`action/shop.lua`
**Ai :**
`ai/combat.lua`
**Data :**
`data/any%/red-blue/paths.lua`
`data/any%/yellow/paths.lua`
`data/movelist.lua`
`data/opponents.lua`
**Util :**
`util/any%/paint.lua`
`util/bridge.lua`
`util/memory.lua`
`util/menu.lua`
`util/utils.lua`
## Modified files (with description) :
`main.lua` --Added tons of settings also newGame and continueGame function
**Action :**
`action/textbox.lua` --Added 1-7 letters naming function
`action/walk.lua` --Added Custom path function + holding B on walk function
**Ai :**
`ai/any%/strategies.lua` --Added reset running values for newGame and continueGame
`ai/any%/red-blue/strategies.lua` --Added Squirtle custom name function
`ai/control.lua` --Added NoSaveCorruption Exp, Catch pidgey and require function at the bottom
**Storage :**
`storage/inventory.lua` --Added burn_heal and coin_case to the list
`storage/pokemon.lua` --Added Abra and Bulbasaur, added attack leech_seed and teleport
**Util :**
`util/input.lua` --Added hold B, removeGame and waiting functions
`util/player.lua` --Added player disinteract command (pressing B instead of A)
`util/settings.lua` --Added battle_style = SET if not yellow, newGame, continueGame and setName function
## Modified folder path in files (dir) :
`main.lua` -- strategies.lua and paint.lua
`action/walk.lua` -- paths.lua
`ai/control.lua` -- strategies.lua and paint.lua

11
Seeds.md Normal file
View File

@ -0,0 +1,11 @@
# Seeds for epic runs
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 logs a number to the Lua console that is the seed for the run. This seed allows you to easily share the run with others.
Have you found a seed that results in a run of `1:53:00` or better? [Let us know](https://github.com/kylecoburn/PokeBot/issues/4), and well add it to the list!
| Time | Frames | Seed | Bot version | Found by |
|---------|--------|--------------|-------------|----------|
| 1:50:37 | 398226 | `1428414915` | v1.3.0 | Gofigga |
To reproduce any of these runs, set [`CUSTOM_SEED` in `main.lua`](https://github.com/kylecoburn/PokeBot/blob/0ec69a6a958d15b3a9dccb492fa83e4a1dcae2e0/main.lua#L5) to the seed number and run the bot.

284
action/battle.lua Normal file
View File

@ -0,0 +1,284 @@
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
Input.cancel()
return false
end
local battleMenu = Memory.value("battle", "menu")
local col = Menu.getCol()
if battleMenu == 106 or (battleMenu == 94 and col == 5) then
return true
elseif battleMenu == 94 then
local rowSelected = Memory.value("menu", "row")
if col == 9 then
if rowSelected == 1 then
Input.press("Up")
else
Input.press("A")
end
else
Input.press("Left")
end
else
Input.press("B")
end
end
local function attack(attackIndex)
if Memory.double("battle", "opponent_hp") < 1 then
Input.cancel()
elseif openBattleMenu() then
Menu.select(attackIndex, true, false, false, false, 3)
end
end
function movePP(name)
local midx = Pokemon.battleMove(name)
if not midx then
return 0
end
return Memory.raw(0x102C + midx)
end
Battle.pp = movePP
-- UTILS
function Battle.swapMove(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
if Memory.value("menu", "text_length") == 127 then
Input.press("B")
else
Input.cancel()
end
elseif Textbox.handle() then
local selected = Memory.value("menu", "selection")
if selected == 239 then
Input.press("A", 2)
else
Input.escape()
end
end
end
function Battle.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

177
action/textbox.lua Normal file
View File

@ -0,0 +1,177 @@
local Textbox = {}
local Input = require "util.input"
local Memory = require "util.memory"
local Menu = require "util.menu"
local alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ *():;[]ab-?!mf/.,"
-- local alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ *():;[]ポモ-?!♂♀/.,"
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)
return alphabet:find(letter, 1, true)
end
function Textbox.name(letter, randomize)
local inputting = Memory.value("menu", "text_input") == 240
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
--Set Special Chars & Get UpperCase
if NameTable[TableNumber] == "<" then
GetUpper = true
lidx = 35
elseif NameTable[TableNumber] == ">" then
GetUpper = true
lidx = 36
elseif NameTable[TableNumber] == "{" then
GetUpper = true
lidx = 40
elseif NameTable[TableNumber] == "}" then
GetUpper = true
lidx = 41
else
GetUpper = string.match(NameTable[TableNumber], '%u')
lidx = getIndexForLetter(string.upper(NameTable[TableNumber]))
end
--Check For Waiting
Waiting = Input.isWaiting()
--Proceed
if not Waiting then
--Get/set Lower/Upper
if GetUpper == nil and ActualUpper == 1 or GetUpper ~= nil and ActualUpper == 0 then
crow = Memory.value("menu", "input_row")
if Menu.balance(crow, 6, true, 6, true) then
ccol = math.floor(Memory.value("menu", "column") / 2)
if Menu.sidle(ccol, 0, 9, true) then
Input.press("A")
if ActualUpper == 1 then
ActualUpper = 0
elseif ActualUpper == 0 then
ActualUpper = 1
end
end
end
--Get/Set Letter
else
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")
if Memory.value("menu", "text_length") == TableNumber then
TableNumber = TableNumber + 1
end
end
end
end
end
end
end)
Waiting = Input.isWaiting()
if TableNumber > StringLenght and not Waiting then
if Memory.value("menu", "text_length") > 0 then
Input.press("Start")
TableNumber = 1
ActualUpper = 1
NameTable = {}
return true
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
-- TODO cancel when menu isn't up
--Reset Values
TableNumber = 1
ActualUpper = 1
NameTable = {}
if Memory.raw(0x10B7) == 3 then
Input.press("A", 2)
elseif randomize then
Input.press("A", math.random(1, 5))
else
Input.cancel()
end
end
end
function Textbox.getName()
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()
return Memory.value("game", "textbox") == 1
end
function Textbox.handle()
if not Textbox.isActive() then
return true
end
Input.cancel()
end
return Textbox

195
action/walk.lua Normal file
View File

@ -0,0 +1,195 @@
local Walk = {}
local lowerGameRun = string.lower(GAME_RUN)
local lowerGameName = string.lower(GAME_NAME)
local secondStratDir = string.lower(GAME_NAME)
if lowerGameRun == "any%" then
if lowerGameName == "red" or lowerGameName == "blue" then
secondStratDir = ".red-blue"
end
end
local Control = require "ai.control"
local Paths = require("data."..lowerGameRun.."."..secondStratDir..".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
local pathIdx = 0
local customIdx = 1
local customDir = 1
local custom_done = false
-- Private functions
local function setPath(index, region)
if PRINT_PATH then
print("Path Idx : "..index.." *******")
end
pathIdx = index
stepIdx = 2
currentMap = region
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)
if PRINT_STEP then
print("Step Idx : "..stepIdx)
end
stepIdx = stepIdx + 1
return Walk.traverse(region)
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
Walk.strategy = nil
end
function Walk.init()
local region = Memory.value("game", "map")
local px, py = Player.position()
if region == 0 and px == 0 and py == 0 then
return false
end
for tries=1,2 do
for i,p in ipairs(Paths) do
if i > 2 and p[1] == region then
local origin = p[2]
if tries == 2 or (origin[1] == px and origin[2] == py) then
setPath(i, region)
return tries == 1
end
end
end
end
end
function Walk.traverse(region)
local newIndex
if not path or currentMap ~= region then
Walk.strategy = nil
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)
newIndex = pathIdx
end
elseif stepIdx > #path then
return
end
local tile = path[stepIdx]
if tile.c then
Control.set(tile)
return completeStep(region)
end
if tile.s then
if Walk.strategy then
Walk.strategy = nil
return completeStep(region)
end
Walk.strategy = tile
elseif step(tile[1], tile[2]) then
Pokemon.updateParty()
return completeStep(region)
end
return newIndex
end
function Walk.canMove()
return Memory.value("player", "moving") == 0 and Memory.value("player", "fighting") == 0
end
-- Custom path
function Walk.invertCustom(silent)
if not silent then
customIdx = customIdx + customDir
end
customDir = customDir * -1
end
function Walk.custom(cpath, increment)
if not cpath then
customIdx = 1
customDir = 1
return
end
if increment then
customIdx = customIdx + customDir
end
local tile = cpath[customIdx]
if not tile then
if customIdx < 1 then
customIdx = #cpath
else
customIdx = 1
end
return customIdx
end
local t1, t2 = tile[1], tile[2]
if t2 == nil then
if Player.face(t1) then
Input.press("A", 2)
end
return t1
end
if step(t1, t2) then
customIdx = customIdx + customDir
end
end
return Walk

File diff suppressed because it is too large Load Diff

995
ai/any%/strategies.lua Normal file
View File

@ -0,0 +1,995 @@
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
local status = {tries = 0, canProgress = nil, initialized = false}
local stats = {}
Strategies.status = status
Strategies.stats = stats
Strategies.updates = {}
Strategies.deepRun = false
local strategyFunctions
-- RISK/RESET
function Strategies.getTimeRequirement(name)
return Strategies.timeRequirements[name]()
end
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
function Strategies.death(extra)
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
function Strategies.setYolo(name)
if not RESET_FOR_TIME then
return false
end
local minimumTime = Strategies.getTimeRequirement(name)
local shouldYolo = Strategies.overMinute(minimumTime)
if Control.yolo ~= shouldYolo then
Control.yolo = shouldYolo
Control.setYolo(shouldYolo)
local prefix
if Control.yolo then
prefix = "en"
else
prefix = "dis"
end
print("YOLO "..prefix.."abled at "..Control.areaName)
end
return Control.yolo
end
-- HELPERS
function Strategies.tweetProgress(message, progress)
if progress then
Strategies.updates[progress] = true
message = message.." http://www.twitch.tv/thepokebot"
end
Bridge.tweet(message)
end
function Strategies.initialize()
if not status.initialized then
status.initialized = true
return true
end
end
function Strategies.canHealFor(damage)
local curr_hp = Pokemon.index(0, "hp")
local max_hp = Pokemon.index(0, "max_hp")
if max_hp - curr_hp > 3 then
local healChecks = {"full_restore", "super_potion", "potion"}
for idx,potion in ipairs(healChecks) do
if Inventory.contains(potion) and Utils.canPotionWith(potion, damage, curr_hp, max_hp) then
return potion
end
end
end
end
function Strategies.hasHealthFor(opponent, extra)
if not extra then
extra = 0
end
local afterHealth = math.min(Pokemon.index(0, "hp") + extra, Pokemon.index(0, "max_hp"))
return afterHealth > Combat.healthFor(opponent)
end
function Strategies.damaged(factor)
if not factor then
factor = 1
end
return Pokemon.index(0, "hp") * factor < Pokemon.index(0, "max_hp")
end
function Strategies.opponentDamaged(factor)
if not factor then
factor = 1
end
return Memory.double("battle", "opponent_hp") * factor < Memory.double("battle", "opponent_max_hp")
end
function Strategies.buffTo(buff, defLevel)
if Battle.isActive() then
status.canProgress = true
local forced
if defLevel and Memory.double("battle", "opponent_defense") > defLevel then
forced = buff
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
function Strategies.completedMenuFor(data)
local count = Inventory.count(data.item)
if count == 0 or (status.startCount and count + (data.amount or 1) <= status.startCount) then
return true
end
return false
end
function Strategies.closeMenuFor(data)
if (not status.menuOpened and not data.close) or data.chain then
return true
end
return Menu.close()
end
function Strategies.useItem(data)
local main = Memory.value("menu", "main")
if not status.startCount then
status.startCount = Inventory.count(data.item)
if status.startCount == 0 then
if Strategies.closeMenuFor(data) then
return true
end
return false
end
end
if Strategies.completedMenuFor(data) then
if Strategies.closeMenuFor(data) then
return true
end
elseif Menu.pause() then
status.menuOpened = true
Inventory.use(data.item, data.poke)
end
end
local function completedSkillFor(data)
if data.map then
if data.map ~= Memory.value("game", "map") then
return true
end
elseif data.x or data.y then
local px, py = Player.position()
if data.x == px or data.y == py then
return true
end
elseif data.done then
if Memory.raw(data.done) > (data.val or 0) then
return true
end
elseif status.tries > 0 and not Menu.isOpen() then
return true
end
return false
end
function Strategies.isPrepared(...)
if not status.preparing then
return false
end
for i,name in ipairs(arg) do
local currentCount = Inventory.count(name)
if currentCount > 0 then
local previousCount = status.preparing[name]
if previousCount == nil or currentCount == previousCount then
return false
end
end
end
return true
end
function Strategies.prepare(...)
if not status.preparing then
status.preparing = {}
end
local item
for idx,name in ipairs(arg) do
local currentCount = Inventory.count(name)
local needsItem = currentCount > 0
local previousCount = status.preparing[name]
if previousCount == nil then
status.preparing[name] = currentCount
elseif needsItem then
needsItem = currentCount == previousCount
end
if needsItem then
item = name
break
end
end
if not item then
return true
end
if Battle.isActive() then
Inventory.use(item, nil, true)
else
Input.cancel()
end
end
-- GENERALIZED STRATEGIES
Strategies.functions = {
startFrames = function()
Strategies.frames = 0
return true
end,
reportFrames = function()
print("FR "..Strategies.frames)
local repels = Memory.value("player", "repel")
if repels > 0 then
print("S "..repels)
end
Strategies.frames = nil
return true
end,
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,
item = function(data)
if Battle.handleWild() then
if data.full and not Inventory.isFull() then
if Strategies.closeMenuFor(data) then
return true
end
return false
end
return Strategies.useItem(data)
end
end,
potion = function(data)
local curr_hp = Pokemon.index(0, "hp")
if curr_hp == 0 then
return false
end
local toHP
if Control.yolo and data.yolo ~= nil then
toHP = data.yolo
else
toHP = data.hp
end
if type(toHP) == "string" then
toHP = Combat.healthFor(toHP)
end
local toHeal = toHP - curr_hp
if toHeal > 0 then
local toPotion
if data.forced then
toPotion = Inventory.contains(data.forced)
else
local p_first, p_second, p_third
if toHeal > 50 then
if data.full then
p_first = "full_restore"
else
p_first = "super_potion"
end
p_second, p_third = "super_potion", "potion"
else
if toHeal > 20 then
p_first, p_second = "super_potion", "potion"
else
p_first, p_second = "potion", "super_potion"
end
if data.full then
p_third = "full_restore"
end
end
toPotion = Inventory.contains(p_first, p_second, p_third)
end
if toPotion then
if Menu.pause() then
Inventory.use(toPotion)
status.menuOpened = true
end
return false
end
--TODO report wanted potion
end
if Strategies.closeMenuFor(data) then
return true
end
end,
teach = function(data)
if data.full and not Inventory.isFull() then
return true
end
local itemName
if data.item then
itemName = data.item
else
itemName = data.move
end
if Pokemon.hasMove(data.move) then
local main = Memory.value("menu", "main")
if main == 128 then
if data.chain then
return true
end
Input.press("B")
elseif Menu.close() then
return true
end
else
if Strategies.initialize() then
if not Inventory.contains(itemName) then
return Strategies.reset("Unable to teach move "..itemName.." to "..data.poke, nil, true)
end
end
local replacement
if data.replace then
replacement = Pokemon.moveIndex(data.replace, data.poke) - 1
else
replacement = 0
end
if Inventory.teach(itemName, data.poke, replacement, data.alt) then
status.menuOpened = true
else
Menu.pause()
end
end
end,
skill = function(data)
if completedSkillFor(data) then
if not Textbox.isActive() then
return true
end
Input.press("B")
elseif not data.dir or Player.face(data.dir) then
if Pokemon.use(data.move) then
status.tries = status.tries + 1
else
Menu.pause()
end
end
end,
fly = function(data)
if Memory.value("game", "map") == data.map then
return true
end
local cities = {
pallet = {62, "Up"},
viridian = {63, "Up"},
lavender = {66, "Down"},
celadon = {68, "Down"},
fuchsia = {69, "Down"},
cinnabar = {70, "Down"},
}
local main = Memory.value("menu", "main")
if main == 228 then
local currentFly = Memory.raw(0x1FEF)
local destination = cities[data.dest]
local press
if destination[1] - currentFly == 0 then
press = "A"
else
press = destination[2]
end
Input.press(press)
elseif not Pokemon.use("fly") then
Menu.pause()
end
end,
bicycle = function()
if Memory.raw(0x1700) == 1 then
if Textbox.handle() then
return true
end
else
return Strategies.useItem({item="bicycle"})
end
end,
swap = function(data)
if Strategies.initialize() then
if not Inventory.contains(data.item) then
return true
end
end
local itemIndex = Inventory.indexOf(data.item)
local destIndex = data.dest
if type(destIndex) == "string" then
destIndex = Inventory.indexOf(destIndex)
end
if itemIndex == destIndex then
if Strategies.closeMenuFor(data) then
return true
end
else
local main = Memory.value("menu", "main")
if main == 128 then
if Menu.getCol() ~= 5 then
Menu.select(2, true)
else
local selection = Memory.value("menu", "selection_mode")
if selection == 0 then
if Menu.select(destIndex, "accelerate", true, nil, true) then
Input.press("Select")
end
else
if Menu.select(itemIndex, "accelerate", true, nil, true) then
Input.press("Select")
end
end
end
else
Menu.pause()
end
end
end,
wait = function()
print("Please save state")
Input.press("Start", 999999999)
end,
emuSpeed = function(data)
-- client.speedmode = data.percent
return true
end,
waitToTalk = function()
if Battle.isActive() then
status.canProgress = false
Battle.automate()
elseif Textbox.isActive() then
status.canProgress = true
Input.cancel()
elseif status.canProgress then
return true
end
end,
waitToPause = function()
local main = Memory.value("menu", "main")
if main == 128 then
if status.canProgress then
return true
end
elseif Battle.isActive() then
status.canProgress = false
Battle.automate()
elseif main == 123 then
status.canProgress = true
Input.press("B")
elseif Textbox.handle() then
Input.press("Start", 2)
end
end,
waitToFight = function(data)
if Battle.isActive() then
status.canProgress = true
Battle.automate()
elseif status.canProgress then
return true
elseif Textbox.handle() then
if data.dir then
Player.interact(data.dir)
else
Input.cancel()
end
end
end,
allowDeath = function(data)
Control.canDie(data.on)
return true
end,
leer = function(data)
local bm = Combat.bestMove()
if not bm or bm.minTurns < 3 then
if Battle.isActive() then
status.canProgress = true
elseif status.canProgress then
return true
end
Battle.automate()
return false
end
local opp = Battle.opponent()
local defLimit = 9001
for i,poke in ipairs(data) do
if opp == poke[1] then
local minimumAttack = poke[3]
if not minimumAttack or stats.nidoran.attack > minimumAttack then
defLimit = poke[2]
end
break
end
end
return Strategies.buffTo("leer", defLimit)
end,
-- ROUTE
swapNidoran = function()
local main = Memory.value("menu", "main")
local nidoranIndex = Pokemon.indexOf("nidoran")
if nidoranIndex == 0 then
if Menu.close() then
return true
end
elseif Menu.pause() then
if yellow then
if Inventory.contains("potion") and Pokemon.info("nidoran", "hp") < 15 then
Inventory.use("potion", "nidoran")
return false
end
else
if Pokemon.info("squirtle", "status") > 0 then
Inventory.use("antidote", "squirtle")
return false
end
if Inventory.contains("potion") and Pokemon.info("squirtle", "hp") < 15 then
Inventory.use("potion", "squirtle")
return false
end
end
local column = Menu.getCol()
if main == 128 then
if column == 11 then
Menu.select(1, true)
elseif column == 12 then
Menu.select(1, true)
else
Input.press("B")
end
elseif main == Menu.pokemon then --TODO check loop
if Memory.value("menu", "selection_mode") == 1 then
Menu.select(nidoranIndex, true)
else
Menu.select(0, true)
end
else
Input.press("B")
end
end
end,
swapHornAttack = function()
if Pokemon.battleMove("horn_attack") == 1 then
return true
end
Battle.swapMove(1, 3)
end,
dodgePalletBoy = function()
return Strategies.dodgeUp(0x0223, 14, 14, 15, 7)
end,
evolveNidorino = function()
if Pokemon.inParty("nidorino") then
Bridge.caught("nidorino")
return true
end
if Battle.isActive() then
status.tries = 0
status.canProgress = true
if Memory.double("battle", "opponent_hp") == 0 then
Input.press("A")
else
Battle.automate()
end
elseif status.tries > 3600 then
print("Broke from Nidorino on tries")
return true
else
if status.canProgress then
status.tries = status.tries + 1
end
Input.press("A")
end
end,
catchFlierBackup = function()
if Strategies.initialize() then
Control.canDie(true)
end
if not Control.canCatch() then
return true
end
local caught = Pokemon.inParty("pidgey", "spearow")
if Battle.isActive() then
if Memory.double("battle", "our_hp") == 0 then
if Pokemon.info("squirtle", "hp") == 0 then
Control.canDie(false)
elseif Utils.onPokemonSelect(Memory.value("battle", "menu")) then
Menu.select(Pokemon.indexOf("squirtle"), true)
else
Input.press("A")
end
else
Battle.handle()
end
else
local birdPath
local px, py = Player.position()
if caught then
if px > 33 then
return true
end
local startY = 9
if px > 28 then
startY = py
end
birdPath = {{32,startY}, {32,11}, {34,11}}
elseif px == 37 then
if py == 10 then
py = 11
else
py = 10
end
Walk.step(px, py)
else
birdPath = {{32,10}, {32,11}, {34,11}, {34,10}, {37,10}}
end
if birdPath then
Walk.custom(birdPath)
end
end
end,
evolveNidoking = function(data)
if Battle.handleWild() then
local usedMoonStone = not Inventory.contains("moon_stone")
if Strategies.initialize() then
if usedMoonStone then
return true
end
if data.early then
if not Control.getMoonExp then
return true
end
if data.poke then
if stats.nidoran.attack > 15 or not Pokemon.inParty(data.poke) then
return true
end
end
if data.exp and Pokemon.getExp() > data.exp then
return true
end
end
end
if usedMoonStone then
if not status.canProgress then
Bridge.caught("nidoking")
status.canProgress = true
end
if Menu.close() then
return true
end
elseif not Inventory.use("moon_stone") then
Menu.pause()
end
end
end,
helix = function()
if Battle.handleWild() then
if Inventory.contains("helix_fossil") then
return true
end
Player.interact("Up")
end
end,
reportMtMoon = function()
if Battle.pp("horn_attack") == 0 then
print("ERR: Ran out of Horn Attacks")
end
if Control.moonEncounters then
local catchPokemon = yellow and "sandshrew" or "paras"
local capsName = Utils.capitalize(catchPokemon)
local parasStatus
local conjunction = "but"
local goodEncounters = Control.moonEncounters < 10
local catchDescription
if Pokemon.inParty(catchPokemon) then
catchDescription = catchPokemon
if goodEncounters then
conjunction = "and"
end
parasStatus = "we caught a "..capsName.."!"
else
catchDescription = "no_"..catchPokemon
if not goodEncounters then
conjunction = "and"
end
parasStatus = "we didn't catch a "..capsName.." :("
end
Bridge.caught(catchDescription)
Bridge.chat(Control.moonEncounters.." Moon encounters, "..conjunction.." "..parasStatus)
Control.moonEncounters = nil
end
Strategies.resetTime("mt_moon", "complete Mt. Moon", true)
return true
end,
dodgeCerulean = function()
return dodgeH{
npc = 0x0242,
sx = 14, sy = 18,
dodge = 19,
offset = 10,
dist = 4
}
end,
dodgeCeruleanLeft = function()
return dodgeH{
npc = 0x0242,
sx = 16, sy = 18,
dodge = 17,
offset = 10,
dist = -7,
left = true
}
end,
playPokeflute = function()
if Battle.isActive() then
return true
end
if Memory.value("battle", "menu") == 95 then
Input.press("A")
elseif Menu.pause() then
Inventory.use("pokeflute")
end
end,
push = function(data)
local pos
if data.dir == "Up" or data.dir == "Down" then
pos = data.y
else
pos = data.x
end
local newP = Memory.raw(pos)
if not status.startPosition then
status.startPosition = newP
elseif status.startPosition ~= newP then
return true
end
Input.press(data.dir, 0)
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
Strategies.initGame(midGame)
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

View File

@ -0,0 +1,331 @@
local lowerGameRun = string.lower(GAME_RUN)
local Strategies = require("ai."..lowerGameRun..".strategies")
local Combat = require "ai.combat"
local Control = require "ai.control"
local Battle = require "action.battle"
local Shop = require "action.shop"
local Textbox = require "action.textbox"
local Walk = require "action.walk"
local Bridge = require "util.bridge"
local Input = require "util.input"
local Memory = require "util.memory"
local Menu = require "util.menu"
local Player = require "util.player"
local Utils = require "util.utils"
local Inventory = require "storage.inventory"
local Pokemon = require "storage.pokemon"
local status = Strategies.status
local stats = Strategies.stats
local strategyFunctions = Strategies.functions
-- TIME CONSTRAINTS
Strategies.timeRequirements = {
nidoran = function()
local timeLimit = 8
if Pokemon.inParty("pidgey") then
timeLimit = timeLimit + 0.67
end
return timeLimit
end,
mt_moon = function()
local timeLimit = 30
if stats.nidoran.attack > 15 and stats.nidoran.speed > 14 then
timeLimit = timeLimit + 0.25
end
if Pokemon.inParty("sandshrew") then
timeLimit = timeLimit + 0.25
end
return timeLimit
end,
}
-- HELPERS
local function depositPikachu()
if not Textbox.isActive() then
Player.interact("Up")
else
local pc = Memory.value("menu", "size")
if Memory.value("battle", "menu") ~= 19 then
local menuColumn = Menu.getCol()
if menuColumn == 5 then
Menu.select(Pokemon.indexOf("pikachu"))
elseif menuColumn == 10 then
Input.press("A")
elseif pc == 3 then
Menu.select(0)
elseif pc == 5 then
Menu.select(1)
else
Input.cancel()
end
else
Input.cancel()
end
end
end
local function takeCenter(pp, startMap, entranceX, entranceY, finishX)
local px, py = Player.position()
local currentMap = Memory.value("game", "map")
local sufficientPP = Pokemon.pp(0, "horn_attack") > pp
if currentMap == startMap then
if not sufficientPP then
if px ~= entranceX then
px = entranceX
else
py = entranceY
end
else
if px == finishX then
return true
end
px = finishX
end
else
if Pokemon.inParty("pikachu") then
if py > 5 then
py = 5
elseif px < 13 then
px = 13
elseif py ~= 4 then
py = 4
else
return depositPikachu()
end
else
if px ~= 3 then
if Menu.close() then
px = 3
end
elseif sufficientPP then
if Textbox.handle() then
py = 8
end
elseif py > 3 then
py = 3
else
strategyFunctions.confirm({dir="Up"})
end
end
end
Walk.step(px, py)
end
-- STRATEGIES
-- dodgePalletBoy
strategyFunctions.shopViridianPokeballs = function()
return Shop.transaction{
buy = {{name="pokeball", index=0, amount=4}, {name="potion", index=1, amount=6}}
}
end
strategyFunctions.catchNidoran = function()
if not Control.canCatch() then
return true
end
local pokeballs = Inventory.count("pokeball")
local caught = Memory.value("player", "party_size") > 1
if pokeballs < (caught and 1 or 2) then
return Strategies.reset("Ran too low on PokeBalls", pokeballs)
end
if Battle.isActive() then
local isNidoran = Pokemon.isOpponent("nidoran")
if isNidoran and Memory.value("battle", "opponent_level") == 6 then
if Strategies.initialize() then
Bridge.pollForName()
end
end
status.tries = nil
if Memory.value("menu", "text_input") == 240 then
Textbox.name()
elseif Memory.value("battle", "menu") == 95 then
if isNidoran then
Input.press("A")
else
Input.cancel()
end
else
Battle.handle()
end
else
Pokemon.updateParty()
local hasNidoran = Pokemon.inParty("nidoran")
if hasNidoran then
Bridge.caught("nidoran")
return true
end
local timeLimit = Strategies.getTimeRequirement("nidoran")
local resetMessage = "find a suitable Nidoran"
if Strategies.resetTime(timeLimit, resetMessage) then
return true
end
local px, py = Player.position()
if py > 48 then
py = 48
elseif px < 9 then
px = 9
else
px = 8
end
Walk.step(px, py) --TODO DSum
end
end
-- leer
strategyFunctions.checkNidoStats = function()
local nidx = Pokemon.indexOf("nidoran")
if Pokemon.index(nidx, "level") == 8 then
local att = Pokemon.index(nidx, "attack")
local def = Pokemon.index(nidx, "defense")
local spd = Pokemon.index(nidx, "speed")
local scl = Pokemon.index(nidx, "special")
Bridge.stats(att.." "..def.." "..spd.." "..scl)
stats.nidoran = {
attack = att,
defense = def,
speed = spd,
special = scl,
}
local statDiff = (16 - att) + (15 - spd) + (13 - scl)
local resets = att < 15 or spd < 14 or scl < 12 --RISK
local nStatus = "Att: "..att..", Def: "..def..", Speed: "..spd..", Special: "..scl
if resets then
return Strategies.reset("Bad Nidoran - "..nStatus)
end
-- if def < 12 then
-- statDiff = statDiff + 1
-- end
local superlative
local exclaim = "!"
if statDiff == 0 then
if def == 14 then
superlative = "God"
exclaim = "! Kreygasm"
else
superlative = "Perfect"
end
elseif att == 16 and spd == 15 then
if statDiff == 1 then
superlative = "Great"
elseif statDiff == 2 then
superlative = "Good"
else
superlative = "Okay"
end
elseif statDiff == 1 then
superlative = "Good"
elseif statDiff == 2 then
superlative = "Okay"
exclaim = "."
else
superlative = "Min stat"
exclaim = "."
end
nStatus = superlative.." Nidoran"..exclaim.." "..nStatus
Bridge.chat(nStatus)
return true
end
end
strategyFunctions.centerViridian = function()
return takeCenter(15, 2, 13, 25, 18)
end
strategyFunctions.fightBrock = function()
local curr_hp = Pokemon.info("nidoran", "hp")
if curr_hp == 0 then
return Strategies.death()
end
if Battle.isActive() then
status.canProgress = true
local __, turnsToKill, turnsToDie = Combat.bestMove()
if turnsToDie and turnsToDie < 2 and Inventory.contains("potion") then
Inventory.use("potion", "nidoran", true)
else
local bideTurns = Memory.value("battle", "opponent_bide")
if bideTurns > 0 then
local onixHP = Memory.double("battle", "opponent_hp")
if status.tries == 0 then
status.tries = onixHP
status.startBide = bideTurns
end
if turnsToKill then
local forced
if turnsToKill < 2 or status.startBide - bideTurns > 1 then
-- elseif turnsToKill < 3 and status.startBide == bideTurns then
elseif onixHP == status.tries then
forced = "leer"
end
Battle.fight(forced)
else
Input.cancel()
end
else
status.tries = 0
strategyFunctions.leer({{"onix", 13}})
end
end
elseif status.canProgress then
return true
elseif Textbox.handle() then
Player.interact("Up")
end
end
strategyFunctions.centerMoon = function()
return takeCenter(5, 15, 11, 5, 12)
end
-- reportMtMoon
-- PROCESS
function Strategies.initGame(midGame)
if not STREAMING_MODE then
-- Strategies.setYolo("")
if Pokemon.inParty("nidoking") then
stats.nidoran = {
attack = 55,
defense = 45,
speed = 50,
special = 45,
}
else
stats.nidoran = {
attack = 16,
defense = 12,
speed = 15,
special = 13,
level4 = true,
}
end
end
end
function Strategies.completeGameStrategy()
status = Strategies.status
end
function Strategies.resetGame()
status = Strategies.status
stats = Strategies.stats
end
return Strategies

406
ai/combat.lua Normal file
View File

@ -0,0 +1,406 @@
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_I
normal = {normal=1.0, fighting=1.0, flying=1.0, poison=1.0, ground=1.0, rock=0.5, bug=1.0, ghost=0.0, fire=1.0, water=1.0, grass=1.0, electric=1.0, psychic=1.0, ice=1.0, dragon=1.0, },
fighting = {normal=2.0, fighting=1.0, flying=0.5, poison=0.5, ground=1.0, rock=2.0, bug=0.5, ghost=0.0, fire=1.0, water=1.0, grass=1.0, electric=1.0, psychic=0.5, ice=2.0, dragon=1.0, },
flying = {normal=1.0, fighting=2.0, flying=1.0, poison=1.0, ground=1.0, rock=0.5, bug=2.0, ghost=1.0, fire=1.0, water=1.0, grass=2.0, electric=0.5, psychic=1.0, ice=1.0, dragon=1.0, },
poison = {normal=1.0, fighting=1.0, flying=1.0, poison=0.5, ground=0.5, rock=0.5, bug=2.0, ghost=0.5, fire=1.0, water=1.0, grass=2.0, electric=1.0, psychic=1.0, ice=1.0, dragon=1.0, },
ground = {normal=1.0, fighting=1.0, flying=0.0, poison=2.0, ground=1.0, rock=2.0, bug=0.5, ghost=1.0, fire=2.0, water=1.0, grass=0.5, electric=2.0, psychic=1.0, ice=1.0, dragon=1.0, },
rock = {normal=1.0, fighting=0.5, flying=2.0, poison=1.0, ground=0.5, rock=1.0, bug=2.0, ghost=1.0, fire=2.0, water=1.0, grass=1.0, electric=1.0, psychic=1.0, ice=2.0, dragon=1.0, },
bug = {normal=1.0, fighting=0.5, flying=0.5, poison=2.0, ground=1.0, rock=1.0, bug=1.0, ghost=0.5, fire=0.5, water=1.0, grass=2.0, electric=1.0, psychic=2.0, ice=1.0, dragon=1.0, },
ghost = {normal=0.0, fighting=1.0, flying=1.0, poison=1.0, ground=1.0, rock=1.0, bug=1.0, ghost=2.0, fire=1.0, water=1.0, grass=1.0, electric=1.0, psychic=0.0, ice=1.0, dragon=1.0, },
fire = {normal=1.0, fighting=1.0, flying=1.0, poison=1.0, ground=1.0, rock=0.5, bug=2.0, ghost=1.0, fire=0.5, water=0.5, grass=2.0, electric=1.0, psychic=1.0, ice=2.0, dragon=0.5, },
water = {normal=1.0, fighting=1.0, flying=1.0, poison=1.0, ground=2.0, rock=2.0, bug=1.0, ghost=1.0, fire=2.0, water=0.5, grass=0.5, electric=1.0, psychic=1.0, ice=1.0, dragon=0.5, },
grass = {normal=1.0, fighting=1.0, flying=0.5, poison=0.5, ground=2.0, rock=2.0, bug=0.5, ghost=1.0, fire=0.5, water=2.0, grass=0.5, electric=1.0, psychic=1.0, ice=1.0, dragon=0.5, },
electric = {normal=1.0, fighting=1.0, flying=2.0, poison=1.0, ground=0.0, rock=1.0, bug=1.0, ghost=1.0, fire=1.0, water=2.0, grass=0.5, electric=0.5, psychic=1.0, ice=1.0, dragon=0.5, },
psychic = {normal=1.0, fighting=2.0, flying=1.0, poison=2.0, ground=1.0, rock=1.0, bug=1.0, ghost=1.0, fire=1.0, water=1.0, grass=1.0, electric=1.0, psychic=0.5, ice=1.0, dragon=1.0, },
ice = {normal=1.0, fighting=1.0, flying=2.0, poison=1.0, ground=2.0, rock=1.0, bug=1.0, ghost=1.0, fire=1.0, water=0.5, grass=2.0, electric=1.0, psychic=1.0, ice=0.5, dragon=2.0, },
dragon = {normal=1.0, fighting=1.0, flying=1.0, poison=1.0, ground=1.0, rock=1.0, bug=1.0, ghost=1.0, fire=1.0, water=1.0, grass=1.0, electric=1.0, psychic=1.0, ice=1.0, dragon=2.0, },
}
local types = {}
types[0] = "normal"
types[1] = "fighting"
types[2] = "flying"
types[3] = "poison"
types[4] = "ground"
types[5] = "rock"
types[7] = "bug"
types[8] = "ghost"
types[20] = "fire"
types[21] = "water"
types[22] = "grass"
types[23] = "electric"
types[24] = "psychic"
types[25] = "ice"
types[26] = "dragon"
local savedEncounters = {}
local enablePP = false
local floor = math.floor
local function isDisabled(mid)
return mid == Memory.value("battle", "disabled")
end
Combat.isDisabled = isDisabled
local function calcDamage(move, attacker, defender, rng)
if move.fixed then
return move.fixed, move.fixed
end
if move.power == 0 or isDisabled(move.id) then
return 0, 0
end
if move.power > 9000 then
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
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 = 0x0FED
else
base = 0x101C
end
for idx=0, 3 do
local val = Memory.raw(base + idx)
if val > 0 then
local moveTable = Movelist.get(val)
if who == 0 then
moveTable.pp = Memory.raw(0x102D + idx)
end
moves[idx + 1] = moveTable
end
end
return moves
end
Combat.getMoves = getMoves
local function modPlayerStats(user, enemy, move)
local effect = move.effects
if effect then
local diff = effect.diff
local hitThem = diff < 0
local stat = effect.stat
if hitThem then
enemy[stat] = math.max(2, enemy[stat] + diff)
else
user[stat] = user[stat] + diff
end
end
return user, enemy
end
local function calcBestHit(attacker, defender, ours, rng)
local bestTurns, bestMinTurns = 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"),
speed = Memory.double("battle", "our_speed"),
type1 = getOurType(0),
type2 = getOurType(1),
moves = getMoves(0),
}
local enemy
if preset then
enemy = Opponents[preset]
local toBoost = enemy.boost
if toBoost then
local currSpec = ours.spec
local booster = toBoost.mp
if (currSpec < 140) == (booster > 1) then
ours.spec = math.floor(currSpec * booster)
end
end
else
enemy = {
id = Memory.value("battle", "opponent_id"),
level = Memory.value("battle", "opponent_level"),
hp = Memory.double("battle", "opponent_hp"),
att = Memory.double("battle", "opponent_attack"),
def = Memory.double("battle", "opponent_defense"),
spec = Memory.double("battle", "opponent_special"),
speed = Memory.double("battle", "opponent_speed"),
type1 = getOpponentType(0),
type2 = getOpponentType(1),
moves = getMoves(1),
}
end
return ours, enemy
end
Combat.activePokemon = activePokemon
local function isSleeping()
return Memory.raw(0x116F) > 1
end
Combat.isSleeping = isSleeping
local function isConfused()
return Memory.raw(0x106B) > 0
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

399
ai/control.lua Normal file
View File

@ -0,0 +1,399 @@
local Control = {}
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
if lowerGameName == "yellow" then
secondStratDir = ".yellow"
else
secondStratDir = ".red-blue"
end
end
local Battle
local Combat = require "ai.combat"
local Strategies
local Bridge = require "util.bridge"
local Memory = require "util.memory"
local Paint = require("util."..lowerGameRun..secondPaintDir..".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
local yellow = YELLOW
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,
viridianExpGlitch = function()
minExp = 236
shouldFight = {{name="rattata"}, {name="pidgey",lvl={2,3}}}
end,
viridianBackupExpGlitch = function()
minExp = 236
shouldFight = {{name="rattata"}, {name="pidgey",lvl={2,3}}}
end,
nidoranBackupExp = function()
minExp = 210
shouldFight = {{name="rattata"}, {name="pidgey"}, {name="nidoran"}, {name="nidoranf",lvl={2}}}
end,
pidgeyBackupExp = function()
minExp = 236
shouldFight = {{name="rattata"}, {name="pidgey"}, {name="nidoran"}, {name="nidoranf"}}
end,
startMtMoon = function()
Control.moonEncounters = 0
Control.canDie(false)
Control.getMoonExp = true
if not yellow then
local nidoStats = Strategies.stats.nidoran
if nidoStats.attack == 16 and not nidoStats.level4 then
Control.getMoonExp = false
end
end
end,
moon1Exp = function()
if Control.getMoonExp then
minExp = 2704
local levels = Strategies.stats.nidoran.level4 and {9, 10} or {10}
shouldFight = {{name="zubat",lvl=levels}}
oneHits = true
end
end,
moon2Exp = function()
if Control.getMoonExp then
minExp = 3011
shouldFight = {{name="zubat"}, {name="paras"}}
oneHits = not withinOneKill(minExp)
end
end,
moon3Exp = function()
if Control.getMoonExp then
local expTotal = Pokemon.getExp()
minExp = 3798
if withinOneKill(minExp) then
shouldFight = {{name="zubat"}, {name="paras"}}
else
shouldFight = nil
end
end
end,
-- CATCH
catchPidgey = function()
shouldCatch = {{name="pidgey"}}
end,
catchNidoran = function()
shouldCatch = {{name="nidoran",lvl={3,4}}, {name="spearow"}}
end,
catchFlier = function()
shouldCatch = {{name="spearow",alt="pidgey",hp=15}, {name="pidgey",alt="spearow",hp=15}}
end,
catchParas = function()
shouldCatch = {{name="paras",hp=16}}
end,
catchOddish = function()
shouldCatch = {{name="oddish",alt="paras",hp=26}}
end,
-- YELLOW
catchNidoranYellow = function()
shouldCatch = {{name="nidoran",lvl={6}}}
end,
moonExpYellow = function()
minExp = 2704 --TODO
shouldFight = {{name="geodude"}, {name="clefairy",lvl={12,13}}}
oneHits = true
end,
catchSandshrew = function()
shouldCatch = {{name="sandshrew"}}
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 = (yellow and 3 or 4) - partySize
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 = battleState == 1
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
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()
Battle = require("action.battle")
Strategies = require("ai."..lowerGameRun..secondStratDir..".strategies")
end
return Control

View File

@ -0,0 +1,929 @@
local lowerGameRun = string.lower(GAME_RUN)
local Strategies = require("ai."..lowerGameRun..".strategies")
local Combat = require "ai.combat"
local Control = require "ai.control"
local Battle = require "action.battle"
local Shop = require "action.shop"
local Textbox = require "action.textbox"
local Walk = require "action.walk"
local Bridge = require "util.bridge"
local Input = require "util.input"
local Memory = require "util.memory"
local Menu = require "util.menu"
local Player = require "util.player"
local Utils = require "util.utils"
local Inventory = require "storage.inventory"
local Pokemon = require "storage.pokemon"
local 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.bulbasaurIChooseYou = function()
if Pokemon.inParty("bulbasaur") then
Bridge.caught("bulbasaur")
return true
end
if Player.face("Up") then
Textbox.name(BULBASAUR_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

View File

@ -0,0 +1,545 @@
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
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")
status.tempDir = true
else
if status.tempDir then
status.tempDir = false
return true
else
Input.press("A")
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

View File

@ -0,0 +1,453 @@
local Paths = {
-- Red's room
{38, {3,6}, {5,6}, {5,1}, {7,1}},
-- Red's house
{39, {7,1}, {7,6}, {3,6}, {3,8}},
-- Into the Wild
{0, {5,6}, {10,6}, {10,1}},
-- Choose your character!
{40, {5,3}, {c="a",a="Pallet Rival"}, {5,4}, {7,4}, {s="squirtleIChooseYou"}, {5,4}, {5,6}, {s="fightBulbasaur"}, {s="split"}, {5,12}},
-- 1: RIVAL 1
-- Let's try this escape again
{0, {12,12}, {c="a",a="Pallet Town"}, {c="viridianExp"}, {c="encounters",limit=4}, {9,12}, {9,2}, {10,2}, {10,-1}},
-- First encounters
{12, {10,35}, {10,30}, {8,30}, {8,24}, {12,24}, {12,20}, {9,20}, {9,14}, {14,14}, {s="dodgePalletBoy"}, {14,2}, {11,2}, {11,-1}},
-- To the Mart
{1, {21,35}, {21,30}, {19,30}, {19,20}, {29,20}, {29,19}},
-- Viridian Mart
{42, {2,5}, {3,5}, {3,8}},
-- Backtracking
{1, {29,20}, {c="encounters",limit=5}, {29,21}, {26,21}, {26,30}, {20,30}, {20,36}},
-- Parkour
{12, {10, 0}, {10,3}, {8,3}, {8,18}, {9,18}, {9,22}, {12,22}, {12,24}, {10,24}, {10,36}},
-- To Oak's lab
{0, {10,0}, {10,7}, {9,7}, {9,12}, {12,12}, {12,11}},
-- Parcel delivery
{40, {5,11}, {5,3}, {4,3}, {4,1}, {5,1}, {s="interact",dir="Down"}, {4,1}, {4,12}},
-- Leaving home
{0, {12,12}, {c="viridianBackupExp"}, {9,12}, {9,2}, {10,2}, {10,-1}},
-- The grass again!?
{12, {10,35}, {10,30}, {8,30}, {8,24}, {12,24}, {12,20}, {9,20}, {9,14}, {14,14}, {s="dodgePalletBoy"}, {14,2}, {11,2}, {11,-1}},
-- Back to the Mart
{1, {21,35}, {21,30}, {19,30}, {19,20}, {29,20}, {29,19}},
-- Viridian Mart redux
{42, {3,7}, {3,5}, {2,5}, {s="shopViridianPokeballs"}, {3,5}, {3,8}},
-- Sidequest
{1, {29,20}, {15,20}, {15,17}, {-1, 17}},
-- Nidoran
{33, {39, 9}, {c="a",a="Nidoran grass"}, {c="nidoranBackupExp"}, {c="encounters",limit=7,extra="spearow"}, {35, 9}, {35,12}, {33,12}, {c="catchNidoran"}, {s="catchNidoran"}, {33,12}, {s="split"}, {37,12}, {37,9}, {40,9}},
-- 2: NIDORAN
-- Out of Viridian City
{1, {0,17}, {c="encounters",limit=10,extra="spearow"}, {16,17}, {16,16}, {18,16}, {18,6}, {s="dodgeViridianOldMan"}, {17,4}, {s="grabTreePotion"}, {17,4}, {17, 0}, {17, -1}},
-- To the Forest
{13, {7,71}, {7,57}, {4,57}, {4,52}, {10,52}, {10,44}, {3,44}, {3,43}},
-- Forest entrance
{50, {4,7}, {c="a",a="Viridian Forest"}, {4,1}, {5,1}, {5,0}},
-- Viridian Forest
{51, {17,47}, {17,43}, {26,43}, {26,34}, {25,34}, {25,32}, {27,32}, {27,20}, {25,20}, {25,12}, {s="grabAntidote"}, {25,9}, {17,9}, {17,16}, {13,16}, {13,3}, {7,3}, {7,22}, {1,22}, {1,19}, {s="grabForestPotion"}, {1,18}, {s="fightWeedle"}, {c="encounters",limit=22,extra="paras"}, {1,16}, {c="potion",b=false}, {s="equipForBrock",anti=true}, {1,5}, {s="equipForBrock"}, {1,-1}},
-- Forest exit
{47, {4,7}, {4,1}, {5,1}, {5,0}},
-- Road to Pewter City
{13, {3,11}, {c="a",a="Pewter City"}, {3,8}, {8,8}, {8,-1}},
-- Pewter City
{2, {18,35}, {18,22}, {19,22}, {19,13}, {10,13}, {10,18}, {16,18}, {16,17}},
-- Brock
{54, {4,13}, {c="a",a="Brock's Gym"}, {4,8}, {1,8}, {1,4}, {4,4}, {4,2}, {s="interact",dir="Up"}, {s="fightBrock"}, {s="split"}, {s="emuSpeed",percent=100}, {4,14}},
-- 3: BROCK
-- To Pewter Mart
{2, {16,18}, {c="potion",b=true}, {10,18}, {10,13}, {21,13}, {21,18}, {23,18}, {23,17}},
-- Pewter Mart
{56, {3,7}, {3,5}, {2,5}, {s="shopPewterMart"}, {2,6}, {3,6}, {3,8}},
-- Leaving Pewter City
{2, {23,18}, {40,18}},
-- Route 3
{14, {0,10}, {c="a",a="Route 3"}, {c="catchFlier"}, {c="pp",on=true}, {s="battleModeSet"}, {8,10}, {8,8}, {11,8}, {11,6}, {s="bugCatcher"}, {11,4}, {12,4}, {s="potion",hp=19}, {13,4}, {s="interact",dir="Right"}, {s="shortsKid"}, {s="tweetAfterBrock"}, {13,5}, {s="potionBeforeCocoons"}, {18,5}, {s="interact",dir="Right"}, {s="swapHornAttack"}, {18,6}, {22,6}, {22,5}, {s="potion",hp=4}, {24,5}, {s="interact",dir="Down"}, {s="fightMetapod"}, {27,5}, {27,9}, {s="catchFlierBackup"}, {37,8}, {37,5}, {49,5}, {49,10}, {57,10}, {57,8}, {59,8}, {59,-1}},
-- To the Center
{15, {9,16}, {c="pp",on=false}, {12,16}, {12,6}, {11,6}, {11,5}},
-- PP up
{68, {3,7}, {3,3}, {s="confirm",dir="Up"}, {3,8}},
-- Enter Mt. Moon
{15, {11,6}, {c="a",a="Mt. Moon"}, {18,6}, {s="split"}, {18,5}},
-- 4: ROUTE 3
-- Mt. Moon F1
{59, {14,35}, {c="startMtMoon"}, {c="catchParas"}, {14,22}, {21,22}, {21,15}, {24,15}, {24,27}, {25,27}, {25,31}, {s="interact",dir="Left"}, {25,32}, {33,32}, {33,31}, {34,31}, {s="interact",dir="Right"}, {35,31}, {35,23}, {s="interact",dir="Right"}, {35,7}, {30,7}, {s="evolveNidorino"}, {c="moon1Exp"}, {28,7}, {16,7}, {16,17}, {2,17}, {2,3}, {s="interact",dir="Up"}, {5,3}, {5,5}},
-- Mt. Moon B2
{60, {5,5}, {5,17}, {21,17}},
-- Mt. Moon B3
{61, {21,17}, {22,17}, {s="evolveNidoking",early=true,poke="paras"}, {23,17}, {23,14}, {27,14}, {27,16}, {33,16}, {33,14}, {36,14}, {36,24}, {32, 24}, {32,31}, {10,31}, {10,18}, {s="evolveNidoking"}, {c="encounters",limit=nil}, {10,17}, {12,17}, {c="moon2Exp"}, {12,9}, {s="potion",hp=7}, {s="interact",dir="Up"}, {13,9}, {c="moon3Exp"}, {13,7}, {s="helix"}, {13,5}, {12,5}, {12,4}, {3,4}, {3,7}, {5,7}},
-- Mt. Moon escape
{60, {23,3}, {27,3}},
-- 5: MT. MOON
-- To Cerulean
{15, {24,6}, {s="reportMtMoon"}, {s="split"}, {24,8}, {35,8}, {35,10}, {61,10}, {61,8}, {79,8}, {79,10}, {90,10}},
-- Enter Cerulean
{3, {0,18}, {c="a",a="Cerulean"}, {14,18}, {s="dodgeCerulean"}, {19,18}, {19,17}},
-- Cerulean Center
{64, {3,7}, {3,3}, {s="confirm",dir="Up"}, {3,8}},
-- To the house
{3, {19,18}, {16,18}, {s="dodgeCeruleanLeft"}, {8,16}, {8,12}, {9,12}, {9,11}},
-- In the house
{230, {2,7}, {2,0}},
-- Outback
{3, {9,9}, {9,8}, {14,8}, {s="interact",dir="Right"}, {9,8}, {9,10}},
-- Out the house
{230, {2,1}, {2,8}},
-- Rival 2
{3, {9,12}, {c="a",a="Cerulean Rival"}, {21,12}, {21,6}, {s="rivalSandAttack"}, {21,-1}},
-- Nugget bridge
--TODO RC early strat
{35, {11,35}, {c="a",a="Nugget Bridge"}, {11,32}, {s="interact",dir="Up"}, {10,32}, {10,29}, {s="interact",dir="Up"}, {11,29}, {11,27}, {s="potion",hp=4}, {11,26}, {s="interact",dir="Up"}, {10,26}, {10,24}, {s="teachThrash"}, {s="potion",hp=4}, {10,23}, {s="interact",dir="Up"}, {11,23}, {11,21}, {s="teachThrash"}, {s="potionForMankey"}, {11,20}, {s="interact",dir="Up"}, {s="redbarMankey"}, {10,20}, {10,19}, {s="teachThrash"}, {10,15}, {s="waitToFight"}, {s="teachThrash"}, {s="split"}, {10,8}, {20,8}},
-- 6: NUGGET BRIDGE
-- To Bill's
{36, {0,8}, {9,8}, {9,6}, {9,6}, {8,6}, {8,5}, {s="interact",dir="Up"}, {s="thrashGeodude"}, {10,5}, {s="hikerElixer"}, {10,4}, {13,4}, {13,6}, {15,6}, {15,4}, {17,4}, {17,7}, {18,7}, {s="interact",dir="Down"}, {20,7}, {20,8}, {22,8}, {22,6}, {35,6}, {35,4}, {36,4}, {s="interact",dir="Right"}, {36,5}, {38,5}, {38,4}, {s="lassEther"}, {45,4}, {45,3}},
-- Save Bill
{88, {2,7}, {2,5}, {5,5}, {s="confirm",dir="Right"}, {1,5}, {s="interact",dir="Up"}, {4,5}, {s="interact",dir="Up"}, {s="waitToTalk"}, {s="potionBeforeGoldeen"}, {s="item",item="escape_rope"}},
-- To Misty
{3, {19,18}, {19,20}, {30,20}, {30,19}},
-- Misty
{65, {4,13}, {c="a",a="Misty's Gym"}, {c="potion",b=false}, {4,8}, {2,8}, {2,5}, {7,5}, {7,3}, {6,3}, {5,3}, {s="waitToFight"}, {s="potionBeforeMisty"}, {5,2}, {s="interact",dir="Left"}, {s="fightMisty"}, {s="split"}, {s="tweetMisty"}, {5,3}, {7,3}, {7,5}, {5,5}, {5,14}},
-- 7: MISTY
-- Past the policeman
{3, {30,20}, {c="potion",b=true,yolo=true}, {8,20}, {8,12}, {27,12}, {27,11}},
-- Wrecked house
{62, {2,7}, {2,2}, {3,2}, {3,0}},
-- Cerulean Rocket
{3, {27,9}, {28,9}, {s="potionBeforeRocket"}, {33,9}, {33,18}, {36,18}, {36,31}, {25,31}, {25,36}},
-- Out of Cerulean
{16, {15,0}, {15,28}, {17,28}, {17,27}},
-- Underground entrance
{71, {3,7}, {3,4}, {4,4}},
-- Underground to Vermilion
{119, {5,4}, {4,4}, {s="jingleSkip"}, {2,4}, {2,41}},
-- Underground exit
{74, {4,4}, {3,8}},
-- Oddish
{17, {17,14}, {c="a",a="Vermilion City"}, {c="catchOddish"}, {17,15}, {s="potion",hp=10,yolo=7}, {17,19}, {s="catchOddish"}, {11,29}, {s="potion",hp=10,yolo=7}, {11,29}, {s="waitToFight",dir="Down"}, {10,29}, {10,30}, {s="potion",hp=10,yolo=7}, {10,31}, {9,31}, {9,36}},
-- Enter Vermilion
{5, {19,0}, {c="disableCatch"}, {19,6}, {21,6}, {21,14}, {23,14}, {23,13}},
-- Vermilion mart
{91, {3,7}, {3,5}, {2,5}, {s="shopVermilionMart"}, {3,5}, {3,8}},
-- To S.S. Anne
{5, {23,14}, {30,14}, {30,26}, {18,26}, {18,31}},
-- Mew
{94, {14,0}, {c="a",a="S.S. Anne"}, {14,3}},
-- First deck
{95, {27,0}, {27,1}, {26,1}, {26,7}, {2,7}, {2,6}},
-- Rival 3
{96, {2,4}, {2,11}, {3,11}, {3,12}, {37,12}, {37,9}, {s="swap",item="potion",dest=2,chain=true}, {s="potion",hp=23,yolo=16,chain=true}, {s="teach",move="bubblebeam",replace="tackle",close=true}, {37,8}, {s="rivalSandAttack"}, {37,5}, {36,5}, {36,4}},
-- Old man Cut
{101, {0,7}, {0,4}, {4,4}, {4,3}, {s="interact",dir="Up"}, {4,5}, {0,5}, {0,7}},
-- Second deck out
{96, {36,4}, {36,12}, {3,12}, {3,11}, {2,11}, {2,4}},
-- First deck out
{95, {2,6}, {2,7}, {26,7}, {26,-1}},
-- Departure
{94, {14,2}},
-- To Surge
{5, {18,29}, {18,26}, {30,26}, {30,14}, {15,14}, {15,17}, {s="potionBeforeSurge"}, {s="swap",item="repel",dest=0,chain=true}, {s="teach",move="cut",poke="oddish",alt="paras",chain=true}, {s="teach",move="dig",poke="paras",alt="squirtle",chain=true}, {s="skill",move="cut",done=0x0D4D}, {15,20}, {12,20}, {12,19}},
-- Trashcans
{92, {4,17}, {c="a",a="Surge's Gym"}, {4,16}, {2,16}, {2,11}, {s="trashcans"}, {4,6}, {4,3}, {5,3}, {5,2}, {s="interact",dir="Up"}, {s="fightSurge"}, {s="split"}, {s="tweetSurge"}, {4,2}, {4,13}, {5,13}, {5,18}},
-- 8: SURGE
-- To bicycle house
{5, {12,20}, {c="a",a="Bicycle Shop"}, {15,20}, {15,19}, {s="skill",move="cut",done=0x0D4D}, {15,14}, {9,14}, {9,13}},
-- Bicycle cert
{90, {2,7}, {2,5}, {0,5}, {0,1}, {2,1}, {s="confirm",dir="Right"}, {s="skill",move="dig",map=90}},
-- Cerulean warp
{3, {19,18}, {19,23}, {16,23}, {16,26}, {13,26}, {13,25}},
-- Bicycle shop
{66, {2,7}, {2,3}, {4,3}, {4,2}, {s="procureBicycle"}, {4,7}, {3,7}, {3,8}},
-- Bicycle out of Cerulean
{3, {13,26}, {s="swap",item="bicycle",dest=1,chain=true}, {s="teach",move="thunderbolt",replace="horn_attack",chain=true}, {s="bicycle"}, {19,26}, {19,27}, {s="skill",move="cut",done=0x0D4D}, {19,29}, {36,29}, {36,16}, {40,16}},
-- TPP's Bane
{20, {0,8}, {c="a",a="Route 9"}, {4,8}, {s="skill",move="cut",done=0x0C17,val=2}, {13,8}, {13,9}, {s="interact",dir="Down"}, {12,9}, {12,12}, {23,12}, {23,11}, {29,11}, {29,12}, {41,12}, {41,10}, {40,10}, {40,9}, {s="interact",dir="Up"}, {41,9}, {41,6}, {39,6}, {39,4}, {45,4}, {45,3}, {51,3}, {51,8}, {60,8}},
-- To the cave
{21, {0,8}, {3,8}, {3,10}, {13,10}, {13,15}, {14,15}, {14,26}, {3,26}, {3,18}, {7,18}, {s="item",item="repel"}, {8,18}, {8,17}},
-- Rock tunnel
{82, {15,3}, {c="a",a="Rock Tunnel"}, {c="potion",b=false}, {15,6}, {23,6}, {23,7}, {s="interact",dir="Down"}, {s="redbarCubone"}, {22,7}, {22,10}, {37,10}, {37,3}},
-- B1
{232, {33,25}, {33,30}, {27,30}, {s="interact",dir="Left"}, {27,31}, {14,31}, {14,29}, {s="interact",dir="Up"}, {17,29}, {17,24}, {25,24}, {25,16}, {37,16}, {37,11}, {s="item",item="repel"}, {37,3}, {27,3}},
-- B2
{82, {5,3}, {5,9}, {11,9}, {11,14}, {17,14}, {17,11}},
-- B1
{232, {23,11}, {14,11}, {14,17}, {8,17}, {8,10}, {7,10}, {s="interact",dir="Left"}, {7,11}, {5,11}, {s="item",item="repel"}, {5,3}, {3,3}},
-- Out of the Tunnel
{82, {37,17}, {32,17}, {32,23}, {37,23}, {37,28}, {28,28}, {26,24}, {23,24}, {s="interact",dir="Left"}, {23,27}, {15,27}, {15,33}},
-- To Lavender Town
{21, {8,54}, {c="a",a="Lavender Town"}, {15,54}, {15,65}, {11,65}, {11,69}, {6,69}, {6,72}},
-- Through Lavender
{4, {6,0}, {6,6}, {0,6}, {0,8}, {-1,8}},
-- Leave Lavender
{19, {59,8}, {52,8}, {52,13}, {47,13}, {s="interact",dir="Left"}, {47,14}, {42,14}, {42,7}, {40,7}, {40,6}, {29,6}, {29,7}, {23,7}, {23,12}, {14,12}, {14,4}, {13,4}, {13,3}},
-- Underground entrance
{80, {3,7}, {3,6}, {4,6}, {4,4}},
-- Underground
{121, {47,2}, {s="bicycle"}, {47,5}, {22,5}, {s="undergroundElixer"}, {2,5}},
-- Underground exit
{77, {4,4}, {4,8}},
-- To Celadon
{18, {5,14}, {s="bicycle"}, {8,14}, {8,8}, {4,8}, {4,3}, {-1,3}},
-- Celadon
{6, {49,11}, {c="a",a="Celadon Mart"}, {14,11}, {14,14}, {10,14}, {10,13}},
-- F1: Department store
{122, {16,7}, {c="potion",b=true,yolo=true}, {c="pp",on=true}, {16,3}, {12,3}, {12,1}},
-- F2
{123, {12,2}, {8,2}, {8,5}, {6,5}, {s="shopTM07"}, {5,5}, {s="shopRepels"}, {9,5}, {s="dodgeDepartment"}, {15,2}, {16,2}, {16,1}},
-- F3
{124, {16,2}, {12,2}, {12,1}},
-- F4: Poke Doll
{125, {12,2}, {10,2}, {10,5}, {5,5}, {s="shopPokeDoll"}, {11,5}, {11,2}, {16,2}, {16,1}},
-- F5
{136, {16,2}, {12,2}, {12,1}},
-- Roof
{126, {15,3}, {12,3}, {s="shopVending"}, {6,3}, {6,4}, {s="giveWater"}, {6,4}, {7,3}, {12,3}, {s="shopExtraWater"}, {15,3}, {15,2}},
-- F5: Buffs
{136, {12,2}, {8,2}, {8,5}, {5,5}, {s="shopBuffs"}, {1,5}, {1,1}},
-- Elevator
{127, {1,3}, {1,2}, {3,2}, {3,1}, {s="deptElevator"}, {2,1}, {2,4}},
-- F1: Exit department store
{122, {1, 2}, {1,7}, {2,7}, {2,8}},
-- Leave Celadon
{6, {8,14}, {s="bicycle"}, {8,15}, {2,15}, {2,18}, {-1,18}},
-- Cut out of Celadon
{27, {39,10}, {34,10}, {s="teach",move="horn_drill",replace="bubblebeam",full=true,chain=true}, {s="skill",move="cut",dir="Up",done=0x0D4D}, {34,6}, {27,6}, {27,4}, {23,4}},
-- Old man's hall
{186, {7,2}, {-1,2}},
-- To the Fly house
{27, {17,4}, {c="a",a="HM02 Fly"}, {10,4}, {10,6}, {7,6}, {7,5}},
-- Fly house
{188, {2,7}, {2,4}, {s="interact",dir="Up"}, {2,5}, {s="split"}, {2,8}},
-- 9: FLY
-- Fly to Lavender
{27, {7,6}, {s="swap",item="super_repel",dest=1,chain=true}, {s="potion",hp=10,chain=true}, {s="teach",move="horn_drill",replace="bubblebeam",chain=true}, {s="item",item="super_repel",chain=true}, {s="swap",item="x_accuracy",dest=2,chain=true}, {s="teach",move="fly",poke="spearow",alt="pidgey",chain=true}, {s="teach",move="rock_slide",replace="poison_sting",chain=true}, {s="fly",dest="lavender",map=4}},
-- To the tower
{4, {3,6}, {c="a",a="Pokemon Tower"}, {14,6}, {14,5}},
-- Pokemon Tower
{142, {10,17}, {10,10}, {18,10}, {18,9}},
-- F2: Rival
{143, {18,9}, {c="setThrash",disable=true}, {18,7}, {16,7}, {16,5}, {15,5}, {s="lavenderRival"}, {5,5}, {5,8}, {3,8}, {3,9}},
-- F3
{144, {3,9}, {3,10}, {6,10}, {6,13}, {8,13}, {8,6}, {17,6}, {17,9}, {18,9}},
-- F4
{145, {18,9}, {s="allowDeath",on=true}, {c="potion",b=false}, {18,7}, {16,7}, {s="interact",dir="Left"}, {s="digFight"}, {16,9}, {c="potion",b=true,yolo=true}, {14,9}, {14,10}, {13,10}, {s="interact",dir="Left"}, {14,10}, {14,8}, {11,8}, {11,9}, {10,9}, {10,12}, {7,12}, {7,11}, {4,11}, {4,10}, {3,10}, {3,9}},
-- F5
{146, {3,9}, {4,9}, {4,11}, {s="interact",dir="Down"}, {4,6}, {13,6}, {13,9}, {9,9}, {9,12}, {14,12}, {14,10}, {18,10}, {s="allowDeath",on=false}, {18,9}},
-- F6
{147, {18,9}, {18,7}, {15,7}, {15,3}, {11,3}, {11,5}, {10,5}, {s="interact",dir="Left"}, {10,6}, {6,6}, {6,7}, {s="interact",dir="Down"}, {6,14}, {10,14}, {10,16}, {s="pokeDoll"}, {9,16}},
-- F7: Top
{148, {9,16}, {10,16}, {10,9}, {s="fightXAccuracy"}, {c="setThrash",disable=false}, {10,7}, {s="thunderboltFirst"}, {10,4}, {s="interact",dir="Up"}},
-- Old man's house
{149, {3,7}, {3,6}, {2,6}, {2,1}, {s="interact",dir="Right"}, {2,8}},
-- 10: POKéFLUTE
-- Lavender -> Celadon
{4, {7,10}, {s="split"}, {s="fly",dest="celadon",map=6}},
-- To Celadon Center
{6, {41,10}, {41,9}},
-- Celadon Center
{133, {3,7}, {3,3}, {s="confirm",dir="Up"}, {3,8}},
-- Leave Celadon
{6, {41,10}, {c="a",a="Snorlax"}, {s="item",item="super_repel",chain=true}, {s="bicycle"}, {41,11}, {14,11}, {14,14}, {2,14}, {2,18}, {-1,18}},
-- トトロだ!
{27, {39,10}, {27,10}, {s="swapXSpeeds"}, {s="playPokeflute"}, {23,10}},
-- Snorlax pass
{186, {7,8}, {-1,8}},
-- Bicycle road
{27, {17,10}, {c="a",a="Bicycle Road"}, {12,10}, {12,13}, {11,13}, {11,18}},
-- Forced down inputs
{28, {11,0}, {11,5}, {15,5}, {15,12}, {s="drivebyRareCandy"}, {15,14}, {18,14}, {18,122}, {13,122}, {13,143}},
-- Cycling road exit
{29, {13,0}, {13,8}, {34,8}},
-- Exit building
{190, {0,4}, {8,4}},
-- Enter Safari City
{29, {40,8}, {s="item",item="super_repel",chain=true}, {s="teach",move="ice_beam",replace="rock_slide",chain=true}, {s="bicycle"}, {50,8}},
-- Safari City
{7, {0,16}, {c="a",a="Safari Zone"}, {3,16}, {3,20}, {23,20}, {23,14}, {29,14}, {29,15}, {35,15}, {35,8}, {37,8}, {37,2}, {22,2}, {22,4}, {18,4}, {18,3}},
-- Safari entrance
{156, {3,5}, {3,2}, {4,2}, {s="confirm",dir="Right"}},
-- Safari 1
{220, {15,25}, {s="bicycle"}, {15,16}, {28,16}, {28,11}, {30,11}},
-- Safari 2
{217, {0,23}, {4,23}, {4,24}, {20,24}, {20,20}, {s="safariCarbos"}, {12,20}, {12,22}, {11,22}, {10,22}, {s="item",item="super_repel",chain=true}, {s="item",item="carbos",poke="nidoking",close=true}, {9,22}, {9,8}, {12,8}, {12,6}, {17,6}, {17,8}, {20,8}, {s="centerSkipFullRestore"}, {20,3}, {7,3}, {7,5}, {-1,5}},
-- Safari 3
{218, {39,31}, {22,31}, {22,22}, {16,22}, {16,28}, {13,28}, {13,9}, {28,9}, {28,3}, {3,3}, {3,36}},
-- Safari 4
{219, {21,0}, {21,5}, {19,5}, {19,6}, {s="interact",dir="Down"}, {19,5}, {7,5}, {7,6}, {3,6}, {3,3}},
-- Warden
{222, {2,7}, {2,6}, {3,6}, {3,4}, {s="interact",dir="Up"}, {3,8}},
-- Safari Warp
{219, {3,4}, {s="skill",move="dig",map=219}},
-- Celadon again
{6, {41,10}, {s="bicycle"}, {41,11}, {50,11}},
-- To Saffron
{18, {0,3}, {c="a",a="Saffron City"}, {4,3}, {4,9}, {10,9}, {10,10}, {12,10}},
-- Thirsty guard
{76, {0,4}, {6,4}},
-- Saffron entry
{18, {18,10}, {s="bicycle"}, {20,10}},
-- Saffron City
{10, {0,18}, {3,18}, {3,22}, {18,22}, {18,21}},
-- Silph Co
{181, {10,17}, {c="a",a="Silph Co."}, {10,9}, {8,9}, {8,1}, {20,1}, {20,0}},
-- Elivator
{236, {1,3}, {1,2}, {3,2}, {3,1}, {s="silphElevator"}, {2,1}, {2,4}},
-- F10
{234, {12,1}, {12,3}, {4,3}, {4,9}, {s="fightSilphMachoke"}, {6,9}, {6,11}, {s="silphCarbos"}, {6,16}, {3,16}, {3,14}, {s="interact",dir="Right"}, {3,15}, {1,15}, {1,13}, {s="item",item="carbos",poke="nidoking",full=true}, {1,12}, {s="interact",dir="Right"}, {1,16}, {3,16}, {s="swapXSpecials"}, {s="teach",move="surf",poke="squirtle",replace="tail_whip",chain=true}, {s="teach",move="earthquake",replace="thrash",chain=true}, {s="item",item="carbos",poke="nidoking",close=true}, {6,16}, {6,9}, {4,9}, {4,1}, {8,1}, {8,0}},
-- F9
{233, {14,1}, {14,3}, {24,3}, {24,16}, {17,16}, {17,15}},
-- Warped
{210, {9,15}, {9,16}, {20,16}, {s="interact",dir="Right"}, {9,16}, {9,15}},
-- Warp back
{233, {17,15}, {17,14}, {17,15}},
-- First card
{210, {9,15}, {9,13}, {8,13}, {s="interact",dir="Left"}, {6,13}, {6,16}, {3,16}, {3,15}},
-- Warp down
{208, {3,15}, {3,14}, {18,14}, {18,9}, {s="interact",dir="Left"}, {14,9}, {14,11}, {11,11}},
-- Rival 5
{212, {5,3}, {c="a",a="Silph Rival"}, {4,3}, {4,2}, {3,2}, {c="potion",b=false}, {s="silphRival"}, {3,7}, {c="potion",b=true,yolo=true}, {5,7}},
-- Giovanni
{235, {3,2}, {c="a",a="Silph Giovanni"}, {3,11}, {2,11}, {2,15}, {s="rareCandyGiovanni"}, {2,16}, {s="interact",dir="Right"}, {2,15}, {5,15}, {s="potion",hp=17,yolo=12}, {6,15}, {6,14}, {s="interact",dir="Up"}, {6,13}, {s="fightXAccuracy"}, {s="fightSilphGiovanni"}, {s="split"}, {s="waitToPause"}, {s="skill",move="dig",map=235}},
-- 11: SILPH CO.
-- Fly to Fuschia
{6, {41,10}, {s="fly",dest="fuchsia",map=7}},
-- To Koga
{7, {19,28}, {c="a",a="Koga's Gym"}, {5,28}, {5,27}},
-- Koga
{157, {4,17}, {9,17}, {9,9}, {7,9}, {s="interact",dir="Up"}, {9,9}, {9,1}, {1,1}, {1,2}, {s="earthquakeElixer",min=2,chain=true}, {s="potionBeforeHypno"}, {1,3}, {2,3}, {2,5}, {1,5}, {c="potion",b=false}, {1,7}, {s="fightHypno"}, {1,9}, {2,9}, {s="earthquakeElixer",min=4}, {4,9}, {s="interact",dir="Down"}, {s="fightKoga"}, {s="split"}, {1,9}, {1,5}, {2,5}, {2,3}, {1,3}, {1,1}, {9,1}, {9,16}, {5,16}, {5,18}},
-- 12: KOGA
-- To the Warden
{7, {5,28}, {s="bicycle"}, {6,28}, {6,30}, {24,30}, {30,30}, {30,28}, {27,28}, {27,27}},
-- HM04 Strength
{155, {4,7}, {4,6}, {2,6}, {2,4}, {s="interact",dir="Up"}, {4,4}, {4,8}},
-- Fly home
{7, {27,28}, {s="fly",dest="pallet",map=0}},
-- Pallet to Cinnabar
{0, {5,6}, {s="item",item="super_repel",chain=true}, {s="item",item="rare_candy",amount=3,poke="nidoking",chain=true}, {s="bicycle"}, {s="allowDeath",on=false}, {3,6}, {s="dodgeGirl"}, {3,17}, {s="skill",move="surf",dir="Right",x=4}, {4,18}},
-- To Cinnabar
{32, {4,0}, {4,14}, {3,14}, {3,90}},
-- Enter Cinnabar Mansion
{8, {3,0}, {c="a",a="Cinnabar Mansion"}, {3,4}, {6,4}, {6,3}},
-- F1
{165, {5,27}, {5,10}},
-- F2
{214, {5,11}, {10,11}, {10,5}, {6,5}, {6,1}},
-- F3
{215, {6,2}, {11,2}, {11,6}, {10,6}, {s="confirm",dir="Up"}, {14,6}, {14,11}, {16,11}, {16,14}},
-- F1 drop
{165, {16,14}, {16,15}, {13,15}, {13,20}, {s="cinnabarCarbos"}, {21,23}},
-- B1
--TODO menu cancel for RC
{216, {23,22}, {23,15}, {21,15}, {s="item",item="super_repel",chain=true}, {s="item",item="carbos",poke="nidoking",close=true}, {17,15}, {17,19}, {18,19}, {18,23}, {17,23}, {17,26}, {18,26}, {s="confirm",dir="Up"}, {14,26}, {14,22}, {12,22}, {12,15}, {24,15}, {24,18}, {26,18}, {26,6}, {24,6}, {24,4}, {20,4}, {s="confirm",dir="Up"}, {24,4}, {24,6}, {12,6}, {12,2}, {11,2}, {s="interact",dir="Left"}, {12,2}, {12,7}, {4,7}, {4,9}, {2,9}, {s="interact",dir="Left"}, {5,9}, {5,10}, {s="teach",move="strength",poke="squirtle",replace="tackle",chain=true}, {s="item",item="rare_candy",amount=2,poke="nidoking",close=true}, {5,12}, {s="interact",dir="Down"}, {5,12}, {s="skill",move="dig",map=216}},
-- Celadon once again
{6, {41,10}, {s="bicycle"}, {41,13}, {36,13}, {36,23}, {25,23}, {25,30}, {35,30}, {35,31}, {s="skill",move="cut",dir="Down",done=0x0D4D}, {35,34}, {5,34}, {5,29}, {12,29}, {12,27}},
-- Erika
{134, {4,17}, {c="a",a="Erika's Gym"}, {4,16}, {1,16}, {1,9}, {0,9}, {0,4}, {1,4}, {s="skill",move="cut",done=0x0D4D}, {4,4}, {s="interact",dir="Up"}, {s="fightErika"}, {s="split"}, {4,5}, {5,5}, {5,6}, {s="skill",move="cut",dir="Down",done=0x0D4D}, {5,18}},
-- 13: ERIKA
-- Fly to Cinnabar
{6, {12,28}, {s="fly",dest="cinnabar",map=8}},
-- Cinnabar
{8, {11,12}, {s="earthquakeElixer",min=4,chain=true}, {s="bicycle"}, {18,12}, {18,3}},
-- Cinnabar Gym
{166, {16,17}, {c="a",a="Blaine's Gym"}, {16,14}, {18,14}, {18,10}, {15,10}, {15,8}, {s="confirm",dir="Up"}, {16,8}, {16,7}, {18,7}, {18,1}, {12,1}, {12,2}, {10,2}, {s="confirm",dir="Up",type="B"}, {12,2}, {12,7}, {10,7}, {10,8}, {9,8}, {s="confirm",dir="Up",type="B"}, {9,11}, {12,11}, {12,13}, {10,13}, {10,14}, {9,14}, {s="confirm",dir="Up",type="B"}, {9,16}, {1,16}, {1,14}, {s="confirm",dir="Up"}, {2,14}, {2,13}, {4,13}, {4,9}, {1,9}, {1,8}, {s="confirm",dir="Up",type="B"}, {2,8}, {2,7}, {4,7}, {4,5}, {3,5}, {3,4}, {c="potion",b=false}, {s="waitToFight",dir="Up"}, {s="split"}, {s="waitToReceive"}, {s="skill",move="dig",map=166}},
-- 14: BLAINE
-- Celadon too many times
{6, {41,10}, {c="potion",b=true,yolo=true}, {s="bicycle"}, {41,11}, {50,11}},
-- Exit Celadon
{18, {0,3}, {4,3}, {4,9}, {10,9}, {10,10}, {12,10}},
-- Saffron gate
{76, {0,4}, {c="a",a="Saffron City"}, {6,4}},
-- Saffron edge
{18, {18,10}, {s="earthquakeElixer",min=4,chain=true}, {s="bicycle"}, {20,10}},
-- Saffron again
{10, {0,18}, {3,18}, {3,6}, {31,6}, {31,4}, {34,4}, {34,3}},
-- Sabrina
{178, {8,17}, {c="a",a="Sabrina's Gym"}, {8,16}, {11,16}, {11,15}, {16,17}, {16,15}, {15,15}, {18,3}, {18,5}, {15,5}, {1,5}, {11,11}, {11,8}, {10,8}, {s="waitToFight",dir="Left"}, {s="split"}, {11,8}, {11,11}, {s="earthquakeElixer",min=4,chain=true}, {s="skill",move="dig",map=178}},
-- 15: SABRINA
-- Celadon
{6, {41,10}, {s="fly",dest="viridian",map=1}},
-- Viridian again
{1, {23,26}, {s="bicycle"}, {19,26}, {19,4}, {27,4}, {27,3}, {34,3}, {34,8}, {32,8}, {32,7}},
-- Giovanni Gym
{45, {16,17}, {c="potion",b=false}, {c="a",a="Giovanni's Gym"}, {16,16}, {14,16}, {14,9}, {13,9}, {13,7}, {15,7}, {15,4}, {12,4}, {12,5}, {10,5}, {c="a",a="Machoke"}, {10,4}, {s="fightGiovanniMachoke"}, {10,5}, {c="a",a="Giovanni's Gym"}, {13,5}, {13,4}, {15,4}, {15,7}, {13,7}, {13,11}, {14,11}, {14,16}, {16,16}, {16,18}},
-- Reset Gym
{1, {32,8}, {32,7}},
-- Giovanni
{45, {16,17}, {c="potion",b=false}, {16,16}, {14,16}, {14,9}, {13,9}, {13,7}, {15,7}, {15,4}, {12,4}, {12,5}, {10,5}, {10,2}, {7,2}, {7,4}, {2,4}, {s="checkGiovanni"}, {2,2}, {s="interact",dir="Up"}, {s="fightGiovanni"}, {s="split"}, {2,4}, {7,4}, {7,2}, {10,2}, {10,5}, {12,5}, {12,4}, {15,4}, {15,7}, {13,7}, {13,11}, {14,11}, {14,16}, {16,16}, {16,18}},
-- 16: GIOVANNI
-- Leave Viridian
{1, {32,8}, {s="bicycle"}, {32,12}, {17,12}, {17,16}, {16,16}, {16,17}, {-1,17}},
-- To Pokemon League
{33, {39,9}, {c="a",a="Viridian Rival"}, {35,9}, {35,12}, {31,12}, {31,5}, {29,5}, {s="viridianRival"}, {16,5}, {16,12}, {5,12}, {5,10}, {11,10}, {11,6}, {8,6}, {8,5}},
-- Pokemon League 1
{193, {4,7}, {4,0}},
-- PL 2
{34, {7,139}, {s="checkEther"}, {s="ether",max=true,chain=true}, {s="bicycle"}, {7,132}, {14,132}, {14,124}, {9,124}, {9,116}, {10,116}, {10,104}, {s="skill",move="surf",y=103}, {10,92}, {7,92}, {7,90}, {s="grabMaxEther"}, {7,72}, {8,72}, {8,71}, {s="item",item="super_repel",chain=true}, {s="bicycle"}, {8,66}, {10,66}, {10,57}, {12,57}, {12,48}, {6,48}, {6,32}, {4,32}, {4,31}},
-- Victory Road
{108, {8,17}, {c="a",a="Victory Road"}, {s="tweetVictoryRoad"}, {8,16}, {4,16}, {4,14}, {5,14}, {s="skill",move="strength"}, {5,15}, {4,15}, {4,16}, {7,16}, {s="push",dir="Right",x=0x0255,y=0x0254}, {7,17}, {9,17}, {9,15}, {8,15}, {8,14}, {15,14}, {15,15}, {16,15}, {16,14}, {s="push",dir="Up",x=0x0255,y=0x0254}, {14,14}, {14,12}, {16,12}, {16,11}, {17,11}, {17,12}, {14,12}, {14,14}, {8,14}, {8,16}, {5,16}, {5,12}, {11,12}, {11,6}, {7,6}, {7,8}, {3,8}, {3,5}, {2,5}, {2,1}, {1,1}},
-- F2
{194, {0,8}, {0,9}, {3,9}, {3,13}, {5,13}, {5,14}, {s="item",item="super_repel",chain=true}, {s="skill",move="strength"}, {4,14}, {4,13}, {3,13}, {3,15}, {4,15}, {4,16}, {3,16}, {s="push",dir="Left",x=0x02B5,y=0x02B4}, {3,11}, {5,11}, {5,8}, {14,8}, {14,14}, {21,14}, {21,16}, {28,16}, {28,11}, {23,11}, {23,7}},
-- F3
{198, {23,7}, {23,6}, {22,6}, {22,4}, {s="skill",move="strength"}, {22,2}, {23,2}, {23,1}, {7,1}, {7,0}, {6,0}, {6,1}, {7,1}, {7,2}, {3,2}, {3,1}, {2,1}, {2,4}, {1,4}, {1,5}, {2,5}, {2,4}, {4,4}, {4,2}, {7,2}, {7,1}, {20,1}, {20,6}, {17,6}, {17,4}, {9,4}, {9,10}, {5,10}, {5,8}, {1,8}, {1,15}, {11,15}, {11,16}, {20,16}, {20,15}, {23,15}},
-- F2
{194, {22,16}, {s="potionBeforeLorelei"}, {s="item",item="super_repel",chain=true}, {s="skill",move="strength"}, {s="bicycle"}, {22,17}, {24,17}, {24,16}, {11,16}, {s="push",dir="Left",x=0x02D5,y=0x02D4}, {21,16}, {21,14}, {25,14}},
-- F3
{198, {27,15}, {27,8}, {26,8}},
-- F2 Exit
{194, {27,7}, {30,7}},
-- Victory end
{34, {14,32}, {18,32}, {18,20}, {14,20}, {14,10}, {13,10}, {13,6}, {10,6}, {10,-1}},
-- Elite Four entrance
{9, {10,17}, {c="a",a="Elite Four"}, {10,5}},
-- Last Center
{174, {7,11}, {15,9}, {15,8}, {s="depositPokemon"}, {7,8}, {7,7}, {s="centerSkip"}, {4,7}, {3,7}, {3,2}, {8,2}, {8,0}},
-- 17: LORELEI
-- Lorelei
{245, {4,5}, {c="a",a="Lorelei"}, {c="potion",b=false}, {4,2}, {s="interact",dir="Right"}, {s="lorelei"}, {s="split"}, {4,0}},
-- Bruno
{246, {4,5}, {c="a",a="Bruno"}, {s="item",item="elixer",poke="nidoking"}, {4,2}, {s="interact",dir="Right"}, {s="bruno"}, {s="split"}, {4,0}},
-- Agatha
{247, {4,5}, {c="a",a="Agatha"}, {s="potion",hp=113,full=true}, {4,2}, {s="interact",dir="Right"}, {s="agatha"}, {s="split"}, {4,1}, {s="prepareForLance"}, {s="ether",close=true}, {4,0}},
-- Lance
{113, {6,11}, {c="a",a="Lance"}, {6,2}, {s="lance"}, {s="split"}, {5,2}, {5,1}, {s="prepareForBlue"}, {5,-1}},
-- Blue
{120, {4,3}, {c="a",a="Blue"}, {s="blue"}, {3,0}},
-- Champion
{118, {4,2}, {s="champion"}}
}
return Paths

View File

@ -0,0 +1,37 @@
local Paths = {
-- Red's room
{38, {3,6}, {5,6}, {5,1}, {7,1}},
-- Red's house
{39, {7,1}, {7,6}, {3,6}, {3,8}},
-- Into the Wild
{0, {5,6}, {10,6}, {10,0}},
-- Choose your character!
{40, {5,3}, {c="a",a="Pallet Rival"}, {5,4}, {7,4}, {s="confirm",dir="Up",type="A"}, {5,3}, {s="confirm",dir="Up",type="B"}, {5,6}, {s="waitToFight"}, {5,12}},
-- Let's try this escape again
{0, {12,12}, {c="a",a="Pallet Town"}, {c="encounters",limit=4}, {9,12}, {9,2}, {10,2}, {10,-1}},
-- First encounters
{12, {10,35}, {10,30}, {8,30}, {8,24}, {12,24}, {12,20}, {9,20}, {9,14}, {14,14}, {s="dodgePalletBoy"}, {14,2}, {11,2}, {11,-1}},
-- To the Mart
{1, {21,35}, {21,30}, {19,30}, {19,20}, {29,20}, {29,19}},
-- Viridian Mart
{42, {2,5}, {3,5}, {3,8}},
-- Backtracking
{1, {29,20}, {c="encounters",limit=5}, {29,21}, {26,21}, {26,30}, {20,30}, {20,36}},
-- Parkour
{12, {10, 0}, {10,3}, {8,3}, {8,18}, {9,18}, {9,22}, {12,22}, {12,24}, {10,24}, {10,36}},
-- To Oak's lab
{0, {10,0}, {10,7}, {9,7}, {9,12}, {12,12}, {12,11}},
-- Parcel delivery
{40, {5,11}, {5,3}, {4,3}, {4,1}, {5,1}, {s="interact",dir="Down"}, {4,1}, {4,12}},
-- Leaving home
{0, {12,12}, {9,12}, {9,2}, {10,2}, {10,-1}},
-- The grass again!?
{12, {10,35}, {10,30}, {8,30}, {8,24}, {12,24}, {12,20}, {9,20}, {9,14}, {14,14}, {s="dodgePalletBoy"}, {14,2}, {11,2}, {11,-1}},
-- Back to the Mart
{1, {21,35}, {21,30}, {19,30}, {19,20}, {29,20}, {29,19}},
-- Viridian Mart redux
{42, {3,7}, {3,5}, {2,5}, {s="shopViridianPokeballs"}, {3,5}, {3,8}},
}
return Paths

1522
data/movelist.lua Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,138 @@
local paths = {
-- Red's room
{38, {3,6}, {1,6}, {1,5}, {0,5}, {0,2}, {s="grabPCPotion"}, {7,2}, {7,1}},
-- Red's house
{39, {7,1}, {7,6}, {3,6}, {3,8}},
-- Into the Wild
{0, {5,6}, {s="checkStrats"}, {10,6}, {10,1}},
-- Choose your character!
{40, {5,3}, {c="a",a="Pallet Rival"}, {5,4}, {8,4}, {s="bulbasaurIChooseYou"}, {8,5}, {5,5}, {5,6}, {s="fightCharmander"}, {s="split"}, {5,12}},
-- 1: RIVAL 1
-- Let's try this escape again
{0, {12,12}, {c="a",a="Pallet Town"}, {c="viridianExpGlitch"}, {c="encounters",limit=4}, {9,12}, {9,2}, {10,2}, {10,-1}},
-- First encounters
{12, {10,35}, {10,30}, {8,30}, {8,24}, {12,24}, {12,20}, {9,20}, {9,14}, {14,14}, {s="dodgePalletBoy"}, {14,2}, {11,2}, {11,-1}},
-- To the Mart
{1, {21,35}, {21,30}, {19,30}, {19,20}, {29,20}, {29,19}},
-- Viridian Mart
{42, {2,5}, {3,5}, {3,8}},
-- Backtracking
{1, {29,20}, {c="encounters",limit=5}, {29,21}, {26,21}, {26,30}, {20,30}, {20,36}},
-- Parkour
{12, {10, 0}, {10,3}, {8,3}, {8,18}, {9,18}, {9,22}, {12,22}, {12,24}, {10,24}, {10,36}},
-- To Oak's lab
{0, {10,0}, {10,7}, {9,7}, {9,12}, {12,12}, {12,11}},
-- Parcel delivery
{40, {5,11}, {5,3}, {4,3}, {4,1}, {5,1}, {s="interact",dir="Down"}, {4,1}, {4,12}},
-- Leaving home
{0, {12,12}, {c="viridianBackupExpGlitch"}, {9,12}, {9,2}, {10,2}, {10,-1}},
-- The grass again!?
{12, {10,35}, {10,30}, {8,30}, {8,24}, {12,24}, {12,20}, {9,20}, {9,14}, {14,14}, {s="dodgePalletBoy"}, {14,2}, {11,2}, {11,-1}},
-- Back to the Mart
{1, {21,35}, {c="a",a="Viridian City"}, {c="encounters",limit=8}, {21,30}, {19,30}, {19,20}, {29,20}, {29,19}},
-- Viridian Mart redux
{42, {3,7}, {3,5}, {2,5}, {s="shopViridian"}, {3,5}, {3,8}},
-- To the forest
{1, {29,20}, {18,20}, {18,6}, {s="dodgeViridianOldMan"}, {17,4}, {s="healTreePotion"}, {15,4}, {s="speak"}, {17,4}, {17, 0}, {17, -1}},
-- Out of Viridian City
{13, {7,71}, {7,57}, {4,57}, {4,52}, {8,52}, {c="a",a="Pidgey grass"}, {c="pidgeyBackupExp"}, {c="catchPidgey"}, {s="catchPidgey"}, {8,46}, {s="split"}, {c="disableCatch"}, {3,46}, {3,43}},
-- 2. PIDGEY
-- Forest entrance
{50, {4,7}, {c="a",a="Viridian Forest"}, {c="encounters",limit=9}, {4,1}, {5,1}, {5,0}},
-- Viridian Forest
{51, {17,47}, {17,43}, {26,43}, {26,34}, {25,34}, {25,32}, {27,32}, {27,20}, {25,20}, {25,12}, {s="grabAntidote"}, {25,9}, {17,9}, {17,16}, {13,16}, {13,3}, {7,3}, {7,22}, {1,22}, {1,19}, {s="grabForestPotion"}, {1,18}, {s="fightWeedle"}, {c="encounters",limit=12}, {1,16}, {s="checkSpec"}, {1,15}, {s="equipForGlitch"}, {1,5}, {s="checkInventory"}, {1,-1}},
-- Forest exit
{47, {4,7}, {4,1}, {5,1}, {5,0}},
-- Road to Pewter City
{13, {3,11}, {c="a",a="Pewter City"}, {3,8}, {8,8}, {8,-1}},
-- Pewter City
{2, {18,35}, {18,26}, {s="checkForPidgey"}, {s="split"}, {18,22}, {19,22}, {19,18}, {36,18}, {s="prepareSave"}, {37,18}, {s="performSkip"}, {s="performReset"}, {s="speak"}, {40,18}},
-- 3: READY FOR SKIP
-- Route3 #1
{14, {0,10}, {c="a",a="Route 3"}, {0,8}, {-1,8}},
-- Back to Pewter City to talk
{2, {39,16}, {36,16}, {s="openPokemonMenu"}, {s="closeMenu"}, {s="speakToGlithGuy"}, {s="split"}, {s="leaveGlitchGuy"}},
-- 4: SKIP DONE
-- Saffron City Poke Center
{182, {3,7}, {c="a",a="Saffron City Brock Skip"}, {3,4}, {s="checkPidgeyHP"}, {s="walkBack"}, {s="split"}, {3,3}, {s="confirm",dir="Up"}, {3,8}},
-- Saffron City
{10, {9,30}, {2,30}, {2,18}, {-1,18}},
-- Enter Guard
{18, {19,10}, {17,10}},
-- Guard House
{76, {5,4}, {3,4}, {s="speak"}, {2,4}, {-1,4}},
-- To Celadon City
{18, {11,10}, {8,10}, {8,9}, {4,9}, {4,3}, {-1,3}},
-- Celadon City
{6, {49,11}, {36,11}, {36,23}, {25,23}, {25,28}, {31,28}, {31,27}},
-- Getting Coin Case
{138, {3,7}, {3,1}, {1,1}, {s="speak"}, {3,1}, {3,8}},
-- Celadon City #2
{6, {31,28}, {25,28}, {25,23}, {36,23}, {36,20}, {28,20}, {28,19}},
-- Game Center
{135, {15,17}, {15,11}, {s="interact",dir="Left"}, {15,9}, {s="speak"}, {15,18}},
-- Celadon City #3
{6, {28,20}, {33,20}, {33,19}},
-- Buy Abra
{137, {4,7}, {4,4}, {2,4}, {2,3}, {s="getAbra"}, {c="a",a="Celadon City Abra"}, {s="split"}, {4,3}, {4,8}},
-- 5. ABRA
-- Celadon City #4
{6, {33,20}, {s="teleport",map=10}},
-- Saffron City after teleport
{10, {9,30}, {9,29}},
-- Poke Center depose
{182, {3,7}, {3,4}, {13,4}, {s="deposeAll",keep="abra"}, {4,4}, {4,8}},
-- Saffron City after depose
{10, {9,30}, {9,32}, {20,32}, {20,36}},
-- Leave Saffron City
{17, {10,0}, {10,2}},
-- South Guard
{73, {3,0}, {3,2}, {s="speak"}, {3,3}, {3,6}},
-- Route6
{17, {10,8}, {10,10}, {7,10}, {7,13}, {5,13}, {5,15}, {s="performTeleportGlitch"}, {s="swapItem",item="coin_case",pos1=5}, {s="teleport",map=10}},
-- 6. TELEPORT GLITCH #1
-- To the Dojo
{10, {9,30}, {c="a",a="Saffron City Teleport #1"}, {s="split"}, {3,30}, {3,7}, {23,7}, {23,4}, {26,4}, {26,3}},
-- Dojo Gym
{177, {4,11}, {4,10}, {2,10}, {2,7}, {s="allowDeath",on=true}, {s="fightGymGuy"}},
-- Saffron City after death
{10, {9,30}, {s="allowDeath",on=false}, {s="openMenu"}, {s="closeMenu"}, {9,32}, {20,32}, {20,36}},
-- MissingNo
{17, {10,0}, {s="closingAutomation"}, {s="battleMissingNo"}, {10,2}},
-- South Guard #2
{73, {3,0}, {3,2}, {s="speak"}, {3,3}, {3,6}},
-- Route6 #2
{17, {10,8}, {10,10}, {7,10}, {7,13}, {5,13}, {5,15}, {s="performTeleportGlitch"}, {s="tossItem",pos=6,amount=2}, {s="teleport",map=10}},
-- 7. TELEPORT GLITCH #2
-- To the Dojo #2
{10, {9,30}, {c="a",a="Saffron City Teleport #2"}, {s="split"}, {3,30}, {3,7}, {23,7}, {23,4}, {26,4}, {26,3}},
-- Dojo Gym #2
{177, {4,11}, {4,10}, {2,10}, {2,7}, {s="allowDeath",on=true}, {s="fightGymGuy"}},
-- Saffron City after death #2
{10, {9,30}, {s="allowDeath",on=false}, {s="openMenu"}, {s="closeMenu"}, {9,32}, {20,32}, {20,36}},
-- MissingNo #2
{17, {10,0}, {s="closingAutomation"}, {s="battleMissingNo"}, {10,2}},
-- South Guard #3
{73, {3,0}, {c="a",a="Hall Of Fame"}, {s="swapItem",pos1=3,pos2=6}, {s="tossItem",pos=6}, {s="tossItem",pos=1}, {s="tossItem",pos=1}, {s="tossItem",pos=1}, {s="tossItem",pos=1}, {s="tossItem",pos=1,amount=253}, {s="swapItem",pos1=1,pos2=2}, {s="swapItem",pos1=2,pos2=1}, {s="tossItem",pos=28,amount=1}, {s="swapItem",pos1=23,pos2=36}, {s="tossTM",pos=36,amount=9}, {s="closeMenu"}, {3,-1}},
-- 8. HALL OF FAME
-- Champion
{118, {s="champion"}}
}
return paths

View File

@ -0,0 +1,138 @@
local paths = {
-- Red's room
{38, {3,6}, {1,6}, {1,5}, {0,5}, {0,2}, {s="grabPCPotion"}, {7,2}, {7,1}},
-- Red's house
{39, {7,1}, {7,6}, {3,6}, {3,8}},
-- Into the Wild
{0, {5,6}, {s="checkStrats"}, {10,6}, {10,1}},
-- Choose your character!
{40, {5,3}, {c="a",a="Pallet Rival"}, {5,4}, {8,4}, {s="bulbasaurIChooseYou"}, {8,5}, {5,5}, {5,6}, {s="fightCharmander"}, {s="split"}, {5,12}},
-- 1: RIVAL 1
-- Let's try this escape again
{0, {12,12}, {c="a",a="Pallet Town"}, {c="viridianExpGlitch"}, {c="encounters",limit=4}, {9,12}, {9,2}, {10,2}, {10,-1}},
-- First encounters
{12, {10,35}, {10,30}, {8,30}, {8,24}, {12,24}, {12,20}, {9,20}, {9,14}, {14,14}, {s="dodgePalletBoy"}, {14,2}, {11,2}, {11,-1}},
-- To the Mart
{1, {21,35}, {21,30}, {19,30}, {19,20}, {29,20}, {29,19}},
-- Viridian Mart
{42, {2,5}, {3,5}, {3,8}},
-- Backtracking
{1, {29,20}, {c="encounters",limit=5}, {29,21}, {26,21}, {26,30}, {20,30}, {20,36}},
-- Parkour
{12, {10, 0}, {10,3}, {8,3}, {8,18}, {9,18}, {9,22}, {12,22}, {12,24}, {10,24}, {10,36}},
-- To Oak's lab
{0, {10,0}, {10,7}, {9,7}, {9,12}, {12,12}, {12,11}},
-- Parcel delivery
{40, {5,11}, {5,3}, {4,3}, {4,1}, {5,1}, {s="interact",dir="Down"}, {4,1}, {4,12}},
-- Leaving home
{0, {12,12}, {c="viridianBackupExpGlitch"}, {9,12}, {9,2}, {10,2}, {10,-1}},
-- The grass again!?
{12, {10,35}, {10,30}, {8,30}, {8,24}, {12,24}, {12,20}, {9,20}, {9,14}, {14,14}, {s="dodgePalletBoy"}, {14,2}, {11,2}, {11,-1}},
-- Back to the Mart
{1, {21,35}, {c="a",a="Viridian City"}, {c="encounters",limit=8}, {21,30}, {19,30}, {19,20}, {29,20}, {29,19}},
-- Viridian Mart redux
{42, {3,7}, {3,5}, {2,5}, {s="shopViridian"}, {3,5}, {3,8}},
-- To the forest
{1, {29,20}, {18,20}, {18,6}, {s="dodgeViridianOldMan"}, {17,4}, {s="healTreePotion"}, {15,4}, {s="speak"}, {17,4}, {17, 0}, {17, -1}},
-- Out of Viridian City
{13, {7,71}, {7,57}, {4,57}, {4,52}, {8,52}, {c="a",a="Pidgey grass"}, {c="pidgeyBackupExp"}, {c="catchPidgey"}, {s="catchPidgey"}, {8,46}, {s="split"}, {c="disableCatch"}, {3,46}, {3,43}},
-- 2. PIDGEY
-- Forest entrance
{50, {4,7}, {c="a",a="Viridian Forest"}, {c="encounters",limit=9}, {4,1}, {5,1}, {5,0}},
-- Viridian Forest
{51, {17,47}, {17,43}, {26,43}, {26,34}, {25,34}, {25,32}, {27,32}, {27,20}, {25,20}, {25,12}, {s="grabAntidote"}, {25,9}, {17,9}, {17,16}, {13,16}, {13,3}, {7,3}, {7,22}, {1,22}, {1,19}, {s="grabForestPotion"}, {1,18}, {s="fightWeedle"}, {c="encounters",limit=12}, {1,16}, {s="checkSpec"}, {1,15}, {s="equipForGlitch"}, {1,5}, {s="checkInventory"}, {1,-1}},
-- Forest exit
{47, {4,7}, {4,1}, {5,1}, {5,0}},
-- Road to Pewter City
{13, {3,11}, {c="a",a="Pewter City"}, {3,8}, {8,8}, {8,-1}},
-- Pewter City
{2, {18,35}, {18,26}, {s="checkForPidgey"}, {s="split"}, {18,22}, {19,22}, {19,18}, {36,18}, {s="prepareSave"}, {37,18}, {s="performSkip"}, {s="performReset"}, {s="speak"}, {40,18}},
-- 3: READY FOR SKIP
-- Route3 #1
{14, {0,10}, {c="a",a="Route 3"}, {0,8}, {-1,8}},
-- Back to Pewter City to talk
{2, {39,16}, {36,16}, {s="openPokemonMenu"}, {s="closeMenu"}, {s="speakToGlithGuy"}, {s="split"}, {s="leaveGlitchGuy"}},
-- 4: SKIP DONE
-- Saffron City Poke Center
{182, {3,7}, {c="a",a="Saffron City Brock Skip"}, {3,4}, {s="checkPidgeyHP"}, {s="walkBack"}, {s="split"}, {3,3}, {s="confirm",dir="Up"}, {3,8}},
-- Saffron City
{10, {9,30}, {2,30}, {2,18}, {-1,18}},
-- Enter Guard
{18, {19,10}, {17,10}},
-- Guard House
{76, {5,4}, {3,4}, {s="speak"}, {2,4}, {-1,4}},
-- To Celadon City
{18, {11,10}, {8,10}, {8,9}, {4,9}, {4,3}, {-1,3}},
-- Celadon City
{6, {49,11}, {36,11}, {36,23}, {25,23}, {25,28}, {31,28}, {31,27}},
-- Getting Coin Case
{138, {3,7}, {3,1}, {1,1}, {s="speak"}, {3,1}, {3,8}},
-- Celadon City #2
{6, {31,28}, {25,28}, {25,23}, {36,23}, {36,20}, {28,20}, {28,19}},
-- Game Center
{135, {15,17}, {16,17}, {16,15}, {s="speak"}, {16,13}, {s="interact",dir="Right"}, {16,11}, {15,11}, {s="interact",dir="Left"}, {15,9}, {s="speak"}, {15,8}, {9,8}, {9,11}, {s="speak"}, {10,11}, {10,15}, {s="speak"}, {10,17}, {15,17}, {15,18}},
-- Celadon City #3
{6, {28,20}, {33,20}, {33,19}},
-- Buy Abra
{137, {4,7}, {4,4}, {2,4}, {2,3}, {s="getAbra"}, {c="a",a="Celadon City Abra"}, {s="split"}, {4,3}, {4,8}},
-- 5. ABRA
-- Celadon City #4
{6, {33,20}, {s="teleport",map=10}},
-- Saffron City after teleport
{10, {9,30}, {9,29}},
-- Poke Center depose
{182, {3,7}, {3,4}, {13,4}, {s="deposeAll",keep="abra"}, {4,4}, {4,8}},
-- Saffron City after depose
{10, {9,30}, {9,32}, {20,32}, {20,36}},
-- Leave Saffron City
{17, {10,0}, {10,2}},
-- South Guard
{73, {3,0}, {3,2}, {s="speak"}, {3,3}, {3,6}},
-- Route6
{17, {10,8}, {10,10}, {7,10}, {7,13}, {5,13}, {5,15}, {s="performTeleportGlitch"}, {s="swapItem",item="coin_case",pos1=5}, {s="teleport",map=10}},
-- 6. TELEPORT GLITCH #1
-- To the Dojo
{10, {9,30}, {c="a",a="Saffron City Teleport #1"}, {s="split"}, {3,30}, {3,7}, {23,7}, {23,4}, {26,4}, {26,3}},
-- Dojo Gym
{177, {4,11}, {4,10}, {2,10}, {2,7}, {s="allowDeath",on=true}, {s="fightGymGuy"}},
-- Saffron City after death
{10, {9,30}, {s="allowDeath",on=false}, {s="openMenu"}, {s="closeMenu"}, {9,32}, {20,32}, {20,36}},
-- MissingNo
{17, {10,0}, {s="closingAutomation"}, {s="battleMissingNo"}, {10,2}},
-- South Guard #2
{73, {3,0}, {3,2}, {s="speak"}, {3,3}, {3,6}},
-- Route6 #2
{17, {10,8}, {10,10}, {7,10}, {7,13}, {5,13}, {5,15}, {s="performTeleportGlitch"}, {s="tossItem",pos=6,amount=2}, {s="teleport",map=10}},
-- 7. TELEPORT GLITCH #2
-- To the Dojo #2
{10, {9,30}, {c="a",a="Saffron City Teleport #2"}, {s="split"}, {3,30}, {3,7}, {23,7}, {23,4}, {26,4}, {26,3}},
-- Dojo Gym #2
{177, {4,11}, {4,10}, {2,10}, {2,7}, {s="allowDeath",on=true}, {s="fightGymGuy"}},
-- Saffron City after death #2
{10, {9,30}, {s="allowDeath",on=false}, {s="openMenu"}, {s="closeMenu"}, {9,32}, {20,32}, {20,36}},
-- MissingNo #2
{17, {10,0}, {s="closingAutomation"}, {s="battleMissingNo"}, {10,2}},
-- South Guard #3
{73, {3,0}, {c="a",a="Hall Of Fame"}, {s="swapItem",pos1=3,pos2=6}, {s="tossItem",pos=6}, {s="tossItem",pos=1}, {s="tossItem",pos=1}, {s="tossItem",pos=1}, {s="tossItem",pos=1}, {s="tossItem",pos=1,amount=253}, {s="swapItem",pos1=1,pos2=2}, {s="swapItem",pos1=2,pos2=1}, {s="tossItem",pos=28,amount=1}, {s="swapItem",pos1=23,pos2=36}, {s="tossTM",pos=36,amount=9}, {s="closeMenu"}, {3,-1}},
-- 8. HALL OF FAME
-- Champion
{118, {s="champion"}}
}
return paths

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

294
main.lua Normal file
View File

@ -0,0 +1,294 @@
--##################################################
--############# ############
--############# 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 = "Blue" -- Set to Red, Blue or Yellow
GAME_RUN = "No Save Corruption" -- Set to "Any%" or "No save corruption" for the run you want
--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 = "aaqrrzz" -- Player name
--No save corruption run Settings
BULBASAUR_NAME = "B" -- Set Bulbasaur name
ABRA_NAME = "A" -- Set Abra name
PIDGEY_NAME = "P" -- Set Pidgey name
STRATS = "" -- Set to "PP" or "Pidgey" if you want a forced Strats run, otherwise the bot will choice
--Any% Speedrun Settings
SQUIRTLE_NAME = "S" -- Set Squirtle name for red/blue
RIVAL_NAME = "> " -- Rival name (no save corruption run use internal set 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 : <=Pk, >=Mn, {=♂, }=♀
--#####################################################################################
--#####################################################################################
--########### ###############
--########### PLEASE DON'T EDIT ANYTHING BELLOW, IT'S AT YOUR RISK ###############
--########### START CODE (hard hats on) ###############
--########### ###############
--#####################################################################################
--#####################################################################################
-- SET VALUES
local VERSION = "2.6.5"
YELLOW = memory.getcurrentmemorydomainsize() > 30000
local START_WAIT = 99
local hasAlreadyStartedPlaying = false
local oldSeconds
local running = true
local lastHP
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 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."..lowerGameRun..secondStratDir..".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."..lowerGameRun..secondPaintDir..".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
-- 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
local OWNER_ANY = "Kylecoburn"
local OWNER_OTHER = "Bouletmarc"
if lowerGameRun == "any%" then
OWNER = OWNER_ANY
else
OWNER = OWNER_ANY.." & "..OWNER_OTHER
end
p("Welcome to PokeBot Version "..VERSION, true)
p("Actually running Pokemon "..GAME_NAME.." "..GAME_RUN.." 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 RebootDone = false
while true do
local currentMap = Memory.value("game", "map")
if currentMap ~= previousMap then
Input.clear()
previousMap = currentMap
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 currentMap == 0 then
if running then
if not hasAlreadyStartedPlaying then
client.reboot_core()
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 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 ~= lastHP then
-- Bridge.hp(curr_hp, Pokemon.index(0, "max_hp"))
-- lastHP = curr_hp
-- end
if curr_hp == 0 and not Control.canDie() and Pokemon.index(0) > 0 then
Strategies.death(currentMap)
elseif Walk.strategy then
if Strategies.execute(Walk.strategy) then
Walk.traverse(currentMap)
end
elseif battleState > 0 then
if not Control.shouldCatch(partySize) then
Battle.automate()
end
elseif Textbox.handle() then
Walk.traverse(currentMap)
end
end
end
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
Paint.draw(currentMap)
end
Input.advance()
emu.frameadvance()
end
Bridge.close()

203
storage/inventory.lua Normal file
View File

@ -0,0 +1,203 @@
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 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 = 0x131E
-- Data
function Inventory.indexOf(name)
local searchID = items[name]
for i=0,19 do
local iidx = ITEM_BASE + i * 2
if Memory.raw(iidx) == searchID then
return i
end
end
return -1
end
function Inventory.count(name)
local index = Inventory.indexOf(name)
if index ~= -1 then
return Memory.raw(ITEM_BASE + index * 2 + 1)
end
return 0
end
function Inventory.contains(...)
for i,name in ipairs(arg) do
if Inventory.count(name) > 0 then
return name
end
end
end
-- Actions
function Inventory.teach(item, poke, replaceIdx, altPoke)
local main = Memory.value("menu", "main")
local column = Menu.getCol()
if main == 144 then
if column == 5 then
Menu.select(replaceIdx, true)
else
Input.press("A")
end
elseif main == 128 then
if column == 5 then
Menu.select(Inventory.indexOf(item), "accelerate", true)
elseif column == 11 then
Menu.select(2, true)
elseif column == 14 then
Menu.select(0, true)
end
elseif main == 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.raw(0x131D) == 20
end
function Inventory.use(item, poke, midfight)
if midfight then
local battleMenu = Memory.value("battle", "menu")
if battleMenu == 94 then
local rowSelected = Memory.value("menu", "row")
if Menu.getCol() == 9 then
if rowSelected == 0 then
Input.press("Down")
else
Input.press("A")
end
else
Input.press("Left")
end
elseif battleMenu == 233 then
Menu.select(Inventory.indexOf(item), "accelerate", true)
elseif Utils.onPokemonSelect(battleMenu) then
if poke then
if type(poke) == "string" then
poke = Pokemon.indexOf(poke)
end
Menu.select(poke, true)
else
Input.press("A")
end
else
Input.press("B")
end
return
end
local main = Memory.value("menu", "main")
local column = Menu.getCol()
if main == 144 then
if Memory.value("battle", "menu") == 95 then
Input.press("B")
else
local idx = 0
if poke then
idx = Pokemon.indexOf(poke)
end
Menu.select(idx, true)
end
elseif main == 128 or main == 60 then
if column == 5 then
Menu.select(Inventory.indexOf(item), "accelerate", true)
elseif column == 11 then
Menu.select(2, true)
elseif column == 14 then
Menu.select(0, true)
else
local index = 0
if poke then
index = Pokemon.indexOf(poke)
end
Menu.select(index, true)
end
elseif main == 228 then
if column == 14 and Memory.value("battle", "menu") == 95 then
Input.press("B")
end
elseif main == Menu.pokemon then
Input.press("B")
else
return false
end
return true
end
return Inventory

307
storage/pokemon.lua Normal file
View File

@ -0,0 +1,307 @@
local Pokemon = {}
local Bridge = require "util.bridge"
local Input = require "util.input"
local Memory = require "util.memory"
local Menu = require "util.menu"
local pokeIDs = {
rhydon = 1,
kangaskhan = 2,
nidoran = 3,
spearow = 5,
voltorb = 6,
nidoking = 7,
ivysaur = 9,
gengar = 14,
nidoranf = 15,
nidoqueen = 16,
cubone = 17,
rhyhorn = 18,
gyarados = 22,
growlithe = 33,
onix = 34,
pidgey = 36,
kadabra = 38,
jinx = 72,
meowth = 77,
pikachu = 84,
dragonair = 89,
sandshrew = 96,
zubat = 107,
ekans = 108,
paras = 109,
weedle = 112,
kakuna = 113,
dewgong = 120,
caterpie = 123,
metapod = 124,
hypno = 129,
weezing = 143,
abra = 148,
alakazam = 149,
pidgeotto = 150,
pidgeot = 151,
bulbasaur = 153,
rattata = 165,
raticate = 166,
nidorino = 167,
geodude = 169,
squirtle = 177,
oddish = 185,
}
local moveList = {
cut = 15,
fly = 19,
sand_attack = 28,
horn_attack = 30,
horn_drill = 32,
tackle = 33,
thrash = 37,
tail_whip = 39,
poison_sting = 40,
leer = 43,
growl = 45,
water_gun = 55,
surf = 57,
ice_beam = 58,
bubblebeam = 61,
strength = 70,
leech_seed = 73,
thunderbolt = 85,
earthquake = 89,
dig = 91,
teleport = 100,
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 double
if not offset then
offset = 0
else
local dataTable = data[offset]
offset = dataTable[1]
double = dataTable[2]
end
local address = getAddress(index) + offset
local value = Memory.raw(address)
if double then
value = value + Memory.raw(address + 1)
end
return value
end
Pokemon.index = index
local function indexOf(...)
for ni,name in ipairs(arg) do
local pid = pokeIDs[name]
for i=0,5 do
local atIdx = index(i)
if atIdx == pid then
return i
end
end
end
return -1
end
Pokemon.indexOf = indexOf
-- Table functions
function Pokemon.battleMove(name)
local mid = moveList[name]
for i=1,4 do
if mid == Memory.raw(0x101B + i) then
return i
end
end
end
function Pokemon.moveIndex(move, pokemon)
local pokemonIdx
if pokemon then
pokemonIdx = indexOf(pokemon)
else
pokemonIdx = 0
end
local address = getAddress(pokemonIdx) + 7
local mid = moveList[move]
for i=1,4 do
if mid == Memory.raw(address + i) then
return i
end
end
end
function Pokemon.info(name, offset)
return index(indexOf(name), offset)
end
function Pokemon.getID(name)
return pokeIDs[name]
end
function Pokemon.getName(id)
for name,pid in pairs(pokeIDs) do
if pid == id then
return name
end
end
end
function Pokemon.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("oddish", "paras", "spearow", "pidgey", "nidoran", "squirtle")
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 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 pokeName = Pokemon.forMove(move)
if main == 141 then
Input.press("A")
elseif main == 128 then
local column = Menu.getCol()
if column == 11 then
Menu.select(1, true)
elseif column == 10 or column == 12 then
local midx = 0
local menuSize = Memory.value("menu", "size")
if menuSize == 4 then
if move == "dig" then
midx = 1
elseif move == "surf" then
if Pokemon.inParty("paras") then
midx = 1
end
end
elseif menuSize == 5 then
if move == "dig" then
midx = 2
elseif move == "surf" then
midx = 1
end
end
Menu.select(midx, true)
else
Input.press("B")
end
elseif main == 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

57
util/any%/paint.lua Normal file
View File

@ -0,0 +1,57 @@
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)
local px, py = Player.position()
drawText(0, 14, currentMap..": "..px.." "..py)
drawText(0, 0, elapsedTime())
if Memory.value("battle", "our_id") > 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 nidx = Pokemon.indexOf("nidoran", "nidorino", "nidoking")
if nidx ~= -1 then
local att = Pokemon.index(nidx, "attack")
local def = Pokemon.index(nidx, "defense")
local spd = Pokemon.index(nidx, "speed")
local scl = Pokemon.index(nidx, "special")
drawText(100, 0, att.." "..def.." "..spd.." "..scl)
end
local enc = " encounter"
if encounters ~= 1 then
enc = enc.."s"
end
drawText(0, 116, Memory.value("battle", "critical"))
drawText(0, 125, Memory.value("player", "repel"))
drawText(0, 134, encounters..enc)
return true
end
function Paint.wildEncounters(count)
encounters = count
end
function Paint.reset()
encounters = 0
end
return Paint

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

148
util/input.lua Normal file
View File

@ -0,0 +1,148 @@
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 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)
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, newgame)
end
function Input.cancel(accept)
if accept and Memory.value("menu", "shop_current") == 20 then
Input.press(accept)
else
local button
if bCancel then
button = "B"
else
button = "A"
end
remainingFrames = 0
sendButton(button, true)
bCancel = not bCancel
end
end
function Input.escape()
local inputTable = {Right=true, Down=true}
joypad.set(inputTable)
bridgeButton("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

152
util/memory.lua Normal file
View File

@ -0,0 +1,152 @@
local Memory = {}
local memoryNames = {
setting = {
text_speed = 0x0D3D,
battle_animation = 0x0D3E,
battle_style = 0x0D3F,
yellow_bitmask = 0x1354,
},
menu = {
settings_row = 0x0C24,
column = 0x0C25,
row = 0x0C26,
current = 0x1FFC,
main_current = 0x0C27,
input_row = 0x0C2A,
size = 0x0C28,
pokemon = 0x0C51,
shop_current = 0x0C52,
transaction_current = 0x0F8B,
selection = 0x0C30,
selection_mode = 0x0C35,
scroll_offset = 0x0C36,
text_input = 0x04B6,
text_length = 0x0EE9,
main = 0x1FF5,
},
player = {
name = 0x1158,
name2 = 0x1159,
moving = 0x1528,
x = 0x1362,
y = 0x1361,
facing = 0x152A,
repel = 0x10DB,
party_size = 0x1163,
},
game = {
map = 0x135E,
battle = 0x1057,
textbox = 0x0FC4,
},
time = {
hours = 0x1A41,
minutes = 0x1A43,
seconds = 0x1A44,
frames = 0x1A45,
},
shop = {
transaction_amount = 0x0F96,
},
progress = {
trashcans = 0x1773,
},
pokemon = {
exp1 = 0x1179,
exp2 = 0x117A,
exp3 = 0x117B,
},
battle = {
attack_turns = 0x1067,
text = 0x1125,
menu = 0x0C50,
accuracy = 0x0D1E,
x_accuracy = 0x1063,
disabled = 0x0CEE,
paralyzed = 0x1018,
opponent_next_move = 0x0CDD,
opponent_last_move = 0x0FCC,
critical = 0x105E,
miss = 0x105F,
our_turn = 0x1FF1,
battle_turns = 0x0CD5,
opponent_bide = 0x106F,
opponent_id = 0x0FE5,
opponent_level = 0x0FF3,
opponent_type1 = 0x0FEA,
opponent_type2 = 0x0FEB,
our_id = 0x1014,
our_status = 0x1018,
our_level = 0x1022,
our_type1 = 0x1019,
our_type2 = 0x101A,
},
}
local doubleNames = {
pokemon = {
attack = 0x117E,
defense = 0x1181,
speed = 0x1183,
special = 0x1185,
},
battle = {
opponent_hp = 0x0FE6,
opponent_max_hp = 0x0FF4,
opponent_attack = 0x0FF6,
opponent_defense = 0x0FF8,
opponent_speed = 0x0FFA,
opponent_special = 0x0FFC,
our_hp = 0x1015,
our_max_hp = 0x1023,
our_attack = 0x1025,
our_defense = 0x1027,
our_speed = 0x1029,
our_special = 0x102B,
},
}
local yellow = YELLOW
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

199
util/menu.lua Normal file
View File

@ -0,0 +1,199 @@
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
-- Private functions
local function getRow(menuType, scrolls)
if menuType and menuType == "settings" then
menuType = menuType.."_row"
else
menuType = "row"
end
local row = Memory.value("menu", menuType)
if scrolls then
row = row + Memory.value("menu", "scroll_offset")
end
return row
end
local function setRow(desired, throttle, scrolls, menuType, loop)
local currentRow = getRow(menuType, scrolls)
if throttle == "accelerate" then
if sliding then
throttle = false
else
local dist = math.abs(desired - currentRow)
if dist < 15 then
throttle = true
else
throttle = false
sliding = true
end
end
else
sliding = false
end
return Menu.balance(currentRow, desired, true, loop, throttle)
end
local function isCurrently(desired, menuType)
if menuType then
menuType = menuType.."_current"
else
menuType = "current"
end
return Memory.value("menu", menuType) == desired
end
Menu.isCurrently = isCurrently
-- Menu
function Menu.getCol()
return Memory.value("menu", "column")
end
function Menu.open(desired, atIndex, menuType)
if isCurrently(desired, menuType) then
return true
end
Menu.select(atIndex, false, false, menuType)
return false
end
function Menu.select(option, throttle, scrolls, menuType, dontPress, loop)
if setRow(option, throttle, scrolls, menuType, loop) then
local delay = 1
if throttle then
delay = 2
end
if not dontPress then
Input.press("A", delay)
end
return true
end
end
function Menu.cancel(desired, menuType)
if not isCurrently(desired, menuType) then
return true
end
Input.press("B")
return false
end
-- Selections
function Menu.balance(current, desired, inverted, looping, throttle)
if current == desired then
sliding = false
return true
end
if not throttle then
throttle = 0
else
throttle = 1
end
local goUp = current > desired == inverted
if looping and math.abs(current - desired) > math.floor(looping / 2) then
goUp = not goUp
end
if goUp then
Input.press("Up", throttle)
else
Input.press("Down", throttle)
end
return false
end
function Menu.sidle(current, desired, looping, throttle)
if current == desired then
return true
end
if not throttle then
throttle = 0
else
throttle = 1
end
local goLeft = current > desired
if looping and math.abs(current - desired) > math.floor(looping / 2) then
goLeft = not goLeft
end
if goLeft then
Input.press("Left", throttle)
else
Input.press("Right", throttle)
end
return false
end
function Menu.setCol(desired)
return Menu.sidle(Menu.getCol(), desired)
end
-- Options
function Menu.setOption(name, desired)
if yellow then
local rowFor = {
text_speed = 0,
battle_animation = 1,
battle_style = 2
}
local currentRow = Memory.raw(0x0D3D, true)
if Menu.balance(currentRow, rowFor[name], true, false, true) then
Input.press("Left")
end
else
local rowFor = {
text_speed = 3,
battle_animation = 8,
battle_style = 13
}
if Memory.value("setting", name) == desired then
return true
end
if setRow(rowFor[name], true, false, "settings") then
Menu.setCol(desired)
end
end
return false
end
-- Pause menu
function Menu.isOpen()
return Memory.value("game", "textbox") == 1 or Memory.value("menu", "current") == 24
end
function Menu.close()
if Memory.value("game", "textbox") == 0 and Memory.value("menu", "main") < 8 then
return true
end
Input.press("B")
end
function Menu.pause()
if Memory.value("game", "textbox") == 1 then
if Memory.value("battle", "menu") == 95 then
Input.cancel()
else
local main = Memory.value("menu", "main")
if main > 2 and main ~= 64 then
return true
end
Input.press("B")
end
else
Input.press("Start", 2)
end
end
return Menu

View File

@ -0,0 +1,60 @@
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)
local px, py = Player.position()
drawText(0, 14, currentMap..": "..px.." "..py)
drawText(0, 0, elapsedTime())
if Memory.value("battle", "our_id") > 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 bidx = Pokemon.indexOf("bulbasaur")
local pidx = Pokemon.indexOf("pidgey")
if bidx ~= -1 then
local scl = Pokemon.index(bidx, "special")
local PP1 = Memory.raw(0xD02D)
local PP2 = Memory.raw(0xD02E)
local PP3 = Memory.raw(0xD02F)
drawText(0, 134, scl.."scl "..PP1.."/"..PP2.."/"..PP3)
end
if pidx ~= -1 then
local hp = Pokemon.index(pidx, "hp")
drawText(100, 134, hp.."HP")
end
local enc = " encounter"
if encounters ~= 1 or encounters ~= 0 then
enc = enc.."s"
end
drawText(0, 125, encounters..enc)
return true
end
function Paint.wildEncounters(count)
encounters = count
end
function Paint.reset()
encounters = 0
end
return Paint

46
util/player.lua Normal file
View File

@ -0,0 +1,46 @@
local Player = {}
local Textbox = require "action.textbox"
local Input = require "util.input"
local Memory = require "util.memory"
local yellow = YELLOW
local facingDirections = {Up=8, Right=1, Left=2, Down=4}
function Player.isFacing(direction)
return Memory.value("player", "facing") == facingDirections[direction]
end
function Player.face(direction)
if Player.isFacing(direction) then
return true
end
if Textbox.handle() then
Input.press(direction, 0)
end
end
function Player.interact(direction)
if Player.face(direction) then
Input.press("A", yellow and 3 or 2)
return true
end
end
function Player.disinteract(direction)
if Player.face(direction) then
Input.press("B", yellow and 3 or 2)
return true
end
end
function Player.isMoving()
return Memory.value("player", "moving") ~= 0
end
function Player.position()
return Memory.value("player", "x"), Memory.value("player", "y")
end
return Player

145
util/settings.lua Normal file
View File

@ -0,0 +1,145 @@
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 yellow = YELLOW
local tempDir
local settings_menu
if yellow then
settings_menu = 93
else
settings_menu = 94
end
local desired = {}
if yellow then
desired.text_speed = 1
desired.battle_animation = 128
desired.battle_style = 64
else
desired.text_speed = 1
desired.battle_animation = 10
desired.battle_style = 10
end
local function isEnabled(name)
if yellow then
local matching = {
text_speed = 0xF,
battle_animation = 128,
battle_style = 64
}
local settingMask = Memory.value("setting", "yellow_bitmask", true)
return bit.band(settingMask, matching[name]) == desired[name]
else
return Memory.value("setting", name) == desired[name]
end
end
-- PUBLIC
function Settings.set(...)
for i,name in ipairs(arg) do
if not isEnabled(name) then
if Menu.open(settings_menu, 1) then
Menu.setOption(name, desired[name])
end
return false
end
end
return Menu.cancel(settings_menu)
end
function Settings.startNewAdventure(startWait)
local startMenu, withBattleStyle
withBattleStyle = "battle_style"
if yellow then
startMenu = Memory.raw(0x0F95) == 0
else
startMenu = Memory.value("player", "name") ~= 0
end
if startMenu and Menu.getCol() ~= 0 then
if Settings.set("text_speed", "battle_animation", withBattleStyle) then
Menu.select(0)
end
elseif math.random(0, startWait) == 0 then
Input.press("Start")
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

125
util/utils.lua Normal file
View File

@ -0,0 +1,125 @@
local Utils = {}
local Memory = require "util.memory"
local EMP = 1
local yellow = YELLOW
-- 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.raw(0x020E) > 0
end
function Utils.onPokemonSelect(battleMenu)
if yellow then
return battleMenu == 27 or battleMenu == 243
end
return battleMenu == 8 or battleMenu == 48 or battleMenu == 184 or battleMenu == 224
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