--GBA Zook Man ZX4 - Casual Display script
--The display is intended to make sense for real-time viewing.
--For BizHawk
--FatRatKnight
local R4u,R4s= memory.read_u32_le,memory.read_s32_le
local R2u,R2s= memory.read_u16_le,memory.read_s16_le
local R1u,R1s= memory.read_u8 ,memory.read_s8
--Would stick function inside function. BizHawk seems to hate it.
local function BoxB(x,y,i,bc,fc) gui.drawRectangle(x-1+5*i,y+7,3,3,bc,fc) end
--*****************************************************************************
local function BarrierDisplay(x,y)
--*****************************************************************************
--37x16 - Displays barrier information in a pretty fashion.
--Squares for each HP. Changes color and shows a timer if barrier is hit.
local BarrierHP = R4u(0x16B4,"IWRAM")
local BarrierTmr= R4u(0x16B8,"IWRAM")
if BarrierHP > 6 then return end --Panic
if BarrierTmr> 48 then return end --Panic
local clr= 0xFF808080
if BarrierHP > 0 then
clr= 0xFF00FFFF
if BarrierTmr > 0 then
BoxB(x,y,BarrierHP,0xFFFF8000,0xFFFFFF00)
local X,Y= x+6,y+13
gui.drawLine( X,Y,X+math.floor((BarrierTmr-1)/2),Y,0xFFFFFF00)
gui.drawPixel(X,Y,0xFFFFFF00) --Because line doesn't draw coord1==coord2
BarrierHP= BarrierHP-1
end
for i= 1, BarrierHP do
BoxB(x,y,i,0xFF0080FF,0xFF0080FF)
end
end
gui.pixelText(x+ 4,y ,"Barrier",clr)
gui.drawLine( x ,y ,x ,y+15,clr)
gui.drawLine( x+ 1,y+15,x+35,y+15,clr)
gui.drawLine( x+36,y ,x+36,y+15,clr)
end
local WpnClr= {
0xFFFF2000, --Flame
0xFF00FF00, --Whirlwind
0xFFC08000, --Dirt
0xFF0040FF, --Bubble
0xFFC0FFFF, --Missile
0xFFFFFF00, --Star
0xFFFF00FF, --Spikeball
0xFF00FFFF --Laser
}
local function BoxW(x,y,bc,fc) gui.drawRectangle(x,y,6,24,bc,fc) end
--*****************************************************************************
local function WeaponDisplay(x,y,w)
--*****************************************************************************
--7x25 - For a single weapon. Will display negative ammo as well.
local HaveWpn= R1u(0x1668+w,"IWRAM") == 1
if not HaveWpn then BoxW(x,y,0xFF606060,0); return end
local clr= WpnClr[w] or 0xFFFFFFFF
local WpnAmmo= R1s(0x165C+w,"IWRAM")
local WpnSelect= R4u(0x15E4,"IWRAM")
if WpnSelect == w then
local FlashingClr= 0xFF008000 + 0x00FF7FFF * (math.floor(emu.framecount()/2)%2)
BoxW(x,y,FlashingClr,0)
WpnAmmo= R4s(0x163C,"IWRAM") --There's an edge case where the array fails
else --Specifically, refilling when ammo negative
BoxW(x,y,clr,0)
end
if WpnAmmo > 11 then return end --Panic
if WpnAmmo <-11 then return end --Panic
if WpnAmmo < 0 then
WpnAmmo= -WpnAmmo
for i= 1, WpnAmmo do
local X,Y= x+2,y+2*i
gui.drawPixel(X ,Y,0xFFFF0000)
gui.drawPixel(X+2,Y,0xFFFF0000)
end
else
for i= 1, WpnAmmo do
local X,Y= x+2,y+24-2*i
gui.drawLine(X,Y,X+2,Y,clr)
end
end
end
--*****************************************************************************
local function ChargeDisplay(x,y,h)
--*****************************************************************************
--3xh - Vertical charge display
local Charge= R4u(0x1608,"IWRAM")
local WpnCooldown=R4u(0x15E0,"IWRAM")
local WpnSelect= R4u(0x15E4,"IWRAM")
local ArmUpgrade= R1u(0x1650,"IWRAM") == 1
local clr= 0xFFC0C0C0
if (Charge >= 140) and ArmUpgrade and (WpnSelect == 0) then
clr= 0xFF20FF20
gui.drawLine(x+1,y+h,x+1,y ,clr)
gui.drawLine(x+2,y+h,x+2,y ,clr)
elseif (Charge > 60) and ArmUpgrade and (WpnSelect == 0) then
clr= 0xFFFFFF00
gui.drawLine(x+1,y+h,x+1,y ,clr)
local dist= math.floor(h/80 * (Charge-60))
gui.drawLine(x+2,y+h,x+2,y+h-dist,0xFF20FF20)
elseif (Charge >= 60) then
clr= 0xFFFFFF00
gui.drawLine(x+1,y,x+1,y+h,clr)
elseif (Charge > 0) then
local dist= math.floor(h/60 * Charge)
gui.drawLine(x+1,y+h,x+1,y+h-dist,0xFFFFFF00)
end
local dist= math.floor(h/18 * math.min(18,WpnCooldown))
gui.drawLine(x ,y+h,x ,y+h-dist,0xFFFF2000)
gui.drawLine(x ,y ,x+2,y ,clr)
gui.drawLine(x ,y+h,x+2,y+h,clr)
end
local PlayerStateText={
[0]= "Normal" ,
"Beam in" , -- 1
"Ldr.Down", -- 2
"Ldr.Up1" , -- 3
"Ldr.Up2" , -- 4
"Ouch!!" , -- 5
nil,
nil,
"On fire!", -- 8
"Laser" , -- 9
"Pre-boss", --10
"Beam out", --11
nil,
"Show off", --13
"Capsule" --14
}
local UpgradeLetter= {[0]="H","A","B","L"}
--*****************************************************************************
local function MiscDisplay(x,y)
--*****************************************************************************
--No idea what I'm putting here. Fun stuff, I suppose.
local s= R4u(0x15F0,"IWRAM") --Player state
local clr= 0xFFFFFFFF
if s == 0 then clr= 0xFFC0C0C0 end
gui.pixelText(x,y,PlayerStateText[s] or ("St " .. s),clr)
y= y+7
s= R1u(0x1EA4,"IWRAM") --Flame charge timer
if s > 0 then
s= math.floor((64-s)/2)
gui.drawLine( x,y,x+s,y,0xFFFFFF00)
gui.drawPixel(x,y, 0xFFFFFF00)
end
y= y+4
if R4u(0x1624,"IWRAM") ~= 0 then --Full invincibility
gui.pixelText(x,y,"D.Immune",0xFFFFFF00)
s= R4u(0x1628,"IWRAM") --Invincibility timer
if s <= 48 then
s= math.floor(s/2)
gui.drawLine( x,y+7,x+s,y+7,0xFFFFFF00)
gui.drawPixel(x,y+7, 0xFFFFFF00)
end --sanity
else
gui.pixelText(x,y,"No mercy",0xFF808080)
end
y= y+11
if R4u(0x1618,"IWRAM") ~= 0 then --Object invincibility
gui.pixelText(x,y,"O.Immune",0xFF00FFFF)
else
gui.pixelText(x,y,"Wpn Inv.",0xFF808080)
end
y= y+11
gui.pixelText(x ,y,"HT:" .. R4u(0x1640,"IWRAM"),0xFFFF2000) --Heart Tanks
gui.pixelText(x+20,y,"AT:" .. R4u(0x1644,"IWRAM"),0xFF0080FF) --Ammo Tanks
y= y+11
for i= 0, 3 do
s= R4u(0x164C+4*i,"IWRAM") ~= 0 --Upgrades
local clr= 0xFF808080
if s then clr= 0xFF00FFFF end
gui.pixelText(x+4+8*i,y,UpgradeLetter[i],clr)
end
end
local BossModeText= {
[0]={"Not" ," here," ," silly!"},
{"Loading.","Please" ," wait..."},
{"Wild" ," BOSS" ,"appears!"},
{"Shoot" ," him" ,"already!"},
{"Opening" ," his big"," mouth."},
{"Yappin',"," not" ,"rappin'."},
{"Uh, I" ," should" ,"show HP."}, --6, I really should show HP instead!
{"The 'no" ," damage'"," state."}, --7, another moment to show HP.
{"How did" ,"you read","this?" },
{"Took" ," long" ,"enough." }
}
--*****************************************************************************
local function BossDisplay(x,y)
--*****************************************************************************
--Gives a status on the current affairs of the boss.
local BossState= R4u(0x16F4,"IWRAM")
if (BossState ~= 6) and (BossState ~= 7) then --Show silly text
local t= BossModeText[BossState]
if t then
gui.pixelText(x,y ,t[1],0xFF00FF00)
gui.pixelText(x,y+ 7,t[2],0xFF00FF00)
gui.pixelText(x,y+14,t[3],0xFF00FF00)
else
gui.pixelText(x,y ,"I didn't",0xFFC0C0C0) --I thought I covered all
gui.pixelText(x,y+ 7,"see this",0xFFC0C0C0) --the BossState numbers!
gui.pixelText(x,y+14,"coming." ,0xFFC0C0C0) --But just in case...
end
else --Boss fighty mode!
local BossHP = R4u(0x1708,"IWRAM")
local BossSub= R4s(0x170C,"IWRAM") --Sub HP units
local BossInv= R4u(0x16F8,"IWRAM")
if BossHP > 11 then return end --Panic
for i= 1, BossHP do
local X= x + ((i-1)%6)*5
local Y= y + 14 - 7*math.floor((i-1)/6)
local v= math.max(0,math.min(5,BossHP*5 + BossSub - i*5))
-- if BossState == 7 then --Need a better immunity check.
-- gui.pixelText(x,y,"Immune:",0xFFFF8000)
-- else
gui.pixelText(x,y,"Boss HP:")
-- end
gui.pixelText(X,Y,v)
end
if BossInv > 96 then return end --Panic
if BossInv > 0 then
local Y= y+21
local len= math.floor(BossInv/3)
gui.drawLine( x,Y,x+len,Y,0xFFFFFF00)
gui.drawPixel(x,Y, 0xFFFFFF00)
end
end
end
local mBossModeText= {
[0]={"Wrong" ,"function"," bub." },
{"Generate"," one" ,"miniboss"},
{"Please" ," hit me" ," now!!" },
{"Strange" ," state." ,"What?" },
{"We hit." ,"Wait 60" ," frames."},
{"Ooh..." ," pretty" ," booms."}
}
--*****************************************************************************
local function MinibossDisplay(x,y)
--*****************************************************************************
--Informs us on what's happening with the miniboss.
local mBossState= R4u(0x16DC,"IWRAM")
if (mBossState < 2) or (mBossState > 4) then
local t= mBossModeText[mBossState]
if t then
gui.pixelText(x,y ,t[1],0xFFFFFF00)
gui.pixelText(x,y+ 7,t[2],0xFFFFFF00)
gui.pixelText(x,y+14,t[3],0xFFFFFF00)
else
gui.pixelText(x,y ,"What is" ,0xFFC0C0C0) --Never assume. Always have
gui.pixelText(x,y+ 7,"this" ,0xFFC0C0C0) --a fallback in case of
gui.pixelText(x,y+14,"thing?" ,0xFFC0C0C0) --unexpected numbers.
end
else
local mBossHP
local addr= 0x2BA8 --1DE4, objSize=0x74, 30th object, offset+0x2C
repeat --My miniboss detector is whoever has more than zero HP...
mBossHP= R2s(addr)
if mBossHP > 0 then break end
addr= addr+0x74
until addr >= 0x3914 --Panic
local mBossInv= R4u(0x16E0,"IWRAM")
gui.pixelText(x,y ,"Miniboss")
gui.pixelText(x,y+ 7,mBossHP)
if mBossInv > 60 then return end --Panic
if mBossInv > 0 then
local Y= y+14
local len= math.floor((60-mBossInv)/2)
gui.drawLine( x,Y,x+len,Y,0xFFFFFF00)
gui.drawPixel(x,Y, 0xFFFFFF00)
end
end
-- gui.pixelText(x,y,mBossState)
end
local MapClrCycle= {
0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,
0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,
0xFFFF0000,0xFFFF0000,0xFF00FF00,0xFF00FF00,0xFF0000FF,
0xFF0000FF,0xFF00FF00,0xFF00FF00,0xFFFF00FF,0xFFFF00FF,
}
--*****************************************************************************
local function MapDisplay(x,y)
--*****************************************************************************
--38x?? - Intended to be... Really basic. We only have 40 pixels width.
--Segments are as wide as 18 blocks. We ain't got a lot o' room.
--Segment rectangle
local Width= R4u(0x153C,"IWRAM")
local Height= R4u(0x1540,"IWRAM")
if Width > 18 then return end --Panic
if Height > (160 - y)/2 then return end --Panic
gui.drawRectangle(x,y,Width*2+1,Height*2+1,0xFFC0C0C0,0)
--Player position (1:128 scale ratio)
local clr= MapClrCycle[emu.framecount()%(#MapClrCycle)+1]
local X= x + 1 + math.floor((R4s(0x1500,"IWRAM")+R4s(0x1E58,"IWRAM") + 8)/128)
local Y= y + 1 + math.floor((R4s(0x1504,"IWRAM")+R4s(0x1E5C,"IWRAM") +24)/128)
gui.drawPixel(X,Y,clr)
end
client.SetGameExtraPadding(0,0,40,0) --7:4, a touch under 16:9
--Uncomment the below line if you want to see what room exact 16:9 gives.
--client.SetGameExtraPadding(0,0,80,20)
--*****************************************************************************
while true do
--*****************************************************************************
--Our overhead.
if R4u(0x16F4,"IWRAM") ~= 0 then
BossDisplay(241,1)
elseif R4u(0x16DC,"IWRAM") ~= 0 then
MinibossDisplay(241,1)
else
gui.pixelText(241,1,string.format("St%2d:%2d",
R4u(0x152C,"IWRAM"), --Stage
R4u(0x1530,"IWRAM") --Segment
))
MapDisplay(241,8)
end
ChargeDisplay(241,33,51)
for i= 1, 8 do
local X= 245+((i-1)%4)*9
local Y= 33 + math.floor((i-1)/4)*27
WeaponDisplay(X,Y,i)
end
BarrierDisplay(242,86)
MiscDisplay( 241,107)
emu.frameadvance()
end