First Commit

First Commit, Alpha version, Will play up to first battle
This commit is contained in:
bouletmarc 2015-07-09 09:47:32 -04:00
parent 85f6a9afa2
commit c8f5de6715
23 changed files with 5813 additions and 0 deletions

296
action/battle.lua Normal file
View File

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

109
action/shop.lua Normal file
View File

@ -0,0 +1,109 @@
local Shop = {}
local Textbox = require "action.textbox"
local Input = require "util.input"
local Memory = require "util.memory"
local Menu = require "util.menu"
local Player = require "util.player"
local Inventory = require "storage.inventory"
--local yellow = YELLOW
--[[function Shop.transaction(options)
local item, itemMenu, menuIdx, quantityMenu
if options.sell then
menuIdx = 1
itemMenu = yellow and 28 or 29
quantityMenu = 158
for i,sit in ipairs(options.sell) do
local idx = Inventory.indexOf(sit.name)
if idx ~= -1 then
item = sit
item.index = idx
item.amount = Inventory.count(sit.name)
break
end
end
end
if not item and options.buy then
menuIdx = 0
itemMenu = yellow and 122 or 123
quantityMenu = 161
for i,bit in ipairs(options.buy) do
local needed = (bit.amount or 1) - Inventory.count(bit.name)
if needed > 0 then
item = bit
item.amount = needed
break
end
end
end
if not item then
if not Textbox.isActive() then
return true
end
Input.press("B")
elseif Player.isFacing(options.direction or "Left") then
if Textbox.isActive() then
local mainMenu = yellow and 245 or 32
if Menu.isCurrently(mainMenu, "shop") then
Menu.select(menuIdx, true, false, "shop")
elseif Menu.getCol() == 15 then
Input.press("A")
elseif Menu.isCurrently(itemMenu, "transaction") then
if Menu.select(item.index, "accelerate", true, "transaction", true) then
if Menu.isCurrently(quantityMenu, "shop") then
local currAmount = Memory.value("shop", "transaction_amount")
if Menu.balance(currAmount, item.amount, false, 99, true) then
Input.press("A")
end
else
Input.press("A")
end
end
else
Input.press("B")
end
else
Input.press("A", 2)
end
else
Player.interact(options.direction or "Left")
end
return false
end
function Shop.vend(options)
local item
menuIdx = 0
for i,bit in ipairs(options.buy) do
local needed = (bit.amount or 1) - Inventory.count(bit.name)
if needed > 0 then
item = bit
item.buy = needed
break
end
end
if not item then
if not Textbox.isActive() then
return true
end
Input.press("B")
elseif Player.face(options.direction) then
if Textbox.isActive() then
if Memory.value("battle", "text") > 1 and Memory.value("battle", "menu") ~= 95 then
Menu.select(item.index, true)
else
Input.press("A")
end
else
Input.press("A", 2)
end
end
return false
end]]
return Shop

192
action/textbox.lua Normal file
View File

@ -0,0 +1,192 @@
local Textbox = {}
local Input = require "util.input"
local Memory = require "util.memory"
local Menu = require "util.menu"
local alphabet_upper = "ABCDEF .GHIJKL ,MNOPQRS TUVWXYZ "
local alphabet_lower = "abcdef .ghijkl ,mnopqrs tuvwxyz "
local alphabet_number = "01234 56789 !?<>/- _{}[] "
-- < = male symbol
-- > = female symbol
-- { or } = "
-- [ or ] = '
local TableNumber = 1
local ActualUpper = 1
local function getIndexForLetter(letter, Mode)
if Mode == "Upper" then
return alphabet_upper:find(letter, 1, true)
elseif Mode == "Lower" then
return alphabet_lower:find(letter, 1, true)
elseif Mode == "Number" then
return alphabet_number:find(letter, 1, true)
end
end
function Textbox.name(letter, randomize)
local inputting = Memory.value("menu", "text_input")
if inputting then
-- Set vars
local lidx
local drow
local dcol
local NameTable = {}
local ColumnMax
--Get values
local crow = Memory.value("text_inputing", "row")
local ccol = Memory.value("text_inputing", "column")
local mode = Memory.value("text_inputing", "mode")
--if letter then
local StringLenght = string.len(letter)
letter:gsub(".",function(letter2)
table.insert(NameTable,letter2)
if NameTable[TableNumber] then
local Mode = "Upper"
--its a letter
if string.match(NameTable[TableNumber], '%a') then
if string.match(NameTable[TableNumber], '%u') then
Mode = "Upper"
elseif string.match(NameTable[TableNumber], '%l') then
Mode = "Lower"
end
--its a number
elseif string.match(NameTable[TableNumber], '%d') then
Mode = "Number"
--its anything but not a letter or a number
else
if string.find(alphabet_upper, NameTable[TableNumber]) ~= nil then
Mode = "Upper"
elseif string.find(alphabet_lower, NameTable[TableNumber]) ~= nil then
Mode = "Lower"
elseif string.find(alphabet_number, NameTable[TableNumber]) ~= nil then
Mode = "Number"
end
end
--Set lidx
lidx = getIndexForLetter(NameTable[TableNumber], Mode)
local Waiting = Input.isWaiting()
--Proceed
if not Waiting then
--Get/set Lower/Upper
if Mode == "Upper" and mode ~= 0 or Mode == "Lower" and mode ~= 1 or Mode == "Number" and mode ~= 2 then
if mode == 2 then
ColumnMax = 6
else
ColumnMax = 8
end
if crow ~= 0 then
Input.press("Up", 2)
elseif crow == 0 then
if ccol < ColumnMax then
Input.press("Right", 2)
else
Input.press("A", 2)
end
end
--Get/Set Letter
else
if mode == 2 then
ColumnMax = 6
else
ColumnMax = 8
end
dcol = math.fmod(lidx - 1, ColumnMax)
if ccol < dcol then
Input.press("Right", 2)
elseif ccol > dcol then
Input.press("Left", 2)
elseif ccol == dcol then
drow = math.ceil(lidx/ColumnMax)-1
if crow < drow then
Input.press("Down", 2)
elseif crow > drow then
Input.press("Up", 2)
elseif crow == drow then
Input.press("A", 2)
TableNumber = TableNumber + 1
end
end
end
end
end
end)
local Waiting = Input.isWaiting()
if TableNumber > StringLenght and not Waiting then
if Memory.value("menu", "text_length")-7 > 0 then
if mode == 2 then
ColumnMax = 6
else
ColumnMax = 8
end
--get column/row
if crow ~= 2 and ccol ~= ColumnMax then
Input.press("Start", 2)
elseif crow == 2 and ccol == ColumnMax then
Input.press("A", 2)
TableNumber = 1
ActualUpper = 1
NameTable = {}
return true
end
end
end
--[[else
if Memory.value("menu", "text_length")-7 > 0 then
Input.press("Start")
return true
end
lidx = nidoIdx
crow = Memory.value("menu", "input_row")
drow = math.ceil(lidx / 9)
if Menu.balance(crow, drow, true, 6, true) then
ccol = math.floor(Memory.value("menu", "column") / 2)
dcol = math.fmod(lidx - 1, 9)
if Menu.sidle(ccol, dcol, 9, true) then
Input.press("A")
end
end]]
--end
else
--Reset Values
TableNumber = 1
ActualUpper = 1
NameTable = {}
if randomize then
Input.press("A", math.random(1, 5))
else
Input.press("A", 2)
--Input.cancel()
end
end
end
function Textbox.isActive()
local Active = false
--if Memory.value("game", "textbox") == 1 or Memory.value("game", "textboxing") == 1 then
if Memory.value("game", "textbox") > 0 then
Active = true
end
return Active
end
function Textbox.handle()
if not Textbox.isActive() then
return true
end
Input.cancel()
end
return Textbox

169
action/walk.lua Normal file
View File

@ -0,0 +1,169 @@
local Walk = {}
local Control = require "ai.control"
local Paths = require "data.paths"
local Input = require "util.input"
local Memory = require "util.memory"
local Player = require "util.player"
local Pokemon = require "storage.pokemon"
local path, stepIdx, currentMap, currentMap2
local pathIdx = 0
local customIdx = 1
local customDir = 1
-- Private functions
local function setPath(index, region)
pathIdx = index
stepIdx = 2
currentMap = region
path = Paths[index]
end
local function completeStep(region)
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, slow)
local px, py = Player.position()
if px == dx and py == dy then
return true
end
--set slow mode
local SlowMode = false
if slow ~= nil and slow == true then
SlowMode = true
end
Input.press(dir(px, py, dx, dy), 0, SlowMode)
end
Walk.step = step
-- Table functions
function Walk.reset()
path = nil
pathIdx = 0
customIdx = 1
customDir = 1
currentMap = nil
currentMap2 = nil
Walk.strategy = nil
end
function Walk.init()
local region = Memory.double("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[3]
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
setPath(pathIdx + 1, region)
newIndex = pathIdx
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], tile[3]) 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
--return Memory.value("player", "moving") == 1 and Memory.value("game", "battle") == 0
return Memory.value("player", "moving") == 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

420
ai/combat.lua Normal file
View File

@ -0,0 +1,420 @@
local Combat = {}
local Movelist = require "data.movelist"
local Opponents = require "data.opponents"
local Utils = require "util.utils"
local Memory = require "util.memory"
local Pokemon = require "storage.pokemon"
local damageMultiplier = { -- http://bulbapedia.bulbagarden.net/wiki/Type_chart#Generation_II
normal = {normal=1.0, fighting=1.0, flying=1.0, poison=1.0, ground=1.0, rock=0.5, bug=1.0, ghost=0.0, steel=0.5, fire=1.0, water=1.0, grass=1.0, electric=1.0, psychic=1.0, ice=1.0, dragon=1.0, dark=1.0, },
fighting = {normal=2.0, fighting=1.0, flying=0.5, poison=0.5, ground=1.0, rock=2.0, bug=0.5, ghost=0.0, steel=2.0, fire=1.0, water=1.0, grass=1.0, electric=1.0, psychic=0.5, ice=2.0, dragon=1.0, dark=2.0, },
flying = {normal=1.0, fighting=2.0, flying=1.0, poison=1.0, ground=1.0, rock=0.5, bug=2.0, ghost=1.0, steel=0.5, fire=1.0, water=1.0, grass=2.0, electric=0.5, psychic=1.0, ice=1.0, dragon=1.0, dark=1.0, },
poison = {normal=1.0, fighting=1.0, flying=1.0, poison=0.5, ground=0.5, rock=0.5, bug=2.0, ghost=0.5, steel=0.0, fire=1.0, water=1.0, grass=2.0, electric=1.0, psychic=1.0, ice=1.0, dragon=1.0, dark=1.0, },
ground = {normal=1.0, fighting=1.0, flying=0.0, poison=2.0, ground=1.0, rock=2.0, bug=0.5, ghost=1.0, steel=2.0, fire=2.0, water=1.0, grass=0.5, electric=2.0, psychic=1.0, ice=1.0, dragon=1.0, dark=1.0, },
rock = {normal=1.0, fighting=0.5, flying=2.0, poison=1.0, ground=0.5, rock=1.0, bug=2.0, ghost=1.0, steel=0.5, fire=2.0, water=1.0, grass=1.0, electric=1.0, psychic=1.0, ice=2.0, dragon=1.0, dark=1.0, },
bug = {normal=1.0, fighting=0.5, flying=0.5, poison=2.0, ground=1.0, rock=1.0, bug=1.0, ghost=0.5, steel=0.5, fire=0.5, water=1.0, grass=2.0, electric=1.0, psychic=2.0, ice=1.0, dragon=1.0, dark=2.0, },
ghost = {normal=0.0, fighting=1.0, flying=1.0, poison=1.0, ground=1.0, rock=1.0, bug=1.0, ghost=2.0, steel=1.0, fire=1.0, water=1.0, grass=1.0, electric=1.0, psychic=0.0, ice=1.0, dragon=1.0, dark=0.5, },
steel = {normal=1.0, fighting=1.0, flying=1.0, poison=1.0, ground=1.0, rock=2.0, bug=1.0, ghost=1.0, steel=0.5, fire=0.5, water=0.5, grass=1.0, electric=0.5, psychic=1.0, ice=2.0, dragon=1.0, dark=1.0, },
fire = {normal=1.0, fighting=1.0, flying=1.0, poison=1.0, ground=1.0, rock=0.5, bug=2.0, ghost=1.0, steel=2.0, fire=0.5, water=0.5, grass=2.0, electric=1.0, psychic=1.0, ice=2.0, dragon=0.5, dark=1.0, },
water = {normal=1.0, fighting=1.0, flying=1.0, poison=1.0, ground=2.0, rock=2.0, bug=1.0, ghost=1.0, steel=1.0, fire=2.0, water=0.5, grass=0.5, electric=1.0, psychic=1.0, ice=1.0, dragon=0.5, dark=1.0, },
grass = {normal=1.0, fighting=1.0, flying=0.5, poison=0.5, ground=2.0, rock=2.0, bug=0.5, ghost=1.0, steel=0.5, fire=0.5, water=2.0, grass=0.5, electric=1.0, psychic=1.0, ice=1.0, dragon=0.5, dark=1.0, },
electric = {normal=1.0, fighting=1.0, flying=2.0, poison=1.0, ground=0.0, rock=1.0, bug=1.0, ghost=1.0, steel=1.0, fire=1.0, water=2.0, grass=0.5, electric=0.5, psychic=1.0, ice=1.0, dragon=0.5, dark=1.0, },
psychic = {normal=1.0, fighting=2.0, flying=1.0, poison=2.0, ground=1.0, rock=1.0, bug=1.0, ghost=1.0, steel=0.5, fire=1.0, water=1.0, grass=1.0, electric=1.0, psychic=0.5, ice=1.0, dragon=1.0, dark=0.0, },
ice = {normal=1.0, fighting=1.0, flying=2.0, poison=1.0, ground=2.0, rock=1.0, bug=1.0, ghost=1.0, steel=0.5, fire=1.0, water=0.5, grass=2.0, electric=1.0, psychic=1.0, ice=0.5, dragon=2.0, dark=1.0, },
dragon = {normal=1.0, fighting=1.0, flying=1.0, poison=1.0, ground=1.0, rock=1.0, bug=1.0, ghost=1.0, steel=0.5, fire=1.0, water=1.0, grass=1.0, electric=1.0, psychic=1.0, ice=1.0, dragon=2.0, dark=1.0, },
dark = {normal=1.0, fighting=0.5, flying=1.0, poison=1.0, ground=1.0, rock=1.0, bug=1.0, ghost=2.0, steel=1.0, fire=1.0, water=1.0, grass=1.0, electric=1.0, psychic=2.0, ice=1.0, dragon=1.0, dark=0.5, },
}
local types = {}
types[0] = "normal"
types[1] = "fighting"
types[2] = "flying"
types[3] = "poison"
types[4] = "ground"
types[5] = "rock"
types[6] = "bug"
types[7] = "ghost" --??
types[8] = "steel" --??
types[10] = "fire"
types[11] = "water"
types[12] = "grass"
types[13] = "electric"
types[14] = "psychic"
types[15] = "ice"
types[16] = "dragon"
types[17] = "dark"
local savedEncounters = {}
local enablePP = false
local floor = math.floor
--[[local function isDisabled(mid)
return mid == Memory.value("battle", "disabled")
end
Combat.isDisabled = isDisabled]]
local function calcDamage(move, attacker, defender, rng)
if move.fixed then
return move.fixed, move.fixed
end
--[[if move.power == 0 or isDisabled(move.id) then
return 0, 0
end
if move.power > 9000 then
if Memory.value("battle", "x_accuracy") == 1 and defender.speed < attacker.speed then
return 9001, 9001
end
return 0, 0
end
if move.name == "Thrash" and Combat.disableThrash then
return 0, 0
end]]
local attFactor, defFactor
if move.special then
--attFactor, defFactor = attacker.spec, defender.spec
attFactor, defFactor = attacker.spec_att, defender.spec_def
else
attFactor, defFactor = attacker.att, defender.def
end
local damage = floor(floor(floor(2 * attacker.level / 5 + 2) * math.max(1, attFactor) * move.power / math.max(1, defFactor)) / 50) + 2
if move.move_type == attacker.type1 or move.move_type == attacker.type2 then
damage = floor(damage * 1.5) -- STAB
end
local dmp = damageMultiplier[move.move_type]
local typeEffect1, typeEffect2 = dmp[defender.type1], dmp[defender.type2]
if defender.type1 == defender.type2 then
typeEffect2 = 1
end
damage = floor(damage * typeEffect1 * typeEffect2)
if move.multiple then
damage = damage * move.multiple
end
if rng then
return damage, damage
end
return floor(damage * 217 / 255), damage
end
local function getOpponentType(ty)
local t1 = types[Memory.value("battle", "opponent_type1")]
if ty ~= 0 then
t1 = types[Memory.value("battle", "opponent_type2")]
if not t1 then
return Memory.value("battle", "opponent_type2")
end
end
if t1 then
return t1
end
return Memory.value("battle", "opponent_type1")
end
Combat.getOpponentType = getOpponentType
function getOurType(ty)
local t1 = types[Memory.value("battle", "our_type1")]
if ty ~= 0 then
t1 = types[Memory.value("battle", "our_type2")]
if not t1 then
return Memory.value("battle", "opponent_type2")
end
end
if t1 then
return t1
end
return Memory.value("battle", "opponent_type1")
end
Combat.getOurType = getOurType
local function getMoves(who)--Get the moveset of us [0] or them [1]
local moves = {}
local base
if who == 1 then
base = Memory.value("battle", "opponent_move_id")
else
base = Memory.value("battle", "our_move_id")
end
for idx=0, 3 do
local val = Memory.raw(base + idx)
if val > 0 then
local moveTable = Movelist.get(val)
if who == 0 then
moveTable.pp = Memory.value("battle", "our_move_pp")
end
moves[idx + 1] = moveTable
end
end
return moves
end
Combat.getMoves = getMoves
local function modPlayerStats(user, enemy, move)
local effect = move.effects
if effect then
local diff = effect.diff
local hitThem = diff < 0
local stat = effect.stat
if hitThem then
enemy[stat] = math.max(2, enemy[stat] + diff)
else
user[stat] = user[stat] + diff
end
end
return user, enemy
end
local function calcBestHit(attacker, defender, ours, rng)
local bestTurns, bestMinTurns = 9001, 9001
local bestDmg = -1
local ourMaxHit
local ret = nil
for idx,move in ipairs(attacker.moves) do
if not move.pp or move.pp > 0 then
local minDmg, maxDmg = calcDamage(move, attacker, defender, rng)
if maxDmg then
local minTurns, maxTurns
if maxDmg <= 0 then
minTurns, maxTurns = 9001, 9001
else
minTurns = math.ceil(defender.hp / maxDmg)
maxTurns = math.ceil(defender.hp / minDmg)
end
if ours then
local replaces
if not ret or minTurns < bestMinTurns or maxTurns < bestTurns then
replaces = true
--elseif maxTurns == bestTurns and move.name == "Thrash" then
-- replaces = defender.hp == Memory.double("battle", "opponent_max_hp")
--elseif maxTurns == bestTurns and ret.name == "Thrash" then
-- replaces = defender.hp ~= Memory.double("battle", "opponent_max_hp")
elseif move.fast and not ret.fast then
replaces = maxTurns <= bestTurns
elseif ret.fast then
replaces = maxTurns < bestTurns
--[[elseif enablePP then
if maxTurns < 2 or maxTurns == bestMaxTurns then
if ret.name == "Earthquake" and (move.name == "Ice-Beam" or move.name == "Thunderbolt") then
replaces = true
elseif move.pp > ret.pp then
if ret.name == "Horn-Drill" then
replaces = true
elseif move.name ~= "Earthquake" then
replaces = true
end
end
end]]
elseif minDmg > bestDmg then
replaces = true
end
if replaces then
ret = move
bestMinTurns = minTurns
bestTurns = maxTurns
bestDmg = minDmg
ourMaxHit = maxDmg
end
elseif maxDmg > bestDmg then -- Opponents automatically hit max
ret = move
bestTurns = minTurns
bestDmg = maxDmg
end
end
end
end
if ret then
ret.damage = bestDmg
ret.maxDamage = ourMaxHit
ret.minTurns = bestMinTurns
return ret, bestTurns
end
end
local function getBestMove(ours, enemy, draw)
if enemy.hp < 1 then
return
end
local bm, bestUs = calcBestHit(ours, enemy, true)
local jj, bestEnemy = calcBestHit(enemy, ours, false)
if not bm then
return
end
if draw and bm.midx then
Utils.drawText(0, 35, ''..bm.midx.." "..bm.name)
end
return bm, bestUs, bestEnemy
end
local function activePokemon(preset)
local ours = {
id = Memory.value("battle", "our_id"),
level = Memory.value("battle", "our_level"),
hp = Memory.double("battle", "our_hp"),
att = Memory.double("battle", "our_attack"),
def = Memory.double("battle", "our_defense"),
--spec = Memory.double("battle", "our_special"),
spec_att = Memory.double("battle", "our_special_attack"),
spec_def = Memory.double("battle", "our_special_defense"),
speed = Memory.double("battle", "our_speed"),
type1 = getOurType(0),
type2 = getOurType(1),
moves = getMoves(0),
}
local enemy
if preset then
enemy = Opponents[preset]
local toBoost = enemy.boost
if toBoost then
--local currSpec = ours.spec
local currSpec = ours.spec_att
local booster = toBoost.mp
if (currSpec < 140) == (booster > 1) then
--ours.spec = math.floor(currSpec * booster)
ours.spec_att = math.floor(currSpec * booster)
end
end
else
enemy = {
id = Memory.value("battle", "opponent_id"),
level = Memory.value("battle", "opponent_level"),
hp = Memory.double("battle", "opponent_hp"),
att = Memory.double("battle", "opponent_attack"),
def = Memory.double("battle", "opponent_defense"),
--spec = Memory.double("battle", "our_special"),
spec_att = Memory.double("battle", "our_special_attack"),
spec_def = Memory.double("battle", "our_special_defense"),
speed = Memory.double("battle", "opponent_speed"),
type1 = getOpponentType(0),
type2 = getOpponentType(1),
moves = getMoves(1),
}
end
return ours, enemy
end
Combat.activePokemon = activePokemon
local function isSleeping()
--return Memory.raw(0x116F) > 1
return Memory.value("battle", "our_status") == 14 --######################
end
Combat.isSleeping = isSleeping
local function isConfused()
--return Memory.raw(0x106B) > 0
return Memory.value("battle", "our_status") == 15 --######################
end
Combat.isConfused = isConfused
-- HP
function Combat.hp()
return Pokemon.index(0, "hp")
end
function Combat.redHP()
return math.ceil(Pokemon.index(0, "max_hp") * 0.2)
end
function Combat.inRedBar()
return Combat.hp() <= Combat.redHP()
end
-- Combat AI
function Combat.factorPP(enabled)
enablePP = enabled
end
function Combat.reset()
enablePP = false
end
function Combat.healthFor(opponent)
local ours, enemy = activePokemon(opponent)
local enemyAttack, turnsToDie = calcBestHit(enemy, ours, false)
return enemyAttack.damage
end
function Combat.inKillRange(draw)
local ours, enemy = activePokemon()
local enemyAttack, __ = calcBestHit(enemy, ours, false)
local __, turnsToKill = calcBestHit(ours, enemy, true)
if not turnsToKill or not enemyAttack then
return false
end
if draw then
Utils.drawText(0, 21, ours.speed.." "..enemy.speed)
Utils.drawText(0, 28, turnsToDie.." "..ours.hp.." | "..turnsToKill.." "..enemy.hp)
end
local hpReq = enemyAttack.damage
local isConfused = isConfused()
if isConfused then
hpReq = hpReq + math.floor(ours.hp * 0.2)
end
if ours.hp <= hpReq then
local outsped = enemyAttack.outspeed
if outsped and outsped ~= true then
outsped = Memory.value("battle", "attack_turns") > 0
end
if outsped or isConfused or turnsToKill > 1 or ours.speed <= enemy.speed or isSleeping() then
return ours, hpReq
end
end
end
local function getBattlePokemon()
local ours, enemy = activePokemon()
if enemy.hp == 0 then
return
end
for idx=1,4 do
local move = ours.moves[idx]
if move then
move.midx = idx
end
end
return ours, enemy
end
function Combat.nonKill()
local ours, enemy = getBattlePokemon()
if not enemy then
return
end
local bestDmg = -1
local ret = nil
for idx,move in ipairs(ours.moves) do
if not move.pp or move.pp > 0 then
local __, maxDmg = calcDamage(move, ours, enemy, true)
local threshold = maxDmg * 0.975
if threshold and threshold < enemy.hp and threshold > bestDmg then
ret = move
bestDmg = threshold
end
end
end
return ret
end
function Combat.bestMove()
local ours, enemy = getBattlePokemon()
if enemy then
return getBestMove(ours, enemy)
end
end
function Combat.enemyAttack()
local ours, enemy = activePokemon()
if enemy.hp == 0 then
return
end
return calcBestHit(enemy, ours, false)
end
return Combat

300
ai/control.lua Normal file
View File

@ -0,0 +1,300 @@
local Control = {}
local Battle
local Strategies
local Combat = require "ai.combat"
local Bridge = require "util.bridge"
local Memory = require "util.memory"
local Paint = require "util.paint"
local Utils = require "util.utils"
local Inventory = require "storage.inventory"
local Pokemon = require "storage.pokemon"
local potionInBattle = true
local encounters = 0
local canDie, shouldFight, minExp
local shouldCatch, attackIdx
local extraEncounter, maxEncounters
local battleYolo
Control.areaName = "Unknown"
Control.moonEncounters = nil
Control.getMoonExp = true
Control.yolo = false
--[[local function withinOneKill(forExp)
return Pokemon.getExp() + 80 > forExp
end]]
local controlFunctions = {
a = function(data)
Control.areaName = data.a
return true
end,
potion = function(data)
if data.b ~= nil then
Control.battlePotion(data.b)
end
battleYolo = data.yolo
end,
encounters = function(data)
if RESET_FOR_TIME then
maxEncounters = data.limit
extraEncounter = data.extra
end
end,
pp = function(data)
Combat.factorPP(data.on)
end,
setThrash = function(data)
Combat.disableThrash = data.disable
end,
disableCatch = function()
shouldCatch = nil
shouldFight = nil
end,
-- EXP
--[[viridianExp = function()
minExp = 210
shouldFight = {{name="rattata",lvl={2,3}}, {name="pidgey",lvl={2}}}
end,
viridianBackupExp = function()
minExp = 210
shouldFight = {{name="rattata",lvl={2,3}}, {name="pidgey",lvl={2,3}}}
end,
nidoranBackupExp = function()
minExp = 210
shouldFight = {{name="rattata"}, {name="pidgey"}, {name="nidoran"}, {name="nidoranf",lvl={2}}}
end,
-- CATCH
catchNidoran = function()
shouldCatch = {{name="nidoran",lvl={3,4}}, {name="spearow"}}
end,
catchFlier = function()
shouldCatch = {{name="spearow",alt="pidgey",hp=15}, {name="pidgey",alt="spearow",hp=15}}
end,]]
}
-- COMBAT
function Control.battlePotion(enable)
potionInBattle = enable
end
function Control.canDie(enabled)
if enabled == nil then
return canDie
end
canDie = enabled
end
local function isNewFight()
if Memory.double("battle", "opponent_hp") == Memory.double("battle", "opponent_max_hp") then
return true
end
end
--[[function Control.shouldFight()
if not shouldFight then
return false
end
local expTotal = Pokemon.getExp()
if expTotal < minExp then
local oid = Memory.value("battle", "opponent_id")
local olvl = Memory.value("battle", "opponent_level")
for i,p in ipairs(shouldFight) do
if oid == Pokemon.getID(p.name) and (not p.lvl or Utils.match(olvl, p.lvl)) then
if oneHits then
local move = Combat.bestMove()
if move and move.maxDamage * 0.925 < Memory.double("battle", "opponent_hp") then
return false
end
end
return true
end
end
end
end`]]
function Control.canCatch(partySize)
if not partySize then
partySize = Memory.value("player", "party_size")
end
local pokeballs = Inventory.count("pokeball")
--local minimumCount = 4 - partySize
local minimumCount = 1
if pokeballs < minimumCount then
Strategies.reset("Not enough PokeBalls", pokeballs)
return false
end
return true
end
--[[function Control.shouldCatch(partySize)
if maxEncounters and encounters > maxEncounters then
local extraCount = extraEncounter and Pokemon.inParty(extraEncounter)
if not extraCount or encounters > maxEncounters + 1 then
Strategies.reset("Too many encounters", encounters)
return false
end
end
if not shouldCatch then
return false
end
if not partySize then
partySize = Memory.value("player", "party_size")
end
if partySize == 4 then
shouldCatch = nil
return false
end
if not Control.canCatch(partySize) then
return true
end
local oid = Memory.value("battle", "opponent_id")
for i,poke in ipairs(shouldCatch) do
if oid == Pokemon.getID(poke.name) and not Pokemon.inParty(poke.name, poke.alt) then
if not poke.lvl or Utils.match(Memory.value("battle", "opponent_level"), poke.lvl) then
local penultimate = poke.hp and Memory.double("battle", "opponent_hp") > poke.hp
if penultimate then
penultimate = Combat.nonKill()
end
if penultimate then
require("action.battle").fight(penultimate.midx)
else
Inventory.use("pokeball", nil, true)
end
return true
end
end
end
end]]
-- Items
function Control.canRecover()
return potionInBattle and (not battleYolo or not Control.yolo)
end
function Control.set(data)
controlFunctions[data.c](data)
end
function Control.setYolo(enabled)
Control.yolo = enabled
end
function Control.setPotion(enabled)
potionInBattle = enabled
end
function Control.encounters()
return encounters
end
function Control.encounter(battleState)
if battleState > 0 then
local wildBattle = false
if battleState == 1 then
wildBattle = true
end
--[[local isCritical
local battleMenu = Memory.value("battle", "menu")
if battleMenu == 94 then
isCritical = false
Control.missed = false
elseif Memory.double("battle", "our_hp") == 0 then
if Memory.value("battle", "critical") == 1 then
isCritical = true
end
elseif not Control.missed then
local turnMarker = Memory.value("battle", "our_turn")
if turnMarker == 100 or turnMarker == 128 then
local isMiss = Memory.value("battle", "miss") == 1
if isMiss then
if not Control.ignoreMiss and Battle.accurateAttack and Memory.value("battle", "accuracy") == 7 then
Bridge.chat("gen 1 missed :( (1 in 256 chance)")
end
Control.missed = true
end
end
end
if isCritical ~= nil and isCritical ~= Control.criticaled then
Control.criticaled = isCritical
end]]
if wildBattle then
local opponentHP = Memory.double("battle", "opponent_hp")
if not Control.inBattle then
Control.escaped = false
if opponentHP > 0 then
Control.killedCatch = false
Control.inBattle = true
encounters = encounters + 1
Paint.wildEncounters(encounters)
Bridge.encounter()
--if Control.moonEncounters then
-- Control.moonEncounters = Control.moonEncounters + 1
--end
end
else
--if opponentHP == 0 and shouldCatch and not Control.killedCatch then
--if opponentHP == 0 and shouldCatch then
--local gottaCatchEm = {"pidgey", "spearow", "paras", "oddish"}
--local opponent = Battle.opponent()
--for i,catch in ipairs(gottaCatchEm) do
-- if opponent == catch then
-- if not Pokemon.inParty(catch) then
-- Bridge.chat("accidentally killed "..Utils.capitalize(catch).." with a "..(isCritical and "critical" or "high damage range").." :(")
-- Control.killedCatch = true
-- end
-- break
-- end
--end
--end
end
end
elseif Control.inBattle then
if Memory.value("battle", "battle_turns") == 0 then
Control.escaped = true
end
Control.inBattle = false
end
end
function Control.reset()
canDie = false
oneHits = false
--shouldCatch = nil
--shouldFight = nil
extraEncounter = nil
potionInBattle = true
encounters = 0
battleYolo = false
maxEncounters = nil
Control.yolo = false
Control.inBattle = false
end
function Control.init()
local LowerGameName = string.lower(GAME_NAME)
Battle = require "action.battle"
Strategies = require("ai."..LowerGameName..".strategies")
end
return Control

178
ai/emerald/strategies.lua Normal file
View File

@ -0,0 +1,178 @@
local Combat = require "ai.combat"
local Control = require "ai.control"
local Strategies = require "ai.strategies"
local Battle = require "action.battle"
local Shop = require "action.shop"
local Textbox = require "action.textbox"
local Walk = require "action.walk"
local Bridge = require "util.bridge"
local Input = require "util.input"
local Memory = require "util.memory"
local Menu = require "util.menu"
local Player = require "util.player"
local Utils = require "util.utils"
local Inventory = require "storage.inventory"
local Pokemon = require "storage.pokemon"
local status = Strategies.status
local strategyFunctions = Strategies.functions
--local bulbasaurScl
--local UsingSTRATS = ""
-- TIME CONSTRAINTS
Strategies.timeRequirements = {
--[[charmander = function()
return 2.39
end,
pidgey = function()
local timeLimit = 7.55
return timeLimit
end,
glitch = function()
local timeLimit = 10.15
if Pokemon.inParty("pidgey") then
timeLimit = timeLimit + 0.67
end
return timeLimit
end,]]
}
-- HELPERS
--[[local function pidgeyDSum()
local sx, sy = Player.position()
if status.tries == nil then
if status.tries then
status.tries.idx = 1
status.tries.x, status.tries.y = sx, sy
else
status.tries = 0
end
end
if status.tries ~= 0 and Control.escaped then
if status.tries[status.tries.idx] == 0 then
tries.idx = tries.idx + 1
if tries.idx > 3 then
tries = 0
end
return pidgeyDSum()
end
if status.tries.x ~= sx or status.tries.y ~= sy then
status.tries[status.tries.idx] = status.tries[status.tries.idx] - 1
status.tries.x, status.tries.y = sx, sy
end
sy = 47
else
sy = 48
end
if sx == 8 then
sx = 9
else
sx = 8
end
Walk.step(sx, sy)
end
local function tackleDSum()
local sx, sy = Player.position()
if status.tries == nil then
if status.tries then
status.tries.idx = 1
status.tries.x, status.tries.y = sx, sy
else
status.tries = 0
end
end
if status.tries ~= 0 and Control.escaped then
if status.tries[status.tries.idx] == 0 then
tries.idx = tries.idx + 1
if tries.idx > 3 then
tries = 0
end
return tackleDSum()
end
if status.tries.x ~= sx or status.tries.y ~= sy then
status.tries[status.tries.idx] = status.tries[status.tries.idx] - 1
status.tries.x, status.tries.y = sx, sy
end
--sx = 1
--else
--sx = 2
end
if sy == 6 then
sy = 8
else
sy = 6
end
Walk.step(sx, sy)
end]]
-- STRATEGIES
local strategyFunctions = Strategies.functions
strategyFunctions.setHour = function()
if Strategies.initialize() then
status.tempDir = false
end
local Main = Memory.value("menu", "main")
local Current = Memory.value("menu", "settings_current")
local Row = Memory.value("menu", "start_menu_row")
local Hours = Memory.value("menu", "hours_row")
local Mins = Memory.value("menu", "minutes_row")
if Main == 29 then
local Waiting = Input.isWaiting()
if not Waiting then
if Current == 76 then
if Hours < GAME_HOURS then
Input.press("Right", 0)
elseif Hours > GAME_HOURS then
Input.press("Left", 0)
else
if Mins < GAME_MINUTES then
Input.press("Right", 0)
elseif Mins > GAME_MINUTES then
Input.press("Left", 0)
else
Input.press("A", 2)
end
end
elseif Current == 78 then
if Row == 1 then
Input.press("Up", 2)
else
Input.press("A", 2)
return true
end
end
end
else
Input.press("A", 2)
end
end
-- PROCESS
function Strategies.completeGameStrategy()
status = Strategies.status
end
function Strategies.resetGame()
--maxEtherSkip = false
status = Strategies.status
stats = Strategies.stats
end
return Strategies

369
ai/strategies.lua Normal file
View File

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

1522
data/movelist.lua Normal file

File diff suppressed because it is too large Load Diff

225
data/opponents.lua Normal file
View File

@ -0,0 +1,225 @@
local Opponents = {
--[[RivalGyarados = {
type1 = "water",
type2 = "flying",
def = 71,
id = 22,
spec = 87,
hp = 126,
speed = 72,
level = 38,
att = 106,
moves = {
{
name = "Hydro-Pump",
accuracy = 80,
power = 120,
id = 56,
special = true,
max_pp = 5,
move_type = "water",
}
},
boost = {
stat = "spec",
mp = 1.5
}
},
HypnoHeadbutt = {
type1 = "psychic",
type2 = "psychic",
def = 58,
id = 129,
spec = 88,
hp = 107,
speed = 56,
level = 34,
att = 60,
moves = {
{
name = "Headbutt",
accuracy = 100,
power = 70,
id = 29,
special = false,
max_pp = 15,
move_type = "normal",
}
}
},
HypnoConfusion = {
type1 = "psychic",
type2 = "psychic",
def = 58,
id = 129,
spec = 88,
hp = 107,
speed = 56,
level = 34,
att = 60,
moves = {
{
name = "Confusion",
accuracy = 100,
power = 50,
id = 93,
special = true,
max_pp = 25,
move_type = "psychic",
}
}
},
KogaWeezing = {
type1 = "poison",
type2 = "poison",
def = 115,
id = 143,
spec = 84,
hp = 115,
speed = 63,
level = 43,
att = 90,
moves = {
{
name = "Self-Destruct",
accuracy = 100,
power = 260,
id = 120,
special = false,
max_pp = 5,
move_type = "normal",
}
}
},
GiovanniRhyhorn = {
type1 = "ground",
type2 = "rock",
def = 97,
id = 18,
spec = 39,
hp = 134,
speed = 34,
level = 45,
att = 89,
moves = {
{
name = "Stomp",
move_type = "normal",
accuracy = 100,
power = 65,
id = 23,
special = false,
max_pp = 20,
damage = 21,
}
}
},
LoreleiDewgong = {
type1 = "water",
type2 = "ice",
def = 100,
id = 120,
spec = 116,
hp = 169,
speed = 89,
level = 54,
att = 90,
moves = {
{
name = "Aurora-Beam",
accuracy = 100,
power = 65,
id = 62,
special = true,
max_pp = 20,
move_type = "ice",
}
},
boost = {
stat = "spec",
mp = 2 / 3
}
},
LanceGyarados = {
type1 = "water",
type2 = "flying",
def = 105,
id = 22,
spec = 130,
hp = 187,
speed = 108,
level = 58,
att = 160,
moves = {
{
name = "Hydro-Pump",
accuracy = 80,
power = 120,
id = 56,
special = true,
max_pp = 5,
move_type = "water",
}
},
boost = {
stat = "spec",
mp = 1.5
}
},
BluePidgeot = {
type1 = "normal",
type2 = "flying",
def = 106,
id = 151,
spec = 100,
hp = 182,
speed = 125,
level = 61,
att = 113,
moves = {
{
name = "Wing-Attack",
accuracy = 100,
power = 35,
id = 17,
special = false,
max_pp = 35,
move_type = "flying",
}
}
},
BlueSky = {
type1 = "normal",
type2 = "flying",
def = 106,
id = 151,
spec = 100,
hp = 182,
speed = 125,
level = 61,
att = 113,
moves = {
{
name = "Sky-Attack",
accuracy = 90,
power = 140,
id = 143,
special = false,
max_pp = 5,
move_type = "flying",
}
}
},]]
}
return Opponents

38
data/paths.lua Normal file
View File

@ -0,0 +1,38 @@
local paths = {
-- Inside truck
{65, {9,9}, {12,9}},
-- Go to Mom House
{9, {11,17}},
-- Mom house
{1, {15,15}, {15,14}, {15,9}},
-- Bedroom
{2, {14,9}, {12,9}, {s="setDirection",dir="Up"}, {s="setHour"}, {12,9}, {14,9}, {14,8}},
-- Dad TV Show
{1, {15,10}, {11,12}, {15,12}, {15,16}},
-- Go to neightbor house
{9, {12,16}, {21,16}, {21,15}},
-- Inside Neighbor house
{3, {9,15}, {9,9}},
-- Inside Bedroom
{4, {8,9}, {8,10}, {12,10}, {s="setDirection",dir="Down"}, {s="speak"}, {8,10}, {8,8}},
-- Inside house
{3, {9,10}, {9,16}},
-- Go Help prof.
{9, {21,16}, {18,16}, {18,8}}
}
--Remake Path for Girl
if GAME_GENDER == 2 then
paths[2] = {9, {20,17}}
paths[3] = {3, {9,15}, {9,14}, {9,9}}
paths[4] = {4, {8,9}, {10,9}, {s="setDirection",dir="Up"}, {s="setHour"}, {10,9}, {8,9}, {8,8}}
paths[5] = {3, {9,10}, {13,12}, {9,12}, {9,16}}
paths[6] = {9, {21,16}, {12,16}, {12,15}}
paths[7] = {1, {15,15}, {15,9}}
paths[8] = {2, {14,9}, {14,10}, {10,10}, {s="setDirection",dir="Down"}, {s="speak"}, {14,10}, {14,8}}
paths[9] = {1, {15,10}, {15,16}}
paths[10][2] = {12,16}
end
return paths

239
main.lua Normal file
View File

@ -0,0 +1,239 @@
--##################################################
--############# ############
--############# 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 = "Emerald" -- Set to Ruby/Sapphire or Emerald
GAME_HOURS = 17 -- Set the internal game hour (0-23h)
GAME_MINUTES = 35 -- Set the internal game minutes (0-59min)
GAME_GENDER = 1 -- Set the player gender (1-2 // boy-girl)
GAME_TEXT_SPEED = 2 -- Set the Text Speed (0-2 // slow-fast)
GAME_BATTLE_ANIMATION = 1 -- Set the battle animation (0-1 // no-yes)
GAME_BATTLE_STYLE = 1 -- Set the battle style (0-1 // shift-set)
GAME_SOUND_STYLE = 1 -- Set the sound style (0-1 // stereo-mono)
GAME_BUTTON_STYLE = 0 -- Set the button style (0-2)
GAME_WINDOWS_STYLE = 4 -- Set the windows style (0-19)
--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 = "TeStInG" -- Player name
RIVAL_NAME = "URRival" -- Rival name
MUDKIP_NAME = "Muddy" -- Set Mudkip name
--NAMES SETTINGS TIPS :
-- - Can use up to 7 letter ingame
-- - Upper, Lower case and Number allowed
-- - Specials Characters :
-- < = male symbol
-- > = female symbol
-- { or } = "
-- [ or ] = '
--#####################################################################################
--#####################################################################################
--########### ###############
--########### PLEASE DON'T EDIT ANYTHING BELLOW, IT'S AT YOUR RISK ###############
--########### START CODE (hard hats on) ###############
--########### ###############
--#####################################################################################
--#####################################################################################
-- SET VALUES
local VERSION = "0.1-BETA"
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
-- LOAD DIR
local LowerGameName = string.lower(GAME_NAME)
local Battle = require "action.battle"
local Textbox = require "action.textbox"
local Walk = require "action.walk"
local Combat = require "ai.combat"
local Control = require "ai.control"
local Strategies = require("ai."..LowerGameName..".strategies")
local Bridge = require "util.bridge"
local Input = require "util.input"
local Memory = require "util.memory"
local Menu = require "util.menu"
local Paint = require "util.paint"
local Utils = require "util.utils"
local Settings = require "util.settings"
local Pokemon = require "storage.pokemon"
-- GLOBAL
function p(...) --print
local string
if #arg == 0 then
string = arg[0]
else
string = ""
for i,str in ipairs(arg) do
if str == true then
string = string.."\n"
else
string = string..str.." "
end
end
end
print(string)
end
-- RESET
local function resetAll()
Strategies.softReset()
Combat.reset()
Control.reset()
Walk.reset()
Paint.reset()
Bridge.reset()
oldSeconds = 0
running = false
Utils.reset()
-- client.speedmode = 200
if CUSTOM_SEED then
Strategies.seed = CUSTOM_SEED
p("RUNNING WITH A FIXED SEED ("..Strategies.seed.."), every run will play out identically!", true)
else
Strategies.seed = os.time()
p("Starting a new run with seed "..Strategies.seed, true)
end
math.randomseed(Strategies.seed)
end
-- EXECUTE
local OWNER = "Bouletmarc"
p("Welcome to PokeBot Version "..VERSION, true)
p("Actually running Pokemon "..GAME_NAME.." Speedruns by "..OWNER, true)
Control.init()
--STREAMING_MODE = not walk.init()
if INTERNAL and STREAMING_MODE then
RESET_FOR_TIME = true
end
if CUSTOM_SEED then
client.reboot_core()
else
hasAlreadyStartedPlaying = Utils.ingame()
end
Strategies.init(hasAlreadyStartedPlaying)
if RESET_FOR_TIME and hasAlreadyStartedPlaying then
RESET_FOR_TIME = false
p("Disabling time-limit resets as the game is already running. Please reset the emulator and restart the script if you'd like to go for a fast time.", true)
end
if STREAMING_MODE then
Bridge.init()
else
Input.setDebug(true)
end
-- MAIN LOOP
local previousMap
while true do
local currentMap = Memory.double("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() and currentMap == 0 then
if running then
if not hasAlreadyStartedPlaying then
if emu.framecount() == 1 then client.reboot_core() end
hasAlreadyStartedPlaying = true
else
resetAll()
end
else
Settings.startNewAdventure(START_WAIT)
end
else
if not running then
Bridge.liveSplit()
running = true
end
--local battleState = Memory.value("game", "battle")
--Control.encounter(battleState)
--local curr_hp = Pokemon.index(0, "hp")
--if curr_hp == 0 and not Control.canDie() and Pokemon.index(0) > 0 then
-- Strategies.death(currentMap)
--elseif Walk.strategy then
if Walk.strategy then
if Strategies.execute(Walk.strategy) then
Walk.traverse(currentMap)
end
--elseif battleState > 0 then
-- if not Control.shouldCatch(partySize) then
-- Battle.automate()
-- end
elseif Textbox.handle() then
Walk.traverse(currentMap)
end
end
end
if 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()

273
storage/inventory.lua Normal file
View File

@ -0,0 +1,273 @@
local Inventory = {}
local Input = require "util.input"
local Memory = require "util.memory"
local Menu = require "util.menu"
local Utils = require "util.utils"
local Pokemon = require "storage.pokemon"
local ItemList = require "storage.itemlist"
--[[local items = {
pokeball = 4,
bicycle = 6,
moon_stone = 10,
antidote = 11,
burn_heal = 12,
paralyze_heal = 15,
full_restore = 16,
super_potion = 19,
potion = 20,
escape_rope = 29,
carbos = 38,
repel = 30,
rare_candy = 40,
helix_fossil = 42,
nugget = 49,
pokedoll = 51,
super_repel = 56,
fresh_water = 60,
soda_pop = 61,
coin_case = 69,
pokeflute = 73,
ether = 80,
max_ether = 81,
elixer = 82,
x_accuracy = 46,
x_speed = 67,
x_special = 68,
cut = 196,
fly = 197,
surf = 198,
strength = 199,
horn_drill = 207,
bubblebeam = 211,
water_gun = 212,
ice_beam = 213,
thunderbolt = 224,
earthquake = 226,
dig = 228,
tm34 = 234,
rock_slide = 248,
}]]
--local ITEM_BASE = Memory.value("inventory", "item_base")
-- Data
function Inventory.indexOf(name)
--local searchID = items[name]
local searchID = ItemList.items[name]
for i=0,19 do
--local iidx = ITEM_BASE + i * 2
local SubIndex = i * 2
local iidx = ITEM_BASE + SubIndex
if Memory.raw(iidx) == searchID then
return i
end
end
return -1
end
function Inventory.count(name)
local index = Inventory.indexOf(name)
if index ~= -1 then
local SubIndex = index * 2
return Memory.raw(ITEM_BASE + SubIndex + 1)
end
return 0
end
function Inventory.contains(...)
for i,name in ipairs(arg) do
if Inventory.count(name) > 0 then
return name
end
end
end
-- Actions
--[[function Inventory.teach(item, poke, replaceIdx, altPoke)
local main = Memory.value("menu", "main")
local column = Menu.getCol()
if main == 144 then
if column == 5 then
Menu.select(replaceIdx, true)
else
Input.press("A")
end
elseif main == 128 then
if column == 5 then
Menu.select(Inventory.indexOf(item), "accelerate", true)
elseif column == 11 then
Menu.select(2, true)
elseif column == 14 then
Menu.select(0, true)
end
elseif main == Menu.pokemon then
Input.press("B")
elseif main == 64 or main == 96 or main == 192 then
if column == 5 then
Menu.select(replaceIdx, true)
elseif column == 14 then
Input.press("A")
elseif column == 15 then
Menu.select(0, true)
else
local idx = 0
if poke then
idx = Pokemon.indexOf(poke, altPoke)
end
Menu.select(idx, true)
end
else
return false
end
return true
end]]
function Inventory.isFull()
return Memory.value("inventory", "item_count") == 20
end
function Inventory.use(item, poke, midfight, BagMenu)
if midfight then
local battleMenu = Memory.value("battle", "menu")
--if battleMenu == 94 then
--open bag menu
if battleMenu == 186 then
local rowSelected = Memory.value("battle", "menuY")
local ColumnSelected = Memory.value("battle", "menuX")
if ColumnSelected == 1 then
if rowSelected == 1 then
Input.press("Down")
else
--select bag
Input.press("A")
end
else
Input.press("Left")
end
--elseif battleMenu == 233 then
--inside bag menu
elseif battleMenu == 128 then
--if its not done
if not give_done then
if column ~= BagMenu then
--select proper bag menu
Menu.setCol(BagMenu)
else
if Memory.value("menu", "shop_current") ~= 70 then
--select the item
Menu.select(Inventory.indexOf(item)+1, "accelerate", "input")
else
--accept the use
Menu.select(1, true, "input")
end
end
--if its done
else
Menu.close()
end
elseif Utils.onPokemonSelect(battleMenu) then
if poke then
--if type(poke) == "string" then
-- poke = Pokemon.indexOf(poke)
--end
Menu.select(poke, true, "input")
else
Input.press("A")
end
else
Input.press("B")
end
return
end
local main = Memory.value("menu", "main")
local column = Menu.getCol()
local give_done = false
--select item menu
if main == 121 then
Menu.select(3, true)
--inside bag menu
elseif main == 50 then
--if its not done
if not give_done then
if column ~= BagMenu then
--select proper bag menu
Menu.setCol(BagMenu)
else
if Memory.value("menu", "shop_current") ~= 66 then
--select the item
Menu.select(Inventory.indexOf(item)+1, "accelerate", "input")
else
--accept the use
Menu.select(1, true, "input")
end
end
--if its done
else
Menu.close()
end
--inside pokemon menu
elseif main == 127 then
local idx = 1
if poke then
idx = poke
end
if Memory.value("menu", "input_row") ~= idx then
Menu.select(idx, true, "input")
else
Input.press("A", 1)
give_done = true
end
else
return false
end
--####################################
--[[if main == 144 then
if Memory.value("battle", "menu") == 95 then
Input.press("B")
else
local idx = 0
if poke then
idx = Pokemon.indexOf(poke)
end
Menu.select(idx, true)
end
elseif main == 128 or main == 60 then
if column == 5 then
Menu.select(Inventory.indexOf(item), "accelerate", true)
elseif column == 11 then
Menu.select(2, true)
elseif column == 14 then
Menu.select(0, true)
else
local index = 0
if poke then
index = Pokemon.indexOf(poke)
end
Menu.select(index, true)
end
elseif main == 228 then
if column == 14 and Memory.value("battle", "menu") == 95 then
Input.press("B")
end
elseif main == Menu.pokemon then
Input.press("B")
else
return false
end]]
return true
end
return Inventory

62
storage/itemlist.lua Normal file
View File

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

325
storage/pokemon.lua Normal file
View File

@ -0,0 +1,325 @@
local Pokemon = {}
local Bridge = require "util.bridge"
local Input = require "util.input"
local Memory = require "util.memory"
local Menu = require "util.menu"
local pokeIDs = {
pidgey = 16,
spearow = 21,
rattata = 19,
nidoranF = 29,
nidoranM = 32,
chikorita = 152,
bayleef = 153,
meganium = 154,
cyndaquil = 155,
quilava = 156,
typhlosion = 157,
totodile = 158,
croconaw = 159,
feraligatr = 160,
sentret = 161,
furret = 162,
hoothoot = 163,
marill = 183,
azumarill = 184,
sudowoodo = 185,
politoed = 186,
hoppip = 187,
}
local moveList = {
cut = 15,
fly = 19,
surf = 57,
strength = 70,
teleport = 100,
watefall = 127,
whirlpool = 250,
sand_attack = 28,
horn_attack = 30,
horn_drill = 32,
tackle = 33,
thrash = 37,
tail_whip = 39,
poison_sting = 40,
leer = 43,
growl = 45,
water_gun = 55,
ice_beam = 58,
bubblebeam = 61,
leech_seed = 73,
thunderbolt = 85,
earthquake = 89,
dig = 91,
rock_slide = 157,
}
--[[local data = {
hp = {1, true},
status = {4},
moves = {8},
pp = {28},
level = {33},
max_hp = {34, true},
attack = {36, true},
defense = {38, true},
speed = {40, true},
special = {42, true},
}]]
local previousPartySize
--[[local function getAddress(index)
return 0x116B + index * 0x2C
end]]
--local function index(index, offset)
--[[local function index(index)
local double
--if not offset then
-- offset = 0
--else
-- local dataTable = data[offset]
-- offset = dataTable[1]
-- double = dataTable[2]
--end
local address = getAddress(index) + offset
local address = getAddress(index)
local value = Memory.raw(address)
if double then
value = value + Memory.raw(address + 1)
end
return value
end
Pokemon.index = index]]
--[[local function indexOf(...)
for ni,name in ipairs(arg) do
local pid = pokeIDs[name]
for i=0,5 do
local atIdx = index(i)
if atIdx == pid then
return i
end
end
end
return -1
end
Pokemon.indexOf = indexOf]]
-- Table functions
function Pokemon.battleMove(name)
local mid = moveList[name]
for i=0,3 do
--if mid == Memory.raw(0x101B + i) then
if mid == Memory.raw(0x062E + i) then
return i
end
end
end
--[[function Pokemon.moveIndex(move, pokemon)
local pokemonIdx
if pokemon then
pokemonIdx = indexOf(pokemon)
else
pokemonIdx = 0
end
local address = getAddress(pokemonIdx) + 7
local mid = moveList[move]
for i=1,4 do
if mid == Memory.raw(address + i) then
return i
end
end
end]]
--function Pokemon.info(name, offset)
--[[function Pokemon.info(name)
--return index(indexOf(name), offset)
return index(indexOf(name))
end]]
function Pokemon.getID(name)
return pokeIDs[name]
end
function Pokemon.getName(id)
for name,pid in pairs(pokeIDs) do
if pid == id then
return name
end
end
end
--[[function Pokemon.getSacrifice(...)
for i,name in ipairs(arg) do
local pokemonIndex = indexOf(name)
if pokemonIndex ~= -1 and index(pokemonIndex, "hp") > 0 then
return name
end
end
end]]
--[[function Pokemon.inParty(...)
for i,name in ipairs(arg) do
if indexOf(name) ~= -1 then
return name
end
end
end]]
--[[function Pokemon.forMove(move)
local moveID = moveList[move]
for i=0,5 do
local address = getAddress(i)
for j=8,11 do
if Memory.raw(address + j) == moveID then
return i
end
end
end
return -1
end]]
--[[function Pokemon.hasMove(move)
return Pokemon.forMove(move) ~= -1
end]]
function Pokemon.updateParty()
--local partySize = Memory.value("player", "party_size")
--if partySize ~= previousPartySize then
--local poke = Pokemon.inParty("tododile", "paras", "spearow", "pidgey", "nidoran", "squirtle")
--local poke = Pokemon.inParty("tododile")
--if poke then
-- Bridge.caught(poke)
-- previousPartySize = partySize
--end
--end
end
--[[function Pokemon.pp(index, move)
local midx = Pokemon.battleMove(move)
return Memory.raw(getAddress(index) + 28 + midx)
end]]
-- General
function Pokemon.isOpponent(...)
local oid = Memory.value("battle", "opponent_id")
for i,name in ipairs(arg) do
if oid == pokeIDs[name] then
return name
end
end
end
function Pokemon.isDeployed(...)
local deployedID = Memory.value("battle", "our_id")
for i,name in ipairs(arg) do
if deployedID == pokeIDs[name] then
return name
end
end
end
function Pokemon.isEvolving()
return false
--return Memory.value("menu", "pokemon") == 144
end
--[[function Pokemon.getExp()
return Memory.raw(0x117A) * 256 + Memory.raw(0x117B)
end]]
--[[function Pokemon.inRedBar()
local curr_hp, max_hp = index(0, "hp"), index(0, "max_hp")
return curr_hp / max_hp <= 0.2
end]]
function Pokemon.use(move)
--local main = Memory.value("menu", "main")
local battlemenu = Memory.value("battle", "menu")
local pokeName = Pokemon.forMove(move)
local column = Memory.value("battle", "menuX")
local row = Memory.value("battle", "menuY")
if battlemenu == 186 then
if column == 2 then
Input.press("Left", 1)
else
if row == 2 then
Input.press("Up", 1)
else
--select move menu
Input.press("A", 1)
end
end
elseif battlemenu == 106 then
local midx = 1
if move then
midx = move
end
Menu.select(midx, true, "input")
else
return false
end
--[[if main == 141 then
Input.press("A")
elseif main == 128 then
local column = Menu.getCol()
if column == 11 then
Menu.select(1, true)
elseif column == 10 or column == 12 then
local midx = 0
local menuSize = Memory.value("menu", "size")
if menuSize == 4 then
if move == "dig" then
midx = 1
elseif move == "surf" then
if Pokemon.inParty("paras") then
midx = 1
end
end
elseif menuSize == 5 then
if move == "dig" then
midx = 2
elseif move == "surf" then
midx = 1
end
end
Menu.select(midx, true)
else
Input.press("B")
end
elseif main == Menu.pokemon then
Menu.select(pokeName, true)
elseif main == 228 then
Input.press("B")
else
return false
end]]
return true
end
--[[function Pokemon.getDVs(name)
local index = Pokemon.indexOf(name)
local baseAddress = getAddress(index)
local attackDefense = Memory.raw(baseAddress + 0x1B)
local speedSpecial = Memory.raw(baseAddress + 0x1C)
return bit.rshift(attackDefense, 4), bit.band(attackDefense, 15), bit.rshift(speedSpecial, 4), bit.band(speedSpecial, 15)
end]]
return Pokemon

148
util/bridge.lua Normal file
View File

@ -0,0 +1,148 @@
local Bridge = {}
local socket
if INTERNAL then
socket = require("socket")
end
local utils = require("util.utils")
local client = nil
local timeStopped = true
local function send(prefix, body)
if client then
local message = prefix
if body then
message = message..","..body
end
client:send(message.."\n")
return true
end
end
local function readln()
if client then
local s, status, partial = client:receive("*l")
if status == "closed" then
client = nil
return nil
end
if s and s ~= "" then
return s
end
end
end
-- Wrapper functions
function Bridge.init()
if socket then
-- io.popen("java -jar Main.jar")
client = socket.connect("127.0.0.1", 13378)
if client then
client:settimeout(0.005)
client:setoption("keepalive", true)
print("Connected to Java!");
return true
else
print("Error connecting to Java!");
end
end
end
function Bridge.tweet(message)
if INTERNAL and STREAMING_MODE then
print("tweet::"..message)
return send("tweet", message)
end
end
function Bridge.pollForName()
Bridge.polling = true
send("poll_name")
end
function Bridge.chat(message, extra, newLine)
if extra then
p(message.." || "..extra, newLine)
else
p(message, newLine)
end
return send("msg", "/me "..message)
end
function Bridge.time(message)
if not timeStopped then
return send("time", message)
end
end
function Bridge.stats(message)
return send("stats", message)
end
function Bridge.command(command)
return send("livesplit_command", command);
end
function Bridge.comparisonTime()
return send("livesplit_getcomparisontime");
end
function Bridge.process()
local response = readln()
if response then
-- print(">"..response)
if response:find("name:") then
return response:gsub("name:", "")
else
end
end
end
function Bridge.input(key)
send("input", key)
end
function Bridge.caught(name)
if name then
send("caught", name)
end
end
function Bridge.hp(curr, max)
send("hp", curr..","..max)
end
function Bridge.liveSplit()
send("start")
timeStopped = false
end
function Bridge.split(finished)
if finished then
timeStopped = true
end
send("split")
end
function Bridge.encounter()
send("encounter")
end
function Bridge.reset()
send("reset")
timeStopped = false
end
function Bridge.close()
if client then
client:close()
client = nil
end
print("Bridge closed")
end
return Bridge

162
util/input.lua Normal file
View File

@ -0,0 +1,162 @@
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 drawText = Utils.drawText
local Waiting = false
local function bridgeButton(btn)
if btn ~= lastSend then
lastSend = btn
Bridge.input(btn)
end
end
--local function sendButton(button, ab, hold, newgame)
--local function sendButton(button, ab, hold)
local function sendButton(button, ab, slow)
local inputTable = {}
if slow ~= nil then
if slow == false then
inputTable = {[button]=true, B=true}
else
inputTable = {[button]=true}
end
else
inputTable = {[button]=true}
end
--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 slow ~= nil then
if slow == true then
drawText(0, 60, button.."+B "..remainingFrames)
else
drawText(0, 60, button.." "..remainingFrames)
end
else
drawText(0, 60, button.." "..remainingFrames)
end
end
if ab then
button = "A,B"
end
bridgeButton(button)
setForFrame = button
end
function Input.isWaiting()
if setForFrame and not Waiting then
Waiting = true
elseif not setForFrame and Waiting then
Waiting = false
end
return Waiting
end
--function Input.press(button, frames, hold, newgame)
--function Input.press(button, frames, hold)
function Input.press(button, frames, slow)
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)
sendButton(button, false, slow)
--sendButton(button, false, hold, newgame)
end
--function Input.cancel(accept)
function Input.cancel()
--if accept and Memory.value("menu", "shop_current") == 20 then
--if accept and Memory.value("menu", "shop_current") == 30 then
-- Input.press(accept)
--else
local button
if bCancel then
button = "B"
else
button = "A"
end
sendButton(button, true)
--sendButton(button, false)
bCancel = not bCancel
--end
end
function Input.escape()
local rowSelected = Memory.value("battle", "menuY")
local columnSelected = Memory.value("battle", "menuX")
if not Input.isWaiting() then
if rowSelected == 1 then
Input.press("Down", 2)
else
if columnSelected == 1 then
Input.press("Right", 2)
else
Input.press("A", 2)
end
end
end
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
return Input

198
util/memory.lua Normal file
View File

@ -0,0 +1,198 @@
local Memory = {}
local memoryNames = {
setting = {
text_speed = 0x5E0A, --139-136-128
battle_animation = 0x5E0C, --141=on 133=off
battle_style = 0x5E0E, --135=shift 132=set
sound_style = 0x5E10, --142=mono 145=stereo
button_style = 0x5E12, --
windows_style = 0x5E14, --0 to 7
},
text_inputing = {
column = 0x2065E,
row = 0x20660,
mode = 0x206A4,
},
--[[inventory = {
item_count = 0x1892,
item_base = 0x1893,
},]]
menu = {
row = 0x5E0A,
input_row = 0x5E08,
settings_row = 0x5E14,
start_menu_row = 0x3CD92,
hours_row = 0x5E0C, --(0-23)
minutes_row = 0x5E0E, --(0-59)
--item_row = 0x110C,
--item_row_size = 0x110D,
column = 0x3CE5D,
current = 0x0820, --
--size = 0x0FA3,
main_current = 0x5E00,
option_current = 0x0859,
settings_current = 0x5E01,
--shop_current = 0x0F87,
--selection = 0x0F78,
text_input = 0x20667, --1=inputing
text_length = 0x217FA, -- -7
main = 0x0819,
--pokemon = 0x0C51, --TO DO, USED WHILE EVOLVING
--selection_mode = 0x0C35, --TO DO, USED WHEN SWAPING MOVE
--transaction_current = 0x0F8B,--TODO, USED FOR SHOPPING
},
player = {
--name = 0x147D,
--name2 = 0x1493,
moving = 0x37593, --1 = moving
facing = 0x37368, --17=S // 34=N // 51=W // 68=E
--repel = 0x1CA1,
--party_size = 0x1CD7,
},
game = {
--battle = 0x122D, --1=wild 2=trainer
ingame = 0x0E08,
textbox = 0x0E40,
--textbox = 0x5DF0,
--textboxing = 0x5ECC,
},
time = {
--hours = 0x24A87,
--minutes = 0x24A88,
--seconds = 0x24A89,
--frames = 0x24A8A,
frames = 0x249C0,
},
--[[shop = {
--transaction_amount = 0x110C,
},]]
battle = {
text = 0x24068, --
menu = 0x5D60, --106=106(att) // 186=94(main) // 128=233(item) // 145=224(pkmon)
--menuX = 0x0FAA, --used for battle menu Row-X
--menuY = 0x0FA9, --used for battle menu Row-Y
--battle_turns = 0x06DD, --USED FOR DSUM ESCAPE??
opponent_id = 0x240DC, --or 0x1204??
opponent_level = 0x24106,
opponent_type1 = 0x240FD,
opponent_type2 = 0x240FE,
--opponent_move_id = 0x240E8, --used to get opponent moves ID's
--opponent_move_pp = 0x24100, --used to get opponent moves PP's
our_id = 0x24084,
--our_status = 0x063A,
our_level = 0x240AE,
our_type1 = 0x240A5,
our_type2 = 0x240A6,
--our_move_id = 0x24090, --used to get our moves ID's
--our_move_pp = 0x240A8, --used to get our moves PP's
--our_pokemon_list = 0x1288 --used to retract any of our Pokemons values (slot 1-6)
--attack_turns = 0x06DC, --NOT USED??
--accuracy = 0x0D1E, --NOT DONE YET
--x_accuracy = 0x1063, --NOT DONE YET
--disabled = 0x0CEE, --NOT DONE YET
--paralyzed = 0x1018, --NOT DONE YET
--critical = 0x105E, --NOT DONE YET
--miss = 0x105F, --NOT DONE YET
--our_turn = 0x1FF1, --NOT DONE YET
--opponent_next_move = 0xC6E4, --NOT USED??
--opponent_last_move = 0x0FCC, --NOT DONE YET AND NOT USED??
--opponent_bide = 0x106F, --NOT DONE YET AND NOT USED??
},
--[[pokemon = {
exp1 = 0x1179,
exp2 = 0x117A, --NOT DONE YET
exp3 = 0x117B,
},]]
}
local doubleNames = {
battle = {
opponent_hp = 0x24104,
opponent_max_hp = 0x24108,
opponent_attack = 0x240DE,
opponent_defense = 0x240E0,
opponent_speed = 0x240E2,
opponent_special_attack = 0x240E4,
opponent_special_defense = 0x240E6,
our_hp = 0x240AC,
our_max_hp = 0x240B0,
our_attack = 0x24086,
our_defense = 0x24088,
our_speed = 0x2408A,
our_special_attack = 0x2408C,
our_special_defense = 0x2408E,
},
game = {
map = 0x37359,
},
player = {
x = 0x37364,
y = 0x37366,
},
--[[pokemon = {
attack = 0x117E,
defense = 0x1181, --NOT DONE YET
speed = 0x1183,
special = 0x1185,
},]]
}
local function raw(address)
if string.len(tostring(address)) == 6 then
memory.usememorydomain("EWRAM")
else
memory.usememorydomain("IWRAM")
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)
local memoryAddress = memoryNames[section]
if key then
memoryAddress = memoryAddress[key]
end
return raw(memoryAddress)
end
function Memory.getAddress(section, key)
local memoryAddress = memoryNames[section]
if key then
memoryAddress = memoryAddress[key]
end
return memoryAddress
end
return Memory

209
util/menu.lua Normal file
View File

@ -0,0 +1,209 @@
local Menu = {}
local Input = require "util.input"
local Memory = require "util.memory"
local sliding = false
-- Private functions
--local function getRow(menuType, scrolls)
local function getRow(menuType)
if menuType then
menuType = menuType.."_row"
else
menuType = "row"
end
local row = Memory.value("menu", menuType)
return row
end
--local function setRow(desired, throttle, scrolls, menuType, loop)
local function setRow(desired, throttle, menuType, loop)
local currentRow = getRow(menuType)
if throttle == "accelerate" then
if sliding then
throttle = false
else
local dist = math.abs(desired - currentRow)
if dist < 15 then
throttle = true
else
throttle = false
sliding = true
end
end
else
sliding = false
end
return Menu.balance(currentRow, desired, true, loop, throttle)
end
local function isCurrently(desired, menuType)
if menuType then
if menuType ~= "main" then
menuType = menuType.."_current"
end
else
menuType = "current"
end
return Memory.value("menu", menuType) == desired
end
Menu.isCurrently = isCurrently
-- Menu
function Menu.getCol()
return Memory.value("menu", "column")
end
function Menu.open(desired, atIndex, menuType)
if isCurrently(desired, menuType) then
return true
end
Menu.select(atIndex, false, menuType)
return false
end
--function Menu.select(option, throttle, scrolls, menuType, dontPress, loop)
function Menu.select(option, throttle, menuType, dontPress, loop)
--Reset MenuType
local menuTypeSent
if menuType == "option" then
menuTypeSent = nil
else
menuTypeSent = menuType
end
if setRow(option, throttle, menuTypeSent, loop) then
local delay = 1
if throttle or menuType == "option" then
delay = 2
end
if not dontPress then
Input.press("A", delay)
end
return true
end
end
function Menu.cancel(desired, menuType)
if not isCurrently(desired, menuType) then
return true
end
Input.press("B")
return false
end
-- Selections
function Menu.balance(current, desired, inverted, looping, throttle)
if current == desired then
sliding = false
return true
end
if not throttle then
throttle = 0
else
throttle = 1
end
local goUp
if inverted then
if desired < current then
goUp = true
else
goUp = false
end
else
goUp = false
end
if looping and math.abs(current - desired) > math.floor(looping / 2) then
goUp = not goUp
end
if goUp then
Input.press("Up", throttle)
else
Input.press("Down", throttle)
end
return false
end
function Menu.sidle(current, desired, looping, throttle)
if current == desired then
return true
end
if not throttle then
throttle = 0
else
throttle = 1
end
local goLeft = current > desired
if looping and math.abs(current - desired) > math.floor(looping / 2) then
goLeft = not goLeft
end
if goLeft then
Input.press("Left", throttle)
else
Input.press("Right", throttle)
end
return false
end
function Menu.setCol(desired, looping, throttle)
return Menu.sidle(Menu.getCol(), desired, looping, throttle)
end
-- Options
function Menu.setOption(name, desired)
local rowFor = {
text_speed = 0,
battle_animation = 1,
battle_style = 2,
sound_style = 3,
button_style = 4,
windows_style = 5,
}
if Memory.value("setting", name) == desired then
return true
end
if setRow(rowFor[name], 2, "input") then
Menu.setCol(desired, false, 2)
end
return false
end
-- Pause menu
function Menu.isOpen()
return Memory.value("game", "textbox") == 1 or Memory.value("menu", "current") == 79
end
function Menu.close()
if Memory.value("game", "textbox") == 0 and Memory.value("menu", "main") == 0 then
return true
end
Input.press("B")
end
function Menu.pause()
if Memory.value("game", "textbox") == 1 then
--if Memory.value("battle", "menu") == 95 then
if Memory.value("battle", "text") == 3 then
Input.cancel()
--[[else
local main = Memory.value("menu", "main")
if main > 2 and main ~= 64 then
return true
end
Input.press("B")]]
elseif Memory.value("battle", "text") == 11 then
return true
else
Input.press("B")
end
else
Input.press("Start", 2)
end
end
return Menu

56
util/paint.lua Normal file
View File

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

42
util/player.lua Normal file
View File

@ -0,0 +1,42 @@
local Player = {}
local Textbox = require "action.textbox"
local Input = require "util.input"
local Memory = require "util.memory"
local facingDirections = {Up=34, Right=68, Left=51, Down=17}
function Player.isFacing(direction)
return Memory.value("player", "facing") == facingDirections[direction]
end
function Player.face(direction)
if Player.isFacing(direction) then
return true
end
if Textbox.handle() then
Input.press(direction, 0)
end
end
function Player.interact(direction, opposite)
if Player.face(direction) then
if not opposite then
Input.press("A", 2)
else
Input.press("B", 2)
end
return true
end
end
function Player.isMoving()
return Memory.value("player", "moving") ~= 0
end
function Player.position()
return Memory.double("player", "x"), Memory.double("player", "y")
end
return Player

122
util/settings.lua Normal file
View File

@ -0,0 +1,122 @@
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 settings_done = false
local Setting_done = false
local desired = {}
desired.text_speed = GAME_TEXT_SPEED
desired.battle_animation = GAME_BATTLE_ANIMATION
desired.battle_style = GAME_BATTLE_STYLE
desired.sound_style = GAME_SOUND_STYLE
desired.button_style = GAME_BUTTON_STYLE
desired.windows_style = GAME_WINDOWS_STYLE
local function isEnabled(name)
return Memory.value("setting", name) == desired[name]
end
-- PUBLIC
function Settings.set(...)
--set vars
local startMenu = Memory.value("menu", "main")
local menuRow = Memory.value("menu", "row")
--set settings
if not settings_done then
for i,name in ipairs(arg) do
if not isEnabled(name) then
--open settings menu
if startMenu ~= 51 then
if menuRow ~= 1 then
Input.press("Down", 2)
else
Input.press("A", 2)
end
--set options
else
Menu.setOption(name, desired[name])
end
return false
end
end
--setting done
settings_done = true
end
--close option menu
if startMenu == 51 then
Input.press("B", 2)
end
if startMenu ~= 51 then
settings_done = false
return true
end
end
function Settings.startNewAdventure(startWait)
local startMenu = Memory.value("menu", "main")
--local MenuCurrent = Memory.value("menu", "current")
local SettingsCurrent = Memory.value("menu", "settings_current")
local Row = Memory.value("menu", "row")
local GenderRow = Memory.value("menu", "settings_row")
--press A
if startMenu == 30 then
Input.press("A", 2)
--press Start
elseif startMenu == 180 or startMenu == 20 or startMenu == 23 then
if not Setting_done and math.random(0, startWait) == 0 then
Input.press("Start")
end
--set settings
elseif startMenu == 49 or startMenu == 51 then
if not Setting_done then
if Settings.set("text_speed", "battle_animation", "battle_style", "sound_style", "button_style", "windows_style") then
Setting_done = true
end
else
if Row ~= 0 then
Input.press("Up", 2)
else
Input.press("A", 2)
end
end
--Set Gender
elseif startMenu == 19 then
if SettingsCurrent == 13 or SettingsCurrent == 14 then
if GenderRow == 1 and GAME_GENDER == 2 then
Input.press("Down", 2)
elseif GenderRow == 2 and GAME_GENDER == 1 then
Input.press("Up", 2)
else
Input.press("A", 2)
end
else
Input.press("A", 2)
end
--Set Name&start adventure
elseif startMenu == 31 then
if SettingsCurrent < 100 then
--reset setting not done
Setting_done = false
--set our name
Textbox.name(PLAYER_NAME, true)
else
--start adventure
Input.press("A", 2)
end
else
Input.press("A", 2)
end
end
return Settings

159
util/utils.lua Normal file
View File

@ -0,0 +1,159 @@
local Utils = {}
local Memory = require "util.memory"
local EMP = 1
-- GENERAL
function Utils.dist(x1, y1, x2, y2)
return math.sqrt(math.pow(x2 - x1, 2) + math.pow(y2 - y1, 2))
end
function Utils.each(table, func)
for key,val in pairs(table) do
func(key.." = "..tostring(val)..",")
end
end
function Utils.eachi(table, func)
for idx,val in ipairs(table) do
if val then
func(idx.." "..val)
else
func(idx)
end
end
end
function Utils.match(needle, haystack)
for i,val in ipairs(haystack) do
if needle == val then
return true
end
end
return false
end
function Utils.key(needle, haystack)
for key,val in pairs(haystack) do
if needle == val then
return key
end
end
return nil
end
function Utils.capitalize(string)
return string:sub(1, 1):upper()..string:sub(2)
end
-- GAME
function Utils.canPotionWith(potion, forDamage, curr_hp, max_hp)
local potion_hp
if potion == "full_restore" then
potion_hp = 9001
elseif potion == "super_potion" then
potion_hp = 50
else
potion_hp = 20
end
return math.min(curr_hp + potion_hp, max_hp) >= forDamage - 1
end
function Utils.ingame()
return Memory.value("game", "ingame") > 0
end
function Utils.onPokemonSelect(battleMenu)
--return battleMenu == 8 or battleMenu == 48 or battleMenu == 184 or battleMenu == 224
return battleMenu == 145
end
function Utils.drawText(x, y, message)
gui.text(x * EMP, y * EMP, message)
end
-- TIME
local Hours = 0
local Minutes = 0
local Seconds = 0
local Current_frame = 0
local Current_frame_changed = 0
function Utils.reset()
Hours = 0
Minutes = 0
Seconds = 0
Current_frame = 0
Current_frame_changed = 0
end
function Utils.igt()
local hours = Hours
local mins = Minutes
local secs = 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()
if not Utils.ingame() then
return "0:00:00"
else
--local secs = Memory.value("time", "seconds")
--local mins = Memory.value("time", "minutes")
--local hours = Memory.value("time", "hours")
local frames = Memory.value("time", "frames")
if Current_frame ~= frames then
Current_frame = frames
Current_frame_changed = Current_frame_changed + 1
if Current_frame_changed == 60 then
Current_frame_changed = 0
Seconds = Seconds + 1
if Seconds == 60 then
Seconds = 0
Minutes = Minutes + 1
if Minutes == 60 then
Minutes = 0
Hours = Hours + 1
end
end
end
end
return Hours..":"..clockSegment(Minutes)..":"..clockSegment(Seconds)
end
end
--[[function Utils.frames()
if Utils.ingame() then
else
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