User File #22202816332864552

Upload All User Files

#22202816332864552 - NES Rad Racer - More advanced lua HUD (v3)

RadRacer_v3.lua
1090 downloads
Uploaded 4/18/2015 9:24 PM by FatRatKnight (see all 245)
This may be the final version, depending on whether I try to improve my TAS later.
Not a lot was added, though. I did figure out the numbers behind bouncing off cars, and now I color things fabulously. Also, the script has its separate calculations to work out the piecewise function behind steering speed. And lastly, I think I found some more precise info on the road curve, but that still needs further work.
Anyway, to recap this HUD:
Top: CPU car info
  • Top row: ID, timer left to respawn, max timer to respawn
  • Left column: Speed, base spawn speed, RNG spawn speed, predicted speeds
  • Upper-right region: Distance, sub-distance, lane offset, sub-offset
  • Lower-right region: Lane change frame timer, unknown, visbility
Right: RNG list; If it's not inside the cycle, a new list is shown
Game HUD, left side:
  • Game frame counter (decimal) (Purpose: record keeping)
  • Game frame counter (hexadecimal) (Purpose: Timing TAS-related stuff)
  • Time remaining
Game HUD, left of tachometer: Info about curves. Needs analysis.
Game HUD, below tachometer: Left/right position and sub-position; Steering power
Game HUD, right of tachometer: Car bounce speed and subpixel
Game HUD, below speedometer: Speed and sub-speed
Game HUD, below progress meter: Distance traveled, distance lost, distance left
The last number on the right side is the course ID. And the extending line between HUD and road display is a visual representation of how much of the current course is done.
--*****************************************************************************
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,R1s= memory.readbyte, memory.readbytesigned
local R2u,R2s= memory.readword, memory.readwordsigned


--*****************************************************************************
local function Roll(r) -- Apparently, this is the RNG function
--*****************************************************************************
    if r == 0 then return 1 end  -- Can't tell what, really.
    r= r*3
    if r >= 384 then r= r+1 end  -- High bit before r*3 probably determines +1
    return r%256                 -- Fit within one byte
end    -- Address 0059 holds the RNG value

--I've pre-generated the table.
--local Init_RNG= {0,1,3,9,27,81,243,218,143,174,11,33,99,41,123,113,83}
local Loop_RNG= {249,236,197,80,240,209,116,92,20,60,180,29,87,5,15,45,
                 135,150,195,74,222,155,210,119,101, 47,141,168}

--*****************************************************************************
local function r_tbl()
--*****************************************************************************
--Well, get the RNG
    local RNG= R1u(0x0059)

--Scan through our list of values to locate where in the loop it is.
--This scan is inefficient, though. I keep looking even after I found it.
--Also scanned every frame rather than whenever RNG changes.
    local Found= false
    for i= 1, #Loop_RNG do
        if RNG == Loop_RNG[i] then
            DrawNum(254,3+i*6,Loop_RNG[i],"white","black")
            Found= true
        else
            DrawNum(254,3+i*6,Loop_RNG[i],"green","black")
        end
    end
    if Found then return end

--Down here, I'm not inside the Loop_RNG. Predict other numbers, then
    DrawNum(240,9,RNG,"white","black")
    for i= 1, 28 do   -- Place a limit, just in case.
        RNG= Roll(RNG)
        if RNG == Loop_RNG[1] then return end      -- Ah, our Loop_RNG. Done!
        DrawNum(240,9+6*i,RNG,"orange","black")
    end
end

--*****************************************************************************
local function CarDisp()  -- Dem CPU cars, man! Needs to see 'em, I say!
--*****************************************************************************
    for i= 0, 2 do  -- There are three lanes. Each lane uses separate memory.
        local X=  61 + 51*i
        local addr= 0x0520+i*16
        local clr,tmr= "green","grey"
        local dst= "grey"

        local ID= R1s(addr)
        if ID < 0 then  clr,tmr= "grey","green"
        else
            dst= "green"
            local distance= R1u(addr+2)
            if distance >=  64 then dst= "cyan"   end  -- Lane change range
            if distance == 102 then dst= "green"  end  -- Empty zone
            if distance >= 103 then dst= "orange" end  -- possible impact now.
            if distance >= 112 then dst= "white"  end  -- Side.
            if distance >= 124 then dst= "orange" end  -- Front.
        end

        DrawNumx(X+10,  9,R1u(addr+ 0),clr,"black")  -- ID
        DrawNum( X+20,  9,R1u(addr+ 3),tmr,"black")  -- Respawn timer
        DrawNum( X+30,  9,R1u(addr+ 6),tmr,"black")  -- Respawn timer init
        DrawNum( X+12, 16,R1u(addr+ 1),clr,"black")  -- Speed
        DrawNum( X+12, 23,R1u(addr+ 4),tmr,"black")  -- Respawn Speed (base)
        DrawNum( X+12, 30,R1u(addr+ 5),tmr,"black")  -- Respawn Speed (RNGplus)
        DrawNum( X+25, 16,R1u(addr+ 2),dst,"black")  -- Dist main
        DrawNumx(X+34, 16,R1u(addr+ 7),clr,"black")  -- Dist sub
        DrawNum (X+25, 23,R1s(addr+ 8),clr,"black")  -- X offset (lane shift)
        DrawNumx(X+34, 23,R1u(addr+11),clr,"black")  -- X sub

        DrawNumx(X+24, 31,R1u(addr+ 9),tmr,"black")  -- Lane change timer
        DrawNumx(X+32, 31,R1u(addr+10),clr,"black")  -- Related to impacts?
        DrawNumx(X+24, 38,R1u(addr+12),clr,"black")  -- Visible position
--        DrawNumx(X+32, 38,R1u(addr+13),clr,"black")  -- 0xFF
--        DrawNumx(X+24, 45,R1u(addr+14),clr,"black")  -- 0xFF
--        DrawNumx(X+32, 45,R1u(addr+15),clr,"black")  -- 0xFF

--Predict the velocities of spawning cars: Three numbers in advance.
        local r= R1u(0x0059)
        for j= 0, 2 do
            r= Roll(r)
            DrawNum(X+12,37+6*j,
                R1u(addr+4)+bit.band(r,R1u(addr+5)),
                "white","black")
        end -- RNG loop

    end -- car loop
end -- function

--*****************************************************************************
local function SpeedCalcs()
--*****************************************************************************
--Paints the info into the HUD. Alas, I bury the position for the paint here.

--To figure out what your speed does, we need it!
    local speed= R1u(0x0038)

--Curves have varying effect based on speed
--Alas, I'm finding it difficult to work out.

--Speed also has an effect on how well you move, X-wise.
    local Xspd= speed                                             --  0~127
    if speed >= 128 then Xspd= 127-math.floor((speed-128)/2) end  --128~184
    if speed >= 184 then Xspd=  99 end                            --184~222
    if speed >= 222 then Xspd=  99-math.floor((speed-222)/2) end  --222~240
    if speed >= 240 then Xspd=  90 end                            --240~255

    DrawNum( 126,188,math.floor(Xspd/32),"white","black")
    DrawNumx(134,188,           Xspd%32 ,"grey" ,"black")

end

local CourseDist= {
[0]=2084864,  -- 509 * 16 * 256
    2072576,  -- 506 * 16 * 256
    2084864,  -- 509 * 16 * 256
    2170880,  -- 530 * 16 * 256
    2093056,  -- 511 * 16 * 256   Haven't figured out what internals to
    1912832,  -- 467 * 16 * 256   pick from to get these distances.
    2048000,  -- 500 * 16 * 256   Hate manufacturing numbers myself. Eyah...
    2158592   -- 527 * 16 * 256
}
local BoingClr= {[0x00]= "grey",[0x01]="cyan",[0x80]="yellow"}
--*****************************************************************************
local function BasicHUD()
--*****************************************************************************
    CarDisp()
    r_tbl()
    SpeedCalcs()

--Timer stuff, on the left side
    DrawNum(  62,183,R2u(0x0029),"yellow","black")   -- Frame timer (dec)
    DrawNumx( 62,189,R2u(0x0029),"yellow","black")   -- Frame timer (hex)
    DrawNum(  62,204,R1u(0x0062),"white" ,"black")   -- Game timer

--Left-right position crud
    local HPos= R2s(0x0030,0x003F)  -- Horizontal position
    local clr= "green"
    if HPos <= -138 then clr= "white" end   --Slowdown
    if HPos >=  136 then clr= "white" end
    if HPos <= -147 then clr= "orange" end  --Obstacles
    if HPos >=  147 then clr= "orange" end  --Going lazy using ifs.
    DrawNum( 112,188,HPos          ,clr     ,"black") -- Position
    DrawNumx(120,188,R1u(0x0039)   ,"grey"  ,"black") -- Sub-position

--Left of tachometer
    DrawNum(  94,183,R1u(0x002B)-31,"grey"  ,"black") -- Curve-to-be
    DrawNum(  94,189,R1u(0x0448)-31,"white" ,"black") -- Curve-right-now
    DrawNumx( 94,195,R1u(0x003C)   ,"grey"  ,"black") -- Curve sub-position

--Right of tachometer
    clr= BoingClr[R1u(0x005A)] or "purple"           -- BOING direction
    DrawNumx(166,183,R1u(0x005C),clr     ,"black")   -- BOING speed
    DrawNumx(166,189,R1u(0x005B),"grey"  ,"black")   -- BOING sub-position

--Speedometer mirror
    DrawNum( 117,204,R1u(0x0038)   ,"white" ,"black")   -- Speed (main)
    DrawNumx(125,204,R1u(0x0058)   ,"grey"  ,"black")   -- Speed (sub)

--Miscellaneous
--    DrawNum( 137,188,R1s(0x03B0),"cyan"  ,"black")   -- Distant curve
    DrawNum( 254,183,R1u(0x00E5),"white" ,"black")   -- Stage

--Distance crud
    local DM= R1u(0x00F9) -- Distance Multiplier
    local Dist= R1u(0x008F)*DM*256 + R1u(0x0031)*256 + R1u(0x0068)
    DrawNum( 116,218,Dist       ,"White" ,"black")   -- Distance traveled
    local Lost= R2u(0x0029)*255 - Dist - 121975
    DrawNum( 150,218,Lost       ,"orange","black")   -- Diff from theoretical

    local CD= CourseDist[R1u(0x00E5)] or 1
    local Remaining= CD-Dist
    local Clr= "cyan"
    if Remaining <= 0 then Clr= "orange"; Remaining= -Remaining end
    DrawNum(116,225,Remaining,Clr,"black")

--There might be a good reason to separate the bytes more.
--However, combining it all to one decimal number is convenient record keeping.

--Lines of Motivation
    Dist= Dist*256
    local Modu= math.floor((Dist%CD) * 256/CD) - 1
    Dist= math.floor(Dist/CD) - 1

    if     Dist >= 255 then gui.line(0,180,255 ,180,"orange")
    elseif Dist >=   0 then gui.line(0,180,Dist,180,"green")  end
    if     Modu >=   0 then gui.line(0,181,Modu,181,"orange") end
end
gui.register(BasicHUD)