804 lines
27 KiB
Lua
804 lines
27 KiB
Lua
-- Multi-track rerecording for FCEUX 2.1.2 or later (script V2)
|
|
-- To make up for the lack of an internal multi-track rerecording in FCEUX.
|
|
-- It may get obsolete when TASEdit comes around.
|
|
--FatRatKnight
|
|
-- Rewind (by Antony Lavelle) added by DarkKobold
|
|
|
|
-- Instructions:
|
|
-- The script has its own buttons, defined in the junk below.
|
|
-- If you're not sure what they do, experiment by pressing the button!
|
|
-- If you're still not sure, I'll give you some basic idea here.
|
|
|
|
-- With the script buttons, defined by key down there, you can
|
|
-- toggle the input on or off for the next frame. Just push
|
|
-- the key button, and you'll see a light change. I don't
|
|
-- recommend HOLDing down the key button, especially if they
|
|
-- overlap with the actual controls you've set up on your
|
|
-- emulator. Especially there, since that's where problems begin.
|
|
|
|
-- I also have options to determine whether the input from
|
|
-- the emulator should apply or whether the script should
|
|
-- bother. Hold space (default keys), and you can switch
|
|
-- individual buttons on or off. The script will decide
|
|
-- whether to ignore the script keys here or the input stored
|
|
-- away in its list, with the options that pop up.
|
|
|
|
-- There's a player switch as well. Just hit the S key
|
|
-- (default keys) and it'll change to player 2. Or 3, or 4.
|
|
-- Or all players at once. This will make the script keys
|
|
-- start working on other players instead of just 1.
|
|
|
|
-- Then there's the joypad input. home or end (default keys)
|
|
-- grants varying levels of control to the emulator joypad.
|
|
-- If you want to get rid of all control from the joypad setup
|
|
-- of the emulator, you can do that. The defaults are to allow
|
|
-- the controllers to toggle whatever input the script has.
|
|
-- I did not implement a way to give this decision to
|
|
-- individual joypad keys.
|
|
|
|
-- That's the basics of controlling input with this script.
|
|
-- The script buttons let you decide input without a frame
|
|
-- advance, and the joypad keys are there if you're so used
|
|
-- to holding things down while pressing frame advance.
|
|
|
|
|
|
-- A few more advanced things are in here. I will briefly mention them:
|
|
-- Rewind: Back up a frame or two with a press of a button!
|
|
-- Seems to hog up CPU greatly. Disable if you want things run smoothly.
|
|
-- Insert/Delete: Found a time saver, yet want to keep later input?
|
|
-- This lets you slide a chunk of frames forward or back.
|
|
-- I display a number that says how many frames are buffered.
|
|
-- Immediate: Good if you want to change input while rewinding.
|
|
-- Okay, its use is quite rare, but it's there.
|
|
|
|
-- Hopefully, this is enough to let you get started. Good luck!
|
|
|
|
|
|
|
|
-- Stuff that you are encouraged to change to fit what you need.
|
|
--*****************************************************************************
|
|
-- Control scheme. Just experiment a little with the buttons.
|
|
|
|
local selectplayer = "S" -- For selecting which player
|
|
local recordingtype= "space" -- For selecting how to record
|
|
local SetImmediate = "numpad5"--Toggle immediate option
|
|
local TrueSwitch = "home" -- Toggle whether joypad can turn off input
|
|
local FalseSwitch = "end" -- Toggle whether joypad can turn on input
|
|
|
|
local show, hide = "pageup", "pagedown" -- Opacity adjuster
|
|
local scrlup, scrldown = "numpad8", "numpad2" -- Move the input display
|
|
local scrlleft, scrlright= "numpad4", "numpad6" -- Not a fast method, though.
|
|
local MorePast, LessPast = "numpad7", "numpad1" -- See more or less input!
|
|
local MoreFuture, LessFuture= "numpad3", "numpad9" -- Good use of screen space
|
|
|
|
local add, remove = "insert", "delete" -- Moves input around
|
|
local rewind = "R" -- The button used to rewind things
|
|
|
|
local key = {"right", "left", "down", "up", "L", "O", "J", "K"}
|
|
local btn = {"right", "left", "down", "up", "start", "select", "B", "A"}
|
|
|
|
-- key links to the keyboard keys, so change those to fit your control needs.
|
|
-- btn links to joypad buttons. Don't change what's in there.
|
|
-- However, feel free to reorder the strings in btn! It will change the display!
|
|
|
|
|
|
--*****************************************************************************
|
|
-- Values to change. players, saveMax, and MsgDuration aren't changed in the
|
|
-- code. All others are simply default values which can dynamically change.
|
|
|
|
local players= 2 -- You may tweak the number of players here.
|
|
|
|
local saveMax= 1000 -- Max Rewind Power. Set to 0 to disable
|
|
|
|
local opaque= 0 -- Default opacity. 0 is solid, 4 is invisible.
|
|
|
|
local dispX, dispY= 10, 99 -- Default input display position.
|
|
|
|
local Past, Future= -10, 10 -- Keep Past negative; Range of input display
|
|
|
|
local immediate= false -- true: Changes apply to list upon happening
|
|
-- false: Changes only apply on frame advance
|
|
local JoyOn = "inv" -- true OR "inv" If true, disable turn-off
|
|
local JoyOff= nil -- false OR nil If false, disable turn-on
|
|
|
|
local MsgDuration= 60 -- How long should messages stay?
|
|
|
|
|
|
--*****************************************************************************
|
|
|
|
-- Stuff that really shouldn't be messed with, unless you plan to change the code significantly.
|
|
local optType= {"Both", "Keys", "List", "Off"} -- Names for option types
|
|
local keys, lastkeys= {}, {} -- To hold keyboard input
|
|
local Pin, thisInput= {}, {} -- Input manipulation array
|
|
local BufInput, BufLen= {},{}-- In case you want to insert or remove frames.
|
|
local option= {} -- Options for individual button input by script
|
|
local fc, lastfc= 0, 0 -- Frame counters
|
|
local FrameAdvance= false -- Frame advance detection
|
|
local pl= 1 -- Player selected
|
|
local plmin, plmax= 1, 1 -- For the all option
|
|
local repeater= 0 -- for holding a button
|
|
local rewinding= false -- For unpaused rewinding
|
|
local MsgTmr= 0 -- To time how long messages stay
|
|
local Message= "Activated Multitrack recording script. Have fun!"
|
|
|
|
|
|
--The stuff below is adapted from the original Rewinding script by Antony Lavelle
|
|
|
|
local saveArray = {};--the Array in which the save states are stored
|
|
local saveCount = 0;--used for finding which array position to cycle through
|
|
local saver; -- the variable used for storing the save state
|
|
local rewindCount = 0;--this stops you looping back around the array if theres nothing at the end
|
|
local savePreventBuffer = 1;--Used for more control over when save states will be saved, not really used in this version much.
|
|
|
|
|
|
-- Initialize tables for each player.
|
|
for i= 1, players do
|
|
Pin[i] = {}
|
|
thisInput[i] = {}
|
|
option[i] = {}
|
|
BufInput[i] = {}
|
|
BufLen[i] = 0
|
|
for j= 1, 8 do
|
|
option[i][btn[j]]= 1
|
|
end
|
|
end
|
|
|
|
-- Intention of options:
|
|
-- "Both" Script buttons and input list used
|
|
-- "Keys" Script buttons used
|
|
-- "List" Input list used
|
|
-- "Off" Script will not interfere with button
|
|
|
|
|
|
--*****************************************************************************
|
|
function press(button)
|
|
--*****************************************************************************
|
|
--FatRatKnight
|
|
-- Checks if a button is pressed.
|
|
-- The tables it accesses should be obvious in the next line.
|
|
|
|
if keys[button] and not lastkeys[button] then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
|
|
--*****************************************************************************
|
|
function pressrepeater(button)
|
|
--*****************************************************************************
|
|
--DarkKobold; Changes by FatRatKnight. Description by FatRatKnight
|
|
-- Checks if a button is pressed. Will keep returning true if held long enough.
|
|
-- Accesses: keys[], lastkeys[], repeater
|
|
|
|
if keys[button] then
|
|
if not lastkeys[button] or repeater >= 3 then
|
|
return true
|
|
else
|
|
repeater = repeater + 1;
|
|
end
|
|
|
|
elseif lastkeys[button] then -- To allow more calls for other buttons
|
|
repeater= 0
|
|
end;
|
|
return false
|
|
end
|
|
|
|
|
|
--*****************************************************************************
|
|
function NewMsg(text)
|
|
--*****************************************************************************
|
|
--FatRatKnight
|
|
-- Sets the message and resets the timer
|
|
-- Accesses: MsgTmr, Message
|
|
|
|
MsgTmr= 0
|
|
Message= text
|
|
end
|
|
|
|
|
|
--*****************************************************************************
|
|
function DispMsg()
|
|
--*****************************************************************************
|
|
--FatRatKnight
|
|
-- Merely displays whatever text happens to be in Message
|
|
-- Accesses: MsgTmr, Message, MsgDuration
|
|
|
|
if MsgTmr < MsgDuration then
|
|
MsgTmr= MsgTmr + 1
|
|
gui.text(0,20,Message)
|
|
end
|
|
end
|
|
|
|
|
|
--*****************************************************************************
|
|
function RewindThis()
|
|
--*****************************************************************************
|
|
--Added by DarkKobold; Made into function that returns T/F by FatRatKnight
|
|
-- Loads a state that was saved just the frame prior
|
|
-- Lets you know whether it was successful by returning true or false.
|
|
-- Accesses: rewindCount, saveArray, saveCount, saveMax
|
|
|
|
if rewindCount<=0 then
|
|
return false -- Failed to rewind
|
|
else
|
|
savestate.load(saveArray[saveCount]);
|
|
saveCount = saveCount-1;
|
|
rewindCount = rewindCount-1;
|
|
if saveCount<=0 then
|
|
saveCount = saveMax;
|
|
end;
|
|
end;
|
|
return true -- Yay, rewind!
|
|
end
|
|
|
|
|
|
--*****************************************************************************
|
|
function GetNextInput(P)
|
|
--*****************************************************************************
|
|
--FatRatKnight
|
|
-- Gets the input from the input table.
|
|
-- Accesses: BufLen[], BufInput[], fc, Pin[]
|
|
|
|
if BufLen[P] > 0 then
|
|
return BufInput[P][BufLen[P]]
|
|
end
|
|
return Pin[P][fc-BufLen[P]]
|
|
end
|
|
|
|
|
|
--*****************************************************************************
|
|
function LoadStoredInput(P)
|
|
--*****************************************************************************
|
|
--FatRatKnight
|
|
-- Loads up input into thisInput.
|
|
-- Found a good reason why it should be called in several spots.
|
|
-- Accesses: option[], thisInput[]
|
|
|
|
local next= GetNextInput(P)
|
|
if next then
|
|
for i= 1, 8 do
|
|
local op= option[P][btn[i]]
|
|
if op == 1 or op == 3 then -- "Both" or "List"
|
|
thisInput[P][btn[i]]= next[btn[i]]
|
|
else
|
|
thisInput[P][btn[i]]= nil
|
|
end
|
|
end
|
|
else -- No input to load, so erase stuff.
|
|
for i= 1, 8 do
|
|
thisInput[P][btn[i]]= nil
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
--*****************************************************************************
|
|
function GetList(P,Offset)
|
|
--*****************************************************************************
|
|
--FatRatKnight
|
|
-- Fetches the input from the list relative to current framecount.
|
|
-- It takes into account insertions and deletions.
|
|
-- Accesses: thisInput[], Pin[], BufLen[], BufInput[]
|
|
|
|
if Offset == 0 then
|
|
return thisInput[P]
|
|
elseif Offset < 0 then
|
|
return Pin[P][fc+Offset]
|
|
end
|
|
|
|
if BufLen[P] > 0 and BufLen[P] > Offset then
|
|
return BufInput[P][BufLen[P]-Offset]
|
|
end
|
|
return Pin[P][fc+Offset-BufLen[P]]
|
|
end
|
|
|
|
|
|
--*****************************************************************************
|
|
function InputSnap(P)
|
|
--*****************************************************************************
|
|
--FatRatKnight
|
|
-- Will shove the input list over.
|
|
-- Might end up freezing for a moment if there's a few thousand frames to shift
|
|
-- Accesses: framed, BufLen, BufInput[], Pin[], players
|
|
|
|
|
|
if BufLen[P] < 0 then -- Squish!
|
|
local pointer= lastfc
|
|
while Pin[P][pointer] do
|
|
Pin[P][pointer]= Pin[P][pointer-BufLen[P]]
|
|
pointer= pointer+1
|
|
end
|
|
end
|
|
|
|
|
|
if BufLen[P] > 0 then -- Foom!
|
|
local pointer= lastfc
|
|
while Pin[P][pointer] do
|
|
pointer= pointer+1
|
|
end
|
|
-- pointer is now looking at a null frame.
|
|
-- Assume later frames are also null.
|
|
|
|
while pointer > lastfc do
|
|
pointer= pointer-1
|
|
Pin[P][pointer+BufLen[P]]= Pin[P][pointer]
|
|
end
|
|
-- pointer should now match lastfc.
|
|
-- Everything at lastfc and beyond should be moved over by BufLen.
|
|
|
|
for i=0, BufLen[P]-1 do
|
|
Pin[P][lastfc +i]= BufInput[P][BufLen[P]-i]
|
|
end
|
|
end
|
|
|
|
BufLen[P]= 0 -- If it ain't zero before, we did stuff to make us want it zero.
|
|
end
|
|
|
|
|
|
--*****************************************************************************
|
|
function NewFrame()
|
|
--*****************************************************************************
|
|
--FatRatKnight
|
|
-- Psuedo-detection of frame-advance or state load.
|
|
-- The lack of a function to otherwise detect state loads calls for this function
|
|
-- Accesses: fc, lastfc, FrameAdvance
|
|
|
|
fc= movie.framecount()
|
|
|
|
for P= 1, players do
|
|
InputSnap(P)
|
|
LoadStoredInput(P)
|
|
end
|
|
end
|
|
|
|
savestate.registerload(NewFrame)
|
|
|
|
|
|
--*****************************************************************************
|
|
local Draw= {} --Draw[button]( Left , Top , color )
|
|
--*****************************************************************************
|
|
--FatRatKnight
|
|
-- A set of functions for which to draw unique objects for each button.
|
|
-- The functions are in a table, so I can index them easy.
|
|
-- For use with DisplayInput()
|
|
|
|
function Draw.right(x,y,color) -- ##
|
|
gui.line(x ,y ,x+1,y ,color) -- #
|
|
gui.line(x ,y+2,x+1,y+2,color) -- ##
|
|
gui.pixel(x+2,y+1,color)
|
|
end
|
|
|
|
function Draw.left(x,y,color) -- ##
|
|
gui.line(x+1,y ,x+2,y ,color) -- #
|
|
gui.line(x+1,y+2,x+2,y+2,color) -- ##
|
|
gui.pixel(x ,y+1,color)
|
|
end
|
|
|
|
function Draw.up(x,y,color) -- #
|
|
gui.line(x ,y+1,x ,y+2,color) -- # #
|
|
gui.line(x+2,y+1,x+2,y+2,color) -- # #
|
|
gui.pixel(x+1,y ,color)
|
|
end
|
|
|
|
function Draw.down(x,y,color) -- # #
|
|
gui.line(x ,y ,x ,y+1,color) -- # #
|
|
gui.line(x+2,y ,x+2,y+1,color) -- #
|
|
gui.pixel(x+1,y+2,color)
|
|
end
|
|
|
|
function Draw.start(x,y,color) -- #
|
|
gui.line(x+1,y ,x+1,y+2,color) -- ###
|
|
gui.line(x ,y+1,x+2,y+1,color) -- #
|
|
end
|
|
|
|
function Draw.select(x,y,color) -- ###
|
|
gui.box(x ,y ,x+2,y+2,color) -- # #
|
|
end -- ###
|
|
|
|
function Draw.A(x,y,color) -- ###
|
|
gui.box(x ,y ,x+2,y+1,color) -- ###
|
|
gui.pixel(x ,y+2,color) -- # #
|
|
gui.pixel(x+2,y+2,color)
|
|
end
|
|
|
|
function Draw.B(x,y,color) -- # #
|
|
gui.line(x ,y ,x ,y+2,color) -- ##
|
|
gui.line(x+1,y+1,x+2,y+2,color) -- # #
|
|
gui.pixel(x+2,y ,color)
|
|
end
|
|
|
|
|
|
--*****************************************************************************
|
|
function DisplayInput()
|
|
--*****************************************************************************
|
|
--FatRatKnight; Changes by DarkKobold.
|
|
-- Shows the input stream that is stored in this script.
|
|
-- Converted into function form. There might be other ways of handling it.
|
|
-- Accesses: dispX, dispY, Pin[], thisInput[], players
|
|
|
|
dispX= math.min(math.max(dispX,-2),219)
|
|
if pl > players then -- All players, man?
|
|
dispX= math.min(dispX,243-16*players)
|
|
end
|
|
|
|
if (Future-Past) > 53 then
|
|
Past= math.max(Past, -53)
|
|
Future= Past + 53
|
|
end
|
|
Past = math.min(Past ,0)
|
|
Future= math.max(Future,0)
|
|
dispY = math.min(math.max(dispY, 3-4*Past),217-4*Future)
|
|
|
|
for i= Past, Future do
|
|
|
|
local X,Y= dispX,dispY
|
|
if i < 0 then
|
|
Y= Y-3
|
|
elseif i > 0 then
|
|
Y= Y+3
|
|
end
|
|
|
|
for P= plmin, plmax do
|
|
local temp= {} -- I may want to pick thisInput instead of the frame list.
|
|
temp= GetList(P,i)
|
|
|
|
local color
|
|
for j= 1, 8 do
|
|
if not temp then
|
|
color= "white"
|
|
elseif temp[btn[j]] then
|
|
color= "green"
|
|
else
|
|
color= "red"
|
|
end
|
|
|
|
if pl <= players then
|
|
Draw[btn[j]]( X +4*j , Y +4*i , color)
|
|
else -- all players
|
|
local bx= X+1 +j +2*players*(j-1) +2*P
|
|
gui.box(bx , Y+4*i , bx+1 , Y+4*i +2 ,color)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local color= "white" -- immediate == true
|
|
if not immediate then
|
|
color= "blue"
|
|
for P= plmin, plmax do
|
|
local TI= GetNextInput(P)
|
|
for i=1, 8 do
|
|
if not TI or (TI[btn[i]] and not thisInput[P][btn[i]]) or (not TI[btn[i]] and thisInput[P][btn[i]])then
|
|
color= "green"
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if pl <= players then
|
|
gui.box(dispX+2,dispY-2,dispX+36,dispY+4,color)
|
|
else
|
|
gui.box(dispX+2,dispY-2,dispX+12 +16*players,dispY+4,color)
|
|
end
|
|
end
|
|
|
|
|
|
-- Primary function. ... Why did I pick itisyourturn, anyway?
|
|
--*****************************************************************************
|
|
function itisyourturn()
|
|
--*****************************************************************************
|
|
keys= input.get() -- We need the keyboard and mouse!
|
|
fc= movie.framecount() -- We also need the Frame Count.
|
|
|
|
-- State: Rewind
|
|
if saveMax > 0 then -- Disable Rewind if none exists
|
|
if pressrepeater(rewind) or rewinding then
|
|
rewinding= false
|
|
if RewindThis() then
|
|
NewFrame()
|
|
movie.rerecordcounting(true)
|
|
end
|
|
if rewindCount <= 0 then
|
|
emu.pause()
|
|
end
|
|
end
|
|
end
|
|
|
|
if (fc == 0) and (lastfc ~= 0) then -- Hacky bit of script...
|
|
NewFrame() -- In case of resets that aren't
|
|
end -- considered stateloads.
|
|
|
|
-- Selection: Players
|
|
if press(selectplayer) then
|
|
pl= pl + 1
|
|
if (pl > players+1) or (players == 1) then
|
|
pl= 1
|
|
end
|
|
|
|
-- For standard player loops for allplayer option
|
|
if pl > players then
|
|
plmin= 1
|
|
plmax= players
|
|
else
|
|
plmin= pl
|
|
plmax= pl
|
|
end
|
|
end
|
|
|
|
|
|
-- Input: Insert or delete frames
|
|
if press(add) then -- Part of the reason for
|
|
for P= plmin, plmax do -- speedy insertions and
|
|
BufLen[P]= BufLen[P]+1 -- deletions is due to the
|
|
if BufLen[P] > 0 then -- fact that I don't shift
|
|
BufInput[P][BufLen[P]]= {} -- frames immediately.
|
|
end
|
|
LoadStoredInput(P)
|
|
end
|
|
end -- I only shift once you
|
|
if press(remove) then -- begin rewind or load a
|
|
for P= plmin, plmax do -- state. At that point,
|
|
BufLen[P]= BufLen[P]-1 -- it may take a while if
|
|
LoadStoredInput(P) -- there's enough frames
|
|
end -- to shift around.
|
|
end
|
|
|
|
-- Input: Should thisInput always instantly write to input table?
|
|
if press(SetImmediate) then
|
|
if not immediate then
|
|
immediate= true
|
|
else
|
|
immediate= false
|
|
end
|
|
end
|
|
|
|
-- Input: Allow joypad to toggle input?
|
|
if press(TrueSwitch) then
|
|
if JoyOn == true then
|
|
JoyOn= "inv"
|
|
NewMsg("Joypad may now cancel input from the list")
|
|
else
|
|
JoyOn= true
|
|
NewMsg("Disabled joypad input cancellation")
|
|
end
|
|
end
|
|
if press(FalseSwitch) then
|
|
if JoyOff == false then
|
|
JoyOff= nil
|
|
NewMsg("Joypad may now write input into the list")
|
|
else
|
|
JoyOff= false
|
|
NewMsg("Disabled joypad input activation")
|
|
end
|
|
end
|
|
|
|
|
|
-- Option: Opacity
|
|
if press(hide) and opaque < 4 then
|
|
opaque= opaque+1
|
|
end
|
|
if press(show) and opaque > 0 then
|
|
opaque= opaque-1
|
|
end
|
|
gui.transparency(opaque)
|
|
|
|
-- Option: Move input display by keyboard
|
|
if pressrepeater(scrlup) then
|
|
dispY= dispY - 1
|
|
end
|
|
if pressrepeater(scrldown) then
|
|
dispY= dispY + 1
|
|
end
|
|
if pressrepeater(scrlleft) then
|
|
dispX= dispX - 1
|
|
end
|
|
if pressrepeater(scrlright) then
|
|
dispX= dispX + 1
|
|
end
|
|
|
|
-- Option: Move input display by mouse
|
|
if keys["leftclick"] and lastkeys["leftclick"] then
|
|
dispX= dispX + keys.xmouse - lastkeys.xmouse
|
|
dispY= dispY + keys.ymouse - lastkeys.ymouse
|
|
end
|
|
|
|
-- Option: Adjust input display range
|
|
if pressrepeater(MorePast) then
|
|
Past= Past-1 -- Remember, Past is negative.
|
|
end -- Making it more negative would
|
|
if pressrepeater(LessPast) then -- let you see more of the past.
|
|
Past= Past+1
|
|
end
|
|
if pressrepeater(MoreFuture) then -- On the other hand, I want to
|
|
Future= Future+1 -- make the Future positive.
|
|
end -- Don't we all want to be
|
|
if pressrepeater(LessFuture) then -- positive about the future?
|
|
Future= Future-1
|
|
end
|
|
|
|
|
|
-- Begin start of frame change
|
|
if FrameAdvance then
|
|
for P= 1, players do
|
|
if not Pin[P][lastfc] then
|
|
InputSnap(P) -- If last frame was empty, kill the shift
|
|
end -- Don't track more than one buffer for loads
|
|
|
|
-- Scroll the BufInput if there's anything in there.
|
|
-- This can potentially slow things if you insert enough frames.
|
|
if BufLen[P] > 0 then
|
|
for i= BufLen[P], 1, -1 do
|
|
BufInput[P][i]= BufInput[P][i-1]
|
|
end
|
|
BufInput[P][1]= Pin[P][lastfc]
|
|
end
|
|
|
|
-- Store input
|
|
Pin[P][lastfc]= joypad.get(P)
|
|
|
|
LoadStoredInput(P)
|
|
end -- Loop for each player
|
|
|
|
-- Resets key input, so it counts as pressed again if held through frame advance.
|
|
for i= 1, 8 do
|
|
lastkeys[key[i]]= nil
|
|
end
|
|
|
|
end
|
|
-- Done checking for a new frame
|
|
|
|
|
|
-- Begin checking key input
|
|
-- Are you setting button options?
|
|
if keys[recordingtype] then
|
|
gui.transparency(0) -- Override current opacity
|
|
for i= 1, 8 do -- key loop
|
|
|
|
-- Set current options
|
|
for P= plmin, plmax do
|
|
if press(key[i]) then
|
|
option[P][btn[i]]= option[plmax][btn[i]]+1
|
|
if option[P][btn[i]] > 4 then
|
|
option[P][btn[i]]= 1
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Display current options
|
|
gui.text(20,20+12*i,btn[i])
|
|
|
|
if pl >= players + 1 then
|
|
local q= optType[option[1][btn[i]]]
|
|
for z= 2, players do
|
|
if q ~= optType[option[z][btn[i]]] then
|
|
q= "???"
|
|
break
|
|
end
|
|
end
|
|
gui.text(50,20+12*i,q)
|
|
else
|
|
gui.text(50,20+12*i,optType[option[pl][btn[i]]])
|
|
end
|
|
|
|
end -- key loop
|
|
|
|
-- Are you setting actual input?
|
|
else
|
|
for i= 1, 8 do
|
|
|
|
-- Toggle keyed input depending on options
|
|
for P= plmin, plmax do
|
|
op= option[P][btn[i]]
|
|
if op == 1 or op == 2 then -- "Both" or "Keys"
|
|
if press(key[i]) then
|
|
if thisInput[P][btn[i]] then
|
|
thisInput[P][btn[i]]= nil
|
|
else
|
|
thisInput[P][btn[i]]= true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
end -- key loop
|
|
|
|
-- Done with input.
|
|
-- The next stuff is just displaying input table.
|
|
-- It's here to hide it when setting options.
|
|
if opaque < 4 then
|
|
DisplayInput()
|
|
end
|
|
end -- Done with "options" if
|
|
|
|
-- Force copy of thisInput into the input table if immediate is on
|
|
if immediate then
|
|
for P= 1, players do
|
|
if not GetNextInput(P) then
|
|
Pin[P][fc-BufLen[P]]= {}
|
|
end -- Inserted frames always defined! Don't check for that
|
|
|
|
if BufLen[P] > 0 then
|
|
for i= 1, 8 do
|
|
BufInput[P][BufLen[P]][btn[i]]= thisInput[P][btn[i]]
|
|
end
|
|
else
|
|
for i= 1, 8 do
|
|
Pin[P][fc-BufLen[P]][btn[i]]= thisInput[P][btn[i]]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Display selected player. Or other odds and ends.
|
|
if pl <= players then
|
|
gui.text(30,10,pl)
|
|
gui.text(45,10,BufLen[pl])
|
|
else
|
|
gui.text(30,10,"A")
|
|
end;
|
|
|
|
DispMsg() -- Also display whatever message is in there.
|
|
|
|
-- Change thisInput to conform to options
|
|
for P= 1, players do
|
|
for i= 1, 8 do
|
|
if thisInput[P][btn[i]] then
|
|
thisInput[P][btn[i]]= JoyOn -- Use the right kind of true
|
|
else
|
|
thisInput[P][btn[i]]= JoyOff -- And the right kind of false
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Feed the input to the emulation.
|
|
if movie.mode() ~= "playback" then
|
|
for P= 1, players do
|
|
joypad.set(P, thisInput[P])
|
|
end
|
|
end
|
|
|
|
lastfc= fc -- Standard "this happened last time" stuff
|
|
lastkeys= keys -- Don't want to keep registering key hits.
|
|
FrameAdvance= false
|
|
|
|
end -- Yes, finally! The end of itisyourturn!
|
|
--*****************************************************************************
|
|
|
|
|
|
gui.register(itisyourturn) -- Need to call while in between frames
|
|
|
|
emu.pause(); -- Immediate pause is nice
|
|
|
|
--*****************************************************************************
|
|
while true do -- Main loop
|
|
--*****************************************************************************
|
|
-- Most of the stuff here by DarkKobold. Minor stuff by FatRatKnight
|
|
-- Rewinding feature originally by Antony Lavelle.
|
|
-- Keep in mind stuff here only happens on a frame advance or when unpaused.
|
|
|
|
FrameAdvance= true
|
|
emu.frameadvance()
|
|
|
|
if saveMax > 0 then -- Don't process if Rewind is disabled
|
|
if keys[rewind] and rewindCount > 0 then
|
|
rewinding= true
|
|
else
|
|
saveCount=saveCount+1;
|
|
rewindCount = math.min(rewindCount + 1,saveMax);
|
|
if saveCount==saveMax+1 then
|
|
saveCount = 1;
|
|
end
|
|
|
|
if saveArray[saveCount] == nil then
|
|
saver = savestate.create();
|
|
else
|
|
saver = saveArray[saveCount];
|
|
end;
|
|
savestate.save(saver);
|
|
saveArray[saveCount] = saver; -- I'm pretty sure this line is unnecessary.
|
|
movie.rerecordcounting(false)
|
|
end
|
|
end
|
|
|
|
end -- Main loop ends |