User File #39129448785778607

Upload All User Files

#39129448785778607 - Zook Man ZX4 - Terrain Tiles painter (v2), added pixels, global map load

ZM_TileOverlayv2.lua
766 downloads
Uploaded 5/20/2017 4:31 AM by FatRatKnight (see all 245)
Because ThunderAxe31's brain refuses to acknowledge how the back foot pixel works. It's not your fault, it's your brain's fault.
I threw together the ImportantPlayerPixels function in a hurry and all that. It paints tiny little arrows indicating what pixel the game cares about based on the situation. I advise ThunderAxe31 to examine these arrows in frame advance. Since I threw the function together in a hurry, I can't really guarantee its accuracy.
On a side note, this script now sticks the maps into a global variable, so that, once I upload an update to the ExternalRadar, the two scripts will be aware of a loaded map already done and share the resource instead of having local copies. Shame a core reboot kills the global variables, and a reboot is done every time you switch movies. Perhaps good for stability, but makes it problematic if a script has several seconds loading time.
Oh, right. Someone did have a smart loader that grabs only a smaller portion, the one relevant to where you're at. I should track that down, or design my own, perhaps.
--GBA Zook Man ZX4 -- Tile Overlay script
--Loads all those pretty maps, then displays tiles as you wander aimlessly.
--FatRatKnight

--memory.usememorydomain("ROM")
local R4u , R4s= memory.read_u32_le , memory.read_s32_le
local R2u , R2s= memory.read_u16_le , memory.read_s16_le
local R1u      = memory.read_u8

local ROMmin,ROMmax= 0x08000000, 0x08000000+memory.getmemorydomainsize("ROM")

--*****************************************************************************
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),1)
    end
end

--*****************************************************************************
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.

  local Maps= {}

  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

  return Maps
end

if not Maps then   --Globalize the Maps table.
  Maps= LoadMaps() --This way it stays loaded after script expires.
end                --Core reboots (such as starting a new movie) still resets.


--x - X location on screen. Might be negative./
--y - Y location on screen.
--i - Block index, in case you put the function in multiple places

local function NullFn() return end

local DefaultColors= {
[0x00]= 0xFFFFFFFF,
[0x10]= 0xFF00FF00,
[0x60]= 0xFFFFFF00,
[0x70]= 0xFF00FFFF
}
--*****************************************************************************
local function Tile_Default(x,y,i)
--*****************************************************************************
  local clr= DefaultColors[i - i%0x10] or 0xFFFF00FF
  gui.pixelText(x,y,string.format("%X",i%0x10),clr)
end

local BlockColors= {
[0]=0x00000000, -- Open air
    0xC0FFFFFF, -- Floor
    0xC0FF2000, -- Spikes
    0xC0000000, -- Walls (basic)
    0x00000000, -- Unused
    0xC000FFFF, -- Walljump

[0x0A]=0x80FFFF00, --Slope magnet downward from above
[0x0B]=0x80FFFF00, --Slope magnet upward from below

[0x14]=0x8040A000, --Ladder top
[0x15]=0x8040FF40, --Ladder mid
[0x16]=0x8040A000, --Ladder bottom

[0x17]=0xC00000FF, --Save Platform mid
[0x18]=0xC00000FF, --Save Platform end

[0x19]=0xC08000FF, --Capsule Head
[0x1A]=0xC08000FF, --Capsule Body
[0x1B]=0xC08000FF, --Capsule Leg
[0x1C]=0xC08000FF, --Capsule Arm

[0x1F]=0xC08000FF, --Autoscroll ender tiles
}
--*****************************************************************************
local function Tile_Blocks(x,y,i)
--*****************************************************************************
  local clr= BlockColors[i] or 0xFFFF00FF
  local bclr= bit.band(clr/2,0xFF000000) + bit.band(clr,0x00FFFFFF)
  gui.drawRectangle(x,y,7,7,clr,bclr)
end

local BreakColors= {
  [0x60]=0x60FF0000, [0x61]=0x60FFFF00, [0x62]=0x6000FF00, [0x63]=0x6000FFFF,
  [0x64]=0x600000FF, [0x65]=0x60FF00FF, [0x66]=0x60FFFFFF, [0x67]=0x60000000,
  [0x68]=0x6080FF00, [0x69]=0x6000FF80, [0x6A]=0x600080FF, [0x6B]=0x608000FF,
  [0x6C]=0x60FF0080, [0x6D]=0x60FF8000, [0x6E]=0x60808080, [0x6F]=0x60800080,
  [0x70]=0x60808000, [0x71]=0x60008080, [0x72]=0x60FF80FF, [0x73]=0x60FFFF80,
  [0x74]=0x6080FFFF, [0x75]=0x6080FF80, [0x76]=0x608080FF
}
--*****************************************************************************
local function Tile_Break(x,y,i)
--*****************************************************************************
  local clr= BreakColors[i] or 0xC0FF00FF
  gui.drawRectangle(x,y,7,7,0,clr)
end

local SlopeHeights= {
  [0x06]={L=7,R=4},[0x07]={L=3,R=0},[0x08]={L=0,R=3},[0x09]={L=4,R=7},
  [0x0C]={L=7,R=6},[0x0D]={L=5,R=4},[0x0E]={L=3,R=2},[0x0F]={L=1,R=0},
  [0x10]={L=0,R=1},[0x11]={L=2,R=3},[0x12]={L=4,R=5},[0x13]={L=6,R=7}
}
--*****************************************************************************
local function Tile_Slope(x,y,i)
--*****************************************************************************
  gui.drawRectangle(x,y,7,7,0x40808000,0x40808000)
  local h= SlopeHeights[i]; if not h then return end
  local y1,y2= h.L+y,h.R+y
  gui.drawLine(x,y1,x+7,y2,0xFFFFFFFF)
end

--[[
00 Air            01 Floor          02 Spikes         03 Solid
04 (unused)       05 Walljump       06 / Steep1       07 / Steep2
08 \ Steep1       09 \ Steep2       0A Magnet down    0B Magnet up
0C / Gentle1      0D / Gentle2      0E / Gentle3      0F / Gentle4
10 \ Gentle1      11 \ Gentle2      12 \ Gentle3      13 \ Gentle4
14 Ladder Top     15 Ladder         16 Ladder Bottom  17 Save platform
18 Save platform  19 Capsule Head   1A Capsule Body   1B Capsule Leg
1C Capsule Arm    1D (unused)       1E (unused)       1F Autoscroll end

60 - 76 Breakable blocks
]]--

local Patterns= {
[0]=NullFn     ,Tile_Blocks,Tile_Blocks,Tile_Blocks, --Air,Floor,Spike,Solid
    nil        ,Tile_Blocks,Tile_Slope ,Tile_Slope , --nil,Wj,Sslope /
    Tile_Slope ,Tile_Slope ,Tile_Blocks,Tile_Blocks, --Sslope \,magnet
    Tile_Slope ,Tile_Slope ,Tile_Slope ,Tile_Slope , --Gslope /
    Tile_Slope ,Tile_Slope ,Tile_Slope ,Tile_Slope , --Gslope \
    Tile_Blocks,Tile_Blocks,Tile_Blocks,Tile_Blocks, --Ladder, sPlatform
    Tile_Blocks,Tile_Blocks,Tile_Blocks,Tile_Blocks, --sPlatform, Capsule
    Tile_Blocks,nil        ,nil        ,Tile_Blocks, --Capsule,nil,nil,AS end

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

--*****************************************************************************
local function TileOverlay2()
--*****************************************************************************
  local Stage, Segment= R4s(0x152C,"IWRAM"),R4s(0x1530,"IWRAM")
  local Map= Maps[Stage]; if not Map then return end
  Map= Map[Segment]     ; if not Map then return end

--Conveniently, the game has width and height loaded in RAM.
  local SegX, SegY= R4s(0x153C,"IWRAM"), R4s(0x1540,"IWRAM")
  if (SegX < 1) or (SegY < 1) or (SegX*SegY > 0x1000) then return end

  local CamX, CamY= R4s(0x14F0,"IWRAM"), R4s(0x14F4,"IWRAM")
  local xp, yp= -(CamX%8), -(CamY%8)

  for y= 0,20 do
    local Top= y*8 + yp
    for x= 0,30 do
      local Left= x*8 + xp
      local xt,yt= (math.floor(CamX/8)+x),(math.floor(CamY/8)+y)
      local Tile= Map[yt*32*SegX + xt]
      if Tile then
        local Fn= Patterns[Tile] or Tile_Default
        Fn(Left,Top,Tile)
      end
    end
  end
end

--*****************************************************************************
local function TileOverlay()
--*****************************************************************************
  local Stage= R4u(  0x152C,"IWRAM")
  if Stage > 16 then
    gui.pixelText(5,5,"Bad Stage")
    return
  end

  local SegmentPtr=   R4u(0x3CD8B0 + 4*Stage,"ROM")
  SegmentPtr= R4u(FetchAddrDomainGBA(SegmentPtr + R4u(0x1530,"IWRAM")*4))
  if (SegmentPtr < ROMmin) or (SegmentPtr >= ROMmax) then
    gui.pixelText(5,5,"Bad Segment index")
    return
  end
  local SegX, SegY= R2s(FetchAddrDomainGBA(SegmentPtr)), R2s(FetchAddrDomainGBA(SegmentPtr+2))
  SegmentPtr= SegmentPtr+4
  if (SegX < 1) or (SegY < 1) or (SegX*SegY > 0x1000) then
    gui.pixelText(5,5,"Bad Segment (wait, how?)")
    return
  end

  local BlockSetPtr= R4u(0x3CD86C + 4*Stage,"ROM")
  local CamX,CamY= R4s(0x14F0,"IWRAM"),R4s(0x14F4,"IWRAM")
  local xp,yp= -(CamX%8), -(CamY%8)

--I will do things inefficiently: For every tile, ask what block.
  for y= 0,20 do
    local Top= y*8 + yp
    for x= 0,30 do
      local Left= x*8 + xp

--PaintBlock
      local xx,yy= math.floor((CamX + x*8)/256), math.floor((CamY + y*8)/256)
      if (xx < SegX) and (yy < SegY) then
        local BlockIndex= R2u(FetchAddrDomainGBA(SegmentPtr + (yy*SegX + xx)*2))
        local Block= R4u(FetchAddrDomainGBA(BlockSetPtr + BlockIndex*4))

        local xt,yt= (math.floor(CamX/8)+x)%32,(math.floor(CamY/8)+y)%32
        local Tile= R1u(FetchAddrDomainGBA(Block + 2 + xt + yt*32))
        local Fn= Patterns[Tile] or Tile_Default
        Fn(Left,Top,Tile)
      end
    end
  end
end

--*****************************************************************************
local function ImportantPlayerPixels(PlX,PlY)
--*****************************************************************************
  local PlAir= R4u(0x1554,"IWRAM") == 1
  local PlLeft=R4u(0x1E64,"IWRAM") == 1
  local PlJump=R4u(0x1E6C,"IWRAM")
--  local PlAir= R4u(0x1500,"IWRAM")

--(16,48) Left fall check
  if (not PlAir) or ((PlJump >= 20) and not PlLeft) then
    gui.drawPixel(PlX+16,PlY+48,0xFFFFFFFF)
    gui.drawPixel(PlX+15,PlY+47,0xFFFFFFFF)
    gui.drawPixel(PlX+16,PlY+47,0xFFFFFFFF)
    gui.drawPixel(PlX+17,PlY+47,0xFFFFFFFF)
  end

--(24,48) Right fall check
  if (not PlAir) or ((PlJump >= 20) and PlLeft) then
    gui.drawPixel(PlX+24,PlY+48,0xFFFFFFFF)
    gui.drawPixel(PlX+23,PlY+47,0xFFFFFFFF)
    gui.drawPixel(PlX+24,PlY+47,0xFFFFFFFF)
    gui.drawPixel(PlX+25,PlY+47,0xFFFFFFFF)
  end

--(16, 8) Left head check
  if PlAir and (PlJump < 20) and (not PlLeft) then
    gui.drawPixel(PlX+16,PlY+ 8,0xFFFF80FF)
    gui.drawPixel(PlX+15,PlY+ 9,0xFFFF80FF)
    gui.drawPixel(PlX+16,PlY+ 9,0xFFFF80FF)
    gui.drawPixel(PlX+17,PlY+ 9,0xFFFF80FF)
  end

--(24, 8) Right head check
  if PlAir and (PlJump < 20) and PlLeft then
    gui.drawPixel(PlX+24,PlY+ 8,0xFFFF80FF)
    gui.drawPixel(PlX+23,PlY+ 9,0xFFFF80FF)
    gui.drawPixel(PlX+24,PlY+ 9,0xFFFF80FF)
    gui.drawPixel(PlX+25,PlY+ 9,0xFFFF80FF)
  end

--( 8,47) Left horizontal check
  if PlLeft then
    gui.drawPixel(PlX+ 8,PlY+47,0xFFFFFF00)
    gui.drawPixel(PlX+ 9,PlY+46,0xFFFFFF00)
    gui.drawPixel(PlX+ 9,PlY+47,0xFFFFFF00)
    gui.drawPixel(PlX+ 9,PlY+48,0xFFFFFF00)
  end

--(32,47) Right horizontal check
  if not PlLeft then
    gui.drawPixel(PlX+32,PlY+47,0xFFFFFF00)
    gui.drawPixel(PlX+31,PlY+46,0xFFFFFF00)
    gui.drawPixel(PlX+31,PlY+47,0xFFFFFF00)
    gui.drawPixel(PlX+31,PlY+48,0xFFFFFF00)
  end

--( 8,32) Left walljump check
  if PlAir and (PlJump >= 20) and PlLeft then
    gui.drawPixel(PlX+ 8,PlY+32,0xFF00FFFF)
    gui.drawPixel(PlX+ 9,PlY+32,0xFF00FFFF)
    gui.drawPixel(PlX+10,PlY+32,0xFF00FFFF)
    gui.drawPixel(PlX+ 9,PlY+31,0xFF00FFFF)
    gui.drawPixel(PlX+10,PlY+30,0xFF00FFFF)
  end

--(32,32) Right walljump check
  if PlAir and (PlJump >= 20) and (not PlLeft) then
    gui.drawPixel(PlX+32,PlY+32,0xFF00FFFF)
    gui.drawPixel(PlX+31,PlY+32,0xFF00FFFF)
    gui.drawPixel(PlX+30,PlY+32,0xFF00FFFF)
    gui.drawPixel(PlX+31,PlY+31,0xFF00FFFF)
    gui.drawPixel(PlX+30,PlY+30,0xFF00FFFF)
  end

end


--*****************************************************************************
while true do
--*****************************************************************************
--Our overhead.

  TileOverlay2()
  ImportantPlayerPixels(R2s(0x1E58,"IWRAM"),R2s(0x1E5C,"IWRAM"))

  emu.frameadvance()
end