User File #38508546028174499

Upload All User Files

#38508546028174499 - Zook Man ZX4 - Basic script

ZookManZX4.lua
946 downloads
Uploaded 4/22/2017 5:25 AM by FatRatKnight (see all 245)
Shh... Secrets. Script #3, intended to be run third.
Displays a few different stats for each onscreen object, like HP, invincibility timer, and stuff. Top-right has position info, lower-right is our RAM watch dump, and top-left has a few timers in mind. Lower-left gets the object counts for lag reasons, and the bottom is our item drop finder.
Well, it's the basic script. Not a lot to say, really.
memory.usememorydomain("IWRAM")
local R4u= memory.read_u32_le
local R4s= memory.read_s32_le
local R2u= memory.read_u16_le
local R2s= memory.read_s16_le
local R1u= memory.read_u8
local R1s= memory.read_s8
local R4x= function(a) return string.format("%X",R4u(a)) end
local R2x= function(a) return string.format("%X",R2u(a)) end

local RAMList2= { --Includes method and color.
-- Address  Type  Color
  {a=0x14F0,m=R4s,c=0xFFFFFFFF}, --Camera X
  {a=0x1EA4,m=R2s,c=0xFFFFFF00}, --Flame charge timer
  {a=0x15F0,m=R4s,c=0xFFC0C0C0}, --Action lock
  {a=0x1E58,m=R2s,c=0xFFFFFFFF}, --Player screen location X
  {a=0x17A0,m=R4x,c=0xFFFFFFFF}, --Frame counter
  {a=0x1530,m=R4s,c=0xFF00FFFF}, --Segment
  {a=0x153C,m=R4s,c=0xFF0080FF}, --Segment width
  {a=0x1540,m=R4s,c=0xFF0080FF}, --Segment height
  {a=0x15E0,m=R4s,c=0xFFFFFFFF}, --Shot cooldown
  {a=0x1608,m=R4s,c=0xFFFFFFFF}, --Charge counter
  {a=0x1E6C,m=R2s,c=0xFF00FF00}, --Jump frames (object data: player)
  {a=0x16E0,m=R4s,c=0xFFC0C0C0}, --Miniboss invincibility timer
  {a=0x16F8,m=R4s,c=0xFFC0C0C0}, --Boss invincibility timer
  {a=0x1708,m=R4s,c=0xFFFF8000}, --Boss HP main
  {a=0x170C,m=R4s,c=0xFFFF8000}, --Boss HP sub
}

local RAMList= {
    0x15E0, --Shot cooldown
    0x1608, --Charge counter
    0x1530, --Segment counter
    0x153C, --Segment width
    0x1540  --Segment height
}

--*****************************************************************************
local function PixelTextRight(x,y,t,c1,c2,f)
--*****************************************************************************
--Specify right pixel instead of left pixel to position our text.
--Should be handy in keeping our numbers relatively readable.

    x= x - 4*(#tostring(t))
    gui.pixelText(x,y,t,c1,c2,f)
end

--*****************************************************************************
local function FetchAddrDomainGBA(a)
--*****************************************************************************
--I am furious at the design away from the bus. It used to exist! Why remove?
--I do not want to code in removing offsets to pointers every time I read one.
--This function was made because I insist on full pointers. Has only what I know.

    if     (a >= 0x02000000) and (a < (0x02000000+memory.getmemorydomainsize("EWRAM"))) then
        return a-0x02000000, "EWRAM"
    elseif (a >= 0x03000000) and (a < (0x03000000+memory.getmemorydomainsize("IWRAM"))) then
        return a-0x03000000, "IWRAM"
    elseif (a >= 0x08000000) and (a < (0x08000000+memory.getmemorydomainsize("ROM"))) then
        return a-0x08000000, "ROM"
    else
        error("Unknown address " .. a,2)
    end
end

--[[
Weaponry:
Basic     (start)         : 1  2  3
Flame     (FarLeft Top   ): 3  5
Whirlwind (MidLeft Top   ): 4  4
Dirtspike (MidRightTop   ): 4 10
Bubble    (FarRightTop   ): 3  0
Missile   (FarLeft Bottom): 4  6
Star      (MidLeft Bottom): 3  6
Spikeball (MidRightBottom): 4  8
Laser     (FarRightBottom): 4 10

14F0:Camera X position
14F4:Camera Y position
1500:
1504:
1508:
1510:
1514:
1518:
151C:
1520:
1524:
1528:
152C:Stage selection
1530:Segment
1534:
1538:
153C:Segment width
1540:Segment height
1544:
1548:
154C:
1550:
1554:
1558:
155C:
1560:X position
1564:Y position
1568:
156C:

15C0:Dash ready timer
15C4:Dash ready direction
15C8:Dash ready flag
15D0:Dashing timer
15D4:Dashing flag
15DC:Weapon shot counter? (Laser)
15E0:Shot cooldown
15E4:Weapon select
15F0:Action lock (zero means we're free)
1608:Frames charged
1618:Object invincibility flag
1624:Invincible flag
1628:Invincible timer
1634:Lives
1638:Player HP
163C:Weapon ammo (selection)
1640:Heart tanks
1644:Ammo tanks
1650:Arm upgrade
165C[12,1]:Weapon ammo
1668[12,1]:Have weapons
1674[12,1]:Beat stages
1680:(Looks temporary)
16B4:Bubble barrier durability
16B8:Bubble barrier timer
16E0:Miniboss invincibility timer
16F8:Boss invincibility timer
1708:Boss HP main
170C:Boss HP sub
17A0:Frame counter (used for RNG)

1EA4,2s - Flame charge timer

0807C18C

IWRAM:1DE4[size=0x74][count=60]  (2B7C is slot 30, our first enemy)
  +00,4s - Screen position X
  +04,4s - Screen position Y
  +08,2x - Sprite ID (?)
  +0A,2x - ID?
  +0C,2x -
  +0E,2x - Flags
  +10,2u - Action Timer 
  +12,2x - Action ID (?)
  +14,4x - ?
  +18,2x - Player shot ID
  +1A,2x -
  +1C,2u - Frames existing
  +1E,2x -
  +20,4x -
  +24,4x -
  +28,2u - nth enemy of the current stage
  +2A,2x -
  +2C,2s - Hit points
  +2E,2x -
  +30,4x -
  +34,4x -
  +38,4x -
  +3C,4x -
  +40,4x - ?
  +44,4x -
  +48,2s - Damage output
  +4A,2x -
  +4C,4x -
  +50,4x -
  +54,4x -
  +58,4x - Invincibility frames
  +5C,4x -
  +60,4x -
  +64,4x -
  +68,4x -
  +6C,4x -
  +70,4x - Pointer
]]--

local ObjColors= {
[0]=0xFFC0C0C0, -- 0,Weapon selection icon
    0xFF20FF20, -- 1,Player
    0xFFC0C0C0, -- 2,Life icon
    0xFFC0C0C0, -- 3,HP bar
    0xFFC0C0C0, -- 4,Weapon bar
    0xFFFFFFFF, -- 5,
    0xFFC0C0C0, -- 6,Dust sprite
    0xFFC0C0C0, -- 7,Dust sprite
    0xFFC0C0C0, -- 8,Dust sprite
    0xFFC0C0C0, -- 9,Dust sprite
    0xFF00FF00, --10,Charge sprite
    0xFFFFFFFF, --11,
    0xFFFFFFFF, --12,
    0xFFFFFFFF, --13,
    0xFFFFFFFF, --14,
    0xFFFFFFFF, --15,
    0xFFFFFFFF, --16,
    0xFFFFFFFF, --17,
    0xFFFFFFFF, --18,
    0xFFC0C0C0, --19,Player muzzle flash
    0xFF00FFFF, --20,Player projectile
    0xFF00FFFF, --21,Player projectile
    0xFF00FFFF, --22,Player projectile
    0xFF00FFFF, --23,Player projectile
    0xFF00FFFF, --24,Player projectile
    0xFF00FFFF, --25,Player projectile
    0xFF00FFFF, --26,Player projectile
    0xFF00FFFF, --27,Player projectile
    0xFF00FFFF, --28,Player projectile (?)
    0xFF00FFFF, --29,Player projectile (?)
    0xFFFFFF00, --30,Enemy
    0xFFFFFF00, --31,Enemy
    0xFFFFFF00, --32,Enemy
    0xFFFFFF00, --33,Enemy
    0xFFFFFF00, --34,Enemy
    0xFFFFFF00, --35,Enemy
    0xFFFFFF00, --36,Enemy
    0xFFFFFF00, --37,Enemy
    0xFFFFFF00, --38,Enemy
    0xFFFFFF00, --39,Enemy
    0xFFFFFF00, --40,Enemy
    0xFFFFFF00, --41,Enemy
    0xFFFFFF00, --42,Enemy
    0xFFFFFF00, --43,Enemy
    0xFFFFFF00, --44,Enemy
    0xFFFFFF00, --45,Enemy
    0xFFFFFF00, --46,Enemy
    0xFFFFFF00, --47,Enemy
    0xFFFFFF00, --48,Enemy
    0xFFFFFF00, --49,Enemy
    0xFFFFC000, --50,
    0xFFFFC000, --51,
    0xFFFFC000, --52,
    0xFFFFC000, --53,
    0xFFFFC000, --54,
    0xFFFFC000, --55,
    0xFFFFC000, --56,
    0xFFFFC000, --57,
    0xFFFFC000, --58,
    0xFFFFC000, --59,
}

--*****************************************************************************
local function DrawBlocks(x,y,a)
--*****************************************************************************
--This idea failed.
--Thought those pointers may have led to something boxy or whatever.
--Instead, we have a mess. But at least it isn't hitting the PANIC.

    local PANIC= 0
    while R2s(a,"ROM") ~= -1 do
        local bX,bY= R1s(a,"ROM"),R1s(a+1,"ROM")
        bX= bX*4+x; bY= bY*4+y
        gui.drawRectangle(bX,bY,4,4,0x60FF00FF,0x60FFFF00)
        a= a+2
        PANIC=PANIC+1; if PANIC > 400 then error("PANIC") end --Just in case
    end
end
--[[Piece of the failed idea
    local ID= R2s(addr+0x08)
    local ptr= R4u(addr+0x70)
    if (ID ~= 0) and (ptr ~= 0) then
        BlockPtr= R4u( FetchAddrDomainGBA(ptr + (ID-1)*4) )
        if BlockPtr > 0x08000000 then --Really should be in the ROM.
            DrawBlocks(X,Y,BlockPtr-0x08000000)
        end
    end
]]--

--*****************************************************************************
local function ObjList()
--*****************************************************************************
--Displays some information on objects.
--Well, more like a specific stat.

  local ObjCount= 0
  for i= 0, 59 do
    local addr= 0x1DE4 + 0x74*i
    local X= R4s(addr+0x00)
    local Y= R4s(addr+0x04)
    local clr= ObjColors[i] or 0xFFFF00FF --List really should be complete...

--    if R2s(addr+0x0A) ~= 0 then
      gui.pixelText(X,Y   ,string.format("%d",R2s(addr+0x2C)),clr)
      gui.pixelText(X,Y+ 7,R2x(addr+0x1C),clr)
--      gui.pixelText(X,Y+14,string.format("%04X",addr),clr)
--    end
    if R2s(addr+0x0A) ~= 0 then ObjCount= ObjCount+1 end
  end
  gui.pixelText(10,153,ObjCount)
end


--*****************************************************************************
local function NullFn() end  --Do not process.
--*****************************************************************************

--*****************************************************************************
local function DoPlyr(addr,x,y)
--*****************************************************************************
--Player stuff. Potentially useful addresses:
--00,4s - Screen position X
--04,4s - Screen position Y
--08,2x - Sprite ID
--10,2u - Action Timer (?)

  gui.pixelText(x,y   ,string.format("%d",R2s(addr+0x08)),0xFF00FF20)
  gui.pixelText(x,y+ 7,string.format("%d",R2s(addr+0x10)),0xFF00FF20)
  gui.pixelText(x,y+14,(R4u(0x17A0)-1)%4,0xFFFFFFFF) --Frame counter
  --gui.pixelText(x,y+21,string.format("%08X",R4u(addr+0x70)),0xFFFFFFFF) --Ptr
end

local Bcount= 0
--*****************************************************************************
local function DoBllt(addr,x,y)
--*****************************************************************************
--Player bullets.
--1C: Frames existed
--48: Damage

  if R2s(addr+0x08) == 0 then return end
  Bcount= Bcount+1 

  gui.pixelText(x,y   ,string.format("%d",R2s(addr+0x1C)),0xFF00FFFF)
  gui.pixelText(x,y+ 7,string.format("%d",R2s(addr+0x48)),0xFF00FFFF)
--  gui.pixelText(x,y   ,string.format("%d",R2s(addr+0x08)),0xFF00FFFF)
--  gui.pixelText(x,y+ 7,string.format("%d",R2s(addr+0x18)),0xFF00FFFF)
--  gui.pixelText(x,y+14,string.format("%08X",R4s(addr+0x70)),0xFF00FFFF)
end

local Ecount= 0
--*****************************************************************************
local function DoOthr(addr,x,y)
--*****************************************************************************
--Enemy data.
--I would like a way to know when things exist.
--I'm guessing it's 0x0A, so I'll do that. Please let me know false negatives.
--Would love a way to recognize who I'm looking at.
--2C - Enemy HP
--58 - Invincibility timer

  if R2s(addr+0x0A) == 0 then return end
  Ecount= Ecount+1

  if R2s(addr+0x3C) == 2 then --Item?
    gui.pixelText(x,y  ,string.format("%d",R2s(addr+0x1C)),0xFF000000,0xFF00FF00) --Timer
    gui.pixelText(x,y+7,string.format("%d",R2s(addr+0x0A)),0xFF000000,0xFF00FF00) --ID

  elseif R2s(addr+0x2C) ~= -1 then --Has HP
    gui.pixelText(x,y  ,string.format("%d",R2s(addr+0x2C)),0xFFFF8000) --HP
    gui.pixelText(x,y+7,string.format("%d",R2s(addr+0x58)),0xFFFF8000) --Inv.Timer
--    gui.pixelText(x,y+14,string.format("%04X",addr),0xFFFF8000) --addr
--    gui.pixelText(x,y  ,string.format("%d",R2s(addr+0x08)),0xFFFF8000) --Spr
--    gui.pixelText(x,y+7,string.format("%02X",R2s(addr+0x14)),0xFFFF8000) --ID

  else --Can't be damaged
    gui.pixelText(x,y  ,string.format("%d",R2s(addr+0x1C)),0xFF000000,0xFFFF8000) --Timer
    gui.pixelText(x,y+7,string.format("%d",R2s(addr+0x08)),0xFF000000,0xFFFF8000) --Sprite
  end
end

local ObjTbl= {
[0]=NullFn, DoPlyr, NullFn, NullFn, NullFn,
    NullFn, NullFn, NullFn, NullFn, NullFn,
    DoBllt, DoBllt, DoBllt, DoBllt, DoBllt,
    DoBllt, DoBllt, DoBllt, DoBllt, DoBllt,
    DoBllt, DoBllt, DoBllt, DoBllt, DoBllt,
    DoBllt, DoBllt, DoBllt, DoBllt, DoBllt,
    DoOthr, DoOthr, DoOthr, DoOthr, DoOthr,
    DoOthr, DoOthr, DoOthr, DoOthr, DoOthr,
    DoOthr, DoOthr, DoOthr, DoOthr, DoOthr,
    DoOthr, DoOthr, DoOthr, DoOthr, DoOthr,
    DoOthr, DoOthr, DoOthr, DoOthr, DoOthr,
    DoOthr, DoOthr, DoOthr, DoOthr, DoOthr
}

--*****************************************************************************
local function ObjList2()
--*****************************************************************************
--Displays some information on objects.
--Well, more like a specific stat.

  Bcount= 0
  Ecount= 0
  local ObjCount= 0
  for i= 0, 59 do
    local addr= 0x1DE4 + 0x74*i
    local X= R4s(addr+0x00)
    local Y= R4s(addr+0x04)
    ObjTbl[i](addr,X,Y)
  end
  PixelTextRight( 9,153,Bcount)
  PixelTextRight(19,153,Ecount)
end
--               _h_H_a+A_H_Aa+h_LaHh_AH_*_A*_+_a
local HeartStr= " h H  +  H   +h   Hh  H      +  "
local AmmoStr = "     a A   Aa    a   A  * A*   a"
local LifeStr = "                L               "
local DirStr  = " < > <>> < ><<> <<>> <> < >> < >"
--*****************************************************************************
local function ItemDrop()
--*****************************************************************************
    gui.pixelText(40,153,HeartStr,0xFFFF8000)
    gui.pixelText(40,153,AmmoStr ,0xFF00C0FF,0)
    gui.pixelText(39,153,LifeStr ,0xFF00FF00,0)
    gui.pixelText(40,146,DirStr  ,0xFFC0C0C0)
    local RNG= R4u(0x17A0)%0x20
    local X= RNG*4 + 40
    gui.drawRectangle(X,153,5,6)
end

local DashDir= {[0]=">","<"}
--*****************************************************************************
local function TimerWatch()
--*****************************************************************************
  if R4s(0x15C0) ~= 0 then --Dash ready
    gui.pixelText( 24,0,DashDir[R4s(0x15C4)] or "?",0xFFFF80FF)--direction
    PixelTextRight(23,0,        R4s(0x15C0)        ,0xFFFF80FF)--timer
  end

  if R4s(0x15D4) ~= 0 then --Dashing
    PixelTextRight(32,0,        R4s(0x15D0)        ,0xFF80FF80)--timer
  end

  if R4s(0x1624) ~= 0 then --Player invincible
    PixelTextRight(40,0,        R4s(0x1628)        ,0xFFFF8000)--timer
  end

  if R4s(0x16B4) ~= 0 then
    PixelTextRight(60,0,        R4s(0x16B4)        ,0xFF0080FF)--Durability
    PixelTextRight(70,0,        R4s(0x16B8)        ,0xFF0080FF)--Timer
  end
end

local DashFrames= 0
--*****************************************************************************
local function DashCount()
--*****************************************************************************
    if R4s(0x15D4) ~= 0 then DashFrames= DashFrames+1 end
    PixelTextRight(32,50,DashFrames,0xFF80FF80)    
end

--*****************************************************************************
local function RAMWatch()
--*****************************************************************************
--A convenient way to add important addresses is from the table at the top.

    local Y= 153 - (#RAMList)*7
    for i= 1, #RAMList do
        PixelTextRight(239,Y+i*7,R2s(RAMList[i]))
    end
end

--*****************************************************************************
local function RAMWatch2()
--*****************************************************************************
--A convenient way to add important addresses is from the table at the top.
--Additionally, we can specify method and color. Yay, customization!
--  {a=0x1530,m=R4s,c=0xFF00FFFF}
--a = address ; m = method ; c = color

    local Y= 153 - (#RAMList2)*7
    for i= 1, #RAMList2 do
        local stat= RAMList2[i]
        PixelTextRight(239,Y+i*7,stat.m(stat.a),stat.c)
    end
end

--*****************************************************************************
local function ListVals(ptr,x)
--*****************************************************************************
  local i= 0
  while R2s(ptr,"ROM") ~= -1 do
    gui.pixelText(x   ,i*7,string.format("%04X",R2u(ptr  ,"ROM")))
--    gui.pixelText(x+12,i*7,R1u(ptr+1,"ROM"))
    ptr= ptr+2
    i=i+1; if i > 30 then return end --Just in case
  end
end

--*****************************************************************************
local function FinalHitboxAttempt()
--*****************************************************************************
--If this fails to call forth inspitation, then hitboxes just aren't happening.
--Those addresses just look suspicious, alright?

  local addr= 0x1DE4 + 0x74*1  --Player object

  local index= R2s(addr+0x08)  --Sprite index
  local ptr=   R4s(addr+0x70)  --Pointer for object

  if     index == 0 then gui.pixelText(50,0,"Null sprite")  --A guess, really
  elseif ptr   == 0 then gui.pixelText(50,0,"Null pointer") --Not a guess
  else
--I don't actually know if the game uses index as is or subtracts an offset.
--If the numbers are used for hitboxery, I want to know.
    ptr= ptr + (index-1)*4

    ptr= R4s(FetchAddrDomainGBA(ptr))  --Fetching some deeper data
    if (ptr >= 0x08000000) and (ptr < 0x08000000+memory.getmemorydomainsize("ROM")) then
      ptr= ptr - 0x08000000   --Why is this subtraction a requirement? VBA don't need it.
      ListVals(ptr,50)
    else
      gui.pixelText(50,0,"Bad pointer")
    end
  end
end

--*****************************************************************************
local function ProgressColors(v)
--*****************************************************************************
    if v > 0 then return 0xFF00C0FF end --Forward!
    if v < 0 then return 0xFFFFC000 end --Backward!
    return 0xFFFF00FF --We held still
end

local Left,Zero,DamageR,DamageL= 0,0,0,0
local cyan= 0xFF00FFFF
local LastX,LastY= 0,0
--*****************************************************************************
local function BasicHUD()
--*****************************************************************************
--General purpose display function. Stick whatever you like here.

    local CamX,CamY=     R4s(0x14F0),R4s(0x14F4)
    local PlScrX,PlScrY= R4s(0x1E58),R4s(0x1E5C)

    local PlX,PlY= PlScrX+CamX, PlScrY+CamY

    PixelTextRight(239,  0,R4s(0x1560),cyan)
    PixelTextRight(239,  7,PlX)
    PixelTextRight(239, 14,PlX-LastX,ProgressColors(PlX-LastX))

    PixelTextRight(239, 25,R4s(0x1564),cyan)
    PixelTextRight(239, 32,PlY)
    PixelTextRight(239, 39,PlY-LastY,ProgressColors(PlY-LastY))

    local BossHP= (R4s(0x1708)-1)*5 + R4s(0x170C)
    PixelTextRight(224,153,BossHP)

--Piece of code to count our movements for pre-dash horizontal stuff.
--[[
    local move= PlX-LastX
    if     move ==  1 then DamageR= DamageR+1
    elseif move ==  0 then Zero= Zero+1
    elseif move == -1 then DamageL= DamageL+1
    elseif move == -2 then Left= Left+1
    end
    PixelTextRight(219,  0,DamageR,0xFFFFFF80)
    PixelTextRight(219,  7,Zero   ,0xFFFFFF00)
    PixelTextRight(219, 14,DamageL,0xFFFFC000)
    PixelTextRight(219, 21,Left   ,0xFFFF4000)
]]--

    LastX,LastY= PlX,PlY
end

local Idle= 0
local BossScan
--*****************************************************************************
local function BossRepeat()
--*****************************************************************************
  local inv= R4s(0x16F8)
  if inv == 0 then
    Idle= Idle+1
  elseif Idle ~= 0 then
    print(string.format("f%d - %d delayed",emu.framecount(),Idle-1))
    Idle= 0
  end
end

--*****************************************************************************
local function BossFirst()
--*****************************************************************************
  local inv= R4s(0x16F8)
  if inv ~= 0 then
    print(string.format("f%d - First hit",emu.framecount()))
    BossScan= BossRepeat
  end
end

BossScan= BossFirst

--*****************************************************************************
while true do
--*****************************************************************************
--Our overhead.
memory.usememorydomain("IWRAM")

    ObjList2()
    ItemDrop()
    RAMWatch2()
    TimerWatch()
--    DashCount()
    BasicHUD()
--    BossScan()
--    FinalHitboxAttempt()
    emu.frameadvance()
end