Attack of the PETSCII Robots SDL
================================
Ported by Vesa Halttunen <vesurijormas.com>
This work is licensed under the Creative Commons Attribution 4.0
International License. To view a copy of this license, visit
http://creativecommons.org/licenses/by/4.0/ or send a letter to Creative
Commons, PO Box 1866, Mountain View, CA 94042, USA.
About
-----
- petrobots.cpp is the main game logic ported line by line from the 6502
files PETROBOTS.ASM and BACKGROUND_TASKS.ASM
- Platform.h is essentially an interface with platform specific
implementation classes
- Various #defines starting with PLATFORM_ can be used to build a variant
with different features using the same platform implementation
- To port to a new platform, create a new PlatformXYZ.cpp/h implementation
based on the existing ones and instantiate it in main() (petrobots.cpp)
- If the target platform version will only support a certain feature set,
feel free to get rid of the unnecessary PLATFORM_ #ifdefs manually or using
a preprocessor
- The SDL version is a generic baseline implementation that should be
customized for each actual target platform
Building
--------
brew install sdl2 sdl2_image
make setup
make
cd SDL
../petrobots
Making of
---------
The first task was to convert the 6502 assembler PET source code to C++
line by line. Even though this won't produce the most elegant high level
language code it ensures that the code behaves exactly like the original
does. PET KERNAL calls and memory accesses were abstracted to an interface
which can be implemented for different platforms.
To make initial testing and verification of the ported code easier an SDL
platform implementation was written first. This allowed the game logic to
be tested on a modern operating system. When eveything seemed to work it
was time to write an implementation of the platform interface for the
Amiga. Since the game shouldn't necessarily require every last drop of the
Amiga's resources to run, the interface was implemented in an AmigaOS
friendly way, multitasking in it's own AmigaOS screen.
The first implementation was very naive: each write to the PET screen
memory would result in copying the respective bytes from the font to the
Amiga bitmap memory using the CPU. This would be highly inefficient on the
Amiga, so the next step was to implement tile based rendering. Tiles would
be copied in 24x24 pixel blocks using the Amiga's blitter, which on 68000
systems is much faster. Initially the tile bitmaps were generated during
startup using the font and the tile data but pre-drawn bitmaps could be
used just as well. After adding support for four bitplanes to get 16 colors
it was already possible to switch to the tiles provided by the graphics
artist.
Double buffering was implemented to hide artefacts caused by modifying the
screen while it's being drawn on display. However, the increase in memory
consumption combined with the highly inefficient manner of switching
between the buffers in an AmigaOS friendly manner called for a better
approach. The bitmaps were made interleaved so that instead of the four
bitplanes following each other in memory, the data for each bitplane row
follows each other in memory. This way any changes to the screen memory
while it's being drawn on display are limited to a small area. Each tile
could be copied with a single blit, improving performance. To reduce the
amount of memory required, a transparency mask was only generated for tiles
requiring one.
The one channel PET-like sound was replaced with a ProTracker module based
sound implementation. Sound effect samples were injected programmatically
to each module upon loading. The module player was modified to allow the
sound effects to be triggered by the game as if they were notes in the song
data. This way there was no need for a separate sound effect player or a
need to make the music and sound effect playback routines aware of each
other. The songs were modified to leave the fourth channel free for sound
effects as often as possible. A separate "no music" module was then added
which has no notes at all and allows sound effects to be played on all four
channels. This completely transformed the game's audio.
Support for pre-drawn graphics for the intro screen, game screen and game
over screen was added. Then it was time to render the current weapon, item,
keys and health using bitmap graphics. Animated player and robot sprites
were implemented. Hardware based screen shaking was implemented and
hardware sprites were used for the cursor. Palette fading made transitions
between different screens a lot smoother. It also allowed the screen to
smoothly flash when taking damage or using the EMP. Suddenly the game
started to look like an Amiga game!
In order to fit the game on one disk, assets had to be compressed. A hand
written 68000 assembler implementation of deflate was used to decompress
gzipped assets. In order to fit the game in memory, these assets had to be
loaded on demand. On Amigas with only 512 kilobytes of chip memory there
was no way to fit both music and sound effects into memory, so a decision
was made to only support sound effects on such systems. On Amigas with more
memory in-game music is loaded from disk on demand. Other assets, like the
intro screen, intro music, game over screen and game over screen are loaded
at startup and kept in memory to make the game over experience more
pleasant.
When the game was mostly complete otherwise, it was time to implement the
live map. While simple in principle, Amiga does not make the implementation
trivial due to its planar graphics. Instead of modifying a single byte to
modify the color of a single pixel, one bit in four different bytes needs
to be modified, making single pixel modifications very slow. Various chunky
to planar implementations were looked into, but ultimately a custom 68000
assembler chunky to planar routine was written for drawing the entire map
and an another custom implementation for manipulating the pixels relevant
for each unit. The end result was a map performant enough to be usable also
on 68000 systems.
Last but not least joystick support was implemented. Most Amiga games only
support a single button Atari style joystick. However, there are Amiga
games that do support the 7 button Amiga CD32 gamepad either natively or
via patches, so it made sense to add support for such a pad as well. In
this mode all the game commands are available through the gamepad. Getting
this code right required digging up some obscure information from forum
discussions and such. Atari style joysticks can also have two buttons, so
support for the second button was added to the joystick mode. This way most
of the game commands are available using such a joystick as well. While not
optimal, at least the game is now fully playable using a traditional
joystick.
TODO
----
- Add screen and map size getters in Program to allow actual screen size to
be determined on startup
|