Codex suggested I start a WIP thread, so here it is. :-) I guess the primary motivation for doing this was because the original XB game, as mentioned in the original thread, is as slow as a glacier and it was recommended that you play in Classic99's CPU overdrive mode. I found this to be the case and wondered what it would be like in assembly.
I'm not much of an "original concept" person when it comes to games, so starting with a working model really helps. I already have a playable code-base, but it is ripped right the moment while I add the next little bit. If anyone is interested I'll post a screen shot and the WIP code in a little bit (once it is stable again.)
I intend to publish the code as part of my unofficial "series" on programming in general, and the TI in particular. I have never actually *finished* a 100% complete game in any language (it is a tall feat!), so this will hopefully be my first!
Uhm, let's see, getting up to date. I'll try to talk about what I have done so far, then post as I go along.
First I had to dissect the original game to see how the spider AI and level generation was done. Since the contest only allows 30 lines of code, there is heavy use of multiple statements on one line and things are very dense and hard to follow in the BASIC listing. For the analysis I strung everything out in a text editor, replaced the target line numbers of GOTO statements with labels, and renamed some variables for better clarity. In my opinion BASIC is *very* hard to read since you cannot indent or use an decent structure, so reformatting makes it a lot easier to understand.
The method Codex used to to "generate" the levels was pretty slick. Instead of programming in all the data for the level (which would have taken up way to much space for a 30-line program), he used random numbers. While there is nothing unusual about random level generation per-se, what Codex did was take advantage of the fact that you can seed the random number generator (RNG) and always get the same sequence of numbers for any given seed. Usually you want to seed the RNG, uh, randomly, as to not get the same sequence each time the game is played. But for building the levels, this is exactly what was needed. Also, since each level gets a little harder (less intersecting and more single width grids), the choice of seed values needed to be determined. I suspect that Codex just started feeding seed values to the RNG until he found certain seeds that gave levels that were satisfactory, then those seed values were coded into a single DATA statement. Thus, for 5 completely different, progressively harder levels that are the same each time you play, all that was needed were 5 numbers. Sweet.
My problem was that I can't use XB's RNG in my assembly program, so I modified Codex's original game to spit out the random data used to generate the levels and I use that in my assembly program to reproduce the original levels. I'm not sure yet how I will deal with levels if I decide to take this further than the original 5.
Speaking of random number generators, the one I'm using is straight out of Tombstone City and it pretty week. Any suggestions or code examples would be greatly appreciated. I'm looking for code that does not rely on anything in ROM or GROM. The current code I have has an unfortunate side affect of the 2 least-significant bits are always 00, 01, 10, 11 and cycle in that order. I always forget that and it bites me every time I go to get a random number between 0 and 3 (like picking a random direction of up, down, left, right.)
The spider AI was simple, yet effective and gave me plenty to work with as far as the "speed up" and such.
The initial coding consisted of getting the screen initialized and reading the level data to get something on the screen. I'm not using any console routines, i.e. nothing from ROM or GROM, so I had to write it all. I like it this way though, and is one of the reasons for doing things in assembly. Hell, if you are not going to code direct to the hardware, you might as well not use assembly. All the console stuff uses BLWP anyway, which is slow! Plus it uses that GLP workspace stuff which I never learned, and GPL takes huge bites out of the precious scratch pad RAM. So, LIMI 0 and never look back. Something you do lose in doing that is the nice VDP interrupt, but we can work around that by polling, which for a game works just as good (if not better.)
After the levels were up, I needed to get the FlyGuy on the screen and moving. Hmm, keyboard and joystick input... ugh. I hate the way the 4A does the keyboard, hell, I hate the whole CRU idea. Ah well. So, off Thierry's Tech pages to read up on the 9901, keyboard, and joysticks. Actually, since I don't need the keyboard (and no, I will not use the KSCAN routine in ROM), getting to the joystick data is really actually very easy and fast. You can set the keyboard read-column to joystick 1 and just leave it that way. Since interrupts are disabled the console ISR won't mess it up, then all you have to do is use the TB (test bit) command in assembly to check the 4 directions. It was super simple and I was very happy about it. :-) Here is the code to set up to read the joystick:
LI R1,>0600 * Keyboard column 6 (joystick 1) LI R12,>0024 * Set the CRU port LDCR R1,3 * Load the CRU, setting the column latch . . . * Test the joystick LI R12,>0006 * Base CRU address for joystick 1 TB 1 * Left
That's it! After the TB command, you can use JNE or JEQ to do whatever you need. For Right you use TB 2, for Down use TB 3, for Up use TB 4. Fire would be TB 0. Now how simple is that!?
Okay, got FlyGuy on the grid, but damn, he is too fast! Pressing and releasing the left or right arrow as fast as I can moves FlyGuy from one side of the level to the other. This is why I love assembly. :-) Gotta slow him down. This is where I see a lot of dumb code in assembly. Slowing things down was typically done with busy-wait loops, but all that does it waste the time and also locks your game to a particular CPU can clock speed.
To control the speed of the game it is best to use a counter. Since it is a pain to use the 9901's counters, and since I have interrupts disabled, I have to poll the 9918A for the vsync signal, which if interrupts were enabled would cause the console's ISR to execute. Even though the vsync is only every 60th of a second, this is plenty fast enough for a human, and it does not matter if our game misses a sync or two because we took too long with game logic. The vsync also gives us a nice way to make a game timer in seconds. Last, it lets us control the speed of objects easily. So, for the FlyGuy I decided I would poll user input every vsync (60th of a second), and if there was a move then wait 1/12th of a second (5 vsync signals) before testing user input again. This makes for a pretty snappy FlyGuy, but still controllable and you can get him where you want him (at least I could). We'll see what the beta testers think though.
I also use this method for the spider speed. Something different about FlyGuy II over the original is that the spiders will move even if you don't. The spiders get to move every half a second, which is pretty quick. I had them every 10th of a second, then every quarter of a second, but those both seemed to fast. Like in the original, when a spider eats a pill they get a boost of speed. For this I used a ramp-up, so basically after one of them eats a pill they moved again in 1/60th of a second, then I increase the ramp-up by two, so they move again in 3/60th of a second, then in 1/12th of a second, and so on until their delay is back to half a second.
Currently I'm working on the part when FlyGuy gets captured. Things need to pause to a few seconds so the player can see what happened, and I'm thinking about some sort of "caught" animation, like most arcade games. Since this is all being written with little planning, I'm doing a lot of rework at this point to make things more logical and flow better, separating out functions and such.
So that's where it is at right the moment. Comments, questions, feedback, etc. are welcome and appreciated.