Adjusting Starfighter

Reading source code is one of those things that I feel that nobody likes, while the advantages are so clear. You can brush up language skills, find out how other people solve things, learn different architectures, find out what the quality of a code base is, et cetera. One cause could be that you don't learn reading other people's code in college. On the contrary, they want you to create little pieces of code that can easily be kept in one head and where correctness is easily demonstrated.

The real world doesn't work like that. If your day job isn't maintaining other people's code in the first place, then you'll get to see your colleague's code at the end of the project when they move on and you get to stay for a little while. Either way, you'll just have to accept that you don't understand everything and get on. For those people who still have that barrier, I did a little writing on how to search through code and getting it to work the way you want it, without feeling lost.

Preparing

First, download Starfighter from the website of Parallel Realities. Make sure to get the executable as well as the source. Then start playing the game!

Note: I'm running Linux. If you're running Windows, install Cygwin, which gives you all the GNU utilities you'll need.

Memory Structures

After I finished the game, I wanted to add Sid's weapon (the Ion Cannon) to the player weapon options. I unpacked the source and tried to compile it. A simple make sufficed, good. The first thing to understand about a program is its memory structures (typedefs, structs, other global variables). Once you understand these, you're often a big step further into understanding the whole concept. Of course struct.h is a candidate, as well as defs.h, since neither has a corresponding .cpp file and probably contains a bunch of constants and type definitions. We can see a list of weapons there, including the W_IONCANNON. Interesting, because now we can find interesting code by searching through the files for this constant!

Shop

I type ls and have a look at the list of files again. Spotting the file shop.cpp, I get the idea that if I would be able to add it to the shop, I would get one step closer to having my own Ion Cannon. So I open up shop.cpp and see what this file consists of. The first thing that comes up is a big switch statement. There's no W_IONCANNON there, so it should probably be added there once we get to know more. I make a mental note.

Then it says in the drawShop() that it loops through the items. I search for drawShop in this file and see that it's called in loadShop(), which basically loads stuff from a file and supplies the shop with the contents. Let's look in the supplied starfighter.pak file. I didn't know how to open it (I thought it was an archive), so I mailed the author and got a reply within the hour. Try that with commercial software.

 The Starfighter pak file uses the following format,
 Header 
 "PACK" - 4 Bytes
 Then the data follows,
 Filename - 56 Bytes
 Data Length - Unsigned int (4 bytes)
 Data (length depends on previous value)
 This is repeated for each filename
 If you look at the source code and at unpack.cpp you will see there is code 
 doing exactly this. That should help you to unpack the data in the pak file.
 Thanks,
 Steve

Duuuuuh, I should've found that out by myself! A file called unpack.cpp should've rung a bell. Looking at it, I see a function called locateDataInPak() but I'm not certain how to unpack and repack it again without some coding. So I leave it for now and see if I can fix it up by replacing an existing item in the shop instead of adding it.

Bullets

But looking through the shop code, that's difficult as well. I don't know what code to replace. Mindlessly, I open up a bunch of files and suddely spot some commented-out debugging code in Starfighter.cpp. It turns on some cheats and defines two weapons. So I put the second weapon on IONCANNON. I compile and run. Doesn't work; instead it still fires a rocket. Hmm, let's investigate further.

I look at the filenames again and see bullet.cpp. It seems to handle the firing of bullets, but when glancing through the code, I don't immediately see why this code still fires a rocket when I changed that debugging code to give me an Ion Cannon. I'm going to debug! First, we need to recompile with debugging information included. That's not done for software that is distributed, since it makes code bigger and slower. The compilation is done using a makefile which is logically called makefile. So, we'll have to adjust that one. The first line looks immediately promising. It says CFLAGS, which defines the compiler flags. To debug, I add -g. I type make clean and make, and see that the flag -O3 is also used. I go back to the makefile and remove that one, since I heard that doesn't go well with debugging. Again, I type make clean and make.

Debugger

If you've never used a debugger, don't worry, I'll show you. Get yourself on the prompt, move into the starfighter-1.1 directory and type gdb starfighter. We get on the gdb prompt. Let's set a breakpoint on line 144: break bullets.cpp:144 (instead of the line number, you can also type the function name). Breakpoint set. Now let's run it: run. The main window appears. I start a new game. I press space to fire what I thought should be an IONCANNON. The breakpoint hits and I run the code line by line to see what happens. This is done with the command next. If you want to see some code surrounding the current line, type list. Stepping through , I see it looks at weapon.id to play the sound. Shit, I should change the weapon ID somewhere as well.

I mindlessly look through some code and can't really find the weapon ID. Is the weapon struct a global or is it connected to the player struct? I see that the game is set up with the newGame() function. I locate it in game.cpp, change the player.weaponType[1] to W_IONCANNON, compile and run without the debugger. The minute I'm in a new game and press the spacebar, it gives a segmentation fault.

I start the debugger again, set the breakpoint, type run, start a new game and pound the spacebar. The debugger kicks in. I do next until it segfaults in addBullet. I don't really know that function so I redo everything and then when I'm at the line that calls addBullet(), I type step instead of the normal next. This moves into the function instead of just running over it, into addBullet(). When I come to the line where it segfaulted (109), I see that one of those variables must have been set to zero. To see the value, I type print attacker->target and see that it's 0. It hasn't been set, that must've happened somewhere else but skipped somehow. This is logical; after all the programmers didn't think of a player using the Ion Cannon.

Sid's Ion Cannon

However, Sid does have a working cannon. Let's search for Sid through the code. On my Linux box, I move into the code directory and type grep -ni sid *. This looks for the string sid in a case-insensitive way and also shows a line number if it finds anything. I see a whole bunch of lines. An interesting one looks to be aliens.cpp, line 995. I don't see anything interesting, except a few lines further it says searchForTarget(). That looks good. I search for that function and it looks complicated but lines 667 and 669 are noteworthy:

 667         i = rand() % MAX_ALIENS;
 668
 669         object *targetEnemy = &enemy[i];

I make a mental note. I guess if there's an aliens.cpp, there must be something for the player and typing ls shows that there's indeed a player.cpp. Looking through the code, a nice place for the code would be in the start of function doPlayer():

 69 void doPlayer()
 70 {
 71         // Search for target if we have ion cannon
 72         int i = rand() % MAX_ALIENS;
 73         object *targetEnemy = &enemy[i];
 74         player.target = targetEnemy;

I recompile by typing make in the starfighter-1.1 directory. I run the game and... it works!! Except, two Ion shots are fired and after that it stops. Must be something with ammo. Edit game.cpp on line 73 and set ammo to 1000. Compiling... It works! Cool! Problems for serious gameplay: it's not like Sid's cannon at all. There are multiple bullets and there shouldn't really be ammo involved (should be unlimited). The firing rate is too fast and the algorithm for targeting randomly picks targets and it should just pick the nearest. Ideally, it should be added to the shop for a nice price. There is more hacking to be done, but I'll leave that up to the reader! Good luck!