User File #38774185940666522

Upload All User Files

#38774185940666522 - Zook Man ZX4 - External Radar, now with one spawn box

ZM_ExternalRadar_v2a.lua
755 downloads
Uploaded 5/4/2017 4:31 AM by FatRatKnight (see all 245)
I've also added commented lines expecting the other three boxes.
If you want to question the fact I have a function (CanvasSpawnBorders) for the rather specific purpose of painting these spawn boxes, then you should also question my decision for making a function that paints exactly one box (CanvasBorder). My decisions are generally based on what information is being handled.
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 CanvasSpawnBorders()
--*****************************************************************************
  cnv.DrawRectangle(OffsetX+248,OffsetY+  0, 52,160,0x40FF8000,0x10FFFFFF) --Right
--  cnv.DrawRectangle(OffsetX- 50,OffsetY+  0, 10,160,0x40FFFFFF,0x10FFFFFF) --Left
--  cnv.DrawRectangle(OffsetX+  0,OffsetY,240, 20,0x40FFFFFF,0x10FFFFFF) --Up
--  cnv.DrawRectangle(OffsetX+  0,OffsetY,240, 20,0x40FFFFFF,0x10FFFFFF) --Down
end

--*****************************************************************************
local function CanvasSpawns()
--*****************************************************************************
--[[Spawn list offsets:
+00,2 - X position
+02,2 - Y position
+04,2 - Does it exist (?)
+06,2 - Enemy to spawn (?)
+08,2 -
+0A,2 -
+0C,4 - ? What, there's some apparently random numbers here...
+10,2 -
]]--
  if Stage > 16 then return end --Just in case
  CanvasSpawnBorders()
  local ptr= R4u(0x3CCF68+4*Stage,"ROM")      --Getting stage pointer
  ptr= R4u(FetchAddrDomainGBA(ptr+4*Segment)) --Getting Segment pointer of stage
  local Xpos= R2s(FetchAddrDomainGBA(ptr))
  local SpIndex= 0
  while Xpos ~= -1 do --For whatever reason, this offset is double-burdened as a magic end marker
    if R2s(FetchAddrDomainGBA(ptr+4)) ~= 0 then --Apparently it's 1 if it exists
      local Ypos= R2s(FetchAddrDomainGBA(ptr+2))
      local clr= 0xFFFFFFFF --White
      if R1u(0x7C24+SpIndex,"IWRAM") ~= 0 then clr= 0xFFC08080 end --Reddish

      Xpos= Xpos - CamX + OffsetX
      Ypos= Ypos - CamY + OffsetY
      SafishRect(Xpos-3,Ypos-3,2,2,clr)
      SafishRect(Xpos-3,Ypos+1,2,2,clr)
      SafishRect(Xpos+1,Ypos-3,2,2,clr)
      SafishRect(Xpos+1,Ypos+1,2,2,clr)
    end

    SpIndex= SpIndex+1; if SpIndex >= 32 then return end --Something's wrong. Don't crash script.
    ptr= ptr+18
    Xpos= R2s(FetchAddrDomainGBA(ptr))
  end
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,"IWRAM")
  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 bClr,fClr= MakeEnemyColor(addr)
        if IsItem then bClr,fClr= 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()
  CanvasSpawns()
  CanvasObjects()
  cnv.Refresh()
end

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

  emu.frameadvance()
end