User File #51685242883649450

Upload All User Files

#51685242883649450 - Donald Land mini-map

mini_map.lua
Game: Donald Land ( NES, see all files )
554 downloads
Uploaded 12/6/2018 3:24 PM by Warepire (see all 18)
fantastic 4 DTC7 script.
-- HOWTO:
-- For the level you want to highlight specific IDs
-- Create a line like this:
-- interesting_blocks[<level>] = { <block>[, <block>, ...] }
-- e.g.
-- interesting_blocks[2] = { 0x79, 0x1b }
local interesting_blocks = {}
interesting_blocks[1] = { 0x47 }
interesting_blocks[2] = { 0x79, 0x1b }
interesting_blocks[3] = { 0x47 }
interesting_blocks[4] = { 0x79 }
interesting_blocks[5] = { 0x38 }
interesting_blocks[6] = { 0x38 }

local level_section_table = {}
-- address when mapped, number of sections
-- everything is in bank 4
level_section_table[01] = { 0xbc36, 2 }
level_section_table[02] = { 0xbc56, 2 }
level_section_table[03] = { 0xbc76, 3 }
level_section_table[04] = { 0xbca6, 3 }
level_section_table[05] = { 0xbcc6, 2 }
level_section_table[06] = { 0xbce6, 5 }
level_section_table[07] = { 0xbd16, 2 }
level_section_table[08] = { 0xbd36, 2 }
level_section_table[09] = { 0xbd56, 8 }
level_section_table[10] = { 0xbdb6, 3 }
level_section_table[11] = { 0xbdd6, 2 }
level_section_table[12] = { 0xbdf6, 23 }

-- BizHawk only!
local EAL = {}
do
    local mem = nil
    local ROM = nil
end
-- EAL Memory namespace functions
EAL.mem = {}
do
    function EAL.mem.read_u8(addr)
        return memory.read_u8(addr)
    end
    function EAL.mem.read_s8(addr)
        return memory.read_s8(addr)
    end
    function EAL.mem.read_u16_be(addr)
        return memory.read_u16_be(addr)
    end
    function EAL.mem.read_s16_be(addr)
        return memory.read_s16_be(addr)
    end
    function EAL.mem.read_s16_be_scatter(hi, lo)
        local val = memory.read_u8(hi)
        val = bit.lshift(val, 8)
        val = bit.bor(val, memory.read_u8(lo))
        if val > 0x7FFF then
            return val - 0x10000
        else
            return val
        end
    end
    function EAL.mem.read_u16_le(addr)
        return memory.read_u16_le(addr)
    end
end
EAL.ROM = {}
do
    -- `addr` is the address as seen from the system bus
    local function get_addr_in_bank(bank, addr)
        local bank_addr = addr - 0x8000
        if addr > 0xC000 then -- high region
            bank_addr = bank_addr - 0x4000
        end
        return bank_addr
    end
    -- `addr` is the address as seen from the system bus
    local function get_offset_in_prg(bank, addr)
        local bank_addr = get_addr_in_bank(bank, addr)
        local offset = (bank * 0x4000) + bank_addr
        return offset
    end
    -- Usage note: `addr` is the address as seen from the system bus
    function EAL.ROM.read_u8(bank, addr)
        local offset = get_offset_in_prg(bank, addr)
        return memory.read_u8(offset, "PRG ROM")
    end

    -- Usage note: `addr` is the address as seen from the system bus
    function EAL.ROM.read_u16_be(bank, addr)
        local offset = get_offset_in_prg(bank, addr)
        return memory.read_u16_be(offset, "PRG ROM")
    end

    function EAL.ROM.read_u16_le(bank, addr)
        local offset = get_offset_in_prg(bank, addr)
        return memory.read_u16_le(offset, "PRG ROM")
    end
end

function get_screen_x_coordinate()
    return EAL.mem.read_u16_le(0x00CC)
end

function get_position()
    local onscreen_x = EAL.mem.read_u8(0x0091)
    local x = onscreen_x + get_screen_x_coordinate()
    local subx = EAL.mem.read_u8(0x0092)
    local y = EAL.mem.read_u8(0x0095)
    return x, subx, y
end
function get_level_info()
    local level = EAL.mem.read_u8(0x0051)
    local section = EAL.mem.read_u8(0x0052)
    return level, section
end

function get_level_length(level)
    local bank = 4
    local highest = 0
    for i=0,(level_section_table[level][2] - 1) do
        local max = EAL.ROM.read_u8(bank, level_section_table[level][1] + (i * 16) + 5)
        if max > highest then
            highest = max
        end
    end
    return highest
end

function get_block_id_addr(level)
    local bank = 7
    local lvl_bank = EAL.ROM.read_u8(bank, 0xC5AD + level)
    local offset_table_addr = EAL.ROM.read_u16_le(bank, 0xD102 + (level * 2)) + 3
    local data_table_addr = EAL.ROM.read_u16_le(bank, 0xCD9F + (level * 2))

    return lvl_bank, offset_table_addr, data_table_addr
end

local canvas = nil
local canvas_width = 0
local canvas_height = 0
function do_block_id_canvas()
    canvas = nil -- Hoping for the garbage collection to not leak too much
    local level, section = get_level_info()
    if level < 1 or level > 12 then
        return
    end

    local level_length = get_level_length(level)
    local screenx = get_screen_x_coordinate()
    local bank, offsets_addr, data_addr = get_block_id_addr(level)

    if interesting_blocks[level] == nil then
        return
    end

    canvas_width = level_length * 256 / 4
    canvas_height = (13 * 16 / 4) + 10 -- allocate 10 pixels for the "progress bar"
    canvas = gui.createcanvas(canvas_width, canvas_height) -- 4 pixels for each block
    canvas.SetDefaultForegroundColor("black")
    canvas.SetDefaultBackgroundColor("white")
    canvas.SetTitle("Level ID Map")

    -- offsets are multiplied by 208
    for x=0,level_length do
        local offset = EAL.ROM.read_u8(bank, offsets_addr + x) * 208 --13*16
        for i=0,15 do -- 1 -> 16
            for y=0,12 do -- 1 -> 13
                local id = EAL.ROM.read_u8(bank, data_addr + offset + i + (y * 16))
                for k,v in ipairs(interesting_blocks[level]) do
                    if id == v then
                        local dx = ((x * 64) + (i * 4))
                        local dy = (y * 4)
                        canvas.DrawRectangle(dx, dy, 4, 4, "red", "red")
                    end
                end
            end
        end
    end

end

local previous_x = 0
function do_level_progress()
    if canvas == nil then -- if we end up called before a level
        return
    end

    local level, section = get_level_info()
    if level < 1 or level > 12 then
        return
    end

    local x, subx, y = get_position()
    x = math.floor(x / 16)
    -- Clearing the canvas is expensive, only draw when there has been a significant movement
    if x == previous_x then
        return
    end

    -- draw one black and one white bar
    canvas.DrawRectangle(0, canvas_height-10, x*4, 10, "black", "black")
    canvas.DrawRectangle(x*4, canvas_height-10, canvas_width-(x*4), 10, "white", "white")
    canvas.Refresh()

    previous_x = x
end

local id0 = nil
local id1 = nil
local id2 = nil
local events = {}
function do_exit()
    --event.unregisterbyid(id0)
    --event.unregisterbyid(id1)
    --event.unregisterbyid(id2)
end
id0 = event.onmemorywrite(do_block_id_canvas, 0x0051)
id1 = event.onmemorywrite(do_level_progress, 0x0091)
id2 = event.onexit(do_exit)

do_block_id_canvas() -- Just in case script is started mid-level
do_level_progress() -- Just in case script is started mid-level

while true do
    emu.frameadvance()
end