QuakeC from Scratch tutorials - Chapter 4: The golden mirror...

Inside3D
 • Front Page
 • Articles
 • Downloads
 • Forums
 • Hosting
 • Help Wanted
 • Interviews
 • Reviews
 • Staff

 
Allied Sites
 - AI Cafe
 - Bot Epidemic
 - Dark Places
 - Flat Earth
 - FrikBot
 - Ghoulish Art
 - Kilbert3D
 - Quake Info Pool
 - QSG: Quake Standards
 - Rusted Fork
 - Stroggs Gone Mad
 - More...
 
Quake
 - Getting Started
 - QuakeC Specs v1.0
 - QuakeC Tutorials
 - Quake Mod Index
 
Quake II
 - Getting Started
 - dll Sourcecode
 - Coding Tutorials
 
.dll Central
 - Getting Started
 - Learning
 - dll Primer
 - dll in C
 - dll in Delphi
 - Compilers
 
Jedi Knight
 - Getting Started
 - COG Specs
 - Sound Specs
 - Tutorials
 
Level Editing
 - Tutorials/Tips
 
 
Telefragged!!!
Created By: Ender
eMail: ender@admdev.com
Difficulty Scale: Hard


Well, it mightn't look like much, but we've come a long way. So far, we have the very most basic elements of Quake. A map, lighting, and Ambient sounds. But still, there is something missing. Something you can't yet see. It should be obvious what it is, as you havn't coded it in. But maybe not. After all, there is a -lot- you havn't coded in.
Player Animation. Something which we will shortly make conspicious by it's absence. In a few lines, I'm going to instruct you on adding a Chasecam to Quake. It's something you will be using a lot of, to make sure things are looking just right.

You are now going to make yet another directory, called 'effects', inside your Scratch\Source directory. Your new directory structure should look as follows:
                      
          - Id1      
C:\quake |- Scratch -         / Weapons   / Entities
         |- Blah     \ Source-|----------- 
         |- Blah              \ Monsters  \ Effects
         |- Blah
          - Blah
Inside this, create a new file. Once again, you will want to add the customary headers. Call this file, 'Chasecam.QC' and place the following code inside it:

/*
+--------+
|Chasecam|
+--------+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+
| Scratch                                      Http://www.admdev.com/scratch |
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+
| Simple Chasecam. Follows player for above and back slightly. Impulse 100   |
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+
*/      

.entity camera;             // Chasecam Entity
void() Chasecam_Update;      // From Chasecam.QC

void() Chasecam_On =
{
 self.camera = spawn();                       // Create a new Camera entity
 self.camera.classname = "camera";            // This is a Camera
 self.camera.movetype  = MOVETYPE_NONE;       // No movetype
 self.camera.solid     = SOLID_NOT;           // Not solid.
 setmodel(self.camera, "progs/s_bubble.spr");
 setsize(self.camera, '0 0 0', '0 0 0');      // Has no size.
 Chasecam_Update();                            // Update cam
 stuffcmd(self, "r_drawviewmodel 0\ncl_bob 0\ncl_rollangle 0\nv_kickpitch 0\nv_kickroll 0\n");
 SetViewPoint(self.camera);
};

void() Chasecam_Off =
{
 stuffcmd(self, "r_drawviewmodel 1\ncl_bob 0.02\ncl_rollangle 2.0\nv_kickpitch 0.6\nv_kickroll 0.6\n");
 SetViewPoint(self);
 remove(self.camera);
};

void() Chasecam_Update =
{
local vector camera_origin;
 if (!self.camera) return;
 makevectors (self.v_angle);
 traceline (self.origin + self.view_ofs, self.origin + (self.view_ofs * 2) +
           (v_forward * -64) + (v_up * -6), 0, self);

 camera_origin_x = trace_endpos_x;
 camera_origin_y = trace_endpos_y;
 camera_origin_z = trace_endpos_z - self.view_ofs_z;
 camera_origin = camera_origin + trace_plane_normal;
 setorigin (self.camera, camera_origin);
};

void() ChaseCam_Toggle =    // Toggle Chasecam on/off
{
 if (!self.camera) {Chasecam_On();} else {Chasecam_Off();}
};
Add this file (Effects/Chasecam.QC) to the bottom of PROGS.SRC.
This is a rather basic chasecam, that works by creating a new entity and telling Quake to set this as the 'viewpoint' entity. The camera is then moved with the player, whenever Chasecam_Update is called.
There are four things left to do. Firstly, we need to precache s_bubble.spr, so load main.qc and put the lineL
precache_model("progs/s_bubble.spr");
with the other precache.
Next, we use a function called SetViewPoint in the Chasecam, to set the viewpoint entity. But this isn't a normal QuakeC function. It's something we will have to add.
Because this is a function related - vaugely - to entity management, it belongs in 'Internal.QC', so open this file. Add the following code at the end of it:

void(entity ent) SetViewPoint =  // Alter the Viewpoint Entity
{
 msg_entity = self;         // This message is to myself. 
 WriteByte(1, SVC_SETVIEW); // Network Protocol: Set Viewpoint Entity
 WriteEntity(1, ent);       // Write entity to clients.
};
This small function act's by using the Quake Network Protocol. SVC number 5, SVC_SETVIEW, is not defined in the usual defs.qc, but is in the one included with Scratch.
There are two other things left to do. Firstly, -when- is Chasecam_Update called? Thus far, only upon creating a camera. We need it to run every frame.
We also need it to run before the Quake physics. This isn't so important with this particular chasecam, but most Chasecam's generally affect the player's angles in such a way that they are required to run before Quake's physics.
If you can remember back to Chapter 1, this is where PlayerPreThink, in Client.QC, becomes useful. Save & Close Internal.QC, and open Client.QC.
Alter PlayerPreThink to read:
void() PlayerPreThink = {CheckImpulses(); Chasecam_Update();};
Also at the top of Client.QC, after the header, add the prototype:
void() Chasecam_Update;      // From Chasecam.QC
Remember from earlier that the Quake Compiler (QCC) doesn't know about this function yet, as Chasecam.QC comes later in the progs.src CheckImpulses? Yes. Now you come to the last point for this tutorial. All Quake players and coders know about impulses. They are those little things you type from the console to call special functions of Quake mod's.. and even.. *gasp* CHEAT!
They also need to be implemented in code, and better sooner than later :) Create yet another file, Impulses.QC, in the main source directory. A header, and (at this point) some simple code will suffice:

/*
+--------+
|Impulses|
+--------+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+
| Scratch                                      Http://www.admdev.com/scratch |
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+
| Handle and execute "Impulse" commands - as entered from console.           |
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+
*/      

void() CheckImpulses =
{
if (self.impulse == 100) {ChaseCam_Toggle();}   // If Impulse 100, toggle cam
self.impulse = 0;                              // Clear impulse list.
};
Very simple. Waiting impulses are help in self.impulse. Here we check the value of this. If it equals 100, turn on the chasecam. After the Impulse has been handled, we reset self.impulse to 0 so we can get the next Impulse command.
Save and close this file, then add it's entry into PROGS.SRC - Because this subroutine always calls functions from earlier in the program, and should only be called from Client.QC itself, it should ALWAYS remain at the bottom of PROGS.SRC.
Note that you will have to add another prototype for CheckImpulses at the top of Client.QC. You should be able to do that yourself by now :)
Compile, and assuming all is well you should be able to enter 'Impulse 100' from the console and the chasecam should activate.
See something wrong? Exactly... this makes way for our next tutorial..






The Inside3D content and design is copyrighted by the Inside3D team, and may not be re-produced or copied without proper consent. All rights reserved. Any work credited to any author not part of InsideQC is copyrighted by that author. If you have any feedback, suggestions or flames please send it in to the author. Inside3D is hosted by telefragged and the design was originated by project-9.