Pages

Wednesday, 26 July 2017

XORin' in the Free World

One day, I thought it might be possible to re-create my Fort Django on the ZX Spectrum. I abandoned this exact goal as there's not that much material that can be re-used and some of my findings discouraged me from this route.

Instead the code became an exercise in game sprite graphics. I wanted to have three huge-ish characters on screen with a smooth framerate, with a game that would again float somewhere near the Saboteur!, Bruce Lee and Dan Dare territory.

I decided on XORed sprites, so I don't have to do masks. XORing graphics means that the underlying pixels are inverted with the sprite pattern. It's a nice method in that with XOR the sprite drawing and erasing routines are exactly the same, because re-drawing on the same position leaves the screen as it was before drawing.

Granted, it can look a mess whenever the sprites overlap. Many complained about colour clash in ZX Spectrum games, but I guess XOR was partly to blame, too. Atic Atac is maybe the best game that uses the XOR approach, and it's very fast.

Early days. Using sprites adapted from Fort Django
I needed to plan the graphics and the game around the XORing artefacts, so it would not get too bothersome. This was the reason to move away from the Fort Django concept, as the ladders and furniture might create too many disturbances around the moving, overlapping graphics.

Another weird technique is to draw the sprites when entering the frame and erasing them on the way out. With a Commodore 64 you could simply check for the suitable scanline, but with a ZX Spectrum this is not possible. I'll simply have to ensure that each frame still does the same things even if there are no enemies on screen. This also means the game is fixed for the 3.5MHz timings. Such an approach is not very elegant nor portable, but it's justified to get a silky-smooth game in a closed environment that the 8-bit computer is.


The sprites, actually

Although XORing is a fast technique, I still had to revise my approach a bit. My initial target of 40x64 sprites with smooth framerate were clearly out, so I went for 40x48 instead, which is the size of the Saboteur human characters.

I can't even have true 40x48 sprites, but by doubling the vertical pixel size it is possible. Not only are the pixels doubled, but the underlying vertical screen resolution needs to be divided by two to make the XOR draw/redraw logic work. So I'm working with 256x96 screen with 1x2 proportioned pixels.
Drawing one "line" of the sprite data as 1x2 pixels.
This has the benefit that as I fetch the sprite graphics, I can assume the two subsequent screen line contents are the same, so the routine does not have to read them separately.

The sprite graphics are drawn from left to right, zig-zagging the two lines. The stack is pointed to a table that has the vertical screen addresses sorted out for every other pixel row coordinate, and these addresses are pop'ed for each sprite line. The horizontal component of the address needs to be added, too.
1x2 pixelled sprite data pictured in GIMP, with one data line highlighted.
The sprite Y-coordinates are also constrained to multiples of 2. This has the benefit that there is never a troublesome screen address boundary between the zig-zagged vertical pixel rows. Within the line, the zig-zagged pixel row can be changed with just incrementing or decrementing the address high register.

Other good things came out of the 1x2 pixels: Instead of needing 256 bytes for each sprite frame, I could fit a graphic frame inside 128 byte boundaries. The 40x64 sprites would not have fit in a 256-byte boundary anyway.

The pixel ratio is not that limiting, as the sprites can be drawn deal with it rather than stubbornly make something that does not fit. My current sprites are hardly the pinnacle of ZX graphics, but it looks promising. And of course the screen portions where the sprites are not drawn, can be in 1x1 pixel format.
Toying around with some gfx pretty much ripped from Dan Dare.
With this tweaking of the resolution I could draw apparent 40x48 pre-shifted pixel areas three times, after which the scanline is at the 16th line of the Display File. This would mean that a moving sprite at the top of the screen could become distorted.

I could use a portion of the screen for a dashboard, as in the image above, and these 16 lines might not matter. I am making a game after all, and not a generic sprite routine.

But then the silly me realized that if the sprites are drawn in a certain order, the scanline intrusion can be made nearly meaningless: If the first sprite to be drawn is at the top of the screen, the second at the middle of the screen and the third in the bottom of the screen, all happens smoothly by "racing the beam". I only have to take care that not all sprites move near the top or bottom at the same time.

The diagram below shows what happens during one frame. These are not based on actual values, the picture is exaggerated for clarity. In this example only the second sprite is truly both a: drawn before the scanline enters the pixel drawing area, and b: erased after the scanline leaves the pixel drawing area.


This brings certain limitations to what can be performed with the sprites in the game. For example, only the player character has full freedom to move all around the screen, whereas the other sprites would stay within invisible "cages". Yet these cages are so lax that by switching sprite positions these cages do not matter much.

Technically, it would even be possible to draw more sprites than the three, if the sprite drawing order is well managed. This is somewhat equivalent of multiplexing sprites on computers that have real sprites and good scanline routines. But I felt this might become a bit too complex programming exercise or limit the "cages" a bit too much.

So, there are now three "big sprites" and the player sprite is always the second sprite to be drawn. The game, whatever it might be like, has to be designed around the small limitation that the enemies can't go everywhere.

Animation frames were another source of trouble. I wanted to save frames by "baking in" the leg motion inside the shifted frames. But this produced too fast animation. So instead of four shifted frames there would be eight sprite frames for simply making the guy walk on screen. The eight frames make a total of 1K graphics laid out in 128-byte address boundaries.

What else? I didn't make a clipping routine, even if it's not too slow to check the coordinates and divert to another sprite drawing routine. I wanted to negotiate some programming time out of this project.


Dude, where's my game?

So, after making the to-hell-with-portability sprite engine, I could concentrate on the game. All I've made since the sprite routines is a tile-drawing/collision routine, a bit more joystick stuff and experimenting with ways to draw unobtrusive objects. It looks nice, but currently a bit limited for a proper game. I'll get back to this in some form, but it doesn't seem to happen anytime soon.


Wednesday, 5 July 2017

The summer holidays Ultima IV bash-through


Much like returning to some books and films time and time again, I feel occasionally compelled to complete Ultima IV. This is not as time-consuming as it sounds, as DOSBox makes the game very fast. I also use any help available on-line, such as the dungeon maps. The last few times I've played the 256-color modded game, but now I chose the more vanilla version. I know there's the luscious Commodore 64 update, but I can't bring myself to play a slow version any more.

It's always wise to keep the party small in the beginning, but this time I tested a theory that I would be better off without recruiting anyone until absolutely necessary (i.e. the very end). This makes the solution even faster, as the combat sequences and dungeon rooms are more simple to navigate with just the one guy. The battles are generally so easy in this game a fully maxed party is not really needed even in the final Abyss dungeon.

I picked a class that can use magic and can wield the magic wands (Mage, Druid, Bard). This time I chose a Druid, although I usually go with a Mage. I upped the character XP and gold by repeatedly exploiting dungeon rooms with a good monster/treasure ratio. The first room in Dungeon Despise is a pretty good starting point for this, it's near the British Castle and you don't need to waste torches to find the room.


When I had the ship and enough gold I went to Buccaneer's Den and bought that Magic Wand plus some of those oh-so-necessary lock picks.

After I had the 8th level character with 800 HP, I simply collected all the runes, all the stones, all keys, all special items (book, bell, candle), all "bonus" items (horn, wheel, skull) and gained Avatarhood in all virtues.

Rune locations I could still remember, but I had forgotten some of the mantras, this wasn't the case last time...

For the end, the party has to have eight characters, and levelling them up is a very tedious business. The game doesn't care if the characters are dead, though. So maybe having three persons with good-ish stats would be enough for the Abyss?

When my party had two members, I tried to educate Iolo by having my main player killed, so as to have an one-member party again. However with my (dead) character at 8th level I could easily encounter Balrons and stuff so it was a bit dangerous. I was happy with Iolo at 6th level and Mariah at 5th level. Then I recruited everyone else, mixed a huge amount of useful spells and headed to the Abyss.


When approaching the Abyss Isle pirate hideout, I came across a bug, shown above. I'm not sure if it is in the original code or a DOSBox (speed) artefact, but amidst proper ship-to-ship buccaneer battles I had to fight a bunch of miniature ships! The game resolved them as "phantoms", which is OK, but they could not move in the water. This means the south-easternmost ship could not be killed as it is out of reach for the players. However, I was lucky to have a couple of Tremor spells which destroyed the phantom shiplet.


And yes, the party was good enough to get through the eight levels of the Stygian Abyss, with on-line help to guide me of course. A couple of dudes died, but this only served to make the combat a bit more bearable. My party began starving inside the dungeon, but that's not a big deal as it's mostly combat rooms anyway.

Then I thought, perhaps the Abyss could be beat with just one character. I reloaded the game, killed off the party except my main character and entered the volcano of Abyss again. I kept my guy in good health and M.P, and also poisoned to avoid the enemy sleep spells. Quickness spells come in handy in certain situations, as having a bunch of Daemons around you can still be dangerous. But yes, it's silly how much easier and faster it is with just one character!

The Poison status preventing Sleep status can be considered a bit of a bug, but it's also a clever counter-move in an otherwise simplistic game engine. Why would there be the poison fountains in the Abyss, if not to offer a chance to gain immunity to the Sleep spells? There are versions that "fix" this feature, and I can understand the Abyss becomes reasonably difficult if this feature is removed, and the one-character approach probably would no longer work.