1 2 3 4 5 6
13 14
Pokota
He/Him
Joined: 2/5/2014
Posts: 779
Question: What's the "best" way to store a collection of strings within the script architecture (NOT asking "What data struct I should use" since that's forced to be "Array of Strings")? Should I split that off into a separate file specifically for definitions like that or dedicate a section of the script for said Array of Strings?
Adventures in Lua When did I get a vest?
Amaraticando
It/Its
Editor, Player (159)
Joined: 1/10/2012
Posts: 673
Location: Brazil
Pokota wrote:
Question: What's the "best" way to store a collection of strings within the script architecture (NOT asking "What data struct I should use" since that's forced to be "Array of Strings")? Should I split that off into a separate file specifically for definitions like that or dedicate a section of the script for said Array of Strings?
It depends on what you are trying to do. Explain more... Are the strings already done before the script starts, or they are generated after, by the user or the game? Is this collection way too big? ------------------------ My question: How do I make a checkbox, in a form, be selected at the start, before the player clicks on it? I was playing with forms.getproperty and setproperty, but I can't do anything with this without errors.
Pokota
He/Him
Joined: 2/5/2014
Posts: 779
Amaraticando wrote:
It depends on what you are trying to do. Explain more... Are the strings already done before the script starts, or they are generated after, by the user or the game? Is this collection way too big?
It's a collection of 65 names (the 64 rings in the Oracles games, plus a "No Ring" entry in position 0). I'm leaning towards it being in a separate file because of the dual nature of the games and the fact that Ring ID values shouldn't change between the two.
Adventures in Lua When did I get a vest?
Site Admin, Skilled player (1254)
Joined: 4/17/2010
Posts: 11475
Location: Lake Char­gogg­a­gogg­man­chaugg­a­gogg­chau­bun­a­gung­a­maugg
Yes, I did that in 2 files. Keeping string arrays in the main script makes it look dirty, especially when arrays are huge and aren't changed during runtime, especially when you have plenty of them. Only keep in mind that lua indexes start with 1.
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.
Pokota
He/Him
Joined: 2/5/2014
Posts: 779
Lua Indexes start with 1 if you don't manually define 0 as an index. Since I have to manually assemble the string array anyway, I may as well define 0 as an index.
Adventures in Lua When did I get a vest?
Amaraticando
It/Its
Editor, Player (159)
Joined: 1/10/2012
Posts: 673
Location: Brazil
Sorry for asking this again, but I'm having a bad time with those functions: string forms.getproperty(int handle, string property) void forms.setproperty(int handle, string property, object value) Whenever I try to get a property, the console writes: Object reference not set to an instance of an object. And when I try to set the property of a checkbox (or buttons etc), during the creation of a form, it doesn't work, giving: LuaInterface.LuaScriptException: <value> Therefore, I don't know what property is and what the value object is. Can someone illuminate me here? I just wanted to checkbox the default options, without the player having to press each one everytime they initiate a script.
adelikat
He/Him
Emulator Coder, Site Developer, Site Owner, Expert player (3573)
Joined: 11/3/2004
Posts: 4754
Location: Tennessee
Amaraticando wrote:
Sorry for asking this again, but I'm having a bad time with those functions: string forms.getproperty(int handle, string property) void forms.setproperty(int handle, string property, object value) Whenever I try to get a property, the console writes: Object reference not set to an instance of an object. And when I try to set the property of a checkbox (or buttons etc), during the creation of a form, it doesn't work, giving: LuaInterface.LuaScriptException: <value> Therefore, I don't know what property is and what the value object is. Can someone illuminate me here? I just wanted to checkbox the default options, without the player having to press each one everytime they initiate a script.
These are experiemental "use at your own risk" methods. They use reflection to look for properties in the object with the name you give. Set can only set things that can be translated from lua to C# succesfully (which is very limited). For a reference of what you can or can't get as a property, refer to .Net winforms documentation
It's hard to look this good. My TAS projects
Amaraticando
It/Its
Editor, Player (159)
Joined: 1/10/2012
Posts: 673
Location: Brazil
Thanks, adelikat. My task can be easily done with the following code, that I'll post just in case someone has the same difficulty:
Language: lua

-- Get the status of a checkbox -- the same task of forms.ischecked local status = forms.getproperty(checkbox_handle, "CheckState") print(status) --> "Checked" or "Unchecked" -- Set the status of a checkbox forms.setproperty(checkbox_handle, "Checked", true) -- or false to force an uncheck
Amaraticando
It/Its
Editor, Player (159)
Joined: 1/10/2012
Posts: 673
Location: Brazil
I've been writing a family of scripts for Super Mario World (and many romhacks), that runs on BizHawk, lsnes and Snes9x. The user can (almost) always select the exact set of things (s)he wants to see. The configurations are saved in an ini file. The priority is to show useful information for TAS'ing, hitboxes, interaction points and a warning for lag and cheat usage. It looks better with maximized window or full screen. More screenshots: http://imgur.com/a/mxwsI Download: https://github.com/rodamaral/smw-tas/wiki/Downloads Commentaries, criticism (specially bug reports, lsnes has 4K lines!) are welcome. Tips on how to use git and distribute this correctly too.
Editor, Expert player (2073)
Joined: 6/15/2005
Posts: 3282
Your script interface looks nice. I was just exploring using the forms functions to make a better Mega Man X script; your layout gives me a good idea of what to expect.
Editor, Emulator Coder
Joined: 8/7/2008
Posts: 1156
have you tried client.SetGameExtraPadding to give yourself a reliable margin for drawing OSD stuff? It'll get scaled with the game. I might could make something similar which doesnt get scaled. I cant promise it works well in whatever build youre using, I'm continually fiddling with it
Amaraticando
It/Its
Editor, Player (159)
Joined: 1/10/2012
Posts: 673
Location: Brazil
I wasn't aware client.SetGameExtraPadding was added. When the new release comes out, I'll gladly add this, as I've done for lsnes.
Pokota
He/Him
Joined: 2/5/2014
Posts: 779
So as I'm not going to be using Basic Bot for the intended purpose (and don't want to clog my bizhawk with Loadstate Failed messages), I figured I may as well write up an RNG-powered Lua script myself. EDIT: What I'm aiming to do is make it platform-independent - say, if I want to run it on an NES game for one run, then a Genesis game the next, then a 2600 game after that. So far I have
Language: lua

checkTime = 0 buttonsPressed = 0 function core() checkTime = emu.framecount() % 10 if (checkTime == 0) then sendRandomButtonPress() end end function sendRandomButtonPress() keysArray = joypad.get(1) i = math.random(table.getn(keysArray)) keysArray[i] = "True" joypad.set(keysArray,1) buttonsPressed = buttonsPressed + 1 end event.onframestart(core)
which fails because table.getn(keysArray) returns 0. Is there another way to check table length that I've overlooked, or is the table library not implemented or am I just being a dumb?
Adventures in Lua When did I get a vest?
Player (75)
Joined: 8/26/2015
Posts: 70
Pokota wrote:
which fails because table.getn(keysArray) returns 0. Is there another way to check table length that I've overlooked, or is the table library not implemented or am I just being a dumb?
`getn` returns the length of the listy part of the table: the number of positive integer indices. As far as I know, there's no built-in way to count the number of keys total. Instead, as you seem to be only using one button press each frame, you could use something like reservoir sampling? That would look something like this (untested):
Language: lua

local keys = joypad.get(1); local count = 1; chosen = nil; for k,v in pairs(keys) do if math.random(count) == 1 then chosen = k; end count = count + 1; end keys[chosen] = true;
Pokota
He/Him
Joined: 2/5/2014
Posts: 779
I'll give that method a try; I'm also gonna try generating a table of button names from joypad.get() and using that as both my length reference as well as my key reference variable (at this point I think I'm just spouting off random words and hoping they make sense) since there's no way to call buttons by integer reference at this time. In short, relearning regex in the context of lua. E: Yeah a reference table might work better since then I can blacklist certain buttons if I find a need to.
Adventures in Lua When did I get a vest?
Skilled player (1741)
Joined: 9/17/2009
Posts: 4981
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
Hey, I'm sorry for this newb question, but how do you change the font color? I tried using:
gui.text(0, 10, 'Flash obtained','RED')
and instead of the white text changing to red, it's the black shadow that changes to red: Edit: Nevermind, I'm stupid. That was the arguement for background. >.<
Post subject: generic PRNG lua script
Pokota
He/Him
Joined: 2/5/2014
Posts: 779
Language: lua

checkTime = 0 buttonsPressed = 0 keyNames = {} keysArray = {} function core() checkTime = emu.framecount() % 10 if (checkTime == 0) then sendRandomButtonPress() end end function sendRandomButtonPress() keysArray = joypad.get() i = math.random(table.getn(keyNames)) push = keyNames[i] if push ~= "Power" and push ~= "P1 Power" and push ~= "P2 Power" then keysArray[push] = "True" end joypad.set(keysArray) buttonsPressed = buttonsPressed + 1 end function getButtonNames() k = 1 for i,v in pairs(joypad.get())do keyNames[k] = i k = k+1 end end getButtonNames() event.onframestart(core)
So now this is working more-or-less as intended; Power gets blacklisted outright, and I haven't tried it with multidisk mode yet so there may be more blacklisting. Right now, I'm curious to find out why it sends multiple button presses - it's currently sending two buttons with each update. My current guess is because the key state table (keysArray) isn't actually getting reset between calls. Still, it is at least able to get massacred by Gary in 1P pokemon, so it's more progress than I had this time last week.
Adventures in Lua When did I get a vest?
Post subject: Re: generic PRNG lua script
Amaraticando
It/Its
Editor, Player (159)
Joined: 1/10/2012
Posts: 673
Location: Brazil
Pokota wrote:
Right now, I'm curious to find out why it sends multiple button presses - it's currently sending two buttons with each update. My current guess is because the key state table (keysArray) isn't actually getting reset between calls.
The code makes it clear that only one key is set per update. I've tested it too. My guess is that you registered core twice. Check the registered functions (F12 in the Lua Console) and see. The best way to avoid it is:
Language: lua

event.unregisterbyname("some-fancy-name-onframestart") -- deletes the first registered function with this name event.onframestart(core, "some-fancy-name-onframestart") -- registers core, on frame start, with this name
Pokota
He/Him
Joined: 2/5/2014
Posts: 779
I fixed it by using a while true do loop to call core() instead of tying it to the frame start. So now the next step in this project is to make a form that can be used to pass both update frequency and an on-the-fly blacklist for buttons (say for if you want to shut off keys for P1 but not P2). Yes I realize I'm just recreating most of what's in Basic Bot, but I find basic bot too... focused on getting results for my purposes of pure entertainment through PRNG.
Language: lua

ccheckTime = 0 buttonsPressed = 0 keyNames = {} keysArray = {} blacklist = {"Power", "Reset"} frequency = 10 function core() checkTime = emu.framecount() % frequency if (checkTime == 0) then sendRandomButtonPress() elseif (checkTime < frequency/2) then joypad.set(keysArray) end end function sendRandomButtonPress() keysArray = joypad.get() i = math.random(table.getn(keyNames)) push = keyNames[i] if checkBlacklist(push) == true then keysArray[push] = "True" end joypad.set(keysArray) buttonsPressed = buttonsPressed + 1 end function getButtonNames() k = 1 for key,v in pairs(joypad.get()) do keyNames[k] = key k = k+1 end end function checkBlacklist(key) result = true for i, v in pairs(blacklist) do if key == blacklist[i] then result = false end end return result end function initializeKeyArray() keysArray = joypad.get() end getButtonNames() initializeKeyArray() while true do core() emu.frameadvance() end
Adventures in Lua When did I get a vest?
Pokota
He/Him
Joined: 2/5/2014
Posts: 779
So I've run into an interesting issue. joypad.get(1) returns nil if there's only one controller in the core. Easy way to see this is to call it in the Gambatte core. Why is this interesting?
Language: lua

-- PRNG BOT version 2 (Greentext) -- Changes (2015-11-07): -- P1 and P2 are now independently checked rather than just using the global joypad.get() results. -- This makes blacklisting less of a chore as joypad.whatever, when passed a player number, -- actually omits the player number prefix. -- The upshot is that perfecting the Blacklist is a little easier. -- -- Also, I removed the Buttons Pressed counter. It was not seeing any practical use. -- It was originally a debug variable, but I ended up debugging using the console anyway. -- -- Also also, documentation. This is the Greentext Update. -- Global variable declarations -- checkTime = 0 -- Placeholder variable. I don't like passing expressions as arguments. keyNamesP1 = {} -- Player 1 Key Names array. keysArrayP1 = {} -- Player 1 Key State array. keyNamesP2 = {} -- Player 2 Key Names array. keysArrayP2 = {} -- Player 2 Key State array. blacklist = {"Power", "Reset"} -- Blacklisted keys array. frequency = 10 -- How often should the bot be updating? Also influences how long the keys are held. mplay = false -- Is there a P2 controller? If not, we don't want to bother mucking around with P2 stuff. function core() -- I like to have a manager functon that only handles calling other functions. checkTime = emu.framecount() % frequency -- Set the frame count checker to the frame count modulo the frequency. if (checkTime == 0) then -- If we're on the right frame, then go ahead and generate a new random key. sendRandomButtonPressP1() -- For the technically-minded, it's actually pseudorandom, but let's not be overly pedantic. if mplay == true then -- If Player 2's controller exists, generate keypresses for it as well. sendRandomButtonPressP2() end elseif (checkTime < frequency/2) then -- Now, not all games accept single-frame inputs. joypad.set(keysArrayP1,1) -- One of the games I tested with had this problem. if mplay == true then -- So, for half of the frames between new keypress generations, send the same presses. joypad.set(keysArrayP2,2) -- This allows 'slower' games to keep up, though it also slows down high-precision games. end -- But do you really want to use a PRNG with a high-precision game? end end function getButtonNames() -- Scraping joypad.get(x) for key names. k = 1 j = 1 for key,v in pairs(joypad.get(1)) do -- For each pair in joypad.get(x) keyNamesP1[k] = key -- keyNamesPX in position k is assigned key... uhh, key. k = k+1 -- and k is incremented. end if joypad.get(2) ~= nil then -- As long as there's a table for P2, do the same for P2. for key,v in pairs(joypad.get(2)) do keyNamesP2[j] = key -- This can be rewritten to use one less for loop. j = j+1 -- However, I like it written this way since it's one less if-then than would be needed. mplay = true -- Since boolean mplay starts false, we set it true. end -- This allows me to tell other functions to skip the P2 stuff if there's no P2. end end function sendRandomButtonPressP1() -- So let's look at sending random keys to player 1's controller. keysArrayP1 = joypad.get(1) -- First, fetch the current keys array. i = math.random(table.getn(keyNamesP1)) -- Generate a random number between 1 and the length of the Player 1 Key Names Array. push = keyNamesP1[i] -- Grab the key referenced by the generated random number. if checkBlacklist(push) == true then -- If the key is not blacklisted, keysArrayP1[push] = "True" -- then set the key to be pressed. end joypad.set(keysArrayP1,1) -- Finally, send the keys array to BizHawk for pressing. end function sendRandomButtonPressP2() -- This is a doublicate of the P1 variant. keysArrayP2 = joypad.get(2) -- Technically, these can be rewritten as a single function with clever argument use. i = math.random(table.getn(keyNamesP2)) -- It's on the docket for the next revision. push = keyNamesP2[i] if checkBlacklist(push) == true then keysArrayP2[push] = "True" end joypad.set(keysArrayP2,2) end function checkBlacklist(key) -- Long story short, certain keys (such as the power button) shouldn't be pressed. result = true for i, v in pairs(blacklist) do if key == blacklist[i] then -- Check to make sure the button is not on the blacklist. result = false -- If it's on the blacklist, DO NOT PRESS THE BUTTON. end end return result end function initializeKeyArray() -- Just taking the key names and putting them into our placeholder arrays. keysArrayP1 = joypad.get(1) -- We don't actually need to do this now, as they're reinitialized every time keys are sent. if mplay == true then -- It's just best practice to initialize variables before using them. keysArrayP2 = joypad.get(2) -- With that said, I do call the arrays before they get defined otherwise. end -- I can't remember if I actually tested without this function. end ---------------------------------------- AND NOW, FOR THE ACTUAL FUNCTION CALLS getButtonNames() initializeKeyArray() while true do core() emu.frameadvance() end
Adventures in Lua When did I get a vest?
Editor, Expert player (2073)
Joined: 6/15/2005
Posts: 3282
Pokota wrote:
So I've run into an interesting issue. joypad.get(1) returns nil if there's only one controller in the core. Easy way to see this is to call it in the Gambatte core. Why is this interesting?
Config -> Controllers... If it doesn't explicitly say "Player 1", "Player 2", etc., then you cannot get controller state by joypad.get(1) or such. In that case, you need to use joypad.get(). joypad.get() can also get Player 1 (or 2 or ...) input, but the key name is prefixed with "P1 " (or "P2 " or ...). In any case, key names are as shown in Config -> Controllers... .
Pokota
He/Him
Joined: 2/5/2014
Posts: 779
Hmm... I'll have to kludge it for the next revision then. Main reason it's important is because this way I don't have to hard code as much stuff. End goal is to make this script work across as many cores as possible.
Adventures in Lua When did I get a vest?
Pokota
He/Him
Joined: 2/5/2014
Posts: 779
Just a quick update. The PRNG Button press function itself has been genericized and now takes player as an int argument. It shouldn't be passed anything else as an argument simply because it passes player to joypad.get() and joypad.set(). Unfortunately, I haven't put in the return nil case kludge yet, this was just a patch job to get the refactoring started. In other words, it will fail for any core in which joypad.get(1) returns nil. In other other words, it will fail if the core only expects one controller (such as GBx)
Language: lua

-- PRNG BOT version 3 (Refraction) -- Just rewriting some variables and sendRandomButtonPress to allow for on-the-fly expansion up to five(!?) players in future updates. -- TODO: return-nil kludge, learn and implement forms. Prepare other functions for on-the-fly expansion. -- Global variable declarations -- checkTime = 0 -- Placeholder variable. I don't like passing expressions as arguments. keyNames = {[1] = {},[2] = {}} -- Key Names array. keysArray = {[1] = {},[2] = {}} -- Key State array. blacklist = {"Power", "Reset"} -- Blacklisted keys array. frequency = 10 -- How often should the bot be updating? Also influences how long the keys are held. mplay = false -- Is there a P2 controller? If not, we don't want to bother mucking around with P2 stuff. function core() -- I like to have a manager functon that only handles calling other functions. checkTime = emu.framecount() % frequency -- Set the frame count checker to the frame count modulo the frequency. if (checkTime == 0) then -- If we're on the right frame, then go ahead and generate a new random key. sendRandomButtonPress(1) -- For the technically-minded, it's actually pseudorandom, but let's not be overly pedantic. if mplay == true then -- If Player 2's controller exists, generate keypresses for it as well. sendRandomButtonPress(2) end elseif (checkTime < frequency/2) then -- Now, not all games accept single-frame inputs. joypad.set(keysArray[1],1) -- One of the games I tested with had this problem. if mplay == true then -- So, for half of the frames between new keypress generations, send the same presses. joypad.set(keysArray[2],2) -- This allows 'slower' games to keep up, though it also slows down high-precision games. end -- But do you really want to use a PRNG with a high-precision game? end end function getButtonNames() -- Scraping joypad.get(x) for key names. k = 1 j = 1 for key,v in pairs(joypad.get(1)) do -- For each pair in joypad.get(x) keyNames[1][k] = key -- keyNamesPX in position k is assigned key... uhh, key. k = k+1 -- and k is incremented. end if joypad.get(2) ~= nil then -- As long as there's a table for P2, do the same for P2. for key,v in pairs(joypad.get(2)) do keyNames[2][j] = key -- This can be rewritten to use one less for loop. j = j+1 -- However, I like it written this way since it's one less if-then than would be needed. mplay = true -- Since boolean mplay starts false, we set it true. end -- This allows me to tell other functions to skip the P2 stuff if there's no P2. end end function sendRandomButtonPress(player) -- So let's look at sending random keys to player 1's controller. keysArray[player] = joypad.get(player) -- First, fetch the current keys array. i = math.random(table.getn(keyNames[player])) -- Generate a random number between 1 and the length of the Player 1 Key Names Array. push = keyNames[player][i] -- Grab the key referenced by the generated random number. if checkBlacklist(push) == true then -- If the key is not blacklisted, keysArray[player][push] = "True" -- then set the key to be pressed. else sendRandomButtonPress(player) end joypad.set(keysArray[player],player) -- Finally, send the keys array to BizHawk for pressing. end function checkBlacklist(key) -- Long story short, certain keys (such as the power button) shouldn't be pressed. result = true for i, v in pairs(blacklist) do if key == blacklist[i] then -- Check to make sure the button is not on the blacklist. result = false -- If it's on the blacklist, DO NOT PRESS THE BUTTON. end end return result end function initializeKeyArray() -- Just taking the key names and putting them into our placeholder arrays. keysArray[1] = joypad.get(1) -- We don't actually need to do this now, as they're reinitialized every time keys are sent. if mplay == true then -- It's just best practice to initialize variables before using them. keysArray[2] = joypad.get(2) -- With that said, I do call the arrays before they get defined otherwise. end -- I can't remember if I actually tested without this function. end ---------------------------------------- AND NOW, FOR THE ACTUAL FUNCTION CALLS getButtonNames() initializeKeyArray() while true do core() emu.frameadvance() end
E: So, the upshot is now I'm going to be requesting a new joypad function: joypad.countPlayers(). Should just return the number of controllers the core will accept inputs for at that time - example, if in N64 mode you have it set up for three controllers, joypad.countPlayers() should return 3.
Adventures in Lua When did I get a vest?
Amaraticando
It/Its
Editor, Player (159)
Joined: 1/10/2012
Posts: 673
Location: Brazil
Pokota wrote:
E: So, the upshot is now I'm going to be requesting a new joypad function: joypad.countPlayers(). Should just return the number of controllers the core will accept inputs for at that time - example, if in N64 mode you have it set up for three controllers, joypad.countPlayers() should return 3.
You can do it yourself:
Language: lua

function joypad.countPlayers() -- returns the highest argument joypad.get(arg) can have / or nil if arg == 1 returns nil or an empty table local next = next local MAX = 100 for count = 1, MAX do local joyget = joypad.get(count) if (joyget == nil or next(joyget) == nil) then if count == 1 then return nil else return count - 1 end end count = count + 1 if count == MAX then error"joypad.countPlayers: something went wrong" end -- sanity check end end
EDIT: it doesn't count possible unconnected controllers.
Pokota
He/Him
Joined: 2/5/2014
Posts: 779
...I'm tempted to put together a Dual Gambatte xml file with 101 games just to see it fail because of a hard-coded max of 100. It'd wreck my computer trying to get to that point, but either way it'd still be fun to try. Lifted your code directly, credited you in code comments.
Language: lua

-- PRNG BOT version 4 (Amaraticando) -- Special thanks to Amaraticando, for the countPlayers() function. -- -- No longer fails for single-controller games, AND works for up to 100 controllers! -- Well, it's only been tested up to four controllers BUT IT WORKS! And it should work for up to 100 controllers. -- TODO: Learn and implement forms. -- Global variable declarations -- checkTime = 0 -- Placeholder variable. I don't like passing expressions as arguments. keyNames = {} -- Key Names array. keysArray = {} -- Key State array. blacklist = {"Power", "Reset"} -- Blacklisted keys array. frequency = 10 -- How often should the bot be updating? Also influences how long the keys are held. mplay = false -- Is there a more than one controller? If not, there's special handling that needs to happen. function core(mode) cap = countPlayers() -- I like to have a manager functon that only handles calling other functions. checkTime = emu.framecount() % frequency -- Set the frame count checker to the frame count modulo the frequency. if (checkTime == 0) then -- For the technically-minded, it's actually pseudorandom, but let's not be overly pedantic. if mode == true then -- If we have multiple controllers connected, for i=1,cap do -- then for each connected controller sendRandomButtonPress(i) -- send a random button press. end else -- if there is only one connected controller sendRandomButtonPress() -- send a random button press. end elseif (checkTime < frequency/2) then if mode == true then -- Now, not all games accept single-frame inputs. for j=1,cap do joypad.set(keysArray[j],j) -- So, for half of the frames between new keypress generations, send the same presses end -- But do you really want to use a PRNG with a high-precision game? else joypad.set(keysArray) end end end function countPlayers() -- returns the highest argument joypad.get(arg) can have / or nil if arg == 1 returns nil or an empty table local next = next -- Written by Amaraticando local MAX = 100 for count = 1, MAX do local joyget = joypad.get(count) if (joyget == nil or next(joyget) == nil) then if count == 1 then return nil else return count - 1 end end count = count + 1 if count == MAX then error"joypad.countPlayers: something went wrong" end -- sanity check end end function getButtonNames(mode) -- Scraping joypad.get(x) for key names. if mode == true then -- If there are multiple controllers connected, for i=1, countPlayers() do -- then for each controller keyNames[i] = {} -- make a new array in the key names array, k = 1 for key in pairs(joypad.get(i)) do -- and for each button on the controller keyNames[i][k] = key -- add it to the key names array. k = k + 1 -- DYNAMICALLY NESTED ARRAYS! end end else k = 1 for key in pairs(joypad.get()) do -- If there's only one controller connected, keyNames[k] = key -- then for each button on the controller k = k + 1 -- and add it to the key names array. end end end function sendRandomButtonPress(player) if player ~= nil then -- So let's look at sending random keys to the controller. keysArray[player] = joypad.get(player) -- First, fetch the current keys array. i = math.random(table.getn(keyNames[player])) -- Generate a random number between 1 and the length of the Key Names Array. push = keyNames[player][i] -- Grab the key referenced by the generated random number. if checkBlacklist(push) == true then -- If the key is not blacklisted, keysArray[player][push] = "True" -- then set the key to be pressed. else -- If the key is blacklisted, sendRandomButtonPress(player) -- then just pick a new key. end joypad.set(keysArray[player],player) -- Finally, send the keys array to BizHawk for pressing. else keysArray = joypad.get() -- Since joypad.get(1) returns nil if the core can only have one controller, i = math.random(table.getn(keyNames)) -- special handling has to be done for such cases. push = keyNames[i] if checkBlacklist(push) == true then keysArray[push] = "True" else sendRandomButtonPress() end joypad.set(keysArray) end end function checkBlacklist(key) -- Long story short, certain keys (such as the power button) shouldn't be pressed. result = true for i, v in pairs(blacklist) do if key == blacklist[i] then -- Check to make sure the button is not on the blacklist. result = false -- If it's on the blacklist, DO NOT PRESS THE BUTTON. end end return result end function initializeKeyArray() -- Just taking the key names and putting them into our placeholder arrays. cap = countPlayers() if cap == nil then mplay = false -- This is also when we determine if the core only expects a single controller. keysArray = joypad.get() else mplay = true for i=1, cap do keysArray[i] = joypad.get(i) end end end ---------------------------------------- AND NOW, FOR THE ACTUAL FUNCTION CALLS initializeKeyArray() getButtonNames(mplay) while true do core(mplay) emu.frameadvance() end
Adventures in Lua When did I get a vest?
1 2 3 4 5 6
13 14