I made a lua script for anyone who are interested.
Tested:
2166 - Megaman Zero 4 (U).gba on
VBA-RR v23.6
Language: lua
local box = {
width = 15,
height = 3,
};
local enemies = {
-- 0x3004F34,
-- 0x3004FF8,
-- 0x30050BC,
-- 0x3005180,
0x03005244,
0x03005308,
0x03005490,
0x030053CC,
0x03005554,
0x03005618,
0x030056DC,
0x030057A0,
0x03005864,
0x03005928,
-- 0x30059EC,
-- 0x3005AB0,
};
local lastDash = 255;
print("MMZ4 - Memory viewer script for VBA, created by zhangsongcui");
function drawCharge(pos, val, y, text)
if val < 5 then
return false;
end
local headEquip = memory.readbyte(0x020366C4);
local maxVal = 120;
if headEquip == 4 then -- Q-Charge1
maxVal = 100;
elseif headEquip == 5 then -- Q-Charge2
maxVal = 80;
elseif headEquip == 6 then -- Q-Charge3
maxVal = 60;
end
local var = math.min(maxVal, val);
gui.box(
pos.X - box.width,
pos.Y - box.height + y,
pos.X + box.width,
pos.Y + box.height + y,
nil, "black");
gui.box(
pos.X - box.width,
pos.Y - box.height + y,
pos.X - box.width * (1 - var / maxVal * 2),
pos.Y + box.height + y,
var < maxVal / 3 and "white" or (var < maxVal and "green" or "yellow"), "clear");
gui.text(pos.X - box.width - 17, pos.Y - box.height + y, text);
gui.text(pos.X + box.width + 3, pos.Y - box.height + y, val);
return true;
end
function drawRemain(pos, val, y, maxVal, text)
if val <= 0 or val > maxVal then
return false;
end
gui.box(
pos.X - box.width,
pos.Y - box.height + y,
pos.X + box.width,
pos.Y + box.height + y,
nil, "black");
gui.box(
pos.X - box.width,
pos.Y - box.height + y,
pos.X - box.width * (1 - val / maxVal * 2),
pos.Y + box.height + y,
"white", "clear");
gui.text(pos.X - box.width - 17, pos.Y - box.height + y, text);
gui.text(pos.X + box.width + 3, pos.Y - box.height + y, val);
return true;
end
gui.register(function()
gui.opacity(.8);
local rng = memory.readlong(0x0202DC38);
if rng == 0 then
return; -- 游戏还没开始
end
gui.text(2, 10, string.format(" RNG: 0x%08X (0x%08X)", rng, (rng * 0x000343FD + 0x00269EC3) % 0x80000000));
gui.text(82, 152, "Ingame Time: " .. memory.readlong(0x0202E8E8));
local isPlaying = memory.readword(0x0202F8E0) ~= 0x03C0; -- 主角血条下面的 Z 字
if not isPlaying then
lastDash = 255;
return;
end
local camera = {
X = memory.readlongsigned(0x0202EA28),
Y = memory.readlongsigned(0x0202EA2C),
};
local hero = {
X = (memory.readlongsigned(0x02036654) - camera.X) / 0x0100 + 120,
Y = (memory.readlongsigned(0x02036658) - camera.Y) / 0x0100 + 80,
SpeedX = memory.readwordsigned(0x02036720),
SpeedY = memory.readwordsigned(0x02036660),
HP = memory.readbyte(0x020366A4),
Invisible = memory.readbyte(0x02036694),
Charge1 = memory.readbyte(0x020366E4),
Charge2 = memory.readbyte(0x020366E5),
Dash = memory.readbyte(0x0203673A),
};
if hero.HP <= 0 then
return;
end
for i, enemy in pairs(enemies) do
local hp = memory.readbyte(enemy);
if hp > 0 then
local x = (memory.readlong(enemy - 0x50) - camera.X) / 0x0100 + 120;
local y = (memory.readlong(enemy - 0x4C) - camera.Y) / 0x0100 + 80;
gui.text(x, y, hp);
end
end
gui.text(2, 18, string.format("WpnUse: [%d, %d, %d]", memory.readword(0x0202E8FC), memory.readword(0x0202E8FE), memory.readword(0x0202E900)));
gui.text(2, 26, string.format(" Speed: (%+.1f, %+.1f)", hero.SpeedX / 0x100, hero.SpeedY / 0x100));
if hero.Dash == _G.lastDash then
hero.Dash = 255;
else
_G.lastDash = hero.Dash;
end
local dy = 4;
local offsety = 7;
if drawCharge(hero, hero.Charge1, dy, 'Crg1') then dy = dy + offsety end;
if drawCharge(hero, hero.Charge2, dy, 'Crg2') then dy = dy + offsety end;
if drawRemain(hero, hero.Invisible, dy, 90, 'Invi') then dy = dy + offsety end;
if drawRemain(hero, hero.Dash, dy, 27, 'Dash') then dy = dy + offsety end;
gui.text(hero.HP > 9 and 7 or 8, 35, hero.HP, "red");
local boss = {
X = (memory.readlongsigned(0x0203A494) - camera.X) / 0x0100 + 120,
Y = (memory.readlongsigned(0x0203A498) - camera.Y) / 0x0100 + 80,
HP = memory.readword(0x0203A4E4),
Invisible = memory.readbyte(0x0203A4D4),
InvisibleLevel = memory.readbyte(0x0203A4D6),
};
if boss.HP > 0 then
if memory.readword(0x0202F8D8) ~= 0x03C0 then -- 指定右侧 Boss 血条下的 W 图标显示
-- Boss 战
gui.text(boss.HP > 9 and 226 or 228, 4, boss.HP, "red", "black");
else
-- 中 Boss 战
gui.text(boss.X, boss.Y, boss.HP, "red", "black");
end
drawRemain(boss, boss.Invisible, 5, 90, "Invi");
if (boss.Invisible > 0) then
gui.text(boss.X - box.width - 17, boss.Y + 9, "Invi Level: " .. boss.InvisibleLevel);
end
end
end)
EDIT: Fix "<0>"