Posts for FatRatKnight

Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
Ilari wrote:
FatRatKnight wrote:
I made that determination with this script:
Ah, I think it is the ordering: on_paint() will occur first, then on_frame().
It still feels odd, however, that I can run the script, hit the frame advance key once, and never see on_frame() called... According to on_paint, anyway. Though, what you say make sense. on_paint would be called, but the script isn't running yet. Then on_frame would be called, but again, no script. Emulation halts here, awaiting further user requests. Then I start the script, and advance one frame. on_paint is called, notably without any indication from the on_frame routine, and acts accordingly. Then on_frame gets called. Then lsnes awaits my next frame advance request. Since on_frame was called, it put an indication of itself in my table. on_paint hasn't been called since then to empty it -- The next time it is called, on the second frame advance, I finally see the indication. How difficult would it be to reorder this? From what I gather, it is apparently: [frame boundary], [emulation], on_paint, on_frame, [frame boundary]. How difficult would it be to turn it into: [frame boundary], on_frame, [emulation], on_paint, [frame boundary], or [frame boundary], [emulation], on_frame, on_paint, [frame boundary]? As it stands, any information that on_frame would provide is not yet seen by on_paint when the emulation halts. Only when on_paint is called again would the information be read; This is possible through gui.repaint() from lua (two gui updates per frame with this function in on_frame is not a good workaround), or advancing one more frame, and have the information it would provide be out of date by one frame. So, I feel where on_frame is located in the ordering is a bit inconvenient. I've stated my reasons why I find it inconvenient. I can understand if there are difficulties in reordering this, as while it looks like a simple switching of the order things happen in, I'd be surprised if the only thing to do was to move a function call up one line. In any case, I believe on_paint (and other display updates) generally should be the very last thing called when lsnes finishes processing whatever and goes back to being idle.
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
Had a short discussion over IRC. I want to post to give some closure here.
Ilari wrote:
Even my laptop (much less powerful than my desktop, and even that is oldish) can run it at about 90fps if no exotic chips are involved.
Running the latest lsnes still isn't much faster. This computer is single-core. As for the specs, I'm somehow unsure about how to go about finding that out.
Ilari wrote:
on_idle? That shouldn't be hard (although one has to be careful not to waste CPU by spending too much time polling if such hook exists!)
Not really for the idea of an on_idle callback. But I'm not against it either. There are enough methods already available I can use without demanding processing power when there is no activity. Still, this would mean that I have access to creation of repeating commands when there is no further activity with input devices. Even on this text here, I can simply hold down some letter and watch as it repeats typing it. Like holding a: aaaaaaa - There are no changes to the keyboard after the initial press, but it still repeats adding the letter several times. At the moment, though, I don't foresee a major need for repetition or animation with emulation paused or no user input changes. Regardless, I suggest allowing the user to tweak the sleep value if this would be implemented. I also like how wxwidgets takes virtually no processing power while idling, and I hope the existence of such an option doesn't change that fact, during such cases where there is no lua running or the script doesn't contain on_idle.
Ilari wrote:
FatRatKnight wrote:
The on_frame() callback is not called [...]
Hmm... Sounds bit odd. I take a look at this.
I made that determination with this script: Download CallbackCheck.lua
Language: lua

local SinceThenTable= {} local Snoops= 0 function on_paint() if #SinceThenTable < 1 then gui.text(1,1,"Nothing!",0xFFFFFF,0) return end local Y= 1 for i= #SinceThenTable, 1, -1 do gui.text(1,Y,SinceThenTable[i],0x7FFF7F,0) Y=Y+20 SinceThenTable[i]= nil end gui.text(180,1,Snoops,0xFFFFFF,0) Snoops= 0 end gui.repaint() function on_input(b) local str= "on_input, false" if b then str= "on_input, true" end table.insert(SinceThenTable, str) end function on_frame() table.insert(SinceThenTable, "on_frame") end function on_snoop() Snoops= Snoops+1 end
Ilari wrote:
Eh, the way on_startup() currently works, it isn't useful. [...]
One thought that crosses my mind might be when you reset back to frame zero. on_reset does not get called when the user decides to go back to the beginning of the movie.
Ilari wrote:
FatRatKnight wrote:
A request: A callback for when a request to advance a frame takes place. [...]
The frame advance button has autorepeat, so one hold may advance more than one frame...
My main thought was that sometime after the screen gets painted, if emulation is about to continue for any reason, then check for the appropriate callback. on_input alone might be enough for what I'm looking for, though.
Ilari wrote:
As to on_input() occuring twice but on_snoop() just notifying each button once, that's very strange. You are sure not even one button is polled twice (two on_snoops for one button), because that would explain two on_input()s (yes, I have seen game poll for just one button). on_input() can legitly occur more than once per frame if game polls more than once per frame (some do). I'll need to check if the logic is right...
I'm pretty sure on_snoop is telling me each button is polled once, and on_input is called twice. But we already mentioned some of this in IRC. I'm working on a basic input script and it told me each button is polled exactly once, and of course there's the above script. But you've also taken steps to fix this by now. I'm glad to have pointed this out, in any case.
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
I very much appreciate the work being put in here. I certainly love an accurate emulation, and am all for the increased emulation accuracy when it comes to TASing. Though my computer can't run the compatibility core at full speed (roughly 50 fps out of 60), a TAS does not require this. I've also been looking into the lua as it is implemented. After getting used to the idea of callbacks and that giving functions certain names lets lsnes automatically call them, I feel right at home. Well, mostly, anyway. I was never a fan of the emulua joypad.get(), as it returns a table with named key values, with the complementary joypad.set() taking such a table. I worked with it because I didn't actually need more than that, but I was still often inconvenienced. I kind of like the lsnes input.get() and input.set() functions, for the reason it returns and takes numbers instead of tables. Though I still find one inconvenience, regardless: Only one button at a time can be returned/modified. No, I'm not suggesting tables, keep away from those! Still, I find myself working with it with better ease than with the emulua joypad functions. A request: Perhaps input.geta() and input.seta() functions that return and take a bit-packed value instead of taking an index and returning or taking a zero/non-zero value. As for callbacks, this is a nice job done so far. Although there's nothing like gui.register of certain emulators (the kind that just continuously runs while emulation is paused), there are callbacks that respond to key hits which can then request a display update, with the same effect I want: Input for lua while emulation is paused. I enjoy being able to run lua while emulation is paused, and this does not waste unnecessary processing when there is nothing in particular happening. A notice: The on_frame() callback is not called when you start a lua script through wxwidgets, then frame advance once. It is called properly on the second and following frame advances after the script begins. A question: How would one call on_startup() in wxwidgets? There does not appear to be a window with which to initiate the emulator with a lua script, and starting the script through the menu doesn't call it (though, in that case, just shove the code in the main body and it's run on script initialization). A request: A callback for when a request to advance a frame takes place. Although, on_input() seems to be doing this job, it's not perfectly clear if this is the intention. I spot two on_input calls each frame during normal non-lag frames in one game I was playing around with (Sim City), and on_snoop tells me just one request for each button to the controller takes place.
Post subject: No improvements to Dungeon Magic, it seems.
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
Failed. I decided to warp on top of some exit in this dungeon, and noted that I could step south. I also noted that, on the first floor of this low-level dungeon, there is a nifty exit on the south edge of the map. Finally, the value of 0095 was 0x86. Increment by 4, and I'm at the castle. If walking onto the south border of any field did what I expected (increment 0095 by 4), then I would have found a neat detour around the whole errand with that sword. However, it didn't do what I had hoped. Instead of finding myself on Y 0 of a different field, I somehow moved one tile north instead. I am definitely facing south, and I definitely stepped forward. But some programmer decided that the game should react differently in this case, and well... This attempt is a failure. Ah, well. Now I won't have a nagging question about this game, at least.
Post subject: I exist. No AH progress. Still on my mind, though.
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
No noted Some potential improvement I can think of. One thought was the fact walking off a field edge while on the overworld affects address 0x0095 by -4 (north), +1 (east), +4 (south), or -1 (west). This requires an open path to the field edge, which typically is walled off in dungeons. Air magic has a "warp back 1 floor" spell that preserves your current X,Y position, leading to some interesting areas, but I can't theorize a useful area to do this in. Regardless, the bottom floor of the final area has a value of 0x8B, with the floor before it being 0x8A. Though I doubt we can abuse a map edge even with warp magic handy, it would be nice if we found a detour around the king & queen without need of picking up that sword. Just some potential improvement I might look into eventually. It's about time I produced a script that shows a basic map. Now you can see exactly where I'm running around instead of seeing this silly blur. Feel free to view the published run using it. Wasn't sure where to put the map. I don't think anyone cares about seeing the character's face anyway. Download DM.lua
Language: lua

local R1u= memory.readbyte local Li= gui.line local Px= gui.pixel local And= bit.band --***************************************************************************** local function Fn() --***************************************************************************** gui.box(14,129,96,211,"black","black") for x= 0, 15 do local L= 15 + 5*x for y= 0, 15 do local T= 130 + 5*y local B= R1u(0x0600 + x + y*16) if And(B,0x0F) == 0x0F then Px(L+2,T+2,"white") end if And(B,0x11) == 0x11 then Li(L ,T ,L+4,T ,"green") elseif And(B,0x01) ~= 0 then Li(L ,T ,L+4,T ,"white") end if And(B,0x22) == 0x22 then Li(L+4,T ,L+4,T+4,"green") elseif And(B,0x02) ~= 0 then Li(L+4,T ,L+4,T+4,"white") end if And(B,0x44) == 0x44 then Li(L ,T+4,L+4,T+4,"green") elseif And(B,0x04) ~= 0 then Li(L ,T+4,L+4,T+4,"white") end if And(B,0x88) == 0x88 then Li(L ,T ,L ,T+4,"green") elseif And(B,0x08) ~= 0 then Li(L ,T ,L ,T+4,"white") end B= R1u(0x0700 + x + y*16) if B ~= 0 then local C= "red" if B == 0x10 then C= "green" elseif B == 0x80 then C= 0x0080FFFF end gui.box(L+1,T+1,L+3,T+3,C,C) end end end end gui.register(Fn)
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
Brian_pso wrote:
[...] give us an average number of levels that could be skipped using this glitch, without losing any time of course.
To add to the request of Brian_pso (as I see it, anyway), I would like a full list of the believed fastest methods to beat all 96 levels. As suggestions for a summary of each method, how about "usual", "chuck/goal sphere (YI1)", "chuck/key", "Yoshi Wings (block duplication)", "stun glitch", etc. No need to limit yourselves to my suggestions. They are, after all, suggestions. Though, it would take some time to type up, this might also help you organize a bit. Even if not, it would help us see what to expect.
Post subject: Thread revival post. A post that revives threads, of course!
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
I've distracted myself on this game for a bit. The resulting .fm2 I got the .fcm to sync on FCEUX -- Convert to .fm2, then insert four blank frames prior to the immediate reset. I was making my own run without the ability to see that .fcm for a while, until I realized how to make the darn thing sync. A nice surprise, then, that I found I beat Blublu's WIP by over 700 frames by the time I collect that second fuel unit. I've developed my own script for this game as well. It reveals locations of off-screen objects by drawing a single pixel with a specified scaling. Also, by holding down space, it blacks out the screen so that these pixels can be more easily seen. Regardless, it reports an object that exists right above the second fuel unit in world 7. I've flown through this invisible object and shot it, and also waited around it for a while, and didn't get it to react. I have not done any digging into memory to see what makes this mystery object work. It's invisible, and resides in the empty space somewhere above the second fuel unit. Anyway, my current script as is: Download SJ.lua
Language: lua

local R1u, R1s= memory.readbyte, memory.readbytesigned --***************************************************************************** local function R2u(a) return R1u(a) + R1u(a+1)*256 end local function R2s(a) return R1u(a) + R1s(a+1)*256 end --***************************************************************************** local lastkeys, keys= input.get(), input.get() --***************************************************************************** local function UpdateKeys() lastkeys= keys; keys= input.get() end --***************************************************************************** local function Limits(V,l,h) return math.max(math.min(V,h),l) end --***************************************************************************** --***************************************************************************** local Draw= {} --***************************************************************************** --Coordinates is the top-left pixel of the 3x5 digit. --Used for drawing compact, colored numbers. local Px,Li= gui.pixel, gui.line Draw[0]= function(x,y,c) -- ### Li(x ,y ,x ,y+4,c)-- # # Li(x+2,y ,x+2,y+4,c)-- # # Px(x+1,y ,c) -- # # Px(x+1,y+4,c) -- ### end Draw[1]= function(x,y,c) -- # Li(x ,y+4,x+2,y+4,c)-- ## Li(x+1,y ,x+1,y+3,c)-- # Px(x ,y+1,c) -- # end -- ### Draw[2]= function(x,y,c) -- ### Li(x ,y ,x+2,y ,c)-- # Li(x ,y+3,x+2,y+1,c)-- ### Li(x ,y+4,x+2,y+4,c)-- # Px(x ,y+2,c) -- ### Px(x+2,y+2,c) end Draw[3]= function(x,y,c) -- ### Li(x ,y ,x+1,y ,c)-- # Li(x ,y+2,x+1,y+2,c)-- ### Li(x ,y+4,x+1,y+4,c)-- # Li(x+2,y ,x+2,y+4,c)-- ### end Draw[4]= function(x,y,c) -- # # Li(x ,y ,x ,y+2,c)-- # # Li(x+2,y ,x+2,y+4,c)-- ### Px(x+1,y+2,c) -- # end -- # Draw[5]= function(x,y,c) -- ### Li(x ,y ,x+2,y ,c)-- # Li(x ,y+1,x+2,y+3,c)-- ### Li(x ,y+4,x+2,y+4,c)-- # Px(x ,y+2,c) -- ### Px(x+2,y+2,c) end Draw[6]= function(x,y,c) -- ### Li(x ,y ,x+2,y ,c)-- # Li(x ,y+1,x ,y+4,c)-- ### Li(x+2,y+2,x+2,y+4,c)-- # # Px(x+1,y+2,c) -- ### Px(x+1,y+4,c) end -- ### Draw[7]= function(x,y,c) -- # Li(x ,y ,x+1,y ,c)-- ## Li(x+2,y ,x+1,y+4,c)-- # end -- # Draw[8]= function(x,y,c) -- ### Li(x ,y ,x ,y+4,c)-- # # Li(x+2,y ,x+2,y+4,c)-- ### Px(x+1,y ,c) -- # # Px(x+1,y+2,c) -- ### Px(x+1,y+4,c) end Draw[9]= function(x,y,c) -- ### Li(x ,y ,x ,y+2,c)-- # # Li(x+2,y ,x+2,y+3,c)-- ### Li(x ,y+4,x+2,y+4,c)-- # Px(x+1,y ,c) -- ### Px(x+1,y+2,c) end Draw[10]=function(x,y,c) -- # Li(x ,y+1,x ,y+4,c)-- # # Li(x+2,y+1,x+2,y+4,c)-- # # Px(x+1,y ,c) -- ### Px(x+1,y+3,c) -- # # end Draw[11]=function(x,y,c) -- ## Li(x ,y ,x ,y+4,c)-- # # Li(x+1,y ,x+2,y+1,c)-- ## Li(x+1,y+4,x+2,y+3,c)-- # # Px(x+1,y+2,c) -- ## end Draw[12]=function(x,y,c) -- # Li(x ,y+1,x ,y+3,c)-- # # Li(x+1,y ,x+2,y+1,c)-- # Li(x+1,y+4,x+2,y+3,c)-- # # end -- # Draw[13]=function(x,y,c) -- ## Li(x ,y ,x ,y+4,c)-- # # Li(x+2,y+1,x+2,y+3,c)-- # # Px(x+1,y ,c) -- # # Px(x+1,y+4,c) -- ## end Draw[14]=function(x,y,c) -- ### Li(x ,y ,x ,y+4,c)-- # Li(x+1,y ,x+2,y ,c)-- ## Li(x+1,y+4,x+2,y+4,c)-- # Px(x+1,y+2,c) -- ### end Draw[15]=function(x,y,c) -- ### Li(x ,y ,x ,y+4,c)-- # Li(x+1,y ,x+2,y ,c)-- ## Px(x+1,y+2,c) -- # end -- # --***************************************************************************** local function __DN_AnyBase(right, y, Number, c, bkgnd, div) --***************************************************************************** -- Works with any base from 2 to 16. Paints the input number. -- Returns the x position where it would paint another digit. -- It only works with integers. Rounds fractions toward zero. if div < 2 then return end -- Prevents the function from never returning. local Digit= {} local Negative= false if Number < 0 then Number= -Number Negative= true end Number= math.floor(Number) c= c or "white" bkgnd= bkgnd or "clear" local i= 0 if Number < 1 then Digit[1]= 0 i= 1 end while (Number >= 1) do i= i+1 Digit[i]= Number % div Number= math.floor(Number/div) end if Negative then i= i+1 end local x= right - i*4 gui.box(x+1, y-1, right+1, y+5,bkgnd,bkgnd) i= 1 while Draw[Digit[i]] do Draw[Digit[i]](right-2,y,c) right= right-4 i=i+1 end if Negative then gui.line(right, y+2,right-2,y+2,c) right= right-4 end return right end --***************************************************************************** local function DrawNum(right, y, Number, c, bkgnd) --***************************************************************************** -- Paints the input number as right-aligned. Decimal version. return __DN_AnyBase(right, y, Number, c, bkgnd, 10) end --***************************************************************************** local function DrawNumx(right, y, Number, c, bkgnd) --***************************************************************************** -- Paints the input number as right-aligned. Hexadecimal version. return __DN_AnyBase(right, y, Number, c, bkgnd, 16) end local OC= { [0]= "white", "white", "white", "green", "green", "green", "green", "green", "green", "green", "green", "blue", "blue", "blue", "blue", "red", "red", "red", "red", "red", "red", "red", "red", "yellow", "yellow", "yellow", "yellow", "yellow", "grey", "grey", "grey" } --***************************************************************************** local function ObjRadar(Factor) -- What factor to scale radar? --***************************************************************************** local CamX, CamY= R2u(0x004C), R2u(0x004E) local OffX= math.floor((1-1/Factor)*128) local OffY= math.floor((1-1/Factor)*100) local count= 0 for i= 0, 0x1E do if R1u(0x0317+i) ~= 0 then count= count+1 gui.pixel( math.floor((R1u(0x0200+i)+R1s(0x021F+i)*256-CamX)/Factor + OffX), math.floor((R1u(0x025D+i)+R1s(0x027C+i)*256-CamY)/Factor + OffY), OC[i] ) end end DrawNum(90,12,count) end --***************************************************************************** local function ObjCount() --***************************************************************************** local P, B, E, A, I, S= 0,0,0,0,0,0 for i= 0, 2 do if R1u(0x0317+i) ~= 0 then P= P+1 end end for i= 3, 10 do if R1u(0x0317+i) ~= 0 then B= B+1 end end for i= 11, 14 do if R1u(0x0317+i) ~= 0 then E= E+1 end end for i= 15, 22 do if R1u(0x0317+i) ~= 0 then A= A+1 end end for i= 23, 27 do if R1u(0x0317+i) ~= 0 then I= I+1 end end for i= 28, 30 do if R1u(0x0317+i) ~= 0 then S= S+1 end end gui.box(238,8,252,56,"black",0) Draw[P](240,10,"white"); Draw[3](248,10,"white") Draw[B](240,18,"white"); Draw[8](248,18,"white") Draw[E](240,26,"white"); Draw[4](248,26,"white") Draw[A](240,34,"white"); Draw[8](248,34,"white") Draw[I](240,42,"white"); Draw[5](248,42,"white") Draw[S](240,50,"white"); Draw[3](248,50,"white") end local DirX={ [0]= 0, 3, 6, 8, 11, 13, 14, 15, 16, 15, 14, 13, 11, 8, 6, 3, 0, -3, -6, -8,-11,-13,-14,-15, -16,-15,-14,-13,-11, -8, -6, -3 } local DirX2={ [0]= 0, 12, 24, 35, 45, 53, 59, 62, 64, 62, 59, 53, 45, 35, 24, 12, 0,-12,-24,-35,-45,-53,-59,-62, -64,-62,-59,-53,-45,-35,-24,-12 } --***************************************************************************** local function DirPow(v) --***************************************************************************** return DirX[v], DirX[(v+8)%32] end -- 0 1 2 3 4 5 6 7 8 -- 0 3 6 8 11 13 14 15 16 --***************************************************************************** local function Fn() --***************************************************************************** UpdateKeys() --BlackBox, to see the dot radar better. if keys.space then gui.box(-1,7,256,200,"black",0) end --BasicHUD DrawNum( 20,10,R2u(0x00C6),"white","black") -- Fuel DrawNum( 20,40,R1u(0x0374),"white","black") -- Direction DrawNum(230,10,R2u(0x004C),"white","black") -- Cam X DrawNum(230,30,R1u(0x0200) + R1u(0x021F)*256,"white","black") -- Player X DrawNum(230,40,R1u(0x025D) + R1u(0x027C)*256,"white","black") -- Player Y --X Velocity local N= 1 if R1u(0x0336) == 1 then N= -1 end DrawNum( 20,20,(R1u(0x029B)+R1u(0x02BA)*256)*N,"white","black") DrawNum( 40,20,DirX[R1u(0x0374)%32] ,"white","black") DrawNum( 54,20,DirX2[R1u(0x0374)%32],"white","black") --Y Velocity N= 1 if R1u(0x0355) == 1 then N= -1 end DrawNum( 20,30,(R1u(0x02D9)+R1u(0x02F8)*256)*N,"white","black") DrawNum( 40,30,DirX[(R1u(0x0374)+8)%32] ,"white","black") DrawNum( 54,30,DirX2[(R1u(0x0374)+8)%32],"white","black") ObjRadar(3) ObjCount() end gui.register(Fn) --***************************************************************************** local function DumpStats() --***************************************************************************** for obj= 0, 0x1E do local color= "white" if R1u(0x0200+obj+0x1F*9) == 0 then color= "grey" end for st= 0, 16 do DrawNumx(9+st*10, 9+obj*7, R1u(0x0200+obj+st*0x1F),color,"black") end end end --gui.register(DumpStats)
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
Chuck-Eat glitch (primary) - Yes. Unrestricted I'm thinking of some glitched run where, though everything is crazy glitchy, still gets pretty repetitive after a while. Regardless, you will have at least two different levels you can base this off. It will get repetitive around the third start select revisit, though... But I do want to see all glitch-exits traversed... An alternative is to restrict the glitch by restricting ourselves from revisiting a level we already left by start select. Chuck-Eat glitch (secondary) - Yes. With restrictions. Obviously, credits trigger and exit counter overwrite is undesired. Technical restriction, really: No overwriting special memory addresses. But outside of that, go nuts. I certainly won't mind seeing a "warp" take place. "stun any sprite" bug - Yes. Unrestricted. Can Mario be "stunned"? What about Yoshi? Message boxes? Moving platforms? Hey, you said any sprite. But besides that, anything that ends the level instantly through highly unanticipated means would be plenty good to see, by my initial thoughts. All-exits - All 99. When I think all exits, I mean all of them. Originally, I believed it to be 96, but now I am aware otherwise. By this alone, a longer run that completes a new-found exit while still keeping a similar level of optimization on other levels should obsolete the older runs. This has the side-effect of transmitting to everyone that, no, getting the goal sphere in Bowser's Castle through either route will not trigger the credits. Now go away. Those are my thoughts.
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
Bobo the King wrote:
Any thoughts, FatRatKnight?
For whatever reason, the require keyword wasn't cooperative at one point when I was trying to get YummyButtons.lua in there, so I went with dofile instead. I actually don't know a lot of details about require, though. Just that loading a lua file is one of the things it can do.
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
I'm pretty sure I still missed a few things. Feel free to report glitches! ... That is, if you want to try it out, anyway. Basically, run the script. A bar should show up. Click some spot on it and, with the left mouse button still held down, frame advance. Be sure to go up or down as you think the value you're looking for goes up or down as well. It does decent estimations, so no need to know exact values this way. The core was easy. The gui was messy. It's pretty processor intensive, and there's certainly a few things that still need to be done (I consider it to be mostly beta at this point). Still, it was indeed a joint project between us two, me and Bobo the King. So now you have confirmation, as if you needed it.
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
Sorry about the wait, but at least I'm updating you all in less than a month this time. 3-2, most of it. LazyMode takes over while the Heavy Ice Agent is still going through that long death animation. I can probably optimize those fights a bit when I'm less tired from TASing. And manipulate another grenade pack somehow. I might be able to find another second in those last scenes somewhere. But for now, enjoy the new boss end for 3-1. Enjoy the optimized parts of 3-2. Enjoy the subtitles script that I tend to update whenever I feel like it. Also, a .vbm file that, in the very first frame of the savestate anchored movie, has an explanation of the 2-2 boss glitch detailing why I don't beat up the first phase right away, along with a demonstration on what happens in there. Pause emulation then run movie to see the explanation. Might be handy to produce a few of these demonstration movies while making the submission text. You never know.
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
Getting a new (or at least a different) keyboard might affect your problem. This problem depends on the keyboard itself, and not anything about the programs in the computer. Failing that, rebinding keys, as others say, is another option available to you. Another option is to use the Auto-hold feature found in a few emulators. FCEUX definitely has it. The emulator can "hold down" a key for you so that you don't have to depend on getting around a keyboard flaw, so you can freely frame advance with 8 buttons held if need be. Good to keep in mind it's there, if you ever need it.
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
Mugg contacted me with an improvement to the 1-2 boss. That is quite a ways back, indeed. I then optimized it, synced up the RNG, and... Well, the results are very much pretty, thankfully. 50 frames are saved due to this improvement. And I have lost no progress, the spliced movie does not break. ... Oh, right. If I did make progress into 3-2 by now, I would have included it in the spliced movie. I'm not a person who likes to keep secrets.
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
3-1 I'm probably going to check the RNG for 3-2, then optimize this battle. It's nice that I can win in one phase. T5 and T1 used the wrong weapon. T2 lacked in grenades. This run had no such problems this time around. It's roughly four seconds of savings to avoid that buried period. Also, I had 18 grenades to spare. Seems I picked up one surplus pack. Basically, if the RNGScan even hinted at a vague possibility of a grenade drop, I was crawling all over it searching for a way to make it drop. There were plenty of times where I picked something up while in mid-travel.
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
Ah, I misunderstood. Whether it's efficient generally depends on how many places you can rely on the script. If the game is short, it's usually best to just do things manually, since you won't be doing that manual work too often anyway. But there are still some short runs (Wizardry for NES) that needed extensive botting. And you're welcome. Hope to see how much use you get from the hitbox script. (Some information scripts can still get pretty tricky. One I'm holding on to, for a different game, allows me to see off-screen hitboxes and calculates the exact RNGs for upcoming item drops. It has proven its necessity in the TAS numerous times.)
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
Now you're asking about something that goes beyond just information. A script that handles keypresses for you is either a macro (largely independent of internal game states, generally a static set of keypresses that can apply to many situations) or a bot (typically reads game memory to calculate what to do or to determine whether success happened). Producing a botting script is outside of my comfort zone. And asking me to make such a script would involve sinking far more of my time than I'm willing to give. In particular, I would have to spend days studying the game mechanics, among other things, to come up with a reasonable bot that can handle a single jump. It's not a bad idea to have scripts that can handle things for you, anything that would be simple yet tedious or repetitive to do manually... Provided that you can get access to such a script. Regardless, if you still want to produce the scripts yourself, I will be happy to walk through the process with you. If not, that's fine too, as either way you go, you'll spend some time on the game to get a well-optimized TAS going.
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
A lua script for hitboxes is actually a pretty tall request. Sure, you paint boxes, how hard can that be? Unfortunately, the person making the script has to find out how the game handles it. Where in memory it is. All that stuff. And code up a script to handle that. Since this thread creation, I was working at the script. No, not every minute of the day, but I spent a fair amount of time on it. This should give an idea on the effort needed to fulfill your request. I did not mention I was working on it so that there would be no chance to disappoint you should I fail on the script. I must warn, however, I did not get the camera worked out fully. Expect glitches with the lua display. Still, this is probably more than what you had before. EDIT: The warning applies less now. I've faked up a good upper byte for the camera now. I assume Barry is always onscreen, and pick an upper byte for the camera accordingly. Download TheFlashHitboxes.lua
Language: lua

local R1u, R1s= memory.readbyte, memory.readbytesigned -- 1 byte local R2u, R2s= memory.readword, memory.readwordsigned -- 2 byte Little endian --***************************************************************************** local function u2R(a) return R1u(a)*256 + R1u(a+1) end -- 2 byte Big endian local function s2R(a) return R1s(a)*256 + R1u(a+1) end -- 2 byte Big endian --***************************************************************************** --memory.readword fails. Numbers in The Flash are big endian. --***************************************************************************** local function HexStr(v) --***************************************************************************** -- Returns a string. If it's negative, specifically forces a negative sign -- instead of returning "FFFFFFxx" stuff. local str= "" if v < 0 then str= "-" v= -v end return str .. string.upper(string.format("%x",v)) end --***************************************************************************** local function GuiTextRight(x,y,str,c1,c2) --***************************************************************************** -- The default for gui.text is left-justified. This is right-justified. -- Specify the right-side pixel instead of the left. -- Ideal for when you are listing numbers, as the magnitude of each digit are -- aligned for easy comparison. str= tostring(str) x= x - (#str)*4 gui.text(x,y,str,c1,c2) end -- 0xC2A0 -- Start of the array containing character information. -- Data size is 0x36. So the second character has info stored in 0xC2D6 -- Apparently, there's room for 23 of these things. --***************************************************************************** local function PutHitbox(obj, CamX, CamY) --***************************************************************************** -- Paints a hitbox. It wants the object base address and camera position. gui.box( math.floor(u2R(obj+0x10)/16) - CamX, -- Left math.floor(u2R(obj+0x12)/16) - CamY, -- Top math.floor(u2R(obj+0x14)/16) - CamX, -- Right math.floor(u2R(obj+0x16)/16) - CamY, -- Bottom 0, -- Fill color (Transparant) 0x0040FFFF -- Border color (Blue) -- If you need 0x80 red or higher, switch to strings "#FF0000FF", as -- a numeric 0x800000FF fails due to the 80Red glitch. ) end --***************************************************************************** local function FakeCamPos() --***************************************************************************** -- Gets the camera position. Actually, fakes the camera position. This uses -- Barry's position to produce the upper byte for the camera. local obj= 0xC2A0 -- The Barry object is located here -- But first, check if Barry exists... Assume he's always the first object. if R1u(obj) == 0xFF then return R1u(0xC145), R1u(0xC146) end -- Now we assume the camera has the greatest possible value that's less than -- Barry's position. local CamX, CamY= R1u(0xC145), R1u(0xC146) local UpperByte= math.floor((u2R(obj+1)/16 - CamX)/256) CamX= CamX + UpperByte*256 UpperByte= math.floor((u2R(obj+3)/16 - CamY)/256) CamY= CamY + UpperByte*256 return CamX, CamY end local StartAddr= 0xC2A0 --***************************************************************************** local function IterateObjects() --***************************************************************************** -- Will go through all 23 objects. local CamX, CamY= FakeCamPos() for i= 0, 22 do local obj= StartAddr + 0x36*i if R1u(obj) ~= 0xFF then -- Does the object exist? PutHitbox(obj, CamX, CamY) end end end gui.register(IterateObjects) -- I prefer the gui over "boundary" -- 0xC2A0 [size=0x36][count=23] Object Array -- +0x00,1x Object ID. 0xFF = No object All 2-byte values -- +0x01,2u X position are assumed to be -- +0x03,2u Y position big endian unless -- +0x05,2s X velocity noted otherwise. -- +0x07,2s Y velocity -- +0x08, -- +0x09, -- +0x0A, -- +0x0B, -- +0x0C, -- +0x0D, -- +0x0E, -- +0x0F, -- +0x10,2u Hitbox, left -- +0x12,2u Hitbox, top -- +0x14,2u Hitbox, right -- +0x16,2u Hitbox, bottom -- +0x18,2u Hitbox, left (mirror?) -- +0x1A,2u Hitbox, top (mirror?) -- +0x1C,2u Hitbox, right (mirror?) -- +0x1E,2u Hitbox, bottom (mirror?) -- +0x20, -- +0x21, -- +0x22, -- +0x23, -- +0x24, -- +0x25, -- +0x26, -- +0x27, -- +0x28, -- +0x29, -- +0x2A, -- +0x2B, -- +0x2C, -- +0x2D, -- +0x2E, -- +0x2F, -- +0x30, -- +0x31, -- +0x32, -- +0x33, -- +0x34, -- +0x35,
Incidentally, the upper byte of the camera position does not need to exist in the game. The hitboxes themselves have all the information necessary to tell when there's a collision, and this does not require a camera. And if that upper byte does not exist, a strange method will need to be used to fake one up to get rid of most of these glitches. Said strange method is using Barry's position to guess the actual camera location.
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
2-4 Now I'm happy. 96 frames off my previous boss attempt. Meaning 76 frames faster than T5 boss win. Total savings are 1652 frames over T5's run, from start of run to start of 3-1. I've also, seen in this file, made a small attempt at pushing into 3-1 for a bit. Although I've already beaten T5's run by around 25 frames in these two scenes, I suspect at least another 10 frames can be gained. I'll be sure to see whether a pair of grenades is what I need for two of the scorpions. Hope I get a 9-damage charged shot in time for that vulture.
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
Technically, editing memory is against the rules. In any and all form. However, if it's a proven emulator bug (the console properly handles the FMV without needing a save after the swap, for example), you might have a decent case explaining what you're doing, and the memory edit serves to improve emulation accuracy. But it's still a memory edit. One that a user must make themselves when running your movie. In any case, we might want to point this oddity out to those currently working on the emulator. It would be preferable to have the emulator do what it's supposed to, rather than having it up to the user to manually affect the memory. But if the actual console also fails in this case, then there isn't a problem -- Save and reload from memory card. If the console has no trouble, bug the emulator developers. As for whether the memory edit workaround will be accepted due to poor emulation... I'm not a judge or an admin around here. I'll let them answer. But I felt you wanted thoughts from someone while we wait for the right person to come post.
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
As far as botting goes, you need a bare minimum of lua code that uses the same CPU as the primary emulator. Enough to know that, yes, the emulator is advancing a frame now. This code could handle strictly what is necessary -- Allow emulator to frame advance, read from memory, apply joypad buttons, whatever. The rest of the code, like the calculations or comparisons using that memory, or deciding a new list of joypad stuff, or a message to load the previous state, can be handled by the secondary code which you multithreaded onto another CPU. But that's my initial thought, anyway. I'm not sure how significant the benefit would be (simple bots won't have much lua-side code, moving this tiny code elsewhere won't improve performance significantly). If the emulator itself is already capable of multithreading (without desync issues), it's probably already making use of all the CPUs, and sticking the lua anywhere would only take from the emulator's processing. Still... Might be good to keep this in mind in case you have a long-term bot running pretty complex code. Smaller applications probably won't get a lot of use out of this, though. Another tool to have around, just in case we need the processing power.
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
2-4... Well, not quite finished, I say. The first phase of the level went by well enough. The first cluster of satellites all appeared directly above, so yay. I did some neat optimizations, more often do I use both front and back shots at once, and in general more efficient stuff getting through. 108 frames faster than T1's attempt. However, the boss, apparently, is 20 frames slower than T5. Next, I'll try the "hold still and use backshots over and over, destroying style in the process" maneuver and then try to beat that doing stuff like I did in that movie, but better Let's see how I do...
Post subject: Time to surprise someone... That was FAST!
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
2-3 55 frames faster than T1's run. T5 was slowest here, and I beat that one by 195 frames, if you're curious. Indeed, a straight-forward stage. No crazy ninja luck manipulation like in the other stages. Thankfully, no adjustments were needed to get my grenade + spread drops. As for why I ran out of ammo, blame the lack of anything that likes to drop stuff. Seriously, those two buildings are my only sources of death-bringing ammo. And that one helpful kid near the end, too. Will likely prettify the end of 2-3 while checking out a few RNG values for 2-4. I suspect there are clouds disguised as space junk in that stage... Yeesh.
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
2-2 Updated subtitles in this post. Should reveal boss HP, except for the 2-2 endboss. 2-3 looks like a simple enough matter of just getting there. Though, I will hate it if the RNG decides I should redo the 2-2 boss for grenade + spread drops in 2-3, rather than just the tail end where the control I have there might not be enough. But I'm pretty satisfied in what I managed to do, anyway. Slow, I may be, but there's progress.
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
Amazing... So that's what I've been missing. It's no less spectacular than I imagined, that is quite certain. It was truly fascinating in a few parts, and I was surprised in a few other parts. This game really does look like a good TAS choice. You have my thanks for the encode. And sorry about going through with the contest closure, but I posted my reasons there. lapogne36, what are your plans, by the way? I don't want to put unwanted pressure on you, though. If you want to stop since I ended the contest, I will be saddened, but it's not up to me to decide what you do. But thanks for participating while the contest was still open, though.
Editor, Experienced Forum User, Published Author, Skilled player (1174)
Joined: 9/27/2008
Posts: 1085
The lack of any replies was quite compelling. The contest has closed. Though, please continue on here. I still dream of a point where we can actually produce the TAS.