Posts for amaurea

1 2
16 17
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
Fractalwizz wrote:
the emulator files sometimes work. If a video had luck manipulation, those parts may not work out like the avis show. So the run would fail and you would be disappointed.
Desyncs aren't a matter of luck manipulation; they can happen whether that is used or not. They shouldn't happen, however, if you use the correct version of the emulator and the correct rom (and you will usually be warned when loading a movie file for the wrong rom). What movies have you had problems with?
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
That's a pretty original script idea, and sounds like something that can open up for many interesting situations, like fighting Zeromus with Dark Knight Cecil, Palom and Porom, for example. Have you tested how crash-prone such unintended party configurations are? I'll try to give this script a test during the next week :)
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
Saturn: Yes, I know that sound should be no more difficult to get to work when avi recording from snes9x than video. However, I don't have access to any windows machines with a sound card, unbelievable as it might sound, and snes9x refuses to produce any audio at all if there is no sound card, even when dumping to avi. That is why I hoped someone could do it. I already process the video in essentially the same manner as you reccomend, but using mencoder instead of virtualdubmod. As for your idea about having ingame sync while displaying all realtime frames: Yes, that should be possible to add. I'll see if I can get it to work. Edit: There, try downloading player.lua again now. The option ingame has now been obsoleted (but is still recognized). It is replaced by the options view_ingame and sync_ingame. What you asked for is achived with view_ingame = false and sync_ingame = true.
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
I thought we were talking about brute forcing the entire game. Sure, if we restrict ourselves to short segments, and make other assumptions about which buttons have any effects (such assumptions will often prevent one from discovering unexpected glitches, by the way), then the problem is manageable. ntclark: I don't get how this dynamic programming stuff is going to help. How would you use that to solve the first 10 seconds of super mario bros. 1, for example?
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
Saturn: I'm glad you found the script useful. It was actually inspired by just the Super Mario Kart you mentioned - see the "TAS ghost" thread for the initial discussion that lead to this script. I am now encoding a new version of the movie, this time in room comparison mode, since that was requested. There will be no sound this time either, sadly (I would be thankful if someone could encode a version with sound). In this version, you will note that the numbers on top of the screen do not stay 0. In room comparison mode, they indicate how many frames the other moves were behind at room entry. Edit: And here it is: http://folk.uio.no/sigurdkn/sm_ingame2.avi
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
The reason why sqrt would not be enough of an improvement, is because the number of tries involved is just too high. Something more like a logarithm would be needed. The NES has 8 binary inputs, which are read 60 times per second. If the shortest way through a game is 10 s long, a brute force search would need 8^600 tries, which is about 10^542 tries. The square root of this is the much smaller 10^271, but the number is still far too big. Let's say you had a massively parallel quantum computer with 10000000000 cores, each capable of testing as many possibilities per second. Then, the calculation would take 10^251 seconds, or 10^244 years. And that's with both massive parallelism and a quantum algorithm for NES emulation with a sqrt speedup. Quantum computers do not help (and probably won't give O(sqrt) for this case anyway), and parallelism helps even less.
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
Ok, I'll give a detailed explanation of how to use my script. 1. For each movie you want to compare with, you start snes9x, edit the recoder script to indicate which smv file it is going to be run for, then load the recorder script http://folk.uio.no/sigurdkn/recorder.lua and then play the smv movie. Two files will be created, with names based on what you put in the recorder script. 2. When you have done this for all the movies you want to compare with, you will not need to run the recorder again. Now edit the configuration section of player.lua http://folk.uio.no/sigurdkn/player.lua to suit your preferences, and indicate the smv files you wish to compare with (these must be smv files for which you have run recorder.lua). The settings are: moviefile, which is not really relevant for current versions of snes9x-lua. Leave it alone. others: This is a list of the files you want to compare with. You will want to edit this. ingame: This is 0 for realtime mode, 1 for realtime synchronization but with skipping of non-realtime frame, and 2 for ingame mode. 0 and 2 are the most relevant. room: this is false for absolute synchronization, and true for room synchronization. This makes all movies enter each room at the same time, so you can compare each room. The recorder.lua file used to include an unsupported call to movie.open. I have fixed this now, so try again if you tried yesterday. To be even more explicit: Say that I have the movies a.smv, b.smv and c.smv, and I want to compare a.smv to the two others. Then I first edit recorder.lua and set moviefile = "b.smv", start snes9x and load recorder.lua. Then, when I am prompted, I start b.smv, and let it run to completion, after which I exit snes9x. Next, I edit recorder.lua and set moviefile = "c.smv", start snes9x, load recorder.lua and then load the movie file c.smv, let it run to completion, and then exit. This should have created four files: b.smv.dump, b.smv.index, c.smv.dump and c.smv.index. The purpose of recorder lua is simply to generate these files, so once you have them, you don't need to run it again. Now, to compare a.smv to these, you edit player.lua. Let's say you want ingame mode with room comparison. Then you make the following changes: others = { "b.smv", "c.smv" } ingame = 2 room = true After this, just start snex9, load player.lua (this could take a few seconds), and then play the movie file a.smv when prompted. With lua version >= 0.06, you can use savestates with the script, as long as they were created while it was running.
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
Cpadolf: The script does support this. I just didn't run it in that mode for this example, as I thought this would be more popular. Have a look in the script section. I usually use the room comparison mode, myself.
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
Here is an, unfortunately soundless, comparision of cpadolf and herooftheday's last any% runs. herooftheday's are orange for the old and yellow for the new, and cpadolf's are blue for the old and white for the new. This movie compares ingame time, the result would be different in realtime mode. http://folk.uio.no/sigurdkn/sm_ingame.avi The hitboxes indicating the other runs were generated by my lua script for super metroid, which is found in the snes9x lua thread.
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
I have now back-ported the super metroid script, which could not be used in current versions of snes9x-lua due to use of the not yet implemented movie.open command. Usage is mostly as described earlier. Run recorder.lua (which has not been changed) once for each movie you want to compare to, edit the configuration section at the top of player.lua to fit your needs, load player.lua in snes9x, and then run the movie file when prompted. If the lua version is less than 0.06, savestates are not supported, otherwise they work, as long as they were created while the script was running. I guess I should try to create a short example movie, too... When it comes to configuration, I recommend ingame=2 and room = true. As described in my original post, 2 pages back, this only shows ingame frames, and lets you compare the movies' performance in each room. But the other modes are also interesting. The files can be found at http://folk.uio.no/sigurdkn/recorder.lua and http://folk.uio.no/sigurdkn/player.lua Edit: I have created two test avis of the script: http://folk.uio.no/sigurdkn/sm_ingame_room.avi http://folk.uio.no/sigurdkn/sm_realtime_room.avi Both use room=true, but the first has ingame=2, and the second has ingame=0. The orange box is herooftheday-smetroid.smv, the yellow is herooftheday2-smetroid.smv, the blue is cpadolf-smetroid.smv, and the white is cpadolf2-smetroid.smv. Notice how all runs exit the room at approximately the same time in the first, while there is a large difference between hero of the day and cpadolf in the second. This illustrates that item pickups are much more expensive in realtime oriented runs than in ingame oriented runs. Edit2: Here are two similar movies with room = false. The runs are now synced by absolute frame/ingame frame, and not by room entry. herooftheday's first run is far behind in this case, since it doesn't do the torizo skip. http://folk.uio.no/sigurdkn/sm_ingame_abs.avi http://folk.uio.no/sigurdkn/sm_realtime_abs.avi Nb. There is no sound in those videos due to snes9x refusing to output an avi with sound on a computer without a sound card. Edit3: Embarrassingly, I forgot that the recorder script also needed to be fixed. It should be fixed now.
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
I have made another script, this time for ff6. Though it is not as complete as I would have wanted, perhaps someone will find it useful. Features: 1. Encounter prediction: predicts steps until encounter, and what will be encountered. Does not predict front/back/pincer/side/preemptive variants, not will it work properly on the floating continent. (there is tentative support for this (which actually makes up a good part of the code), but it is buggy, and only works well when one is on the world map, and has 3 or 4 chars in party, so it is disabled by default) 2. Outline treasure chests and show their contents and status (green = untaken, red = taken) 3. Outline squares that trigger events. Blue = transition to new area, light blue = transition to same area, very light blue = transition to map, yellow = unclassified event. 4. In battle, hp and mp bars are displayed over the enemies, as well as their battle gauges. Their level is also displayed. On Gau's turns, a "?" is displayed by any monster whose rage is not known. Additionally, the enemies' resistances etc. are displayed in a compact but hopefully readable manner, as a set of 4 3x3 squares of pixels, where each has the following format: fil ; fire (red) ice (blue) lightning (yellow) pwp ; poison (green) wind (gray) holy (white) ew ; earth (brown) water (blue) The order of the squares is, from left to right, absorb, nullify, resist, weak. 5. Extra information is displayed for the currently selected monster. Normally, only its drops and hp/mp are displayed, but if Locke is selecting the monster with the steal command, its steals are also listed. If Relm is selecting a monster with control or sketch, that information will also be indicated. 6. While in the rage menu, information about what the selected rage does is displayed. Only the attack part of the rage is displayed so far, since I haven't thought of a compact way of displaying all the status information associated with a rage. This time, I have tested it on an unmodified snes9x improvement, and it should work there (as opposed to my super metroid script, which I am tempted to rewrite to accommodate the lacks of the current snes9x). The script is available at http://folk.uio.no/sigurdkn/ff6.lua Suggestions for improvements are welcome, but it is unlikely that anything that has to do with 0x7e00be will be implemented, as I've been stuck on predicting what it will be for half a week now.
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
Ah, so someone has tried it! As I said, I thought I had removed all occurrences of the non-standard scripting I have added to my own version, but those slipped through, I see. I could try to make one which does not depend on it (it could read how far the movie has come by the time the script is loaded, or wait for the move to load), but since the next version is supposed to be released soon, and I would be surprised if movie.open isn't there, I think I will wait.
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
I have made a ghost script of the same kind as the one Dromiceius wrote, but for Super Metroid. It consists of two parts, a recorder and a player. One runs the recorder one time for each movie one wants to compare with. It writed files with position, room transition and hitbox data. One then runs the player script, which reads these files, and superimposes hitboxes on the run you want to compare the others to. The recorder has only one option: The smv file to record for. Each time you run the recorder, you must edit the line at the top of the file specifying this. The player has several options. You must first specify which smv will be the main movie, i.e. the one that snes9x will run now, and that the others will be compared to. Next, you can specify a list of smvs to compare it with. The recorder must have been run for all of these. The colors option specifies which colors should be used for the hitboxes of the other smvs. The most interesting options are the ingame and room options. With ingame = 0, you will see all frames. With ingame = 1, frames where the ingame time does not change are not displayed, but realtime is still used for synchronization. Finally, ingame = 2 uses ingame time both for deciding which frames to display, and for synchronization. Since we have runs that focus on both realtime and ingame time, this option is quite handy. The room option specifies whether room comparison mode should be used. If room = 0, things will be synchronized by using the absolute frame number, while with room = 1, it will find the closest matching room transition, and sync from the start of that, so in this mode, all the movies will enter each room at the same time, and you can compare how they traverse each room. If moviefile is undefined, a main smv file is not loaded, and you can play while comparing to others (this is usually a depressing experience :P). Savestates should work, and not mess up sync, and fast forwarding and frame advance, too. I made this script on a slightly modified snes9x, but I don't think I use any non-standard lua calls here. It would be nice if somebody could test, though. The files are at http://folk.uio.no/sigurdkn/recorder.lua and http://folk.uio.no/sigurdkn/player.lua. Ps. The initialization during player.lua takes a bit of time (about 2-3 s per comparison movie, on my computer). All of these scripts load the smv files themselves, so do not load them manually. Pps. I have also made a modified version of snes9x which plays several movies concurrently, and superimposes graphics from all but one of them on the last one. It basically works like this script, but with all sprites instead of just hit boxes. It, does not support savestates or frame skipping, though, and will be much work to port to windows, so I probably won't finish it.
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
When I stated that any desync during rerecording was proof of savestate problems, I had forgotten that nondeterministic emulation is another likely source of desyncs, that can happen even in the absence of savestates. Bisqwit reminded me of this on IRC, but I thought I'd post it here, so my earlier comments don't mislead people. Nondeterministic emulation is, in a way, just as problematic as savestate problems, as the game might end up in a different state than it would have been in on the actual console, but since it happens even during normal gameplay, any significant deviations would have been noticed and corrected already.
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
Actually, whether one can trust a multimedia tas is not only a question of whether the author can be trusted. If the savestates are incomplete, one will end up in an unrealistic state after loading one. If the game progresses through states A, B, C, D, E, and you made a savestate at C, loading it at point E, the new version of history will be A, B, C*, where C* is different from C if the savestates are unreliable. The state C* might not even be reachable normally. Inaccuracies like this can accumulate whenever you load a state. As an extreme example of why this is a problem, suppose that the "on the ground" flag of some game is not properly saved in the savestate, and that C was in the air, while E was on the ground. C* could then end up being able to jump, even though it is in the air. Any gains from this would not be legitimate. There is a way to check if this problem exists. Record your resulting inputs, and play them back to the emulator. If this causes a desync, then one of the state loads brought you back to an incorrect state, and the multimedia file cannot be trusted.
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
Ok, I think I have fixed all the off-by-one-frame and desync problems in my super metroid ghost script now. I have also tried to modify it to be playable on an unmodified snes9x-lua (i.e. one without my own hacks). The files are http://folk.uio.no/sigurdkn/player.lua and http://folk.uio.no/sigurdkn/recorder.lua. As described earlier, this script superimposes hitboxes for Samus from other smvs on the screen. I think this script should be useful both for making assisted and unassited speedruns, as well as when watching and comparing them. Feedback would be appreciated.
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
Yes, those should do it... but it seems that my version is 0.05. I can find the source code for 0.04 and 0.05, but 0.06 seems to lack it.
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
Dehacked, is there a way to store some information in savestates? I found a few bugs in my script, and I need this feature to be able to fix it. I need to know both which room I am in and which room I came from, but there is no way to recover the latter if a savestate is loaded. I seem to remember talk of such a feature before, but I can't find it.
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
I also had hopes for this emulator, but the real problem isn't that PSAuthor is so unresponsive, it is the fact that the emulator is closed source. Rerecording usually takes lots of tweaking before it finally works well enough to be used (just look at the snes9x improvement series, for example). Even if PSAuthor had been entusiastic about this, rerecording would probably not be flawless on his first attempt, and then we would have to beg for him to fix every bug we find. So offering to help him is the correct thing to do, but from what I've seen on his forum, he seems to be dead set against that. Perhaps fixing another psx emulator would be easier. Making a plugin-based emulator desync-proof could perhaps be accomplished by writing new tas-friendly plugins that make sure they save their state information correctly. It sounds like a daunting task, but atleast we wouldn't be at PSAuthor's mercy then.
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
My original attempt used lots of savestates to jump between several movies in the same emulator, trying to grab only the sprite layer from all but one of one of them, and put them on top of the image from the other. I wrote a quicker set of savestates (using no compression and only memory) for this, and added lua functions for adding together several screenshots with transparent parts (and avoiding putting the screenshot in a string), as well as functions for updating the image without frame advancing, turning on and off backgrounds in lua, etc. but it was too slow to be practical, and eded up desyncing anyway. It could have looked nice, though. It sounds, from what you write, like you have implemented ghosting already yourself. If so, it would be nice to see. Using two (or more) emulators was my original plan, but being unfamiliar with interprocess communication, I fell back on trying to do it with one emulator. One thing my script does better than such an approach, is the room ghost mode, where you can compare how different movies traverse each room. Oh, and I can't see how it's possible to implement clipped rectangles by using your rectangle code. One would have to use drawline instead. After all,
+------------------+
|    |        +---+|
| A  |        | B ||
|----+        +---+|
|                  |
|                  |
|  Screen          |
|                  |
+------------------+
A is a clipped rectangle. Some of its edges are off screen, so we only see two of them in this case. B is a shrunk rectangle. All its edges are visible. I think my implementation of drawrect is the most sensible, as it allows drawing of clipped rectangles, and spares the user from having to perform a very common check.
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
Here is an improvement to drawbox, to prevent it from crashing when drawing boxes that extend outside the screen:
int x1m = min(max(0,x1),255);
    int y1m = min(max(0,y1),238);
    int x2m = min(max(0,x2),255);
    int y2m = min(max(0,y2),238);

    // top surface
    if(y1 == y1m) for (i=x1m; i <= x2m; i++)
        gui_data[y1*256 + i] = colour;

    // bottom surface
    if(y2 == y2m) for (i=x1m; i <= x2m; i++)
        gui_data[y2*256 + i] = colour;

    // left surface
    if(x1 == x1m) for (i=y1m; i <= y2m; i++)
        gui_data[i*256+x1] = colour;

    // right surface
    if(x2 == x2m) for (i=y1m; i <= y2m; i++)
        gui_data[i*256+x2] = colour;
This replaces the end of drawbox. I have also found this function handy:
// movie.open()
//
//   Opens movie playback/recording
static int movie_open(lua_State *L) {
    const char *filename = lua_tostring(L,1);
    int readonly = TRUE;
    int sync = 0;
    Settings.WrongMovieStateProtection = false;
    S9xMovieOpen(filename, readonly, sync);
    return 0;
}
Using these changes, I have made a ghosting script for Super Metroid. I suspect that it might sometimes be off by 1 frame, and it has not been completely tested how it reacts to savestates, but perhaps it will come in handy. It consists of two parts: recorder.lua:
-- the movie file to use
moviefile = "herooftheday2-smetroid.smv"
-- output file. contains ingame frame, scroll, position and hitbox information
dumpfile = moviefile..".dump"
io.output(dumpfile)
-- index file. contains room transition information
indexfile = io.open(moviefile..".index", "w")
snes9x.speedmode("turbo")
movie.open(moviefile)
prev = {}
prev.roomid1 = memory.readword(0x7ED552)
prev.roomid2 = memory.readword(0x7ED554)
prev.roomid3 = memory.readword(0x7ED556)
while true do
    if movie.mode() then
        igframe = 3600*memory.readbyte(0x7E09DE)+60*memory.readbyte(0x7E09DC)+memory.readbyte(0x7E09DA)
        roomid1 = memory.readword(0x7ED552)
        roomid2 = memory.readword(0x7ED554)
        roomid3 = memory.readword(0x7ED556)
        scrollx, scrolly = memory.readword(0x7E0911), memory.readword(0x7E0915)
        posx, posy = memory.readword(0x7E0AF6), memory.readword(0x7E0AFA)
        hitx, hity = memory.readword(0x7E0AFE), memory.readword(0x7E0B00)
        io.write(igframe, " ", roomid1, " ", roomid2, " ", roomid3, " ", scrollx, " ",
            scrolly, " ", posx, " ", posy, " ", hitx, " ", hity, "\n")
        -- keep track of door transitions. this is used in room ghost mode
        if roomid1 ~= prev.roomid1 or roomid2 ~= prev.roomid2 or roomid3 ~= prev.roomid3 then
            indexfile:write(roomid1, " ", roomid2, " ", roomid3, " ", prev.roomid1, " ", prev.roomid2, " ", prev.roomid3, " ", movie.framecount(), " ", igframe, "\n")
        end
        prev.roomid1 = roomid1
        prev.roomid2 = roomid2
        prev.roomid3 = roomid3
    end
    snes9x.frameadvance()
end
Specify which smv to use at the top of this file, and it will generate a .dump and .index file for that movie. Do this for all the movies you want a ghost for. The main part of the script is in player.lua:
moviefile = "cpadolf2-smetroid.smv"
others = { "herooftheday-smetroid.smv", "herooftheday2-smetroid.smv", "cpadolf-smetroid.smv", "cpadolf2-smetroid.smv" }
colors = { "red", "green", "blue", "white" }
        
-- ingame mode:
-- 0: Show all real-time frames
-- 1: Skip non-ingame frames for main character 
-- 2: Skip non-ingame frames for everybody
ingame = 2

-- room comparison mode:
-- 0: Normal
-- 1: Synchroniced room entry
room = 1

-- set up movie data
data = {}
pattern = "(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)"
for i,name in ipairs(others) do
    print("Loading "..name.." position data")
    local filedata = {}
    io.input(name..".dump")
    for line in io.lines() do
        local entry = {}
        _,_,entry.igframe,entry.roomid1, entry.roomid2, entry.roomid3,
        entry.scrollx, entry.scrolly, entry.posx, entry.posy,
        entry.hitx, entry.hity = string.find(line,pattern)
        table.insert(filedata, entry)
    end
    filedata.progress = 1
    filedata.sync = 0
    filedata.offset = 0
    filedata.init = false
    table.insert(data,filedata)
    snes9x.frameadvance() -- hack to avoid snes9x thinking we are hanging
end
-- set up index data
index = {}
pattern = "(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)"
for i,name in ipairs(others) do
    print("Loading "..name.." room transition data")
    local filedata = {}
    io.input(name..".index")
    for line in io.lines() do
        local entry = {}
        _,_,entry.roomid1, entry.roomid2, entry.roomid3,
        entry.roomid1p, entry.roomid2p, entry.roomid3p,
        entry.frame, entry.igframe = string.find(line,pattern)
        local key = entry.roomid1.." "..entry.roomid2.." "..entry.roomid3.." "..
            entry.roomid1p.." "..entry.roomid2p.." "..entry.roomid3p
        if not filedata[key] then filedata[key] = {} end
        local val = {}
        val.frame = entry.frame
        val.igframe = entry.igframe
        table.insert(filedata[key],val)
    end
    table.insert(index,filedata)
    snes9x.frameadvance() -- hack to avoid snes9x thinking we are hanging
end

-- find the frame corresponding to a given in-game frame.
-- Does a halving search, but is optimized for the case where
-- we are quite close already
seek = function(t, igf)
    local tmp = 4
    local a = t.progress
    local b = t.progress
    -- setup search window
    while igf - t[a].igframe < 0 do
        b = a
        a = a - tmp
        tmp = tmp * 2
        if a <1> 0 do
        a = b
        b = b + tmp
        tmp = tmp * 2
        if b > table.getn(t) then b = table.getn(t) end
    end
    if t[b].igframe == igf then return b end
    -- now do a halving search.
    -- We know that the value we want is between a and b
    -- and that a < b
    while true do
        local mid = (a+b)/2
        if t[mid].igframe - igf == 0 then return mid
        elseif t[mid].igframe - igf <0> 0 then igframe = 0
        else beginning = false end
    end
    -- igframe = memory.readword(0x7E1842)

    -- unless we are in real time mode, we will skip frames where the
    -- incame frame counter does not change
    if ingame > 0 and igframe ~= igframe_old then
        skippedframes = count-lastcount-1
        snes9x.speedmode("normal")
        -- build the full room id. not sure if all these are needed
        roomid1 = memory.readword(0x7ED552)
        roomid2 = memory.readword(0x7ED554)
        roomid3 = memory.readword(0x7ED556)
        roomid = ""..roomid1.." "..roomid2.." "..roomid3
        -- information for drawing the hitboxes
        scrollx, scrolly = memory.readword(0x7E0911), memory.readword(0x7E0915)
        posx, posy = memory.readword(0x7E0AF6), memory.readword(0x7E0AFA)
        hitx, hity = memory.readword(0x7E0AFE), memory.readword(0x7E0B00)

        -- have we had any room transitions?
        if roomid ~= roomid_old then transition = true else transition = false end
        for i = 1, table.getn(data) do
            -- do we need to fix our position in the file?
            if stloaded then
                if ingame > 0 then data[i].progress = seek(data[i],igframe)
                else data[i].progress = count+1 end
            end

            -- however, in room ghost mode, we will find the closest
            -- matching room transition.
            if room == 1 and transition then
                local frame
                if ingame < 2 then frame = count else frame = igframe end
                local trans = index[i][roomid.." "..roomid_old]
                if trans then
                    local diff = 1e55, bj
                    for j = 1, table.getn(trans) do
                        local tframe
                        if ingame < 2 then tframe = trans[j].frame
                        else tframe = trans[j].igframe end
                        local cdiff = math.abs(frame-tframe)
                        if cdiff <diff> 0 do
                        data[i].progress = data[i].progress + 1
                    end
                    data[i].init = true
                end
                -- eat frames where igframe does not change
                while data[i][data[i].progress].igframe -
                    data[i][data[i].progress+1].igframe == 0 do
                    data[i].progress = data[i].progress+1
                end
                data[i].progress = data[i].progress+1
            else data[i].progress = count+1 end
            c = data[i].progress
            if c < table.getn(data[i]) then
                local entry = data[i][c]
                if roomid1 - entry.roomid1 == 0 and
                    roomid2 - entry.roomid2 == 0 and
                    roomid3 - entry.roomid3 == 0 then
                    x = entry.posx-scrollx
                    y = entry.posy-scrolly
                    gui.drawbox(x-entry.hitx,y-entry.hity,x+entry.hitx,y+entry.hity,colors[i])
                end
            end
        end
        roomid_old = roomid
        lastcount = count
    else snes9x.speedmode("maximum") end
    igframe_old = igframe
    frameinc()
    snes9x.frameadvance()
end
The options of interest are moviefile, others, ingame and room, and are documented in the file.
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
Xkeepers verion sounds a bit simpler, but would be less visually pleasing, as the ghost's character sprite and its animation would be missing then. Is frame-advance and load savestate really that fragile operations? My idea of implementing this, was to modify the emulator to have a mode where it can be signaled to start, frame advance, save/load state, etc, and then make another program that starts up several of these, tells them all to load the rom and their respective movie files, and then frame advances them all, collects information from them, draws the frame, advances, and so on. The hardest part would probably be to get the player sprite (ideally only the visible part of it, if parts of it are hidden behind something). But there are good arguments for running through all the movies one by one first. One could then note the frames for entry/exit for each area, and create savestates for each of these. These savestates could then be used in the process described in the first paragraph. Normally, the best run will leave the other one behind, after a while, and one would not see much of the gost, but with such savestates, one could shift the gost to make them (say) enter every room at the same frame, giving automatic room-by-room comparisons. I can read and write C and C++, but I am not familiar with Snes9x or memory watchers, or windows programming, but it could be a fun thing to try after my master is finished (in 2 months). DeHackEd: I am looking forward to seeing the result of your test with Mega Man X!
Post subject: Idea: TAS Ghost (like in racing games)
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
It would be nice to have a program that lets you see a ghost image of other TASes (of the same game, preferably) in a TAS. It could be implemented by running several emulators at once, and designating one of them as the main one. The total image would then start out as the main one, and then fetch the character's sprite and location (which room and which position within the room), and if the room is the same as the one in the main emulator, it would paste the sprite on top of that image at the appropriate location. This sounds hard, but doable, though the code would have to be adapted to each game (game specifics lie in: grabbing the player sprite and location). I am considering trying to make this for Super Metroid, the main hold-back being that all the emulators and memory watch utilities seem to be for windows.
Experienced Forum User, Published Author, Former player
Joined: 2/19/2007
Posts: 424
Location: UK
How about a run of Fire Emblem 4 - seisen no keifu? I enjoyed that game, and it is supposed to be one of the best Fire Emblem games. Since runs of other FE-games are on the way now, it would be nice to see one of this one too. It is in Japanese, though, but the dialogue would be skipped anyway, so that shouldn't matter much, and translations are available too.
1 2
16 17