Posts for Bobo_the_King

1 2
26 27 28
34 35
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Ferret Warlord wrote:
This needs to be made reality stat!
I know! I want to make it a commercial or something, but I have no idea for what (stopbean.com, I guess...). The funny thing is, I'm not a Mr. Bean fan and I've watched almost nothing of the show. I saw the movie, what, fifteen years ago? That's about all.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Ferret Warlord wrote:
Answer 2: Surprising. Season 1 was pretty uneven already.
I agree, and I was thinking of mentioning that, but I wanted my post to be more succinct. While every episode in season one brought something new to the table, some of them were pretty lackluster. In fact, now that I think about it, the quality seemed to be inconsistent within most episodes, not just between episodes. For example, I think Feeling Pinkie Keen had some of the best jokes of the season, but I couldn't stand its anti-skepticism message. That may be what's most shocking about season two-- there are now whole episodes that fall short, rather than just pieces of episodes. On the other hand, I don't think I ever would have become interested in this show if I hadn't watched all of season one in a marathon session... multiple times. I haven't had the time to do the same with season two (I've seen most of its episodes only once), so it will be interesting to see how it stands up to several viewings. My guess is that it's a measurable downgrade from season one, but not as bad as one would think at first glance.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
arflech wrote:
This is an excellent mashup of the Yoshi's Island intro. with the most recent episode of the series: https://www.youtube.com/watch?v=l45-xhzC2bQ
These are a few of my favorite things.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Warp wrote:
Is it just me, or has the quality of the show slightly but consistently declined lately?
I agree. One of the reasons I was drawn to MLP is because it seemed a lot like old episodes of The Simpsons, particularly because it felt like such an expansive universe. In early Simpsons episodes, if they needed a new character for some reason, they made one. I thought MLP was going down the same road with characters like Mr. Breezy and the Quill and Sofa shop owner, but now it looks like it's defocused from Twilight and even the mane six and is sort of stagnating on second-tier characters. That's just my guess, though it certainly doesn't help that this season is severely lacking in songs and Fluttershy (seriously, where the hell has she been?). I think there's still plenty to like about the show, but if the decline continues at this rate (which I doubt), I don't think I'll last through the third season.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
I had a dream last night that I was browsing the internet. I glance at an ad for "stopbean.com", which consists of nothing but a text box and a confirmation button. I move on to another website. Suddenly, Rowan Atkinson comes up behind me and starts harrassing me-- poking me, slapping me, flicking me. I push him away and quickly change back to the tab with the advertisement. I then see that it's subtitled, "Your #1 site for getting Mr. Bean to stop attacking you." I try to enter something into the text box, which is really hard since I'm turned halfway away from my computer, kicking Mr. Bean away with one foot. Finally, I manage to type "stopbean=true" and click the button. I turn around and he's standing there, smiling politely at me.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
RakuRocRex wrote:
Aw... But why do I have to finish elementary? I told Bobo I'd fix my grammar and I did.
Just edit your post and forget about it. Arflech: cut the kid some slack... ... for now.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Noob Irdoh wrote:
1979-05-08.
You've got a birthday buddy with Bablo, back on page 1. I doubt he's still around, though. Edit: I share a birthday with two famous composers and another less famous composer died on my birthday. I also share my birthday with a famous female world leader and a prominent news/talk show host who died a few years ago. I could just tell everyone my birthday, but I think it's more fun to drop hints.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
I'd rather just work with you here if that's okay. (I have a YouTube account, but for convoluted reasons, I can't log into it.) Do you have a copy of the ROM?
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
RakuRocRex wrote:
Fine, then. I'm ready to TAS now because I'm Raku. Thanks, brother, for helping me out. (This took 5 minutes to write. Hope you like it.)
That's much appreciated, and I mean that sincerely. What game would you like to TAS? You mentioned VBA. I think you also mentioned Mario & Luigi: Superstar Saga (though I couldn't find that post). Someone else is already working on it, but you can practice it in the meantime. There's no one formula for TASing any game-- it all depends on the game. What game do you want to work on?
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
ur gona b baned b4 long wahhhh! By the way, "I'm" is spelled with an apostrophe. Edit:
RakuRocRex wrote:
hey but i came to tsa not to grammar teech me tas
See, this is where the rubber meets the road. People here will not be inclined to teach you about TASing because you haven't demonstrated a willingness to learn how to type coherent sentences. When you type everything with care, I will personally teach you how to TAS a game.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
I'm going to politely request this one more time: Begin your sentences with capital letters, end them with periods, and fix any spelling errors along the way. Show your sincerity. You are quickly approaching the event horizon, beyond which no one will take you seriously.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
RakuRocRex wrote:
i hate englsh class and i only liek vba 21 ;0
Look around you. Look at how others here formulate their thoughts into coherent sentences. I'd venture to guess that English is no one's favorite subject on this site, but we are still very mindful of how we present ourselves. I'm not trying to give you a hard time, Raku-- plenty of other people will do that. In fact, I keep an open mind to any new member. First and foremost, however, newcomers have to demonstrate that they want to be taken seriously; in your case, that means being careful with how you write things. Your homework is to begin every sentence with a capital letter and end it with a period. Also, check your spelling before you submit anything. Chances are, your browser has a built-in spell-checker. When you type something in, if anything has a red underline underneath it, you need to go back and fix its spelling. Saying you don't like English and failing to use basic spelling and grammar is like saying you don't like P.E. and failing to stand upright. Anyway, I hate to be the first person to criticize you so... uh... welcome to TASVideos!
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
RakuRocRex wrote:
Noob Irdoh wrote:
An example of something similar to a TAS is a Piano Roll, look at this video: http://www.youtube.com/watch?v=5395Y0hwJGU With a Piano Roll you can get a piano to play any notes, even faster or more complex than what a human would be able to play. You can take all the time in the world to prepare your Piano Roll in the way you want it to be, and if there is a mistake, you can re-record it to play in the correct way. Likewise, a TAS is a list of button presses, ideally (or really, see Nesbot and Droid64) fed to a joypad, to play a game as perfectly as possible, even doing things a human can't do. There are several ways to create a TAS, with many tools you can use (hence the name). You can slow down the game and play a single frame (to make it simple, 1/60th of second in America, and 1/50th of second in Europe), you can go back and fix your mistakes, you can find out that the "random" ites are not really random and manage to get the best items all the times, and so on. Nobody prevents you from using all the techniques in the world while you create that list of button presses. Once it is ready, that input should always replay in the same way (still like the Piano Roll); if it desyncs, then something went wrong. Many TAS-related concepts can find examples in simple words in the real world. For example, you might read of a "frame rule" sometimes, e.g. "I can beat that level 10 frames faster but there is a 20 frames rule so it's pointless". Imagine you're on the street, running towards the station because you have to catch a train. Of course you try to run as fast as you can, but... there is a traffic light right next to the station. If you arrive there while the traffic light is red, all your running was wasted because you have to wait for the green. This is a frame rule. If this happens in a TAS, the "runner" can either slow down (but it would look unprofessional and not entertaining) or do something unexpected to keep the viewer's interest, such as collect all the coins, kill all the enemies, run backwards, dance, whatever. As long as you arrive at the traffic light in time for the green. My English is bad but I hope this is clear. Either way, welcome to the forum!
oh thnks is vba 21 a god tas emulator for tasing? yo can slow down game and maek it go bak is dat good? suk at grammar sry
Oh, thanks. Is VBA 21 a good emulator for TASing? You can use slowdown and savestates-- is that good? I suck at spelling, grammar, and punctuation, sorry. You'll never improve if you don't try.
Post subject: Re: on-screen + compat. error
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
marzojr wrote:
Bobo the King wrote:
I also could use the byte range for Genesis's RAM.
Genesis RAM goes from 0xFF0000 to 0xFFFFFF. With Sega CD, there is also "program RAM" at 0x20000 to 0x3FFFF and "work RAM" at 0x200000 to 0x23FFFF. I have no idea about the 32X.
Thanks, marzojr! Compatibility with Gens has now been implemented! It took entirely too long, however. Gens seems to be the black sheep of the emulators I've tested so far. It doesn't have the bit module pre-loaded, it doesn't have emu=gens (easy to fix, but still annoying), and it only checks its default directory for required lua scripts, not the directory of the parent script. As such, if you plan on using this script in Gens, copy all scripts to Gens's default directory, which should be the same directory that the emulator is in. For the bit module, I downloaded a Lua-native programmed bit module from here. This module doesn't have such extensive features as the official release (for example, its bit.band, bit.bor, and bit.bxor functions only take two arguments; I could fix it easily, but since we never use those functions with more than two arguments, I'll leave well enough alone), so it is only loaded if the emulator is Gens. I also noted some slowdown when displaying results. I don't know what's causing it off the top of my head. FatRatKnight is implementing his own custom font and display functions for standardization and because some emulators' Lua GUI functions look really ugly. For emulators like Gens and VBA, I think the default font looks decent, so it may be worth checking whether the slowdown can be stopped by using its native output. Finally, this affects nothing, but I will mention it for full disclosure. Because I have too many copies of these scripts floating around, sometimes changes are made to one copy but not the "official" copy. As such, if you have downloaded the script, you may have noticed that SmartRAMSearch.lua doesn't match the copy I presented in the forum post. The copy you have is the one without the functions broken up into other Lua scripts. In fact, if you do break up the functions into other Lua scripts, SmartRAMSearch.lua won't even run properly! That is because all those other scripts need to declare global functions, not local functions. I have fixed all this and now what you see is exactly what you get.
Post subject: Re: on-screen + compat. error
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
antd wrote:
Sorry for my stupid questions :D "Move the box to where you want it" Does this mean it will only work for things that are on-screen? Let's say for example that mario's 'timer' is very difficult to find using traditional ram searches. Could I use this tool to find the address? And how about critical hits in Final fantasy? byte range 0x000000,0x1FFFFF
You can use this script to find any non-constant RAM address you want! The position and size of the bar have absolutely nothing to do with the program's results. I only used Mario jumping as an example because it is particularly easy to track pixel by pixel. (However, because it's so easy to see what's going on with Mario's height, it isn't particularly interesting to use the script.) Your screenshot had me worried for a moment, but then I noticed you were pointing out its incompatibility with pcsx-rr. You may manually insert the lines
Language: lua

elseif pcsx then --PlayStation byterange={ {0x000000,0x1FFFFF}}
into the SmartRAMSearch.lua code under the "getsystem" function to get you started. I'll also need to know the native resolution of the game. A brief check online indicates that the resolution varies from game to game, so you should also insert the lines
Language: lua

L,U,R,D= 180,50,189,149 SCRBOTTOM,SCRRIGHT=640,480
which may allow you to scroll the bar off the screen in some games, so be careful! I'll make all those changes myself and update the code and download link shortly. Edit: Changes complete! Be forewarned: 2 MB of RAM to be copied every frame is positively massive. Expect it to take about ten seconds to advance a single frame. Do not press the spacebar unless you're ready to press ctrl-alt-del as well. However, if you could confirm that the basic program is working properly, I'd much appreciate it. True compatibility with PCSX will come when we implement pre-filtering. I also could use the byte range for Genesis's RAM.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
The scripts have been updated to use "dofile" instead of "require". I still have no idea what the underlying problem is, since I have a copy of the script with "require" at the top running right now.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
FractalFusion wrote:
I got an error beginning with:
...\side\snes9x v1.43\smartramsearch\SmartRAMSearch.lua:1: module 'BasicFunctions.lua' not found: no field package.preload['BasicFunctions.lua'] no file '.\BasicFunctions\lua.lua' ...
Even though I have all the files ("BasicFunctions.lua", "SmartRAMSearch.lua", "Sorts.lua", "VectorOps.lua", "YummyButton.lua") in one folder labelled "smartramsearch". I tested both versions of Snes9x, as well as FCEUX 2.1.4 and VBA v22.
Interesting. It worked on my computer just before I submitted it. You can delete the require("BasicFunctions.lua") statement at the top of the SmartRAMSearch.lua script and manually copy in the boolto10 function. Tell me if that gives you a new error with the second "require" statement. If so, just copy in the rest of the files manually. Any thoughts, FatRatKnight?
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
TASeditor wrote:
Bobo the King wrote:
TASeditor wrote:
Is this really needed? The example with Super Mario Bros. height position listen like a lot of work, compared with the normal RAM Search (just a few points to register by saying, with help of the graphical expression of the RAM Mario either moves up or down, value is greater or less.) Does it work for CPU-cycled RNGs, because it's not possible to find RAM Adresses, which are constant?
You are correct. Finding Mario's height with this function is overkill-- I only mentioned it as a simple example. Try to think of a RAM value that isn't so obvious As for CPU-cycled RNGs, I'm afraid I don't understand the question.
I mean RNGs, that change value by a event e.g. enemy is shooting, so this means the value could be constant.
It might be possible to find such an RNG with this script. You would hold the mouse at a certain level until an enemy fires (assuming you know what triggers the RNG change), then move it to an arbitrary point and advance a few more frames. I think the change counter in the default RAM search would also do the trick.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
TASeditor wrote:
Is this really needed? The example with Super Mario Bros. height position listen like a lot of work, compared with the normal RAM Search (just a few points to register by saying, with help of the graphical expression of the RAM Mario either moves up or down, value is greater or less.) Does it work for CPU-cycled RNGs, because it's not possible to find RAM Adresses, which are constant?
You are correct. Finding Mario's height with this function is overkill-- I only mentioned it as a simple example. Try to think of a RAM value that isn't so obvious As for CPU-cycled RNGs, I'm afraid I don't understand the question.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Moderators: This is not just a copy of the first post. I am including this code here as a record of earlier versions of the script. As the script is updated, I will add their code to this topic as well as editing the original post. I will delete this message after we create our next version of the script. Download Version 1.0 here! Version 1.0 Current features • A vertical bar to accept user input. Clicking the bar while frame advancing generates the input vector. • Shift-clicking to move the bar. • Resize boxes above and below the bar. • GUI output that displays top-candidate RAM addresses, their "score" (cos2(theta)), and their current value. • Cross-compatibility with several emulators. The script has been tested to work in VBA, FCEUX, and SNES9x. It is probably also compatible with Gens. • Excluded mirror values. Many RAM addresses have mirrored values. Including them wastes processing power and clutters up the GUI output. • Pretty rainbow colors in the input bar. (Does not work in FCEUX.) Features to be implemented Because FatRatKnight and I are working on different aspects of the script, I will split the improvements into technical and GUI categories. Technical improvements: • Pre-filtering. This currently falls under two categories: - Change pre-filtering. This would be similar to the default RAM Search's change count feature. It would allow the user to exclude addresses that have or haven't changed, according to what they expect from the address they're searching for. - Dot product pre-filtering. This would work exactly like the program's basic function except that addresses that aren't a sufficiently good match would be strictly excluded from future searches. This could be set by a number of different methods: keep the top x% of matches, keep the top n matches, or keep only matches whose score is above a certain threshold. • Downsampling. This means recording the RAM addresses only once every n frames. • Derivatives. Sometimes you do not know whether the value you are looking for is a function or its derivative. An example of this (and one of the things that inspired me to work on this script) is the hilly terrain in the racing game Skitchin': Is it stored as a height for graphical purposes or as a slope, which affects the player's acceleration directly? • Time lag. Sometimes there is a delay between what the user sees on the screen and what the RAM address is doing internally. In these instances, we want to compare the two vectors offset by some time delay (with a normalization factor thrown in). This is known as the cross-correlation. It is very computationally intensive, however, and cannot reasonably be implemented without pre-filtering. Also, the cross-correlation is most quickly computed by taking the discrete Fourier transform of the vectors, although it may only be faster to do so if there is an especially large time lag. • Words and other series of bytes. Sometimes we're not looking for a single byte. Endianness should also be considered. • Wrap-around. For periodic variables (e.g., the position of fire bars in Super Mario Bros. or the progression of the music), the value can jump from at or near 255 back to 0 (or vice versa). The user would have to set a threshold below which wrap-around would be applied. • Signed values. Currently, all values are presumed unsigned. • Expanded compatibility. This script only works with 8- and 16-bit consoles, as we do not yet know the memory map of other consoles. • An improved sorting algorithm. For ease of programming and getting this script published promptly, an insertion sort was implemented. This should be changed to a merge sort or other sort that executes in O(n*log(n)). GUI improvements: • Graphical tie-in of the above-mentioned improvements. • Options to change the vertical input bar into a horizontal bar. A circle could also be used. • A menu system that will complement keyboard shortcuts for features. • Arrows signifying the resize buttons. • Context-sensitive cursor icons. • Text boxes for user input. • Customizable output, including less or more information and fewer or more matches. For long lists of matches, the user should also be able to scroll through results. • Continuing to gather user input after the cursor leaves the bar. We also wish to further break the script up into smaller functions for ease of editing. The scripts Download BasicFunctions.lua
Language: lua

local function boolto10(boole) if boole then return 1 else return 0 end end
Download VectorOps.lua
Language: lua

local function getaverage(vector) local total=0 local n=0 for k,v in pairs(vector) do total=total+v n=n+1 end local average=total/n return average end local function detrend(vector) local average=getaverage(vector) for k,v in pairs(vector) do vector[k]=v-average end return vector end local function dotproduct(v1,v2) local total=0 for k,v in pairs(v1) do total=total+v*v2[k] end return total end local function getmag(vector) local magsquared=dotproduct(vector,vector) local magnitude=math.sqrt(magsquared) return magnitude end local function normalize(vector) local magnitude=getmag(vector) if magnitude>0 then for k,v in pairs(vector) do vector[k]=v/magnitude end end return vector end
Download Sorts.lua
Language: lua

--This function executes a selection sort on a vector, finding the best N values and organizing them into a table. --Note that the computation time is O(n^2). For efficiency, consider merge sort, whose speed is O(n*log(n)). --The advantage of selection sort is that it's easy to program and is probably still quite efficient as long as N is small. local function selectionsort(vector,N) local sorted={} for i=1,N do sorted[i]={-1,0} for k,v in pairs(vector) do if v>sorted[i][2] then sorted[i]={k,v} end end vector[sorted[i][1]]=0 end return sorted end --Insertion sort provided by FatRatKnight. SortedTable={} function InsertVal(v) local n=#SortedTable+1 while n>1 do if v<SortedTable[n-1] then break end SortedTable[n]=SortedTable[n-1] n=n-1 end SortedTable[n]=v end
Download YummyButton.lua
Language: lua

--[[ Welcome to my Buttons script! To start, put in the following lines: require("thisScript") NewBtn{x= 1, y= 1, fn= function() ((Various code here)) end} And all of a sudden, you have this bluish button on the top left corner of the emulator window, once you run the script. Use the mouse to click the button. The code you put in will happen. Now, there are ways to configure these buttons to your desires. For one thing, make as many NewBtn objects as you like. This script will keep track of 'em. For another, change that x and y up there if you don't like the NewBtn being stuck in the top-left corner. Finally, there's various options you can use to tweak your NewBtn desires. REQUIRED: x - X position. Where is the button horizontally? y - Y position. Where do we put it vertically? fn - Function. The button had better do something!! Optional: text - Cute text to display on button. May size button automatically. x1 - Identical to the otherwise required x. If both present, x has priority x2 - The right side of the button y1 - Identical to y. y1 can take y's place, but if you make both, y is used y2 - The bottom side of the button width - Width of button. Would interfere with x2, but x2 takes priority. height- Height of button. Use if you don't feel like using y2. color - General color scheme of the button. This affects all aspects of color. MBC - Main Border Color. "Main" is when your mouse isn't over the button. MFC - Main Fill Color. ... I use "Main" for lack of better words... SBC - Sub Border Color. "Sub" is used when you're mousing over the button. SFC - Sub Fill Color. Anyone have better words for me? tag - Spare variable. Included functions: draw - Draws the button. Don't bother using it, it's automatically called. recolor- Modifies the color scheme. Takes in one value depicting base color. move - Moves the button so that the top-left corner matches the x,y input. While it is packaged for simple use as is, the more savvy programmers may want to read on. I pass the button object to the clicked button function as the only parameter. I can't think of any real way of passing variables you want for it, but I added the "tag" variable for this purpose. Technically, after the button is constructed, you can add new parameters to the button object itself, but the "tag" variable seemed like a convenient thing to add to the constructor. In addition, I put the HandleBtns function in gui.register by default, but go ahead and call it in another function and replace the gui.register callback. This function will return whatever the button function returns, so if it is in your interest to have the button function return something, it will do so through _FRK_Btn.HandleBtns. The NewBtn constructor returns the button object it created as a table for you to mess around with, should you have a reason to. Maybe you want to move it around or change its colors, who knows. Finally, _FRK_Btn.MB holds all my buttons. You can, in the middle of creating new buttons, set another variable (MB2, perhaps?), set _FRK_Btn.MB = {}, then create more buttons. You'll have two different sets of buttons which you can switch between, but you'll have to make your own functions to do the switch. But if you're not experienced in lua programming, none of the previous four paragraphs are of any interest to you. The simple use of these buttons are good enough for many uses. ]]-- _FRK_Btn= {} local EMUCHWID= 6 local EMUCHHGT= 8 if stylus then EMUCHWID= 6; EMUCHHGT= 8 elseif snes9x then EMUCHWID= 4; EMUCHHGT= 6 elseif VBA then EMUCHWID= 4; EMUCHHGT= 6 end _FRK_Btn.ClickType= "leftclick" _FRK_Btn.MB= {} keys, lastkeys= {}, {} function UpdateKeys() lastkeys= keys; keys= input.get() end function Press(k) return keys[k] and not lastkeys[k] end --***************************************************************************** function _FRK_Btn.FindBtn() --***************************************************************************** if (keys.xmouse == lastkeys.xmouse) and (keys.ymouse == lastkeys.ymouse) then return end local X,Y= keys.xmouse, keys.ymouse for i= 1, #_FRK_Btn.MB do -- linear scan, darn it! local n= _FRK_Btn.MB[i] if (Y >= n.y1) and (Y <= n.y2) and (X >= n.x1) and (X <= n.x2) then return i end end return -1 end --***************************************************************************** function _FRK_Btn.ClickBtn(id) --***************************************************************************** if id and _FRK_Btn.MB[id] then return _FRK_Btn.MB[id]:fn() end end --***************************************************************************** function _FRK_Btn.DrawUnsel(B) --***************************************************************************** gui.box(B.x1, B.y1, B.x2, B.y2, B.MFC, B.MBC) gui.text(B.xt, B.yt, B.text) end --***************************************************************************** function _FRK_Btn.DrawSel(B) --***************************************************************************** gui.box(B.x1, B.y1, B.x2, B.y2, B.SFC, B.SBC) gui.text(B.xt, B.yt, B.text) end --***************************************************************************** function _FRK_Btn.ShowBtns() --***************************************************************************** B= _FRK_Btn.MB for i= 1, #B do B[i]:draw() end end --***************************************************************************** function _FRK_Btn.Recolor(B,color) --***************************************************************************** -- Does not like strings. Still will accept them, however. local t= type(color) if t == "string" then -- Uh, no. No tricky color stuff. Sorry. B.MBC= Cns.color B.SBC= Cns.color B.MFC= 0x00000040 B.SFC= 0xC0C0C0C0 --Oh, not a string! Better be table or number, please. else if t == "table" then color= 0 local q= color.r or color[1] if q then color= bit.lshift(q,24) end q= color.g or color[2] if q then color= bit.bor(color,bit.lshift(q,16)) end q= color.b or color[3] if q then color= bit.bor(color,bit.lshift(q, 8)) end q= color.a or color[4] if q then color= bit.bor(color,q) else color= bit.bor(color,0xFF) end end B.MBC= color B.MFC= bit.rshift(bit.band(color,0xFEFEFEFE),1) B.SBC= bit.bor(color+bit.band(0xFFFFFFFF-color,0xFEFEFEFE)/2,0xFF) B.SFC= color end end --***************************************************************************** function _FRK_Btn.Move(B, x,y) --***************************************************************************** -- Moves the whole button for you! if x then local XDiff= x - B.x1 B.x1= x B.x2= B.x2 + XDiff B.xt= B.xt + XDiff end if y then local YDiff= y - B.y1 B.y1= y B.y2= B.y2 + YDiff B.yt= B.yt + YDiff end end --***************************************************************************** function _FRK_Btn.NewBtn(Cns) --***************************************************************************** -- the button constructor! --Insert universal function pointers while creating a new table. local NewTbl= { draw= _FRK_Btn.DrawUnsel, recolor= _FRK_Btn.Recolor, move= _FRK_Btn.Move } --Insert presumed information. NewTbl.fn= Cns.fn NewTbl.x1= Cns.x or Cns.x1 NewTbl.y1= Cns.y or Cns.y1 NewTbl.tag= Cns.tag --Check if user defined the right side. if Cns.x2 then NewTbl.x2= math.max(NewTbl.x1,Cns.x2) NewTbl.x1= math.min(NewTbl.x1,Cns.x2) elseif Cns.width then NewTbl.x2= NewTbl.x1 + Cns.width end --Check if user defined the bottom side. if Cns.y2 then NewTbl.y2= math.max(NewTbl.y1,Cns.y2) NewTbl.y1= math.min(NewTbl.y1,Cns.y2) elseif Cns.height then NewTbl.y2= NewTbl.y1 + Cns.height end --Look for text. Do tricky stuff based on where to put the darn text or --how to resize the button. if Cns.text then NewTbl.text= tostring(Cns.text) local len= #NewTbl.text * EMUCHWID if NewTbl.x2 then NewTbl.xt= math.floor((NewTbl.x1 + NewTbl.x2 - len)/2) else NewTbl.x2= NewTbl.x1 + len + 3 NewTbl.xt= NewTbl.x1 + 2 end if NewTbl.y2 then NewTbl.yt= math.floor((NewTbl.y1 + NewTbl.y2 - 8)/2) else NewTbl.y2= NewTbl.y1 + EMUCHHGT + 4 NewTbl.yt= NewTbl.y1 + 2 end else NewTbl.text= "" NewTbl.xt= NewTbl.x1 NewTbl.yt= NewTbl.y1 if not NewTbl.x2 then NewTbl.x2= NewTbl.x1 + 8 end if not NewTbl.y2 then NewTbl.y2= NewTbl.y1 + 8 end end --Mess with colors! if Cns.color then NewTbl:recolor(Cns.color) else NewTbl.MBC= Cns.MBC or 0x0020FFC0 NewTbl.MFC= Cns.MFC or 0x00000040 NewTbl.SBC= Cns.SBC or 0x0080FFFF NewTbl.SFC= Cns.SFC or 0x2000C0C0 end --Finally add the button to our MB array. local index= #(_FRK_Btn.MB)+1 _FRK_Btn.MB[index]= NewTbl --Here's the button object in case you want to handle it yourself. return NewTbl end local ID= -1 --***************************************************************************** function _FRK_Btn.HandleBtns() --***************************************************************************** UpdateKeys() local id= _FRK_Btn.FindBtn() if id then if ID ~= -1 then _FRK_Btn.MB[ID].draw= _FRK_Btn.DrawUnsel end if id ~= -1 then _FRK_Btn.MB[id].draw= _FRK_Btn.DrawSel end ID= id end _FRK_Btn.ShowBtns() if Press(_FRK_Btn.ClickType) and (ID ~= -1) then return _FRK_Btn.ClickBtn(ID) end end --============================================================================= if not FRK_NoAuto then --============================================================================= gui.register(_FRK_Btn.HandleBtns) NewBtn= _FRK_Btn.NewBtn end
Download SmartRAMSearch.lua
Language: lua

dofile("BasicFunctions.lua") dofile("VectorOps.lua") dofile("Sorts.lua") --Certain parameters are emulator-specific. --In particular, we need to know the range of relevant RAM addresses and place the bar where it is visible within the window. local function getsystem() if FCEU then --NES byterange={ {0x0000,0x07FF}, {0x2000,0x2008}, {0x4000,0x7FFF}} L,U,R,D= 180,50,189,149 SCRBOTTOM,SCRRIGHT=239,255 elseif vba then --GBx byterange={ {0x8000,0xDFFF}, {0xFF80,0xFFFF}} L,U,R,D= 140,10,149,109 SCRBOTTOM,SCRRIGHT=143,159 elseif snes9x then --SNES byterange={ {0x7E0000,0x7FFFFF}} L,U,R,D= 180,50,189,149 elseif gens then --Genesis (and similar) byterange={ {0x000000,0x3FFFFF}} elseif stylus then --DS print("Compatibility pending...") else --Emulator not recognized print("Error: What emulator are you using?") end end --Initialize this stuff... getsystem() local mindex=0 range={} for i=1,#byterange do for j=byterange[i][1],byterange[i][2] do range[mindex]=j mindex=mindex+1 end end --range=0x7FFF addresses={} for i = 1,#range do addresses[range[i]]={} end X={} costhetasq={} --Converts a hue angle to RGB. Normalized to the range [0,255]. local function huetoRGB(hueangle,alpha) local R,G,B=0,0,0 if not(alpha) then alpha=0x7F end --Alpha defaults to 0x7F. hueangle=hueangle%360 local huefraction=hueangle/60-math.floor(hueangle/60) --This is a sawtooth function if hueangle>=0 and hueangle<60 then R,G,B=1,huefraction,0 elseif hueangle>=60 and hueangle<120 then R,G,B=1-huefraction,1,0 elseif hueangle>=120 and hueangle<180 then R,G,B=0,1,huefraction elseif hueangle>=180 and hueangle<240 then R,G,B=0,1-huefraction,1 elseif hueangle>=240 and hueangle<300 then R,G,B=huefraction,0,1 elseif hueangle>=300 and hueangle<360 then R,G,B=1,0,1-huefraction end R,G,B=math.floor(0xFF*R),math.floor(0xFF*G),math.floor(0xFF*B) return {R,G,B,alpha} --Output is in table form. end --############################################################################# --############################################################################# local BestTable= false local function CalcNow() for i,v in pairs(addresses) do addresses[i]=detrend(addresses[i]) addresses[i]=normalize(addresses[i]) end X=detrend(X) X=normalize(X) for i,v in pairs(addresses) do costhetasq[i]=dotproduct(X,addresses[i])^2 end BestTable=selectionsort(costhetasq,20) for i=1,#BestTable do BestTable[i][3]=memory.readbyte(BestTable[i][1]) end end local function GetData(UserInput) for i,v in pairs(addresses) do addresses[i][emu.framecount()]=memory.readbyte(i) end X[emu.framecount()]=UserInput end local function NullFn() end -- The "Do Nothing" function. --############################################################################# --############################################################################# --***************************************************************************** local function InBox(Lf,Up,Rt,Dn , x,y) --***************************************************************************** -- Just seeing if the chosen pixel resides within the chosen boundaries. -- Does not like it if the "box" you specify has reversed x- or y-axis. return (x >= Lf) and (x <= Rt) and (y >= Up) and (y <= Dn) 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 --############################################################################# --############################################################################# --Let's construct buttons! Yay! local Flag_DrawRainbow= false -- Bah, hate flags. Any workarounds? local ValFindFn, MoveMe FRK_NoAuto= true dofile("YummyButton.lua") --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% local InputBar= _FRK_Btn.NewBtn{ --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% x1= 5, y1= 15, x2= 14, y2= 99, MBC= 0x00FF00FF, MFC= 0, SBC= 0x7FFF7FFF, SFC= 0, ------------------------------------------------------------------------------- fn= function(B) ------------------------------------------------------------------------------- if keys.shift then return MoveMe end return ValFindFn end } local function rt(B) return B.tag end --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% local StretchBoxUp= _FRK_Btn.NewBtn{ --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% x1= InputBar.x1-5, y1= InputBar.y1-15, x2= InputBar.x2+5, y2= InputBar.y1+5, MBC= "#FFFFFF40", MFC= 0, SBC= "#FFFFFFC0", SFC= 0, fn= function(B) return B.tag end } ------------------------------------------------------------------------------- StretchBoxUp.tag= function() ------------------------------------------------------------------------------- local DeltaY= keys.ymouse - lastkeys.ymouse DeltaY= math.max(DeltaY, -InputBar.y1) --Don't put beyond screen edge! DeltaY= math.min(DeltaY, InputBar.y2-InputBar.y1-10) InputBar.y1= InputBar.y1+DeltaY StretchBoxUp.y1= StretchBoxUp.y1+DeltaY StretchBoxUp.y2= StretchBoxUp.y2+DeltaY end --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% local StretchBoxDn= _FRK_Btn.NewBtn{ --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% x1= InputBar.x1-5, y1= InputBar.y2-5, x2= InputBar.x2+5, y2= InputBar.y2+15, MBC= "#FFFFFF40", MFC= 0, SBC= "#FFFFFFC0", SFC= 0, fn= function(B) return B.tag end } ------------------------------------------------------------------------------- StretchBoxDn.tag= function() ------------------------------------------------------------------------------- local DeltaY= keys.ymouse - lastkeys.ymouse DeltaY= math.max(DeltaY, InputBar.y1-InputBar.y2+10) DeltaY= math.min(DeltaY, SCRBOTTOM - InputBar.y2) --Screen edge InputBar.y2= InputBar.y2+DeltaY StretchBoxDn.y1= StretchBoxDn.y1+DeltaY StretchBoxDn.y2= StretchBoxDn.y2+DeltaY end --***************************************************************************** ValFindFn= function() --***************************************************************************** Flag_DrawRainbow= true end --***************************************************************************** MoveMe= function() --***************************************************************************** local DeltaX= keys.xmouse - lastkeys.xmouse local DeltaY= keys.ymouse - lastkeys.ymouse DeltaX= math.min(DeltaX, SCRRIGHT - InputBar.x2) DeltaY= math.min(DeltaY, SCRBOTTOM - InputBar.y2) DeltaX= math.max(DeltaX, -InputBar.x1) DeltaY= math.max(DeltaY, -InputBar.y1) InputBar:move(InputBar.x1+DeltaX, InputBar.y1+DeltaY) StretchBoxUp:move(StretchBoxUp.x1+DeltaX, StretchBoxUp.y1+DeltaY) StretchBoxDn:move(StretchBoxDn.x1+DeltaX, StretchBoxDn.y1+DeltaY) end ------------------------------------------------------------------------------- InputBar.GetVal= function(B,k) ------------------------------------------------------------------------------- local h= B.y2 - B.y1 local n= k.ymouse - B.y1 return (1 - n/h) * 1024 -- The multiplication is fairly arbitrary end ------------------------------------------------------------------------------- InputBar.DrawSel= function(B) --Override standard button code ------------------------------------------------------------------------------- gui.box(B.x1, B.y1, B.x2, B.y2, B.SFC, B.SBC) -- May as well box ourself Flag_DrawRainbow= true -- Would rather make only one call, not thousands. end ------------------------------------------------------------------------------- InputBar.DrawRainbow= function(B,y) ------------------------------------------------------------------------------- y=B.y1*boolto10(y<=B.y1) + y*boolto10(B.y1<y and y<=B.y2) + B.y2*boolto10(B.y2<y) for i=y,B.y2 do local hueangle=240 - 240/(B.y1-B.y2)*(i-B.y2) local RGBTable=huetoRGB(hueangle,0x7F) gui.line(B.x1,i,B.x2,i,RGBTable) end gui.line(B.x1,y,B.x2,y,"white") end ------------------------------------------------------------------------------- InputBar.MouseHit= function(B,k) ------------------------------------------------------------------------------- if InBox(B.x1,B.y1,B.x2,B.y2, k.xmouse,k.ymouse) then return InputBar:GetVal(k) end return nil end --############################################################################# --############################################################################# local ShowTable= false --***************************************************************************** local function PaintStat(T,y) --***************************************************************************** gui.box(2,y-1,70,y+5,0x000000FF,0x000000FF) local temp= T[1] -- Address for i= 0, 3 do local n= bit.band(bit.rshift(temp,i*4),0xF) Draw[n](16-4*i, y, -1) end temp= T[2]*10000 -- Certainty local color= "red" if temp >= 900 then color= "white" elseif temp >= 800 then color= "green" elseif temp >= 700 then color= "yellow" end __DN_AnyBase(44, y, temp, color, 0, 10) temp= memory.readbyte(T[1]) -- Memory value __DN_AnyBase(68, y, temp, "white", 0, 10) end --############################################################################# --############################################################################# --Keyboard (and indirectly, location where mouse is stored) --local keys, lastkeys= input.get(), input.get() --local function UpdateKeys() lastkeys= keys; keys= input.get() end --local function Press(k) return keys[k] and (not lastkeys[k]) end -- Functions are supplied by YummyButtons.lua. Neato. local KF= {} --***************************************************************************** local function KeyFunctions() --***************************************************************************** for key,v in pairs(keys) do if not lastkeys[key] then if KF[key] then KF[key]() end end end end ------------------------------------------------------------------------------- KF["space"]= function() -- The CALCULATE NOW function. ------------------------------------------------------------------------------- CalcNow() end ------------------------------------------------------------------------------- KF["home"]= function() -- Display toggle ------------------------------------------------------------------------------- ShowTable= not ShowTable end --############################################################################# --############################################################################# local Val= nil --***************************************************************************** local function ControlHUD() --***************************************************************************** gui.box(L,U,R,D,0,0x00FF00FF) -- See if we click the all-important box if InBox(L,U,R,D , keys.xmouse , keys.ymouse) then gui.line(L,keys.ymouse,R,keys.ymouse, -1) local h= D - U -- Get height of box local n= (keys.ymouse - U) -- Figure out where mouse is relative to box Val= (1 - n/h) * 999 -- Linear scoring of mouse position. gui.text(200,210,math.floor(Val)) else Val= nil end if keys.shift and keys.leftclick then L= L + keys.xmouse - lastkeys.xmouse R= R + keys.xmouse - lastkeys.xmouse U= U + keys.ymouse - lastkeys.ymouse D= D + keys.ymouse - lastkeys.ymouse end end local HoldFn --***************************************************************************** local function HandleGui() --***************************************************************************** --Main handler. Things to do, yes. local Fn= _FRK_Btn.HandleBtns() HoldFn= Fn or HoldFn if not keys.leftclick then HoldFn= nil end if HoldFn then HoldFn() end KeyFunctions() -- Maybe display list of results. This is only a glorified print() so far. -- There are planned alterations to do... if ShowTable and BestTable then for i= 1, #BestTable do PaintStat(BestTable[i],9*i) end end -- Paint the rainbow! if Flag_DrawRainbow then InputBar:DrawRainbow(keys.ymouse) Flag_DrawRainbow= false end end gui.register(HandleGui) --***************************************************************************** local function CheckImmediateInput() --***************************************************************************** local ImmediateInput= input.get() if ImmediateInput.leftclick and not ImmediateInput.shift then return InputBar:MouseHit(ImmediateInput) end end --***************************************************************************** local function HandleBefore() --***************************************************************************** --Handle frame-advancey stuff. local Val= CheckImmediateInput() if Val then GetData(Val) end end emu.registerbefore(HandleBefore)
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Download the latest version here! Quick instructions As per DarkKobold's request, I'm including a basic guide at the top of this post. These are just bare-bones instructions to get you started. The full technical description and detailed instructions are after all these purdy pictures. Introduction If you're anything like me, you've encountered some difficulties with emulators' native RAM Search functions. Although the built-in RAM Search offers precision and comparative ease when you know just what to look for, oftentimes the RAM address you seek is somewhat ill-defined. Everyone has experience narrowing down a RAM address until the search has to be reset or, even worse, making some small mistake that cannot be undone. That is why FatRatKnight and I are proud to present Smart RAM Searching. This Lua script aims to make RAM searching intuitive, internally intelligent, and externally idiot-proof. The mathematical premise is simple if you have some vector algebra under your belt. We take a user's mouse movements with each frame as the input vector, let's call it v. This vector and vectors representing all RAM addresses (denote them by w) for each frame are then detrended (their average value subtracted) and normalized (divided by their magnitude). The program then takes the dot product between the normalized input vector and the normalized RAM address vectors. Its output is a list of RAM addresses that maximize the magnitude of this dot product, since
v.w=|v|*|w|*cos(theta)
   =cos(theta)             (for unit vectors)
where the period denotes the dot product. The closer cos^2(theta) is to 1, the closer the user's input is to being just a multiple of the RAM address's value. Incidentally, this method also minimizes the "residual sum of squares" (the sum of the squares of the differences between the two vectors). Why use this program? This program has several advantages over the default RAM Search function: • It is insensitive to the address's range. It does not matter whether the address's value varies between 0 and 10, 197 and 235, or 0 and 256. As long as the user faithfully tracks its contour, the script will pick out the best match. • It is insensitive to sign differences. This is especially useful for tracking things like the y coordinate. Oftentimes, the y coordinate decreases with increasing height. • It is relatively insensitive to small human errors. Because the user's input is qualitative, small errors (such as misplacing the mouse for a single frame) will not appreciably affect the program's output. Note that the same applies to the RAM address itself, which may (for whatever reason) fluctuate in a manner inconsistent with what the user perceives. This program also has some disadvantages compared with the default RAM Search function: • It cannot find RAM addresses whose values are constant. This is because the vectors are detrended. On the other hand, if the value is constant, why would you even attempt to use this script instead of using the default RAM Search or, better yet, just writing the value down? • It offers no real advantages if the value in question is known. For example, if enemy HP is displayed on the screen, that value can simply be input into RAM Search to efficiently find its address. • It is slow. In its current implementation, this script typically takes a few seconds to execute a RAM search in FCEUX if the user's input is about 100 frames. That is because it must compare the user's input with every RAM address for every frame. The script is best used with FCEUX or Game Boy games in VBA, both of which have comparatively less RAM than other systems. FatRatKnight and I are working on some pre-filtering options that will make this program run much faster and allow it to be used efficiently in other emulators. Current features • A vertical bar to accept user input. Clicking the bar while frame advancing generates the input vector. • Shift-clicking to move the bar. • Resize boxes above and below the bar. • GUI output that displays top-candidate RAM addresses, their "score" (cos2(theta)), and their current value. • Cross-compatibility with several emulators. The script has been tested to work in VBA, FCEUX, and SNES9x. It is probably also compatible with Gens. • Excluded mirror values. Many RAM addresses have mirrored values. Including them wastes processing power and clutters up the GUI output. • Pretty rainbow colors in the input bar. (Does not work in FCEUX.) Features to be implemented Because FatRatKnight and I are working on different aspects of the script, I will split the improvements into technical and GUI categories. Technical improvements: • Pre-filtering. This currently falls under two categories: - Change pre-filtering. This would be similar to the default RAM Search's change count feature. It would allow the user to exclude addresses that have or haven't changed, according to what they expect from the address they're searching for. - Dot product pre-filtering. This would work exactly like the program's basic function except that addresses that aren't a sufficiently good match would be strictly excluded from future searches. This could be set by a number of different methods: keep the top x% of matches, keep the top n matches, or keep only matches whose score is above a certain threshold. • Downsampling. This means recording the RAM addresses only once every n frames. • Derivatives. Sometimes you do not know whether the value you are looking for is a function or its derivative. An example of this (and one of the things that inspired me to work on this script) is the hilly terrain in the racing game Skitchin': Is it stored as a height for graphical purposes or as a slope, which affects the player's acceleration directly? • Time lag. Sometimes there is a delay between what the user sees on the screen and what the RAM address is doing internally. In these instances, we want to compare the two vectors offset by some time delay (with a normalization factor thrown in). This is known as the cross-correlation. It is very computationally intensive, however, and cannot reasonably be implemented without pre-filtering. Also, the cross-correlation is most quickly computed by taking the discrete Fourier transform of the vectors, although it may only be faster to do so if there is an especially large time lag. • Words and other series of bytes. Sometimes we're not looking for a single byte. Endianness should also be considered. • Wrap-around. For periodic variables (e.g., the position of fire bars in Super Mario Bros. or the progression of the music), the value can jump from at or near 255 back to 0 (or vice versa). The user would have to set a threshold below which wrap-around would be applied. • Signed values. Currently, all values are presumed unsigned. • Expanded compatibility. This script only works with 8- and 16-bit consoles, as we do not yet know the memory map of other consoles. • An improved sorting algorithm. For ease of programming and getting this script published promptly, an insertion sort was implemented. This should be changed to a merge sort or other sort that executes in O(n*log(n)). GUI improvements: • Graphical tie-in of the above-mentioned improvements. • Options to change the vertical input bar into a horizontal bar. A circle could also be used. • A menu system that will complement keyboard shortcuts for features. • Arrows signifying the resize buttons. • Context-sensitive cursor icons. • Text boxes for user input. • Customizable output, including less or more information and fewer or more matches. For long lists of matches, the user should also be able to scroll through results. • Continuing to gather user input after the cursor leaves the bar. • Manual override of game system and/or screen resolution. GBx, SGB, and GBA games all use different screen resolutions and there seems to be no easy way to differentiate between which game is which in Lua. The same goes for PSX games, which use several different resolutions. Sega games may use different resolutions and they also have different memory maps depending on the system, according to marzojr. The user should be able to toggle between pre-set screen resolutions. • Fix the slowdown that occurs when results are displayed in Gens (and other emulators?). We also wish to further break the script up into smaller functions for ease of editing. How to use the script Although the script is designed to be very intuitive, there are a few basic instructions. First, download all the scripts posted below and place them in the same directory. Run the script SmartRAMSearch.lua in the emulator of your choice (preferably FCEUX or VBA). Either immediately or after frame advancing once, a green rectangle (the user input bar) should appear. This bar can be moved by holding the shift key and then clicking and dragging it. It can also be resized by clicking and dragging the boxes above and below it. Do something in the game that will affect the RAM address you are looking for in a predictable way. (It is suggested you make a movie and work from that.) While the RAM address is changing, click and hold the bar and then frame advance. Continue to frame advance while the mouse is still held. As you frame advance, move the mouse up and down with the contour you think the RAM address's value is following. For example, if you wish to know the RAM address corresponding to Mario's height in Super Mario Bros., just follow him with your cursor while you execute a jump. Input will not be accepted if your cursor leaves the bar. Once you have collected data for many frames (a few dozen to a few hundred), press the spacebar to have the program calculate the closest-matching RAM addresses. To display these addresses, press the "home" key. The first column contains the RAM addresses, sorted by how closely they matched with your input, the second column contains their "score", which is just cos^2(theta) times 10,000, and the third column contains their current values. What we need from you • Your encouragement. • Your constructive criticism. • Your suggestions. • Your success stories. If this topic stagnates, then we will assume there is no demand for this script and stop updating it. The scripts As mentioned above, you should download these scripts to the same directory. Open the game of your choice and run "SmartRAMSearch.lua". Download BasicFunctions.lua
Language: lua

function boolto10(boole) if boole then return 1 else return 0 end end
Download VectorOps.lua
Language: lua

function getaverage(vector) local total=0 local n=0 for k,v in pairs(vector) do total=total+v n=n+1 end local average=total/n return average end function detrend(vector) local average=getaverage(vector) for k,v in pairs(vector) do vector[k]=v-average end return vector end function dotproduct(v1,v2) local total=0 for k,v in pairs(v1) do total=total+v*v2[k] end return total end function getmag(vector) local magsquared=dotproduct(vector,vector) local magnitude=math.sqrt(magsquared) return magnitude end function normalize(vector) local magnitude=getmag(vector) if magnitude>0 then for k,v in pairs(vector) do vector[k]=v/magnitude end end return vector end
Download Sorts.lua
Language: lua

--This function executes a selection sort on a vector, finding the best N values and organizing them into a table. --Note that the computation time is O(n^2). For efficiency, consider merge sort, whose speed is O(n*log(n)). --The advantage of selection sort is that it's easy to program and is probably still quite efficient as long as N is small. function selectionsort(vector,N) local sorted={} for i=1,N do sorted[i]={-1,0} for k,v in pairs(vector) do if v>sorted[i][2] then sorted[i]={k,v} end end vector[sorted[i][1]]=0 end return sorted end --Insertion sort provided by FatRatKnight. SortedTable={} function InsertVal(v) local n=#SortedTable+1 while n>1 do if v<SortedTable[n-1] then break end SortedTable[n]=SortedTable[n-1] n=n-1 end SortedTable[n]=v end
Download YummyButton.lua
Language: lua

--[[ Welcome to my Buttons script! To start, put in the following lines: require("thisScript") NewBtn{x= 1, y= 1, fn= function() ((Various code here)) end} And all of a sudden, you have this bluish button on the top left corner of the emulator window, once you run the script. Use the mouse to click the button. The code you put in will happen. Now, there are ways to configure these buttons to your desires. For one thing, make as many NewBtn objects as you like. This script will keep track of 'em. For another, change that x and y up there if you don't like the NewBtn being stuck in the top-left corner. Finally, there's various options you can use to tweak your NewBtn desires. REQUIRED: x - X position. Where is the button horizontally? y - Y position. Where do we put it vertically? fn - Function. The button had better do something!! Optional: text - Cute text to display on button. May size button automatically. x1 - Identical to the otherwise required x. If both present, x has priority x2 - The right side of the button y1 - Identical to y. y1 can take y's place, but if you make both, y is used y2 - The bottom side of the button width - Width of button. Would interfere with x2, but x2 takes priority. height- Height of button. Use if you don't feel like using y2. color - General color scheme of the button. This affects all aspects of color. MBC - Main Border Color. "Main" is when your mouse isn't over the button. MFC - Main Fill Color. ... I use "Main" for lack of better words... SBC - Sub Border Color. "Sub" is used when you're mousing over the button. SFC - Sub Fill Color. Anyone have better words for me? tag - Spare variable. Included functions: draw - Draws the button. Don't bother using it, it's automatically called. recolor- Modifies the color scheme. Takes in one value depicting base color. move - Moves the button so that the top-left corner matches the x,y input. While it is packaged for simple use as is, the more savvy programmers may want to read on. I pass the button object to the clicked button function as the only parameter. I can't think of any real way of passing variables you want for it, but I added the "tag" variable for this purpose. Technically, after the button is constructed, you can add new parameters to the button object itself, but the "tag" variable seemed like a convenient thing to add to the constructor. In addition, I put the HandleBtns function in gui.register by default, but go ahead and call it in another function and replace the gui.register callback. This function will return whatever the button function returns, so if it is in your interest to have the button function return something, it will do so through _FRK_Btn.HandleBtns. The NewBtn constructor returns the button object it created as a table for you to mess around with, should you have a reason to. Maybe you want to move it around or change its colors, who knows. Finally, _FRK_Btn.MB holds all my buttons. You can, in the middle of creating new buttons, set another variable (MB2, perhaps?), set _FRK_Btn.MB = {}, then create more buttons. You'll have two different sets of buttons which you can switch between, but you'll have to make your own functions to do the switch. But if you're not experienced in lua programming, none of the previous four paragraphs are of any interest to you. The simple use of these buttons are good enough for many uses. ]]-- _FRK_Btn= {} local EMUCHWID= 6 local EMUCHHGT= 8 if stylus then EMUCHWID= 6; EMUCHHGT= 8 elseif snes9x then EMUCHWID= 4; EMUCHHGT= 6 elseif VBA then EMUCHWID= 4; EMUCHHGT= 6 end _FRK_Btn.ClickType= "leftclick" _FRK_Btn.MB= {} keys, lastkeys= {}, {} function UpdateKeys() lastkeys= keys; keys= input.get() end function Press(k) return keys[k] and not lastkeys[k] end --***************************************************************************** function _FRK_Btn.FindBtn() --***************************************************************************** if (keys.xmouse == lastkeys.xmouse) and (keys.ymouse == lastkeys.ymouse) then return end local X,Y= keys.xmouse, keys.ymouse for i= 1, #_FRK_Btn.MB do -- linear scan, darn it! local n= _FRK_Btn.MB[i] if (Y >= n.y1) and (Y <= n.y2) and (X >= n.x1) and (X <= n.x2) then return i end end return -1 end --***************************************************************************** function _FRK_Btn.ClickBtn(id) --***************************************************************************** if id and _FRK_Btn.MB[id] then return _FRK_Btn.MB[id]:fn() end end --***************************************************************************** function _FRK_Btn.DrawUnsel(B) --***************************************************************************** gui.box(B.x1, B.y1, B.x2, B.y2, B.MFC, B.MBC) gui.text(B.xt, B.yt, B.text) end --***************************************************************************** function _FRK_Btn.DrawSel(B) --***************************************************************************** gui.box(B.x1, B.y1, B.x2, B.y2, B.SFC, B.SBC) gui.text(B.xt, B.yt, B.text) end --***************************************************************************** function _FRK_Btn.ShowBtns() --***************************************************************************** B= _FRK_Btn.MB for i= 1, #B do B[i]:draw() end end --***************************************************************************** function _FRK_Btn.Recolor(B,color) --***************************************************************************** -- Does not like strings. Still will accept them, however. local t= type(color) if t == "string" then -- Uh, no. No tricky color stuff. Sorry. B.MBC= Cns.color B.SBC= Cns.color B.MFC= 0x00000040 B.SFC= 0xC0C0C0C0 --Oh, not a string! Better be table or number, please. else if t == "table" then color= 0 local q= color.r or color[1] if q then color= bit.lshift(q,24) end q= color.g or color[2] if q then color= bit.bor(color,bit.lshift(q,16)) end q= color.b or color[3] if q then color= bit.bor(color,bit.lshift(q, 8)) end q= color.a or color[4] if q then color= bit.bor(color,q) else color= bit.bor(color,0xFF) end end B.MBC= color B.MFC= bit.rshift(bit.band(color,0xFEFEFEFE),1) B.SBC= bit.bor(color+bit.band(0xFFFFFFFF-color,0xFEFEFEFE)/2,0xFF) B.SFC= color end end --***************************************************************************** function _FRK_Btn.Move(B, x,y) --***************************************************************************** -- Moves the whole button for you! if x then local XDiff= x - B.x1 B.x1= x B.x2= B.x2 + XDiff B.xt= B.xt + XDiff end if y then local YDiff= y - B.y1 B.y1= y B.y2= B.y2 + YDiff B.yt= B.yt + YDiff end end --***************************************************************************** function _FRK_Btn.NewBtn(Cns) --***************************************************************************** -- the button constructor! --Insert universal function pointers while creating a new table. local NewTbl= { draw= _FRK_Btn.DrawUnsel, recolor= _FRK_Btn.Recolor, move= _FRK_Btn.Move } --Insert presumed information. NewTbl.fn= Cns.fn NewTbl.x1= Cns.x or Cns.x1 NewTbl.y1= Cns.y or Cns.y1 NewTbl.tag= Cns.tag --Check if user defined the right side. if Cns.x2 then NewTbl.x2= math.max(NewTbl.x1,Cns.x2) NewTbl.x1= math.min(NewTbl.x1,Cns.x2) elseif Cns.width then NewTbl.x2= NewTbl.x1 + Cns.width end --Check if user defined the bottom side. if Cns.y2 then NewTbl.y2= math.max(NewTbl.y1,Cns.y2) NewTbl.y1= math.min(NewTbl.y1,Cns.y2) elseif Cns.height then NewTbl.y2= NewTbl.y1 + Cns.height end --Look for text. Do tricky stuff based on where to put the darn text or --how to resize the button. if Cns.text then NewTbl.text= tostring(Cns.text) local len= #NewTbl.text * EMUCHWID if NewTbl.x2 then NewTbl.xt= math.floor((NewTbl.x1 + NewTbl.x2 - len)/2) else NewTbl.x2= NewTbl.x1 + len + 3 NewTbl.xt= NewTbl.x1 + 2 end if NewTbl.y2 then NewTbl.yt= math.floor((NewTbl.y1 + NewTbl.y2 - 8)/2) else NewTbl.y2= NewTbl.y1 + EMUCHHGT + 4 NewTbl.yt= NewTbl.y1 + 2 end else NewTbl.text= "" NewTbl.xt= NewTbl.x1 NewTbl.yt= NewTbl.y1 if not NewTbl.x2 then NewTbl.x2= NewTbl.x1 + 8 end if not NewTbl.y2 then NewTbl.y2= NewTbl.y1 + 8 end end --Mess with colors! if Cns.color then NewTbl:recolor(Cns.color) else NewTbl.MBC= Cns.MBC or 0x0020FFC0 NewTbl.MFC= Cns.MFC or 0x00000040 NewTbl.SBC= Cns.SBC or 0x0080FFFF NewTbl.SFC= Cns.SFC or 0x2000C0C0 end --Finally add the button to our MB array. local index= #(_FRK_Btn.MB)+1 _FRK_Btn.MB[index]= NewTbl --Here's the button object in case you want to handle it yourself. return NewTbl end local ID= -1 --***************************************************************************** function _FRK_Btn.HandleBtns() --***************************************************************************** UpdateKeys() local id= _FRK_Btn.FindBtn() if id then if ID ~= -1 then _FRK_Btn.MB[ID].draw= _FRK_Btn.DrawUnsel end if id ~= -1 then _FRK_Btn.MB[id].draw= _FRK_Btn.DrawSel end ID= id end _FRK_Btn.ShowBtns() if Press(_FRK_Btn.ClickType) and (ID ~= -1) then return _FRK_Btn.ClickBtn(ID) end end --============================================================================= if not FRK_NoAuto then --============================================================================= gui.register(_FRK_Btn.HandleBtns) NewBtn= _FRK_Btn.NewBtn end
Download bit.lua
Language: lua

--[[--------------- LuaBit v0.4 ------------------- a bitwise operation lib for lua. http://luaforge.net/projects/bit/ How to use: ------------------- bit.bnot(n) -- bitwise not (~n) bit.band(m, n) -- bitwise and (m & n) bit.bor(m, n) -- bitwise or (m | n) bit.bxor(m, n) -- bitwise xor (m ^ n) bit.brshift(n, bits) -- right shift (n >> bits) bit.blshift(n, bits) -- left shift (n << bits) bit.blogic_rshift(n, bits) -- logic right shift(zero fill >>>) Please note that bit.brshift and bit.blshift only support number within 32 bits. 2 utility functions are provided too: bit.tobits(n) -- convert n into a bit table(which is a 1/0 sequence) -- high bits first bit.tonumb(bit_tbl) -- convert a bit table into a number ------------------- Under the MIT license. copyright(c) 2006~2007 hanzhao (abrash_han@hotmail.com) --]]--------------- do ------------------------ -- bit lib implementions local function check_int(n) -- checking not float if(n - math.floor(n) > 0) then error("trying to use bitwise operation on non-integer!") end end local function to_bits(n) check_int(n) if(n < 0) then -- negative return to_bits(bit.bnot(math.abs(n)) + 1) end -- to bits table local tbl = {} local cnt = 1 while (n > 0) do local last = math.mod(n,2) if(last == 1) then tbl[cnt] = 1 else tbl[cnt] = 0 end n = (n-last)/2 cnt = cnt + 1 end return tbl end local function tbl_to_number(tbl) local n = table.getn(tbl) local rslt = 0 local power = 1 for i = 1, n do rslt = rslt + tbl[i]*power power = power*2 end return rslt end local function expand(tbl_m, tbl_n) local big = {} local small = {} if(table.getn(tbl_m) > table.getn(tbl_n)) then big = tbl_m small = tbl_n else big = tbl_n small = tbl_m end -- expand small for i = table.getn(small) + 1, table.getn(big) do small[i] = 0 end end local function bit_or(m, n) local tbl_m = to_bits(m) local tbl_n = to_bits(n) expand(tbl_m, tbl_n) local tbl = {} local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n)) for i = 1, rslt do if(tbl_m[i]== 0 and tbl_n[i] == 0) then tbl[i] = 0 else tbl[i] = 1 end end return tbl_to_number(tbl) end local function bit_and(m, n) local tbl_m = to_bits(m) local tbl_n = to_bits(n) expand(tbl_m, tbl_n) local tbl = {} local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n)) for i = 1, rslt do if(tbl_m[i]== 0 or tbl_n[i] == 0) then tbl[i] = 0 else tbl[i] = 1 end end return tbl_to_number(tbl) end local function bit_not(n) local tbl = to_bits(n) local size = math.max(table.getn(tbl), 32) for i = 1, size do if(tbl[i] == 1) then tbl[i] = 0 else tbl[i] = 1 end end return tbl_to_number(tbl) end local function bit_xor(m, n) local tbl_m = to_bits(m) local tbl_n = to_bits(n) expand(tbl_m, tbl_n) local tbl = {} local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n)) for i = 1, rslt do if(tbl_m[i] ~= tbl_n[i]) then tbl[i] = 1 else tbl[i] = 0 end end --table.foreach(tbl, print) return tbl_to_number(tbl) end local function bit_rshift(n, bits) check_int(n) local high_bit = 0 if(n < 0) then -- negative n = bit_not(math.abs(n)) + 1 high_bit = 2147483648 -- 0x80000000 end for i=1, bits do n = n/2 n = bit_or(math.floor(n), high_bit) end return math.floor(n) end -- logic rightshift assures zero filling shift local function bit_logic_rshift(n, bits) check_int(n) if(n < 0) then -- negative n = bit_not(math.abs(n)) + 1 end for i=1, bits do n = n/2 end return math.floor(n) end local function bit_lshift(n, bits) check_int(n) if(n < 0) then -- negative n = bit_not(math.abs(n)) + 1 end for i=1, bits do n = n*2 end return bit_and(n, 4294967295) -- 0xFFFFFFFF end local function bit_xor2(m, n) local rhs = bit_or(bit_not(m), bit_not(n)) local lhs = bit_or(m, n) local rslt = bit_and(lhs, rhs) return rslt end -------------------- -- bit lib interface bit = { -- bit operations bnot = bit_not, band = bit_and, bor = bit_or, bxor = bit_xor, rshift = bit_rshift, lshift = bit_lshift, bxor2 = bit_xor2, blogic_rshift = bit_logic_rshift, -- utility func tobits = to_bits, tonumb = tbl_to_number, } end --[[ for i = 1, 100 do for j = 1, 100 do if(bit.bxor(i, j) ~= bit.bxor2(i, j)) then error("bit.xor failed.") end end end --]]
Download SmartRAMSearch.lua
Language: lua

--Certain parameters are emulator-specific. --In particular, we need to know the range of relevant RAM addresses and place the bar where it is visible within the window. local function getsystem() --These are the default values if the emulator isn't specified. Watch out! It will take a very long time to execute and you may scroll the bar off the screen! byterange={ {0x000000,0x1FFFFF}} L,U,R,D= 180,50,189,149 SCRBOTTOM,SCRRIGHT=480,640 if FCEU then --NES byterange={ {0x0000,0x07FF}, {0x2000,0x2008}, {0x4000,0x7FFF}} L,U,R,D= 180,50,189,149 SCRBOTTOM,SCRRIGHT=239,255 elseif vba then --GBx byterange={ {0x8000,0xDFFF}, {0xFF80,0xFFFF}} L,U,R,D= 140,10,149,109 SCRBOTTOM,SCRRIGHT=143,159 elseif snes9x then --SNES byterange={ {0x7E0000,0x7FFFFF}} L,U,R,D= 180,50,189,149 SCRBOTTOM,SCRRIGHT=222,255 elseif gens then --Genesis (and similar) byterange={ {0xFF0000,0xFFFFFF}} L,U,R,D= 180,50,189,149 SCRBOTTOM,SCRRIGHT=223,319 emu=gens dofile("bit.lua") elseif pcsx then --PlayStation byterange={ {0x000000,0x1FFFFF}} L,U,R,D= 180,50,189,149 SCRBOTTOM,SCRRIGHT=480,640 elseif stylus then --DS print("Compatibility pending...") else --Emulator not recognized print("Error: What emulator are you using?") end end --Initialize this stuff... dofile("BasicFunctions.lua") dofile("VectorOps.lua") dofile("Sorts.lua") getsystem() local mindex=0 range={} for i=1,#byterange do for j=byterange[i][1],byterange[i][2] do range[mindex]=j mindex=mindex+1 end end --range=0x7FFF addresses={} for i = 1,#range do addresses[range[i]]={} end X={} costhetasq={} --Converts a hue angle to RGB. Normalized to the range [0,255]. local function huetoRGB(hueangle,alpha) local R,G,B=0,0,0 if not(alpha) then alpha=0x7F end --Alpha defaults to 0x7F. hueangle=hueangle%360 local huefraction=hueangle/60-math.floor(hueangle/60) --This is a sawtooth function if hueangle>=0 and hueangle<60 then R,G,B=1,huefraction,0 elseif hueangle>=60 and hueangle<120 then R,G,B=1-huefraction,1,0 elseif hueangle>=120 and hueangle<180 then R,G,B=0,1,huefraction elseif hueangle>=180 and hueangle<240 then R,G,B=0,1-huefraction,1 elseif hueangle>=240 and hueangle<300 then R,G,B=huefraction,0,1 elseif hueangle>=300 and hueangle<360 then R,G,B=1,0,1-huefraction end R,G,B=math.floor(0xFF*R),math.floor(0xFF*G),math.floor(0xFF*B) return {R,G,B,alpha} --Output is in table form. end --############################################################################# --############################################################################# local BestTable= false local function CalcNow() for i,v in pairs(addresses) do addresses[i]=detrend(addresses[i]) addresses[i]=normalize(addresses[i]) end X=detrend(X) X=normalize(X) for i,v in pairs(addresses) do costhetasq[i]=dotproduct(X,addresses[i])^2 end BestTable=selectionsort(costhetasq,20) for i=1,#BestTable do BestTable[i][3]=memory.readbyte(BestTable[i][1]) end end local function GetData(UserInput) for i,v in pairs(addresses) do addresses[i][emu.framecount()]=memory.readbyte(i) end X[emu.framecount()]=UserInput end local function NullFn() end -- The "Do Nothing" function. --############################################################################# --############################################################################# --***************************************************************************** local function InBox(Lf,Up,Rt,Dn , x,y) --***************************************************************************** -- Just seeing if the chosen pixel resides within the chosen boundaries. -- Does not like it if the "box" you specify has reversed x- or y-axis. return (x >= Lf) and (x <= Rt) and (y >= Up) and (y <= Dn) 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 --############################################################################# --############################################################################# --Let's construct buttons! Yay! local Flag_DrawRainbow= false -- Bah, hate flags. Any workarounds? local ValFindFn, MoveMe FRK_NoAuto= true dofile("YummyButton.lua") --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% local InputBar= _FRK_Btn.NewBtn{ --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% x1= 5, y1= 15, x2= 14, y2= 99, MBC= 0x00FF00FF, MFC= 0, SBC= 0x7FFF7FFF, SFC= 0, ------------------------------------------------------------------------------- fn= function(B) ------------------------------------------------------------------------------- if keys.shift then return MoveMe end return ValFindFn end } local function rt(B) return B.tag end --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% local StretchBoxUp= _FRK_Btn.NewBtn{ --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% x1= InputBar.x1-5, y1= InputBar.y1-15, x2= InputBar.x2+5, y2= InputBar.y1+5, MBC= "#FFFFFF40", MFC= 0, SBC= "#FFFFFFC0", SFC= 0, fn= function(B) return B.tag end } ------------------------------------------------------------------------------- StretchBoxUp.tag= function() ------------------------------------------------------------------------------- local DeltaY= keys.ymouse - lastkeys.ymouse DeltaY= math.max(DeltaY, -InputBar.y1) --Don't put beyond screen edge! DeltaY= math.min(DeltaY, InputBar.y2-InputBar.y1-10) InputBar.y1= InputBar.y1+DeltaY StretchBoxUp.y1= StretchBoxUp.y1+DeltaY StretchBoxUp.y2= StretchBoxUp.y2+DeltaY end --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% local StretchBoxDn= _FRK_Btn.NewBtn{ --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% x1= InputBar.x1-5, y1= InputBar.y2-5, x2= InputBar.x2+5, y2= InputBar.y2+15, MBC= "#FFFFFF40", MFC= 0, SBC= "#FFFFFFC0", SFC= 0, fn= function(B) return B.tag end } ------------------------------------------------------------------------------- StretchBoxDn.tag= function() ------------------------------------------------------------------------------- local DeltaY= keys.ymouse - lastkeys.ymouse DeltaY= math.max(DeltaY, InputBar.y1-InputBar.y2+10) DeltaY= math.min(DeltaY, SCRBOTTOM - InputBar.y2) --Screen edge InputBar.y2= InputBar.y2+DeltaY StretchBoxDn.y1= StretchBoxDn.y1+DeltaY StretchBoxDn.y2= StretchBoxDn.y2+DeltaY end --***************************************************************************** ValFindFn= function() --***************************************************************************** Flag_DrawRainbow= true end --***************************************************************************** MoveMe= function() --***************************************************************************** local DeltaX= keys.xmouse - lastkeys.xmouse local DeltaY= keys.ymouse - lastkeys.ymouse DeltaX= math.min(DeltaX, SCRRIGHT - InputBar.x2) DeltaY= math.min(DeltaY, SCRBOTTOM - InputBar.y2) DeltaX= math.max(DeltaX, -InputBar.x1) DeltaY= math.max(DeltaY, -InputBar.y1) InputBar:move(InputBar.x1+DeltaX, InputBar.y1+DeltaY) StretchBoxUp:move(StretchBoxUp.x1+DeltaX, StretchBoxUp.y1+DeltaY) StretchBoxDn:move(StretchBoxDn.x1+DeltaX, StretchBoxDn.y1+DeltaY) end ------------------------------------------------------------------------------- InputBar.GetVal= function(B,k) ------------------------------------------------------------------------------- local h= B.y2 - B.y1 local n= k.ymouse - B.y1 return (1 - n/h) * 1024 -- The multiplication is fairly arbitrary end ------------------------------------------------------------------------------- InputBar.DrawSel= function(B) --Override standard button code ------------------------------------------------------------------------------- gui.box(B.x1, B.y1, B.x2, B.y2, B.SFC, B.SBC) -- May as well box ourself Flag_DrawRainbow= true -- Would rather make only one call, not thousands. end ------------------------------------------------------------------------------- InputBar.DrawRainbow= function(B,y) ------------------------------------------------------------------------------- y=B.y1*boolto10(y<=B.y1) + y*boolto10(B.y1<y and y<=B.y2) + B.y2*boolto10(B.y2<y) for i=y,B.y2 do local hueangle=240 - 240/(B.y1-B.y2)*(i-B.y2) local RGBTable=huetoRGB(hueangle,0x7F) gui.line(B.x1,i,B.x2,i,RGBTable) end gui.line(B.x1,y,B.x2,y,"white") end ------------------------------------------------------------------------------- InputBar.MouseHit= function(B,k) ------------------------------------------------------------------------------- if InBox(B.x1,B.y1,B.x2,B.y2, k.xmouse,k.ymouse) then return InputBar:GetVal(k) end return nil end --############################################################################# --############################################################################# local ShowTable= false --***************************************************************************** local function PaintStat(T,y) --***************************************************************************** gui.box(2,y-1,70,y+5,0x000000FF,0x000000FF) local temp= T[1] -- Address for i= 0, 3 do local n= bit.band(bit.rshift(temp,i*4),0xF) Draw[n](16-4*i, y, -1) end temp= T[2]*10000 -- Certainty local color= "red" if temp >= 900 then color= "white" elseif temp >= 800 then color= "green" elseif temp >= 700 then color= "yellow" end __DN_AnyBase(44, y, temp, color, 0, 10) temp= memory.readbyte(T[1]) -- Memory value __DN_AnyBase(68, y, temp, "white", 0, 10) end --############################################################################# --############################################################################# --Keyboard (and indirectly, location where mouse is stored) --local keys, lastkeys= input.get(), input.get() --local function UpdateKeys() lastkeys= keys; keys= input.get() end --local function Press(k) return keys[k] and (not lastkeys[k]) end -- Functions are supplied by YummyButtons.lua. Neato. local KF= {} --***************************************************************************** local function KeyFunctions() --***************************************************************************** for key,v in pairs(keys) do if not lastkeys[key] then if KF[key] then KF[key]() end end end end ------------------------------------------------------------------------------- KF["space"]= function() -- The CALCULATE NOW function. ------------------------------------------------------------------------------- CalcNow() end ------------------------------------------------------------------------------- KF["home"]= function() -- Display toggle ------------------------------------------------------------------------------- ShowTable= not ShowTable end --############################################################################# --############################################################################# local Val= nil --***************************************************************************** local function ControlHUD() --***************************************************************************** gui.box(L,U,R,D,0,0x00FF00FF) -- See if we click the all-important box if InBox(L,U,R,D , keys.xmouse , keys.ymouse) then gui.line(L,keys.ymouse,R,keys.ymouse, -1) local h= D - U -- Get height of box local n= (keys.ymouse - U) -- Figure out where mouse is relative to box Val= (1 - n/h) * 999 -- Linear scoring of mouse position. gui.text(200,210,math.floor(Val)) else Val= nil end if keys.shift and keys.leftclick then L= L + keys.xmouse - lastkeys.xmouse R= R + keys.xmouse - lastkeys.xmouse U= U + keys.ymouse - lastkeys.ymouse D= D + keys.ymouse - lastkeys.ymouse end end local HoldFn --***************************************************************************** local function HandleGui() --***************************************************************************** --Main handler. Things to do, yes. local Fn= _FRK_Btn.HandleBtns() HoldFn= Fn or HoldFn if not keys.leftclick then HoldFn= nil end if HoldFn then HoldFn() end KeyFunctions() -- Maybe display list of results. This is only a glorified print() so far. -- There are planned alterations to do... if ShowTable and BestTable then for i= 1, #BestTable do PaintStat(BestTable[i],9*i) end end -- Paint the rainbow! if Flag_DrawRainbow then InputBar:DrawRainbow(keys.ymouse) Flag_DrawRainbow= false end end gui.register(HandleGui) --***************************************************************************** local function CheckImmediateInput() --***************************************************************************** local ImmediateInput= input.get() if ImmediateInput.leftclick and not ImmediateInput.shift then return InputBar:MouseHit(ImmediateInput) end end --***************************************************************************** local function HandleBefore() --***************************************************************************** --Handle frame-advancey stuff. local Val= CheckImmediateInput() if Val then GetData(Val) end end emu.registerbefore(HandleBefore)
A little extra thanks Although I have tried to emphasize that this is a joint project, I would still like to extend my thanks to FatRatKnight, who kindly volunteered his time to create the GUI. I do not have the programming know-how to efficiently make a GUI, so this project would have gone nowhere without him. Even though I am in charge of the technical aspects of the program, I still feel FatRatKnight has created most of its true "content". If anything in the scripts seems to be programmed elegantly, chances are good that FatRatKnight wrote it.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
It's been a while since I've played PM:TTYD, so I don't quite remember the physics of the paper airplane offhand. What I suggest you do as a first step is investigate things like the plane's terminal velocity for different angles and how it accelerates when you alter its trajectory just slightly. If its angle of descent changes from 20 degrees to 21 degrees, how long does it take to reach its new terminal velocity? Does it accelerate uniformly? You may eventually be able to deduce the horizontal and vertical components of its velocity as a function of descent angle and time. If there were no friction, the path you should take would be a cycloid.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
CoolKirby wrote:
Sorry to triple post, but I need some help. I finally got to the first Paper Plane panel and I'm trying to optimally fly to the platform below and walk up to the Thousand-Year Door room. Could someone please help me with the angle and speed of the Paper Plane? I hope to be able to learn from whatever help I get to do more complicated Plane flying later by myself. I would appreciate any help; just post below and I will PM you the movie file and some savestates.
Sounds like you need some calculus of variations.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Nach wrote:
Or conversely I can say I hope you don't acquire slaves, since from your description, it sounds like you'll mistreat the human beings that they are.
Most of us don't need a book to tell us not to "own" people, much less how to treat them.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
I realize I'm late to the party, but I'd like to weigh in. I voted for "fastest" because as far as I'm concerned, syncing on the console is almost irrelevant. Consistency is the most important criterion when evaluating a run. Suppose we have a terrible emulator that wreaks all sorts of havoc on the gameplay. (I'm having a little trouble thinking up examples, but let's say the RNG is inaccurate, text boxes don't load properly, and the game's intro sequence doesn't load properly and is skipped.) For the purposes of publishing a run, it is irrelevant that it isn't console-accurate but rather that this emulator is the standard for TASVideos. Our goal, as I see it, is to compare TASes with other TASes, not TASes with unassisted speedruns (not directly, at least). Taken to a ridiculous extreme, if SNES9x takes Earthbound's code as its input but reproduces Super Mario World on the screen, but the game is beaten as fast as possible, then that is all that matters. With that said, the scenario cooked up by DarkKobold seems to be deliberately controversial. If a run syncs up at a cost of just 100 frames, I'd be tempted to opt for the synced version. As a matter of principle, though, I have to side with the fastest version.
1 2
26 27 28
34 35