Latenite, VS 2005 and two screens.
Monday, 31st October 2005
Long time no update...
I haven't done any more work on FireTrack, that is now officially a dead project.
Rearranged PC
I spent some part of this weekend rearranging my PC so I could take advantage of my DVI port. With a DVI→VGA adapter I could connect up the other 17" monitor that was currently going unused on my PS2. I also had a new second-hand (new to me, that is) VCR to throw into the mix - here are the results! (Hover for notes).
Once again, I'm astounded as to how expensive cables are on the high street (last time it was a USB cable that threw me - £14 in Dixons, I ended up getting it for 99p on eBay). To connect the VCR to the VGA box I needed to convert the only output it gave me, SCART, to something usable - S-Video or composite. The only cable Dixons sold that converted SCART to composite out cost a whopping £39.99... no thanks! Maplin wasn't so bad, a two-way (switchable) SCART→S-Video/composite was only a tenner. Only thing left for me to get is a new VGA cable from my PC to the VGA box - my current cable is thinner than most serial cables I have! (In the photo of the VGA box, it's the one on the left) Needless to say, the image is very fuzzy and there is serious ghosting.
Visual Studio 2005
I recently received the Beta 2 in the post, and I'm (generally) highly impressed. However, I've run into a number of oddities along the way, not to mention some infuriating bugs. The worst is the image resource editor for adding images to menus and the like. Add a couple of images, set the image on a menu item and watch as it erases all your menus, adds them all back again before crashing with the error message:
...at which point the IDE closes.
Another oddity is that after such a crash my program started crashing when it closed. The error was in this code (added by the form designer);
protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); }
My only other major beef with VS2005 is that while most of the visual style bugs have been ironed out, some still remain. The worst one for me is the TabControl, as aligning the tab buttons to anything other than top results in some very odd results. Are MS ever going to decide on a consistent theme?
Latenite
Latenite is back in redevelopment - hopefully this version will be released! I have completely rewritten it from scratch in C# (previously in VB.NET) - for one weekend's work, it's not looking too shabby.
Yes, it looks like a direct rip-off of VS, but is geared towards Z80 editing. I'm sticking to making it look/work like a simplified VS, as that seems to be the best way for things to work (after all, most image editing apps feel like variations on a theme of Photoshop). Some new features:
- Fully XML-based help system makes help files considerably more manageable. HTML-based help viewer (rather than RTF) allows for better presentation and slicker (working!) hyperlinks.
- Error logs are also returned as XML for neatness.
- Compilation is now based around a bunch of sensible environment variables (the old system used a cryptic set of command-line arguments - %1 to %5). For example, this is the script for generic TI compilation:
CD %SOURCE_DIR%
SET PATH=%PATH%;"%COMPILE_DIR%\ti"
TASM -80 -i -b "%SOURCE_PATH%" "%SOURCE_FILE_NOEXT%.bin" | TASMERR > "%ERROR_LOG%"
devpac8%1 "%SOURCE_FILE_NOEXT%"
DEL "%SOURCE_FILE_NOEXT%.bin" - Ability to undo more than one edit and redo again! - This is a big one! (A new class that just adds to the )
- Sexy icon laden menus. If you create a compile script and save a PNG in the same folder with the same filename (so ZX Spectrum.cmd with a picture of a speccy as ZX Spectrum.png) it appears on the build menu.
- Faster and more accurate line selection.
There is still a lot (of very dull stuff, no less) to do - project organisation, settings saving/loading, text search... Bah. One day!
Useful script?
Thursday, 6th October 2005
If you have a website of your own, you might want to have your journal on it. (I do ).
This script can be used to generate a simple HTML document (such as this one!) containing a brief description of your recent entries, a title and a link to the item itself.
<?php # UPDATE_JOURNAL.PHP # Ben Ryves 2005. # CONFIG: $journal_id = 273102; # ID number of your journal. $items = 8; # How many recent journal entries do you want? $saved_page = 'journal.htm'; # Which page do you want to save the journal to? $password = 'password'; # What's the password required to fire off an update? if (isset($_POST['submit'])) { if ($_POST['password']==$password) { # Run the update script: $url='http://www.gamedev.net/community/forums/lib/rss.asp?mode=journal&jn='.$journal_id; $handle = fopen($url,'r'); $writer = fopen($saved_page,'w'); if (!$handle) { echo "<p>The RSS feed is not available at the moment - sorry.</p>"; } else { global $cur_pos, $buffer; $buffer = '; while (!feof ($handle)) { $buffer .= fgets($handle, 4096); } fclose($handle); $cur_pos = strpos($buffer,"<item>"); for ($i=0; $i<$items; ++$i) { $title = get_between_text("<title>","</title>"); if ($title===false) break; $link = get_between_text("<link>","</link>"); $description = html_entity_decode(preg_replace("#<(.*?)>#i","",get_between_text("<description>","</description>")),ENT_QUOTES); $description = preg_replace("#<(.*)#i","...",$description); fwrite($writer,"<p><b><a href=\"$link\" target=\"_blank\">$title</a></b><br /><small>$description</small></p>"); } fclose($writer); echo "<p>$saved_page has been updated!</p>"; return; } } else { echo "<p>Invalid password: sorry!</p>"; unset($_POST['submit']); } } ?> <form method="post"> <p>Please enter your password: <input type="password" name="password" /> <input type="submit" value="Go!" name="submit" /> </p> </form> <?php function get_between_text($before, $after) { global $buffer, $cur_pos; $cur_pos = strpos($buffer, $before, $cur_pos); if ($cur_pos===false) { return false; } else { $cur_pos+=strlen($before); $e = strpos($buffer, $after, $cur_pos); return substr($buffer, $cur_pos, $e-$cur_pos); } } ?>
You need to set a password just to stop people from running the script (it's a pretty basic system). You'll also need to set the correct ID number. It's a very quick-and-dirty script, so if you have any problems with it give me a shout!
Scripted attacks
Monday, 3rd October 2005
Attack patterns can now be simply scripted as a list of 4-byte chunks, covering which enemy to use, the delay between adding them, how many of them to add, the delay between them and a delay after adding the last one.
For example, this:
.db $04,16,8,100
...would add 8 enemies of type $04, adding one every 16 game ticks and then pausing for 100 game ticks after adding the last one then progressing to the next scripted enemy.
To support this, I've added some new per-level parameters, covering:
- Attack type (random or 'scripted') with delay between enemies or a pointer to script to follow.
- Speed of enemies.
- Speed of landscape scrolling (used very little - bonus levels with no enemies/mines scroll past extra-quickly).
- Maximum number of mines.
Using all the above, I can easily configure each level's attack patterns quite simply.
Doing this has identified a number of bugs (mostly where new enemies were being initialised without clearing out a particular byte, which means that certain sequences would start in odd places) which have now been ironed out.
I have also picked up work again on my music system for the game. I have a very bad 12-bar-blues demo running with it - I need to find a decent pitch-to-period table as the one I calculated in Excel sounds slightly wrong. There are also some minor-ish reset bugs (the first time in-game a note is played the instrument is full volume for one frame plus some minor synch issues). Looks like I'll have to write the music in Notepad, though - who'd have thought that getting low-level access to the sound card was so bloody difficult in anything other than C (and I'm damned if I'm going to have to write a GUI system for the Windows console, and Win32 is too mucky to deal with for such as simple application). This is great fun, as you can imagine - take, for example, this: (the 12-bar-blues demo for the testbed for the music system)
; Demo tune demo_tune: ; Instrument table: .db 2 ; Number of instruments .dw simple .dw vib ; Sequence table: .db 6 ; Number of sequences .dw run_c .dw run_f .dw run_g .dw bass_c .dw bass_f .dw bass_g ; Tune! .db %00000000, %00000000 .db %00000011, %00000001 .db %10000000, %10001000 .db %00000000, %00000000 .db %10000000, %10001000 .db %00000001, %00000000 .db %00000100, %00000001 .db %10000000, %10001000 .db %00000000, %00000000 .db %00000011, %00000001 .db %10000000, %10001000 .db %00000010, %00000000 .db %00000101, %00000001 .db %10000000, %10001000 .db %00000001, %00000000 .db %00000100, %00000001 .db %10000000, %10001000 .db %00000000, %00000000 .db %00000011, %00000001 .db %10000000, %10001000 .db %00000000, %00000000 .db %10000000, %10001000 .db %11111111 ; Sequences: run_c: .db 1 .db (144>>8)+%01000000 .db (144&%11111111) .db $10 .db (112>>8)+%01000000 .db (112&%11111111) .db $10 .db (93>>8)+%01000000 .db (93&%11111111) .db $10 .db (82>>8)+%01000000 .db (82&%11111111) .db $10 .db (77>>8)+%01000000 .db (77&%11111111) .db $10 .db (82>>8)+%01000000 .db (82&%11111111) .db $10 .db (93>>8)+%01000000 .db (93&%11111111) .db $10 .db (112>>8)+%01000000 .db (112&%11111111) .db $10 .db %11000000 run_f: .db 1 .db (105>>8)+%01000000 .db (105&%11111111) .db $10 .db (82>>8)+%01000000 .db (82&%11111111) .db $10 .db (68>>8)+%01000000 .db (68&%11111111) .db $10 .db (60>>8)+%01000000 .db (60&%11111111) .db $10 .db (56>>8)+%01000000 .db (56&%11111111) .db $10 .db (60>>8)+%01000000 .db (60&%11111111) .db $10 .db (68>>8)+%01000000 .db (68&%11111111) .db $10 .db (82>>8)+%01000000 .db (82&%11111111) .db $10 .db %11000000 run_g: .db 1 .db (93>>8)+%01000000 .db (93&%11111111) .db $10 .db (72>>8)+%01000000 .db (72&%11111111) .db $10 .db (60>>8)+%01000000 .db (60&%11111111) .db $10 .db (53>>8)+%01000000 .db (53&%11111111) .db $10 .db (49>>8)+%01000000 .db (49&%11111111) .db $10 .db (53>>8)+%01000000 .db (53&%11111111) .db $10 .db (60>>8)+%01000000 .db (60&%11111111) .db $10 .db (72>>8)+%01000000 .db (72&%11111111) .db $10 .db %11000000 bass_c: .db 0 .db (307>>8)+%01000000 .db (307&%11111111) .db 8 .db (144>>8)+%01000000 .db (144&%11111111) .db 8 .db %11111111 bass_f: .db 0 .db (224>>8)+%01000000 .db (224&%11111111) .db 8 .db (105>>8)+%01000000 .db (105&%11111111) .db 8 .db %11111111 bass_g: .db 0 .db (198>>8)+%01000000 .db (198&%11111111) .db 8 .db (93>>8)+%01000000 .db (93&%11111111) .db 8 .db %11111111 ; Instruments: simple: .db 4 ; Length .db 255 .db -64 .db 8 .db 0 .db 32 .db 1 .db 255 .db 0 .db 0 vib: .db 2 .db 128 .db 50 .db 2 .db 128 .db -50 .db 2 .db 128 .db 50 .db 1 .db 128 .db -50 .db 1 .db 128 .db 50 .db 3 .db 128 .db -50 .db 10 .db 128 .db 14 .db 0
Thankfully, the assembler can handle me sticking sums in rather than hard-coded values in places making things a lot simpler... but it's still a bit mucky. Ah well. There is no noise channel set up, either, so no krch-krch-krch style beats from the white-noise generator as such.
Enemies Galore
Friday, 30th September 2005
I completely rewrote every single attack pattern from scratch (pretty much - the precalculated paths were left intact) last night. The result of having the ability to start any enemy attack pattern at any time (within reason - I only provide 8 enemy slots!) results in nothing short of sheer mayhem. Yes, collision detection was disabled for the video. I'm looking at you, evolutional...
Is that enough enemies, do you think? [AVI/MPEG-4/1.62MB]
The small blobs that drift towards me are the aforementioned mines. The attack frequency, number of enemies on-screen and number of mines is far in excess of anything that you'd encounter in the game - this was just a test to add as much to the game as possible.
Another plus-point of the rewrite is that the game now runs correctly inside MEKA, another widely-used Sega Master System emulator. It had issues with certain attacks keeping their sprites way off screen as they ran in, which worked fine on hardware and in Emukon, but not in Meka for some bizarre reason.
One particular running bug that has been very difficult to trace was an annoying flicker in the sprites. Every so often, some of the sprites would disappear for a single frame. Normally it would be a couple, but sometimes ALL the sprites would vanish.
What could it be? Video timing issues? No, Emukon (the emulator I'm using) pops up incorrectly timed VDP writes in a handy window. Enemy drawing code that was accessing another sprite's data, switching it off? In a similar vein, had I miscounted pointer increments in one particular chunk of code for handling enemies, so in certain conditions it would offset all further array writes by a byte, writing incorrect data into the "enemy disable" byte? After much painful analysis, it appears the routines were pretty watertight... Not PUSHing/POPping registers to the stack before calling a destructive routine?
Well, after much pulling out of hair I have found the problem.
The VDP (the graphics chip I'm controlling) has a certain amount of VRAM. One area in that VRAM is dedicated to a sprite table. First you have a list of Y-coordinates, then a list of X-coordinates followed by sprite index numbers. There is a "magic" Y coordinate, $D0. When the VDP hits this particular Y coordinate, it stops drawing sprites! You can (and I do) use this as a stop byte on the sprite table to tell the VDP when you've finished adding sprites (as each frame has a variable number of sprites on it - I'm trying to keep the game nice and dynamic).
So, what's the problem with that? It turns out that SOMETHING - star code, explosion code, bullet code, enemy attack code, I'm not sure which - is trying to draw sprites with a Y coordinate of $D0! Adding this block of code into my sprite drawing routines (I have two - one for 8x16 sprites, such as the stars, bullets and score digits and another that pairs them up for 16x16 sprites, the size of everything else) fixed the rare flicker bug.
cp $D0 jr nz,+ inc a +:
Nice and simple, but it does the trick.

ARGH!
Thursday, 29th September 2005
Fire Track (on the BBC Micro) is probably the fastest, hardest, most adrenaline-packed space shooter I've ever played!
I'm playing my way through the extended version to try and work out the game mechanics a bit more. I've been using an ancient "infinite lives" cheat disk with it, but even so... whew, this game is tough.
I have saved state images for Levels 1 to 22, though, at last. My nerves are shot to pieces, in any case!
It appears that I have to rethink the way enemies are handled. Currently, I store a list of 8 enemies and assume they all follow the same pattern. Wrong. For most of the game, you can have lots of enemies on-screen, each from a different pattern! Or the patterns can overlap, even. What I think I'll do instead is have a list of enemies with ID (attack type - I'll use the sprite number), (X,Y) coordinate (if it's (0,0) it means the slot is free, no enemy) then 5 bytes of extra storage that the enemy attack handler for that enemy can do whatever the Hell they like with. If I have a timer that decreases and fires off the next enemy whenever it underflows (either a predetermined one from a table of enemy attacks or just picking a random one - or, for some of the bonus levels, none at all). Plus, I can rewrite it without lots of use of the slow IX register.
I have, however, added mine support. There's a function to add a mine at a particular (x,y), where it gets set up correctly and will drift towards you (the DX and DY are set up when you add the mine). They aren't part of the collision detection routines yet (I'll add them with the enemy system rewrite) but already scare me
The above is the worst-case scenario - there are not usually more than 4-5 mines on screen at any one time.
As you can probably also see, there is still no black bar at the top of the display. I can't quite work out how to enable/handle line interrupts from the VDP, but have found a nice part of the VRAM that isn't being written to that is just the right size for use as an alternate name table. The way this'll work is that I switch the background layer to another location in VRAM for which tiles to use that is all blacked out, wait until I hit a scanline 8 pixels down then switch back to the name table which contains my real background layer. I don't have to deal with cutting out any sprites as I have 8 sprites dedicated to the score at the top, and as you probably know by now from my constant moaning about it there is an 8-sprite-per-scanline limit hence my score will stop anything else from being drawn in the sprite layer at the top.
Finally, here's a picture of the biggest pest I know - linkified for the spider-haters. These buggers are not very large (5-8cms, biggest approaching 10cms) but very annoying - they seem to like following set paths, and will run around back and forth on the edge of my vision. This particular one was very reluctant to be picked up and lobbed out of the window...