User File #38775709014610846

Upload All User Files

#38775709014610846 - Zook Man ZX4 - External Radar, spawning borders

ZM_ExternalRadar_v3.lua
765 downloads
Uploaded 5/4/2017 6:10 AM by FatRatKnight (see all 245)
Nifty. Thanks Dacicus for figuring out the perimeter bounds.
The corner boxes only apply if you're moving diagonally in that direction. Otherwise, they are inert and spawn points do not react to them.
Hopefully this addition to the script makes it better at dealing with things decidedly off-screen. It was nice being able to see the moment enemies appear before. Now it's ever nicer to know ahead of time when they will, and if it can be avoided with camera trickery.
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- 40,OffsetY+  0,  8,160,0x40FF8000,0x10FFFFFF) --Left
  cnv.DrawRectangle(OffsetX+  0,OffsetY- 40,240,  8,0x40FF8000,0x10FFFFFF) --Up
  cnv.DrawRectangle(OffsetX+  0,OffsetY+168,240, 32,0x40FF8000,0x10FFFFFF) --Down

  cnv.DrawRectangle(OffsetX+241,OffsetY- 40, 59,  8,0x40FF20FF) --UpRight
  cnv.DrawRectangle(OffsetX+248,OffsetY- 40, 52, 39,0x40FF20FF)
  cnv.DrawRectangle(OffsetX+241,OffsetY+168, 59, 32,0x40FF20FF) --DownRight
  cnv.DrawRectangle(OffsetX+248,OffsetY+161, 52, 39,0x40FF20FF)
  cnv.DrawRectangle(OffsetX- 40,OffsetY- 40, 39,  8,0x40FF20FF) --UpLeft
  cnv.DrawRectangle(OffsetX- 40,OffsetY- 40,  8, 39,0x40FF20FF)
  cnv.DrawRectangle(OffsetX- 40,OffsetY+168, 39, 32,0x40FF20FF) --DownLeft
  cnv.DrawRectangle(OffsetX- 40,OffsetY+161,  8, 39,0x40FF20FF)
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