User File #638209636235052914

Upload All User Files

#638209636235052914 (unlisted) - DisplayWaitTimes.lua

DisplayWaitTimes.lua
System: Game Boy Color
33 downloads
Uploaded 5/29/2023 1:27 PM by SBD (see all 61)
Requires WaitTimes.txt to be loaded in the same directory. (warning: 58 MB text file) Depending on your CPU this script might take up to a few minutes to boot. Use DisplayInfo.lua to find the right parameters.
-- This script displays all the times within the Pirate Ship's rotation where the Save Glitch can be performed
-- based on different configurations of the inventory.
--
-------------------------------------------
---------- START OF USER INPUT ------------
-------------------------------------------


local FRAME1 =  499 -- first possible frame for the first save
local FRAME2 =  743 -- first possible frame for the second save
local ODD    = 0xB4 -- ODD and EVEN of the FIRST Save
local EVEN   = 0x9C -- as per DisplayInfo.lua

local FirstSaveHasShovel  = false
local SecondSaveHasShovel = false

local DecoupledFrames     = false -- set if copying one file over the other so you can wait independently on both files
-- The last option requires extra resets so it's pretty slow.
-- The TAS doesn't use it so feel free to ignore it.


local MAXLINES       = 6 -- max number of possibilities shown in each column
local FONTSIZE       = 15
local WINDOWPOSITION = "right of screen" -- options: top-left
--                                                   top-right
--                                                   bottom-left
--                                                   bottom-right
--                                                   right of screen


local LENIENCY = 100  -- times that are within this range of the optimum will be displayed in blue


-----------------------------------------
----------- END OF USER INPUT -----------
-----------------------------------------


local SHIPSCREENADDRESS, SHIPYADDRESS, SHIPXADDRESS, SHIPDIRADDRESS = 0xC6EC, 0xC6ED, 0xC6EE, 0xC6EF
local ROOMADDRESS = 0xC62C                    -- first address after the splice
local INVENTORYSTART = 0xC688                 -- = B-button
local LENGTH = 0x269                          -- half the length of the second part
local INVENTORYLENGTH = 8                     -- half the length of the inventory
local PERIOD = 1856                           -- time it takes for the Pirate Ship to do one full rotation
local DELTAFRAME = (FRAME2 - FRAME1) % PERIOD -- time it takes to get from the first save to the second

local INDEX1, INDEX2 = 2, 2
if FirstSaveHasShovel then INDEX1 = 4 end
if SecondSaveHasShovel then INDEX2 = 4 end
local INDEX = INDEX1 * INDEX2 - 1

local SwordOffset  = 1
local ShovelOffset = 2
if FirstSaveHasShovel then SwordOffset = 2; ShovelOffset = 3 end

local SWORD, SHOVEL  = 0x05, 0x15

local black, white, green, blue = 0xFF000000, 0xFFFFFFFF, 0xFF00FF00, 0xFF0000FF

local COLUMNWIDTH = 7.5 * FONTSIZE
local COLUMNWIDTHover2, COLUMNWIDTHover4 = COLUMNWIDTH/2, COLUMNWIDTH/4
local WINDOWWIDTH, WINDOWHEIGHT = (INDEX + 1) * COLUMNWIDTH, ((INDEX1 + INDEX2)/2 + 2 + MAXLINES) * FONTSIZE

local WINDOWX, WINDOWY
if WINDOWPOSITION == "top-left" then
    WINDOWX, WINDOWY = 0, 0
elseif WINDOWPOSITION == "top-right" then
    WINDOWX, WINDOWY = 0x7FFF, 0
elseif WINDOWPOSITION == "bottom-left" then
    WINDOWX, WINDOWY = 0, 0x7FFF
elseif WINDOWPOSITION == "bottom-right" then
    WINDOWX, WINDOWY = 0x7FFF, 0x7FFF
elseif WINDOWPOSITION == "right of screen" then
    WINDOWX, WINDOWY = client.xpos() + client.screenwidth() + 1, client.ypos()
else
    print("Invalid window-position. Defaulting to \"right of screen\"")
    WINDOWX, WINDOWY = client.xpos() + client.screenwidth() + 1, client.ypos()
end


local function adjust(odd, even)
    --truncate and adjust for carry
    return (odd + math.floor(even / 0x100)) % 0x100, even % 0x100
end


local mt = {} -- table that stores all possibilities for the first and second save
for i = 0, INDEX do
    mt[i] = {}
    for j = 0, 0xFF do
        mt[i][j] = {}
        for k = 0, 0xFF do
            mt[i][j][k] = {}
        end
    end
end


local OddModifiers, EvenModifiers = {}, {}
-- Modifiers to D and E based on inventory state
-- Order of bits from LSB to MSB: Sword first save
--                                (Shovel first save)
--                                Sword second save
--                                (Shovel second save)
--
-- 0 means item is in an odd slot, 1 means it's in an even slot

for index = 0, INDEX do
    
    OddModifiers[index]  = ((index >> SwordOffset) % 2 - index % 2) * SWORD
    EvenModifiers[index] = -OddModifiers[index]
    
    local OddModifier, EvenModifier = 0, 0
    if FirstSaveHasShovel then
        if SecondSaveHasShovel then
            OddModifier  = (index >> 3) - (index >> 1) % 2
            EvenModifier = -OddModifier
        else
            EvenModifier  = (index >> 1) % 2
            OddModifier = 1 - EvenModifier
        end
        
    elseif SecondSaveHasShovel then
        EvenModifier  = -(index >> 2)
        OddModifier = -1 - EvenModifier
    end
    
    OddModifiers [index] = OddModifiers [index] + OddModifier  * SHOVEL
    EvenModifiers[index] = EvenModifiers[index] + EvenModifier * SHOVEL
    
end


-- initialize mt
for line in io.lines("WaitTimes.txt") do
    local odd, even, frame1, frame2 = tonumber(string.sub(line,1,2),16) + ODD, tonumber(string.sub(line,4,6),16) + EVEN, tonumber(string.sub(line,8,11)), tonumber(string.sub(line,13,16))
    
    for index = 0, INDEX do
        local odd, even = adjust(odd + OddModifiers[index], even + EvenModifiers[index])
        local first  = (frame1 - FRAME1) % PERIOD              -- lost frames before first save
        local second = (frame2 - frame1 - DELTAFRAME) % PERIOD -- lost frames before second save
        if DecoupledFrames then second = (frame2 - FRAME2) % PERIOD end
        local total  = first + second                          -- total time loss
        if DecoupledFrames then total = math.max(first, second) end
        
        mt[index][odd][even][#mt[index][odd][even] + 1] = {first, second, total}
    end
end

-- order the times in mt from fastest to slowest
for i = 0, INDEX do
    for odd = 0, 0xFF do
        for even = 0, 0xFF do
            table.sort(mt[i][odd][even], function(a, b) return (a[3] < b[3]) end)
        end
    end
end



local window = gui.createcanvas(WINDOWWIDTH, WINDOWHEIGHT, WINDOWX, WINDOWY)
window.SetTitle("Possible Wait Times")
local oldD, oldE

while true do
    ::nextframe::
    
    -- calculate checksum
    local D,E = 0,0
    for i = 0, LENGTH do
        E = E + memory.readbyte(ROOMADDRESS + 2*i)
        D = D + memory.readbyte(ROOMADDRESS + 2*i + 1)
    end
    E = E - memory.readbyte(SHIPSCREENADDRESS) - memory.readbyte(SHIPXADDRESS)
    D = D - memory.readbyte(SHIPYADDRESS) - memory.readbyte(SHIPDIRADDRESS)
    for i = 0, INVENTORYLENGTH do
        E = E - memory.readbyte(INVENTORYSTART + 2*i)
        D = D - memory.readbyte(INVENTORYSTART + 2*i + 1)
    end
    
    -- if D and E haven't changed, don't redraw the window
    if oldD == D and oldE == E then
        emu.frameadvance()
        goto nextframe
    end
    oldD, oldE = D, E
    
    D, E = adjust(D, E)
    window.Clear(black)
    window.Refresh()
    
    
    local row = 0
    
    -- print header row
    for i = 0, INDEX do
        local string = "sword : "
        if i % 2 == 0 then string = string .. "odd"
        else string = string .. "even" end
        window.DrawString(i * COLUMNWIDTH + COLUMNWIDTHover2, row, string, white, nil, FONTSIZE, nil, nil, "center")
    end
    row = row + FONTSIZE
    
    if FirstSaveHasShovel then
        for i = 0, INDEX do
            local string = "shovel: "
            if (i >> 1) % 2 == 0 then string = string .. "odd"
            else string = string .. "even" end
            window.DrawString(i * COLUMNWIDTH + COLUMNWIDTHover2, row, string, white, nil, FONTSIZE, nil, nil, "center")
        end
        row = row + FONTSIZE
    end
    row = row + FONTSIZE
    
    for i = 0, INDEX do
        local string = "sword : "
        if (i >> SwordOffset) % 2 == 0 then string = string .. "odd"
        else string = string .. "even" end
        window.DrawString(i * COLUMNWIDTH + COLUMNWIDTHover2, row, string, white, nil, FONTSIZE, nil, nil, "center")
    end
    row = row + FONTSIZE
    
    if SecondSaveHasShovel then
        for i = 0, INDEX do
            local string = "shovel: "
            if (i >> ShovelOffset) %2 == 0 then string = string .. "odd"
            else string = string .. "even" end
            window.DrawString(i * COLUMNWIDTH + COLUMNWIDTHover2, row, string, white, nil, FONTSIZE, nil, nil, "center")
        end
        row = row + FONTSIZE
    end
    

    -- draw horizontal line
    window.DrawLine(0, row, WINDOWWIDTH, row, white)
    row = row + FONTSIZE
    
    -- draw vertical lines
    for i = 1, INDEX do
        local column = COLUMNWIDTH * i
        window.DrawLine(column, 0, column, WINDOWHEIGHT, white)
    end
    
    
    -- calculate optimum
    local min = math.maxinteger
    for i = 0, INDEX do
        if mt[i][D][E][1] and mt[i][D][E][1][3] < min then
            min = mt[i][D][E][1][3]
        end
    end
    
    -- print the times
    for i = 0, INDEX do
        local row = row
        local column = i * COLUMNWIDTH + COLUMNWIDTHover4
        
        for k = 1, MAXLINES do
            if not mt[i][D][E][k] then break end
            
            local colour = white
            if mt[i][D][E][k][3] == min then colour = green
            elseif mt[i][D][E][k][3] <= min + LENIENCY then colour = blue end
            
            window.DrawText(column                   , row, mt[i][D][E][k][1], colour, nil, FONTSIZE, nil, nil, "center")
            window.DrawText(column + COLUMNWIDTHover2, row, mt[i][D][E][k][2], colour, nil, FONTSIZE, nil, nil, "center")
            
            row = row + FONTSIZE
            k = k + 1
        end
    end
    
    
    emu.frameadvance()
end