1 2
5 6
Editor, Skilled player (1172)
Joined: 9/27/2008
Posts: 1085
http://tasvideos.org/userfiles/info/12525896296055538 - Completed run. I did try to get those blue asteroids to follow me long enough to reach the boss, but failed this time around. TASing this segment has a far different feel than the other parts. The game really seems to take a completely different path here. I may try again, hopefully to get them to stick around long enough to take up enough object slots to stop the boss from spawning properly. That's my guess on how it happens. After a certain point, these blue things stop accelerating up or down, and accelerate in whatever the left-right direction they're facing to maximum speed. Anyway, hopefully most of it is entertaining enough. I'll probably look at a few points and see if I can make better changes. It's submit worthy as it stands, anyway.
Player (243)
Joined: 8/10/2008
Posts: 113
I did some real-time testing regarding the despawning of the fifth eye of the last boss. I used FatRatKnight's video and made a save states a little before the area I've had the most luck with previously for the despawning. I first wanted to do a few test attempts to get a feeling for the game again. On the third try, I managed to get it. I then spent well over an hour trying to replicate it while recording but to no avail. From what I remember, I used to have ~10% success rate of despawning the eye on the pal-version, so not succeeding for over an hour seems exceptional. As I mentioned in my previous posts on this trick, I know the RAM-address to look at, but I don't understand the underlying mechanics that allow this. I therefore don't know if it's more difficult on the ntsc-version (but I can at least confirm that it's still possible) or if it's just something about memory state of the movie file that makes it difficult. I plan to eventually get back to this game for a console speedrun, which means I'll get many more opportunities to test this under different conditions. I'll post here if I find something of interest (unless the trick has already been figured out by then).
Editor, Skilled player (1172)
Joined: 9/27/2008
Posts: 1085
I'm going to dig through my notes on what the memory addresses are...
There are 31 objects. The listed address is the player's.
Everything up to +0x1E of that is the same stat for other objects.
   200 = X-pos(lo)
   21F = X-pos(hi)
   23E = Y-subpos
   25D = Y-pos(lo)
   27C = Y-pos(hi)
   29B = X-Spd(lo)
   2BA = X-Spd(hi)
   2D9 = Y-Spd(lo)
   2F8 = Y-Spd(hi)
   317 = Obj ID (zero means it doesn't exist)
   336 = X-Dir (bit 0x01)
   355 = Y-Dir (bit 0x01)
   374 = Facing (For the player, at least)
HP is elsewhere, but it doesn't line up nicely with this stuff. I will need to look through various things to get an idea. I wouldn't be surprised if they simply don't have the stat existing, such as... Well, HP for things that don't take damage or do so in an alternative fashion (like the player).
Site Admin, Skilled player (1236)
Joined: 4/17/2010
Posts: 11274
Location: RU
If a movie that despawns the eye gets recorded, I will find out how to reproduce it. And why some enemy wasn't stationary. Because then it would sound like a good improvement.
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.
Player (243)
Joined: 8/10/2008
Posts: 113
feos, I actually have it recorded, just that it was done on the E-version (because I only had a pal-console at that time). Quote from page 5 of this thread:
* I've managed to despawn one of the final boss's eyes a couple of more times and also got it recorded with a newer emu (fceux 2.1.4a, (E) [!]-rom): http://dehacked.2y.net/microstorage.php/info/1267680606/Solar%20Jetman%20-%20Hunt%20for%20the%20Golden%20Warpship%20%28E%29.fm2 The addresses 0461, 0C61, 1461 and 1C61 all change value from 94 to 0 at frame 640 and that's what triggers the despawning. The other eyes have neighboring addresses, but I've never managed to despawn any of them.
Site Admin, Skilled player (1236)
Joined: 4/17/2010
Posts: 11274
Location: RU
E movie: Xcam = 2416: $8235, 0 read from pointer ($9469) to $C0 Xcam = 2418: First horizontal eye (upper) is skipped (since $C0=0) Xcam = 2418: $8235, 3 read from pointer ($9422) to $C0 Xcam = 2420: Second horizontal eye (middle) spawns (since $C0=3) U movie: Xcam = 2418: $8235, 4 read from pointer ($93F4) to $C0 Xcam = 2418: First horizontal eye (upper) spawns (since $C0=4) Xcam = 2420: Second horizontal eye (middle) spawns (since $C0=4) From that, I presume some routine got shifted in time during the E movie gameplay. Will see tomorrow what caused that. Can I have a E movie where that eye spawns?
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.
Player (243)
Joined: 8/10/2008
Posts: 113
The despawning is a very precise trick. If you just stop holding A for one frame a little before frame 640, the memory value won't change to 0. I can make a new movie for you, but it should be easy enough for you to just use the existing movie.
Site Admin, Skilled player (1236)
Joined: 4/17/2010
Posts: 11274
Location: RU
Uh, I can't get it to work. Part of it is having 1 in $03AF, which gets written there only at that place, since the big stone that gets broken is in slot 0x1C, and it's descriptor at $03AF (that looks like sprite timer) becomes 1 if there are enough shots on the screen. When the huge rock is crushed, small stones, if shot, have their timers going 4 to 1 in loops, but when there's enough lag, most of that is skipped, routine gets interrupted by PPU stuff, and 1 sticks in that address forever, while the stone itself isn't crushed. That 1 is then used by Y register, when stuff is written to adresses $0460+. When Y is 1, 0 is written to $0461. There you go. So basically, the whole trick is trying random shit to get these 2 during the same few frames. I could get that 1 there, but the rock only crashed into ONE piece, and then nothing cool happened. http://tasvideos.org/userfiles/info/12852061334348625
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.
Site Admin, Skilled player (1236)
Joined: 4/17/2010
Posts: 11274
Location: RU
As for flying enemy, seems to happen only for ones with ID 0xA4 (really stationary are those with ID 0x97). Then, when ($5B && 7 == 3), 2 is written to $430+slot, and it starts flying. $5B is part of RNG that is bit-shifted every frame.
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.
Player (243)
Joined: 8/10/2008
Posts: 113
I've managed to warp from planet 1 to planet 8! Link to the video and the powerpak save state is found here: https://forum.speeddemosarchive.com/post/solar_jetman_nes_7.html If this can be repeated, it would save several minutes off the current TAS. I have poked around a bit in the code, but I'm not sure what to look for next. Unfortunately, I haven't been able to reproduce this, so there is no emulator movie to dissect. My best guess is that a random enemy somehow got attributed the warp value. Maybe lag or some kind of overflow are involved, similar to the eye despawn described above?
Player (243)
Joined: 8/10/2008
Posts: 113
I've managed to record an emulator movie showing the warp from 1 to 8: http://tasvideos.org/userfiles/info/58524046396026308 The second warp object value is stored in $325. Some of the other properties can easily be found by applying an offset to the addresses in FatRatKnight's post higher up. I'm primarily trying to trace back in the code why it spawns (frame 2609). I'm struggling a bit though, so any help analyzing the mechanics behind would be much appreciated. Firing at a steady pace to ensure the object adresses are kept full seems to be a key. The resulting bullet pattern also appears to matter. I haven't been able to identify what determines if it's a correct bullet pattern or not though. I would also be interested in why it gets the value corresponding to the warp (#$0A). I've traced it back a few steps, but I eventually get stuck on RAM-addresses whose purpose I have trouble finding. As far as I can tell, if something spawns, it's always going to be the second warp though. So it's more out of curiosity than an actual need for manipulating something.
Editor, Skilled player (1172)
Joined: 9/27/2008
Posts: 1085
Skipping an entire planet off the current route sounds rather interesting. I may not want to TAS, but I'm into analyzing things for now, so let's see what makes this tick. I've only briefly looked at the provided video. So I see the results of it, not its setup. A moving warp where it should be stationary is interesting. If shooting stuff to fill out the object addresses is apparently needed, then I suspect the timing of the interrupt routine is key to corrupting the warp. It sounds like something that should not happen but does. So, time to crack out my old scripts to relearn what's part of the object data, and the debugger to see what it's trying to do when being created. Any information on whether you might have shot the warp before the powerpak save state?
Patashu
He/Him
Joined: 10/2/2005
Posts: 4017
In ktwo's second post they have a movie that reproduces the glitch, so you shouldn't need to analyse the powerpak version further.
My Chiptune music, made in Famitracker: http://soundcloud.com/patashu My twitch. I stream mostly shmups & rhythm games http://twitch.tv/patashu My youtube, again shmups and rhythm games and misc stuff: http://youtube.com/user/patashu
Editor, Skilled player (1172)
Joined: 9/27/2008
Posts: 1085
I see it. Hadn't taken a look at it until recently, and I had a few assumptions about "begins from savestate." Without looking at it, I don't really have a good defense. Trying to build a lot of the context from scratch isn't helping much, though. I've been trying some disassembly and setting breakpoints, though I wouldn't say I've gotten things figured out. We can add 0393 to our list of object stats. Apparently, hitting a warp takes whatever is around that location (depending on object index, of course) and uses it as our destination planet. In the case of the movie, the object is at offset +0x0E, so 03A1 is our address when we hit that odd warp. As for what fills stat 0393, I'm not entirely certain. There's some kind of index that goes through increments based on how many objects exist, I think? The fact we have another warp object means an extra spot for an increment of this number, so the second warp leads to planet 8. We're out of object slots, having filled all four of the parts slots in the object table, so no additional increments look to be possible. 00BB is temporarily used to assist in filling 0393. Plenty of sub-frame calculations are done, so no guarantees memory watch is helpful. No analysis on how the extra warp came to be. This isn't really a conclusive analysis, though. I don't have all that great an idea on the pattern they decided to code in.
Post subject: Debuggery. It's not solved, but I'm fetching more info.
Editor, Skilled player (1172)
Joined: 9/27/2008
Posts: 1085
Okay, okay. If I'm going to figure out much, I might need to track down how the game determines a hit. The Trace Logger and the Code/Data Logger together on FCEUX does amazing things. Set the Code/Data Logger, run the game on things leading up to, but not including the collision, then tell the Trace Logger to log only the newly mapped code the Code/Data Logger hasn't passed over yet. Sure enough, I find the code that works out how the bullets collide with enemies and other stuff they can collide with. The routine starts at 02:8272. I don't entirely understand how to tell bank, but I'm pretty sure that 02 has something to do with what bits of code is paged in. That's not too important anyway. What is noted is that I set a breakpoint on $8858, and it only triggers whenever bullets smack into something. Okay, it also triggers on loading a planet or something, but the breakpoint I set wasn't smart enough to care about what page it's on (it wasn't bank 02). So, what counts as a bullet hit? The routine at $8272 checks for a number of collisions, but I'm going to skip ahead to the part where it handles player bullets. Many, many bytes later, I'm looking at $8434. I see LDY #$03, and I know player bullets are IDs 03 to 0A. It scans until it finds a valid player bullet, then does a bunch of processing. I'm running my published TAS, and set a break for when I shot the warp. The Trace Log reports that a successfully detected bullet (ID 04) had stat 0317 (with ID 04, address 031B) == 08, which was not zero, which was not less than 7, and address 000D was equal to zero. After these conditions were checked, it then did LDX #$17, where enemies and other shootable things fill IDs 17 to 1E, and then starts checking stat 0317 of those things. Skip ahead to when it checks ID #$19, the object ID which spawns a warp portal when shot. 0317,X (== B8) wasn't zero, wasn't less than 6, and 0374,X (== 9D) wasn't FF. Without looking too deep, I also spot stats 0200 and 021F of both the bullet and the target being mathed together, then the same for 025D and 027C. Hitbox position checks. So... bullet existed, target existed and apparently accepting hits, and the two intersect. Call function. Let's open the movie that got our Warp to 8. Breakpoint on function $8858, and see what I get. Frame 2596: Object ID 1C (== B8) was hit by bullet ID 09, and got our first warp spawner. Frame 2597: Object ID 1C was hit again, by apparently the same bullet ID 09 as before. One frame apart? Normally the game does things every two frames. Also, the same function was called by the same offending bullet and target. I'm starting to think some interrupt acrobatics is taking place.
Post subject: Random plans for low money in later planets.
Editor, Skilled player (1172)
Joined: 9/27/2008
Posts: 1085
Alright, guesses. I'm going to guess that when the interrupt happens, it happens during the all-important collision function, and it wants to try that function back from the start. So, it retries the same bullet-target hitbox check and runs it again. Except the function was fully complete, had successfully spawned the object in its entirety, and the spawner code then looks for the next open slot as it didn't un-create the thing. If that's the case, then I'm going to hate trying to find the magic combination of sprites to busy the hit detection function by exactly enough. I will leave that to someone else to work out. I am willing, however, to generate a script that can tell you precisely when the important collision happened relative to the interrupt, but not willing to do the ground work to make it happen. Regardless, I have my question. Let's see if it's the right question to ask. Hah! Interrupt during handling the effect of the collision! But what is the consequence of this? Well, when the double hit occurred, the first hit had enough time to read $00BA, use it as an index to stats in the $500s ($052D,Y, $0547,Y, $0561,Y, $057B,Y), and copy the X,Y position of the shot object into those spots, then store the value 20 into $0513,Y. It also increments $00BA. Then it jumps to $8922 where it asks about $0374,X, with X still referencing the shot object, and it not being #$FF, it'll then go on to various management, which I haven't carefully looked at. The interrupt happens here, cutting off the management, and apparently never comes back to it. As a result, the bullet and shot object are still free to interact normally. The stuff over in the 500s are things for the game to spawn later. We didn't stuff just one portal in there, but stuffed two there thanks to a well-timed interrupt. So that the game thinks that it should generate the two a few frames later. Anyway, I still haven't cracked heavily into code analysis, but judging from the timing of the interrupt, and what the interrupt apparently does when it comes to the hit detection code, I'm around 80% certain it's based on the code being unstable around interrupts. Which is why the bullet spam is needed, I guess. ... Anyone need a script? EDIT: If this is successful, we're starting planet 8 with the starter 1000 cash. We'll then warp to 13, then start pushing through 12. The Sports Jetpod is really far out of reach with this route, so we'll no longer swing wormholes for fun and profit. No carrying them to items, no using them for breaking the 3.0 speed limit, none of that is possible. We can buy one Double-Strength Thruster or one Anti-Gravity for Planets 8 and 13, and we can buy two more after the singular challenge room prior to Planet 12. From what I remember, we'll still want to self-destruct in Planet 12, as while I do cart a wormhole all the way to that last fuel unit (in the published run), there is another wormhole a short distance further ahead. Due to menu time, we're highly discouraged from picking up a Double-Strength Thruster on Planet 8, then a second one on Planet 12. Anti-Gravity is very quick into the menu, so we can pick one up early and another later without loss. Therefore, we should only have one Double-Strength if we need it for Planet 13, but we can have one or two if we use Anti-Gravity instead. So, two Anti-Gravities and one Double-Strength Thruster, or two Double-Strength Thrusters and one Anti-Gravity? And which one for Planet 13? And when making the sacrifice at Planet 12, it looks likely we'll want both flavors for the first half, considering we're carting around a lot of fuel. We're doing a lot less carting around after the sacrifice, so one item should work fine. But it is possible to route in just one item on the first half so we'll have both for the final run, if that somehow saves more time. So many potential routes with the Double-Strength Thrusters and Anti-Gravity... P13 DST -> P12 DST+AG / AG P13 DST -> P12 AG / DST+AG P13 AG -> P12 DST+AG / DST P13 AG -> P12 DST+AG / AG P13 AG -> P12 DST / DST+AG P13 AG -> P12 AG / DTS+AG
Player (243)
Joined: 8/10/2008
Posts: 113
Thanks for all the information posted so far. While I'm very interested to learn more about what's going on, I don't have the motivation right now to contribute much (or even follow in detail what you have found). It's temporary though and I'll take a look at it when the time comes. However, I think it's unlikely I'll work on an improvement for implementing the warp to 8 (and let's not forget despawning one of the eyes of the final boss!). Don't let that stop you from publishing what you have found in this game though (scripts etc). Personally I was hoping you would feel the urge to make the improvement yourself (along with finishing the R.C. Pro-Am II TAS!), but that's clearly something that should come from own motiviation and not because someone else wanting it done... From RTA-testing, I've gotten the second warp about 4 times now, I think. A rough estimate is that I've done a few hundred attempts of "blind" testing to get this result. By "blind" I mean that I've just mashed like I normally would to shoot the warp, but without any specific mashing pattern or otherwise looking for specific enemy patterns etc to be present. When posting about this finding, other players have reported also getting the second warp, but it had only been shared in various chats. Anyways, this was just to say that it doesn't seem like the conditions need to be "needle-in-a-haystack" specific for this to occur. I would be surprised if it turns out to be a difficult decision on what to buy. My impression of anti-gravity is that it's pretty slow without boosters. I would expect that buying double-strength boosters after 8 and 13 is the fastest. But it's not something I've tested, so it's just a gut feeling.
Editor, Skilled player (1172)
Joined: 9/27/2008
Posts: 1085
I did some light testing, at least. I just took my TAS, slammed out 8 bullets, avoid killing that nearby turret, and have the 8th existing bullet be the trigger for the warp. Failure. I did watch the cycle count between "hit detected" and "interrupt happened" decrease from 3678 to 589. Maybe a simultaneous hit can push it over? Maybe spawn another enemy? The successful movie spams my script output (the script is sloppy, though). I see 109 cycles between hit detected and interrupt happening, and I think that's close to the low end we're aiming for. At least, I hope it's all about the interrupts. Download SJ_CycleCount_FCEUX.lua
Language: lua

local R1u= memory.readbyte local Hits= {} --***************************************************************************** local function Hit_Check() --***************************************************************************** if R1u(0x8858) ~= 0xBD then return end --Bank check --Sloppy target check (like, real sloppy) local Reg_X= memory.getregister("x") if (Reg_X < 0x17) or (Reg_X >= 0x1F) then return end table.insert(Hits,debugger.getcyclescount(()) --Add entry end memory.registerexec(0x8858, Hit_Check) --***************************************************************************** local function NMI_Check() --***************************************************************************** local Cycles= debugger.getcyclescount() debugger.resetcyclescount() --A reset function! Convenient. local hit= table.remove(Hits,1) while hit do print(string.format("Frame=%d - %d/%d(%d) Target hit.", emu.framecount(), hit, Cycles, Cycles-hit )) hit= table.remove(Hits,1) end end memory.registerexec(0xFFE3,NMI_Check)
1 2
5 6