From 49fbf59301aba74a47ea209f0b89054658c0c772 Mon Sep 17 00:00:00 2001 From: Samira Urzua Date: Sun, 4 Jan 2026 13:42:30 -0300 Subject: [PATCH 01/18] Major refactor: created display.lua to handle rendering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix: Gen 4 enemy Pokémon only appeared if the script started during a battle Fix: Pokémon data loaded before it was ready, causing incorrect display Fix: crashes when loading a Pokémon with a nonexistent species ID Feature: shiny detection and display for Gen 3–5 --- data/display.lua | 229 +++++++++++++++++++++++++++++++++++++ data/gamesdata.lua | 132 +++++++++++----------- data/memory.lua | 275 +++++++++++++++++++++++++++------------------ data/tables.lua | 8 +- ylingstats.lua | 258 +++++++++++++++--------------------------- 5 files changed, 559 insertions(+), 343 deletions(-) create mode 100644 data/display.lua diff --git a/data/display.lua b/data/display.lua new file mode 100644 index 0000000..9474c6c --- /dev/null +++ b/data/display.lua @@ -0,0 +1,229 @@ +-- yPokemonStats Display Module +-- Handles all GUI rendering and display functions + +Display = {} + +local resolution = {x = 0, y = 0} + +Display.GRID = { + COLS = 40, + ROWS = 16 +} + +Display.GRID.MAX_COL = Display.GRID.COLS - 1 +Display.GRID.MAX_ROW = Display.GRID.ROWS - 1 + +function Display.setResolution(res) + resolution.x = res.x + resolution.y = res.y +end + +function Display.colToPixelX(col) + if col < 0 or col > Display.GRID.MAX_COL then + error("col must be in range [0, " .. Display.GRID.MAX_COL .. "]" .. "value is: " .. col, 2) + end + return (resolution.x * col) / Display.GRID.COLS + 2 +end + +function Display.rowToPixelY(row) + if row < 0 or row > Display.GRID.MAX_ROW then + error("row must be in range [0, " .. Display.GRID.MAX_ROW .. "]" .. "value is: " .. row, 2) + end + return (resolution.y * row) / Display.GRID.ROWS + 2 +end + +function Display.mainRender(settings, pokemon, gen, version, status, mode, substatus, lastpid, more, table) + -- Prepare display variables + local tmpcolor = status == 1 and "green" or "red" + + -- Display pokemon stats + Display.statsDisplay(settings, pokemon, gen, mode, substatus, lastpid, table, tmpcolor) + + -- Species + local g = gen <= 2 + gui.text(Display.colToPixelX(0), Display.rowToPixelY(g and 14 or 1), pokemon["species"] .. ":" .. pokemon["speciesname"], tmpcolor) + + -- HP display + gui.text(Display.colToPixelX(g and 0 or 11), Display.rowToPixelY(g and 13 or 0), "HP:" .. pokemon["hp"]["current"] .. "/" .. pokemon["hp"]["max"], tmpcolor) + + -- Frame counter + Display.frameCounter(Display.colToPixelX(0), Display.rowToPixelY(Display.GRID.MAX_ROW), version) + + -- Shiny status + gui.text(Display.colToPixelX(30), Display.rowToPixelY(Display.GRID.MAX_ROW), pokemon["shiny"] == 1 and "Shiny" or "Not shiny", pokemon["shiny"] == 1 and "green" or "red") + + -- "More" menu + if more == 1 then + local helditem = pokemon["helditem"] == 0 and "none" or table["items"][gen][pokemon["helditem"]] + Display.moreMenu(settings, pokemon, gen, version, status, table, helditem, lastpid) + end +end + +-- Display "no Pokemon" warning +function Display.noPokemon(settings) + gui.text(Display.colToPixelX(0), Display.rowToPixelY(0), "No Pokemon", "red") +end + +-- Display help menu +function Display.showHelp(settings, table) + gui.box(Display.colToPixelX(2) - 5, Display.rowToPixelY(3) - 5, Display.colToPixelX(39) - 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 = { {settings["key"].SWITCH_MODE, ": IVs, EVs, Stats and Contest stats", 5}, + {settings["key"].SWITCH_STATUS, ": Player team / Enemy team", 4}, + {settings["key"].SUB_STATUS, ": Pokemon slot (1-6)", 3}, + {settings["key"].TOGGLE_MORE, ": Show more data", 2}, + {settings["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 + +function Display.statsDisplay(settings, pokemon, gen, mode, substatus, lastpid, table, color) + local statCount = gen <= 2 and 5 or 6 + local labels = gen <= 2 and table["gen1labels"] or table["labels"] + + -- Get IV data based on generation + local ivData = pokemon[table["modesorder"][mode]] + + -- Display stats + for i = 1, statCount do + local col = Display.GRID.COLS - (i * 3) + + -- Label at top + gui.text(Display.colToPixelX(col), Display.rowToPixelY(0), labels[statCount + 1 - i], table["colors"][statCount + 1 - i]) + + -- IV value one row below + local iv = ivData[statCount + 1 - i] + local ivText = iv == 31 and (iv .. "*") or iv + gui.text(Display.colToPixelX(col), Display.rowToPixelY(1), ivText, table["colors"][statCount + 1 - i]) + + -- Nature indicator (Gen 3+, not for mode 4) + if gen >= 3 and mode ~= 4 then + Display.natureIndicator(col, 1, i, pokemon, table) + end + end + + -- Status indicator + Display.statusIndicator((gen <= 2 and 15 or 0), (gen <= 2 and Display.GRID.MAX_ROW or 0), substatus[1], mode, table, color) + + -- PID (Gen 3+) + if gen >= 3 then + gui.text(Display.colToPixelX(15), Display.rowToPixelY(Display.GRID.MAX_ROW), "PID: " .. bit.tohex(lastpid)) + end +end + +-- Display nature stat modifiers (green for boost, red for reduction, grey for neutral) +function Display.natureIndicator(col, row, statIndex, pokemon, table) + local inc, dec = pokemon["nature"]["inc"], pokemon["nature"]["dec"] + + if inc ~= dec then + if statIndex == table["statsorder"][inc + 2] then + gui.text(Display.colToPixelX(col)+1, Display.rowToPixelY(row)+2, "__", "green") + elseif statIndex == table["statsorder"][dec + 2] then + gui.text(Display.colToPixelX(col)+1, Display.rowToPixelY(row)+2, "__", "red") + end + else + if statIndex == table["statsorder"][inc + 1] then + gui.text(Display.colToPixelX(col)+1, Display.rowToPixelY(row)+2, "__", "grey") + end + end +end + +-- Display status indicator (P1, P2, etc.) +function Display.statusIndicator(col, row, substatus, mode, table, color) + local statusText = (color == "green" and "P" or "E") .. substatus .. " (" .. table["modes"][mode] .. ")" + gui.text(Display.colToPixelX(col), Display.rowToPixelY(row), 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(settings, pokemon, gen, version, status, table, helditem, lastpid) + gui.box(Display.colToPixelX(2) - 5, Display.rowToPixelY(3) - 5, Display.colToPixelX(39) - 5, Display.rowToPixelY(13) + 5, "#ffffcc", "#ffcc33") + + if gen >= 3 then + Display.moreMenuGen3Plus(2, 3, pokemon, gen, table, helditem) + else + Display.moreMenuGen12(2, 3, pokemon, gen, version, status, table, helditem) + 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(col, row, pokemon, gen, table, 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(col), Display.rowToPixelY(row), "Nature") + gui.text(Display.colToPixelX(col + 8), Display.rowToPixelY(row), table["nature"][pokemon["nature"]["nature"] + 1], natureColor) + + local ability = gen == 3 and table["gen3ability"][pokemon["species"]][pokemon["ability"] + 1] or pokemon["ability"] + local pokerus = pokemon["pokerus"] == 0 and "no" or "yes" + + local rightCol = col + 20 + 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(rightCol), Display.rowToPixelY(row + 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(col, row, pokemon, gen, version, status, table, helditem) + gui.text(Display.colToPixelX(col), Display.rowToPixelY(row), "TID: " .. pokemon["TID"] .. " / Item: " .. helditem) + + if gen == 2 or (version == "POKEMON YELL" and status == 1 and pokemon["species"] == 25) then + gui.text(Display.colToPixelX(col), Display.rowToPixelY(row + 2), "Friendship : " .. pokemon["friendship"]) + end +end + +-- Display Hidden Power +function Display.hiddenPower(col, row, pokemon, table) + gui.text(Display.colToPixelX(col), Display.rowToPixelY(row), "H.Power") + gui.text(Display.colToPixelX(col + 8), Display.rowToPixelY(row), + table["type"][pokemon["hiddenpower"]["type"] + 1] .. " " .. pokemon["hiddenpower"]["base"], + table["typecolor"][pokemon["hiddenpower"]["type"] + 1]) +end + +-- Display moves list +function Display.movesList(col, row, pokemon, table) + gui.text(Display.colToPixelX(col), Display.rowToPixelY(row), "Moves:") + for i = 1, 4 do + if table["move"][pokemon["move"][i]] ~= nil then + gui.text(Display.colToPixelX(col), Display.rowToPixelY(row + i), + table["move"][pokemon["move"][i]] .. " - " .. pokemon["pp"][i] .. "PP") + end + end +end + +-- Display performance stats (secret feature) +function Display.performanceStats(settings, lastclocktime, meanclocktime, highestclocktime, count) + gui.text(Display.colToPixelX(2), Display.rowToPixelY(6), "Last clock time: " .. numTruncate(lastclocktime * 1000, 2) .. "ms") + gui.text(Display.colToPixelX(2), Display.rowToPixelY(7), "Mean clock time: " .. numTruncate(meanclocktime * 1000, 2) .. "ms") + gui.text(Display.colToPixelX(2), Display.rowToPixelY(8), "Most clock time: " .. numTruncate(highestclocktime * 1000, 2) .. "ms") + gui.text(Display.colToPixelX(2), Display.rowToPixelY(9), "Data fetched: " .. 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/memory.lua b/data/memory.lua index 94d0630..ef4b086 100644 --- a/data/memory.lua +++ b/data/memory.lua @@ -1,11 +1,11 @@ -- Functions and variables definition function bts(bytes,l) -- Bytes array to string - local name = "" - for i=1,l do + 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 @@ -15,21 +15,21 @@ function numTruncate(x,n) -- Truncate to n decimals end function hasValue (tab, val) - for index, value in ipairs(tab) do - if value == val then - return true - end - end + for index, value in ipairs(tab) do + if value == val then + return true + end + end - return false + return false 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,73 +39,89 @@ 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 +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 + + -- 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 + + return addr + (desc.add or 0) +end + +function statusChange(input) + if input[key.SWITCH_MODE] and not prev[key.SWITCH_MODE] then + local max = (gen <= 2) and 3 or 4 + mode = (mode < max) and (mode + 1) or 1 + end + if input[key.SWITCH_STATUS] and not prev[key.SWITCH_STATUS] then + status = (status == 1) and 2 or 1 + end + if input[key.SUB_STATUS] and not prev[key.SUB_STATUS] 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 + else + substatus[status] = (substatus[status] < 6) and (substatus[status] + 1) or 1 end - end - if input[settings["key"][4]] and not prev[settings["key"][4]] then + end + if input[key.TOGGLE_MORE] and not prev[key.TOGGLE_MORE] then help = 0 - more = more == 1 and 0 or 1 - end - if input[settings["key"][5]] and not prev[settings["key"][5]] then + more = (more == 1) and 0 or 1 + end + if input[key.TOGGLE_HELP] and not prev[key.TOGGLE_HELP] then more = 0 - help = help == 1 and 0 or 1 + help = (help == 1) and 0 or 1 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 + substatus = {1, 1, 1} + yling = (yling == 1) and 0 or 1 end - prev=input + prev = input end function checkLast(pid,checksum,start,gen) -- Compares pid and checksum with current pid and checksum @@ -138,34 +154,44 @@ function checkLast(pid,checksum,start,gen) -- Compares pid and checksum with cur end end +local function shinyValue(p) + local pid_low = bit.band(p.pid, 0xFFFF) + local pid_high = bit.rshift(p.pid, 16) + + return bit.bxor( + bit.bxor(p.OTTID, p.OTSID), + bit.bxor(pid_low, pid_high) + ) +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 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 - end + 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 + end + function gettop(a) -- Rshift for data decryption return(rshift(a,16)) end - + if gen == 1 then -- -Gen 1 routine local pokemon={} pokemon["hp"] = {} @@ -192,7 +218,7 @@ 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) + pokemon["helditem"] = mbyte(start+0x07) else -- Enemy pokemon["hp"]["max"] = 0x100*mbyte(start+0xF) + mbyte(start+0x10) pokemon["TID"] = "0" @@ -209,45 +235,49 @@ 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) + 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 + else + pokemon["shiny"] = 0 end else pokemon["shiny"] = 0 end + if version == "POKEMON YELL" and pokemon["species"] == 25 and status == 1 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) @@ -303,6 +333,19 @@ 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] + 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 + else + pokemon["shiny"] = 0 + end + else + pokemon["shiny"] = 0 + end + return pokemon elseif gen == 3 then -- Routine for Gen 3 @@ -348,8 +391,16 @@ function fetchPokemon(start) -- Fetches Pokemon info from memory and returns a t 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) + if shinyValue(pokemon) < 8 then + pokemon["shiny"] = 1 + else + pokemon["shiny"] = 0 + end + 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={} @@ -400,9 +451,9 @@ function fetchPokemon(start) -- Fetches Pokemon info from memory and returns a t 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)} @@ -417,6 +468,14 @@ 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) + if shinyValue(pokemon) < 8 then + pokemon["shiny"] = 1 + else + pokemon["shiny"] = 0 + end + return pokemon - end -end + end +end \ No newline at end of file diff --git a/data/tables.lua b/data/tables.lua index 1d7e9a1..a7f060a 100644 --- a/data/tables.lua +++ b/data/tables.lua @@ -25,7 +25,13 @@ 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}} + +key = { SWITCH_MODE = "J", -- Switch mode (EV,IV,Stats) + SWITCH_STATUS = "K", -- Switch status (Enemy / player) + SUB_STATUS = "L", -- Sub Status + (Pokemon Slot) + TOGGLE_MORE = "M", -- Show more data + TOGGLE_HELP = "H"} -- Toggle help -- 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) diff --git a/ylingstats.lua b/ylingstats.lua index c5e9c97..06e5142 100644 --- a/ylingstats.lua +++ b/ylingstats.lua @@ -4,191 +4,111 @@ -- 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] +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]) +settings={} +settings["key"] = { SWITCH_MODE = "J", -- Switch mode (EV,IV,Stats) + SWITCH_STATUS = "K", -- Switch status (Enemy / player) + SUB_STATUS = "L", -- Sub Status + (Pokemon Slot) + TOGGLE_MORE = "M", -- Show more data + TOGGLE_HELP = "H"} -- Toggle help -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 +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 - 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 prev = input.get() -- Preparing the input tables - allows to check if a key has been pressed - 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 +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 + Display.showHelp(settings, table) + end + + if status == 1 then -- Resolve pokemon memory address + 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 * (substatus[status] - 1) + + if start and (memory.readdwordunsigned(start) ~= 0 or memory.readbyteunsigned(start) ~= 0) then + if checkLast(lastpid, lastchecksum, start, gen) == 0 or not pokemon or not pokemon["species"] then + local fetched = fetchPokemon(start) + count = 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 -- Check that stats loaded correctly + pokemon = fetched + if gen >= 3 then + lastpid = pokemon["pid"] + 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 + lastpid = pokemon["species"] + 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 + Display.mainRender(settings, pokemon, gen, version, status, mode, substatus, lastpid, more, table) + else -- No PID found + if status == 1 then -- If player team just reset to slot 1 + substatus[1] = 1 + else -- If enemy team just reset to slot 1 + substatus[2] = 1 + end + Display.noPokemon(settings) + 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) + Display.performanceStats(settings, lastclocktime, meanclocktime, highestclocktime, count) 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)) end -gui.register(main) +gui.register(main) \ No newline at end of file From 62d3785f354092bad9c7af7a51c77bb1528b1684 Mon Sep 17 00:00:00 2001 From: Samira Urzua Date: Sun, 4 Jan 2026 14:59:54 -0300 Subject: [PATCH 02/18] Rename grid col/row variables to colX and rowY adding redundancy to improve readability --- data/display.lua | 80 +++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 42 deletions(-) diff --git a/data/display.lua b/data/display.lua index 9474c6c..63e7e83 100644 --- a/data/display.lua +++ b/data/display.lua @@ -5,31 +5,28 @@ Display = {} local resolution = {x = 0, y = 0} -Display.GRID = { - COLS = 40, - ROWS = 16 -} +Display.GRID = {COLSX = 40, ROWSY = 16} -Display.GRID.MAX_COL = Display.GRID.COLS - 1 -Display.GRID.MAX_ROW = Display.GRID.ROWS - 1 +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(col) - if col < 0 or col > Display.GRID.MAX_COL then - error("col must be in range [0, " .. Display.GRID.MAX_COL .. "]" .. "value is: " .. col, 2) +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 * col) / Display.GRID.COLS + 2 + return (resolution.x * colX) / Display.GRID.COLSX + 2 end -function Display.rowToPixelY(row) - if row < 0 or row > Display.GRID.MAX_ROW then - error("row must be in range [0, " .. Display.GRID.MAX_ROW .. "]" .. "value is: " .. row, 2) +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 * row) / Display.GRID.ROWS + 2 + return (resolution.y * rowY) / Display.GRID.ROWSY + 2 end function Display.mainRender(settings, pokemon, gen, version, status, mode, substatus, lastpid, more, table) @@ -47,10 +44,10 @@ function Display.mainRender(settings, pokemon, gen, version, status, mode, subst gui.text(Display.colToPixelX(g and 0 or 11), Display.rowToPixelY(g and 13 or 0), "HP:" .. pokemon["hp"]["current"] .. "/" .. pokemon["hp"]["max"], tmpcolor) -- Frame counter - Display.frameCounter(Display.colToPixelX(0), Display.rowToPixelY(Display.GRID.MAX_ROW), version) + Display.frameCounter(Display.colToPixelX(0), Display.rowToPixelY(Display.GRID.MAX_ROWY), version) -- Shiny status - gui.text(Display.colToPixelX(30), Display.rowToPixelY(Display.GRID.MAX_ROW), pokemon["shiny"] == 1 and "Shiny" or "Not shiny", pokemon["shiny"] == 1 and "green" or "red") + gui.text(Display.colToPixelX(30), Display.rowToPixelY(Display.GRID.MAX_ROWY), pokemon["shiny"] == 1 and "Shiny" or "Not shiny", pokemon["shiny"] == 1 and "green" or "red") -- "More" menu if more == 1 then @@ -91,52 +88,52 @@ function Display.statsDisplay(settings, pokemon, gen, mode, substatus, lastpid, -- Display stats for i = 1, statCount do - local col = Display.GRID.COLS - (i * 3) + local colX = Display.GRID.COLSX - (i * 3) -- Label at top - gui.text(Display.colToPixelX(col), Display.rowToPixelY(0), labels[statCount + 1 - i], table["colors"][statCount + 1 - i]) + gui.text(Display.colToPixelX(colX), Display.rowToPixelY(0), labels[statCount + 1 - i], table["colors"][statCount + 1 - i]) -- IV value one row below local iv = ivData[statCount + 1 - i] local ivText = iv == 31 and (iv .. "*") or iv - gui.text(Display.colToPixelX(col), Display.rowToPixelY(1), ivText, table["colors"][statCount + 1 - i]) + gui.text(Display.colToPixelX(colX), Display.rowToPixelY(1), ivText, table["colors"][statCount + 1 - i]) -- Nature indicator (Gen 3+, not for mode 4) if gen >= 3 and mode ~= 4 then - Display.natureIndicator(col, 1, i, pokemon, table) + Display.natureIndicator(colX, 1, i, pokemon, table) end end -- Status indicator - Display.statusIndicator((gen <= 2 and 15 or 0), (gen <= 2 and Display.GRID.MAX_ROW or 0), substatus[1], mode, table, color) + Display.statusIndicator((gen <= 2 and 15 or 0), (gen <= 2 and Display.GRID.MAX_ROWY or 0), substatus[1], mode, table, color) -- PID (Gen 3+) if gen >= 3 then - gui.text(Display.colToPixelX(15), Display.rowToPixelY(Display.GRID.MAX_ROW), "PID: " .. bit.tohex(lastpid)) + gui.text(Display.colToPixelX(15), Display.rowToPixelY(Display.GRID.MAX_ROWY), "PID: " .. bit.tohex(lastpid)) end end -- Display nature stat modifiers (green for boost, red for reduction, grey for neutral) -function Display.natureIndicator(col, row, statIndex, pokemon, table) +function Display.natureIndicator(colX, rowY, statIndex, pokemon, table) local inc, dec = pokemon["nature"]["inc"], pokemon["nature"]["dec"] if inc ~= dec then if statIndex == table["statsorder"][inc + 2] then - gui.text(Display.colToPixelX(col)+1, Display.rowToPixelY(row)+2, "__", "green") + gui.text(Display.colToPixelX(colX)+1, Display.rowToPixelY(rowY)+2, "__", "green") elseif statIndex == table["statsorder"][dec + 2] then - gui.text(Display.colToPixelX(col)+1, Display.rowToPixelY(row)+2, "__", "red") + gui.text(Display.colToPixelX(colX)+1, Display.rowToPixelY(rowY)+2, "__", "red") end else if statIndex == table["statsorder"][inc + 1] then - gui.text(Display.colToPixelX(col)+1, Display.rowToPixelY(row)+2, "__", "grey") + gui.text(Display.colToPixelX(colX)+1, Display.rowToPixelY(rowY)+2, "__", "grey") end end end -- Display status indicator (P1, P2, etc.) -function Display.statusIndicator(col, row, substatus, mode, table, color) +function Display.statusIndicator(colX, rowY, substatus, mode, table, color) local statusText = (color == "green" and "P" or "E") .. substatus .. " (" .. table["modes"][mode] .. ")" - gui.text(Display.colToPixelX(col), Display.rowToPixelY(row), statusText, color) + gui.text(Display.colToPixelX(colX), Display.rowToPixelY(rowY), statusText, color) end -- Display frame counter @@ -166,17 +163,16 @@ function Display.moreMenu(settings, pokemon, gen, version, status, table, heldit end -- More menu details for Gen 3-5 -function Display.moreMenuGen3Plus(col, row, pokemon, gen, table, helditem) +function Display.moreMenuGen3Plus(colX, rowY, pokemon, gen, table, 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(col), Display.rowToPixelY(row), "Nature") - gui.text(Display.colToPixelX(col + 8), Display.rowToPixelY(row), table["nature"][pokemon["nature"]["nature"] + 1], natureColor) + 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 pokerus = pokemon["pokerus"] == 0 and "no" or "yes" - local rightCol = col + 20 local details = { {"OT ID : " .. pokemon["OTTID"]}, {"OT SID : " .. pokemon["OTSID"]}, @@ -187,34 +183,34 @@ function Display.moreMenuGen3Plus(col, row, pokemon, gen, table, helditem) } for i, detail in ipairs(details) do - gui.text(Display.colToPixelX(rightCol), Display.rowToPixelY(row + i - 1), detail[1]) + 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(col, row, pokemon, gen, version, status, table, helditem) - gui.text(Display.colToPixelX(col), Display.rowToPixelY(row), "TID: " .. pokemon["TID"] .. " / Item: " .. helditem) +function Display.moreMenuGen12(colX, rowY, pokemon, gen, version, status, table, helditem) + gui.text(Display.colToPixelX(colX), Display.rowToPixelY(rowY), "TID: " .. pokemon["TID"] .. " / Item: " .. helditem) if gen == 2 or (version == "POKEMON YELL" and status == 1 and pokemon["species"] == 25) then - gui.text(Display.colToPixelX(col), Display.rowToPixelY(row + 2), "Friendship : " .. pokemon["friendship"]) + gui.text(Display.colToPixelX(colX), Display.rowToPixelY(rowY + 2), "Friendship : " .. pokemon["friendship"]) end end -- Display Hidden Power -function Display.hiddenPower(col, row, pokemon, table) - gui.text(Display.colToPixelX(col), Display.rowToPixelY(row), "H.Power") - gui.text(Display.colToPixelX(col + 8), Display.rowToPixelY(row), +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(col, row, pokemon, table) - gui.text(Display.colToPixelX(col), Display.rowToPixelY(row), "Moves:") +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(col), Display.rowToPixelY(row + i), + gui.text(Display.colToPixelX(colX), Display.rowToPixelY(rowY + i), table["move"][pokemon["move"][i]] .. " - " .. pokemon["pp"][i] .. "PP") end end From 028293026f75ed6af9e88443fe5071c3d8441ffd Mon Sep 17 00:00:00 2001 From: Samira Urzua Date: Thu, 8 Jan 2026 06:07:27 -0300 Subject: [PATCH 03/18] Major refactor, cleanup of variables and resolve some variable scope issues - Move state management and key press input handling to main script - Group and localize main script variables to prevent global scope issues - Migrate all rendering code to display.lua module - Improve variable names for better code clarity - Fix Gen 1-2 screen layout to eliminate overlapping text - Restore accidentally removed contest stats labels - Fix global variable conflict causing memory read functions to access incorrect locations --- data/display.lua | 171 +++++++++++++++++++++++++++++----------------- data/gen1data.lua | 1 - data/memory.lua | 46 ++----------- data/tables.lua | 10 --- ylingstats.lua | 120 ++++++++++++++++++++------------ 5 files changed, 193 insertions(+), 155 deletions(-) diff --git a/data/display.lua b/data/display.lua index 63e7e83..d03c37b 100644 --- a/data/display.lua +++ b/data/display.lua @@ -5,7 +5,7 @@ Display = {} local resolution = {x = 0, y = 0} -Display.GRID = {COLSX = 40, ROWSY = 16} +Display.GRID = {COLSX = 40, ROWSY = 17} Display.GRID.MAX_COLX = Display.GRID.COLSX - 1 Display.GRID.MAX_ROWY = Display.GRID.ROWSY - 1 @@ -17,31 +17,60 @@ 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) + error("colX must be in range [0, " .. Display.GRID.MAX_COLX .. "]" .. " value is: " .. colX, 2) end - return (resolution.x * colX) / Display.GRID.COLSX + 2 + return ((resolution.x) * 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) + error("rowY must be in range [0, " .. Display.GRID.MAX_ROWY .. "]" .. " value is: " .. rowY, 2) end - return (resolution.y * rowY) / Display.GRID.ROWSY + 2 + return ((resolution.y-2) * rowY) / Display.GRID.ROWSY + 2 end -function Display.mainRender(settings, pokemon, gen, version, status, mode, substatus, lastpid, more, table) +function Display.getRightAlignedColumn(text) + local textLength = string.len(text) + return Display.GRID.COLSX - textLength - 1 +end + +function Display.mainRender(pokemon, gen, version, state, lastpid, monitor, key, table) + if not pokemon or not pokemon["species"] then + Display.noPokemon() + return + end + --Help + Display.showHelp(key, state, table) + -- Prepare display variables - local tmpcolor = status == 1 and "green" or "red" + local tmpcolor = state.selectedPkmnSide == 1 and "green" or "red" -- Display pokemon stats - Display.statsDisplay(settings, pokemon, gen, mode, substatus, lastpid, table, tmpcolor) + Display.statsDisplay(pokemon, gen, state.mode, state.pokemonSlot, lastpid, table, tmpcolor) - -- Species - local g = gen <= 2 - gui.text(Display.colToPixelX(0), Display.rowToPixelY(g and 14 or 1), pokemon["species"] .. ":" .. pokemon["speciesname"], tmpcolor) + -- Status indicator + Display.statusIndicator((gen <= 2 and 17 or 0), (gen <= 2 and Display.GRID.MAX_ROWY or 0), state.pokemonSlot[1], state.mode, table, tmpcolor) + -- PID (Gen 3+) + if gen >= 3 then + gui.text(Display.colToPixelX(15), Display.rowToPixelY(Display.GRID.MAX_ROWY), "PID: " .. bit.tohex(lastpid)) + end + + -- Species display + if gen <= 2 then + local speciesText = pokemon["species"] .. ":" .. pokemon["speciesname"] + gui.text(Display.colToPixelX(Display.getRightAlignedColumn(speciesText)), Display.rowToPixelY(0), speciesText, tmpcolor) + else + gui.text(Display.colToPixelX(0), Display.rowToPixelY(1), pokemon["species"] .. ":" .. pokemon["speciesname"], tmpcolor) + end + -- HP display - gui.text(Display.colToPixelX(g and 0 or 11), Display.rowToPixelY(g and 13 or 0), "HP:" .. pokemon["hp"]["current"] .. "/" .. pokemon["hp"]["max"], tmpcolor) + if gen <= 2 then + local hpText = pokemon["hp"]["current"] .. "/" .. pokemon["hp"]["max"] .. ":HP" + gui.text(Display.colToPixelX(Display.getRightAlignedColumn(hpText)), Display.rowToPixelY(1), hpText, tmpcolor) + else + gui.text(Display.colToPixelX(11), Display.rowToPixelY(0), "HP:" .. pokemon["hp"]["current"] .. "/" .. pokemon["hp"]["max"], tmpcolor) + end -- Frame counter Display.frameCounter(Display.colToPixelX(0), Display.rowToPixelY(Display.GRID.MAX_ROWY), version) @@ -50,66 +79,80 @@ function Display.mainRender(settings, pokemon, gen, version, status, mode, subst gui.text(Display.colToPixelX(30), Display.rowToPixelY(Display.GRID.MAX_ROWY), pokemon["shiny"] == 1 and "Shiny" or "Not shiny", pokemon["shiny"] == 1 and "green" or "red") -- "More" menu - if more == 1 then + if state.more == 1 then local helditem = pokemon["helditem"] == 0 and "none" or table["items"][gen][pokemon["helditem"]] - Display.moreMenu(settings, pokemon, gen, version, status, table, helditem, lastpid) + Display.moreMenu(pokemon, gen, version, state, table, helditem, lastpid) end + + Display.performanceStats(monitor) end -- Display "no Pokemon" warning -function Display.noPokemon(settings) +function Display.noPokemon() gui.text(Display.colToPixelX(0), Display.rowToPixelY(0), "No Pokemon", "red") end -- Display help menu -function Display.showHelp(settings, table) +function Display.showHelp(key, state, table) + if state.help ~= 1 then + return + end gui.box(Display.colToPixelX(2) - 5, Display.rowToPixelY(3) - 5, Display.colToPixelX(39) - 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 = { {settings["key"].SWITCH_MODE, ": IVs, EVs, Stats and Contest stats", 5}, - {settings["key"].SWITCH_STATUS, ": Player team / Enemy team", 4}, - {settings["key"].SUB_STATUS, ": Pokemon slot (1-6)", 3}, - {settings["key"].TOGGLE_MORE, ": Show more data", 2}, - {settings["key"].TOGGLE_HELP, ": Toggle this menu", 1}} - + 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 -function Display.statsDisplay(settings, pokemon, gen, mode, substatus, lastpid, table, color) +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 labels = gen <= 2 and table["gen1labels"] or table["labels"] + local statsLabels = mode == 4 and contests or (gen <= 2 and gen1labels or labels) -- Get IV data based on generation - local ivData = pokemon[table["modesorder"][mode]] + local ivData = pokemon[modesorder[mode]] -- Display stats - for i = 1, statCount do - local colX = Display.GRID.COLSX - (i * 3) - - -- Label at top - gui.text(Display.colToPixelX(colX), Display.rowToPixelY(0), labels[statCount + 1 - i], table["colors"][statCount + 1 - i]) - - -- IV value one row below - local iv = ivData[statCount + 1 - i] - local ivText = iv == 31 and (iv .. "*") or iv - gui.text(Display.colToPixelX(colX), Display.rowToPixelY(1), ivText, table["colors"][statCount + 1 - i]) - - -- Nature indicator (Gen 3+, not for mode 4) - if gen >= 3 and mode ~= 4 then - Display.natureIndicator(colX, 1, i, pokemon, table) + 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) + gui.text(Display.colToPixelX(rightCol), Display.rowToPixelY(row+2), displayText, table["colors"][i]) + end + else + -- Gen 3+: Horizontal display + for i = 1, statCount do + local colX = Display.GRID.COLSX - (i * 3) + + -- Label at top + gui.text(Display.colToPixelX(colX), Display.rowToPixelY(0), statsLabels[statCount + 1 - i], table["colors"][statCount + 1 - i]) + + -- IV value one row below + local iv = ivData[statCount + 1 - i] + local ivText = mode == 1 and iv == 31 and (iv .. "*") or iv + gui.text(Display.colToPixelX(colX), Display.rowToPixelY(1), ivText, table["colors"][statCount + 1 - i]) + + -- Nature indicator (not for mode 4/contests) + if mode ~= 4 then + Display.natureIndicator(colX, 1, i, pokemon, table) + end end - end - - -- Status indicator - Display.statusIndicator((gen <= 2 and 15 or 0), (gen <= 2 and Display.GRID.MAX_ROWY or 0), substatus[1], mode, table, color) - - -- PID (Gen 3+) - if gen >= 3 then - gui.text(Display.colToPixelX(15), Display.rowToPixelY(Display.GRID.MAX_ROWY), "PID: " .. bit.tohex(lastpid)) end end @@ -119,20 +162,21 @@ function Display.natureIndicator(colX, rowY, statIndex, pokemon, table) if inc ~= dec then if statIndex == table["statsorder"][inc + 2] then - gui.text(Display.colToPixelX(colX)+1, Display.rowToPixelY(rowY)+2, "__", "green") + gui.text(Display.colToPixelX(colX), Display.rowToPixelY(rowY)+2, "__", "green") elseif statIndex == table["statsorder"][dec + 2] then - gui.text(Display.colToPixelX(colX)+1, Display.rowToPixelY(rowY)+2, "__", "red") + gui.text(Display.colToPixelX(colX), Display.rowToPixelY(rowY)+2, "__", "red") end else if statIndex == table["statsorder"][inc + 1] then - gui.text(Display.colToPixelX(colX)+1, Display.rowToPixelY(rowY)+2, "__", "grey") + gui.text(Display.colToPixelX(colX), Display.rowToPixelY(rowY)+2, "__", "grey") end end end -- Display status indicator (P1, P2, etc.) -function Display.statusIndicator(colX, rowY, substatus, mode, table, color) - local statusText = (color == "green" and "P" or "E") .. substatus .. " (" .. table["modes"][mode] .. ")" +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 @@ -148,13 +192,13 @@ function Display.frameCounter(x, y, version) end -- Display "More" menu with additional Pokemon details -function Display.moreMenu(settings, pokemon, gen, version, status, table, helditem, lastpid) +function Display.moreMenu(pokemon, gen, version, state, table, helditem, lastpid) gui.box(Display.colToPixelX(2) - 5, Display.rowToPixelY(3) - 5, Display.colToPixelX(39) - 5, Display.rowToPixelY(13) + 5, "#ffffcc", "#ffcc33") if gen >= 3 then Display.moreMenuGen3Plus(2, 3, pokemon, gen, table, helditem) else - Display.moreMenuGen12(2, 3, pokemon, gen, version, status, table, helditem) + Display.moreMenuGen12(2, 3, pokemon, gen, version, state, table, helditem) end -- Hidden Power and Moves (all gens) @@ -189,10 +233,10 @@ function Display.moreMenuGen3Plus(colX, rowY, pokemon, gen, table, helditem) end -- More menu details for Gen 1-2 -function Display.moreMenuGen12(colX, rowY, pokemon, gen, version, status, table, helditem) +function Display.moreMenuGen12(colX, rowY, pokemon, gen, version, state, table, helditem) gui.text(Display.colToPixelX(colX), Display.rowToPixelY(rowY), "TID: " .. pokemon["TID"] .. " / Item: " .. helditem) - if gen == 2 or (version == "POKEMON YELL" and status == 1 and pokemon["species"] == 25) then + 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 @@ -217,9 +261,12 @@ function Display.movesList(colX, rowY, pokemon, table) end -- Display performance stats (secret feature) -function Display.performanceStats(settings, lastclocktime, meanclocktime, highestclocktime, count) - gui.text(Display.colToPixelX(2), Display.rowToPixelY(6), "Last clock time: " .. numTruncate(lastclocktime * 1000, 2) .. "ms") - gui.text(Display.colToPixelX(2), Display.rowToPixelY(7), "Mean clock time: " .. numTruncate(meanclocktime * 1000, 2) .. "ms") - gui.text(Display.colToPixelX(2), Display.rowToPixelY(8), "Most clock time: " .. numTruncate(highestclocktime * 1000, 2) .. "ms") - gui.text(Display.colToPixelX(2), Display.rowToPixelY(9), "Data fetched: " .. count .. "x") +function Display.performanceStats(monitor) + if monitor.yling ~= 1 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/gen1data.lua b/data/gen1data.lua index e4a290f..2bbc8d7 100644 --- a/data/gen1data.lua +++ b/data/gen1data.lua @@ -21,5 +21,4 @@ table["gen1id"]={ 112,115,32,35,21,100,34,80,2,103, 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 diff --git a/data/memory.lua b/data/memory.lua index ef4b086..0ada972 100644 --- a/data/memory.lua +++ b/data/memory.lua @@ -91,40 +91,7 @@ function resolveBase(desc) return addr + (desc.add or 0) end -function statusChange(input) - if input[key.SWITCH_MODE] and not prev[key.SWITCH_MODE] then - local max = (gen <= 2) and 3 or 4 - mode = (mode < max) and (mode + 1) or 1 - end - if input[key.SWITCH_STATUS] and not prev[key.SWITCH_STATUS] then - status = (status == 1) and 2 or 1 - end - if input[key.SUB_STATUS] and not prev[key.SUB_STATUS] then - if gen <= 2 and status == 2 then - substatus[2] = 1 - else - substatus[status] = (substatus[status] < 6) and (substatus[status] + 1) or 1 - end - end - if input[key.TOGGLE_MORE] and not prev[key.TOGGLE_MORE] then - help = 0 - more = (more == 1) and 0 or 1 - end - if input[key.TOGGLE_HELP] and not prev[key.TOGGLE_HELP] then - more = 0 - help = (help == 1) and 0 or 1 - 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 - end - prev = input -end - -function checkLast(pid,checksum,start,gen) -- Compares pid and checksum with current pid and checksum +function checkLast(pid, checksum, start, gen, selectedPkmnSide) -- Compares pid and checksum with current pid and checksum local mdword=memory.readdwordunsigned local mword=memory.readwordunsigned local mbyte=memory.readbyteunsigned @@ -136,7 +103,7 @@ function checkLast(pid,checksum,start,gen) -- Compares pid and checksum with cur 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) @@ -150,7 +117,6 @@ function checkLast(pid,checksum,start,gen) -- Compares pid and checksum with cur return 1 else return 0 - end end @@ -164,7 +130,7 @@ local function shinyValue(p) ) end -function fetchPokemon(start) -- Fetches Pokemon info from memory and returns a table with all the data +function fetchPokemon(start, gen, selectedPkmnSide) -- 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 @@ -197,7 +163,7 @@ function fetchPokemon(start) -- Fetches Pokemon info from memory and returns a t 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"]={} @@ -268,7 +234,7 @@ function fetchPokemon(start) -- Fetches Pokemon info from memory and returns a t pokemon["shiny"] = 0 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 @@ -281,7 +247,7 @@ function fetchPokemon(start) -- Fetches Pokemon info from memory and returns a t 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) diff --git a/data/tables.lua b/data/tables.lua index a7f060a..785146c 100644 --- a/data/tables.lua +++ b/data/tables.lua @@ -27,23 +27,13 @@ dofile "data/pokemondata.lua" -- Pokemon names, abilities, moves -- Consoles resolutions by gen (to display things right on every game) table["resolutions"] = {gameBoyColor = {x=160, y=144}, gameBoyAdvance = {x=240, y=160}, nintendoDS = {x=256, y=192}} -key = { SWITCH_MODE = "J", -- Switch mode (EV,IV,Stats) - SWITCH_STATUS = "K", -- Switch status (Enemy / player) - SUB_STATUS = "L", -- Sub Status + (Pokemon Slot) - TOGGLE_MORE = "M", -- Show more data - TOGGLE_HELP = "H"} -- Toggle help - -- 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 06e5142..4df5fbd 100644 --- a/ylingstats.lua +++ b/ylingstats.lua @@ -13,7 +13,7 @@ dofile "data/memory.lua" -- Functions and Pokemon table generation dofile "data/display.lua" -- Display module local gamedata = getGameInfo() -- Gets game info -version, lan, gen, resolution = gamedata[1], gamedata[2], gamedata[3], gamedata[4] +local version, lan, gen, resolution = gamedata[1], gamedata[2], gamedata[3], gamedata[4] Display.setResolution(resolution) -- Note : "O" seems to be the letter used in DSi enhanced games @@ -41,74 +41,110 @@ end print("Welcome to yPokeStats Unified ") print("Game :", games[version][lan][1]) -settings={} -settings["key"] = { SWITCH_MODE = "J", -- Switch mode (EV,IV,Stats) - SWITCH_STATUS = "K", -- Switch status (Enemy / player) - SUB_STATUS = "L", -- Sub Status + (Pokemon Slot) - TOGGLE_MORE = "M", -- Show more data - TOGGLE_HELP = "H"} -- Toggle help +pkmnSide = {PLAYER = 1, ENEMY = 2} -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 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 + +local state = {selectedPkmnSide = pkmnSide.PLAYER, mode = 3, help = 0, more = 0, pokemonSlot = {1, 1}} +local cache = {lastpid = 0, lastchecksum = 0, lastPokemonSide = 1} +local pokemon = nil +local monitor = {yling = 0, count = 0, clockcount = 0, totalclocktime = 0, lastclocktime = 0, highestclocktime = 0, meanclocktime = 0} 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 -- useless) + nClock = os.clock() -- Set the clock (for performance monitoring) statusChange(input.get()) -- Check for key input and changes status - if help==1 then -- Help screen display - Display.showHelp(settings, table) + -- Clear pokemon data when status changes to prevent displaying wrong pokemon + if cache.lastPokemonSide ~= state.selectedPkmnSide then + pokemon = nil end + cache.lastPokemonSide = state.selectedPkmnSide - if status == 1 then -- Resolve pokemon memory address + -- 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 * (substatus[status] - 1) + 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 checkLast(lastpid, lastchecksum, start, gen) == 0 or not pokemon or not pokemon["species"] then - local fetched = fetchPokemon(start) - count = count + 1 + if checkLast(cache.lastpid, cache.lastchecksum, start, gen, state.selectedPkmnSide) == 0 or not pokemon 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 -- Check that stats loaded correctly + if fetched["hp"] and fetched["hp"]["max"] > 0 and fetched["hp"]["max"] < 1000 then pokemon = fetched if gen >= 3 then - lastpid = pokemon["pid"] - lastchecksum = pokemon["checksum"] + cache.lastpid = pokemon["pid"] + cache.lastchecksum = pokemon["checksum"] else - lastpid = pokemon["species"] - lastchecksum = pokemon["ivs"] + cache.lastpid = pokemon["species"] + cache.lastchecksum = pokemon["ivs"] end end end end - Display.mainRender(settings, pokemon, gen, version, status, mode, substatus, lastpid, more, table) - else -- No PID found - if status == 1 then -- If player team just reset to slot 1 - substatus[1] = 1 - else -- If enemy team just reset to slot 1 - substatus[2] = 1 + else + -- No PID found - reset pokemonSlot + if state.selectedPkmnSide == pkmnSide.PLAYER then + state.pokemonSlot[1] = 1 + else + state.pokemonSlot[2] = 1 end - Display.noPokemon(settings) 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) - Display.performanceStats(settings, lastclocktime, meanclocktime, highestclocktime, count) - end + -- 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 + + -- Render + Display.mainRender(pokemon, gen, version, state, cache.lastpid, monitor, key, table) end -gui.register(main) \ No newline at end of file +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 = 0 + state.more = (state.more == 1) and 0 or 1 + end + if input[key.TOGGLE_HELP] and not prev[key.TOGGLE_HELP] then + state.more = 0 + state.help = (state.help == 1) and 0 or 1 + end + if input["Y"] and not prev["Y"] then + state.more = 0 + state.help = 0 + monitor.yling = (monitor.yling == 1) and 0 or 1 + end + prev = input +end \ No newline at end of file From 7171b18301c32a300f9fdca95a361902eb1501b8 Mon Sep 17 00:00:00 2001 From: Samira Urzua Date: Thu, 8 Jan 2026 06:47:35 -0300 Subject: [PATCH 04/18] Minor UI adjustments to improve display, made screen margins consistant and aligned shiny value to the right. --- data/display.lua | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/data/display.lua b/data/display.lua index d03c37b..a8f784b 100644 --- a/data/display.lua +++ b/data/display.lua @@ -5,7 +5,7 @@ Display = {} local resolution = {x = 0, y = 0} -Display.GRID = {COLSX = 40, ROWSY = 17} +Display.GRID = {COLSX = 38, ROWSY = 17} Display.GRID.MAX_COLX = Display.GRID.COLSX - 1 Display.GRID.MAX_ROWY = Display.GRID.ROWSY - 1 @@ -19,7 +19,7 @@ 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) * colX) / Display.GRID.COLSX + 2 + return ((resolution.x-2) * colX) / Display.GRID.COLSX + 2 end function Display.rowToPixelY(rowY) @@ -31,7 +31,7 @@ end function Display.getRightAlignedColumn(text) local textLength = string.len(text) - return Display.GRID.COLSX - textLength - 1 + return Display.GRID.COLSX - textLength end function Display.mainRender(pokemon, gen, version, state, lastpid, monitor, key, table) @@ -76,7 +76,8 @@ function Display.mainRender(pokemon, gen, version, state, lastpid, monitor, key, Display.frameCounter(Display.colToPixelX(0), Display.rowToPixelY(Display.GRID.MAX_ROWY), version) -- Shiny status - gui.text(Display.colToPixelX(30), Display.rowToPixelY(Display.GRID.MAX_ROWY), pokemon["shiny"] == 1 and "Shiny" or "Not shiny", pokemon["shiny"] == 1 and "green" or "red") + shinyText = pokemon["shiny"] == 1 and "Shiny" or "Not shiny" + gui.text(Display.colToPixelX(Display.getRightAlignedColumn(shinyText)), Display.rowToPixelY(Display.GRID.MAX_ROWY), shinyText, pokemon["shiny"] == 1 and "green" or "red") -- "More" menu if state.more == 1 then @@ -97,7 +98,7 @@ function Display.showHelp(key, state, table) if state.help ~= 1 then return end - gui.box(Display.colToPixelX(2) - 5, Display.rowToPixelY(3) - 5, Display.colToPixelX(39) - 5, Display.rowToPixelY(12) + 5, "#ffffcc", "#ffcc33") + 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") @@ -193,7 +194,7 @@ end -- Display "More" menu with additional Pokemon details function Display.moreMenu(pokemon, gen, version, state, table, helditem, lastpid) - gui.box(Display.colToPixelX(2) - 5, Display.rowToPixelY(3) - 5, Display.colToPixelX(39) - 5, Display.rowToPixelY(13) + 5, "#ffffcc", "#ffcc33") + 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, helditem) From 459abf7f86fbe5ce91069a06fb969c6ea258e4e1 Mon Sep 17 00:00:00 2001 From: Samira Urzua Date: Sat, 10 Jan 2026 16:32:22 -0300 Subject: [PATCH 05/18] Fix: Keep rendering the whole UI when a pokemon is not available --- data/display.lua | 84 +++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 43 deletions(-) diff --git a/data/display.lua b/data/display.lua index a8f784b..f9d3b40 100644 --- a/data/display.lua +++ b/data/display.lua @@ -35,62 +35,52 @@ function Display.getRightAlignedColumn(text) end function Display.mainRender(pokemon, gen, version, state, lastpid, monitor, key, table) - if not pokemon or not pokemon["species"] then - Display.noPokemon() - return - end - --Help - Display.showHelp(key, state, table) - - -- Prepare display variables + -- 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) + Display.performanceStats(monitor) - -- Display pokemon stats + -- 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) - -- Status indicator - Display.statusIndicator((gen <= 2 and 17 or 0), (gen <= 2 and Display.GRID.MAX_ROWY or 0), state.pokemonSlot[1], state.mode, table, tmpcolor) - -- PID (Gen 3+) if gen >= 3 then - gui.text(Display.colToPixelX(15), Display.rowToPixelY(Display.GRID.MAX_ROWY), "PID: " .. bit.tohex(lastpid)) - end - - -- Species display - if gen <= 2 then - local speciesText = pokemon["species"] .. ":" .. pokemon["speciesname"] - gui.text(Display.colToPixelX(Display.getRightAlignedColumn(speciesText)), Display.rowToPixelY(0), speciesText, tmpcolor) - else - gui.text(Display.colToPixelX(0), Display.rowToPixelY(1), pokemon["species"] .. ":" .. pokemon["speciesname"], tmpcolor) + 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 - local hpText = pokemon["hp"]["current"] .. "/" .. pokemon["hp"]["max"] .. ":HP" - gui.text(Display.colToPixelX(Display.getRightAlignedColumn(hpText)), Display.rowToPixelY(1), hpText, tmpcolor) + gui.text(Display.colToPixelX(Display.getRightAlignedColumn(hpText)), Display.rowToPixelY(1), hpText, pokemon and tmpcolor or "red") else - gui.text(Display.colToPixelX(11), Display.rowToPixelY(0), "HP:" .. pokemon["hp"]["current"] .. "/" .. pokemon["hp"]["max"], tmpcolor) + gui.text(Display.colToPixelX(11), Display.rowToPixelY(0), hpText, pokemon and tmpcolor or "red") end - -- Frame counter - Display.frameCounter(Display.colToPixelX(0), Display.rowToPixelY(Display.GRID.MAX_ROWY), version) - - -- Shiny status - shinyText = pokemon["shiny"] == 1 and "Shiny" or "Not shiny" - gui.text(Display.colToPixelX(Display.getRightAlignedColumn(shinyText)), Display.rowToPixelY(Display.GRID.MAX_ROWY), shinyText, pokemon["shiny"] == 1 and "green" or "red") - - -- "More" menu - if state.more == 1 then - local helditem = pokemon["helditem"] == 0 and "none" or table["items"][gen][pokemon["helditem"]] - Display.moreMenu(pokemon, gen, version, state, table, helditem, lastpid) + -- Toggles if pokemon + if pokemon then + -- Shiny status + shinyText = pokemon["shiny"] == 1 and "Shiny" or "Not shiny" + gui.text(Display.colToPixelX(Display.getRightAlignedColumn(shinyText)), Display.rowToPixelY(Display.GRID.MAX_ROWY), shinyText, pokemon["shiny"] == 1 and "green" or "red") + + -- "More" menu + if state.more == 1 then + local helditem = pokemon["helditem"] == 0 and "none" or table["items"][gen][pokemon["helditem"]] + Display.moreMenu(pokemon, gen, version, state, table, helditem, lastpid) + end end - Display.performanceStats(monitor) -end --- Display "no Pokemon" warning -function Display.noPokemon() - gui.text(Display.colToPixelX(0), Display.rowToPixelY(0), "No Pokemon", "red") end -- Display help menu @@ -121,8 +111,16 @@ function Display.statsDisplay(pokemon, gen, mode, pokemonSlot, lastpid, table, c 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 - local ivData = pokemon[modesorder[mode]] + -- 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 @@ -150,7 +148,7 @@ function Display.statsDisplay(pokemon, gen, mode, pokemonSlot, lastpid, table, c gui.text(Display.colToPixelX(colX), Display.rowToPixelY(1), ivText, table["colors"][statCount + 1 - i]) -- Nature indicator (not for mode 4/contests) - if mode ~= 4 then + if pokemon and mode ~= 4 then Display.natureIndicator(colX, 1, i, pokemon, table) end end From ee759766f3dcb62accea1ac0a0d562332d5e1763 Mon Sep 17 00:00:00 2001 From: Samira Urzua Date: Sat, 10 Jan 2026 16:34:19 -0300 Subject: [PATCH 06/18] Triggers a fetch pokemon when it detects a current HP change --- data/memory.lua | 74 ++++++++++++++++++++++++++++++++----------------- ylingstats.lua | 2 +- 2 files changed, 49 insertions(+), 27 deletions(-) diff --git a/data/memory.lua b/data/memory.lua index 0ada972..21eb28e 100644 --- a/data/memory.lua +++ b/data/memory.lua @@ -91,33 +91,55 @@ function resolveBase(desc) return addr + (desc.add or 0) end -function checkLast(pid, checksum, start, gen, selectedPkmnSide) -- 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 - - if gen <= 2 then - currentpid = gen == 1 and table["gen1id"][mbyte(start)] or mbyte(start) - 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) - end - else - currentpid = mdword(start) - currentchecksum = gen == 3 and mdword(start+6) or mword(start+6) - end +function isPokemonChanged(pid, checksum, start, gen, selectedPkmnSide, previousHP) + local mdword = memory.readdwordunsigned + local mword = memory.readwordunsigned + local mbyte = memory.readbyteunsigned + local bnd, bxr = bit.band, bit.bxor + local rshift = bit.rshift + + local currentpid, currentchecksum, currentHP + if gen <= 2 then + currentpid = gen == 1 and table["gen1id"][mbyte(start)] or mbyte(start) + if selectedPkmnSide == pkmnSide.PLAYER then + currentchecksum = gen == 1 and mword(start+0x1B) or mword(start+0x15) + 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 - if lastpid == currentpid and lastchecksum == currentchecksum then - return 1 - else - return 0 - end + elseif gen == 3 then + currentpid = mdword(start) + currentchecksum = mdword(start+6) + currentHP = mword(start+86) + else + currentpid = mdword(start) + currentchecksum = mword(start+6) + + local function mult32(a,b) + 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 + + 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 local function shinyValue(p) diff --git a/ylingstats.lua b/ylingstats.lua index 4df5fbd..74fbf72 100644 --- a/ylingstats.lua +++ b/ylingstats.lua @@ -78,7 +78,7 @@ function main() -- Main function - display (check memory.lua for calculations) -- Fetch pokemon data if valid memory found if start and (memory.readdwordunsigned(start) ~= 0 or memory.readbyteunsigned(start) ~= 0) then - if checkLast(cache.lastpid, cache.lastchecksum, start, gen, state.selectedPkmnSide) == 0 or not pokemon or not pokemon["species"] 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 From d5eb092df478aa4df2a3d34ce4a002e3bc6efa06 Mon Sep 17 00:00:00 2001 From: Samira Urzua Date: Mon, 12 Jan 2026 16:42:33 -0300 Subject: [PATCH 07/18] memory.lua cleanup, localize variables and remove repeated code --- data/display.lua | 2 +- data/memory.lua | 78 ++++++++++++++++++++---------------------------- 2 files changed, 33 insertions(+), 47 deletions(-) diff --git a/data/display.lua b/data/display.lua index f9d3b40..790aedf 100644 --- a/data/display.lua +++ b/data/display.lua @@ -64,7 +64,7 @@ function Display.mainRender(pokemon, gen, version, state, lastpid, monitor, key, 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(11), Display.rowToPixelY(0), hpText, pokemon and tmpcolor or "red") + gui.text(Display.colToPixelX(10), Display.rowToPixelY(0), hpText, pokemon and tmpcolor or "red") end -- Toggles if pokemon diff --git a/data/memory.lua b/data/memory.lua index 21eb28e..b3acafb 100644 --- a/data/memory.lua +++ b/data/memory.lua @@ -1,5 +1,14 @@ --- Functions and variables definition -function bts(bytes,l) -- Bytes array to string +-- 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]) @@ -8,13 +17,13 @@ function bts(bytes,l) -- Bytes array to string return name end -function numTruncate(x,n) -- Truncate to n decimals +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 -function hasValue (tab, val) +local function hasValue (tab, val) for index, value in ipairs(tab) do if value == val then return true @@ -24,6 +33,23 @@ function hasValue (tab, val) return false end +local function getbits(a,b,d) -- Get bits (kinda obvious right ?) + return rshift(a,b)%lshift(1,d) +end + +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, resolution if string.find(bts(memory.readbyterange(0x0134, 12),12), "POKEMON") or string.find(bts(memory.readbyterange(0x0134, 12),12), "PM") then @@ -92,12 +118,7 @@ function resolveBase(desc) end function isPokemonChanged(pid, checksum, start, gen, selectedPkmnSide, previousHP) - local mdword = memory.readdwordunsigned - local mword = memory.readwordunsigned - local mbyte = memory.readbyteunsigned - local bnd, bxr = bit.band, bit.bxor - local rshift = bit.rshift - + local prng local currentpid, currentchecksum, currentHP if gen <= 2 then currentpid = gen == 1 and table["gen1id"][mbyte(start)] or mbyte(start) @@ -120,16 +141,7 @@ function isPokemonChanged(pid, checksum, start, gen, selectedPkmnSide, previousH else currentpid = mdword(start) currentchecksum = mword(start+6) - - local function mult32(a,b) - 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 - + local prng = currentpid for i = 0x88, 0x8E, 2 do prng = mult32(prng,0x41C64E6D) + 0x6073 @@ -153,33 +165,7 @@ local function shinyValue(p) end function fetchPokemon(start, gen, selectedPkmnSide) -- 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 - end - - function gettop(a) -- Rshift for data decryption - return(rshift(a,16)) - end - if gen == 1 then -- -Gen 1 routine local pokemon={} pokemon["hp"] = {} From cff5a1ac55918c01d7a4adabbcc976a209d34b11 Mon Sep 17 00:00:00 2001 From: Samira Urzua Date: Mon, 12 Jan 2026 18:45:36 -0300 Subject: [PATCH 08/18] Remove pokemon comment since it's not longer needed --- data/gen1data.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/data/gen1data.lua b/data/gen1data.lua index 2bbc8d7..4ba7172 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, From 5e49a3340f979116bda784cc0d6d5025fdb634b6 Mon Sep 17 00:00:00 2001 From: Samira Urzua Date: Mon, 12 Jan 2026 18:56:06 -0300 Subject: [PATCH 09/18] Normalize indentation to tabs and open help window by default --- data/memory.lua | 60 ++++++++++++++++++++++++------------------------- ylingstats.lua | 2 +- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/data/memory.lua b/data/memory.lua index b3acafb..10ab966 100644 --- a/data/memory.lua +++ b/data/memory.lua @@ -119,39 +119,39 @@ end 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 selectedPkmnSide == pkmnSide.PLAYER then - currentchecksum = gen == 1 and mword(start+0x1B) or mword(start+0x15) - 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 + local currentpid, currentchecksum, currentHP + if gen <= 2 then + currentpid = gen == 1 and table["gen1id"][mbyte(start)] or mbyte(start) + if selectedPkmnSide == pkmnSide.PLAYER then + currentchecksum = gen == 1 and mword(start+0x1B) or mword(start+0x15) + 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 = mword(start+6) + elseif gen == 3 then + currentpid = mdword(start) + currentchecksum = mdword(start+6) + currentHP = mword(start+86) + else + currentpid = mdword(start) + currentchecksum = mword(start+6) - 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 + 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 + return pid ~= currentpid or checksum ~= currentchecksum or previousHP ~= currentHP end local function shinyValue(p) diff --git a/ylingstats.lua b/ylingstats.lua index 74fbf72..a5b2b0c 100644 --- a/ylingstats.lua +++ b/ylingstats.lua @@ -78,7 +78,7 @@ function main() -- Main function - display (check memory.lua for calculations) -- 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 + 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 From e58f5f6a2eedc71cb2357654db9466e71be972b6 Mon Sep 17 00:00:00 2001 From: Samira Urzua Date: Mon, 12 Jan 2026 20:17:17 -0300 Subject: [PATCH 10/18] Refactor: change numeric flags (1/0) to boolean --- data/display.lua | 12 ++++++------ data/memory.lua | 35 +++++++++-------------------------- ylingstats.lua | 20 ++++++++++---------- 3 files changed, 25 insertions(+), 42 deletions(-) diff --git a/data/display.lua b/data/display.lua index 790aedf..ac20c9f 100644 --- a/data/display.lua +++ b/data/display.lua @@ -70,11 +70,11 @@ function Display.mainRender(pokemon, gen, version, state, lastpid, monitor, key, -- Toggles if pokemon if pokemon then -- Shiny status - shinyText = pokemon["shiny"] == 1 and "Shiny" or "Not shiny" - gui.text(Display.colToPixelX(Display.getRightAlignedColumn(shinyText)), Display.rowToPixelY(Display.GRID.MAX_ROWY), shinyText, pokemon["shiny"] == 1 and "green" or "red") + 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 == 1 then + if state.more then local helditem = pokemon["helditem"] == 0 and "none" or table["items"][gen][pokemon["helditem"]] Display.moreMenu(pokemon, gen, version, state, table, helditem, lastpid) end @@ -85,7 +85,7 @@ end -- Display help menu function Display.showHelp(key, state, table) - if state.help ~= 1 then + 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") @@ -214,7 +214,7 @@ function Display.moreMenuGen3Plus(colX, rowY, pokemon, gen, table, helditem) 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 pokerus = pokemon["pokerus"] == 0 and "no" or "yes" + local pokerus = not pokemon["pokerus"] and "no" or "yes" local details = { {"OT ID : " .. pokemon["OTTID"]}, @@ -261,7 +261,7 @@ end -- Display performance stats (secret feature) function Display.performanceStats(monitor) - if monitor.yling ~= 1 then + if not monitor.yling then return end gui.text(Display.colToPixelX(2), Display.rowToPixelY(6), "Last clock time: " .. numTruncate(monitor.lastclocktime * 1000, 2) .. "ms") diff --git a/data/memory.lua b/data/memory.lua index 10ab966..971aaae 100644 --- a/data/memory.lua +++ b/data/memory.lua @@ -233,13 +233,9 @@ function fetchPokemon(start, gen, selectedPkmnSide) -- Fetches Pokemon info from -- 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 - else - pokemon["shiny"] = 0 - 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 selectedPkmnSide == pkmnSide.PLAYER then @@ -271,7 +267,7 @@ function fetchPokemon(start, gen, selectedPkmnSide) -- Fetches Pokemon info from 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["pokerus"] = mbyte(0x1C) ~= 0 pokemon["hp"]={} pokemon["hp"]["current"]=0x100*mbyte(start+0x22) + mbyte(start+0x23) pokemon["hp"]["max"]=0x100*mbyte(start+0x24) + mbyte(start+0x25) @@ -294,7 +290,7 @@ function fetchPokemon(start, gen, selectedPkmnSide) -- Fetches Pokemon info from 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["pokerus"]=false pokemon["stats"]={} pokemon["stats"][1]=pokemon["hp"]["max"] pokemon["stats"][2]=0x100*mbyte(start+0x14) + mbyte(start+0x15) @@ -311,13 +307,9 @@ function fetchPokemon(start, gen, selectedPkmnSide) -- Fetches Pokemon info from -- 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] - 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 - else - pokemon["shiny"] = 0 - 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 return pokemon @@ -360,18 +352,14 @@ function fetchPokemon(start, gen, selectedPkmnSide) -- Fetches Pokemon info from 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["pokerus"]=(getbits(pokemon["misc"][1],0,8) ~= 0) 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) - if shinyValue(pokemon) < 8 then - pokemon["shiny"] = 1 - else - pokemon["shiny"] = 0 - end + pokemon["shiny"] = (shinyValue(pokemon) < 8) return pokemon elseif gen >= 4 then -- Routine for gens 4 and 5 @@ -444,12 +432,7 @@ function fetchPokemon(start, gen, selectedPkmnSide) -- Fetches Pokemon info from lastpid = pokemon["pid"] -- Gen 4+ Shiny Calculation (PID/SID Based) - if shinyValue(pokemon) < 8 then - pokemon["shiny"] = 1 - else - pokemon["shiny"] = 0 - end - + pokemon["shiny"] = (shinyValue(pokemon) < 8) return pokemon end end \ No newline at end of file diff --git a/ylingstats.lua b/ylingstats.lua index a5b2b0c..598e906 100644 --- a/ylingstats.lua +++ b/ylingstats.lua @@ -49,10 +49,10 @@ local key = { SWITCH_MODE = "J", -- Switch mode (EV,IV,Stats) TOGGLE_MORE = "M", -- Show more data TOGGLE_HELP = "H"} -- Toggle help -local state = {selectedPkmnSide = pkmnSide.PLAYER, mode = 3, help = 0, more = 0, pokemonSlot = {1, 1}} -local cache = {lastpid = 0, lastchecksum = 0, lastPokemonSide = 1} +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 = 0, count = 0, clockcount = 0, totalclocktime = 0, lastclocktime = 0, highestclocktime = 0, meanclocktime = 0} +local monitor = {yling = false, count = 0, clockcount = 0, totalclocktime = 0, lastclocktime = 0, highestclocktime = 0, meanclocktime = 0} local prev = input.get() -- Preparing the input tables - allows to check if a key has been pressed @@ -134,17 +134,17 @@ function statusChange(input) end end if input[key.TOGGLE_MORE] and not prev[key.TOGGLE_MORE] then - state.help = 0 - state.more = (state.more == 1) and 0 or 1 + state.help = false + state.more = not state.more end if input[key.TOGGLE_HELP] and not prev[key.TOGGLE_HELP] then - state.more = 0 - state.help = (state.help == 1) and 0 or 1 + state.more = false + state.help = not state.help end if input["Y"] and not prev["Y"] then - state.more = 0 - state.help = 0 - monitor.yling = (monitor.yling == 1) and 0 or 1 + state.more = false + state.help = false + monitor.yling = not monitor.yling end prev = input end \ No newline at end of file From 2d1c571fb773fbceb2247e63dba7b4252e7a2bde Mon Sep 17 00:00:00 2001 From: Samira Urzua Date: Mon, 12 Jan 2026 20:19:21 -0300 Subject: [PATCH 11/18] Fix: relocate numTruncate function to fix crash when displaying performance stats --- data/display.lua | 6 ++++++ data/memory.lua | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/data/display.lua b/data/display.lua index ac20c9f..b54f320 100644 --- a/data/display.lua +++ b/data/display.lua @@ -259,6 +259,12 @@ function Display.movesList(colX, rowY, pokemon, table) 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 diff --git a/data/memory.lua b/data/memory.lua index 971aaae..8aab438 100644 --- a/data/memory.lua +++ b/data/memory.lua @@ -17,12 +17,6 @@ local function bts(bytes,l) -- Bytes array to string return name 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 - local function hasValue (tab, val) for index, value in ipairs(tab) do if value == val then From 5cb391c7a885ef4d9e222af8f9569822e6b640b1 Mon Sep 17 00:00:00 2001 From: Samira Urzua Date: Mon, 12 Jan 2026 20:35:12 -0300 Subject: [PATCH 12/18] Fix: Include rendering time on the performance calculations --- data/display.lua | 1 - ylingstats.lua | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/data/display.lua b/data/display.lua index b54f320..f6f6f8b 100644 --- a/data/display.lua +++ b/data/display.lua @@ -40,7 +40,6 @@ function Display.mainRender(pokemon, gen, version, state, lastpid, monitor, key, 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) - Display.performanceStats(monitor) -- Pokemon data -- if pokemon then diff --git a/ylingstats.lua b/ylingstats.lua index 598e906..ce949d2 100644 --- a/ylingstats.lua +++ b/ylingstats.lua @@ -104,6 +104,9 @@ function main() -- Main function - display (check memory.lua for calculations) end end + -- Render + Display.mainRender(pokemon, gen, version, state, cache.lastpid, monitor, key, table) + -- Script performance monitoring clocktime = os.clock() - nClock monitor.clockcount = monitor.clockcount + 1 @@ -112,8 +115,7 @@ function main() -- Main function - display (check memory.lua for calculations) monitor.highestclocktime = clocktime > monitor.highestclocktime and clocktime or monitor.highestclocktime monitor.meanclocktime = monitor.totalclocktime / monitor.clockcount - -- Render - Display.mainRender(pokemon, gen, version, state, cache.lastpid, monitor, key, table) + Display.performanceStats(monitor) end gui.register(main) From 0311fe20c339c68bab29809537b0564080d63ded Mon Sep 17 00:00:00 2001 From: Samira Urzua Date: Wed, 14 Jan 2026 02:59:27 -0300 Subject: [PATCH 13/18] Fixed a bug that caused the pokemon nature indicator to display over the wrong stat --- data/display.lua | 49 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/data/display.lua b/data/display.lua index f6f6f8b..1adcde5 100644 --- a/data/display.lua +++ b/data/display.lua @@ -131,46 +131,45 @@ function Display.statsDisplay(pokemon, gen, mode, pokemonSlot, lastpid, table, c local displayText = ivText .. ":" .. statsLabels[i] local rightCol = Display.getRightAlignedColumn(displayText) - gui.text(Display.colToPixelX(rightCol), Display.rowToPixelY(row+2), displayText, table["colors"][i]) + 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 - for i = 1, statCount do - local colX = Display.GRID.COLSX - (i * 3) + -- 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[statCount + 1 - i], table["colors"][statCount + 1 - i]) + gui.text(Display.colToPixelX(colX), Display.rowToPixelY(0), statsLabels[i], table["colors"][i]) -- IV value one row below - local iv = ivData[statCount + 1 - i] + 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"][statCount + 1 - i]) + 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 - Display.natureIndicator(colX, 1, i, pokemon, table) + 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 nature stat modifiers (green for boost, red for reduction, grey for neutral) -function Display.natureIndicator(colX, rowY, statIndex, pokemon, table) - local inc, dec = pokemon["nature"]["inc"], pokemon["nature"]["dec"] - - if inc ~= dec then - if statIndex == table["statsorder"][inc + 2] then - gui.text(Display.colToPixelX(colX), Display.rowToPixelY(rowY)+2, "__", "green") - elseif statIndex == table["statsorder"][dec + 2] then - gui.text(Display.colToPixelX(colX), Display.rowToPixelY(rowY)+2, "__", "red") - end - else - if statIndex == table["statsorder"][inc + 1] then - gui.text(Display.colToPixelX(colX), Display.rowToPixelY(rowY)+2, "__", "grey") - end - end -end - -- Display status indicator (P1, P2, etc.) function Display.statusIndicator(colX, rowY, pokemonSlot, mode, table, color) statModes = {"IVs", "EVs", "Stats", "Cont."} From 374d176e31d70b246f272a47819bf2036f302f4e Mon Sep 17 00:00:00 2001 From: Samira Urzua Date: Wed, 14 Jan 2026 03:30:10 -0300 Subject: [PATCH 14/18] =?UTF-8?q?Remove=20held=20items=20from=20Gen=201=20?= =?UTF-8?q?Pok=C3=A9mon=20(they=20cannot=20hold=20items)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/display.lua | 21 +++++++++++++-------- data/gen1data.lua | 3 +-- data/memory.lua | 2 -- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/data/display.lua b/data/display.lua index 1adcde5..9b32d34 100644 --- a/data/display.lua +++ b/data/display.lua @@ -74,8 +74,7 @@ function Display.mainRender(pokemon, gen, version, state, lastpid, monitor, key, -- "More" menu if state.more then - local helditem = pokemon["helditem"] == 0 and "none" or table["items"][gen][pokemon["helditem"]] - Display.moreMenu(pokemon, gen, version, state, table, helditem, lastpid) + Display.moreMenu(pokemon, gen, version, state, table) end end @@ -189,13 +188,13 @@ function Display.frameCounter(x, y, version) end -- Display "More" menu with additional Pokemon details -function Display.moreMenu(pokemon, gen, version, state, table, helditem, lastpid) +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, helditem) + Display.moreMenuGen3Plus(2, 3, pokemon, gen, table) else - Display.moreMenuGen12(2, 3, pokemon, gen, version, state, table, helditem) + Display.moreMenuGen12(2, 3, pokemon, gen, version, state, table) end -- Hidden Power and Moves (all gens) @@ -204,7 +203,8 @@ function Display.moreMenu(pokemon, gen, version, state, table, helditem, lastpid end -- More menu details for Gen 3-5 -function Display.moreMenuGen3Plus(colX, rowY, pokemon, gen, table, helditem) +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] @@ -230,8 +230,13 @@ function Display.moreMenuGen3Plus(colX, rowY, pokemon, gen, table, helditem) end -- More menu details for Gen 1-2 -function Display.moreMenuGen12(colX, rowY, pokemon, gen, version, state, table, helditem) - gui.text(Display.colToPixelX(colX), Display.rowToPixelY(rowY), "TID: " .. pokemon["TID"] .. " / Item: " .. helditem) +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"]) diff --git a/data/gen1data.lua b/data/gen1data.lua index 4ba7172..fe6579d 100644 --- a/data/gen1data.lua +++ b/data/gen1data.lua @@ -19,5 +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["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 8aab438..1ea92d7 100644 --- a/data/memory.lua +++ b/data/memory.lua @@ -186,7 +186,6 @@ function fetchPokemon(start, gen, selectedPkmnSide) -- Fetches Pokemon info from 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" @@ -203,7 +202,6 @@ function fetchPokemon(start, gen, selectedPkmnSide) -- Fetches Pokemon info from 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"]] From 51e7f27713fc6fed77350cd952ad085679873847 Mon Sep 17 00:00:00 2001 From: Samira Urzua Date: Wed, 14 Jan 2026 05:38:08 -0300 Subject: [PATCH 15/18] Feature: Added a detailed Pokerus display --- data/display.lua | 8 +++++++- data/memory.lua | 22 +++++++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/data/display.lua b/data/display.lua index 9b32d34..a98afed 100644 --- a/data/display.lua +++ b/data/display.lua @@ -212,7 +212,13 @@ function Display.moreMenuGen3Plus(colX, rowY, pokemon, gen, table) 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 pokerus = not pokemon["pokerus"] and "no" or "yes" + 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"]}, diff --git a/data/memory.lua b/data/memory.lua index 1ea92d7..cb56d27 100644 --- a/data/memory.lua +++ b/data/memory.lua @@ -150,7 +150,7 @@ end local function shinyValue(p) local pid_low = bit.band(p.pid, 0xFFFF) - local pid_high = bit.rshift(p.pid, 16) + local pid_high = rshift(p.pid, 16) return bit.bxor( bit.bxor(p.OTTID, p.OTSID), @@ -158,6 +158,18 @@ local function shinyValue(p) ) end +function pokerusDays(pokerusByte) + if pokerusByte == 0 then + return 0, 0 + 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 @@ -259,7 +271,7 @@ function fetchPokemon(start, gen, selectedPkmnSide) -- Fetches Pokemon info from 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) ~= 0 + 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) @@ -282,7 +294,7 @@ function fetchPokemon(start, gen, selectedPkmnSide) -- Fetches Pokemon info from 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"]=false + pokemon["pokerusStrainDays"], pokemon["pokerusDays"] = 0, 0 pokemon["stats"]={} pokemon["stats"][1]=pokemon["hp"]["max"] pokemon["stats"][2]=0x100*mbyte(start+0x14) + mbyte(start+0x15) @@ -344,7 +356,7 @@ function fetchPokemon(start, gen, selectedPkmnSide) -- Fetches Pokemon info from 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) ~= 0) + 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) @@ -414,7 +426,7 @@ function fetchPokemon(start, gen, selectedPkmnSide) -- Fetches Pokemon info from 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)} 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]) pokemon["hp"]={} pokemon["hp"]["current"]=decrypted[0x8E] pokemon["hp"]["max"]=decrypted[0x90] From 3ee56a4181c86b05613e63c2dbbdb8e49bd51ba4 Mon Sep 17 00:00:00 2001 From: Samira Urzua Date: Wed, 14 Jan 2026 05:40:58 -0300 Subject: [PATCH 16/18] Fix: Corrected an issue where the pokerus value was not being read most of the times --- data/memory.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/memory.lua b/data/memory.lua index cb56d27..fe7dd93 100644 --- a/data/memory.lua +++ b/data/memory.lua @@ -426,7 +426,7 @@ function fetchPokemon(start, gen, selectedPkmnSide) -- Fetches Pokemon info from 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)} pokemon["stats"]={decrypted[0x90],decrypted[0x92],decrypted[0x94],decrypted[0x98],decrypted[0x9A],decrypted[0x96]} - pokemon["pokerusStrainDays"], pokemon["pokerusDays"] = pokerusDays(decrypted[0x82]) + pokemon["pokerusStrainDays"], pokemon["pokerusDays"] = pokerusDays(decrypted[0x82+offset["D"]]) pokemon["hp"]={} pokemon["hp"]["current"]=decrypted[0x8E] pokemon["hp"]["max"]=decrypted[0x90] From 0fcfe58e2418b0b49d1c7599acf86ef2d9eae8f2 Mon Sep 17 00:00:00 2001 From: Samira Urzua Date: Wed, 14 Jan 2026 07:01:46 -0300 Subject: [PATCH 17/18] =?UTF-8?q?Fix=20Pok=C3=A9mon=20XP=20on=20gen=204:?= =?UTF-8?q?=20use=20full=2032-bit=20value=20instead=20of=2016-bit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/memory.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/memory.lua b/data/memory.lua index fe7dd93..16c181b 100644 --- a/data/memory.lua +++ b/data/memory.lua @@ -412,7 +412,7 @@ function fetchPokemon(start, gen, selectedPkmnSide) -- Fetches Pokemon info from 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"]] From 995e04fb86cc14d93b61ca825bbdb9a41f0646fc Mon Sep 17 00:00:00 2001 From: Samira Urzua Date: Wed, 14 Jan 2026 14:35:03 -0300 Subject: [PATCH 18/18] Fix: Gen 5 nature calculation to read from memory instad of use PID --- data/memory.lua | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/data/memory.lua b/data/memory.lua index 16c181b..b4be79c 100644 --- a/data/memory.lua +++ b/data/memory.lua @@ -375,11 +375,6 @@ function fetchPokemon(start, gen, selectedPkmnSide) -- Fetches Pokemon info from 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 @@ -425,6 +420,19 @@ function fetchPokemon(start, gen, selectedPkmnSide) -- Fetches Pokemon info from 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["pokerusStrainDays"], pokemon["pokerusDays"] = pokerusDays(decrypted[0x82+offset["D"]]) pokemon["hp"]={}