QuakeC from Scratch tutorials - Chapter 1: These worldly gifts...
Created By: Ender
eMail: ender@admdev.com
Difficulty Scale: Hard


[Scratch base files]

Welcome to Chapter 1. Assuming you've all read the Preface (and if you havn't, please do so now) you should have a fair idea of what we are hoping to accomplish in this series of tutorials.

So let's jump right into it. Begin by making a new directory in your Quake directory. In these tutorial, I'll assume your quake directory is C:\quake, and you've created your Scratch directory as C:\quake\scratch.
In this directory, create a 'source' directory. Under the source directory, create a 'weapons' directory, a 'entities' directory and a 'monsters' directory.
Your tree structure should look like this:
                      
          - Id1      
C:\quake |- Scratch -        / Weapons
         |- Blah     \ Source- Entities
         |- Blah             \ Monsters
         |- Blah
          - Blah
Now download the Scratch base files, from the link at the top of the page. Unzip this .zip archive into the source directory.

In this zip archive, are five 'template' files:
                      
  Progs.src   -- If you dont know what this is, what are you reading this for?
  Defs.qc     -- A stripped down version of the normal one. Bare essentials.
  Main.qc     -- Empty (except for a header)
  Client.qc   -- Also empty (except for the header)
  Dummys.qc   -- Contains remove(self); statements for all the Quake entities.
Defs.qc, Main.qc and Client.qc we dont need to worry about for the moment.
Lets have a quick look at dummys.qc

All Quake maps have entities. These are things like info_player_start, weapon_shotgun and light_fluro.
Each of these entities has a corrosponding QuakeC spawn function of the same name, which is called to create these entities every time Quake encounters one as it loads a map.

If this spawn function is missing, Quake will print an error message to the console. As, to begin with, we wont have any of these coded, you are looking at pages and pages of errors. To save you having to wait for all these to scroll offscreen, I've supplied this file with definitions for all the normal Quake map entities.
All the spawn functions for these entities do is call remove(self), which you hopefully know will simply remove the entity. At the beginning of a spawn function, 'self' is set to the entity you are creating.
In the following tutorials, when you start adding in things like lights, you will start deleting the definitions from dummys.qc and start placing them in their own file.

Now onto acually 'spawning the world'.
If you try and compile the source now, it simply won't work. The first, and most important, milestone is to simply get a map to load.
The Quake1 engine expects 11 functions to already exist in the source, and calls them internally.

These are:

  worldspawn        - Called when the world.. umm.. spawns!
  main              - Have no idea when this is called...
  StartFrame        - Called at the start of each frame
  ClientConnect     - Called when a client connected to the server
  ClientDisconnect  - Called when a client disconnects from the server
  ClientKill        - Called when a client issues the 'kill' command
  PutClientInServer - Called to spawn the clients player entity
  PlayerPreThink    - Called every frame, before physics.
  PlayerPostThink   - Called every frame, AFTER physics.
  SetNewParms       - Called on level change
  SetChangeParms    - Called on level change
So, the first step is to create these functions.
Place the following lines in main.qc:

 void() main = {};
 void() worldspawn = {};
 void() SetNewParms = {};
 void() SetChangeParms = {};
 void() StartFrame = {};

And add the following lines to client.qc:

 void() ClientKill = {};
 void() ClientConnect = {};
 void() ClientDisconnect = {};
 void() PlayerPreThink = {};
 void() PlayerPostThink = {};
 void() PutClientInServer = {};


Compile this, and try running Quake with the 'scratch' progs.dat Argh! Player.mdl isnt precached? Never mind, this is easily fixed.
At the top of main.qc (after the title) add the line:
void() precaches;
Then inside worldspawn add the line:
precaches();
And at the END of main.qc, add the acual precaches function:

void() precaches =
{
 precache_model ("progs/player.mdl");
};
Now, recompile and try Quake again.
Success! But why (assuming your using the start map) have you spawned outside the level, and at that funny angle?
Well, you have spawned at position (0,0), which may not necessarily be inside the level. The next thing you need to do is to spawn a player at one of the info_player_start entities.
Open up client.qc.
Scroll down to 'PutClientInServer' and alter it as follows:

void() PutClientInServer =
{
local entity spawn_spot;             // This holds where we want to spawn
spawn_spot = find (world, classname, "info_player_start"); // Find it :)

self.classname = "player";           // I'm a player!
self.health = self.max_health = 100; // My health (and my max) is 100
self.takedamage = DAMAGE_AIM;        // I can be fired at
self.solid = SOLID_SLIDEBOX;         // Things sort of 'slide' past me
self.movetype = MOVETYPE_WALK;       // Yep, I want to walk.
self.flags = FL_CLIENT;              // Yes, I'm a client.

self.origin = spawn_spot.origin + '0 0 1'; // Move to the spawnspot location
self.angles = spawn_spot.angles;     // Face the angle the spawnspot indicates
self.fixangle = TRUE;                // Turn this way immediately

setmodel (self, "progs/player.mdl"); // Set my player to the player model
setsize (self, VEC_HULL_MIN, VEC_HULL_MAX); // Set my size

self.view_ofs = '0 0 22';            // Center my view
self.velocity = '0 0 0';             // Stop any old movement
};
Recompile, and run Quake....
Yes! Now, it works! You can walk around the level (although somewhat bare..)
In the next Chapter, we will look into adding some atmosphere, by adding in the lights which are so conspicious by their absense.. Then in Chapter 3, we will add some more atmosphere with some Ambient sounds.