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