CCCPaste

KatAM Camera Script

-- KatAM level mapping tool by Dotsarecool
-- outputs a bunch of images of the level to be stitched together by another program
local FOLDER = "C:/Users/Frieze/Desktop/KatAM/"

local BACKGROUND_LAYER_REGISTER = 0x3691
local LEVEL_NUMBER = 0x20F40
local PREV_LEVEL = 0x20FE6
local LEVEL_WIDTH = 0x235EC
local LEVEL_HEIGHT = 0x235EE
local KIRBY_X_POS = 0x20F20
local KIRBY_Y_POS = 0x20F24
local GAME_MODE_PTR = 0x20F58

-- camera position
local CAMERA_X_POS = 0x23660
local CAMERA_Y_POS = 0x23662
-- "updated" camera position
-- if camera is out of bounds, this register holds the camera position mod the level width/height
local CAMERA_X_POS_2 = 0x23664
local CAMERA_Y_POS_2 = 0x23666

local ALL_LEVELS = {0x0065, 0x0066, 0x0067, 0x0068, 0x006A, 0x006B, 0x006C, 0x006E, 0x0071, 0x0072, 0x0073, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007E, 0x007F, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B4, 0x00B5, 0x00BE, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C8, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x0122, 0x0123, 0x012C, 0x0130, 0x0134, 0x0136, 0x0138, 0x0139, 0x013B, 0x013C, 0x013D, 0x013E, 0x0141, 0x0142, 0x0143, 0x0144, 0x0145, 0x0146, 0x0186, 0x0190, 0x0191, 0x0192, 0x0193, 0x0194, 0x0195, 0x0196, 0x0197, 0x0198, 0x0199, 0x019B, 0x019C, 0x019D, 0x019E, 0x019F, 0x01A0, 0x01A1, 0x01A2, 0x01A3, 0x01A4, 0x01A5, 0x01A6, 0x01A7, 0x01A8, 0x01A9, 0x01AA, 0x01AB, 0x01EA, 0x01F4, 0x01F5, 0x01F6, 0x01F7, 0x01F8, 0x01FB, 0x01FC, 0x01FD, 0x01FE, 0x01FF, 0x0200, 0x0201, 0x0202, 0x0203, 0x0204, 0x0205, 0x020B, 0x020C, 0x020D, 0x020E, 0x020F, 0x0210, 0x0211, 0x0212, 0x0213, 0x0214, 0x0215, 0x0216, 0x0217, 0x0218, 0x0219, 0x021A, 0x021B, 0x021C, 0x021D, 0x021E, 0x021F, 0x0220, 0x0221, 0x0222, 0x0223, 0x0227, 0x0229, 0x022A, 0x022B, 0x022C, 0x022D, 0x023A, 0x023B, 0x024E, 0x024F, 0x0250, 0x0258, 0x0259, 0x025B, 0x025C, 0x025D, 0x025E, 0x0260, 0x0261, 0x0262, 0x0263, 0x0264, 0x0265, 0x0266, 0x0268, 0x0269, 0x026A, 0x026B, 0x026C, 0x026D, 0x026E, 0x026F, 0x02B2, 0x02BC, 0x02BE, 0x02C0, 0x02C1, 0x02C2, 0x02C3, 0x02C4, 0x02C5, 0x02C6, 0x02C7, 0x02C8, 0x02CA, 0x02CC, 0x02CD, 0x02CF, 0x02D0, 0x02DA, 0x02DB, 0x02DC, 0x02DD, 0x02DE, 0x02DF, 0x02E0, 0x02E1, 0x02E2, 0x02E3, 0x02E4, 0x02E5, 0x02E6, 0x02E7, 0x02E8, 0x02E9, 0x0316, 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x032A, 0x032B, 0x032E, 0x032F, 0x0330, 0x0333, 0x0335, 0x0336, 0x0337, 0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F, 0x0340, 0x037A, 0x038D, 0x038E, 0x038F, 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0396, 0x03CA}

-- Start Main --

function snapshot()
    local i = 0
    local x = 0
    local y = 0
    local done = false
    local level = -1;
    local w = 0
    local h = 0

    console.writeline("Starting...")

    -- disable the background for prettier pictures
    memory.usememorydomain("IWRAM")
    memory.writebyte(BACKGROUND_LAYER_REGISTER, 0x08)
    memory.usememorydomain("EWRAM")
    -- grab level info
    level = memory.read_u16_le(LEVEL_NUMBER)
    -- grab width and height of level so we know how big the final map should be
    -- (I should have used these values to iterate over the level space, but these values were found after the majority of the script was written)
    w = memory.read_u16_le(LEVEL_WIDTH)
    h = memory.read_u16_le(LEVEL_HEIGHT)

    while not done do
        i = i + 1

        memory.write_s16_le(CAMERA_X_POS, x)
        memory.write_s16_le(CAMERA_Y_POS, y)
        memory.write_s16_le(KIRBY_X_POS, 0)
        memory.write_s16_le(KIRBY_Y_POS, 0)
        emu.frameadvance()
        
        -- if we hit the right edge of the level
        if memory.read_s16_le(CAMERA_X_POS_2) ~= x then
            x = 0
            y = y + 20 * 8

            -- move camera down
            memory.write_s16_le(CAMERA_X_POS, x)
            memory.write_s16_le(CAMERA_Y_POS, y)
            memory.write_s16_le(KIRBY_X_POS, 0)
            memory.write_s16_le(KIRBY_Y_POS, 0)
            emu.frameadvance()
            
            -- if we hit the bottom edge of the level
            if memory.read_s16_le(CAMERA_Y_POS_2) ~= y then
                -- we done, boys
                done = true
            end
        end
        
        if not done then
            local j = 0
            while j < 1 do -- set 1 to something higher to enable slow mode
                memory.write_s16_le(CAMERA_X_POS, x)
                memory.write_s16_le(CAMERA_Y_POS, y)
                memory.write_s16_le(KIRBY_X_POS, 0)
                memory.write_s16_le(KIRBY_Y_POS, 0)
            
                --gui.drawText(20, 45, "x: " .. x)
                --gui.drawText(20, 55, "y: " .. y)
                
                emu.frameadvance()
                j = j + 1
            end
            -- save the image
            client.screenshot(FOLDER .. "level-" .. level .. "_w-" .. w .. "_h-" .. h .."/y-" .. y .."_x-" .. x .. ".png")
            
            x = x + 30 * 8
        end
    end

    memory.usememorydomain("IWRAM")
    -- enable background only
    memory.writebyte(BACKGROUND_LAYER_REGISTER, 0x01)
    emu.frameadvance()
    -- save image of the background
    client.screenshot(FOLDER .. "level-" .. level .. "_w-" .. w .. "_h-" .. h .."/b.png")

    -- restore background layers
    memory.writebyte(BACKGROUND_LAYER_REGISTER, 0x1B)
    console.writeline("Done.")
end

for i=1,#ALL_LEVELS do
    memory.usememorydomain("EWRAM")
    memory.write_s16_le(PREV_LEVEL, ALL_LEVELS[i]) -- set level to return to after death
    
    local input = joypad.get()
    input["R"] = true
    joypad.set(input)
    emu.frameadvance() -- bring up ability chooser
    input["R"] = false
    input["Select"] = true
    joypad.set(input)
    emu.frameadvance() -- restart room
    input["Select"] = false
    joypad.set(input)
    emu.frameadvance()
    emu.frameadvance()
    emu.frameadvance()
    emu.frameadvance() -- wait for room to load
    memory.write_s32_le(GAME_MODE_PTR, 0x08FA1001)
    emu.frameadvance() -- freeze kirby but allow camera movement
    -- (to prevent enemies from hitting kirby during screenshot/camera shake effect)
    
    for j=1,60 do
        emu.frameadvance() -- wait for fade out to finish
    end
    
    snapshot() -- screenshot level
    
end