The game exists in three versions, each based on a completely different technological basis. These three versions are:
In addition there are variations of the C64 version with minor differences:
Devolution is the working title (first called Wastlands) of a series of unfinished PC game projects we started from the mid-90s until around 2007. What they all had in common was that they were supposed to be a classic real-time strategy game (RTS) in a post-apocalyptic scenario. We started with a 2D engine under MS-DOS and later moved to MS-Windows and 3D. For more information about these projects, see the Windigo-Design section.
Although you can find devolution in an English dictionary, it was intended as a made-up word with a different meaning. Either as a combination of the words 'dying' and 'evolution' or as the addition of a de- to evolution to give it a reversed meaning. Honestly, I have no idea whether either of these two derivations make sense to a native English speaker. But since the project title had been around for so long, I decided to stick with it.
All versions of Devolution are inspired by Max Design's 1993 PC game Burntime. Burntime is a unique mix of survival, crafting, strategy and action game. It's a great game and one of my favorite games of all time, but in my opinion the game becomes a bit monotonous once you manage to establish a stable economic foundation and the focus shifts to the strategy part.
So the initial idea for Devolution on PC was to combine the atmospheric setting, the survival and item crafting ideas of Burntime with the mechanics of a classic RTS like Warcraft to create a game that has the key features of Burntime, but is more challenging and more motivating in the late game.
Unfortunately, none of these projects was ever finished. In September 2022 i decided to finally complete this unfinished projects with a C64 version. For the C64 relaunch of the concept, the idea was still to have some sort of real-time action, but of course with reduced complexity due to the C64's limited capabilities. The main focus in this game is now on rather simple tactical decisions, but with a very high frequency. The crafting aspect boils down to collecting items that give you different bonuses, and the idea behind the survival element is to have a mechanic for eliminating enemies and add a few additional tactical options.
Ultimately, Devolution became more of a combination of Burntime and the board game Risk than the originally planned RTS.
I was asked if the game runs on real hardware at all. Honestly I couldn't tell as I've never tried it before. I was planning to try the game on a real C64 during testing, but was afraid of the hassle it would take to set it up and that my old hardware would either not boot or even start burning. So I skipped that part of the test.
But when the question came up, I decided to at least try. Luckily I have a bunch of C64 and all sorts of accessories like hard drives, cables, power supplies and joysticks stowed everywhere. And very important: even a tube television. I'm a retro victim and have bought all sorts of modern extensions over the years without ever trying most of them. So, in addition to the original hardware, I own an Sd2iec and an XU1541.
I have several good looking C64s in a display case but tried a very faded one I found in my attic - still afraid it would fizzle out and burn. To my surprise, once I connected the cables and turned on the device, everything worked fine. So I tried two different ways to get the game to work on the real hardware.
Disk driveI randomly grabbed a floppy disk from a box full of 5.25" floppy disks and connected a disk drive (1541-II) to a laptop via XU1541/USB. After installing the software, you can easily copy the disk image that we have released as download to a floppy disk. Then I connected the disk drive to the C64 and loaded the first file. The loader appeared and after the intro the game loads and starts. I completed the first level with no problems.
Sd2iecAs expected, I didn't get the disk version to work via Sd2iec. The disk version has a dedicated loader, that only works with real disk drives. So I tried loading the game file directly and launching it via the SYS command. However, I have no way to type anything after loading. I think I did something wrong, but didn't have the patience to read all the documentation. Instead I just downloaded the cracked version of Laxity from csdb.dk and copied it to the SD card. It loads and works fine.
Conclusion: The game works fine on real hardware. But I am completely surprised that after so many years the hardware is still working properly and that randomly selecting a floppy disk that is over 30 years old, copying files onto it and running it was no problem.
A very complex issue was the development and balancing of the jumping mechanic. There are basically four different types of jumps in this game:
To visualize jumps in a nice curving motion, I divided a jump into three phases: ascending, gliding, and descending. The moving (running) speed when the jump is started determines the ascending phase duration and the longer (higher) you ascend, the longer you glide and the more distance you can overcome.
This would result in a formula like: jumping distance = hero speed * constant
That sounds pretty simple... in theory. Unfortunately, there are two opposing factors that caused me some headaches in the process of implementing jumping.
[1] Different player characters
You can choose between two playable heroes and one distinguishing feature is the jumping range. At first I was very confident that I could solve this problem by slightly modifying my formula to:
jumping distance = hero speed * constant + hero jumping bonus
The individual hero bonus can also be added to the constant which multiplies with speed, but none of these approaches solved the following resulting problem: Depending on the balancing, one of the characters could no longer successfully pass the single-block obstacles. Either the barbarian fell into every single-block-abyss or the Amazon jumped over every single-block stepping stone. So I ended up adding additional code to adjust the formula for this special case, to make sure that each hero can perform a single-block jump when standing.
[2] High Jump vs Distance Jump
There are obstacles where with a jump not a distance, but a difference in height that has to be overcome. To solve this I changed the ascending angle and duration.
This resulted in two problems:
The higher you jump, the greater the distance you jump. So I had to make sure, that a player could jump onto a single block obstacle without affecting the jump distance balance for short and long jumps.
The other problem was that either the barbarian couldn't jump onto single block obstacles or the Amazon could jump onto two block obstacles.
Again, I've finally added some additional code to improve the behavior in this case.
The bottom line is that everything would have been a lot easier with just one playable character, but I think the ability to play the game a variety of ways was worth the effort.
To visualize the project development, I collected a few screenshots from different versions. With the exception of the last screenshot, all images show a fairly early state of the game, but at this stage of the project, the changes between versions are more visible. In later project stages there where more gameplay changes, that are harder to visualize on screenshots.
In the beginning the graphics were very simple as the focus was on implementing the core game mechanics. After some time more details were added, like the status bar. The changes to sprites and level graphics are also clearly visible. The letters on the left side in some screenshots are an orientation when working on scrolling.
A few impressions how the first level and also the interface and the sprites changed the development process.
Other parts of the game have also been improved over multiple iterations. In the intro, for example, the texts and the background graphics were changed several times and a frame for the graphics was added at the end.
The first menu consisted only of the water effect. Later I added the graphics and a menu. The menu itself changed several times.
To create larger graphics, I sometimes work with the following approach: First I draw a very rough sketch and then gradually work out the details. Sometimes I use graphics I found through web research as model graphics. For example, the windigo in the loading screen is a collage of a picture of a random sitting person plus a mix of a windigo artwork and photo of a deer skull. I don't copy these images, I use them for inspiration and to get a rough idea for poses and details.
For the game's menu, I wanted to depict the hands of a drowning man sticking out of the water, surprised by the deadly tide while looting the dungeon.
Hands are quite difficult to draw and of course I didn't find exactly the pose I had in mind. So I just photographed my own hands and used them as a template. Since the space between the two hands was very small, the menu later replaced the second hand.
The images above show the development process of the menu. During this process, the name of the game also changed several times, but that's another story.
Another approach is drawing a rough sketch with the mouse and then work out the details pixel by pixel, like i did for the title screen.
The levels in Deathflood are created using a custom multicolor charset and are 40 columns wide and up to 75 rows high. There are 19 levels and the avarage height is about 41 rows, so the total level data consists of about 31000 cells. In addition to the background color and two common colors, each character in the multicolor charset can have one individual color. So, to store a single cell uncompressed, two bytes are needed: one byte for the character used and one byte for the individual color, which results in a total of about 62000 bytes. Since there are only 16 differen colors you can halve the color data with litte effort.
My first, and a very well-known, approach to compress level data was using tiles. A tile is composed of several characters and instead of storing individual cell data, only the index of the tile is needed to encode the cells and colors used by that tile. In addition, the description of each tile has to be stored. The smaller the tiles, the smaller the compression potential of each tile. The larger the tiles are, the more tile description overhead needs to be stored. A good compromise is a very common tile size of 2x2 characters. Up to 256 different tiles can be used by storing the indexes in one byte. This would reduces the level data to 1/4, but adds 8 byte per tile for character and color description. Since Deathflood consists of three different worlds which use the same character set but with different color sets, I would need one tile definition for each world. Still this would be a very effective way to reduce the size of the level data. The big disadvantage of this approach is there is a lot of repetition and a reduction of unique details. Because of these limitations, I discarded this approach very soon.
Instead I decided to use various compression techniques to reduce the level data size. The individual character color information can be reduced in a very simple way by using a color-looup-table. This means that each character is bound to a certain color. But that limitation is no big drawback. Since the game has three worlds with different color schemes, I need one lookup-table for each world. So three lookup tables with 256 byte each sum up to 768 byte instead of 31000 bytes of color information. That's quite an effective reduction. I improved the ratio a little bit more by compressing the color tables with RLE. By using an automatic optimization that reorders the carset and overwrites colors for unused chars, to create large sequences of idential colors, the RLE performance improved even more. In the end I need less than 214 bytes for the color information of all three worlds.
The character data of most levels is compressed with LZ77 and for three very small levels I used RLE because LZ77 becomes the more effective the larger the input is. For small inputs, LZ77 doesn't perform that good.
I've excluded the metadata (like enemies and level logic) in this comparison, because the metadata are the same for each approach.
Approach | Screen Data | Color Data / Tile Data | Total |
---|---|---|---|
Uncompressed | ~ 31000 bytes | ~ 15500 bytes | ~ 46500 bytes |
2x2 Tiles | 7750 bytes | 6144 bytes (256 tiles * 8 byte each * 3 worlds) | 13894 bytes |
Compressed | 214 bytes | 13239 bytes | 13453 bytes |
To summarize the result, the 'tile'-approach and the 'compression'-approach give quite similiar results. I'm pretty sure that with the 'tile'-approach the data could have been squeezed even more by reducing the number of tiles per world. But the loss of detail and individuality is a serious design limitation. Additionally, the 'tile'-approach requires a lot of manual work to optimize the tilesets across all levels of a world.
At the beginning of this project I had a very vague idea of how much content would fit into the memory of a C64. At that point, most of my C64 coding experience was about 30 years old and mostly with C64-Basic. I could estimate the approximate size of some rather static resources like charset and sprites, but I had absolutely no idea how much level data and code would fit into the available memory.
So I approached the problem in a way that's called agile these days, starting with implementing the core mechanics and then improving and expanding everything over many iterations. Actually, my process of adding new features or content was to find a few empty bytes in memory or to identify some code or data I could squeeze out some bytes by optimization and then add the new stuff. That way in the end up nearly using all of the available memory.
But this iterative process was also quite painful as I had to the re-manage my memory organization multiple times, especially when I learned that some memory locations have limitation on what kind of data you can be store there. On the other hand when I realized the benefits of the Zeropage, I moved nearly all my variables there and freed unbelievable amounts of memory. The Zeropage contains almost 256 memory locations that can be used with special assembler instructions that are cheaper in terms of memory consumption and processor cycles.
The C64 has a total of 64 kilobyte RAM, but there are a lot of limitations. Firstly it's a shared memory. So among other things it's used for:
Then there are two large blocks of 8 kilobytes each, where ROM data is mapped to RAM. One block contains the Basic interpreter and the other contains the kernal code. Both mappings can be disabled, making those memory regions usable. Using the Basic interpreter memory is easy, but using the kernal memory is a bit tricky.
Depending on which kernal functions you want to keep using (e.g. key input processing), you need to preserve some parts of it. The solution is to disable the ROM mapping and copy the kernal code into the same RAM location. Then the kernal code ist still available when the ROM mapping is disabled and you can overwrite unnecessary parts. I spend some time reading documentation to figure out which parts of the kernal region aren't necessary to run my game. That gave me a rough idea about the contents, but I ended up just wiping smaller chunks of kernel memory and then checking to see if my game was still running. This way, I've made most of that memory usable for my game in a fairly short amount of time.
Simplified C64 Memory Map
(green = usable, orange = limited use / restricted usability, red = not usable for other purpose)
Size | Content |
---|---|
8 kb | Kernal ROM |
4 kb | IO (Color RAM + memory used by Audio / Graphics and Input Chips) |
4 kb | Free |
8 kb | Basic ROM |
39 kb | Free |
~1 kb | Zeropage/Stack etc |
When I started this project, I decided as my personal challenge that the whole game should fit into the memory of the C64. At this point, I only had the basic game concept and a very rough idea of the size of the game resources like graphics, sound, sprites and levels.
After spending some thoughts on the memory map layout, I knew that I'd need some creative memory management and of course some compression algorithms. During the project, I did some experiments with different compression algorithms and in the final game, I'm using four techniques for different kinds of data:
RLE (Run Lenght Enconding)
RLE is not very complex but effective when you have a lot of simple character repetitions. In the most simple version, you use one byte for the current symbol and one byte for the number of consecutive occurrences of the symbol. This works well for sequences of more than two identical symbols, but if the input has a lot of variety, the output becomes even larger.
I wrote an advanced variation, where I use the MSB (most significant bit) as a flag to determine, if the current symbol is compressed or not. The drawbacks are that I can only process inputs with no more than 128 different symbols and that I have to maintain and store a mapping of the limited symbol set to the actual data. Another improvement I added is that for inputs that only use the first 128 symbols I can set the mapping table size to 0.
LZ77 (Lempel-Ziv 77)
LZ77 uses dictionaries created during compression or decompression and so its strength is to compress repeating sequences. This works very well on input data with complex redundancies, but the drawback is that my decompression implementation needs 2 kb buffer for the dictionary. I used a sample implementation to adapt the compression for C# and the decompression for assembler and added only minor optimizations. After some evaluation, I decided that for my purpose a buffer size of 2 kb is the best trade-off between space consumption and compression.
Text
For text, I use four bits (nibble) to encode the following 16 symbols:
If the compressed symbol hasn't been found, there is another nibble mapped to another 12 characters, some special characters and null termination. If a character is not part of these two sets, another byte is used to store it uncompressed. So in worst case you have to pay two bytes to encode one, but in average case, texts can be compressed down to 55-60%. A big plus is that repeating spaces are very cheap in my implementation so that I can use spaces for positioning text and text fragments on the screen.
Custom
In addition I have some situational compression, for example in the level metadata I store x-coordinates. Since all levels in this game are all exactly 40 bytes in witdh, I use the upper two bits for other metadata. Another example is an approach to save some sprite data memory by mirroring the hero sprites after selection. That way I can save 1/4 of hero sprite data. I considered doing this for all sprites, but that would have reduced the number of different foes per level since this only works if I use shared sprite data to create the mirrored data. I decided that this would be too much headache for too little gain.
I also did a conversion for LZW but with my data it always performed slightly weaker than my LZ77 implementation. I also worked on a more sophisticated version of my RLE variation by adding a treatment of redundant sequences. The results are better than those of my current RLE implementation, but it's not worth the additional code overhead.
The game's setting has a quite classic fantasy background. It's not very unique, but rather a mash-up of influences I grew up with. Here, I want to honor the most important inspirations.
A very common fantasy setting in games, books and movies is a hero on his quest for gathering fame and riches, encountering and fighting an evil antagonist. Classic characters in such a settings are the heroic warrior, the evil wizard and the beautiful damsel/princess. The Conan movies with Arnold Schwarzenegger are very prototypical for this setup. Some may consider them as trash, but in my opinion, they are great fantasy movies with very dark and impressive visuals. I think, these movies had a strong impact on the visualization of the general archetype of hero figures in a lot of fantasy media productions. And they are also one source inspiration for the characters and the background of this game.
The basic setting and the visuals are also inspired by the C64 Barbarian games from Palace Software. For those of you who really missed it: The first one is a duel game where you play a sword wielding barbarian fighting other barbarians in an evil wizard's arena.
At the end of the game, you also have to beat the wizard to rescue the princess. Beside the great visuals, the game is memorable for the head chopping and a little green leprechaun, dragging corpses out of the arena. I played that game a lot, even though I wasn't that good at it. But I loved the stunning visual and acoustical presentation.
The second game is a multi-screen action adventure. In this part of the series, you can choose to play either as the barbarian or the princess. I played this one not so much, although it has very impressive visuals too. The subtitle of the second game is 'Dungeon of Drax', where Drax is the evil wizard. One level in Deathflood is named 'Arena of Drax' as a tribute to this series.
Playing the board game HeroQuest enthusiastically for some years was certainly a useful experience for designing the levels and the level visuals for Deathflood. And although I didn't do it on purpose, at least the mummies are probably inspired by HeroQuest. If you don't know it yet: HeroQuest is a board game where you have to explore a dungeon that consists of a lot of props that are rearranged for each game, based on a mission book. You start your quest with up to four cliched fantasy heroes (of course there is a barbarian) fighting against hordes of monsters controlled by the game master. The game came with great miniatures made by Games Workshop.
A lesser-known inspiration for this game is another C64 game called 'Tower of Terror'. The game was published by 'CP Verlag' on a disk-mag called 'Golden Disk'. I played it for many hours and enjoyed it very much. Again, it's a game about a sword wielding barbarian, a princess and an evil wizard. But this time, it's a jump and run where you can collect different weapons and have to beat a wide range of foes. I'm not very skilled in playing jump and runs and prefer slow strategy games. But since I enjoyed this one very much, it was basically the motivation to start an action game on my own. The Deathflood subtitle 'Dungeon of Doom' is a reference to this game.
I grew up in the eighties and once I got in touch with a C64 I desperately wanted one of these fascinating machines. When I finally got one I initially spend a lot of time playing games, but very soon I wondered how the magic behind these games works and wanted to create my own ones. So I taught myself coding with the help of the manual that came with the C64 and started my first projects, but never really made it beyond Basic. After the C64 was replaced by an Intel 486 and I started to work with C and 16bit Assembler. I occasionally tried to code a few lines of assembler on the commodore, but nothing more than little graphical effects.
Around 2010 I had to travel a lot and one day I picked a C64-assembler book from my shelf for reading during one of those trips. That book caught my interest in 8bit assembler again and I drafted a few game concepts. One concept developed around a simple idea for an interrupt effect to visualize rising water. I drew a concept art for the title, a few sprites and backgrounds. In 2020 I stumbled over that concept art again and spontaneously set up a cross-compiler setting and started to finally implement a tech-demo of the core effect. After that, I decided to spend a few months with that project.