User File #51619492272730334

Upload All User Files

#51619492272730334 - NES Donald Land - General purpose script

DL_General_v9.lua
Game: Donald Land ( NES, see all files )
577 downloads
Uploaded 12/3/2018 4:20 PM by FatRatKnight (see all 245)
Shows hitboxes and a bunch of information. It's also our RAM Watch, but that was underutilized anyway. It has my guess on Ronald's hitbox rather than anything firmly known.
It also has my early guess on the wall hit detection, although now I realize the magic pixel is just at the feet level and not the vertical orange bars I put down. The green vertical bar was to assist in figuring out the item collection, and seems mostly accurate.
The "average speedometer" and "distance check" on the left needs to be calibrated to the start of each stage.
local RAMwatch= {
    {a1=0x0042,a2=0x0043,c=0xFFFFFFFF},  --Screen loader, right side
    {a1=0x0046,a2=0x0047,c=0xFFFFFF00},  --Screen loader, left side
    {a1=0x0051,a2=nil   ,c=0xFFFFFFFF},  --Stage
    {a1=0x04ED,a2=nil   ,c=0xFFFF00FF},  --Scr
    {a1=0x04EE,a2=nil   ,c=0xFF00FFFF},  --Scr
    {a1=0x00DA,a2=nil   ,c=0xFF00FF00},  --Game State
    {a1=0x008B,a2=nil   ,c=0xFFFFFFFF},  --RNG
}

local SpeedCheck= {
    {frame=  413, pos= 88, TgtSp= 4.5}, -- 1 Home Town
    {frame= 1948, pos= 36, TgtSp= 4.5}, -- 2 Lake Side
    {frame= 4264, pos= 36, TgtSp= 4.5}, -- 3 Forest
    {frame= 6957, pos= 96, TgtSp= 1.0}, -- 4 Sky
    {frame=10768, pos= 52, TgtSp= 16/7}, -- 5 Oasis
    {frame=13759, pos= 52, TgtSp= 16/7}, -- 6 Cave
    {frame=16332, pos= 68, TgtSp= 4.5}, -- 7 Pond
    {frame=19859, pos= 36, TgtSp= 4.5}, -- 8 Harbor
    {frame=21467, pos= 80, TgtSp= 2.0}, -- 9 Ocean
    {frame=26125, pos= 52, TgtSp= 4.5}, --10 Ghost Town
    {frame=28359, pos= 36, TgtSp= 16/7}, --11 Dark Forest
    {frame=31111, pos= 52, TgtSp= 4.5}, --12 Castle
}

local r1u_ , r1s_= memory.read_u8 , memory.read_s8
local r1u= function(a) return r1u_(a,"System Bus") end
local R1U= function(a) return r1u_(a,"PRG ROM") end
local r1s= function(a) return r1s_(a,"System Bus") end
local r2u= function(a,b) return r1u(a) + r1u(b or a+1)*256 end
local r2s= function(a,b) return r1u(a) + r1s(b or a+1)*256 end

local Tx= gui.pixelText
local function Hex(v,l)
    if l then return string.format("%0" .. tostring(l) .. "X", v) end
    return (string.format("%X",v))
end

--*****************************************************************************
local function DecimalStr2(v)
--*****************************************************************************
--Converts to nice-looking decimal numbers.
--The appearance is inherently imprecise, as /100 doesn't align like /256.
--Still, you are a mere human, so you get this treatment.

    local Negation= ""
    if v < 0 then Negation= "-"; v= -v end
    v= math.floor(v*100/256)
    local s= string.format("%02d",v%100)
    v= math.floor(v/100)
    return string.format("%s%d.%s",Negation,v,s)
end

--*****************************************************************************
local function DecimalStr(v)
--*****************************************************************************
--Converts to nice-looking decimal numbers. Yay, copy/paste!
--The appearance is inherently imprecise, as /100 doesn't align like /256.
--Still, you are a mere human, so you get this treatment.

    local Negation= ""
    if v < 0 then Negation= "-"; v= -v end
    v= math.floor(v*1000/256)
    local s= string.format("%03d",v%1000)
    v= math.floor(v/1000)
    return string.format("%s%d.%s",Negation,v,s)
end

--*****************************************************************************
local function DisplayRAM()
--*****************************************************************************
    local Y= 32
    for i= 1, #RAMwatch do
        local RAM= RAMwatch[i]
        if RAM.a2 then
            Tx(235,Y,string.format("%5d",r2u(RAM.a1,RAM.a2)),RAM.c)
        else
            Tx(243,Y,string.format("%3d",r1u(RAM.a1))       ,RAM.c)
        end
        Y= Y+7
    end
end

local OldChanges= nil
--*****************************************************************************
local function ChangesHappened(...)
--*****************************************************************************
--Single instance function. Do not call in multiple places.
--Checks if things are different since the last frame.

    if not OldChanges then
        OldChanges= {...}
        return true  --Pretend things are fine
    end

    local Changes  --Get recent into old, and hold the old just a little longer
    Changes, OldChanges= OldChanges, {...}
    for i= 1, #OldChanges do
        if Changes[i] ~= OldChanges[i] then return true end
    end
    return false --No changes? Well, snap
end

--*****************************************************************************
local function CR_ChangesHappened(...)
--*****************************************************************************
--Doing something ridiculously complicated with coroutines.
--This is just so we can have arbitrary copies of this function.
--I'm not ready with this particular function. Need to study coroutines a bit.

    local OldChanges= {...}
    local c= true
    while true do
        local Changes
        Changes, OldChanges= OldChanges, {coroutine.yield(c)}
        c= false
        for i= 1, #Changes do
            c= (Changes[i] ~= OldChanges[i]) or c
        end
    end
end

--*****************************************************************************
local function AverageSpeed()
--*****************************************************************************
    local Stage= r1u(0x0051)
    local Check= SpeedCheck[Stage]
    if not Check then return end

    local Frames= (emu.framecount() - Check.frame)
    if Frames <= 0 then return end

    local TgtSp= Check.TgtSp
    local PlX= r2u(0x0092,0x0091)
    local CamX= r2s(0x00CC)
    local Distance= PlX + CamX*256 - Check.pos*256

    PlX= Distance - Frames*(TgtSp*0x100)
    local clr= 0xFF00FF80
    if PlX < 0 then PlX= -PlX; clr= 0xFFFF8000 end
    Tx(  1, 40,string.format("%8s",DecimalStr(PlX)),clr)
    PlX= math.floor(Distance/Frames)
    Tx(  1, 33,string.format("%8s",DecimalStr(PlX)),0xFF00FF00)
end

-------------------------------------------------------------------------------

--*****************************************************************************
local function PaintHitbox(ID,CamX)
--*****************************************************************************
    if r1u(0x04F3+ID) == 2 then
        local ScrX= r2u(0x054B+ID,0x0543+ID)-CamX
        local ScrY= r2u(0x0563+ID,0x055B+ID)
        local W= r1u(0x0573+ID)
        local H= r1u(0x057B+ID)
        gui.drawBox(ScrX-W,ScrY-H,ScrX+W,ScrY+H,0xC0FFFF40,0x40000000)
    end
end

--*****************************************************************************
local function ExistCheck(ID,CamX)
--*****************************************************************************
    local clr= 0
    if r1u(0x04F3+ID) ~= 0 then clr= 0xFFFFFF00 end
    gui.drawRectangle(180+7*ID,32,5,5,0xFFFFFFFF,clr)
end

--*****************************************************************************
local function ObjScan()
--*****************************************************************************
--Should get every object out there.
--I think I got the hitboxes. At least, this looks sensible.

    local CamX= r2u(0x00CC)
    for i= 0, 7 do
        PaintHitbox(i,CamX)
        ExistCheck(i,CamX)
    end
end

-------------------------------------------------------------------------------

--local PosChanged= coroutine.create(CR_ChangesHappened,0,0,0)
--*****************************************************************************
local function PlayerData()
--*****************************************************************************

    --Position
    local PlX, PlY= r2u(0x0092,0x0091), r2u(0x0094,0x0093)
    local CamX= r2s(0x00CC)
    local c= ChangesHappened(PlX,PlY,CamX)

    local clr= 0xFFFFFFFF
    if not c then clr= 0xFFFF4000 end
    Tx(100,  8,string.format("CAMERA: %4s", CamX),clr)
    Tx(100, 16,string.format("X%8s",DecimalStr(PlX + CamX*256)),clr)
    Tx(100, 24,string.format("Y%8s",DecimalStr(PlY)),clr)
    Tx( 80, 24,string.format("%3d",r1u(0x0095)),clr)

    PlX,PlY= math.floor(PlX/256),math.floor(PlY/256)
    gui.drawLine(PlX   ,PlY   ,PlX   ,PlY+16,0xFF00FF00)
    gui.drawLine(PlX+12,PlY   ,PlX+12,PlY+16,0xFFFF8000)
    gui.drawLine(PlX-12,PlY   ,PlX-12,PlY+16,0xFFFF8000)
    gui.drawBox( PlX- 6,PlY-14,PlX+ 6,PlY+14,0xC0FFFFFF,0x2000FF00)

    --Speed
    local BaseSpeed= r2s(0x0099,0x0098)
    local Boost=     r2s(0x00AA,0x00A9)
    Tx(206, 8,string.format("Base: %6s",DecimalStr(BaseSpeed)),0xFFFFFF00)
    Tx(206,14,string.format("Boost:%6s",DecimalStr(Boost))    ,0xFF00FFFF)
    Tx(206,20,string.format("Total:%6s",DecimalStr(BaseSpeed+Boost)))

    local SpeedY=   r2s(0x009D,0x009C)
    local JumpMode= r1u(0x00AB)
    local JumpTime= r1u(0x00AC)
    local clr= 0xFFC0C0C0 -- Light grey, nothing spectacular
    if (JumpMode == 1) then
        clr= 0xFF00FFFF
        Tx(172,16,string.format("%2d",math.max(14-JumpTime,0)),0xFF00FF00)
    end
    if (JumpMode == 2) or (JumpTime >= 14) then clr= 0xFFFF8000 end
    Tx(160, 8,string.format("%6s",DecimalStr(SpeedY)),clr)

    --Apple check
    if r1u(0x04F9) ~= 0 then
       Tx(193,16,string.format("%3d",r1u(0x04D7)),0xFFFFFF00)
    end
    if r1u(0x04FA) ~= 0 then
       Tx(193,24,string.format("%3d",r1u(0x04D8)),0xFFFFFF00)
    end
    if bit.band(r1u(0x00D4), 64) ~= 0 then
       gui.drawEllipse(183,18,8,9,0xFFFF0000,0xFFFF0000)
       gui.pixelText(185,19,"B",0xFFFFFFFF,0x00000000)
    end

    --Spawn check
    PlX= r1u(0x0092)
    local Spawnny= (CamX%128)*256 + PlX%256
    local clr= 0xFFFFFFFF
    if Spawnny < (4*256) then clr= 0xFFFF8000 end
    Tx( 84,32,string.format("Spawn:%7s",DecimalStr(Spawnny)),clr)

    --Other crud
    if r1u(0x00D7) == 0 then gui.pixelText(193,8,"GO!") end
end

-------------------------------------------------------------------------------

--local OldRNG= r1u(0x008B)
--*****************************************************************************
local function BasicHUD()
--*****************************************************************************

    --local RNG= r1u(0x008B)
    --Tx(50,40,string.format("%3d",(RNG-OldRNG)%256))
    --OldRNG= RNG

    ObjScan()
    PlayerData()
    AverageSpeed()
    DisplayRAM()
end

while true do
    BasicHUD()
    emu.frameadvance()
end

--[[
0091,2u - X position relative to screen, big endian
0098,2s - X Speed (Main), big endian
009A,2s - X Speed (?), big endian
00A9,2s - X Boost, big endian
009C,2s - Y speed, big endian
00AB,1x - Jump mode. 0=didn't jump, 1=Upward rise, 2=Falling part
00AC,1u - Jump timer. If it's 14 or more, A will not work.
00D7,1x - Control lock

0240-030F - Terrain (region 1)
0330-03FF - Terrain (region 2)

04D5,1u[2] - Apple timer 0-80
04D7,1x[2] - Apple
04D9,1x[2] - Apple

04F9,1x    - Apple

Objects (8 addresses per)
04F3 - 0x02:Exist  0x01:Off-screen
0543 - (*256) Super X pos
054B - (   1) X pos
0553 - (/256) Sub-X pos
055B - (*256) Super Y pos
0563 - (   1) Y pos
056B - (/256) Sub-Y pos
0573 - Hitbox X
057B - Hitbox Y
0583 - X speed
058B - X sub-speed
0593 - Y speed
059B - Y sub-speed

        local Y= 12 + 7*i
        Tx( 90,Y,Hex(r1u(0x04F3+i),2),0xFFFFFFFF)

        Tx(100,Y,Hex(r1u(0x0543+i),2),0xFFFFFFFF) --X
        Tx(109,Y,Hex(r1u(0x054B+i),2),0xFFFFFFFF)
        Tx(118,Y,Hex(r1u(0x0553+i),2),0xFFC0C0C0)

        Tx(130,Y,Hex(r1u(0x055B+i),2),0xFFFFFFFF) --Y
        Tx(139,Y,Hex(r1u(0x0563+i),2),0xFFFFFFFF)
        Tx(148,Y,Hex(r1u(0x056B+i),2),0xFFC0C0C0)

        Tx(160,Y,Hex(r1u(0x0573+i),2),0xFFFFFFFF)
        Tx(169,Y,Hex(r1u(0x057B+i),2),0xFFFFFFFF)
        Tx(178,Y,Hex(r1u(0x0583+i),2),0xFFFFFFFF)
        Tx(187,Y,Hex(r1u(0x058B+i),2),0xFFFFFFFF)
        Tx(196,Y,Hex(r1u(0x0593+i),2),0xFFFFFFFF)
        Tx(205,Y,Hex(r1u(0x059B+i),2),0xFFFFFFFF)
        Tx(214,Y,Hex(r1u(0x05A3+i),2),0xFFFFFFFF)
        Tx(223,Y,Hex(r1u(0x05AB+i),2),0xFFFFFFFF)
]]