Anyway I wanted to try implementing such game so I started working on it 4 weeks before the Abbuc 2023 deadline.
What is so special about this game is that there are multiple software sprites (hi-res) that are supposed to point on the invisible target. After some tests I decided to go for 16×16 pixel sprites… however it requires some math to calculate the angle of each arrow towards the goal.
I was able to find nice solution on codebase64 site containing c64 resources called atan2 , really lovely stuff. This solution could do the trick, quickly calculates the angle between source and target point… utilizing nice table optimization depending on the angle octant.
Other problem to solve was the sprite management. As there is no software that could prepare the data for me, I had to write it on my own. I called it Arrower. This piece of software does exactly what I needed:
- open an image of an arrow
- generate 128 faces of the rotated arrow – each octant (45 degrees) is represented with 16 faces
- remap the sprite data so each arrow data is stored in on the 32byte row (2bytes*16 lines)
I was thinking about some optimization of the memory footprint by introducing sprite mirroring, however it was not that needed, since the whole data for 1 arrow type took just 4kB. There are 5 types of arrows in the game, they can be used freely (the data are present at all time, no swapping).
I decided to go with the bitmap mode, so the video ram is sequential line after line. Displaying a soft sprite in this mode it pretty straightforward stuff, however what about optimizations?
Usually in games with soft sprites it makes sense to consider preshifting – in case of hi-res that means 7 additional faces (bit-shift values) for each sprite. That is impossible in this case where we have 128 faces * 5 arrows * 32 bytes * 8 shifts (including the original one) = 160kB of data. We could require some extmem (oh no!) or reduce the on-screen arrow types or introduce vertical/horizontal mirroring to save some space…. but what about speed?
The drawing routine would need to be more complex, again introducing some forks that would take care about different octants, so some angles would go with the simple routine and others with the more complex one… so there could be variant speeds.
After considering multiple options I picked this one consisting of 2 points:
- no mirroring horizontal/vertical
- no preshifting (each sprite will be shifted at the time of drawing)
Important is to know that most of the arrows will not move, just rotate… so there is no need to shift them, they will be simply read from the arrow array and written directly to videoram.
Moving arrows will be more-less exceptional, so only small number of arrows will actually move within a single level, however it has to be as fast as possible anyway… since there is lot of drawing every frame.
Let’s think about the fine scrolling of a software sprite in hi-res. It is calculation intensive and slow if you are not using preshifting. The best shortcut I could come with was to do the shifting in zero page… to save some CPU cycles together with the sprite remapping to the 32byte row (output of Arrower).
The routine copies 32bytes of sprite data to be shifted into zero page. Performs rotation on the whole 32bytes in a row maximum 4 times to the right or maximum 3 times to the left… depending on the required bit-shift value. What is nice about this approach is that you just need to reserve 1 additional byte for the shift remainder at the beginning of the zero-page buffer or and the end of it (depending on the shift direction).
The drawing is then pretty simple, you draw 3 consequent bytes of a sprite data and increase index by 2 bytes, however you also have to mask left and right byte (of those 3) with an AND mask depending on the bit-shift value.
I did not do any sprite deletion/cleanup so it is visible in some levels that some sprites are leaving pixel traces as they move and rotate, but the overall game speed and fluency was more important.
Ok, so softsprites are now handled, what about the levels?
Well, it is pretty normal to expect that I will use some of my tools that I’m used to work with… and in case of DotA there is no exception… MapMaker:
I just created some font in my FontMaker that helped me to represent the level items, for instance:
- green – arrow type
- blue – showing the item size (2×2 chars) to avoid unwanted overlapping
- red dot – initial position of a target dot, followed by its index
- violet – like blue, but defining that the item (arrow or dot) is dynamic (moving), it is always followed by object index and also there is a char adjacent to the violet area describing the nature of movement (in this case O = moving in circles)
Once the level is designed, it is required to take care of the dynamic part … that requires an engine.
Yes, “engine”, what a lovely thing to say. What a hard thing to code.
I had no time for that “sh!t”. Every new level would need new rules to be implemented there, no, no… let’s hack it!
Yes! Every level has it’s own code taking care of level dynamics. Xo xo, loving it! So I created table of jumps into environment subroutines based on the currently selected level, updating position of the dynamic (indexed) elements. For the level above it looks like this:
When level starts, it is initialized by jumping to l22-3, then every frame it jumps to l22..and that’s it!
I know it may be a bit wonky, but it was much easier to take the routine from previous level and extending it with some different dynamics compared to code some freakin’ engine that would control it parametrically.
I spent some time with the game title. It is slightly misleading, but I made sure that it makes sense it is called the way it is called. DotA (Defense of the Ancients) is a multiplayer community mod for W3:TFT, that was later bought by some AAA game company and released commercially with a slightly different letter casing. Here in my DotA, your mission is to find invisible dot… Each dot has its own letter like dotC dotB … but once you find dotA you proceed to the next level.
The other fact that is supporting the title is the soundtrack, this video helped me a lot: https://www.youtube.com/watch?v=JxE4gNBxCHU
As usual in my recent projects the game supports stereo and if detected the music is played in pseudo stereo mode.
Starting with the Binary Parasite I’m implementing NTSC color compatibility into my projects, so not only speed but also the colors are matched (as much as possible) to PAL when playing on NTSC system.
I hope you enjoyed the game even though it is an one timer. It was quite fun to code it and many lessons were learned during the 4-weeks long process of development.