CounterStrike clone
Created By: Koolio
eMail: koolio@mdqnet.net
Difficulty Scale: Hard


If you've player CounterStrike you know that when you connect to the game you start as an observer untill you select a team and the round is over. Today's lesson will cover the observer mode that lasts as long as you don't have a team. Alrighty then, let's start by opening client.qc and find PutClientInServer. Now remove everything between the header and before the local entity spot; So this part:

/*
===========
PutClientInServer

called each time a player is spawned
============
*/
void() DecodeLevelParms;
void() PlayerDie;


void() PutClientInServer =
{
And paste this there instead:

/*
===========
PutClientInServer
Koolio, CS Bomb mission
Makes you an observer if you don't have a team or spawns you if you selected one
called each time a player is spawned
============
*/
void() DecodeLevelParms;
void() PlayerDie;

void() PutObserverInServer; //Koolio,Declare observer mode before it's being used
				    //It's located in src/cs-bomb/observer.qc

void() PutClientInServer =		//Koolio,The normal putclientinserver
{
if (self.team == NO_TEAM) // if you don't have a team selected 
{ 
PutObserverInServer(); // Run the observer func 
return; // and forget the rest 
}
Let's explain what this does. We need to declare PutObserverInServer first because we are going to use it before the compiler sees in in the progs.src (because we will be putting it in a seperate file) Then when the PutClientInServer func gets executed (every time you spawn, respawn etc) the code checks if you have a team selected. If so it just does it's normal things (make you spawn) but if you don't then it runs the PutObserverInServer and stops the PutClientInServer func (that's what the return; does)
Now find PlayerPrethink, which is run every frame (ten times a second) and find this:

	if (intermission_running)
	{
		IntermissionThink ();	// otherwise a button could be missed between
		return;					// the think tics
	}
After that paste this:

//Koolio, makes you an observer if you don't have a team
	//or makes you spawn if you do have a team
	if (self.team == NO_TEAM && self.classname != "observer") //This should explain itself
					    //the classname check is to prevent keeping respawning all the time. 
					    //Try removing the classname check to see what I mean
	PutObserverInServer(); //Make me an observer
	if (self.team != NO_TEAM && self.classname != "player") //And vise versa
	PutClientInServer();
In proper English: Every time PlayerPrethink is run this piece of code checks if you don't have a team and what classname you have. The PutClientInServer func will rename the classname which is normally player to observer. If I didn't check for the classname you'd keep respawning as an observer untill you select a team. If this is all true then we spawn as an observer. The next thing we do if check if the player does have a team and it's not NO_TEAM (which is 0) and his name isn't player. If that's true then we spawn as a normal client. Now you might wonder how do we check if something is true or false. If you write something like:

if (self.classname == "monkey") 
you're checking if something is true. The == means 'is this true?' and if so returns TRUE and proceeds, otherwise if fails, stops the current check and moves on to the next line. The same goes for checking if something is not true but instead you'd write:

if (self.classname != "monkey")
Another thing we used here was the double & which means AND. Thus if you write:

if (self.classname != "monkey" && self.classname != "aap")
You'd do two checks and it's only true if both are correct, otherwise it fails. You can also do an OR check like so:

if (self.classname == "monkey" || self.classname == "aap")
Now the check returns TRUE if either is correct and only fails when neither are. You don't really have to worry about the TRUE and FALSE bit, it just means if it's true it moves on and otherwise it skips the check and moves on to the line after it. On a side note: a lot of people confuse the == with =. If you'd write:

if (self.classname = "monkey")
that piece of code wouldn't check if your classname is monkey but it would just make your classname monkey. Everytime you need to assign a value, name or etc to something use =, if you need to check something use ==. Yes, that explaination is pretty long but remember we're here to explain not just provide with copy & paste stuff.

If you already knew the above part you probably skipped it, if not you've just learned something new. Let's move on shall we? Find ClientConnect and after:

	bprint (" entered the game\n");
Paste:

	self.team = NO_TEAM;//Koolio, you're teamless on connecting
This makes sure you don't have a team when you connect. Save and close. Next stop: weapons.qc. Because observers can't participate we'll have to stop them from attacking. Find W_Attack and after:

	if (!W_CheckNoAmmo ())
		return;
add:

if (!self.team)
return;
This checks if we have a team and if so stops attacking. The last thing we have to do is make a new file called observer.qc and paste the following in it:

//Observer mode, you start out as this if you don't have a team
//Basically modified PutClientInServer to be more like an observer mode

void() PutObserverInServer =		//Koolio, Just like PutClientInServer
{
if (self.team != NO_TEAM) // if you already have a team 
{ 
PutClientInServer(); // And run the normal PutClientInServer
centerprint (self, "You have a team");
return; // and forget the rest 
}
	local	entity spot;

	spot = SelectSpawnPoint ();

	self.classname = "observer";
	self.health = 666;			//Heh, just for fun
	self.takedamage = DAMAGE_NO;  	//You can't take damage
	self.solid = SOLID_NOT;       	//You're not solid (so you can't (be) telefrag(ged)
	self.movetype = MOVETYPE_NOCLIP;	//You can move just like in noclipping mode
	self.show_hostile = 0;			//The rest is pretty much the same as in PutClientInServer
	self.max_health = 666;
	self.flags = FL_CLIENT;
	self.super_damage_finished = 0;
	self.radsuit_finished = 0;
	self.invisible_finished = 0;
	self.invincible_finished = 0;
	self.effects = 0;
	self.invincible_time = 0;

	self.items = self.items - ( // take all of their stuff
            IT_AXE | IT_SHOTGUN | IT_SUPER_SHOTGUN | IT_NAILGUN | IT_SUPER_NAILGUN 
            | IT_GRENADE_LAUNCHER | IT_ROCKET_LAUNCHER | IT_KEY1 | IT_KEY2); 

	W_SetCurrentAmmo ();

	self.attack_finished = time;	
	self.deadflag = DEAD_NO;
// pausetime is set by teleporters to keep the player from moving a while
	self.pausetime = 0;
	

	self.origin = spot.origin + '0 0 1';
	self.angles = spot.angles;
	self.fixangle = TRUE;		// turn this way immediately

// oh, this is no longer a hack!  =)
	//setmodel (self, "progs/eyes.mdl");
	//modelindex_eyes = self.modelindex;

	setmodel (self, "progs/eyes.mdl"); //We're going to replace this soon
	modelindex_player = self.modelindex;

	setsize (self, VEC_HULL_MIN, VEC_HULL_MAX);
	
	self.view_ofs = '0 0 22';

	player_stand1 ();
	
	if (deathmatch || coop)
	{
		makevectors(self.angles);
		//spawn_tfog (self.origin + v_forward*20); //You can uncomment this if you want the normal fog to apear
									 //Like it does with players
	}

	spawn_tdeath (self.origin, self);
};
Save and close observer.qc and open progs.src. After client.qc add observer.qc, save close and compile. Now when you spawn you enter observer mode with 666 health and armor with a blue quad vision. When you select a team you'll respawn as a terror or counter and play like normal. Note: if you change team during normal play you won't respawn as the new team, as our check prevents it (your classname is not observer anymore) Enjoy observing!
Side note: In CounterStrike observer mode you can move like you're swimming, this observer mode behaves just like noclipping mode. If you want swimming like behavior you'll have to make Quake believe you're swimming, there's a tut somewhere here that does something like that (http://www.inside3d.com/qctut/qctut-22.shtml).

All code not originally found and stuff changed written by Koolio. Tutorial written by Koolio, koolio@mdqnet.net, and HTML-ized by Kryten, kryten@inside3d.com You can use this tut in your mod provided you give me credit.