User File #38508617604349368

Upload All User Files

#38508617604349368 - Zook Man ZX4 - External Radar script

ZM_ExternalRadar.lua
908 downloads
Uploaded 4/22/2017 5:29 AM by FatRatKnight (see all 245)
Shh... Secrets. Stand-alone script.
This script combines the virtues of both the terrain tiles and the hitboxes scripts, but pastes the info onto a separate canvas. The viewing area is 2x of the game's normal viewing range, so we're seeing half a screen out in each direction past the visual boundaries. It's a heavyweight script, so be aware of slowdowns when running it.
The terrain tiles loading is not aware of the tiles script, so if both are running, there will be two copies of the same data loaded into lua. They are several hundred KB in size, so do be aware.
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 ROMmin,ROMmax= 0x08000000, 0x08000000+memory.getmemorydomainsize("ROM")

local cnv= gui.createcanvas(480,320)

--for k,v in pairs(cnv) do print(k,type(v)) 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(string.format("Unknown address %08X", a),2)
    end
end

local Maps= {}

--*****************************************************************************
local function LoadMaps()
--*****************************************************************************
--This function tracks down every stage, every segment in each stage, every
--block in each segment, and every tile in each block. Then it stitches
--together the tiles as it would fit in the block.

  for Stage= 0, 16 do
    local StageMaps= {} --Construct new table

    local BlockSetPtr= R4u(0x3CD86C + 4*Stage,"ROM")
    local SegmentBase= R4u(0x3CD8B0 + 4*Stage,"ROM")
    local Segment= 0
    while true do
      local SegmentPtr= R4u(FetchAddrDomainGBA(SegmentBase + Segment*4))
      if (SegmentPtr < ROMmin) or (SegmentPtr >= ROMmax) then break end

      local SegX, SegY= R2s(FetchAddrDomainGBA(SegmentPtr)), R2s(FetchAddrDomainGBA(SegmentPtr+2))
      SegmentPtr= SegmentPtr+4

      local SegmentMap= {}
      for y= 0, SegY-1 do
        for x= 0, SegX-1 do
          local BlockIndex= R2u(FetchAddrDomainGBA(SegmentPtr + (y*SegX + x)*2))
          local BlockPtr= R4u(FetchAddrDomainGBA(BlockSetPtr + 4*BlockIndex))
          for yy= 0, 31 do
            for xx= 0, 31 do
              local i= xx + x*32 + yy*SegX*32 + y*SegX*1024
              SegmentMap[i]= R1u(FetchAddrDomainGBA(BlockPtr + 2 + xx + 32*yy))
            end
          end
        end
      end

      StageMaps[Segment]= SegmentMap
      Segment= Segment+1
    end

    Maps[Stage]= StageMaps --Stash what we've got!
    print("Loaded Stage " .. Stage)
  end
end
LoadMaps()

local Stage, Segment, SegX, SegY, CamX, CamY
--*****************************************************************************
local function UpdateStats()
--*****************************************************************************
  Stage=     R4u(0x152C)              --Which stage we're playing
  Segment=   R4s(0x1530)              --What part in that stage
  SegX,SegY= R4s(0x153C),R4s(0x1540)  --Size of that part
  CamX,CamY= R4s(0x14F0),R4s(0x14F4)  --Camera location
end

--*****************************************************************************
local function SafishRect(x,y,w,h,bc,fc)
--*****************************************************************************
  if x < 0 then return end
  if y < 0 then return end
  if x+w > 479 then return end
  if y+h > 319 then return end
  cnv.DrawRectangle(x,y,w,h,bc,fc)
end

local TileClrs= {
[0]=0xFF000080,0xFF808080,0xFF801000,0xFF000000, --Air,Floor,Spike,Solid
    0xFF000000,0xFF008080,0xFF808040,0xFF808040, --nil,Wj,Sslope /
    0xFF808040,0xFF808040,0xFF808000,0xFF808000, --Sslope \,magnet
    0xFF808040,0xFF808040,0xFF808040,0xFF808040, --Gslope /
    0xFF808040,0xFF808040,0xFF808040,0xFF808040, --Gslope \
    0xFF006030,0xFF008000,0xFF006030,0xFF800080, --Ladder, sPlatform
    0xFF800080,0xFF800080,0xFF800080,0xFF800080, --sPlatform, Capsule
    0xFF800080,0xFF000000,0xFF000000,0xFF408080, --Capsule,nil,nil,AS end

  [0x60]=0xFF800080, [0x61]=0xFF800080, [0x62]=0xFF800080, [0x63]=0xFF800080,
  [0x64]=0xFF800080, [0x65]=0xFF800080, [0x66]=0xFF800080, [0x67]=0xFF800080,
  [0x68]=0xFF800080, [0x69]=0xFF800080, [0x6A]=0xFF800080, [0x6B]=0xFF800080,
  [0x6C]=0xFF800080, [0x6D]=0xFF800080, [0x6E]=0xFF800080, [0x6F]=0xFF800080,
  [0x70]=0xFF800080, [0x71]=0xFF800080, [0x72]=0xFF800080, [0x73]=0xFF800080,
  [0x74]=0xFF800080, [0x75]=0xFF800080, [0x76]=0xFF800080
}

local OffsetX,OffsetY= 120,80
--*****************************************************************************
local function CanvasTerrain()
--*****************************************************************************
  if Stage > 16 then return end
  local Area= Maps[Stage][Segment]; if not Area then return end

  local Left= math.max(
    math.floor((CamX-OffsetX)/8),     --Left edge
    0                                 --Segment left
  )
  local Top=  math.max(
    math.floor((CamY-OffsetY)/8),     --Top edge
    0                                 --Segment top
  )
  local Right= math.min(
    math.floor((CamX-OffsetX+480)/8), --Right edge
    SegX*32 - 1                       --Segment right
  )
  local Bottom= math.min(
    math.floor((CamY-OffsetY+320)/8), --Bottom edge
    SegY*32 - 1                       --Segment bottom
  )

  for Y= Top, Bottom do
    local DispY= Y*8 + OffsetY - CamY
    for X= Left, Right do
      local DispX= X*8 + OffsetX - CamX
      local tile= Area[X + Y*SegX*32]
      cnv.DrawRectangle(
        DispX,DispY,7,7,TileClrs[tile],TileClrs[tile]
      )
    end
  end
end

--*****************************************************************************
local function CanvasBorder()
--*****************************************************************************
  cnv.DrawRectangle(OffsetX,OffsetY,239,159,0x40FFFFFF)
end

--*****************************************************************************
local function CanvasPlayer()
--*****************************************************************************
  local addr= 0x1DE4 + 0x74*1
  local Spr= R2s(addr+0x08,"IWRAM")
  local pX, pY= R4s(addr+0x00,"IWRAM"), R4s(addr+0x04,"IWRAM")
  local BoxAddr= 0x087B64 + 0x10*Spr
  local Left=  R4u(BoxAddr+ 8,"ROM")
  local Right= R4u(BoxAddr+12,"ROM")
  local Up=    R4u(BoxAddr+ 0,"ROM")
  local Down=  R4u(BoxAddr+ 4,"ROM")
  SafishRect(pX+Left+OffsetX,pY+Up+OffsetY,Right,Down,0xFFFFFFFF,0x8000FFFF)
end

--*****************************************************************************
local function CanvasBullet()
--*****************************************************************************
  local bID= R4u(0x15E4,"IWRAM")
  for i= 11, 29 do --Player projectiles zone
    local addr= 0x1DE4 + 0x74*i
    local bSpr= R2s(addr+0x08,"IWRAM") --Sprite
    if (bSpr ~= 0) and (bID < 9) then
      local bX, bY= R4s(addr+0x00,"IWRAM"), R4s(addr+0x04,"IWRAM")
      local HitBoxData
      if bID == 0 then
        HitBoxData= R4u(0x06EFDC + bSpr*4,"ROM")
        --No error catch. Try not to run this with absurdly high Sprite ID.
      else
        local bPtr= R4u(0x07E36C + bID*4,"ROM")
        HitBoxData= bPtr + 0x10*bSpr
      end
      local Left=  R4u(FetchAddrDomainGBA(HitBoxData+ 8))
      local Right= R4u(FetchAddrDomainGBA(HitBoxData+12))
      local Up=    R4u(FetchAddrDomainGBA(HitBoxData+ 0))
      local Down=  R4u(FetchAddrDomainGBA(HitBoxData+ 4))
      SafishRect(bX+Left+OffsetX,bY+Up+OffsetY,Right,Down,0xFF00FFFF,0x8000FF00)
    end
  end
end

local eFill= {
     0x60FF0000,0x60FF4000,0x60FF8000,0x60FFC000,0x60FFFF00
}
--*****************************************************************************
local function MakeEnemyColor(addr)
--*****************************************************************************
--Colored based on HP.
  local HP= R2s(addr+0x2C)
  local border= 0xFFFFFF00
  local fill= eFill[HP]
  if  HP <= 0 then border= 0xFFFF00FF; fill= 0x60FF00FF  end
  return border,fill or 0x6080FF00
end

--*****************************************************************************
local function CanvasEnemy()
--*****************************************************************************
  if Stage > 16 then return end
  local StagePtr= R4u(0x3CD5C4 + 4*Stage,"ROM")
  for i= 30, 59 do --Enemies occupy these slots only
    local addr= 0x1DE4 + 0x74*i
    local eSpr, eID= R2s(addr+0x08,"IWRAM"), R2u(addr+0x0A,"IWRAM") --Sprite, ID
    if (eSpr ~= 0) and (eID ~= 0) and (eID < 31) then
      local eX, eY= R4s(addr+0x00,"IWRAM"), R4s(addr+0x04,"IWRAM")
      local IsItem= (R2s(addr+0x3C,"IWRAM") == 2)
      local EnemyPtr
      if IsItem then
        EnemyPtr= R4u(0x06885C + eID*4,"ROM")
      else
        EnemyPtr= R4u(FetchAddrDomainGBA(StagePtr+eID*4))
      end
      local HitBoxData= EnemyPtr + 0x10*eSpr
      local Left=  R4u(FetchAddrDomainGBA(HitBoxData+ 8))
      local Right= R4u(FetchAddrDomainGBA(HitBoxData+12))
      local Up=    R4u(FetchAddrDomainGBA(HitBoxData+ 0))
      local Down=  R4u(FetchAddrDomainGBA(HitBoxData+ 4))
      if (Right ~= 0) and (Down ~= 0) then
        local border,fill= MakeEnemyColor(addr)
        if IsItem then border,fill= 0xFF00FF00, 0x600000FF end
        SafishRect(eX+Left+OffsetX,eY+Up+OffsetY,Right,Down,bClr,fClr)
      else
        SafishRect(eX+OffsetX,eY+OffsetY,3,3,0xFFFF8000)
      end
    end
  end
end

--*****************************************************************************
local function CanvasObjects()
--*****************************************************************************
  CanvasPlayer()
  CanvasBullet()
  CanvasEnemy()
end

--*****************************************************************************
local function HandleCanvas()
--*****************************************************************************
  cnv.Clear(0xFF404040)
  UpdateStats()
  CanvasTerrain()
  CanvasBorder()
  CanvasObjects()
  cnv.Refresh()
end

--[[
local ScaleDiv= 2
local OffsetX= 0
local OffsetY= 0
--*****************************************************************************
local function Rescale(v)
--*****************************************************************************
  ScaleDiv= v
  OffsetX= (240 - math.floor(240/v))/2
  OffsetY= (160 - math.floor(160/v))/2
end
Rescale(ScaleDiv)

--*****************************************************************************
local function DrawRescaledBorder()
--*****************************************************************************
  if ScaleDiv <= 1 then return end
  gui.drawRectangle(
    OffsetX,
    OffsetY,
    math.floor(240/ScaleDiv),
    math.floor(160/ScaleDiv),
    0x80C0C0C0 --Yay, border color
  )
end

--*****************************************************************************
local function DrawScaledHitbox(x1,y1,x2,y2,fc,bc)
--*****************************************************************************
  if x1 == x2 then return end
  x1= x1/ScaleDiv+OffsetX
  y1= y1/ScaleDiv+OffsetY
  x2= x2/ScaleDiv+OffsetX
  y2= y2/ScaleDiv+OffsetY
  gui.drawBox(x1,y1,x2,y2,fc,bc)
end
local dbox= DrawScaledHitbox

--*****************************************************************************
local function PlayerHitbox()
--*****************************************************************************
  local addr= 0x1DE4 + 0x74*1
  local Spr= R2s(addr+0x08,"IWRAM")
  local pX, pY= R4s(addr+0x00,"IWRAM"), R4s(addr+0x04,"IWRAM")
  local BoxAddr= 0x087B64 + 0x10*Spr
  local Left=  R4u(BoxAddr+ 8,"ROM")
  local Right= R4u(BoxAddr+12,"ROM")+Left
  local Up=    R4u(BoxAddr+ 0,"ROM")
  local Down=  R4u(BoxAddr+ 4,"ROM")+Up
  dbox(pX+Left,pY+Up,pX+Right,pY+Down)
end

--*****************************************************************************
local function BulletHitboxes()
--*****************************************************************************
--I'm hating these things.

  for i= 10, 29 do --Player projectiles zone
    local addr= 0x1DE4 + 0x74*i
    local bSpr, bID= R2s(addr+0x08,"IWRAM"), R2u(0x15E4,"IWRAM") --Sprite, ID
    if (bSpr ~= 0) and (bID < 9) then
      local bX, bY= R4s(addr+0x00,"IWRAM"), R4s(addr+0x04,"IWRAM")
      local HitBoxData
      if bID == 0 then
        HitBoxData= R4u(0x06EFDC + bSpr*4,"ROM")
        --No error catch. Try not to run this with absurdly high Sprite ID.
      else
        local bPtr= R4u(0x07E36C + bID*4,"ROM")
        HitBoxData= bPtr + 0x10*bSpr
      end
      local Left=  R4u(FetchAddrDomainGBA(HitBoxData+ 8))
      local Right= R4u(FetchAddrDomainGBA(HitBoxData+12))+Left
      local Up=    R4u(FetchAddrDomainGBA(HitBoxData+ 0))
      local Down=  R4u(FetchAddrDomainGBA(HitBoxData+ 4))+Up
      dbox(bX+Left,bY+Up,bX+Right,bY+Down)
    end
  end
end

--*****************************************************************************
local function EnemyHitboxes()
--*****************************************************************************
--Found a grail. Not sure if it's holy.

  local Stage= R4u(0x152C,"IWRAM");  if Stage > 16 then return end
  local StagePtr= R4u(0x3CD5C4 + 4*Stage,"ROM")
  for i= 30, 59 do --Enemies occupy these slots only
    local addr= 0x1DE4 + 0x74*i
    local eSpr, eID= R2s(addr+0x08,"IWRAM"), R2u(addr+0x0A,"IWRAM") --Sprite, ID
    if (eSpr ~= 0) and (eID ~= 0) and (eID < 31) then
      local eX, eY= R4s(addr+0x00,"IWRAM"), R4s(addr+0x04,"IWRAM")
      local EnemyPtr= R4u(FetchAddrDomainGBA(StagePtr+eID*4))
      local HitBoxData= EnemyPtr + 0x10*eSpr
      local Left=  R4u(FetchAddrDomainGBA(HitBoxData+ 8))
      local Right= R4u(FetchAddrDomainGBA(HitBoxData+12))+Left
      local Up=    R4u(FetchAddrDomainGBA(HitBoxData+ 0))
      local Down=  R4u(FetchAddrDomainGBA(HitBoxData+ 4))+Up
      dbox(eX+Left,eY+Up,eX+Right,eY+Down)
    end
  end
end
]]--

--*****************************************************************************
while true do
--*****************************************************************************
--Our overhead.
  HandleCanvas()
--  Fn()
--  DrawRescaledBorder()
--  EnemyHitboxes()
--  BulletHitboxes()
--  PlayerHitbox()

  emu.frameadvance()
end