The principal of this game is that all the components are implemented from scratch with a minimal of OS specific code and all code are public domain (c++). As few external dependencies as possible. STL for lists and so on, OpenGL for graphics, fmod for sound (previously DirectSound, but to be able to port to Linux and MacOS this library was chosen), for keyboard, mouse and gamepad input DirectInput is used in Windows, pure X11 in Linux and Native on MacOS.
Own elements that are implemented include image loading classes (targa, jpeg), a pixel text class (to have text show the same on any OS), a GUI drawn from simple images, and also a network socket class for UDP and TCP.
It has up until now been done by one person, first version around year 2001 and put on sourceforge 2006.
The initial coding, graphics and sounds are made by me, the mp3 music by Omar Soriano, and a new AI engine is being developed with support of the SourceForge community.
It's a pretty simple approach at the moment - a player (client) sends a message whenever the input changes (a key is pressed/released), or a message is sent after a max time of 0.5 sec if there is no change to input. Also a message is sent when a player is killed and for other events. The master client controls the computer enemies, and a message is sent for every computer enemy whenever there is a change in its state (bullet fired, killed, movement change). A message consists of current position, velocity, direction and input/movement, and other events such as firing and score... Each client is then playing up the other players movements every frame with the last known info from a message. When a new message arrives the players current state is corrected, and this info will be used until the next message.
The networking was first implemented as multicast, then as UDP from one client to all others + special server messages. Then to avoid all the problems with firewalls and routers it was switched to TCP to server which then sends it to all clients, so that the only connection is that to the server. The cost of going from UDP to TCP via server is that the time to get a message doubles (among other things), but it was hard to play multiplayer before since all clients will have to reach all others, and there is always someone that can't get it to work.
Still there is no compensation technique used to compensate for messages taking different time to reach different players, so the game is slightly out of sync for all players. However it should be a lot easier to implement that now when the server is the hub for communication and the game runs with a fixed timestep length for physics calculation (4ms).
The game level network code is in need of a major overhaul, to implement techniques from Networking for Game Programmers for example, and maybe going back to UDP in case it can be done without firewall trouble for the players (help needed).
The replay was quite easy to implement since the messages sent/received over the network could just be saved in a big list together with the timestep when they happened. It is actually done by tapping in to the network message class. Saving all important messages that is going out and coming in when the replay is created, and overriding network receive with messages from the replay at the correct timestep when showing a replay.
The current AI began as an experiment, and it still is because its not working very well and does not shoot at opponents.
First the A* shortest path algorithm is used to generate a path from the current ship position to the goal. All black tiles have the cost of 1 (diagonal 1.42), solid tiles cost is infinite and border tiles cost 10 to make the algorithm select tiles away from the walls where possible. This generates a path with turns that are in steps of 45deg from the center of one tile to one of its neighbors (one line segment for every tile). To smooth out the path and reduce the number of line segments the tile map is traversed with two lines offset from the path to check for a wall, if no wall is found the segments between are removed.
Then we have the steering AI that will try having the ship follow the calculated path as best it can. It works in an unusual and perhaps not so efficient way. It considers all combinations of moves it can make in timesteps (100ms) forward in time and gives a score to each move and builds a score/move sequence recursively. The moves for the best scored sequence will give the next move - much like a chess AI would do.
The ship can make one of six moves at any one time - nothing/thrust/right/left/right+thrust/left+thrust. This makes the number of move combinations very large very quickly (it runs 5 levels deep currently). The score for a move has three components/strategies, 1) How close to the path the move will put the ship 2) How much closer to the goal the move will put the ship 3) How the ship is pointing compared to direction toward the goal. The scoring is just tested until it somewhat works...
There are several flaws in this approach. First the calculation load is very heavy, the shortest path works on the tile grid which is to imprecise, and also it produces unreliable results because the scoring may sometimes make it worth more to stay close to the calcluated path instead of going towards the goal (To defeat this problem the score was capped at a certain distance from the calculated path but it did not work well). It may be possible to play around with the scores or invent new strategies but it's probably the wrong approach to begin with.
The blue lines are the tile grid, the free red line is the velocity (ship will end up at the end of the line in one sec if no change is done), the red line connected to the white lines is the path to follow (red is current segment). The AI path calculation and steering is done in a thread asynchronous to the game (it cannot be done in sync of the game because of the CPU load), so therefore it's only updated perhaps twice a second. The first text is the ship pointing in degrees, the 2nd is the difference between the ship pointing angle and the angle of the line between the ship position and the first goal (end of red line).
Explanation will come soon.
To be able to create YouTube videos with sound a moviemaker class was implemented. When enabled it redirects the fmod audio output to a .wav file, then reduces the frame rate (to 30) and saves each rendered OpenGL frame as a compressed targa. It may require a pretty fast computer because of the large data size (~20 MB/s at 800x600). At the end of a capture session a mencoder command file is written to disc to be able to easily generate a movie from the saved frames and audio. To get perfect A/V sync the frame rate is adjusted to "number of frames"/"length of audio".
It would have been nice to encode the video on the fly but to keep it simple and cross platform this approach was kept.
OpenGL without glut on Windows, Linux and Mac
The OpenGL class was first implemented for Windows only using glw methods, then it was straight forward to implement it in Linux with glx methods. Except that a bitmap font class had to be implemented in place of the previous ttf font class.
The Mac version was not as easy. At first I tried to compile the X version on Mac, and it compiled and ran, but sometimes with a white screen only and low level error messages printed on the console. It was clear that OpenGL under X was not stable on Mac. Native OpenGL code had to be used, and that means calls from Objective-C. So to get it to work and avoid writing a lot of Objective-C, part of FsSimpleWindow was used.
The reason for avoiding Objective-C is simple: I don't like it. Still I had to make minor extensions and changes to the code, to set the window title and detect when the program should exit and so on.
This is not a tutorial, but the full source code is available for download. Just copy the code in graph and common if you want a good starting point.
The only differences between the different OS implementations is that fullscreen is not implemented for Mac and Linux.
The other major part that is different on different OS is getting input from the keyboard, mouse and joystick. So a wrapper class was done for input.
I know there are other libraries to do about the same, but I don't like dependencies and like doing things from scratch and also have the source so things can be fixed.
There are a number of utilities made for this game over time. Perhaps the most unique is a program that can set the Unix executable flag on a file within a zip file. Before it was implemented I searched a lot for a windows program that can do this, but without luck. It is quite useful if you want to create a release zip package from Windows valid for Mac and Linux directly after unzipping.
Download zip_exec v1.10. The source code can be found in the game source package.