1 2
8 9 10
13 14
Warepire
He/Him
Editor
Joined: 3/2/2010
Posts: 2178
Location: A little to the left of nowhere (Sweden)
feos wrote:
GetBlock() is insanely slow, because for every action, there's a call from C# to lua core written in C, that generates a ton of overhead compared to the same function running in Gens. For whatever reason, not wiping the offscreen data (and dropping all the bounds checks) slows me down a few fps. The reason I keep mentioning the size of the resulting table is because it does slow the emu down if the level map is too wide, even if I don't even iterate through all the entries.
You could read 4 pixels at a time to reduce it from 16 reads to 4, then manually split the data. Something like (c-like pseudo code, adapt as necessary):
Language: c

for (i = 0; i < 4; i++) pixels = rl(a1+(i*4)) final.contour[i*4] = (pixels & (0xFF << (i * 8))) >> i * 8 //repeat for i+1, +2, +3
Site Admin, Skilled player (1255)
Joined: 4/17/2010
Posts: 11495
Location: Lake Char­gogg­a­gogg­man­chaugg­a­gogg­chau­bun­a­gung­a­maugg
Looks weird, did you forget to disable html?
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.
Warepire
He/Him
Editor
Joined: 3/2/2010
Posts: 2178
Location: A little to the left of nowhere (Sweden)
I did, should be ok now.
Site Admin, Skilled player (1255)
Joined: 4/17/2010
Posts: 11495
Location: Lake Char­gogg­a­gogg­man­chaugg­a­gogg­chau­bun­a­gung­a­maugg
This didn't speed me up, since it requires using the bit library for splitting.
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.
Editor, Skilled player (1203)
Joined: 9/27/2008
Posts: 1085
Library-free splitting, if I understand modulo right:
Language: lua

local FourBytes= 0x87654321 local Byte1= FourBytes%0x100 local Byte2= (FourBytes/0x100)%0x100 Byte2= Byte2 - (Byte2%1) local Byte3= (FourBytes/0x10000)%0x100 Byte3= Byte3 - (Byte3%1) local Byte4= (FourBytes/0x1000000)%0x100 Byte4= Byte4 - (Byte4%1)
Then again, this assumes library function calls are worse than operator calls by a rather significant amount. You can probably skip the subtraction by comparing >= 1 as opposed to ~= 0. Haven't had much in the way of further ideas, so I've been pretty quiet. That, and other projects have been distracting me, and a few errands as well.
Post subject: Is this even possible
Joined: 1/16/2017
Posts: 12
So I am trying to build a LUA script for my TAS. Basically, the battle I get into is completely based on what frame I press a direction on the D-pad to land on a tile and start the battle. My traditional method is press down, wait and see what the enemies are in a battle and if I don't like the result, press down a frame later instead and repeat until I'm satisfied (sometimes takes 100 tries/frames). I have found the values in memory that show all of the 3 monster's HP so when I am looking for a favorable battle, I just wait until all 3 monster's HP show up at 14 or more, signifying that I have 3 of the right type. I'm looking through the TASEditor documentation Here and Here but, I'm not seeing anything for telling TAStudio to rewind to the frame that I grab when I start the script. Is this the case that I can't jump around my video with any lua methods or am I missing something? Edit: Zeromus saved my life with his most recent build of bizhawk. I can now run tastudio.setplayback([int][string]) to jump around in the piano roll! This is the crappy code I have written so far
local monster1HP = 0x009567
local monster2HP = 0x00959C
local monster3HP = 0x0095D1
local framesToBattle = 101
local memDomain = "WRAM"
local startFrame = emu.framecount()
local moveFrame = 0

function gameInfo()
  memory.usememorydomain(memDomain)

  monster1HP = memory.read_u8(0x009567)
  monster1HP = memory.read_u8(0x00959C)
  monster1HP = memory.read_u8(0x0095D1)

  if emu.framecount() >= (startFrame + framesToBattle + moveFrame) then
  	gui.drawText(5,25, "battle!")
  	if monster1HP >= 14 and monster2HP >= 14 and monster3HP >= 14 then
  		gui.drawText(5,15, "3 centepedes")
  	end
  	if monster1HP <= 12 or monster2HP <= 12 or monster3HP <= 12 then
  		moveFrame++
  		emu.setplayback(startFrame+moveFrame)
  	end
  end

end

while true do
  gameInfo()
  emu.frameadvance()
end
This is exactly what I am going for: http://recordit.co/kWLWoK8PY6
Editor, Player (175)
Joined: 4/7/2015
Posts: 331
Location: Porto Alegre, RS, Brazil
Is there another way to get the computer screen resolution and store its width and height in variables? The only way I could get was this:
Language: lua

local handle = io.popen("wmic desktopmonitor get screenheight, screenwidth") local result = handle:read("*a") handle:close() local resolution = {} for num in string.gmatch(result, "%d+") do resolution[#resolution + 1] = num end local screen_width, screen_height = resolution[2], resolution[1] print("width:"..screen_width..", height:"..screen_height) -- width:1366, height:768
It works for any emulator I guess (I don't know about other OS, I'm using Win7), but would be nice if BizHawk had a function that return this, something like client.oswidth()/client.osheight() or just os.width()/os.height().
Games are basically math with a visual representation of this math, that's why I make the scripts, to re-see games as math. My things: YouTube, GitHub, Pastebin, Twitter
Editor, Player (54)
Joined: 12/25/2004
Posts: 634
Location: Aguascalientes, Mexico
You can use these 2 functions: client.screenheight() client.screenwidth()
I'm the best in the Universe! Remember that!
Editor, Player (175)
Joined: 4/7/2015
Posts: 331
Location: Porto Alegre, RS, Brazil
samurai goroh wrote:
You can use these 2 functions: client.screenheight() client.screenwidth()
These return the size of the BizHawk window, not the resolution of you computer :<
Games are basically math with a visual representation of this math, that's why I make the scripts, to re-see games as math. My things: YouTube, GitHub, Pastebin, Twitter
creaothceann
He/Him
Editor
Joined: 4/7/2005
Posts: 1874
Location: Germany
brunovalads wrote:
the resolution of you computer
What if there's more than one screen and the program is partly on both? :)
CrazyTerabyte
He/Him
Joined: 5/7/2005
Posts: 74
creaothceann wrote:
brunovalads wrote:
the resolution of you computer
What if there's more than one screen and the program is partly on both? :)
What if each display has a different resolution? Or if one of the displays is portrait? (like this: ▄▄█)
Editor, Player (175)
Joined: 4/7/2015
Posts: 331
Location: Porto Alegre, RS, Brazil
CrazyTerabyte wrote:
creaothceann wrote:
What if there's more than one screen and the program is partly on both? :)
What if each display has a different resolution? Or if one of the displays is portrait? (like this: ▄▄█)
My point is: Windows stores these values in registers (and maybe somewhere else too), BizHawk could have a function to get these
Games are basically math with a visual representation of this math, that's why I make the scripts, to re-see games as math. My things: YouTube, GitHub, Pastebin, Twitter
Post subject: I've been busy
Joined: 1/16/2017
Posts: 12
Hey guys, I've been working with Zeromus to figure out how to script some things I needed in LUA. He also built some new versions of Bizhawk so that I could run these scripts. One of them I use to find the frame where an enemy misses me, one for finding crits and one for finding a battle that gives me the enemies I want.
local missSprite
local framesUntilMiss = {}
framesUntilMiss.melee = 491
framesUntilMiss.ranged = 173
local memDomain = "VRAM"
local startFrame = emu.framecount()
local moveFrameInput = 0
local frameToInput = startFrame + moveFrameInput
local foundEnemyMiss = false
local melee = true
emu.limitframerate(false)
function gameInfo()

	if(emu.framecount() == frameToInput) then
		joypad.set({["P1 A"] = true})
	else
		joypad.set({["P1 A"] = null})
	end

	memory.usememorydomain(memDomain)
	missSprite = memory.read_u16_be(0x0544)
	if(melee) then
      gui.drawText(5,5, "Enemy Miss In: " .. (startFrame + framesUntilMiss.melee - emu.framecount()))
	  if emu.framecount() >= (startFrame + framesUntilMiss.melee + moveFrameInput) then
	  	if tonumber(missSprite) == 17952 then
	  		client.togglepause()
	  	else
	  		moveFrameInput = moveFrameInput + 1
	  		frameToInput = startFrame + moveFrameInput
	  		tastudio.setplayback(startFrame)
	  	end
	  end
	else
	  gui.drawText(5,5, "Crit in: " .. (startFrame + framesUntilMiss.ranged - emu.framecount()))
	end
end

while (foundEnemyMiss ~= true) do
  gameInfo()
  emu.frameadvance()
end

local critSprite
local monster2HP
local monster3HP
local framesToCrit = {}
framesToCrit.melee = 75
framesToCrit.ranged = 173
local memDomain = "VRAM"
local startFrame = emu.framecount()
local moveFrameInput = 0
local frameToInput = startFrame + moveFrameInput
local foundCriticalHit = false
local melee = false
emu.limitframerate(false)

function gameInfo()

	if(emu.framecount() == frameToInput) then
		joypad.set({["P1 A"] = true})
	else
		joypad.set({["P1 A"] = null})
	end

	memory.usememorydomain(memDomain)
	critSprite = memory.read_u16_be(0x0544)
	
	if(melee) then
	gui.drawText(5,5, "Crit in: " .. (frameToInput + framesToCrit.melee - emu.framecount()))
  if emu.framecount() >= (startFrame + framesToCrit.melee + moveFrameInput) then
  	if tonumber(critSprite) == 13348 then
  		client.togglepause()
  	else
  		moveFrameInput = moveFrameInput + 1
  		frameToInput = startFrame + moveFrameInput
  		tastudio.setplayback(frameToInput-1)
  	end
  end
	else
	gui.drawText(5,5, "Crit in: " .. (frameToInput + framesToCrit.ranged - emu.framecount()))
  if emu.framecount() >= (startFrame + framesToCrit.ranged + moveFrameInput) then
  	if tonumber(critSprite) == 13348 then
  		foundCriticalHit = true
  		client.togglepause()
  	else
  		moveFrameInput = moveFrameInput + 1
  		frameToInput = startFrame + moveFrameInput
  		tastudio.setplayback(frameToInput-1)
  	end
  end
end
end

while (foundCriticalHit ~= true) do
  gameInfo()
  emu.frameadvance()
end

local monster1HP
local monster2HP
local monster3HP
local framesToBattle = 115
local memDomain = "WRAM"
local startFrame = emu.framecount()
local moveFrameInput = 0
local frameToInput = startFrame + moveFrameInput
local foundEncounter = false


function gameInfo()

	if(emu.framecount() == frameToInput) then
		joypad.set({["P1 Down"] = true, ["P1 R"] = true})
	else
		joypad.set({["P1 Down"] = null, ["P1 R"] = null})
	end

	memory.usememorydomain(memDomain)
	monster1HP = memory.read_u8(0x009567)
	monster2HP = memory.read_u8(0x00959C)
	monster3HP = memory.read_u8(0x0095D1)
	
  gui.drawText(5,5, "Battle in: " .. (frameToInput + framesToBattle - emu.framecount()))
  if emu.framecount() >= (startFrame + framesToBattle + moveFrameInput) then
  	if (tonumber(monster1HP)) >= 14 and (tonumber(monster2HP)) >= 14 and (tonumber(monster3HP)) >= 14 and (tonumber(monster3HP)) ~= 91 then
  		 if (emu.framecount() == startFrame + 115) then
			while emu.framecount() ~= startFrame + 385 do 
				gui.drawText(5,25, "Script removing control in: " .. startFrame + 385 - emu.framecount() .. " frames")
				emu.frameadvance()
			end
		foundEncounter = true
	end
  	else
  		moveFrameInput = moveFrameInput + 1
  		frameToInput = startFrame + moveFrameInput
  		tastudio.setplayback(frameToInput-1)
  	end
  end

end

while (foundEncounter ~= true) do
  gameInfo()
  emu.frameadvance()
  if(foundEncounter) then client.togglepause() end
end

Pokota
He/Him
Joined: 2/5/2014
Posts: 779
Language: lua

address = 0x09c75c memory_bank_offset = 0x80000000 character_offset = 0x84 files = { [0]=io.open("cloud.txt", "w+"), io.open("barret.txt", "w+"), io.open("tifa.txt", "w+"), io.open("aeri.txt", "w+"), io.open("nanaki.txt", "w+"), io.open("yuffie.txt", "w+"), io.open("reeve.txt", "w+"), io.open("vincent.txt", "w+"), io.open("cid.txt", "w+") } function setAeri() value = mainmemory.read_u8(address) if (value >= 0x100) then value = value % 256 elseif (value >=0) then timer = bit.band(value,1) if timer == 0 then mainmemory.write_u8(0x09C8D8, 0x54) mainmemory.write_u8(0x09C8D9, 0x48) console.log("Aerith") elseif timer == 1 then mainmemory.write_u8(0x09C8D8, 0x53) mainmemory.write_u8(0x09C8D9, 0xFF) console.log("Aeris") else console.log("Value is neither odd nor even") end else console.log("I was passed a negative value") end writeKillCountToFile(0) end function writeKillCountToFile(index) f = files[index] location = address + (character_offset * index) result = mainmemory.read_u8(location) f:seek("set") f:write(result) f:flush() end event.onmemorywrite(function() setAeri() end, memory_bank_offset + address, "setAeri") for i=1,8 do event.onmemorywrite(function() writeKillCountToFile(i) end, memory_bank_offset + address + (character_offset * i), "charIndex"..i) end
This appears to work as I need it to. There may be issues with the file not getting clobbered correctly when a kill count exceeds 255 (since I'm only doing a read_u8 and I'm not actually clobbering the file after initialization), but that's a simple fix. Please note that there is a flaw with this script - as it stands, it is a full battle behind. I'll do some science this coming week to see if I can fix that.
Adventures in Lua When did I get a vest?
Active player (328)
Joined: 2/23/2005
Posts: 786
I'm wondering, is there any way to actually draw on a form made with forms.newform? I've created a script that draws a minimap of the currently loaded stage, about 800 tiles total, and it's a bit clunky trying to draw it on a side buffer.
Editor, Player (175)
Joined: 4/7/2015
Posts: 331
Location: Porto Alegre, RS, Brazil
CtrlAltDestroy wrote:
I'm wondering, is there any way to actually draw on a form made with forms.newform?
I posted this there in the BizHawk Emulator Development thread, since it's a nice request that I'm interested.
Games are basically math with a visual representation of this math, that's why I make the scripts, to re-see games as math. My things: YouTube, GitHub, Pastebin, Twitter
Active player (328)
Joined: 2/23/2005
Posts: 786
Not quite sure where to post this. I'm trying to utilize DrawNew's "false" parameter to draw on top of the previous frame's drawings. My script draws many tiles per frame, and it heavily lags unless I only update the tiles that have changed. Problem is, when emu.frameadvance hits, it creates some weirdness with the emulator's double frame buffering. The surface flips to the backbuffer and new drawings are put on what used to be the backbuffer. If you run this script, you can see the flickering behavior:
Language: lua

client.SetClientExtraPadding(0,16,0,0); local pos = 0; while true do gui.DrawNew("native", false); -- player.png is a 16x16 png file gui.drawImage("player.png", pos, 8); pos = pos+16; emu.frameadvance(); end
Is this unintended behavior, or do I have to just account for this?
Pokota
He/Him
Joined: 2/5/2014
Posts: 779
From this other thread
Masterjun wrote:
When create a new canvas you need to save the LuaCanvas it's returning, like so:
Language: lua

local myCoolCanvas = gui.createcanvas(640,480)
As to things you can do with that, looking at the code for the LuaCanvas, you can use SetTitle(), Clear(), Refresh(), DrawRectangle(), and DrawText(). So like:
Language: lua

myCoolCanvas.DrawText(10,50,"sup") -- default color is white so might be hard to see myCoolCanvas.DrawRectangle(64,32,16,16)
brunovalads wrote:
Masterjun wrote:
default color is white so might be hard to see
Language: lua

-- Background colour in 0xaarrggbb format, use it first before drawing anything myCoolCanvas.Clear(0xFFE9C662) -- Setting a title for the window with any string myCoolCanvas.SetTitle("My Cool Canvas!!!") -- Refreshing to be able to draw each frame (or amount of time your loop takes). -- USE IT AFTER ALL CANVAS DRAWINGS myCoolCanvas.Refresh()
Also, you can print text with different sizes and fonts, just like gui.drawText
DrawText(int x, int y, string message, Color? color = null, int? fontsize = null, string fontfamily = null, string fontstyle = null)
Try different font sizes to see which draws your info better without taking too much space. And try fonts that come with Windows, but not all of the works so you have to test (is nothing is printed, then the font doesn't work here). Finally,
Language: lua

left_gap = 144 right_gap = 100 top_gap = 20 bottom_gap = 50 client.SetGameExtraPadding(left_gap, top_gap, right_gap, bottom_gap)
creates these gaps used in the Super Mario World script. You can fit a lot of info there, specially for seeing sprites before they enter the screen or a map of the level, for example.
Adventures in Lua When did I get a vest?
Experienced player (658)
Joined: 5/16/2009
Posts: 235
So I was trying to build a lua script for Bizhawk that displays data on the screen. I notice that if I try to display a lot of data (calling draw functions many times) and I make a loop with emu.frameadvance(), emulator slows down a lot since it tries to refresh the whole thing every frame. Is there any way to solve that issue? When I do lua scripts on Desmume I usually call gui.register() to draw stuff, and emu.registerafter() for memory reading / writing and calculations. Is there any similar solution in Bizhawk?
Editor, Player (175)
Joined: 4/7/2015
Posts: 331
Location: Porto Alegre, RS, Brazil
mkdasher wrote:
So I was trying to build a lua script for Bizhawk that displays data on the screen.
Can you show a screenshot to understand better the situation?
mkdasher wrote:
I notice that if I try to display a lot of data [...] emulator slows down a lot
Do all this data need to be printed everytime? I mean, for example if it's a player info, usually you wanna see it inside the level, not on the overworld/menu/etc, so you can make a conditional for this info based on the value of the game mode address.
Games are basically math with a visual representation of this math, that's why I make the scripts, to re-see games as math. My things: YouTube, GitHub, Pastebin, Twitter
dan
He/Him
Joined: 9/23/2016
Posts: 25
mkdasher wrote:
When I do lua scripts on Desmume I usually call gui.register() to draw stuff, and emu.registerafter() for memory reading / writing and calculations. Is there any similar solution in Bizhawk?
See http://tasvideos.org/BizHawk/LuaFunctions.html, specifically the event tab. The following works, although you may need a command to unregister the function if you reload the script. I've noticed a weird quirk where you have multiple definitions of the same function and the workaround is to manually clear the registered functions.
local function mkdasher()
  .
  :
end

-- define an anonymous wrapper function that calls the named function
event.onframestart(function() mkdasher() end, "mkdasher")
Amaraticando
It/Its
Editor, Player (162)
Joined: 1/10/2012
Posts: 673
Location: Brazil
dan wrote:
The following works, although you may need a command to unregister the function if you reload the script. I've noticed a weird quirk where you have multiple definitions of the same function and the workaround is to manually clear the registered functions.
local function mkdasher()
  .
  :
end

-- define an anonymous wrapper function that calls the named function
event.onframestart(function() mkdasher() end, "mkdasher")
I don't see how any of the events would help, since there's no gui/on_paint event. Using onframestart or onframeend will call the registered function as often as putting it inside a while loop. Just test incrementing a counter and displaying it on screen: both will update the counter just like the frame counter is incremented, even if the emulator is running at 240 fps (and obviously, not every video frame is being displayed on the monitor).
dan
He/Him
Joined: 9/23/2016
Posts: 25
Amaraticando wrote:
I don't see how any of the events would help, since there's no gui/on_paint event. Using onframestart or onframeend will call the registered function as often as putting it inside a while loop.
There are other events like event.onmemoryread and event.onmemorywrite. I specifically gave a partially correct answer to encourage going to the link and reading about the other events. I have not used them, but I'm guessing they would be analagous to what mkdasher mentioned above
mkdasher wrote:
emu.registerafter() for memory reading / writing and calculations.
Pokota
He/Him
Joined: 2/5/2014
Posts: 779
Just remember that the event memory hooks use the full bus address, not just the partial domain address. using 0x151ac will give different results than from using 0x200151ac
Adventures in Lua When did I get a vest?
Experienced player (658)
Joined: 5/16/2009
Posts: 235
brunovalads wrote:
Can you show a screenshot to understand better the situation?
This would be an example:
brunovalads wrote:
Do all this data need to be printed everytime?
No, in fact I have stuff like that button at the top right to hide the right part of the screen (also that'd also stop making all the pokemon calculation and drawing), but for example the square grid at the bottom left slows down the emulator itself.
dan wrote:
See http://tasvideos.org/BizHawk/LuaFunctions.html, specifically the event tab. The following works, although you may need a command to unregister the function if you reload the script. I've noticed a weird quirk where you have multiple definitions of the same function and the workaround is to manually clear the registered functions.
local function mkdasher()
  .
  :
end

-- define an anonymous wrapper function that calls the named function
event.onframestart(function() mkdasher() end, "mkdasher")
I don't see how that helps either. That still causes the emulator to lag, since it waits for the function to be drawn completely. Also I have another question, everytime frameadvance is called the whole lua is cleared. Is there a way to refresh the lua manually and only update it when I want to? That'd probably be a workaround if possible.
1 2
8 9 10
13 14