Tech breakdown for Wolf64
I've written several FPS for Commodore machines, starting with The Keep (VIC-20, 3.5k, raycaster) in 2011, followed by a sector-based Doom port for the VIC-20 in 2013, and a 64k height-enhanced raycaster version of Doom for the C64 in 2026. I was able to pull a significant amount of code from these projects to establish the enemy AI, player movement and collision, and weapon state machines.
Here, the screen mode is multicolour extended so we can get two unique colours per tile. That allows for a 38x40 viewport. The bitmap is set up with one colour index for the top half of each tile and the other unique colour on the bottom half. Then we just paint the colours (double buffered). This is how the old game MOOD works.
The map is stored as a straight 64x64 array, converted directly from the original game maps. Each byte is either a solid block, a door, an enemy, a patrol point, an item, etc. To save some memory I remap some of the wall tiles to others. For example, each wall material (stone, brick, wood, purple?) only has one decoration option, and remap some items to others (for example, there isn't a hanging cage without a skeleton).
I dropped scoring and treasure from the game, but I think that was a bad decision, and am working on a patch to add this back. When I started I thought having all those items lying around was going to kill performance, but it's not awful, and it's generally in a treasure room where the framerate isn't crucial (no combat).
The raycaster does as little work as possible. The only multiplies (using quarter square lookup tables) are to set up each column. Then we use a lookup table to convert the distance travelled into a wall height, which is clamped to 75. If the height is less than 50 it uses a unique unrolled column painter to draw the column, floor and ceiling. To save some memory the close tall walls are drawn with a loop. The rationale is that the raycast was short and cheap so we have more time to draw the column. We store the distance per column for later.
Items and enemies are then gathered, sorted, and drawn on top, using the distance per column as a cull. Items are gathered from the map by making a bounding box around the view frustum then gathering the items in the box. Enemies are kept in a separate array, max 64 per level. (Episode 1 has a max of 73 enemies, so I just randomly drop 9 from I think E1M5 when baking the map.) These are only updated and drawn if close to the player. Items and enemies also use baked column renderers. They are clamped to a maximum size.
Audio data is the pc speaker sounds from the original, cut down to one third the number of samples (so they can be played using the 50Hz timer interrupt), preserving the rising and falling edges.
Textures are taken from the original game, cut down to a quarter or a third the size, mapped to the C64 palette, and hand pixelled. I took some care to ensure that for items on the floor, or the bottom of wall textures, I didn't use the same colour as the floor, so things don't blend together.
Weapons are double size high resolution sprites. I generally use all eight sprites for these. Animation is just shifting the sprites around and turning on and off the muzzle flash. Again, I avoided the floor grey colour. In this case I opted for purple to replace it, which gives the weapons a nice C64 feel.
I used AI extensively, for tooling, conversion, and blocking out code. I just acted as a technical director, telling the AI what algorithms to use, where to put data, what data to use and what to data to drop. This game took less than a week start to finish. That's just more fun than dealing with every last BCS for months on end.
Files
Get Wolf64
Wolf64
A port of the classic FPS to the C64
More posts
- Open source benefits - game is noticeably faster!20 days ago
- Added treasure!27 days ago
- Mouse support; brighter sounds; menu polish29 days ago
Comments
Log in with itch.io to leave a comment.
Hey, I've made an EasyFlash CRT image using Milasoft's Disk2EasyFlash, seems to work without issue. If you tell me where to send it, I'm happy to email it to you. Or you can email me. It's just my username at gmail.