2008-08-02 18:33:39 +00:00
-- BeeBee, LuaBot Frontend v1.07
-- qFox, 2 August 2008
2008-08-02 22:54:58 +00:00
-- we need iup, so include it here (also takes care of cleaning up dialog when script exits)
2008-08-02 23:11:33 +00:00
require ' auxlib ' ;
2008-07-31 23:31:49 +00:00
local botVersion = 1 ; -- check this version when saving/loading. this will change whenever the botsave-file changes.
function createTextareaTab ( reftable , tmptable , token , tab , fun , val ) -- specific one, at that :)
2008-08-01 21:58:18 +00:00
reftable [ token ] = iup.multiline { title = " Contents " , expand = " YES " , border = " YES " } ; -- ,value=val};
2008-07-31 23:31:49 +00:00
tmptable [ token ] = iup.vbox { iup.label { title = " function " .. fun .. " () \n local result = no; " } , reftable [ token ] , iup.label { title = " return result; \n end; " } } ;
tmptable [ token ] . tabtitle = tab ;
end ;
function createTextareaTab2 ( reftable , tmptable , token , fun , arg ) -- specific one, at that :) this one generates no return values
2008-08-01 21:58:18 +00:00
reftable [ token ] = iup.multiline { title = " Contents " , expand = " YES " , border = " YES " } ; --,value=fun};
2008-07-31 23:31:49 +00:00
if ( arg ) then
tmptable [ token ] = iup.vbox { iup.label { title = " function " .. fun .. " (wasOk) -- wasOk (boolean) is true when the attempt was ok \n " } , reftable [ token ] , iup.label { title = " end; " } } ;
else
tmptable [ token ] = iup.vbox { iup.label { title = " function " .. fun .. " () \n " } , reftable [ token ] , iup.label { title = " end; " } } ;
end ;
tmptable [ token ] . tabtitle = fun ;
end ;
function createGUI ( n )
-- this table will keep the references for easy and fast lookup. <STRID, itemobj>
local reftable = { } ;
-- this table we wont keep, it holds references to (mostly) cosmetic elements of the dialog
local tmptable = { } ;
-- ok, dont be intimidated by the next eight blocks of code. they basically all say the same!
-- every line creates an element for the gui and sets it up. every block is a tabbed pane and
-- they are all put into another tabbed pane themselves. all references are put into a table
-- paired with the tokens in the basicbot framework. this allows us to easily walk through
-- all the pairs of <tokens,references> and replace them in the file, uppon writing.
reftable.ROMNAME = iup.text { title = " rom name " , size = " 300x " , value = " something_or_the_other.rom " } ;
tmptable.ROMNAME = iup.hbox { iup.label { title = " Rom name: " , size = " 50x " } , reftable.ROMNAME , iup.fill { } } ;
reftable.COMMENT = iup.text { title = " comment " , size = " 300x " , value = " a botscript for some game " } ;
tmptable.COMMENT = iup.hbox { iup.label { title = " Comment: " , size = " 50x " } , reftable.COMMENT , iup.fill { } } ;
reftable.VERSION = iup.text { title = " version " , size = " 70x " , value = " 1.00 " } ;
tmptable.VERSION = iup.hbox { iup.label { title = " Version: " , size = " 50x " } , reftable.VERSION , iup.fill { } } ;
tmptable.SAVE = iup.button { title = " Save contents " } ;
-- the callback is set after the dialog is created. we need the references to all controls for saving to work :)
tmptable.LOAD = iup.button { title = " Load contents " } ;
tmptable.WRITE = iup.button { title = " Write bot script " } ;
general = iup.vbox { tmptable.ROMNAME , tmptable.COMMENT , tmptable.VERSION , iup.fill { size = " 5x " , } , tmptable.SAVE , iup.fill { size = " 5x " , } , tmptable.LOAD , iup.fill { size = " 5x " , } , tmptable.WRITE , iup.fill { } } ;
general.tabtitle = " General " ;
createTextareaTab ( reftable , tmptable , " bA1 " , " A " , " isPressedA1 " , " a1 " ) ;
createTextareaTab ( reftable , tmptable , " bB1 " , " B " , " isPressedB1 " , " b1 " ) ;
createTextareaTab ( reftable , tmptable , " START1 " , " Start " , " isPressedStart1 " , " start1 " ) ;
createTextareaTab ( reftable , tmptable , " SELECT1 " , " Select " , " isPressedSelect1 " , " select1 " ) ;
createTextareaTab ( reftable , tmptable , " UP1 " , " Up " , " isPressedUp1 " , " up1 " ) ;
createTextareaTab ( reftable , tmptable , " DOWN1 " , " Down " , " isPressedDown1 " , " down1 " ) ;
createTextareaTab ( reftable , tmptable , " LEFT1 " , " Left " , " isPressedLeft1 " , " left1 " ) ;
createTextareaTab ( reftable , tmptable , " RIGHT1 " , " Right " , " isPressedRight1 " , " right1 " ) ;
tabs1 = iup.vbox { iup.tabs { tmptable.bA1 , tmptable.bB1 , tmptable.START1 , tmptable.SELECT1 , tmptable.UP1 , tmptable.DOWN1 , tmptable.LEFT1 , tmptable.RIGHT1 } } ;
tabs1.tabtitle = " Player 1 " ;
createTextareaTab ( reftable , tmptable , " bA2 " , " A " , " isPressedA2 " , " a2 " ) ;
createTextareaTab ( reftable , tmptable , " bB2 " , " B " , " isPressedB2 " , " b2 " ) ;
createTextareaTab ( reftable , tmptable , " START2 " , " Start " , " isPressedStart2 " , " start2 " ) ;
createTextareaTab ( reftable , tmptable , " SELECT2 " , " Select " , " isPressedSelect2 " , " select2 " ) ;
createTextareaTab ( reftable , tmptable , " UP2 " , " Up " , " isPressedUp2 " , " up2 " ) ;
createTextareaTab ( reftable , tmptable , " DOWN2 " , " Down " , " isPressedDown2 " , " down2 " ) ;
createTextareaTab ( reftable , tmptable , " LEFT2 " , " Left " , " isPressedLeft2 " , " left2 " ) ;
createTextareaTab ( reftable , tmptable , " RIGHT2 " , " Right " , " isPressedRight2 " , " right2 " ) ;
tabs2 = iup.vbox { iup.tabs { tmptable.bA2 , tmptable.bB2 , tmptable.START2 , tmptable.SELECT2 , tmptable.UP2 , tmptable.DOWN2 , tmptable.LEFT2 , tmptable.RIGHT2 } } ;
tabs2.tabtitle = " Player 2 " ;
createTextareaTab ( reftable , tmptable , " bA3 " , " A " , " isPressedA3 " , " a3 " ) ;
createTextareaTab ( reftable , tmptable , " bB3 " , " B " , " isPressedB3 " , " b3 " ) ;
createTextareaTab ( reftable , tmptable , " START3 " , " Start " , " isPressedStart3 " , " start3 " ) ;
createTextareaTab ( reftable , tmptable , " SELECT3 " , " Select " , " isPressedSelect3 " , " select3 " ) ;
createTextareaTab ( reftable , tmptable , " UP3 " , " Up " , " isPressedUp3 " , " up3 " ) ;
createTextareaTab ( reftable , tmptable , " DOWN3 " , " Down " , " isPressedDown3 " , " down3 " ) ;
createTextareaTab ( reftable , tmptable , " LEFT3 " , " Left " , " isPressedLeft3 " , " left3 " ) ;
createTextareaTab ( reftable , tmptable , " RIGHT3 " , " Right " , " isPressedRight3 " , " right3 " ) ;
tabs3 = iup.vbox { iup.tabs { tmptable.bA3 , tmptable.bB3 , tmptable.START3 , tmptable.SELECT3 , tmptable.UP3 , tmptable.DOWN3 , tmptable.LEFT3 , tmptable.RIGHT3 } } ;
tabs3.tabtitle = " Player 3 " ;
createTextareaTab ( reftable , tmptable , " bA4 " , " A " , " isPressedA4 " , " a4 " ) ;
createTextareaTab ( reftable , tmptable , " bB4 " , " B " , " isPressedB4 " , " b4 " ) ;
createTextareaTab ( reftable , tmptable , " START4 " , " Start " , " isPressedStart4 " , " start4 " ) ;
createTextareaTab ( reftable , tmptable , " SELECT4 " , " Select " , " isPressedSelect4 " , " select4 " ) ;
createTextareaTab ( reftable , tmptable , " UP4 " , " Up " , " isPressedUp4 " , " up4 " ) ;
createTextareaTab ( reftable , tmptable , " DOWN4 " , " Down " , " isPressedDown4 " , " down4 " ) ;
createTextareaTab ( reftable , tmptable , " LEFT4 " , " Left " , " isPressedLeft4 " , " left4 " ) ;
createTextareaTab ( reftable , tmptable , " RIGHT4 " , " Right " , " isPressedRight4 " , " right4 " ) ;
tabs4 = iup.vbox { iup.tabs { tmptable.bA4 , tmptable.bB4 , tmptable.START4 , tmptable.SELECT4 , tmptable.UP4 , tmptable.DOWN4 , tmptable.LEFT4 , tmptable.RIGHT4 } } ;
tabs4.tabtitle = " Player 4 " ;
createTextareaTab2 ( reftable , tmptable , " ONSTART " , " onStart " , false ) ;
createTextareaTab2 ( reftable , tmptable , " ONFINISH " , " onFinish " , false ) ;
createTextareaTab2 ( reftable , tmptable , " ONSEGMENTSTART " , " onSegmentStart " , false ) ;
createTextareaTab2 ( reftable , tmptable , " ONSEGMENTEND " , " onSegmentEnd " , false ) ;
createTextareaTab2 ( reftable , tmptable , " ONATTEMPTSTART " , " onAttemptStart " , false ) ;
createTextareaTab2 ( reftable , tmptable , " ONATTEMPTEND " , " onAttemptEnd " , true ) ;
createTextareaTab2 ( reftable , tmptable , " ONINPUTSTART " , " onInputStart " , false ) ;
createTextareaTab2 ( reftable , tmptable , " ONINPUTEND " , " onInputEnd " , false ) ;
tabs5 = iup.vbox { iup.tabs { tmptable.ONSTART , tmptable.ONFINISH , tmptable.ONSEGMENTSTART , tmptable.ONSEGMENTEND , tmptable.ONATTEMPTSTART , tmptable.ONATTEMPTEND , tmptable.ONINPUTSTART , tmptable.ONINPUTEND } } ;
tabs5.tabtitle = " Events " ;
createTextareaTab ( reftable , tmptable , " SCORE " , " score " , " getScore " , " score " ) ;
createTextareaTab ( reftable , tmptable , " TIE1 " , " tie1 " , " getTie1 " , " tie1 " ) ;
createTextareaTab ( reftable , tmptable , " TIE2 " , " tie2 " , " getTie2 " , " tie2 " ) ;
createTextareaTab ( reftable , tmptable , " TIE3 " , " tie3 " , " getTie3 " , " tie3 " ) ;
createTextareaTab ( reftable , tmptable , " TIE4 " , " tie4 " , " getTie4 " , " tie4 " ) ;
tabs6 = iup.vbox { iup.tabs { tmptable.SCORE , tmptable.TIE1 , tmptable.TIE2 , tmptable.TIE3 , tmptable.TIE4 } } ;
tabs6.tabtitle = " Score " ;
createTextareaTab ( reftable , tmptable , " ISRUNEND " , " isRunEnd " , " isRunEnd " , " isRunEnd " ) ;
createTextareaTab ( reftable , tmptable , " MUSTROLLBACK " , " mustRollBack " , " mustRollBack " , " mustRollBack " ) ;
createTextareaTab ( reftable , tmptable , " ISSEGMENTEND " , " isSegmentEnd " , " isSegmentEnd " , " isSegmentEnd " ) ;
createTextareaTab ( reftable , tmptable , " ISATTEMPTEND " , " isAttemptEnd " , " isAttemptEnd " , " isAttemptEnd " ) ;
createTextareaTab ( reftable , tmptable , " ISATTEMPTOK " , " isAttemptOk " , " isAttemptOk " , " isAttemptOk " ) ;
tabs7 = iup.vbox { iup.tabs { tmptable.ISRUNEND , tmptable.MUSTROLLBACK , tmptable.ISSEGMENTEND , tmptable.ISATTEMPTEND , tmptable.ISATTEMPTOK } } ;
tabs7.tabtitle = " Selection " ;
playertabs = iup.tabs { general , tabs1 , tabs2 , tabs3 , tabs4 , tabs5 , tabs6 , tabs7 , title } ;
2008-08-01 21:58:18 +00:00
handles [ n ] = iup.dialog { playertabs , title = " BeeBee; BasicBot Frontend " , size = " 450x200 " }
2008-07-31 23:31:49 +00:00
handles [ n ] : showxy ( iup.CENTER , iup.CENTER )
-- now set the callback function for the save button. this will use all the references above.
-- these remain ok in the anonymous function by something called "closures". this means that
-- these variables, although local to the scope of the function, will remain their value in
-- the anonymous function. hence we can refer to them and fetch their contents, even though
-- you cant refer to them outside the context of the createGUI function.
tmptable.WRITE . action =
function ( self , n )
local file = iup.filedlg { allownew = " YES " , dialogtype = " SAVE " , directory = " ./lua " , showhidden = " YES " , title = " Save botfile " } ;
file : popup ( iup.ANYWHERE , iup.ANYWHERE ) ;
if ( file.value == " NULL " ) then
iup.Message ( " An error occurred trying to save your settings " ) ;
return ;
elseif ( file.status == " -1 " ) then
iup.Message ( " IupFileDlg " , " Operation canceled " ) ;
return ;
end
-- ok, file selected, if an error occurred or user canceled, the function already returned, so lets write the bot!
-- get the framework first. we need it to find the relevant tokens
local fh = assert ( io.open ( " basicbot_framework.lua " , " r " ) ) ;
local framework = fh : read ( " *a " ) ;
fh : close ( ) ;
-- now replace all tokens by gui values
-- this is where the reftable comes in very handy :p
for token , obj in pairs ( reftable ) do
local st00pid = ( reftable [ token ] . value or " " ) ;
framework = string.gsub ( framework , " -- " .. token , st00pid , 1 ) ; -- if nothing was entered, obj.value returns nil (not ""), so we have to make that translation
end ;
-- open the file, if old file, clear it
if ( file.status == " 1 " ) then
fh = assert ( io.open ( file.value , " wb " ) ) ;
else -- (file.status == "0")
fh = assert ( io.open ( file.value , " w+b " ) ) ; -- clear file contents
end ;
-- write it
fh : write ( framework ) ;
-- close it (automatically flushed)
fh : close ( ) ;
fh = nil ;
iup.Message ( " Success " , " Bot written to " .. file.value .. " ! " ) ;
end ;
tmptable.SAVE . action =
function ( self , n )
local file = iup.filedlg { allownew = " YES " , dialogtype = " SAVE " , directory = " ./lua " , showhidden = " YES " , title = " Save botfile " , extfilter = " BasicBot (*.bot)|*.bot|All files (*.*)|*.*| " } ;
file : popup ( iup.ANYWHERE , iup.ANYWHERE ) ;
if ( file.status == 1 ) then -- cancel
return ;
end ;
-- open the file, if old file, clear it
if ( file.status == " 1 " ) then
fh = assert ( io.open ( file.value , " wb " ) ) ;
else -- (file.status == "0")
fh = assert ( io.open ( file.value , " w+b " ) ) ; -- clear file contents
end ;
-- allow us to detect the botfile version (warn the user if it's different?)
fh : write ( botVersion .. " \n " ) ;
-- now replace all tokens by gui values
-- this is where the reftable comes in very handy :p
for token , obj in pairs ( reftable ) do
print ( " ------ " ) ;
print ( token .. " control -> " .. tostring ( obj ) ) ;
print ( " .value: " .. tostring ( obj.value ) ) ;
local st00pid = obj.value ;
if ( not st00pid ) then st00pid = " " ; end ;
print ( string.len ( st00pid ) ) ;
fh : write ( string.len ( st00pid ) .. " \n " ) ;
if ( string.len ( st00pid ) > 0 ) then fh : write ( st00pid ) ; end ;
fh : write ( " \n " ) ;
end ;
fh : close ( ) ;
iup.Message ( " Success " , " Settings saved! " ) ;
end ;
tmptable.LOAD . action =
function ( self , n )
-- this function currently crashes fceux without notification
-- possibly because offsets are badly calculated, but serves as an example now
local file = iup.filedlg { allownew = " NO " , dialogtype = " OPEN " , directory = " ./lua " , showhidden = " YES " , title = " Save botfile " , extfilter = " BasicBot (*.bot)|*.bot|All files (*.*)|*.*| " } ;
file : popup ( iup.ANYWHERE , iup.ANYWHERE ) ;
if ( file.status == 1 ) then -- cancel
2008-08-01 21:58:18 +00:00
iup.Message ( " Success " , " Canceled by you... " ) ;
2008-07-31 23:31:49 +00:00
return ;
end ;
local nellen = string.len ( " \n " ) ; -- platform independent
fh = assert ( io.open ( file.value , " r " ) ) ;
2008-08-01 21:58:18 +00:00
fh : read ( " *n " ) ; -- version
fh : read ( " *l " ) ; -- return
local len ;
local data ;
2008-07-31 23:31:49 +00:00
for token , crap in pairs ( reftable ) do
2008-08-01 21:58:18 +00:00
len = fh : read ( " *n " ) ; -- read line (length)
if ( not len ) then
iup.Message ( " Warning " , " End of file reached too soon! " ) ;
break ;
end ; -- no more data... (should we erase the rest?)
fh : read ( " *l " ) ; -- return
data = fh : read ( len ) ;
if ( not data ) then
iup.Message ( " Warning " , " End of file reached too soon! " ) ;
break ;
end ; -- no more data... (should we erase the rest?)
reftable [ token ] . value = data ;
fh : read ( " *l " ) ; -- return
2008-07-31 23:31:49 +00:00
end ;
2008-08-01 21:58:18 +00:00
iup.Message ( " Success " , " Settings loaded! " ) ;
2008-07-31 23:31:49 +00:00
end ;
end ;
dialogs = dialogs + 1 ;
createGUI ( dialogs ) ;
while ( true ) do
FCEU.frameadvance ( ) ;
end ;