Weapon Recoil Code for Doom 3

The following code will allow you to add recoil to any weapon in Doom 3. By applying a force vector parallel to but opposite the direction the player is facing when firing, this mod much more accurately models the force massive firearms such as those found in Doom 3 would produce (Newton's laws, I assume, still apply in the 22nd Century). I have chosen to release this as code, rather than a mod, because a) such a small hack barely deserves such a release, b) the code can be easily modified and applied to any weapon per the users' preference, and c) this method is much more educational.

To begin, you'll probably have to unpack the PK4 files in the Doom 3/base directory. PK4 files are simply renamed .ZIP files, and can be opened or created with many common utilities such as WinZIP and WinRAR. Unpacking these files is a good idea for modders anyway, as it allows easy, quick access to a wide array of editable material.

With the files unpacked, look for the directory named "script" in "Doom 3/base". In it, you'll find a number of files with the extension .script; we're interested in the "weapon_weaponname" group. Specifically, for this tutorial, we'll be editing weapon_rocketlauncher.script, though these instructions can be easily applied to any weapon.

Before you do anything, BACK UP the appropriate script file. I suggest just making a copy and changing the extension to .BAK. Now, open the file with an advanced text editor like Editpad (Windows Notepad doesn't handle some special characters correctly, and may cause problems). Search for the text "void weapon_rocketlauncher::Fire() {" - this is the script called, predictably enough, whenever the weapon discharges -and add the following code on the very next line:

vector pAng = $player1.getViewAngles();
vector recoil = '0 0 0';
float recoilAmount = 600;
recoil_z += recoilAmount*(sys.sin(pAng_x));
recoil_y = -1*recoilAmount*(sys.cos(pAng_x)*sys.sin(pAng_y));
recoil_x = -1*recoilAmount*(sys.cos(pAng_x)*sys.cos(pAng_y));
$player1.setLinearVelocity($player1.getLinearVelocity()+recoil);

Save the file, and that's it! The rocket launcher will now give the player a good, solid kick backwards when fired. You can test it by starting a new game (or loading a map from the console), pulling down the console by pressing control-alt-tilde (~, key to the left of the 1), and typing the command "give all". Then pull your rocket launcher and go to town. You can even increase/decrease the amount of that kick by editing the "recoilAmount" variable (in this example, 600).

For those more curious about this modification, I've included a heavily commented version of this code below. Note that understanding it will require at least an intermediate knowledge of scripting and trigonometry. This code is functional, as it only adds comments, but I've included the un-commented version above because cutting and pasting into a text editor sometimes creates problems with line breaks due to wrapping.

// Begin rocket recoil code
// First, get the angle where the player is looking.
// The vector getViewAngles returns has rotation of the player for x, tilt up/down of the player for y, and always seems to have 0 for z
vector pAng = $player1.getViewAngles();
// Recoil will be, once we're done, an x/y/z vector for the direction the player should fly during recoil.
vector recoil = '0 0 0';
// recoilAmount, used as a multiplier below, controls the severity of the recoil - smaller numbers = less recoil and vice versa
float recoilAmount = 600;
// How far up/down (z axis) the player flies is controlled entirely by the tilt of the weapon, obviously
recoil_z += recoilAmount*(sys.sin(pAng_x));
// x and y are largely controlled by the rotation of the player (pAng_y).
// However, we must multiply this value by cos(tilt) because if the player is facing up or down at all, part of the force applied to the 2d plane is transferred to z
// For example, note when the player looks straight down: sin(90) above will return 1, the maximum, so z moves by a lot. cos(90) is zero, meaning it will cancel out the x and y movement altogether, which is as it should be when aiming straight down or straight up
// Meanwhile, if we face straight ahead with no tilt, sin(0) will be 0, and there will be no change in z. When the weapon isn't tilted, cos(0) is 1, and ALL of the force is felt in x/y directions
recoil_y = -1*recoilAmount*(sys.cos(pAng_x)*sys.sin(pAng_y));
recoil_x = -1*recoilAmount*(sys.cos(pAng_x)*sys.cos(pAng_y));
// setLinearVelocity applies an x/y/z vector that determines what direction and how fast the player is currently moving in units/second
// Basically, once per second, the x linear velocity is added to player location x. Same for y; same for z.
// Note that we add the recoil to getLinearVelocity. This means that if the player was already moving, he continues moving in that direction MODIFIED by the recoil.
// Failure to consider current velocity would result in the player's movement being ONLY the recoil all of a sudden, which can be odd it certain situations (falling from a great height, for example)
$player1.setLinearVelocity($player1.getLinearVelocity()+recoil);
// End rocket recoil code

There you have it. Feel free to edit this code to your heart's intent and include it in your own mods. Have fun!