Site hosted by Angelfire.com: Build your free website today!



Home Turret Defense UPS Tutorials



Essential Files

Quake C Files (thanks to inside3d.com)
Quake C Compiler (thanks to inside3d.com)

Available Tutorials



Starting Your First Mod VERY EASY
How to display text/when to use it VERY EASY
All about sounds VERY EASY
Data Types EASY
Jump Higher With More Health(introduction to IF commands)EASY


1.Starting Your First Mod back to top
So your ready to begin modding quake? Well it's a great experience. The language is known as QuakeC (hence .qc files). It is based on the C computer language. Any knowledge of this will make your modding experience much easier. Well lets get to it.
The first thing you are going to do (assuming that you have Quake installed on your computer) is download the un-compiled .qc files. These are what you will be editing, or modding, and are the brains behind your mod. What these files will give you is access to all the code that makes quake what it is. Files such as weapons.qc will contain all of the games data on the weapons. Want to make the rocket launcher fire 100 rockets at once? This is were to do it. The .qc files were originaly realesed by John Carmack (i believe?) and are well commented and easy to read.
Now that you have the .qc files, you are going to want to show quake that you have a mod to play! Get into your quake folder (C:\quake) and make a new folder. (Right click -> new -> make new folder) Now name the folder, preferably something short and catchy as this will be the name of you mod (dont worry you can always change it :p )in this folder you are going to make a new folder and title it "qcs". (C:\quake\sweetmod\qcs) Now paste all of those .qc files into this folder. This will not actually be read by your mod, but this is were you will be editing the code.
Now you need to compile that code! "Compile" is what you must do to the simple text .qc files for the quake engine to read them. As you can see the .qc files can be opened in any text editor (i use wordpad), but the quake engine cannot read them like that. The compiler will translate the text inside these .qc files into a new file that the game can read. This is called "progs.dat". Progs.dat is your mod. All the code you write is compiled into this one file. Once you have the compiler, paste it into your qcs folder. Now lets make a mod!
You should have a folder inside your quake folder with the name of your mod. Inside that folder there should be another folder called "qcs", and inside this folder all of the .qc files and a complier should be waiting. Now run the compiler! Depending on what compiler you are using something will happen. With the compiler i provided a screen will pop up and scroll through all of the .qc files. This is the compiler reading your .qc files and writing them to progs.dat! The compiler will finish and should close automaticly. Now look for progs.dat in your mod folder (c:\quake\mod\progs.dat). You have made your first mod! Exciting huh? No? I guess your right... Lets make something new!
Go into your qcs folder and open weapons.qc (excited yet?) Now open a find window (or find it yourself heh heh) and search for "W_Fire_Shotgun". This should bring you down the page maybe 1/8 of the way. You will see a lot of code here, dont get scared! Look around, read around, the more you are exposed to it the quicker you will learn it. What you are looking for is a number, i know there are a lot, but this number will change the number of bullets the shotgun fires. Can you find it? "FireBullets(...)" is the one! What you are looking at is a function. FireBullets is a function defined elsewere in the file that takes the data in the ('s and )'s and applies it to what it plans to do. Right now the you should see this:

FireBullets (20, dir, '0.04 0.04 0');

You see that 20? Lets make it look like this:

FireBullets (100, dir, '0.08 0.08 0');

You now have the FireBullets function firing 100 shots, in the players direction (dir), with accuracy of '0.08 0.08 0' (slightly less because of the huge increase in shots, give them a chance!) Now before you save, go back and add this!

FireBullets (100, dir, '0.08 0.08 0'); // I added this!

Now you have commented your work. // is the signal for the compiler to ignore anything else on that line. Therefore you can // a line of code anywere and it will not make it into the game, because the compiler will ignore it. Be carefull you dont comment lines of needed code, unless you want to run the mod without that line! NOW SAVE, COMPILE, RUN! (to run your mod, or any other mod, go to run: "c:\quake\quake.exe -game mymod" | this is going to run quake.exe in the c:\quake folder, with the extension to run your game. Find a baddy and give the trigger a pull!


2.The Writing's On The Walls. Showing Text In Your Mod back to top
There are a few ways to display text in quake. They are the functions sprint(), centerprint(), and bprint(). They each work in their own ways, and have various limits. We will be working off the first tutorial with the shotgun, to keep it simple. Fin this line:

FireBullets (20, dir, '0.04 0.04 0');

Here, as you learned in tutorial 1, is the function that the shotgun uses to fire bullets. Add a new line after this one to look like this:

FireBullets (20, dir, '0.04 0.04 0');
Sprint(self,"BANG! BANG!\n");

What have we done?!?! Calm down. You have just made the game print "BANG! BANG!" when you fire the shotgun. Stupid i know, unless you the person playing your mod is deaf. See it wasn't so dumb. Anyway, lets look at what this does. The function sprint() requires a few parameters to work, like FireBullets() did. Self in sprint(self,"BANG! BANG!\n"); refers to the entity that will recieve the text. Self is any entity that runs this function. This can be other entities depending upon what's going on, but that gets more complex. For now just use self. The next line in sprint(self,"BANG! BANG!\n"); is the text to print. You are writing BANG BANG, corny, but thats what it doing. The \n is telling the game that this is the end of this line. This is basicly an "enter" button in your text. Without this all the text would run accross the top of your screen making it difficult to understand. When wouldnt you use this? Lets see:

FireBullets (20, dir, '0.04 0.04 0');
sprint(self,"BANG!"); //no \n here
sprint(self,"BANG!"); //no \n here
sprint(self,"BANG\n"); //there it is!

Now this will print the same thing, but its broken apart. This is useful if you are printing variables or whatever else you want. This will be discused in future tutorials. You can compile and run this whenever you want, make sure to put those ";" at the end of the lines!
centerprint() is the next way to display something. This uses the same basic function, except it will print in the middle of your screen(centerprint...) . put in the entity to print to and your message, just like before. With centerprint() however, you will not be able to break it apart as we did before. Centerprint overwrites whatever is printed in the center when it is called again. So:

FireBullets (20, dir, '0.04 0.04 0');
centerprint(self,"BANG!"); //no \n here
centerprint(self,"BANG!"); //no \n here
centerprint(self,"BANG\n"); //there it is!

Can you guess what will happen? The only thing that will show up is the final BANG that you printed, why? You overwrote the previous BANG's with the new one. Even with \n after then they would still be overwritten. What you would be able to do is this:

FireBullets (20, dir, '0.04 0.04 0');
centerprint(self,"BANG! \n BANG! \n BANG! \n");

You would now have BANG! printed 3 times in the center of your screen, one on top of the other, on top of the other. This is because the \n broke the line apart and in the same function put 2 lines of text. Therefore the old text was not over written. Centerprint is useful in cases were sprint would not be, as sprint stacks up on the top of the screen and can get anoying.
bprint() is the final print function we will go over. bprint() sends a message to every client (player) in the game. Therefore can u guess the difference with bprint()? There is no need for a entity parameter, because it goes to everyone.

FireBullets (20, dir, '0.04 0.04 0');
bprint("BANG! \n BANG! \n BANG! \n");

This will tell everyone that your firing your shotgun, and if you do this they will probably wish they could shoot you back because that would be quite anoyying! bprint() is used to announce deaths and new/leaving clients, and other such global notices.
Now you can let the player know whats going on in your mod, and in a few ways. Check out fallow ups to learn more about text.

2.Shhhh, Did You Hear That? Sound In Quakeback to top
Sounds, the text functions biggest enemy. Well not really, just trying to make codeing sound funny... Well, sounds played in quake run through another, can you guess?, function! Go to that W_Fire_Shotgun function and find this:
sound (self, CHAN_WEAPON, "weapons/guncock.wav", 1, ATTN_NORM);
Here you see sound() and some parameters inside. As you now know, self refers to who the sound will be playing. CHAN_WEAPON is the sound channel that the noise will paly on. Sound channels are the different channels that sound can play on at one time. I do not know exactly how this works but I believe it is the way the game communicates with the sound card and how many sounds it can play at a time. The problem with channels is that if 2 sounds play at the same time on the same channel, one will over-ride the other. The available sound channels are CHAN_WEAPON, CHAN_VOICE, CHAN_ITEM, CHAN_BODY, and CHAN_AUTO, wich can be used to make a new channel, and then you dont need to worry about over-riding other sounds. Now the filename of the sound is put in quotes, as are most strings in quakeC. Next there is a the "attention" of the sound. ATTN_NONE would be none, meaning the sound would be heard everywere in the level. ATTN_NORM means the sound originates from the "self" and anyone within a certain distance can hear it.There is an ATTN_IDLE state, not sure what that does, and 3 is ATTN_STATIC, also not sure about that one. Sounds will play once, and then terminate, unless the sound file is a repeating file. Not much fun to have with this but now you know how it works!

2.Data types/uses, because god knows i can't remember everything!back to top
This might be a little bit harder to grasp if you are new to coding, or maybe even if your not. I will explain the ways to store data in Quake and how to recall it and use it. What is data storage? It is the backbone to your mod! Input must be stored, or it is pretty much useless. You may know some types of input, "impulse 9" for all you cheaters (it's ok we all do), "impulse 255" for that one shambler you couldnt get past, but theres many more. In quake, "impulse" is one way of sending data to the game. But every thing you do is a form of data. Your movement speed, your ammo, health, weapon, even your location in the world of quake. It is all stored somewhere and is being used or changed constantly.
Understanding data: Open up your defs.qc file. In here you see many lines of code, most likely very confusing. These are the deffinitions of quake variables. (get it? defs.qc...) Look around, you will see many things. Notice how the lines are started, float, string, entity? These are all types of data storage.
FLOAT, or .float, is numerical data. This means that you can store things such as "a players health is 100". This would look like:

self.health = 100;

You see, .health is being set to 100. .health would be defined in defs.qc as ".float health". This gets a " . " infront of float is because it is a data type that can be assigned to any entity. This is very important to understand, because should it be defined as "float health" without the period, health would not be changable for each entity, it would be a singular value. float's without the period are used for global values, such as game time, frag limits, and other numerical values that any function may need to call. The other types of deffinitions are quite similar.
VECTOR, or .vector, is the same thing as a float value, with one exception. It has 3 storage fields. To understand this think about an object in the room you are in, yourself even. Now look at the center of the room. The center of the room holds a vector of 0 0 0. This means that it is 0 units on the X axis, 0 units on the Y axis, and 0 units on the Z axis from the center. You on the otherhand, most likely, are not 0 units from the center. You are however many feet away from it. You can look at this as your origin. Your origin is your relation to the world's center (0 0 0). Your origin is a vector. It has 3 values, your X value, Y value, and Z value. This would look in quake like this:

self.origin = '25 8 -15';

That means you are 25 units x from the center, 8 units y, and -15 units z. I dont expect you to try to get to this position in your room because -15 z means below the center of the room, and you might have nice floors.(hehe get a shovel). But now you should understand what a vector is. Vectors can also be used to store other data than location, anything that you want to assign 3 values too. To recall a single value from a vector, simply take add _x, _y, _z to it. Say you wanted to print how much higher or lower you are than the center of the map.

.vector myspace;
self.myspace = self.origin;
if(self.myspace_z < 0)
{
sprint(self,"You are below the center");
}
if(self.myspace_z > 0)
{
sprint(self,"You are above the center");
}

Do you see how our new vector "myspace" was given a value? "myspace" was assigned the same value as out location. This means that it takes the same x,y,z as self.origin. Then the self.myspace_z is checked and depending on your altitude, a message is printed. Now you can store values as vecotrs and extract their data whenever you need it!
STRING, or .string, is the data type responsible for text storage. Data such as player names, map names, classnames, and other text in the game, is stored in strings. Strings work like numerical storage in that they can be assigned to entities as .string, like player names, or a global string, like map names. To display a string, you would write the string in the sprint() function, like this:

sprint(self,self.netname);
sprint(self,"\n");

That would print your name on the top of the screen, AND if you remember correctly, the "\n" must be printed after it to enter the line. Strings, not just for your cat...
ENTITY, or .entity, is the data field for making entities or remembering an entity. Entities fields can remember and access data from other entities. Say i (self) had an entity value assigned to my .friend field, i could derive my .friend's name and print it. Look:

.entity friend; self.friend = attacker;>//someone who attacks you, a player, maybe a monster.
sprint(self,"My new friend is ");
sprint(self,self.friend.netname);
sprint(self,"!\n");

Say this was run when somone attacked me, they would become my .friend, and their netname would be printed. The entity field is also in another place in that example, do you see it? The "self" in the sprint() function is an entity! It is you. Could you not change that for a different effect?

.entity friend; self.friend = attacker;>//someone who attacks you, a player, maybe a monster.
sprint(self.friend,"Your new friend is ");
sprint(self.friend,self.netname);
sprint(self.friend,"!\n");

Now the output will go to the person who attacked you. Isn't this fun? Now you know how to use data fields, go make a mod!


2.Smoking Can Hurts Your Athletic Abilities, As can gunshot Wounds And Shambler Encountersback to top
This is going to show you how to use a players health to determine how high he should jump. This will introduce you to a few new commands (if you've been fallowing along) such as the if/else if/else commands. First we are going to need to find the jump command. It is located in the client.qc file. Open this up and find the PlayerJump function. Near the bottom of this function you will find:

self.velocity_z = self.velocity_z + 270;

What you see here is a call to change a property of the player. When he presses jump, his velocity is changed. Notice the "velocity_z" is the only thing that is changed, not the whole velocity. This is becuse the player may be running any direction and we would not want the change the way hes running, just his jump. Lets say that we want the player to be able to reach his max jump height (270) when his health is above 70. This way you can play the standard quake maps and still reach those hard to reach places. Try to figure out how we will do this. We want to use an if command. Start your if command above the previous code so it looks like this:

if(self.health >= 70)
{
self.velocity_z = self.velocity_z + 270;

}
else
{
self.velocity_z = self.velocity_z + self.health + 150;
}

Well what do we have here? Using both IF and ELSE commands. What will this do? Lets look. You press jump-> the above code is run. It checks to see if your health is greater than or equal to 70, and if it is, you jump at the normal height. But if your health does not meet the requirement, the else case is run. You now only jump 150 + your health (just for added fun). This means the highest you can jump is (150 + 69) not enough to reach some of those secrets you love in quake. Go grab a health pack!
You want an even cooler effect? Let's use an "else if" check! Add this to your code:

if(self.health >= 70)
{

self.velocity_z = self.velocity_z + 270;
}
else if(self.health >15)
{
self.velocity_z = self.velocity_z + self.health + 150;
}
else
{
sprint(self,"You are too injured to jump!\n");
sound (self, CHAN_VOICE, "soldier/pain1.wav", 1, ATTN_NORM);
// let the monsters know im weak
}

Great! Now what did we do? Lets review. There is an "else if" check after the "if" statement was closed. Now it checks if your health is above 15, and then it does just what it did before. But if you just took on a fleet of Shamblers with your axe, you might be below 15 health. Now you can't jump! Instead of jumping, the player will grunt and a message will let you know what happened. Save and Compile!