diff --git a/data/display.lua b/data/display.lua new file mode 100644 index 0000000..a98afed --- /dev/null +++ b/data/display.lua @@ -0,0 +1,286 @@ +-- yPokemonStats Display Module +-- Handles all GUI rendering and display functions + +Display = {} + +local resolution = {x = 0, y = 0} + +Display.GRID = {COLSX = 38, ROWSY = 17} + +Display.GRID.MAX_COLX = Display.GRID.COLSX - 1 +Display.GRID.MAX_ROWY = Display.GRID.ROWSY - 1 + +function Display.setResolution(res) + resolution.x = res.x + resolution.y = res.y +end + +function Display.colToPixelX(colX) + if colX < 0 or colX > Display.GRID.MAX_COLX then + error("colX must be in range [0, " .. Display.GRID.MAX_COLX .. "]" .. " value is: " .. colX, 2) + end + return ((resolution.x-2) * colX) / Display.GRID.COLSX + 2 +end + +function Display.rowToPixelY(rowY) + if rowY < 0 or rowY > Display.GRID.MAX_ROWY then + error("rowY must be in range [0, " .. Display.GRID.MAX_ROWY .. "]" .. " value is: " .. rowY, 2) + end + return ((resolution.y-2) * rowY) / Display.GRID.ROWSY + 2 +end + +function Display.getRightAlignedColumn(text) + local textLength = string.len(text) + return Display.GRID.COLSX - textLength +end + +function Display.mainRender(pokemon, gen, version, state, lastpid, monitor, key, table) + -- UI data -- + local tmpcolor = state.selectedPkmnSide == 1 and "green" or "red" + Display.showHelp(key, state, table) + Display.statusIndicator((gen <= 2 and 17 or 0), (gen <= 2 and Display.GRID.MAX_ROWY or 0), state.pokemonSlot[1], state.mode, table, tmpcolor) + Display.frameCounter(Display.colToPixelX(0), Display.rowToPixelY(Display.GRID.MAX_ROWY), version) + + -- Pokemon data -- + if pokemon then + speciesText = pokemon["species"] .. ":" .. pokemon["speciesname"] + else + speciesText = "0:No pokemon" + end + + Display.statsDisplay(pokemon, gen, state.mode, state.pokemonSlot, lastpid, table, tmpcolor) + + -- PID (Gen 3+) + if gen >= 3 then + local pidStr = pokemon and bit.tohex(lastpid) or "00000000" + gui.text(Display.colToPixelX(15), Display.rowToPixelY(Display.GRID.MAX_ROWY), "PID: " .. pidStr) + end + gui.text((gen <= 2) and Display.colToPixelX(Display.getRightAlignedColumn(speciesText)) or Display.colToPixelX(0), (gen <= 2) and Display.rowToPixelY(0) or Display.rowToPixelY(1), speciesText, pokemon and tmpcolor or "red") + + -- HP display + local hp = (pokemon and pokemon["hp"]["current"] or 0) .. "/" .. (pokemon and pokemon["hp"]["max"] or 0) + local hpText = gen <= 2 and (hp .. ":HP") or ("HP:" .. hp) + if gen <= 2 then + gui.text(Display.colToPixelX(Display.getRightAlignedColumn(hpText)), Display.rowToPixelY(1), hpText, pokemon and tmpcolor or "red") + else + gui.text(Display.colToPixelX(10), Display.rowToPixelY(0), hpText, pokemon and tmpcolor or "red") + end + + -- Toggles if pokemon + if pokemon then + -- Shiny status + local shinyText = pokemon["shiny"] and "Shiny" or "Not shiny" + gui.text(Display.colToPixelX(Display.getRightAlignedColumn(shinyText)), Display.rowToPixelY(Display.GRID.MAX_ROWY), shinyText, pokemon["shiny"] and "green" or "red") + + -- "More" menu + if state.more then + Display.moreMenu(pokemon, gen, version, state, table) + end + end + + +end + +-- Display help menu +function Display.showHelp(key, state, table) + if not state.help then + return + end + gui.box(Display.colToPixelX(2) - 5, Display.rowToPixelY(3) - 5, Display.colToPixelX(Display.GRID.MAX_COLX) - 5, Display.rowToPixelY(12) + 5, "#ffffcc", "#ffcc33") + gui.text(Display.colToPixelX(2), Display.rowToPixelY(3), "yPokemonStats", "#ee82ee") + gui.text(Display.colToPixelX(2), Display.rowToPixelY(4), "http://github.com/yling", "#87cefa") + gui.text(Display.colToPixelX(2), Display.rowToPixelY(5), "-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-", "#ffcc33") + local helpLines = { {key.SWITCH_MODE, ": IVs, EVs, Stats and Contest stats", 5}, + {key.SWITCH_SELECTED_POKEMON, ": Player team / Enemy team", 4}, + {key.POKEMON_SLOT, ": Pokemon slot (1-6)", 3}, + {key.TOGGLE_MORE, ": Show more data", 2}, + {key.TOGGLE_HELP, ": Toggle this menu", 1}} + for i, line in ipairs(helpLines) do + gui.text(Display.colToPixelX(2), Display.rowToPixelY(6 + i), line[1] .. line[2], table["colors"][line[3]]) + end +end + +local labels = {"HP","AT","DF","SA","SD","SP"} -- Stats labels (Keep in the same order) +local contests = {"CO","BE","CU","SM","TH","FE"} -- Contest stats labels (Keep in the same order) +local gen1labels = {"HP","AT","DF","SP","SC"} +local modesorder={"iv","ev","stats","contest"} + +function Display.statsDisplay(pokemon, gen, mode, pokemonSlot, lastpid, table, color) + local statCount = gen <= 2 and 5 or 6 + local statsLabels = mode == 4 and contests or (gen <= 2 and gen1labels or labels) + + -- Get IV data based on generation, default to 0s if no pokemon + local ivData + if not pokemon then + ivData = {} + for i = 1, statCount do + ivData[i] = 0 + end + else + ivData = pokemon[modesorder[mode]] + end + + -- Display stats + if gen <= 2 then + -- Gen 1-2: Vertical display on right side with "number:statname" format + for i = 1, statCount do + local row = i - 1 + local iv = ivData[i] + local ivText = mode == 1 and iv == 15 and ( "*" .. iv) or iv + local displayText = ivText .. ":" .. statsLabels[i] + + local rightCol = Display.getRightAlignedColumn(displayText) + local xPos = Display.colToPixelX(rightCol) + local yPos = Display.rowToPixelY(row+2) + gui.text(xPos, yPos, displayText, table["colors"][i]) + end + else + -- Gen 3+: Horizontal display (looping in reverse order) + for i = statCount, 1, -1 do + local colX = Display.GRID.COLSX - ((statCount + 1 - i) * 3) + + -- Label at top + gui.text(Display.colToPixelX(colX), Display.rowToPixelY(0), statsLabels[i], table["colors"][i]) + + -- IV value one row below + local iv = ivData[i] + local ivText = mode == 1 and iv == 31 and (iv .. "*") or iv + gui.text(Display.colToPixelX(colX), Display.rowToPixelY(1), ivText, table["colors"][i]) + + -- Nature indicator (not for mode 4/contests) + if pokemon and mode ~= 4 then + local inc, dec = pokemon["nature"]["inc"], pokemon["nature"]["dec"] + local xPos = Display.colToPixelX(colX) + local yPos = Display.rowToPixelY(1) + 2 + + if inc ~= dec then -- If nature changes stats + if i == table["statsorder"][inc + 2] then -- If the nature increases current stat + gui.text(xPos, yPos, "__", "green") -- Display a green underline + elseif i == table["statsorder"][dec + 2] then -- If the nature decreases current stat + gui.text(xPos, yPos, "__", "red") -- Display a red underline + end + else -- If neutral nature + if i == table["statsorder"][inc + 2] then + gui.text(xPos, yPos, "__", "grey") -- If the nature is neutral on current stat + end + end + end + end + end +end + +-- Display status indicator (P1, P2, etc.) +function Display.statusIndicator(colX, rowY, pokemonSlot, mode, table, color) + statModes = {"IVs", "EVs", "Stats", "Cont."} + local statusText = (color == "green" and "P" or "E") .. pokemonSlot .. " (" .. statModes[mode] .. ")" + gui.text(Display.colToPixelX(colX), Display.rowToPixelY(rowY), statusText, color) +end + +-- Display frame counter +function Display.frameCounter(x, y, version) + local frame + if version == "POKEMON EMER" then + frame = "F. E/R: " .. emu.framecount() .. "/" .. memory.readdwordunsigned(0x020249C0) + else + frame = "F. E: " .. emu.framecount() + end + gui.text(x, y, frame) +end + +-- Display "More" menu with additional Pokemon details +function Display.moreMenu(pokemon, gen, version, state, table) + gui.box(Display.colToPixelX(2) - 5, Display.rowToPixelY(3) - 5, Display.colToPixelX(Display.GRID.MAX_COLX) - 5, Display.rowToPixelY(13) + 5, "#ffffcc", "#ffcc33") + + if gen >= 3 then + Display.moreMenuGen3Plus(2, 3, pokemon, gen, table) + else + Display.moreMenuGen12(2, 3, pokemon, gen, version, state, table) + end + + -- Hidden Power and Moves (all gens) + Display.hiddenPower(2, 4, pokemon, table) + Display.movesList(2, 6, pokemon, table) +end + +-- More menu details for Gen 3-5 +function Display.moreMenuGen3Plus(colX, rowY, pokemon, gen, table) + local helditem = pokemon["helditem"] == 0 and "none" or table["items"][gen][pokemon["helditem"]] + local naturen = pokemon["nature"]["nature"] > 16 and pokemon["nature"]["nature"] - 16 or pokemon["nature"]["nature"] + local natureColor = table["typecolor"][naturen] + + gui.text(Display.colToPixelX(colX), Display.rowToPixelY(rowY), "Nature") + gui.text(Display.colToPixelX(colX + 8), Display.rowToPixelY(rowY), table["nature"][pokemon["nature"]["nature"] + 1], natureColor) + + local ability = gen == 3 and table["gen3ability"][pokemon["species"]][pokemon["ability"] + 1] or pokemon["ability"] + local pokerusStrainDays = pokemon["pokerusStrainDays"] + local pokerusDays = pokemon["pokerusDays"] + + local pokerus = + (pokerusStrainDays ~= 0 and pokerusDays ~= 0) and (pokerusDays .. "/" .. pokerusStrainDays .. " days") + or (pokerusStrainDays ~= 0 and pokerusDays == 0) and ("Cured " .. pokerusStrainDays) + or "Never" + + local details = { + {"OT ID : " .. pokemon["OTTID"]}, + {"OT SID : " .. pokemon["OTSID"]}, + {"XP : " .. pokemon["xp"]}, + {"Item : " .. helditem}, + {"Pokerus : " .. pokerus}, + {"Friendship : " .. pokemon["friendship"]} + } + + for i, detail in ipairs(details) do + gui.text(Display.colToPixelX(colX + 20), Display.rowToPixelY(rowY + i - 1), detail[1]) + end + gui.text(Display.colToPixelX(2), Display.rowToPixelY(12), "Ability : " .. table["ability"][ability]) +end + +-- More menu details for Gen 1-2 +function Display.moreMenuGen12(colX, rowY, pokemon, gen, version, state, table) + if gen == 2 then + local helditem = pokemon["helditem"] == 0 and "none" or table["items"][gen][pokemon["helditem"]] + gui.text(Display.colToPixelX(colX), Display.rowToPixelY(rowY), "TID: " .. pokemon["TID"] .. " / Item: " .. helditem) + else + gui.text(Display.colToPixelX(colX), Display.rowToPixelY(rowY), "TID: " .. pokemon["TID"]) + end + + if gen == 2 or (version == "POKEMON YELL" and state.pokemonSlot == 1 and pokemon["species"] == 25) then + gui.text(Display.colToPixelX(colX), Display.rowToPixelY(rowY + 2), "Friendship : " .. pokemon["friendship"]) + end +end + +-- Display Hidden Power +function Display.hiddenPower(colX, rowY, pokemon, table) + gui.text(Display.colToPixelX(colX), Display.rowToPixelY(rowY), "H.Power") + gui.text(Display.colToPixelX(colX + 8), Display.rowToPixelY(rowY), + table["type"][pokemon["hiddenpower"]["type"] + 1] .. " " .. pokemon["hiddenpower"]["base"], + table["typecolor"][pokemon["hiddenpower"]["type"] + 1]) +end + +-- Display moves list +function Display.movesList(colX, rowY, pokemon, table) + gui.text(Display.colToPixelX(colX), Display.rowToPixelY(rowY), "Moves:") + for i = 1, 4 do + if table["move"][pokemon["move"][i]] ~= nil then + gui.text(Display.colToPixelX(colX), Display.rowToPixelY(rowY + i), + table["move"][pokemon["move"][i]] .. " - " .. pokemon["pp"][i] .. "PP") + end + end +end + +local function numTruncate(x,n) -- Truncate to n decimals + local o=math.pow(10,n) + local y=math.floor(x*o) + return y/o +end + +-- Display performance stats (secret feature) +function Display.performanceStats(monitor) + if not monitor.yling then + return + end + gui.text(Display.colToPixelX(2), Display.rowToPixelY(6), "Last clock time: " .. numTruncate(monitor.lastclocktime * 1000, 2) .. "ms") + gui.text(Display.colToPixelX(2), Display.rowToPixelY(7), "Mean clock time: " .. numTruncate(monitor.meanclocktime * 1000, 2) .. "ms") + gui.text(Display.colToPixelX(2), Display.rowToPixelY(8), "Most clock time: " .. numTruncate(monitor.highestclocktime * 1000, 2) .. "ms") + gui.text(Display.colToPixelX(2), Display.rowToPixelY(9), "Data fetched: " .. monitor.count .. "x") +end \ No newline at end of file diff --git a/data/gamesdata.lua b/data/gamesdata.lua index 96dfc4c..4c28675 100644 --- a/data/gamesdata.lua +++ b/data/gamesdata.lua @@ -42,89 +42,91 @@ games["lan"][2]["E"]={0xAE0D,0xD218,0x2D68} -- US gen 2 games["lan"][2]["F"]={0xC66F,0xE2F2,0x5073,0x97DC,0x8249,0x6ECD,0xF442,0x5393,0x4B06,0x8CFB,0xBADB,0x0CCE} -- PAL gen 2 -- Gen 1 -games["POKEMON RED"]["F"]={"Pokemon Red (PAL)",0xD170,0xCFEA,44} -- OK -games["POKEMON RED"]["E"]={"Pokemon Red (US)",0xD16B,0xCFE5,44} -- OK -games["POKEMON RED"]["J"]={"Pokemon Red (JAP)",0xD12B,0xCFCC,44} -- OK +games["POKEMON RED"]["F"]={"Pokemon Red (PAL)",{ptr=0xD170},{ptr=0xCFEA},44} +games["POKEMON RED"]["E"]={"Pokemon Red (US)",{ptr=0xD16B},{ptr=0xCFE5},44} +games["POKEMON RED"]["J"]={"Pokemon Red (JAP)",{ptr=0xD12B},{ptr=0xCFCC},44} -games["POKEMON BLUE"]["F"]={"Pokemon Blue (PAL)",0xD170,0xCFEA,44} -- OK -games["POKEMON BLUE"]["E"]={"Pokemon Blue (US)",0xD16B,0xCFE5,44} -- OK -games["POKEMON BLUE"]["J"]={"Pokemon Blue (JAP)",0xD12B,0xCFCC,44} -- OK +games["POKEMON BLUE"]["F"]={"Pokemon Blue (PAL)",{ptr=0xD170},{ptr=0xCFEA},44} +games["POKEMON BLUE"]["E"]={"Pokemon Blue (US)",{ptr=0xD16B},{ptr=0xCFE5},44} +games["POKEMON BLUE"]["J"]={"Pokemon Blue (JAP)",{ptr=0xD12B},{ptr=0xCFCC},44} -games["POKEMON YELL"]["F"]={"Pokemon Yellow (PAL)",0xD16F,0xCFE9,44} -- OK -games["POKEMON YELL"]["E"]={"Pokemon Yellow (US)",0xD16A,0xCFE4,44} -- OK -games["POKEMON YELL"]["J"]={"Pokemon Yellow (JAP)",0xD12B,0xCFCC,44} -- OK +games["POKEMON YELL"]["F"]={"Pokemon Yellow (PAL)",{ptr=0xD16F},{ptr=0xCFE9},44} +games["POKEMON YELL"]["E"]={"Pokemon Yellow (US)",{ptr=0xD16A},{ptr=0xCFE4},44} +games["POKEMON YELL"]["J"]={"Pokemon Yellow (JAP)",{ptr=0xD12B},{ptr=0xCFCC},44} -games["POKEMON GREE"]["J"]={"Pokemon Green (JAP)",0xD12B,0xCFCC,44} -- OK +games["POKEMON GREE"]["J"]={"Pokemon Green (JAP)",{ptr=0xD12B},{ptr=0xCFCC},44} -- Gen 2 -games["POKEMON_GLDA"]["F"]={"Pokemon Gold (PAL)", 0xDA2A,0xD0EF,48} -- OK -games["POKEMON_GLDA"]["E"]={"Pokemon Gold (US)", 0xDA2A,0xD0EF,48} -- OK -games["POKEMON_GLDA"]["J"]={"Pokemon Gold (JAP)",0xD9F0,0xD0E1,48} -- OK +games["POKEMON_GLDA"]["F"]={"Pokemon Gold (PAL)",{ptr=0xDA2A},{ptr=0xD0EF},48} +games["POKEMON_GLDA"]["E"]={"Pokemon Gold (US)",{ptr=0xDA2A},{ptr=0xD0EF},48} +games["POKEMON_GLDA"]["J"]={"Pokemon Gold (JAP)",{ptr=0xD9F0},{ptr=0xD0E1},48} -games["POKEMON_SLVA"]["F"]={"Pokemon Silver (PAL)",0xDA2A,0xD0EF,48} -- OK -games["POKEMON_SLVA"]["E"]={"Pokemon Silver (US)",0xDA2A,0xD0EF,48} -- OK -games["POKEMON_SLVA"]["J"]={"Pokemon Silver (JAP)",0xD9F0,0xD0E1,48} -- OK +games["POKEMON_SLVA"]["F"]={"Pokemon Silver (PAL)",{ptr=0xDA2A},{ptr=0xD0EF},48} +games["POKEMON_SLVA"]["E"]={"Pokemon Silver (US)",{ptr=0xDA2A},{ptr=0xD0EF},48} +games["POKEMON_SLVA"]["J"]={"Pokemon Silver (JAP)",{ptr=0xD9F0},{ptr=0xD0E1},48} + +games["PM_CRYSTALB"]["E"]={"Pokemon Crystal 1.1 (US)",{ptr=0xDCDF},{ptr=0xD206},48} +games["PM_CRYSTALB"]["F"]={"Pokemon Crystal 1.1 (PAL)",{ptr=0xDCDF},{ptr=0xD206},48} +games["PM_CRYSTALB"]["J"]={"Pokemon Crystal 1.1 (JAP)",{ptr=0xDCA5},{ptr=0xD237},48} -games["PM_CRYSTALB"]["E"]={"Pokemon Crystal 1.1 (US)",0xDCDF,0xD206,48} -- OK -games["PM_CRYSTALB"]["F"]={"Pokemon Crystal 1.1 (PAL)",0xDCDF,0xD206,48} -- OK -games["PM_CRYSTALB"]["J"]={"Pokemon Crystal 1.1 (JAP)",0xDCA5,0xD237,48} -- OK -- Gen 3 -games["POKEMON RUBY"]["E"]={"Pokemon Ruby (US)",0x03004360,0x30045C0,100} -- RUBY US - OK -games["POKEMON RUBY"]["F"]={"Pokemon Ruby (PAL)",0x03004370,0x30045D0,100} -- RUBY FR - OK -games["POKEMON RUBY"]["J"]={"Pokemon Ruby (JAP)",0x3004290,0x30044F0,100} -- RUBY J -- OK +games["POKEMON RUBY"]["E"]={"Pokemon Ruby (US)",{ptr=0x03004360},{ptr=0x030045C0},100} -- RUBY US - OK +games["POKEMON RUBY"]["F"]={"Pokemon Ruby (PAL)",{ptr=0x03004370},{ptr=0x030045D0},100} -- RUBY FR - OK +games["POKEMON RUBY"]["J"]={"Pokemon Ruby (JAP)",{ptr=0x03004290},{ptr=0x030044F0},100} -- RUBY J - OK -games["POKEMON SAPP"]["E"]={"Pokemon Sapphire (US)",0x03004360,0x30045C0,100} -- SAPPHIRE US - OK -games["POKEMON SAPP"]["F"]={"Pokemon Sapphire (PAL)",0x03004370,0x30045D0,100} -- SAPPHIRE FR - OK -games["POKEMON SAPP"]["J"]={"Pokemon Sapphire (JAP)",0x3004290,0x30044F0,100} -- SAPPHIRE J -- OK +games["POKEMON SAPP"]["E"]={"Pokemon Sapphire (US)",{ptr=0x03004360},{ptr=0x030045C0},100} -- SAPPHIRE US - OK +games["POKEMON SAPP"]["F"]={"Pokemon Sapphire (PAL)",{ptr=0x03004370},{ptr=0x030045D0},100} -- SAPPHIRE FR - OK +games["POKEMON SAPP"]["J"]={"Pokemon Sapphire (JAP)",{ptr=0x03004290},{ptr=0x030044F0},100} -- SAPPHIRE J - OK -games["POKEMON EMER"]["E"]={"Pokemon Emerald (US)",0x20244EC,0x2024744,100} -- EMERALD US - OK -games["POKEMON EMER"]["F"]={"Pokemon Emerald (PAL)",0x20244EC,0x2024744,100} -- EMERALD FR - OK -games["PKMN EMER P "]["F"]={"Pokemon Emeraude+",0x20244EC,0x2024744,100} -- EMERAUDE PLUS - OK -games["POKEMON EMER"]["J"]={"Pokemon Emerald (JAP)",0x2024190,0x20242E8,100} -- EMERALD -- OK +games["POKEMON EMER"]["E"]={"Pokemon Emerald (US)",{ptr=0x020244EC},{ptr=0x02024744},100} -- EMERALD US - OK +games["POKEMON EMER"]["F"]={"Pokemon Emerald (PAL)",{ptr=0x020244EC},{ptr=0x02024744},100} -- EMERALD FR - OK +games["PKMN EMER P "]["F"]={"Pokemon Emeraude+",{ptr=0x020244EC},{ptr=0x02024744},100} -- EMERAUDE PLUS - OK +games["POKEMON EMER"]["J"]={"Pokemon Emerald (JAP)",{ptr=0x02024190},{ptr=0x020242E8},100} -- EMERALD J - OK -games["POKEMON FIRE"]["E"]={"Pokemon Fire Red (US)",0x02024284,0x0202402C,100} -- FIRE RED US - OK -games["POKEMON FIRE"]["F"]={"Pokemon Fire Red (PAL)",0x02024284,0x0202402C,100} -- FIRE RED FR - OK -games["POKEMON FIRE"]["J"]={"Pokemon Fire Red (JAP)",0x20241E4,0x2023F8C,100} -- FIRE RED JAP - OK +games["POKEMON FIRE"]["E"]={"Pokemon Fire Red (US)",{ptr=0x02024284},{ptr=0x0202402C},100} -- FIRE RED US - OK +games["POKEMON FIRE"]["F"]={"Pokemon Fire Red (PAL)",{ptr=0x02024284},{ptr=0x0202402C},100} -- FIRE RED FR - OK +games["POKEMON FIRE"]["J"]={"Pokemon Fire Red (JAP)",{ptr=0x020241E4},{ptr=0x02023F8C},100} -- FIRE RED JAP - OK -games["POKEMON LEAF"]["E"]={"Pokemon Leaf Green (US)",0x02024284,0x0202402C,100} -- LEAF GREEN US - OK -games["POKEMON LEAF"]["F"]={"Pokemon Leaf Green (PAL)",0x02024284,0x0202402C,100} -- LEAF GREEN US - OK -games["POKEMON LEAF"]["J"]={"Pokemon Leaf Green (JAP)",0x20241E4,0x2023F8C,100} -- LEAF GREEN JAP - OK +games["POKEMON LEAF"]["E"]={"Pokemon Leaf Green (US)",{ptr=0x02024284},{ptr=0x0202402C},100} -- LEAF GREEN US - OK +games["POKEMON LEAF"]["F"]={"Pokemon Leaf Green (PAL)",{ptr=0x02024284},{ptr=0x0202402C},100} -- LEAF GREEN FR - OK +games["POKEMON LEAF"]["J"]={"Pokemon Leaf Green (JAP)",{ptr=0x020241E4},{ptr=0x02023F8C},100} -- LEAF GREEN JAP - OK -- Gen 4 -games["POKEMON D"]["F"]={"Pokemon Diamond (PAL)", memory.readdword(0x0210712C) + 0xD2AC, memory.readdword(memory.readdword(0x0210712C) + 0x364C8) + 0x774, 0xEC} -- OK -games["POKEMON D"]["E"]={"Pokemon Diamond (US)", memory.readdword(0x0210712C) + 0xD2AC, memory.readdword(memory.readdword(0x0210712C) + 0x364C8) + 0x774, 0xEC} -- OK -games["POKEMON D"]["J"]={"Pokemon Diamond (JAP)", memory.readdword(0x0210712C) + 0xD2AC, memory.readdword(memory.readdword(0x0210712C) + 0x364C8) + 0x774, 0xEC} -- UNTESTED +games["POKEMON D"]["F"]={"Pokemon Diamond (PAL)",{ptr=0x0210712C,add=0xD2AC},{ptr=0x0210712C,chain={0x364C8},add=0x774},0xEC} -- OK +games["POKEMON D"]["E"]={"Pokemon Diamond (US)", {ptr=0x0210712C,add=0xD2AC},{ptr=0x0210712C,chain={0x364C8},add=0x774},0xEC} -- OK +games["POKEMON D"]["J"]={"Pokemon Diamond (JAP)",{ptr=0x0210712C,add=0xD2AC},{ptr=0x0210712C,chain={0x364C8},add=0x774},0xEC} -- UNTESTED -games["POKEMON P"]["F"]={"Pokemon Pearl (PAL)", memory.readdword(0x0210712C) + 0xD2AC, memory.readdword(memory.readdword(0x0210712C) + 0x364C8) + 0x774, 0xEC} -- OK -games["POKEMON P"]["E"]={"Pokemon Pearl (US)", memory.readdword(0x0210712C) + 0xD2AC, memory.readdword(memory.readdword(0x0210712C) + 0x364C8) + 0x774, 0xEC} -- OK -games["POKEMON P"]["J"]={"Pokemon Pearl (JAP)", memory.readdword(0x0210712C) + 0xD2AC, memory.readdword(memory.readdword(0x0210712C) + 0x364C8) + 0x774, 0xEC} -- UNTESTED +games["POKEMON P"]["F"]={"Pokemon Pearl (PAL)", {ptr=0x0210712C,add=0xD2AC},{ptr=0x0210712C,chain={0x364C8},add=0x774},0xEC} -- OK +games["POKEMON P"]["E"]={"Pokemon Pearl (US)", {ptr=0x0210712C,add=0xD2AC},{ptr=0x0210712C,chain={0x364C8},add=0x774},0xEC} -- OK +games["POKEMON P"]["J"]={"Pokemon Pearl (JAP)", {ptr=0x0210712C,add=0xD2AC},{ptr=0x0210712C,chain={0x364C8},add=0x774},0xEC} -- UNTESTED -games["POKEMON PL"]["F"]={"Pokemon Platinum (PAL)", memory.readdword(0x02101D2C) + 0xD094, memory.readdword(memory.readdword(0x02101D2C) + 0x352F4) + 0x7A0, 0xEC} -- OK -games["POKEMON PL"]["E"]={"Pokemon Platinum (US)", memory.readdword(0x02101D2C) + 0xD094, memory.readdword(memory.readdword(0x02101D2C) + 0x352F4) + 0x7A0, 0xEC} -- OK -games["POKEMON PL"]["J"]={"Pokemon Platinum (JAP)", memory.readdword(0x02101D2C) + 0xD094, memory.readdword(memory.readdword(0x02101D2C) + 0x352F4) + 0x7A0, 0xEC} -- UNTESTED +games["POKEMON PL"]["F"]={"Pokemon Platinum (PAL)",{ptr=0x02101D2C,add=0xD094},{ptr=0x02101D2C,chain={0x352F4},add=0x7A0},0xEC} -- OK +games["POKEMON PL"]["E"]={"Pokemon Platinum (US)", {ptr=0x02101D2C,add=0xD094},{ptr=0x02101D2C,chain={0x352F4},add=0x7A0},0xEC} -- OK +games["POKEMON PL"]["J"]={"Pokemon Platinum (JAP)",{ptr=0x02101D2C,add=0xD094},{ptr=0x02101D2C,chain={0x352F4},add=0x7A0},0xEC} -- UNTESTED -games["POKEMON SS"]["F"]={"Pokemon SoulSilver (PAL)", memory.readdword(0x021D110C) + 0x12, memory.readdword(0x021D110C) + 0x4EFD2, 0xEC} -- OK -games["POKEMON SS"]["E"]={"Pokemon SoulSilver (US)", memory.readdword(0x021D10EC) + 0x12, memory.readdword(0x021D10EC) + 0x4EFD2, 0xEC} -- OK -games["POKEMON SS"]["J"]={"Pokemon SoulSilver (JAP)", memory.readdword(0x021D10EC) + 0x12, memory.readdword(0x021D10EC) + 0x4EFD2, 0xEC} -- UNTESTED +games["POKEMON SS"]["F"]={"Pokemon SoulSilver (PAL)",{ptr=0x021D110C,add=0x12},{ptr=0x021D110C,add=0x4EFD2},0xEC} -- OK +games["POKEMON SS"]["E"]={"Pokemon SoulSilver (US)", {ptr=0x021D10EC,add=0x12},{ptr=0x021D10EC,add=0x4EFD2},0xEC} -- OK +games["POKEMON SS"]["J"]={"Pokemon SoulSilver (JAP)",{ptr=0x021D10EC,add=0x12},{ptr=0x021D10EC,add=0x4EFD2},0xEC} -- UNTESTED -games["POKEMON HG"]["F"]={"Pokemon HeartGold (PAL)", memory.readdword(0x021D110C) + 0x12, memory.readdword(0x021D110C) + 0x4EFD2, 0xEC} -- OK -games["POKEMON HG"]["E"]={"Pokemon HeartGold (US)", memory.readdword(0x021D10EC) + 0x12, memory.readdword(0x021D10EC) + 0x4EFD2, 0xEC} -- OK -games["POKEMON HG"]["J"]={"Pokemon HeartGold (JAP)", memory.readdword(0x021D10EC) + 0x12, memory.readdword(0x021D10EC) + 0x4EFD2, 0xEC} -- UNTESTED +games["POKEMON HG"]["F"]={"Pokemon HeartGold (PAL)",{ptr=0x021D110C,add=0x12},{ptr=0x021D110C,add=0x4EFD2},0xEC} -- OK +games["POKEMON HG"]["E"]={"Pokemon HeartGold (US)", {ptr=0x021D10EC,add=0x12},{ptr=0x021D10EC,add=0x4EFD2},0xEC} -- OK +games["POKEMON HG"]["J"]={"Pokemon HeartGold (JAP)",{ptr=0x021D10EC,add=0x12},{ptr=0x021D10EC,add=0x4EFD2},0xEC} -- UNTESTED -- Gen 5 -games["POKEMON B"]["E"]={"Pokemon Black (US)", 0x02234934, 0x0226AC74, 0xDC} -- UNTESTED -games["POKEMON B"]["J"]={"Pokemon Black (JAP)", 0x02234934, 0x0226AC74, 0xDC} -- UNTESTED -games["POKEMON B"]["F"]={"Pokemon Black (PAL)", 0x02234934, 0x0226AC74, 0xDC} -- OK - -games["POKEMON W"]["E"]={"Pokemon White (US)", 0x02234954, 0x0226AC94, 0xDC} -- UNTESTED -games["POKEMON W"]["J"]={"Pokemon White (JAP)", 0x02234954, 0x0226AC94, 0xDC} -- UNTESTED -games["POKEMON W"]["F"]={"Pokemon White (PAL)", 0x02234954, 0x0226AC94, 0xDC} -- OK - -games["POKEMON B2"]["E"]={"Pokemon Black 2 (US)", 0x0221E3EC, 0x02258834, 0xDC} -- UNTESTED -games["POKEMON B2"]["J"]={"Pokemon Black 2 (JAP)", 0x0221E3EC, 0x02258834, 0xDC} -- UNTESTED -games["POKEMON B2"]["F"]={"Pokemon Black 2 (PAL)", 0x0221E3EC, 0x02258834, 0xDC} -- OK - -games["POKEMON W2"]["E"]={"Pokemon White 2 (US)", 0x0221E42C, 0x02258874, 0xDC} -- UNTESTED -games["POKEMON W2"]["J"]={"Pokemon White 2 (JAP)", 0x0221E42C, 0x02258874, 0xDC} -- UNTESTED -games["POKEMON W2"]["F"]={"Pokemon White 2 (PAL)", 0x0221E42C, 0x02258874, 0xDC} -- OK \ No newline at end of file +games["POKEMON B"]["E"] = {"Pokemon Black (US)", {ptr=0x02234934}, {ptr=0x0226AC74}, 0xDC} -- UNTESTED +games["POKEMON B"]["J"] = {"Pokemon Black (JAP)", {ptr=0x02234934}, {ptr=0x0226AC74}, 0xDC} -- UNTESTED +games["POKEMON B"]["F"] = {"Pokemon Black (PAL)", {ptr=0x02234934}, {ptr=0x0226AC74}, 0xDC} -- OK +games["POKEMON B"]["O"] = {"Pokemon Black (USA, Europe)", {ptr=0x022349B4}, {ptr=0x0226ACF4}, 0xDC} -- OK + +games["POKEMON W"]["E"] = {"Pokemon White (US)", {ptr=0x02234954}, {ptr=0x0226AC94}, 0xDC} -- UNTESTED +games["POKEMON W"]["J"] = {"Pokemon White (JAP)", {ptr=0x02234954}, {ptr=0x0226AC94}, 0xDC} -- UNTESTED +games["POKEMON W"]["F"] = {"Pokemon White (PAL)", {ptr=0x02234954}, {ptr=0x0226AC94}, 0xDC} -- OK + +games["POKEMON B2"]["E"] = {"Pokemon Black 2 (US)", {ptr=0x0221E3EC}, {ptr=0x02258834}, 0xDC} -- UNTESTED +games["POKEMON B2"]["J"] = {"Pokemon Black 2 (JAP)", {ptr=0x0221E3EC}, {ptr=0x02258834}, 0xDC} -- UNTESTED +games["POKEMON B2"]["F"] = {"Pokemon Black 2 (PAL)", {ptr=0x0221E3EC}, {ptr=0x02258834}, 0xDC} -- OK + +games["POKEMON W2"]["E"] = {"Pokemon White 2 (US)", {ptr=0x0221E42C}, {ptr=0x02258874}, 0xDC} -- UNTESTED +games["POKEMON W2"]["J"] = {"Pokemon White 2 (JAP)", {ptr=0x0221E42C}, {ptr=0x02258874}, 0xDC} -- UNTESTED +games["POKEMON W2"]["F"] = {"Pokemon White 2 (PAL)", {ptr=0x0221E42C}, {ptr=0x02258874}, 0xDC} -- OK \ No newline at end of file diff --git a/data/gen1data.lua b/data/gen1data.lua index e4a290f..fe6579d 100644 --- a/data/gen1data.lua +++ b/data/gen1data.lua @@ -1,4 +1,3 @@ --- table["gen1id"]={"Rhydon","Kangaskhan","NidoranM","Clefairy","Spearow","Voltorb","Nidoking","Slowbro","Ivysaur","Exeggutor","Lickitung","Exeggcute","Grimer","Gengar","NidoranF","Nidoqueen","Cubone","Rhyhorn","Lapras","Arcanine","Mew","Gyarados","Shellder","Tentacool","Gastly","Scyther","Staryu","Blastoise","Pinsir","Tangela","MissingNo. (Scizor)","MissingNo. (Shuckle)","Growlithe","Onix","Fearow","Pidgey","Slowpoke","Kadabra","Graveler","Chansey","Machoke","Mr. Mime","Hitmonlee","Hitmonchan","Arbok","Parasect","Psyduck","Drowzee","Golem","MissingNo. (Heracross)","Magmar","MissingNo. (Ho-Oh)","Electabuzz","Magneton","Koffing","MissingNo. (Sneasel)","Mankey","Seel","Diglett","Tauros","MissingNo. (Teddiursa)","MissingNo. (Ursaring)","MissingNo. (Slugma)","Farfetch'd","Venonat","Dragonite","MissingNo. (Magcargo)","MissingNo. (Swinub)","MissingNo. (Piloswine)","Doduo","Poliwag","Jynx","Moltres","Articuno","Zapdos","Ditto","Meowth","Krabby","MissingNo. (Corsola)","MissingNo. (Remoraid)","MissingNo. (Octillery)","Vulpix","Ninetales","Pikachu","Raichu","MissingNo. (Delibird)","MissingNo. (Mantine)","Dratini","Dragonair","Kabuto","Kabutops","Horsea","Seadra","MissingNo. (Skarmory)","MissingNo. (Houndour)","Sandshrew","Sandslash","Omanyte","Omastar","Jigglypuff","Wigglytuff","Eevee","Flareon","Jolteon","Vaporeon","Machop","Zubat","Ekans","Paras","Poliwhirl","Poliwrath","Weedle","Kakuna","Beedrill","MissingNo. (Houndoom)","Dodrio","Primeape","Dugtrio","Venomoth","Dewgong","MissingNo. (Kingdra)","MissingNo. (Phanpy)","Caterpie","Metapod","Butterfree","Machamp","MissingNo. (Donphan)","Golduck","Hypno","Golbat","Mewtwo","Snorlax","Magikarp","MissingNo. (Porygon2)","MissingNo. (Stantler)","Muk","MissingNo. (Smeargle)","Kingler","Cloyster","MissingNo. (Tyrogue)","Electrode","Clefable","Weezing","Persian","Marowak","MissingNo. (Hitmontop)","Haunter","Abra","Alakazam","Pidgeotto","Pidgeot","Starmie","Bulbasaur","Venusaur","Tentacruel","MissingNo. (Smoochum)","Goldeen","Seaking","MissingNo. (Elekid)","MissingNo. (Magby)","MissingNo. (Miltank)","MissingNo. (Blissey)","Ponyta","Rapidash","Rattata","Raticate","Nidorino","Nidorina","Geodude","Porygon","Aerodactyl","MissingNo. (Raikou)","Magnemite","MissingNo. (Entei)","MissingNo. (Suicune)","Charmander","Squirtle","Charmeleon","Wartortle","Charizard","MissingNo. (Larvitar)","MissingNo. Kabutops Fossil (Pupitar)","MissingNo. Aerodactyl Fossil (Tyranitar)","MissingNo. Ghost (Lugia)","Oddish","Gloom","Vileplume","Bellsprout","Weepinbell","Victreebel"} table["gen1id"]={ 112,115,32,35,21,100,34,80,2,103, 108,102,88,94,29,31,104,111,131,59, 151,130,90,72,92,123,120,9,127,114, @@ -20,6 +19,4 @@ table["gen1id"]={ 112,115,32,35,21,100,34,80,2,103, 18,121,1,3,73,0,118,119,0,0, 0,0,77,78,19,20,33,30,74,137, 142,0,81,0,0,4,7,5,8,6, - 0,0,0,0,43,44,45,69,70,71} -table["gen1labels"]={"HP","AT","DF","SP","SC"} -table["items"][1]={"Master Ball","Ultra Ball","Great Ball","Poké Ball","Town Map","Bicycle","?????","Safari Ball","Pokédex","Moon Stone","Antidote","Burn Heal","Ice Heal","Awakening","Parlyz Heal","Full Restore","Max Potion","Hyper Potion","Super Potion","Potion","BoulderBadge","CascadeBadge","ThunderBadge","RainbowBadge","SoulBadge","MarshBadge","VolcanoBadge","EarthBadge","Escape Rope","Repel","Old Amber","Fire Stone","Thunderstone","Water Stone","HP Up","Protein","Iron","Carbos","Calcium","Rare Candy","Dome Fossil","Helix Fossil","Secret Key","?????","Bike Voucher","X Accuracy","Leaf Stone","Card Key","Nugget","PP Up*","Poké Doll","Full Heal","Revive","Max Revive","Guard Spec.","Super Repel","Max Repel","Dire Hit","Coin","Fresh Water","Soda Pop","Lemonade","S.S. Ticket","Gold Teeth","X Attack","X Defend","X Speed","X Special","Coin Case","Oak's Parcel","Itemfinder","Silph Scope","Poké Flute","Lift Key","Exp. All","Old Rod","Good Rod","Super Rod","PP Up","Ether","Max Ether","Elixer","Max Elixer"} \ No newline at end of file + 0,0,0,0,43,44,45,69,70,71} \ No newline at end of file diff --git a/data/memory.lua b/data/memory.lua index 94d0630..b4be79c 100644 --- a/data/memory.lua +++ b/data/memory.lua @@ -1,35 +1,55 @@ --- Functions and variables definition -function bts(bytes,l) -- Bytes array to string - local name = "" - for i=1,l do +-- yPokemonStats Memory Module +-- Handles all memory reading functions + +local mdword=memory.readdwordunsigned +local mword=memory.readwordunsigned +local mbyte=memory.readbyteunsigned +local mbyterange=memory.readbyterangeunsigned +local bnd,br,bxr=bit.band,bit.bor,bit.bxor +local rshift, lshift=bit.rshift, bit.lshift + +local function bts(bytes,l) -- Bytes array to string + local name = "" + for i=1,l do nchar = bytes[i]==0 and "" or string.char(bytes[i]) - name = name..nchar - end - return name + name = name..nchar + end + return name end -function numTruncate(x,n) -- Truncate to n decimals - local o=math.pow(10,n) - local y=math.floor(x*o) - return y/o +local function hasValue (tab, val) + for index, value in ipairs(tab) do + if value == val then + return true + end + end + + return false end -function hasValue (tab, val) - for index, value in ipairs(tab) do - if value == val then - return true - end - end +local function getbits(a,b,d) -- Get bits (kinda obvious right ?) + return rshift(a,b)%lshift(1,d) +end - return false +local function gettop(a) -- Rshift for data decryption + return(rshift(a,16)) +end + +local function mult32(a,b) -- 32 bits multiplication + local c = rshift(a,16) + local d = a % 0x10000 + local e = rshift(b,16) + local f = b % 0x10000 + local g = (c*f + d*e) % 0x10000 + return g*0x10000 + d*f end function getGameInfo() -- Reads game memory to determine which game is running - local version, lan, gen, sel + local version, lan, gen, resolution if string.find(bts(memory.readbyterange(0x0134, 12),12), "POKEMON") or string.find(bts(memory.readbyterange(0x0134, 12),12), "PM") then version = bts(memory.readbyterange(0x0134, 12),12) - lan = memory.readword(0x014E) - regions = {"J","E","F"} + lan = memory.readword(0x014E) + regions = {"J","E","F"} for i=1,2 do -- Finds the proper region and gen for j=1,3 do @@ -39,139 +59,125 @@ function getGameInfo() -- Reads game memory to determine which game is running end end end - - sel = table["consoles"][1] - elseif string.find(bts(memory.readbyterange(0x080000A0, 12),12), "POKEMON") or string.find(bts(memory.readbyterange(0x080000A0, 12),12), "PKMN") then - version = bts(memory.readbyterange(0x080000A0, 12),12) - lan = string.char(memory.readbyte(0x080000AF)) - gen = 3 - sel = table["consoles"][2] - elseif string.find(bts(memory.readbyterange(0x023FF000,12),12), "POKEMON") then - version = bts(memory.readbyterange(0x023FF000,12),12) - lan = string.char(memory.readbyte(0x023FFE0F)) - gamecode = bts(memory.readbyterange(0x023FF000+0xA8C,4),4) -- Or E0C ? - gen = 4 - sel = table["consoles"][3] - elseif string.find(bts(memory.readbyterange(0x023FFE00,12),12), "POKEMON") then - version = bts(memory.readbyterange(0x023FFE00,12),12) - lan = string.char(memory.readbyte(0x023FFE0F)) - gamecode = bts(memory.readbyterange(0x023FFE00+0xA8C,4),4) -- Or E0C ? - gen = 5 - sel = table["consoles"][3] - else - version = 0 - lan = 0 - gen = 0 - sel = table["consoles"][1] - end - -- I think all PAL regions use the same adresses. Here's the dirty hack to make them all work - -- Note : "O" seems to be the letter used in DSi enhanced games - if lan == "D" or lan == "H" or lan == "I" or lan == "O" or lan =="S" or lan == "X" or lan == "Y" or lan == "Z" then - lan = "F" + + resolution = table["resolutions"].gameBoyColor + elseif string.find(bts(memory.readbyterange(0x080000A0, 12),12), "POKEMON") or string.find(bts(memory.readbyterange(0x080000A0, 12),12), "PKMN") then + version = bts(memory.readbyterange(0x080000A0, 12),12) + lan = string.char(memory.readbyte(0x080000AF)) + gen = 3 + resolution = table["resolutions"].gameBoyAdvance + elseif string.find(bts(memory.readbyterange(0x023FF000,12),12), "POKEMON") then + version = bts(memory.readbyterange(0x023FF000,12),12) + lan = string.char(memory.readbyte(0x023FFE0F)) + gamecode = bts(memory.readbyterange(0x023FF000+0xA8C,4),4) -- Or E0C ? + gen = 4 + resolution = table["resolutions"].nintendoDS + elseif string.find(bts(memory.readbyterange(0x023FFE00,12),12), "POKEMON") then + version = bts(memory.readbyterange(0x023FFE00,12),12) + lan = string.char(memory.readbyte(0x023FFE0F)) + gamecode = bts(memory.readbyterange(0x023FFE00+0xA8C,4),4) -- Or E0C ? + gen = 5 + resolution = table["resolutions"].nintendoDS + else + version = 0 + lan = 0 + gen = 0 + resolution = table["resolutions"].gameBoyColor end - local gamedata = {version, lan, gen, sel} + local gamedata = {version, lan, gen, resolution} return gamedata end -function statusChange(input) -- Manages modes change through key presses - if input[settings["key"][1]] and not prev[settings["key"][1]] then - local max = gen <=2 and 3 or 4 - if mode < max then mode=mode+1 else mode=1 end - end - if input[settings["key"][2]] and not prev[settings["key"][2]] then - if status==1 then status = 2 else status = 1 end - end - if input[settings["key"][3]] and not prev[settings["key"][3]] then - if gen <= 2 and status == 2 then - substatus[2] = 1 - elseif substatus[status] < 6 then - substatus[status] = substatus[status]+1 - else - substatus[status]=1 - end - end - if input[settings["key"][4]] and not prev[settings["key"][4]] then - help = 0 - more = more == 1 and 0 or 1 - end - if input[settings["key"][5]] and not prev[settings["key"][5]] then - more = 0 - help = help == 1 and 0 or 1 +function resolveBase(desc) + if not desc or not desc.ptr then return nil end + + -- No add and no chain = direct address (don't dereference) + if not desc.add and not desc.chain then + return desc.ptr end - if input["Y"] and not prev["Y"] then - more = 0 - help = 0 - status = 1 - substatus = {1,1,1} - yling = yling == 1 and 0 or 1 + + -- Always dereference ptr first + local addr = memory.readdword(desc.ptr) + if addr == 0 then return nil end + + -- Follow chain if present + if desc.chain then + for _, off in ipairs(desc.chain) do + addr = memory.readdword(addr + off) + if addr == 0 then return nil end + end end - prev=input + + return addr + (desc.add or 0) end -function checkLast(pid,checksum,start,gen) -- Compares pid and checksum with current pid and checksum - local mdword=memory.readdwordunsigned - local mword=memory.readwordunsigned - local mbyte=memory.readbyteunsigned - - local lastpid = pid - local lastchecksum = checksum - local currentchecksum - local currentpid - +function isPokemonChanged(pid, checksum, start, gen, selectedPkmnSide, previousHP) + local prng + local currentpid, currentchecksum, currentHP if gen <= 2 then currentpid = gen == 1 and table["gen1id"][mbyte(start)] or mbyte(start) - if status == 1 then + if selectedPkmnSide == pkmnSide.PLAYER then currentchecksum = gen == 1 and mword(start+0x1B) or mword(start+0x15) - else - currentchecksum = gen == 1 and mword(start+0xC) or mword(start+0x06) + currentHP = gen == 1 + and (0x100*mbyte(start+0x01) + mbyte(start+0x02)) + or (0x100*mbyte(start+0x22) + mbyte(start+0x23)) + else + currentchecksum = gen == 1 and mword(start+0x0C) or mword(start+0x06) + currentHP = gen == 1 + and (0x100*mbyte(start+0x01) + mbyte(start+0x02)) + or (0x100*mbyte(start+0x10) + mbyte(start+0x11)) end + + elseif gen == 3 then + currentpid = mdword(start) + currentchecksum = mdword(start+6) + currentHP = mword(start+86) else currentpid = mdword(start) - currentchecksum = gen == 3 and mdword(start+6) or mword(start+6) - end + currentchecksum = mword(start+6) - if lastpid == currentpid and lastchecksum == currentchecksum then - return 1 - else - return 0 - + local prng = currentpid + for i = 0x88, 0x8E, 2 do + prng = mult32(prng,0x41C64E6D) + 0x6073 + if i == 0x8E then + currentHP = bxr(mword(start+i), rshift(prng,16)) + end + end end + + return pid ~= currentpid or checksum ~= currentchecksum or previousHP ~= currentHP end -function fetchPokemon(start) -- Fetches Pokemon info from memory and returns a table with all the data - local mdword=memory.readdwordunsigned - local mword=memory.readwordunsigned - local mbyte=memory.readbyteunsigned - local mbyterange=memory.readbyterangeunsigned - local bnd,br,bxr=bit.band,bit.bor,bit.bxor - local rshift, lshift=bit.rshift, bit.lshift - local prng - - function getbits(a,b,d) -- Get bits (kinda obvious right ?) - return rshift(a,b)%lshift(1,d) - end - - function mult32(a,b) -- 32 bits multiplication - local c=rshift(a,16) - local d=a%0x10000 - local e=rshift(b,16) - local f=b%0x10000 - local g=(c*f+d*e)%0x10000 - local h=d*f - local i=g*0x10000+h - return i +local function shinyValue(p) + local pid_low = bit.band(p.pid, 0xFFFF) + local pid_high = rshift(p.pid, 16) + + return bit.bxor( + bit.bxor(p.OTTID, p.OTSID), + bit.bxor(pid_low, pid_high) + ) +end + +function pokerusDays(pokerusByte) + if pokerusByte == 0 then + return 0, 0 end - - function gettop(a) -- Rshift for data decryption - return(rshift(a,16)) - end - + + local daysLeft = bit.band(pokerusByte, 0x0F) + local strain = rshift(pokerusByte, 4) + local strainDays = (strain % 4) + 1 + + return strainDays, daysLeft +end + +function fetchPokemon(start, gen, selectedPkmnSide) -- Fetches Pokemon info from memory and returns a table with all the data + local prng if gen == 1 then -- -Gen 1 routine local pokemon={} pokemon["hp"] = {} pokemon["stats"]={} --d46f - if status == 1 then -- Your Pokemon + if selectedPkmnSide == pkmnSide.PLAYER then -- Your Pokemon pokemon["hp"]["max"] = 0x100*mbyte(start+0x22) + mbyte(start+0x23) pokemon["TID"] = 0x100*mbyte(start+0x0C) + mbyte(start+0x0D) pokemon["pp"]={} @@ -192,7 +198,6 @@ function fetchPokemon(start) -- Fetches Pokemon info from memory and returns a t pokemon["stats"][4]=0x100*mbyte(start+0x28) + mbyte(start+0x29) pokemon["stats"][5]=0x100*mbyte(start+0x2A) + mbyte(start+0x2B) pokemon["xp"] = 0x10000*mbyte(start+0x0E)+0x100*mbyte(start+0x0F)+mbyte(start+0x10) - pokemon["helditem"] = mbyte(start+0x07) else -- Enemy pokemon["hp"]["max"] = 0x100*mbyte(start+0xF) + mbyte(start+0x10) pokemon["TID"] = "0" @@ -209,49 +214,48 @@ function fetchPokemon(start) -- Fetches Pokemon info from memory and returns a t pokemon["stats"][4]=0x100*mbyte(start+0x15) + mbyte(start+0x16) pokemon["stats"][5]=0x100*mbyte(start+0x17) + mbyte(start+0x18) pokemon["xp"] = mbyte(start+0x23) - pokemon["helditem"]=mbyte(start+0x23) end pokemon["species"] = table["gen1id"][mbyte(start)] - pokemon["speciesname"] = table["pokemon"][pokemon["species"]] + pokemon["speciesname"] = table["pokemon"][pokemon["species"]] pokemon["hp"]["current"] = 0x100*mbyte(start+0x01) + mbyte(start+0x02) - pokemon["move"] = {mbyte(start+0x08),mbyte(start+0x09),mbyte(start+0x0A),mbyte(start+0x0B)} + pokemon["move"] = {mbyte(start+0x08),mbyte(start+0x09),mbyte(start+0x0A),mbyte(start+0x0B)} pokemon["iv"]={} - pokemon["iv"][2] = getbits(pokemon["ivs"],4,4) - pokemon["iv"][3] = getbits(pokemon["ivs"],0,4) - pokemon["iv"][4] = getbits(pokemon["ivs"],8,4) - pokemon["iv"][5] = getbits(pokemon["ivs"],12,4) + pokemon["iv"][2] = getbits(pokemon["ivs"],4,4) + pokemon["iv"][3] = getbits(pokemon["ivs"],0,4) + pokemon["iv"][4] = getbits(pokemon["ivs"],8,4) + pokemon["iv"][5] = getbits(pokemon["ivs"],12,4) pokemon["iv"][1] = ((pokemon["iv"][2] % 2) * (2^3)) + ((pokemon["iv"][3] % 2) * (2^2)) + ((pokemon["iv"][4] % 2) * (2^1)) + ((pokemon["iv"][5] % 2) * (2^0)) - pokemon["hiddenpower"]={} + pokemon["hiddenpower"]={} pokemon["hiddenpower"]["type"]=4*pokemon["iv"][2]%4+4*pokemon["iv"][3]%4 pokemon["hiddenpower"]["base"]=((5*(math.floor(pokemon["iv"][5]/8)+2*math.floor(pokemon["iv"][4]/8)+4*math.floor(pokemon["iv"][3]/8)+8*math.floor(pokemon["iv"][2]/8))+pokemon["iv"][5]%4)/2)+31 - pokemon["pid"]=pokemon["species"] + pokemon["pid"]=pokemon["species"] pokemon["checksum"]=pokemon["ivs"] - if pokemon["iv"][3] == 10 and pokemon["iv"][4] == 10 and pokemon["iv"][5] == 10 then + -- Gen 1 Shiny Calculation (IV Based) + if pokemon["iv"][3] == 10 and pokemon["iv"][4] == 10 and pokemon["iv"][5] == 10 then local atk = pokemon["iv"][2] - if atk == 2 or atk == 3 or atk == 6 or atk == 7 or atk == 10 or atk == 11 or atk == 14 or atk == 15 then - pokemon["shiny"] = 1 - end + pokemon["shiny"] = (atk == 2 or atk == 3 or atk == 6 or atk == 7 or atk == 10 or atk == 11 or atk == 14 or atk == 15) else - pokemon["shiny"] = 0 + pokemon["shiny"] = false end - if version == "POKEMON YELL" and pokemon["species"] == 25 and status == 1 then + + if version == "POKEMON YELL" and pokemon["species"] == 25 and selectedPkmnSide == pkmnSide.PLAYER then pokemon["friendship"] = mbyte(start+0x305) end - return pokemon + return pokemon - elseif gen == 2 then -- Gen 2 routine + elseif gen == 2 then -- Gen 2 routine local pokemon={} pokemon["species"] = mbyte(start) - pokemon["speciesname"] = table["pokemon"][pokemon["species"]] + pokemon["speciesname"] = table["pokemon"][pokemon["species"]] pokemon["helditem"] = mbyte(start+0x01) pokemon["move"] = {mbyte(start+0x02),mbyte(start+0x03),mbyte(start+0x04),mbyte(start+0x05)} pokemon["xp"] = 0x10000*mbyte(start+0x08)+0x100*mbyte(start+0x09)+mbyte(start+0x0A) - if status == 1 then -- Player + if selectedPkmnSide == pkmnSide.PLAYER then -- Player pokemon["TID"] = 0x100*mbyte(start+0x06) + mbyte(start+0x07) pokemon["ev"] = {} pokemon["ev"][1] = 0x100*mbyte(start+0x0B) + mbyte(start+0x0C) @@ -267,7 +271,7 @@ function fetchPokemon(start) -- Fetches Pokemon info from memory and returns a t pokemon["iv"][5] = getbits(pokemon["ivs"],12,4) pokemon["pp"]={mbyte(start+0x17),mbyte(start+0x18),mbyte(start+0x19),mbyte(start+0x1A)} pokemon["friendship"] = mbyte(start+0x1B) - pokemon["pokerus"] = mbyte(0x1C) + pokemon["pokerusStrainDays"], pokemon["pokerusDays"] = pokerusDays(mbyte(0x1C)) pokemon["hp"]={} pokemon["hp"]["current"]=0x100*mbyte(start+0x22) + mbyte(start+0x23) pokemon["hp"]["max"]=0x100*mbyte(start+0x24) + mbyte(start+0x25) @@ -290,7 +294,7 @@ function fetchPokemon(start) -- Fetches Pokemon info from memory and returns a t pokemon["hp"]["current"]=0x100*mbyte(start+0x10) + mbyte(start+0x11) pokemon["hp"]["max"]=0x100*mbyte(start+0x12) + mbyte(start+0x13) pokemon["friendship"]=0 - pokemon["pokerus"]=0 + pokemon["pokerusStrainDays"], pokemon["pokerusDays"] = 0, 0 pokemon["stats"]={} pokemon["stats"][1]=pokemon["hp"]["max"] pokemon["stats"][2]=0x100*mbyte(start+0x14) + mbyte(start+0x15) @@ -303,6 +307,15 @@ function fetchPokemon(start) -- Fetches Pokemon info from memory and returns a t pokemon["hiddenpower"]={} pokemon["hiddenpower"]["type"]=4*pokemon["iv"][2]%4+4*pokemon["iv"][3]%4 pokemon["hiddenpower"]["base"]=((5*(math.floor(pokemon["iv"][5]/8)+2*math.floor(pokemon["iv"][4]/8)+4*math.floor(pokemon["iv"][3]/8)+8*math.floor(pokemon["iv"][2]/8))+pokemon["iv"][5]%4)/2)+31 + + -- Gen 2 Shiny Calculation (Same logic as Gen 1 - IV Based) + if pokemon["iv"][3] == 10 and pokemon["iv"][4] == 10 and pokemon["iv"][5] == 10 then + local atk = pokemon["iv"][2] + pokemon["shiny"] = (atk == 2 or atk == 3 or atk == 6 or atk == 7 or atk == 10 or atk == 11 or atk == 14 or atk == 15) + else + pokemon["shiny"] = false + end + return pokemon elseif gen == 3 then -- Routine for Gen 3 @@ -343,13 +356,17 @@ function fetchPokemon(start) -- Fetches Pokemon info from memory and returns a t pokemon["hiddenpower"]={} pokemon["hiddenpower"]["type"]=math.floor(((pokemon["iv"][1]%2 + 2*(pokemon["iv"][2]%2) + 4*(pokemon["iv"][3]%2) + 8*(pokemon["iv"][6]%2) + 16*(pokemon["iv"][4]%2) + 32*(pokemon["iv"][5]%2))*15)/63) pokemon["hiddenpower"]["base"]=math.floor((( getbits(pokemon["iv"][1],1,1) + 2*getbits(pokemon["iv"][2],1,1) + 4*getbits(pokemon["iv"][3],1,1) + 8*getbits(pokemon["iv"][6],1,1) + 16*getbits(pokemon["iv"][4],1,1) + 32*getbits(pokemon["iv"][5],1,1))*40)/63 + 30) - pokemon["pokerus"]=getbits(pokemon["misc"][1],0,8) + pokemon["pokerusStrainDays"], pokemon["pokerusDays"] = pokerusDays(getbits(pokemon["misc"][1], 0, 8)) pokemon["hp"]={} pokemon["hp"]["current"] = mword(start+86) pokemon["hp"]["max"] = mword(start+88) pokemon["stats"]={mword(start+88),mword(start+90),mword(start+92),mword(start+96),mword(start+98),mword(start+94)} + + -- Gen 3 Shiny Calculation (PID/SID Based) + pokemon["shiny"] = (shinyValue(pokemon) < 8) + return pokemon - elseif gen >= 4 then -- Routine for gens 4 and 5 + elseif gen >= 4 then -- Routine for gens 4 and 5 local pokemon={} local decrypted={} @@ -358,11 +375,6 @@ function fetchPokemon(start) -- Fetches Pokemon info from memory and returns a t pokemon["checksum"] = mword(pokemon["addr"]+6) pokemon["shift"] = (rshift((bnd(pokemon["pid"],0x3E000)),0xD)) % 24 - pokemon["nature"]={} - pokemon["nature"]["nature"]=pokemon["pid"]%25 - pokemon["nature"]["inc"]=math.floor(pokemon["nature"]["nature"]/5) - pokemon["nature"]["dec"]=pokemon["nature"]["nature"]%5 - -- Offsets offset={} offset["A"] = (table["growth"][pokemon["shift"]+1]-1) * 32 @@ -395,21 +407,34 @@ function fetchPokemon(start) -- Fetches Pokemon info from memory and returns a t pokemon["helditem"] = decrypted[0x0A+offset["A"]] pokemon["OTTID"] = decrypted[0x0C+offset["A"]] pokemon["OTSID"] = decrypted[0x0E+offset["A"]] - pokemon["xp"] = decrypted[0x10+offset["A"]] + pokemon["xp"] = decrypted[0x10+offset["A"]] + (decrypted[0x12+offset["A"]] * 0x10000) pokemon["friendship"] = getbits(decrypted[0x14+offset["A"]],0,8) pokemon["ability"] = getbits(decrypted[0x14+offset["A"]],8,8) pokemon["markings"] = decrypted[0x16+offset["A"]] pokemon["language"] = decrypted[0x17+offset["A"]] - pokemon["ev"]={getbits(decrypted[0x18+offset["A"]],0,8),getbits(decrypted[0x18+offset["A"]],8,8),getbits(decrypted[0x1A+offset["A"]],0,8),getbits(decrypted[0x1C+offset["A"]],0,8),getbits(decrypted[0x1C+offset["A"]],8,8),getbits(decrypted[0x1A+offset["A"]],8,8)} - pokemon["contest"]={getbits(decrypted[0x1E+offset["A"]],0,8),getbits(decrypted[0x1E+offset["A"]],8,8),getbits(decrypted[0x20+offset["A"]],0,8),getbits(decrypted[0x20+offset["A"]],8,8),getbits(decrypted[0x22+offset["A"]],0,8),getbits(decrypted[0x22+offset["A"]],8,8)} - pokemon["ribbon"]={decrypted[0x24+offset["A"]],decrypted[0x26+offset["A"]]} + pokemon["ev"]={getbits(decrypted[0x18+offset["A"]],0,8),getbits(decrypted[0x18+offset["A"]],8,8),getbits(decrypted[0x1A+offset["A"]],0,8),getbits(decrypted[0x1C+offset["A"]],0,8),getbits(decrypted[0x1C+offset["A"]],8,8),getbits(decrypted[0x1A+offset["A"]],8,8)} + pokemon["contest"]={getbits(decrypted[0x1E+offset["A"]],0,8),getbits(decrypted[0x1E+offset["A"]],8,8),getbits(decrypted[0x20+offset["A"]],0,8),getbits(decrypted[0x20+offset["A"]],8,8),getbits(decrypted[0x22+offset["A"]],0,8),getbits(decrypted[0x22+offset["A"]],8,8)} + pokemon["ribbon"]={decrypted[0x24+offset["A"]],decrypted[0x26+offset["A"]]} -- Block B pokemon["move"]={decrypted[0x28+offset["B"]],decrypted[0x2A+offset["B"]],decrypted[0x2C+offset["B"]],decrypted[0x2E+offset["B"]]} pokemon["pp"]={getbits(decrypted[0x30+offset["B"]],0,8),getbits(decrypted[0x30+offset["B"]],8,8),getbits(decrypted[0x32+offset["B"]],0,8),getbits(decrypted[0x32+offset["B"]],8,8)} pokemon["ivs"]=decrypted[0x38+offset["B"]] + lshift(decrypted[0x3A+offset["B"]],16) pokemon["iv"]={getbits(pokemon["ivs"],0,5),getbits(pokemon["ivs"],5,5),getbits(pokemon["ivs"],10,5),getbits(pokemon["ivs"],20,5),getbits(pokemon["ivs"],25,5),getbits(pokemon["ivs"],15,5)} + + -- Nature handling: Gen 5 stores it explicitly at 0x41, Gen 4 derives from PID + pokemon["nature"]={} + if gen == 5 then + -- Gen 5: Nature is stored at offset 0x41 in Block B + pokemon["nature"]["nature"] = getbits(decrypted[0x40+offset["B"]],8,8) -- 0x41 is upper byte of 0x40 word + else + -- Gen 4: Nature derived from PID + pokemon["nature"]["nature"] = pokemon["pid"]%25 + end + pokemon["nature"]["inc"]=math.floor(pokemon["nature"]["nature"]/5) + pokemon["nature"]["dec"]=pokemon["nature"]["nature"]%5 + pokemon["stats"]={decrypted[0x90],decrypted[0x92],decrypted[0x94],decrypted[0x98],decrypted[0x9A],decrypted[0x96]} - pokemon["pokerus"]=getbits(decrypted[0x82],0,8) + pokemon["pokerusStrainDays"], pokemon["pokerusDays"] = pokerusDays(decrypted[0x82+offset["D"]]) pokemon["hp"]={} pokemon["hp"]["current"]=decrypted[0x8E] pokemon["hp"]["max"]=decrypted[0x90] @@ -417,6 +442,9 @@ function fetchPokemon(start) -- Fetches Pokemon info from memory and returns a t pokemon["hiddenpower"]["type"]=math.floor(((pokemon["iv"][1]%2 + 2*(pokemon["iv"][2]%2) + 4*(pokemon["iv"][3]%2) + 8*(pokemon["iv"][6]%2) + 16*(pokemon["iv"][4]%2) + 32*(pokemon["iv"][5]%2))*15)/63) pokemon["hiddenpower"]["base"]=math.floor((( getbits(pokemon["iv"][1],1,1) + 2*getbits(pokemon["iv"][2],1,1) + 4*getbits(pokemon["iv"][3],1,1) + 8*getbits(pokemon["iv"][6],1,1) + 16*getbits(pokemon["iv"][4],1,1) + 32*getbits(pokemon["iv"][5],1,1))*40)/63 + 30) lastpid = pokemon["pid"] + + -- Gen 4+ Shiny Calculation (PID/SID Based) + pokemon["shiny"] = (shinyValue(pokemon) < 8) return pokemon - end -end + end +end \ No newline at end of file diff --git a/data/tables.lua b/data/tables.lua index 1d7e9a1..785146c 100644 --- a/data/tables.lua +++ b/data/tables.lua @@ -25,19 +25,15 @@ dofile "data/gamesdata.lua" -- Games titles and memory adresses dofile "data/pokemondata.lua" -- Pokemon names, abilities, moves -- Consoles resolutions by gen (to display things right on every game) -table["consoles"]={{160,144},{240,160},{256,192}} +table["resolutions"] = {gameBoyColor = {x=160, y=144}, gameBoyAdvance = {x=240, y=160}, nintendoDS = {x=256, y=192}} -- Things to display (only has a cosmetic effect but might produce overlapping text if changed) -table["labels"]={"HP","AT","DF","SA","SD","SP"} -- Stats labels (Keep in the same order) -table["contests"]={"CO","BE","CU","SM","TH","FE"} -- Contest stats labels (Keep in the same order) table["colors"]={"#ff0000","#f08030","#f8d030","#6890f0","#78c850","#f85888"} -- Stats colors (Keep in the same order) table["typecolor"]={"#CB5F48","#7DA6DE","#B468B7","#CC9F4F","#B2A061","#94BC4A","#846AB6","#89A1B0","#EA7A3C","#539AE2","#71C558","#E5C531","#E5709B","#70CBD4","#6A7BAF","#736C75"} -- Types colors (Keep in the same order) -table["modes"]={"IVs", "EVs", "Stats", "Cont."} -- Modes names (Keep in the same order) table["nature"]={"Hardy","Lonely","Brave","Adamant","Naughty","Bold","Docile","Relaxed","Impish","Lax","Timid","Hasty","Serious","Jolly","Naive","Modest","Mild","Quiet","Bashful","Rash","Calm","Gentle","Sassy","Careful","Quirky"} -- Natures names (Keep in the same order) table["type"]={"Fighting","Flying","Poison","Ground","Rock","Bug","Ghost","Steel","Fire","Water","Grass","Electric","Psychic","Ice","Dragon","Dark"} -- Types names (Keep in the same order) -- Data used for calculation - modifying it will break everything (but you can still do it if you want) -table["modesorder"]={"iv","ev","stats","contest"} table["statsorder"]={1,2,3,6,4,5} table["growth"]={1,1,1,1,1,1, 2,2,3,4,3,4, 2,2,3,4,3,4, 2,2,3,4,3,4} table["attack"]={2,2,3,4,3,4, 1,1,1,1,1,1, 3,4,2,2,4,3, 3,4,2,2,4,3} diff --git a/ylingstats.lua b/ylingstats.lua index c5e9c97..ce949d2 100644 --- a/ylingstats.lua +++ b/ylingstats.lua @@ -4,191 +4,149 @@ -- https://github.com/yling if emu and not memory then - console:log("This script is made to work with VBA-rr. It will not work with mGBA.") - return false + console:log("This script is made to work with VBA-rr. It will not work with mGBA.") + return false end -dofile "data/tables.lua" -- Tables with games data, and various data - including names -dofile "data/memory.lua" -- Functions and Pokemon table generation +dofile "data/tables.lua" -- Tables with games data, and various data - including names +dofile "data/memory.lua" -- Functions and Pokemon table generation +dofile "data/display.lua" -- Display module local gamedata = getGameInfo() -- Gets game info -version, lan, gen, sel = gamedata[1],gamedata[2],gamedata[3],gamedata[4] +local version, lan, gen, resolution = gamedata[1], gamedata[2], gamedata[3], gamedata[4] +Display.setResolution(resolution) -settings={} -settings["pos"]={} -- Fixed blocks coordinates {x,y}{x,y} -settings["pos"][1]={2,2} -settings["pos"][2]={10,sel[2]/6} -settings["pos"][3]={2,sel[2]/64*61} -settings["key"]={ "J", -- Switch mode (EV,IV,Stats) - "K", -- Switch status (Enemy / player) - "L", -- Sub Status + (Pokemon Slot) - "M", -- Toggle + display - "H" } -- Toggle help +-- Note : "O" seems to be the letter used in DSi enhanced games +-- (Couldn't find any source for this, NDSi Enhanced french version of pokemon black still shows "F", Spanish NDSi Enhanced version still shows "S") +-- "O" seems to be (USA, Europe) +-- https://wiki.no-intro.org/index.php?title=Nintendo_-_Nintendo_DSi_(Digital)_(CDN)_dat_notes#Region_Code_to_No-Intro_Region_Map +if games[version][lan] == nil then -- If language is not available default to french + if lan == "D" or lan == "H" or lan == "I" or lan == "O" or lan == "S" or lan == "X" or lan == "Y" or lan == "Z" then + lan = "F" + end +end +if version == 0 or not games[version] or not games[version][lan] then + if games[version] and games[version]["E"] then + print("This version is supported, but not in this language. Check gamesdata.lua to add it.") + else + print("This game isn't supported. Is it a hackrom ? It might work but you'll have to add it yourself. Check gamesdata.lua") + end + print("Version: "..version) + print("Language: "..lan) + print("Language: "..bit.tohex(lan)) + return +end print("Welcome to yPokeStats Unified ") +print("Game :", games[version][lan][1]) +pkmnSide = {PLAYER = 1, ENEMY = 2} -if version ~= 0 and games[version][lan] ~= nil then - print("Game :", games[version][lan][1]) - - status, mode, help=1,1,1 -- Default status and substatus - 1,1,1 is Player's first Pokémon - substatus={1,1,1} - lastpid,lastchecksum=0,0 -- Will be useful to avoid re-loading the same pokemon over and over again - count,clockcount,totalclocktime,lastclocktime,highestclocktime,yling=0,0,0,0,0,0 -- Monitoring - useless - - local prev={} -- Preparing the input tables - allows to check if a key has been pressed - prev=input.get() - - function main() -- Main function - display (check memory.lua for calculations) - nClock = os.clock() -- Set the clock (for performance monitoring -- useless) - statusChange(input.get()) -- Check for key input and changes status - - if help==1 then -- Help screen display - gui.box(settings["pos"][2][1]-5,settings["pos"][2][2]-5,sel[1]-5,settings["pos"][2][2]+sel[2]/2,"#ffffcc","#ffcc33") - gui.text(settings["pos"][2][1],settings["pos"][2][2],"yPokemonStats","#ee82ee") - gui.text(settings["pos"][2][1],settings["pos"][2][2]+sel[2]/16,"http://github.com/yling","#87cefa") - gui.text(settings["pos"][2][1],settings["pos"][2][2]+sel[2]/16*2,"-+-+-+-+-","#ffcc33") - gui.text(settings["pos"][2][1],settings["pos"][2][2]+sel[2]/16*3,settings["key"][1]..": IVs, EVs, Stats and Contest stats",table["colors"][5]) - gui.text(settings["pos"][2][1],settings["pos"][2][2]+sel[2]/16*4,settings["key"][2]..": Player team / Enemy team",table["colors"][4]) - gui.text(settings["pos"][2][1],settings["pos"][2][2]+sel[2]/16*5,settings["key"][3]..": Pokemon slot (1-6)",table["colors"][3]) - gui.text(settings["pos"][2][1],settings["pos"][2][2]+sel[2]/16*6,settings["key"][4]..": Show more data",table["colors"][2]) - gui.text(settings["pos"][2][1],settings["pos"][2][2]+sel[2]/16*7,settings["key"][5]..": Toggle this menu",table["colors"][1]) - end +local key = { SWITCH_MODE = "J", -- Switch mode (EV,IV,Stats) + SWITCH_SELECTED_POKEMON = "K", -- Switch pokemon side (Enemy / player) + POKEMON_SLOT = "L", -- Pokemon Slot + TOGGLE_MORE = "M", -- Show more data + TOGGLE_HELP = "H"} -- Toggle help - start = status==1 and games[version][lan][2]+games[version][lan][4]*(substatus[1]-1) or games[version][lan][3]+games[version][lan][4]*(substatus[2]-1) -- Set the pokemon start adress - - if memory.readdwordunsigned(start) ~= 0 or memory.readbyteunsigned(start) ~= 0 then -- If there's a PID - if checkLast(lastpid,lastchecksum,start,gen) == 0 or pokemon["species"] == nil then -- If it's not the last loaded PID (cause you know) or if the pokemon data is empty - pokemon = fetchPokemon(start) -- Fetch pokemon data at adress start - count=count+1 -- Times data has been fetched from memory (for monitoring - useless) - lastpid = gen >= 3 and pokemon["pid"] or pokemon["species"] -- Update last loaded PID - lastchecksum = gen >= 3 and pokemon["checksum"] or pokemon["ivs"] - end - - -- Permanent display -- - labels = mode == 4 and table["contests"] or table["labels"] -- Load contests labels or stats labels - tmpcolor = status == 1 and "green" or "red" -- Dirty tmp var for status and substatus color for player of enemy - tmpletter = status == 1 and "P" or "E" -- Dirty tmp var for status and substatus letter for player of enemy - tmptext = tmpletter..substatus[1].." ("..table["modes"][mode]..")" -- Dirty tmp var for current mode - helditem = pokemon["helditem"] == 0 and "none" or table["items"][gen][pokemon["helditem"]] - - -- GEN 1 & 2 - if gen <= 2 then - for i=1,5 do -- For each DV - gui.text(settings["pos"][1][1]+(i-1)*sel[1]/5,settings["pos"][1][2],table["gen1labels"][i], table["colors"][i]) -- Display label - gui.text(settings["pos"][1][1]+sel[1]/5/4+(i-1)*sel[1]/5,settings["pos"][1][2], pokemon[table["modesorder"][mode]][i], table["colors"][i]) - gui.text(settings["pos"][1][1]+sel[1]*4/10,settings["pos"][3][2], tmptext, tmpcolor) -- Display current status (using previously defined dirty temp vars) - local shiny = pokemon["shiny"] == 1 and "Shiny" or "Not shiny" - local shinycolor = pokemon["shiny"] == 1 and "green" or "red" - gui.text(settings["pos"][1][1]+sel[1]*7/10,settings["pos"][3][2],shiny,shinycolor) +local state = {selectedPkmnSide = pkmnSide.PLAYER, mode = 3, help = true, more = false, pokemonSlot = {1, 1}} +local cache = {lastpid = 0, lastchecksum = 0, lastPokemonSide = pkmnSide.PLAYER} +local pokemon = nil +local monitor = {yling = false, count = 0, clockcount = 0, totalclocktime = 0, lastclocktime = 0, highestclocktime = 0, meanclocktime = 0} - end - - -- GEN 3, 4 and 5 - else - for i=1,6 do -- For each IV - gui.text(settings["pos"][1][1]+(i+1)*sel[1]/8,settings["pos"][1][2],labels[i], table["colors"][i]) -- Display label - gui.text(settings["pos"][1][1]+sel[1]/8/2+(i+1)*sel[1]/8,settings["pos"][1][2], pokemon[table["modesorder"][mode]][i], table["colors"][i]) -- Display current mode stat - if mode ~= 4 then -- If not in contest mode - if pokemon["nature"]["inc"]~=pokemon["nature"]["dec"] then -- If nature changes stats - if i==table["statsorder"][pokemon["nature"]["inc"]+2] then -- If the nature increases current IV - gui.text(settings["pos"][1][1]+sel[1]/8/2+sel[1]/8*(i+1),settings["pos"][1][2]+3, "__", "green") -- Display a green underline - elseif i==table["statsorder"][pokemon["nature"]["dec"]+2] then -- If the nature decreases current IV - gui.text(settings["pos"][1][1]+sel[1]/8/2+sel[1]/8*(i+1),settings["pos"][1][2]+3, "__", "red") -- Display a red underline - end - else -- If neutral nature - if i==table["statsorder"][pokemon["nature"]["inc"]+1] then -- If current IV is HP - gui.text(settings["pos"][1][1]+sel[1]/8/2+sel[1]/8*(i+1),settings["pos"][1][2]+3, "__", "grey") -- Display grey underline - end - end - end - end - gui.text(settings["pos"][1][1],settings["pos"][1][2], tmptext, tmpcolor) -- Display current status (using previously defined dirty temp vars) - gui.text(settings["pos"][1][1]+sel[1]*4/10,settings["pos"][3][2], "PID: "..bit.tohex(lastpid)) -- Last PID - end - - -- All gens - gui.text(settings["pos"][1][1], settings["pos"][1][2]+sel[2]/16, pokemon["species"]..": "..pokemon["speciesname"].." - "..pokemon["hp"]["current"].."/"..pokemon["hp"]["max"], tmpcolor) -- Pkmn National Number, Species name and HP - frame = version == "POKEMON EMER" and "F. E/R: "..emu.framecount().."/"..memory.readdwordunsigned(0x020249C0) or "F. E: "..emu.framecount() - gui.text(settings["pos"][3][1],settings["pos"][3][2], frame) -- Emu frame counter - - -- "More" menu -- - if more == 1 then - gui.box(settings["pos"][2][1]-5,settings["pos"][2][2]-5,sel[1]-5,settings["pos"][2][2]+sel[2]/2,"#ffffcc","#ffcc33") -- Cute box - -- For gen 3, 4, 5 - if gen >= 3 then - naturen = pokemon["nature"]["nature"] > 16 and pokemon["nature"]["nature"]-16 or pokemon["nature"]["nature"] -- Dirty trick to use types colors for natures - naturecolor = table["typecolor"][naturen] -- Loading the tricked color - gui.text(settings["pos"][2][1],settings["pos"][2][2], "Nature") - gui.text(settings["pos"][2][1]+sel[2]/4,settings["pos"][2][2],table["nature"][pokemon["nature"]["nature"]+1],naturecolor) - -- Fetching held item name (if there's one) - pokerus = pokemon["pokerus"] == 0 and "no" or "yes" -- Jolly little yes or no for Pokerus - ability = gen == 3 and table["gen3ability"][pokemon["species"]][pokemon["ability"]+1] or pokemon["ability"] -- Fetching proper ability id for Gen 3 - - gui.text(settings["pos"][2][1]+sel[1]/2,settings["pos"][2][2], "OT ID : "..pokemon["OTTID"]) - gui.text(settings["pos"][2][1]+sel[1]/2,settings["pos"][2][2]+sel[2]/16, "OT SID : "..pokemon["OTSID"]) - gui.text(settings["pos"][2][1]+sel[1]/2,settings["pos"][2][2]+2*sel[2]/16, "XP : "..pokemon["xp"]) - gui.text(settings["pos"][2][1]+sel[1]/2,settings["pos"][2][2]+3*sel[2]/16, "Item : "..helditem) - gui.text(settings["pos"][2][1]+sel[1]/2,settings["pos"][2][2]+4*sel[2]/16, "Pokerus : "..pokerus) - gui.text(settings["pos"][2][1]+sel[1]/2,settings["pos"][2][2]+5*sel[2]/16, "Friendship : "..pokemon["friendship"]) - gui.text(settings["pos"][2][1]+sel[1]/2,settings["pos"][2][2]+6*sel[2]/16, "Ability : "..table["ability"][ability]) - - -- For gen 1 & 2 +local prev = input.get() -- Preparing the input tables - allows to check if a key has been pressed + +function main() -- Main function - display (check memory.lua for calculations) + nClock = os.clock() -- Set the clock (for performance monitoring) + statusChange(input.get()) -- Check for key input and changes status + + -- Clear pokemon data when status changes to prevent displaying wrong pokemon + if cache.lastPokemonSide ~= state.selectedPkmnSide then + pokemon = nil + end + cache.lastPokemonSide = state.selectedPkmnSide + + -- Resolve pokemon memory address + if state.selectedPkmnSide == pkmnSide.PLAYER then + base = resolveBase(games[version][lan][2]) -- Player pokemon + else + base = resolveBase(games[version][lan][3]) -- Enemy pokemon + end + + size = games[version][lan][4] + start = base + size * (state.pokemonSlot[state.selectedPkmnSide] - 1) + + -- Fetch pokemon data if valid memory found + if start and (memory.readdwordunsigned(start) ~= 0 or memory.readbyteunsigned(start) ~= 0) then + if not pokemon or isPokemonChanged(cache.lastpid, cache.lastchecksum, start, gen, state.selectedPkmnSide, pokemon["hp"]["current"]) or not pokemon["species"] then + local fetched = fetchPokemon(start, gen, state.selectedPkmnSide) + monitor.count = monitor.count + 1 + + if fetched and fetched["species"] and fetched["speciesname"] then + if fetched["hp"] and fetched["hp"]["max"] > 0 and fetched["hp"]["max"] < 1000 then + pokemon = fetched + if gen >= 3 then + cache.lastpid = pokemon["pid"] + cache.lastchecksum = pokemon["checksum"] else - gui.text(settings["pos"][2][1],settings["pos"][2][2], "TID: "..pokemon["TID"].." / Item: "..helditem) - if version == "POKEMON YELL" and status == 1 and pokemon["species"] == 25 or gen == 2 then - gui.text(settings["pos"][2][1],settings["pos"][2][2]+sel[2]/16*2, "Friendship : "..pokemon["friendship"]) - end - end - - -- For all gens - gui.text(settings["pos"][2][1],settings["pos"][2][2]+sel[2]/16, "H.Power") - gui.text(settings["pos"][2][1]+sel[2]/4,settings["pos"][2][2]+sel[2]/16, table["type"][pokemon["hiddenpower"]["type"]+1].." "..pokemon["hiddenpower"]["base"], table["typecolor"][pokemon["hiddenpower"]["type"]+1]) - gui.text(settings["pos"][2][1],settings["pos"][2][2]+3*sel[2]/16, "Moves:") - for i=1,4 do -- For each move - if table["move"][pokemon["move"][i]] ~= nil then - gui.text(settings["pos"][2][1],settings["pos"][2][2]+(i+3)*sel[2]/16, table["move"][pokemon["move"][i]].." - "..pokemon["pp"][i].."PP") -- Display name and PP - end - end - end - else -- No PID found - if status == 1 then -- If player team just decrement n - substatus[1] = 1 - elseif status == 2 then -- If enemy - if substatus[2] == 1 then -- If was trying first enemy go back to player team - status = 1 - else -- Else decrement n - substatus[2] = 1 + cache.lastpid = pokemon["species"] + cache.lastchecksum = pokemon["ivs"] end - else -- Shouldn't happen but hey, warn me if it does - print("Something's wrong.") end - gui.text(settings["pos"][1][1],settings["pos"][1][2],"No Pokemon", "red") -- Beautiful red warning end - - -- Script performance (useless) - clocktime = os.clock()-nClock - clockcount = clockcount + 1 - totalclocktime = totalclocktime+clocktime - lastclocktime = clocktime ~= 0 and clocktime or lastclocktime - highestclocktime = clocktime > highestclocktime and clocktime or highestclocktime - meanclocktime = totalclocktime/clockcount - if yling==1 then -- I lied, there's a secret key to display script performance, but who cares besides me? (It's Y) - gui.text(settings["pos"][2][1],2*settings["pos"][2][2],"Last clock time: "..numTruncate(lastclocktime*1000,2).."ms") - gui.text(settings["pos"][2][1],2*settings["pos"][2][2]+sel[2]/16,"Mean clock time: "..numTruncate(meanclocktime*1000,2).."ms") - gui.text(settings["pos"][2][1],2*settings["pos"][2][2]+2*sel[2]/16,"Most clock time: "..numTruncate(highestclocktime*1000,2).."ms") - gui.text(settings["pos"][2][1],2*settings["pos"][2][2]+3*sel[2]/16,"Data fetched: "..count.."x") - end + end + else + -- No PID found - reset pokemonSlot + if state.selectedPkmnSide == pkmnSide.PLAYER then + state.pokemonSlot[1] = 1 + else + state.pokemonSlot[2] = 1 + end end -else -- Game not in the data table - if games[version]["E"] ~= nil then - print("This version is supported, but not in this language. Check gamesdata.lua to add it.") - else - print("This game isn't supported. Is it a hackrom ? It might work but you'll have to add it yourself. Check gamesdata.lua") - end - print("Version: "..version) - print("Language: "..bit.tohex(lan)) + + -- Render + Display.mainRender(pokemon, gen, version, state, cache.lastpid, monitor, key, table) + + -- Script performance monitoring + clocktime = os.clock() - nClock + monitor.clockcount = monitor.clockcount + 1 + monitor.totalclocktime = monitor.totalclocktime + clocktime + monitor.lastclocktime = clocktime ~= 0 and clocktime or monitor.lastclocktime + monitor.highestclocktime = clocktime > monitor.highestclocktime and clocktime or monitor.highestclocktime + monitor.meanclocktime = monitor.totalclocktime / monitor.clockcount + + Display.performanceStats(monitor) end gui.register(main) + +function statusChange(input) + if input[key.SWITCH_MODE] and not prev[key.SWITCH_MODE] then + local max = (gen <= 2) and 3 or 4 + state.mode = (state.mode < max) and (state.mode + 1) or 1 + end + if input[key.SWITCH_SELECTED_POKEMON] and not prev[key.SWITCH_SELECTED_POKEMON] then + state.selectedPkmnSide = (state.selectedPkmnSide == 1) and 2 or 1 + end + if input[key.POKEMON_SLOT] and not prev[key.POKEMON_SLOT] then + if gen <= 2 and state.selectedPkmnSide == pkmnSide.ENEMY then + state.pokemonSlot[2] = 1 + else + state.pokemonSlot[state.selectedPkmnSide] = (state.pokemonSlot[state.selectedPkmnSide] < 6) and (state.pokemonSlot[state.selectedPkmnSide] + 1) or 1 + end + end + if input[key.TOGGLE_MORE] and not prev[key.TOGGLE_MORE] then + state.help = false + state.more = not state.more + end + if input[key.TOGGLE_HELP] and not prev[key.TOGGLE_HELP] then + state.more = false + state.help = not state.help + end + if input["Y"] and not prev["Y"] then + state.more = false + state.help = false + monitor.yling = not monitor.yling + end + prev = input +end \ No newline at end of file