User File #22335975284900107

Upload All User Files

#22335975284900107 - NES Gauntlet - Map script

Gauntlet_map.lua
1049 downloads
Uploaded 4/24/2015 9:20 PM by FatRatKnight (see all 245)
This maps things really, really well. I hope. Since the script knows of the basic walls, and that it also knows of all map objects, this should cover everything there can possibly be in the stage.
Hit space to get a map sprawled out into the Output Console, as well as an object listing. I use "B" for whatever reason, to have the script spit out potential exits. I can confirm that room 98 really has no exits.
This doesn't actually peek into the ROM, only whatever is loaded in RAM. So I still need to reach the room in question to map it, and it still wouldn't get all the variations of it (treasure room exits, for the most part). Still, this gives me a near-perfect knowledge of each room so I won't miss any detail.
--*****************************************************************************
local Draw= {}
--*****************************************************************************
--Coordinates is the top-left pixel of the 3x5 digit.
--Used for drawing compact, colored numbers.

local Px,Li= gui.pixel, gui.line

Draw[0]= function(x,y,c) -- ###
    Li(x  ,y  ,x  ,y+4,c)-- # #
    Li(x+2,y  ,x+2,y+4,c)-- # #
    Px(x+1,y  ,c)        -- # #
    Px(x+1,y+4,c)        -- ###
end

Draw[1]= function(x,y,c) --  #
    Li(x  ,y+4,x+2,y+4,c)-- ##
    Li(x+1,y  ,x+1,y+3,c)--  #
    Px(x  ,y+1,c)        --  #
end                      -- ###

Draw[2]= function(x,y,c) -- ###
    Li(x  ,y  ,x+2,y  ,c)--   #
    Li(x  ,y+3,x+2,y+1,c)-- ###
    Li(x  ,y+4,x+2,y+4,c)-- #
    Px(x  ,y+2,c)        -- ###
    Px(x+2,y+2,c)
end

Draw[3]= function(x,y,c) -- ###
    Li(x  ,y  ,x+1,y  ,c)--   #
    Li(x  ,y+2,x+1,y+2,c)-- ###
    Li(x  ,y+4,x+1,y+4,c)--   #
    Li(x+2,y  ,x+2,y+4,c)-- ###
end

Draw[4]= function(x,y,c) -- # #
    Li(x  ,y  ,x  ,y+2,c)-- # #
    Li(x+2,y  ,x+2,y+4,c)-- ###
    Px(x+1,y+2,c)        --   #
end                      --   #

Draw[5]= function(x,y,c) -- ###
    Li(x  ,y  ,x+2,y  ,c)-- #
    Li(x  ,y+1,x+2,y+3,c)-- ###
    Li(x  ,y+4,x+2,y+4,c)--   #
    Px(x  ,y+2,c)        -- ###
    Px(x+2,y+2,c)
end

Draw[6]= function(x,y,c) -- ###
    Li(x  ,y  ,x+2,y  ,c)-- #
    Li(x  ,y+1,x  ,y+4,c)-- ###
    Li(x+2,y+2,x+2,y+4,c)-- # #
    Px(x+1,y+2,c)        -- ###
    Px(x+1,y+4,c)
end
                         -- ###
Draw[7]= function(x,y,c) --   #
    Li(x  ,y  ,x+1,y  ,c)--  ##
    Li(x+2,y  ,x+1,y+4,c)--  #
end                      --  #

Draw[8]= function(x,y,c) -- ###
    Li(x  ,y  ,x  ,y+4,c)-- # #
    Li(x+2,y  ,x+2,y+4,c)-- ###
    Px(x+1,y  ,c)        -- # #
    Px(x+1,y+2,c)        -- ###
    Px(x+1,y+4,c)
end

Draw[9]= function(x,y,c) -- ###
    Li(x  ,y  ,x  ,y+2,c)-- # #
    Li(x+2,y  ,x+2,y+3,c)-- ###
    Li(x  ,y+4,x+2,y+4,c)--   #
    Px(x+1,y  ,c)        -- ###
    Px(x+1,y+2,c)
end

Draw[10]=function(x,y,c) --  #
    Li(x  ,y+1,x  ,y+4,c)-- # #
    Li(x+2,y+1,x+2,y+4,c)-- # #
    Px(x+1,y  ,c)        -- ###
    Px(x+1,y+3,c)        -- # #
end

Draw[11]=function(x,y,c) -- ##
    Li(x  ,y  ,x  ,y+4,c)-- # #
    Li(x+1,y  ,x+2,y+1,c)-- ##
    Li(x+1,y+4,x+2,y+3,c)-- # #
    Px(x+1,y+2,c)        -- ##
end

Draw[12]=function(x,y,c) --  #
    Li(x  ,y+1,x  ,y+3,c)-- # #
    Li(x+1,y  ,x+2,y+1,c)-- #
    Li(x+1,y+4,x+2,y+3,c)-- # #
end                      --  #

Draw[13]=function(x,y,c) -- ##
    Li(x  ,y  ,x  ,y+4,c)-- # #
    Li(x+2,y+1,x+2,y+3,c)-- # #
    Px(x+1,y  ,c)        -- # #
    Px(x+1,y+4,c)        -- ##
end

Draw[14]=function(x,y,c) -- ###
    Li(x  ,y  ,x  ,y+4,c)-- #
    Li(x+1,y  ,x+2,y  ,c)-- ##
    Li(x+1,y+4,x+2,y+4,c)-- #
    Px(x+1,y+2,c)        -- ###
end

Draw[15]=function(x,y,c) -- ###
    Li(x  ,y  ,x  ,y+4,c)-- #
    Li(x+1,y  ,x+2,y  ,c)-- ##
    Px(x+1,y+2,c)        -- #
end                      -- #

--*****************************************************************************
local function __DN_AnyBase(right, y, Number, c, bkgnd, div)
--*****************************************************************************
-- Works with any base from 2 to 16. Paints the input number.
-- Returns the x position where it would paint another digit.
-- It only works with integers. Rounds fractions toward zero.

    if div < 2 then return end  -- Prevents the function from never returning.

    local Digit= {}
    local Negative= false
    if Number < 0 then
        Number= -Number
        Negative= true
    end

    Number= math.floor(Number)
    c= c or "white"
    bkgnd= bkgnd or "clear"

    local i= 0
    if Number < 1 then
        Digit[1]= 0
        i= 1
    end

    while (Number >= 1) do
        i= i+1
        Digit[i]= Number % div
        Number= math.floor(Number/div)
    end

    if Negative then  i= i+1  end
    local x= right - i*4
    gui.box(x+1, y-1, right+1, y+5,bkgnd,bkgnd)

    i= 1
    while Draw[Digit[i]] do
        Draw[Digit[i]](right-2,y,c)
        right= right-4
        i=i+1
    end

    if Negative then
        gui.line(right, y+2,right-2,y+2,c)
        right= right-4
    end
    return right
end
--*****************************************************************************
local function DrawNum(right, y, Number, c, bkgnd)
--*****************************************************************************
-- Paints the input number as right-aligned. Decimal version.
    return __DN_AnyBase(right, y, Number, c, bkgnd, 10)
end
--*****************************************************************************
local function DrawNumx(right, y, Number, c, bkgnd)
--*****************************************************************************
-- Paints the input number as right-aligned. Hexadecimal version.
    return __DN_AnyBase(right, y, Number, c, bkgnd, 16)
end

-------------------------------------------------------------------------------
local R1u= memory.readbyte

local keys, lastkeys= {},{}
local function UpdateKeys() lastkeys= keys; keys= input.get() end
local function Press(k) return keys[k] and not lastkeys[k] end

local IDchar= {  -- Dang it, I need to figure out the best ASCII characters.
[0]=" ","/","&","[","X","%",nil,"A","B","C","a","b","c","+","+",nil,
    "e","E","g","h","j","y","m","n","d","z","q","W","G","G","G","H",
    "H","H","J","J","J","Y","Y","Y","M","M","M","N","N","N","$","L",
    "5","f","F","p","P","K","i","r","R",nil,"S","I","w",nil,"*","?",
    nil,"U","U","U","U","U","\\","v","t","1",nil,"0"
}

local IDname= {  -- Here's all the objects I've identified.
[0x00]= "empty",
[0x01]= "panel_stun",
[0x02]= "wall?",
[0x03]= "block",
[0x04]= "wall_secret",
[0x05]= "wall_break",
--[0x06]
[0x07]= "wall_switch1" ,[0x08]= "wall_switch2" ,[0x09]= "wall_switch3",
[0x0A]= "panel_switch1",[0x0B]= "panel_switch2",[0x0C]= "panel_switch3",
[0x0D]= "door1"        ,[0x0E]= "door2",
--[0x0F]
[0x10]= "exit1"        ,[0x11]= "exit2",
[0x12]= "ghost",
[0x13]= "grunt",
[0x14]= "demon",
[0x15]= "lobber",
[0x16]= "sorc",
[0x17]= "gruntX",
[0x18]= "death",
[0x19]= "bubble",
[0x1A]= "sorcDX",
[0x1B]= "dragon_head",
[0x1C]= "gen_ghost1"   ,[0x1D]= "gen_ghost2"   ,[0x1E]= "gen_ghost3",
[0x1F]= "gen_grunt1"   ,[0x20]= "gen_grunt2"   ,[0x21]= "gen_grunt3",
[0x22]= "gen_demon1"   ,[0x23]= "gen_demon2"   ,[0x24]= "gen_demon3",
[0x25]= "gen_lobber1"  ,                        [0x27]= "gen_lobber3",
[0x28]= "gen_sorc1"    ,[0x29]= "gen_sorc2"    ,[0x2A]= "gen_sorc3",
                                                [0x2D]= "gen_gruntX3",
[0x2E]= "treasure",
[0x2F]= "chest",
[0x30]= "treasure5",
[0x31]= "foodjug",
[0x32]= "foodbowl",
[0x33]= "potion1",
[0x34]= "potion2",
[0x35]= "key",
[0x36]= "invisibility",
[0x37]= "repulsive",
[0x38]= "reflecting",
--[0x39]
[0x3A]= "super_shot",
[0x3B]= "invincibility",
[0x3C]= "dragon_body",
--[0x3D]
[0x3E]= "transporter",
[0x3F]= "clue?",
--[0x40]
[0x41]= "upgrade_shot",
[0x42]= "upgrade_speed",
[0x43]= "upgrade_magic",
[0x44]= "upgrade_fight",
[0x45]= "upgrade_armor",
[0x46]= "potion_bad",
[0x47]= "fake_exit",
[0x48]= "dragon_tail",
[0x49]= "player",
--[0x4A]
[0x4B]= "orb"
}
local container= {  -- To let me know which things can hold other things.
    [0x04]=true,  -- Secret wall
    [0x2F]=true   -- Chest
}


local bitchar= {[0]=" ","#"}  -- For text mapping.
--*****************************************************************************
local function DetailMapperPrint()
--*****************************************************************************
--Prints an ASCII map to the Output Console.
--This version will fill in all those neat little objects, too.
--The tilde ~ is used if it doesn't find a defined character in IDchar

    local map= {}
    for i= 0, 31 do map[i]= {} end

--Fill map walls
    for y= 0, 27 do
        local a= 0x0300+4*y
        local v= R1u(a)+R1u(a+1)*0x100+R1u(a+2)*0x10000+R1u(a+3)*0x1000000
        for x= 0, 31 do
            local b= bit.band(1,bit.rshift(v,x))
            map[x][y]= bitchar[b]
        end
    end

--Insert objects
    for i= 0, 0x7E do
        local ID= R1u(0x0400+i)
        if ID ~= 0 then
            local x,y= math.floor(R1u(0x0480+i)/8), math.floor(R1u(0x0500+i)/8)
            map[x][y]= IDchar[ID] or "~"
        end
    end

--Print map
    print("+--------------------------------+")
    for y= 0, 27 do
        local str= "|"
        for x= 0, 31 do
            str= str .. map[x][y]
        end
        print(str.."|")
    end
    print("+--------------------------------+")
end

--*****************************************************************************
local function MapperPrint()
--*****************************************************************************
--Prints an ASCII map to the Output Console.
--This does just the solid walls and potential floor. Nothing else.
--Currently unused, though.

    print("+--------------------------------+")
    for y= 0, 31 do
        local s= "|"
        local a= 0x0300+4*y
        local v= R1u(a)+R1u(a+1)*0x100+R1u(a+2)*0x10000+R1u(a+3)*0x1000000
        for x= 0, 31 do
            local b= bit.band(1,bit.rshift(v,x))
            s= s .. bitchar[b]
        end
        print(s.."|")
    end
    print("+--------------------------------+")
end

--*****************************************************************************
local function ListStuff()
--*****************************************************************************
--Lists stuff to Output Console. Well, every single object on the map.
--ID number, X pos, Y pos, spare number, spare timer, my name for object.
--Alas, it's not smart enough to detect contained "exit2". Peek at spare number

    for i= 0, 0x7F do
        local ID= R1u(0x0400+i)
        if ID ~= 0 then
            local str= "%02X %02d %02d %02X %02X:%s"
            if container[ID] then str= str .. " > " .. (IDname[R1u(0x0580+i)] or "???") end
            print(string.format(str,
                ID,
                R1u(0x0480+i)/8, -- X loc
                R1u(0x0500+i)/8, -- Y loc
                R1u(0x0580+i),   -- Spare num
                R1u(0x0600+i),   -- Timer?
                IDname[ID] or "???"
            ))
        end
    end
    print("===")
end

--*****************************************************************************
local function ExitScan()
--*****************************************************************************
--Scans the level for exits, contained or otherwise.
--After finding any, they are printed to Output Console.

    print(R1u(0x00A3)+1)
    for i= 0, 0x7F do
        local ID= R1u(0x0400 + i)
        local str= "open:   "

        if ID == 0x04 then
            str= "secret: "
            ID= R1u(0x0580 + i)
        elseif ID == 0x2F then
            str= "chest:  "
            ID= R1u(0x0580 + i)
        end
--Notably, if it doesn't contain an exit, I won't display it anyway.

        if     ID == 0x10 then  -- Normal exit
            print(string.format("%s%sx%02d y%02d",str,"exit @ ",
                R1u(0x0480+i)/8,R1u(0x0500+i)/8
            ))
        elseif (ID == 0x11) or (ID >= 0x80) then  -- Special exit
--By smart design, the container doesn't hold special exit.
--Instead, it holds the exit's destination. Then morphs into the special exit,
--I only need to read that same byte anyway, ha!
            print(string.format("%s%s%03d)x%02d y%02d",str,"EXIT(",
                R1u(0x0580+i)-127,R1u(0x0480+i)/8,R1u(0x0500+i)/8
            ))
        end
    end
    print("-=-=-")
end

--*****************************************************************************
local function ScanKeys()
--*****************************************************************************
--Handles the input so I can use the print functions on demand.

    UpdateKeys()
    if Press("space") then DetailMapperPrint(); ListStuff() end
    if Press("B")     then ExitScan() end
end

local bitcolor= {[0]="grey","white"}
--*****************************************************************************
local function Mapper()
--*****************************************************************************
--Map display. Just the basic walls, however.

    gui.box(0,8,33,41,"black","black")
    for y= 0, 31 do
        local Y= y+9
        local a= 0x0300+4*y
        local v= R1u(a)+R1u(a+1)*0x100+R1u(a+2)*0x10000+R1u(a+3)*0x1000000
        for x= 0, 31 do
            local b= bit.band(1,bit.rshift(v,x))
            gui.pixel(x+1,Y,bitcolor[b])
        end
    end
end

--*****************************************************************************
local function BasicHUD()
--*****************************************************************************
--This will reveal ALL secrets! Mwahaha!
    ScanKeys()
    Mapper()

--... Uh, all it does is paint numbers where all objects are...
    local mapx= (R1u(0x0000)%2)*256 + R1u(0x0002)
    local mapy= R1u(0x0003)
    for i= 0, 0x7F do
        local ID= R1u(0x0400+i)
        if ID ~= 0 then
            local x,y= R1u(0x0480+i)*2,R1u(0x0500+i)*2
            x= (x-mapx)%512
            y= (y-mapy)%512
            if x < 246 and y < 216 then  -- At least try to paint it on screen
                DrawNumx(x+8,y  ,ID           ,"white","black")
                DrawNumx(x+8,y+6,R1u(0x0580+i),"white","black")
            end
        end
    end
end
gui.register(BasicHUD)