I can't get a perfect camhack but I am close to getting one that mostly keeps up. I just need to fix up the silly race. Need to change the hack a bit there because everything you know is wrong.
The basic strategy is to use the rom hack I described earlier to make it scroll when the marble is falling. I wasn't able to produce that result with lua. I am also making lua code that reduces the threshold to start scrolling. I was able to fix the desync in aerial race by hexing one value.
Done! The following lua script syncs whether or not you do the hack described earlier. That will allow for more scrolling. I should point out that this does do a little more than just change graphics; in the silly race in particular one of the mini marbles gets crushed and the bird pattern is different. The overall finish is at the same time but the score is different.
--Marble Madness camhack script for Aglar's run.
--Script by TheAxeMan
--
--I usually put the rewind code in a separate module but combined it here for distribution.
--
--This module is designed to provide "frame rewind" where
--holding down R and pressing frame advance once will bring
--you back one frame. You should be able to pick up from
--there with no trouble.
messagesToDisplay = {}
dispCount = 0
newframe = false
gui.register(
function()
dispCount = dispCount+1
for i,m in pairs(messagesToDisplay) do
gui.text(m.x, m.y, m.text)
end;
newframe = false
end
)
local function addMessage(x, y, text)
table.insert(messagesToDisplay, {x=x, y=y, text=text})
end;
local function clearMessages()
messagesToDisplay = {}
end;
local function readRewindButton()
keysPressed = input.get()
return keysPressed["R"];
end;
rewindSaveStateBuffer = {}
rewindInputBuffer = {}
rewindBufferDepth = 0
rewindLastFramecount = movie.framecount()
--This gets set to "rewind" when rewinding
rewindLastFrameAction = "advance"
function manageRewind()
if readRewindButton() then
if rewindBufferDepth > 2 then
addMessage(10,10, "rewinding")
local loadIndex = movie.framecount()-2
savestate.load(rewindSaveStateBuffer[loadIndex])
joypad.set(1, rewindInputBuffer[loadIndex+1])
rewindBufferDepth = rewindBufferDepth-1
--FCEU.frameadvance()
--clearMessages()
--newframe=true
elseif rewindBufferDepth <= 2 then
local loadIndex = movie.framecount()-1
savestate.load(rewindSaveStateBuffer[loadIndex])
--joypad.set(1, rewindInputBuffer[loadIndex])
addMessage(10,10, "end of buffer")
end;
else
--Add to buffer
local bufferIndex = movie.framecount()
if rewindSaveStateBuffer[bufferIndex] == nil then
rewindSaveStateBuffer[bufferIndex] = savestate.create()
end;
savestate.save(rewindSaveStateBuffer[bufferIndex])
rewindInputBuffer[bufferIndex] = joypad.read(1)
rewindBufferDepth = rewindBufferDepth + 1
end;
addMessage(10,20, "Buffer depth is "..rewindBufferDepth)
end;
function frameAdvanceWithRewind()
FCEU.frameadvance()
manageRewind();
clearMessages()
newframe=true
end;
function pauseWithRewind()
FCEU.pause()
manageRewind();
end;
FCEU.pause()
while true do
--Aggressively move screen down
scrollpos = 50
--Be less aggressive at beginning of intermediate race
if movie.framecount() > 3050 and movie.framecount() < 3480 then scrollpos=100 end;
--Don't do this during part of aerial race
if movie.framecount() > 5100 and movie.framecount() < 5460 then scrollpos=300 end;
--if movie.framecount() > 5100 and movie.framecount() < 6000 then scrollpos=300 end;
--In the Silly race everything you know is wrong
if movie.framecount() > 6500 and movie.framecount() < 7800 then scrollpos = 300 end;
--Disable during Ultimate race to avoid desync
if movie.framecount() > 8400 then scrollpos=300 end;
relpos = memory.readbyte(0x0450)
if relpos > scrollpos then
memory.writebyte(0x0450, 155)
end;
--Special aerial fix
if movie.framecount() == 5500 then
memory.writebyte(0x00A0, 0x33)
end;
--In the Silly race everything you know is wrong
if movie.framecount() > 6500 and movie.framecount() < 7800 then
--This forces screen to just keep scrolling.
memory.writebyte(0x0458, 127)
end;
frameAdvanceWithRewind()
end;