User File #33868816476826407

Upload All User Files

#33868816476826407 - (NES) Zelda II, my general script. Version 1 (prototype)

Zelda2_v1.lua
961 downloads
Uploaded 9/25/2016 6:34 AM by FatRatKnight (see all 245)
Still a prototype. I'm using integer versions.
Added a snippet of code that attempts to identify world map locations where an exit will drop you off at. One would hope this lets you know where you're teleporting to. Numbers in the bottom right may be of assistance. Additionally, for room exits, I invert the colors if it's 0x3F, and instead show a number from 0 to 3. That's the selector for our world map thing.
Then again, I don't have much experience with these glitches about somehow making use of alternative exits, so I haven't checked how viable the numbers I'm showing are. Also, there is a *known* failure with my script when we're talking about the Maze, as for some reason, the standard 0x3F I expect to see isn't showing up, yet it exits to the overworld. I have things to look at.
--[[  Leeland Kirwan (FatRatKnight), 2014
Allows user input to "mix" with TAS Editor using XOR logic.
If Recording is checked, this script will do its intended job.

Tap a P1 key to toggle input. If "All" is selected, will use other player keys
for related player. If an individual player is selected, uses P1 controls. You
can hold down a key to affect multiple frames, toggling the key on each frame.

For reference, Superimpose uses OR logic, and does not allow user input to
clear stored input. A great inconvience for those wishing to use the keyboard
to toggle individual input rather than mouse.
]]--

local btnstr= {A = "A", B   = "B", select= "S", start= "T",
               up= "U", down= "D", left  = "L", right= "R"}
local btnval= {A = 0x01, B   = 0x02, select= 0x04, start= 0x08,
               up= 0x10, down= 0x20, left  = 0x40, right= 0x80}
local plval= {["1P"]= 1, ["2P"]= 2, ["3P"]= 3, ["4P"]= 4}
local OldJoy= {{},{},{},{}}
--*****************************************************************************
local function InputToggle()
--*****************************************************************************
--Needs readonly mode to work, but won't interfere too much otherwise.
--Toggles inputs recorded in TAS Editor.

    if not taseditor.engaged() then return end  -- Escape

    taseditor.clearinputchanges() -- Guessing something horrible w/o this.

    local frame= emu.framecount()

    local plSel= plval[taseditor.getrecordermode()]
    local plLow = plSel or 1   -- Get the for loop range!
    local plHigh= plSel or 4   -- Hardcoded 4 doesn't appear harmful...

    local P1joy= nil                                -- If one player, get P1.
    if plSel then P1joy= joypad.getimmediate(1) end -- Otherwise, get all.

    local changed= false  -- Flag, so I don't interfere too much
    local History= ""     -- Information for TASer in History list

    for pl= plLow, plHigh do
        local pad= taseditor.getinput(frame,pl)
        local joy= P1joy or joypad.getimmediate(pl) -- Pick out the joypad

        local mask= 0                     -- Convert messy table to numbers
        for btn,pressed in pairs(joy) do
            if pressed and not OldJoy[pl][btn] then
                mask= bit.bor(mask,btnval[btn])
                History= History .. pl .. btnstr[btn]
            end
        end

        taseditor.submitinputchange(frame,pl,bit.bxor(pad,mask))
        OldJoy[pl]= joy  -- To avoid repeated triggers
        changed= changed or (mask ~= 0) -- If the bitmask did something, apply!
    end

    if changed then taseditor.applyinputchanges(History) end
end
taseditor.registerauto(InputToggle)

--#############################################################################
--#############################################################################
--My own functions for number display. FCEUX default text isn't liked by me.

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

--#############################################################################
--#############################################################################
--Renaming things to my desires

local R1u= memory.readbyte
local R1s= memory.readbytesigned
local R2u= memory.readword
local R2s= memory.readwordsigned
local R1U= rom.readbyte


--#############################################################################
--#############################################################################
--Debug registry

--*****************************************************************************
local function DetectChange()
--*****************************************************************************
--For when the game shifts to another "world".
--Like going into a town or a palace, or a different part of the continent.

    print("Changed at frame " .. emu.framecount())
end
memory.registerwrite(0x0748,DetectChange)

--*****************************************************************************
local function FnHit()
--*****************************************************************************
    print(string.format("%02X %02X %02X:%02X - Zone",
        R1u(0x0706),
        R1u(0x0707),
        R1u(0x070A),
        R1u(0x056C)
    ))
end
--memory.registerexec(0xCD40,FnHit)
--C2E6 is called every frame. Arr...
--CD40 is exclusive to "world" shifting. So, what calls it?

--#############################################################################
--#############################################################################
--Keyhook functions

--*****************************************************************************
local function PrintExits()
--*****************************************************************************
    for i= 0, 0x3E do
        print(string.format("%02X: %3d %3d . %3d %3d : %02X.%d %02X.%d %02X.%d %02X.%d",
            i,
            R1u(0x6A00+i),
            R1u(0x6A3F+i),
            R1u(0x6A7E+i),
            R1u(0x6ABD+i),
            math.floor(R1u(0x6AFC+i*4)/4),R1u(0x6AFC+i*4)%4,
            math.floor(R1u(0x6AFD+i*4)/4),R1u(0x6AFD+i*4)%4,
            math.floor(R1u(0x6AFE+i*4)/4),R1u(0x6AFE+i*4)%4,
            math.floor(R1u(0x6AFF+i*4)/4),R1u(0x6AFF+i*4)%4
        ))
    end
end

--#############################################################################
--#############################################################################
--Main

local RoomsPosX= {[0]=190,200,200,210}
local RoomsPosY= {[0]= 12, 16,  9, 12}

local WorldExit= {[0]=0x00461F+0x10,0x0060FC+0x10,0x00861F+0x10}

--*****************************************************************************
local function StatusHUD()
--*****************************************************************************
--Player info. Magic, health, position, velocity, you know.

    local v, clr

--Coordinates
    DrawNum( 16,  9,R2s(0x004D,0x003B),"white","black")
    DrawNumx(24,  9,R1u(0x03D6),"grey","black")
    DrawNumx(36,  9,R1s(0x0070),"cyan","black")
    DrawNum( 16, 15,R2s(0x0029,0x0019),"white","black")
    DrawNumx(24, 15,R1u(0x03E6),"grey","black")
    DrawNumx(36, 15,R1s(0x057D),"cyan","black")

--Rooms
    v= R1u(0x0561)
    DrawNumx(180,  9,v,"cyan","black")
    v= 0x6AFC + v*4
    for i= 0, 3 do
        local X= RoomsPosX[i]
        local Y= RoomsPosY[i]
        local room= R1u(v+i)
        if math.floor(room/4) == 0x3F then
            DrawNum(X,Y,room%4,"black","white")
        else
            DrawNum(X,Y,room/4,"white","black")
        end
    end

--List of exits
    v= R1u(0x0748)
    DrawNumx(254,194,v,"orange","black")
    v= WorldExit[R1u(0x0706)] + v
    for i= 0, 3 do
        local Y= 204+7*i
        local CoordY= R1U(v+i)
        local fClr, bClr= "white","black"
        if CoordY == 0 then fClr,bClr= bClr,fClr end
        DrawNum(254,Y,R1U(v+0x00+i)%128,fClr,bClr)
        DrawNum(240,Y,R1U(v+0x3F+i)% 64,fClr,bClr)
    end

--Attack
    v= R1u(0x0777)
    v= R1U(0x01E66C + 0x10 + v)
    DrawNum(  8, 23,v,"orange","black")

--Magic
    DrawNum( 44,28,math.floor((R1u(0x0773)+1)/2),"white","black")
    v= R1u(0x070C)
    if v ~= 0 then DrawNum( 44,34,v,"cyan","black") end

    v= R1u(0x0749)*8 + R1u(0x0778) --Spell Selection and Magic Level
    v= R1U(0x000D7A + 0x10 + v)/2 --Spell cost table
    clr= "orange"
    if bit.band(R1u(0x074A),0x04) ~= 0 then clr= "grey" end
    DrawNum( 46,18,v,clr,"black")

    DrawNum( 54,9,R1u(0x0749),"white","black")
    DrawNum( 44,9,R1u(0x074A),"white","black")

--Life
    DrawNum(116,28,math.floor((R1u(0x0774)+1)/2),"white","black")
    v= R1u(0x070D)
    if v ~= 0 then DrawNum(116,34,v,"cyan","black") end

--EXP

end

--*****************************************************************************
local function BoxHUD()
--*****************************************************************************
    local CamX= R2u(0x072C,0x072A)

--Link
    local AtkPow= R1U(0x01E66C + 0x10 + R1u(0x0777))
    local LifeLv= R1u(0x0779)
    local Life  = R1u(0x0774)

    local X= R2u(0x004D,0x003B) - CamX
    local Y= R2u(0x0029,0x0019) - 0x100
    gui.box(X,Y,X+3,Y+3)

--Baddies
    for i= 0, 5 do
        if R1u(0x00B6+i) == 1 then
            X= R2u(0x004E+i,0x003C+i) - CamX
            Y= R2u(0x002A+i,0x001A+i) - 0x100
--            gui.box(X,Y,X+3,Y+3,nil,"cyan")
            local hits= math.ceil(R1u(0x00C2+i)/AtkPow)
            DrawNum(X,Y,hits,"cyan","black")
            local dmg= R1u(0x6DF9 + R1u(0x00A1+i))%8
            dmg= R1U(0x01E2AE + 0x10 + dmg*8 + LifeLv)
            DrawNum(X,Y+6,dmg/2,"orange","black")
        end
    end

--Projectiles
    for i= 0, 5 do
        if R1u(0x0087+i) ~= 0 then
            X= R2u(0x0054+i,0x0042+i) - CamX
            Y= R2u(0x0030+i,0x0020+i) - 0x100
            local dmg= R1u(0x6D17 + R1u(0x0087+i))%8
            dmg= R1U(0x01E2AE + 0x10 + dmg*8 + LifeLv)
            DrawNum(X,Y,dmg/2,"orange","black")
        end
    end

--Sword beam
end

--*****************************************************************************
local function WorldHUD()
--*****************************************************************************

--Coordinates
    DrawNum( 13,  9,R1u(0x0073),"white","black")
    DrawNum( 13, 15,R1u(0x0074),"white","black")
end

--*****************************************************************************
local function BasicHUD()
--*****************************************************************************
    UpdateKeys()
    if Press("space") then PrintExits() end

    local mode= R1u(0x0768)
    DrawNumx(9,202,mode,"white","black")

    if mode == 6 then
        StatusHUD()
        BoxHUD()
        gui.text(2,210,"Side")
    elseif mode == 0 then
        WorldHUD()
        gui.text(2,210,"World")
    else
        gui.text(2,210,"What?")
    end

    DrawNumx(254,  9,R1u(0x0706),"white","black")
    DrawNumx(254, 15,R1u(0x0707),"white","black")
    DrawNumx(254, 21,R1u(0x070A),"white","black")
    DrawNumx(254, 27,R1u(0x056C),"white","black")
    DrawNumx(254, 33,R1u(0x0748),"orange","black")
    DrawNumx(254, 39,R1u(0x0561),"cyan","black")

--[[
    local LifeLv= R1u(0x0779)

--Link
    DrawNumx( 24,43,R2u(0x0029,0x0019),"cyan","black") --Y pos
    DrawNumx( 40,43,R2u(0x004D,0x003B),"cyan","black") --X pos
    DrawNumx( 50,43,R1u(0x03D6),"cyan","black") --X sub
    DrawNumx( 60,43,R1u(0x03E6),"cyan","black") --Y ??
    DrawNumx( 70,43,R1u(0x005F),"cyan","black") --Facing
    DrawNumx( 80,43,R1u(0x0070),"cyan","black") --?
    DrawNumx( 90,43,R1u(0x0080),"cyan","black") --?


--Baddie
    for y= 0, 5 do
        local Y= y*7+50
        local clr= "grey"
        local state= R1u(0x00B6+y)
        if state ~= 0 then clr= "white" end
        DrawNumx(  8,Y,R1u(0x00A1+y),clr,"black")
        DrawNumx( 24,Y,R2u(0x002A+y,0x001A+y),clr,"black") --Y pos
        DrawNumx( 40,Y,R2u(0x004E+y,0x003C+y),clr,"black") --X pos
        DrawNumx( 50,Y,R1u(0x03D7+y),clr,"black") --X sub
        DrawNumx( 60,Y,R1u(0x03E7+y),clr,"black") --Y ??
        DrawNumx( 70,Y,R1u(0x0060+y),clr,"black") --Facing
        DrawNumx( 80,Y,R1u(0x0071+y),clr,"black") --?
        DrawNumx( 90,Y,R1u(0x0081+y),clr,"black") --?
        DrawNumx(100,Y,R1u(0x00C2+y),clr,"black") --
        DrawNumx(110,Y,R1u(0x00BC+y),clr,"black") --
        DrawNumx(120,Y,state,clr,"black") --

-- DmgCalc
        local v= R1u(0x00A1+y)
        v= R1u(0x6DF9 + v)%8
        v= R1U(0x01E2AE + 0x10 + v*8 + LifeLv)
        DrawNum(134,Y,v/2,clr,"black")
    end

--Projectile
    for y= 0, 5 do
        local Y= y*7+92
        local clr= "orange"
--        local state= R1u(0x00B6+y)
--        if state ~= 0 then clr= "white" end
        DrawNumx(  8,Y,R1u(0x0087+y),clr,"black")
        DrawNumx( 24,Y,R2u(0x0030+y,0x0020+y),clr,"black") --Y pos
        DrawNumx( 40,Y,R2u(0x0054+y,0x0042+y),clr,"black") --X pos
        DrawNumx( 50,Y,R1u(0x03DD+y),clr,"black") --X sub
        DrawNumx( 60,Y,R1u(0x03ED+y),clr,"black") --Y ??
        DrawNumx( 70,Y,R1u(0x0066+y),clr,"black") --Facing
        DrawNumx( 80,Y,R1u(0x0077+y),clr,"black") --?

-- DmgCalc
        local v= R1u(0x0087+y)
        v= R1u(0x6D17 + v)%8
        v= R1U(0x01E2AE + 0x10 + v*8 + LifeLv)
        DrawNum(134,Y,v/2,clr,"black")
    end
]]--

--[[
    for y= 0, 0x0F do
        local Y= y*7+50
        local clr= "white"
--        if R1u(0x0019 + y) == 0 then clr= "grey" end
        DrawNumx(16,Y,R2u(0x0029+y,0x0019+y),clr,"black") --Y pos
        DrawNumx(32,Y,R2u(0x004D+y,0x003B+y),clr,"black") --X pos
        DrawNumx(42,Y,R1u(0x005F+y),clr,"black") --Facing
        DrawNumx(52,Y,R1u(0x0070+y),clr,"black") --?
        DrawNumx(62,Y,R1u(0x0080+y),clr,"black") --?
        DrawNumx(72,Y,R1u(0x03D6+y),clr,"black") --X sub
        DrawNumx(82,Y,R1u(0x03E6+y),clr,"black") --Y ??
        DrawNumx(92,Y,R1u(0x00C1+y),clr,"black") --
        DrawNumx(102,Y,R1u(0x00A0+y),clr,"black") --
    end
]]--
end
gui.register(BasicHUD)

--for spell= 0, 7 do
--    local s= ""
--    for lv= 1, 8 do
--        if s ~= "" then s= s .. " " end
--        s= string.format("%s%3d",
--            s,
--            R1U(0x000D7A + 0x10 + spell*8 + lv)/2
--        )
--    end
--    print(s)
--end

--#############################################################################
--#############################################################################
--Immediate (That is, run-once code)

--*****************************************************************************
local function ExtractTable(addr)
--*****************************************************************************
    for i= 0, 0x3E do
        print(string.format("%02X: %d.%3d %d.%2d . %3d %3d : %02X.%d %02X.%d %02X.%d %02X.%d",
            i,
            math.floor(R1U(addr+0x0000+i)/128),R1U(addr+0x0000+i)%128,
            math.floor(R1U(addr+0x003F+i)/ 64),R1U(addr+0x003F+i)%64,
            math.floor(R1U(addr+0x007E+i)/  1),
            math.floor(R1U(addr+0x00BD+i)/  1),
            math.floor(R1U(addr+0x00FC+i*4)/4),R1U(addr+0x00FC+i*4)%4,
            math.floor(R1U(addr+0x00FD+i*4)/4),R1U(addr+0x00FD+i*4)%4,
            math.floor(R1U(addr+0x00FE+i*4)/4),R1U(addr+0x00FE+i*4)%4,
            math.floor(R1U(addr+0x00FF+i*4)/4),R1U(addr+0x00FF+i*4)%4
        ))
    end
end
--ExtractTable(0x0060FC + 0x10)

--#############################################################################
--#############################################################################
-- Ugh, need this loop on the very bottom...

--*****************************************************************************
while true do
--*****************************************************************************
-- Exists to detect frame advances, and thus clear the OldJoy array.
    for i= 1, 4 do OldJoy[i]= {} end -- Retrigger keys

-- Any code that needs this loop can go here.

    emu.frameadvance()
end

--[[
00DE - ((important for spells to have this zero))
0487 - Timer for selection repetition
0501 - An array of something?
0524 - ((important for spells to have this zero))
0564 - Magic (mirror)
0565 - Life (mirror)
057D - Vertical velocity
070D - Life restore
0743 - vertical related?
0744 - vertical related?
0749 - Spell selection
074A - Spell related
074C - (())
0768 - Mode?
0773 - Magic
0774 - Life
0775 - EXP (hi) ?
0776 - EXP (lo)
0777 - Attack level
0778 - Magic level
0779 - Life level
]]--