[Tutorial] Team Specific Gameplay

Discuss programming in the QuakeC language.
Post Reply
ceriux
Posts: 2230
Joined: Sat Sep 06, 2008 3:30 pm
Location: Indiana, USA

[Tutorial] Team Specific Gameplay

Post by ceriux »

Ok, because i was having issues with setting skins to the player model and figured it out. i wanted to write a tutorial. But i cant find a really basic way to just set it up. SO im going to write a fairly large tutorial on coding in team specific gameplay. this code is basic and could be expanded upon. but, it should be a good start for anyone trying to learn. ill try and add in as many comments as possible.

So, lets get started.

our first step is to make two new .qc files. Call the first one
moddefs.qc
and the second
menu.qc
.

the reason in which i say to make your own defs.qc files is its easier to keep try of all of your custom definitions this way.

Next : open up menu.qc.

in this file is where you will handle about 99% of your menu code.

the first thing we want to do is make the menu it's self.

so TYPE THIS OUT YOURSELF into the top of your menu.qc.


Code: Select all


void() Menus =

{
	if (!self.menu) // if your not in a menu , do NOT print it " ! means not"
		return;
	if (self.menu == 1) // but if the menu equals " == means equals or equal to" 1.
	
		centerprint(self, "Select Your Team \n 1. Green Team \n 2. Red Team \n"); // then print the menu. centerprint() prints a message in the center of the players screen. self means your printing the message to the owner - the player in this case. after the comma  you put your message or menu text, it must be quoted.

};

the next step "still in menu .qc" is to make the first part of the menu selection code.

so right below the "Menus" function you just made. put this:

Code: Select all


void(float inp) Select =

{
	if (!self.menu) // if not in a menu do nothing.
		return;
	if (self.menu == 1) // if the menu is on menu 1.
	{
		if (inp == 1) // and if inp equals 1. do stuff.
		{
			stuffcmd(self,"color 3 3"); // this is a stuffcmd stuffcmds can be used for a lot of things how you use them are up to you. this one sets the players colors to green.
			self.pteam = 1; // sets the players team through pteam, pteam is a .float.
			setmodel(self,"progs/player.mdl"); // sets the players model.
			self.skin = 1; // sets the players skin number on the model.
			modelindex_player = self.modelindex; // im not exactly sure how to explain this but i know with out it the skin will not set  , YOU MUST HAVE THIS.
			self.movetype = MOVETYPE_WALK; // sets the players move type to walk.
	                self.menu = 0; // turns the menu off.
			self.impulse = 0; // resets the impulse number.
			centerprint(self,"\n"); // prints nothing and closes the menu.
		}
		if (inp == 2) // all the same stuff but for the other team.
		{
			stuffcmd(self,"color 4 4");
			self.pteam = 2;
			setmodel(self,"progs/player.mdl");
			self.skin = 2;
			modelindex_player = self.modelindex;
			self.movetype = MOVETYPE_WALK;
			self.menu = 0;
			self.impulse = 0;
			centerprint(self,"\n");
		}

	}
	Menus();
	self.menutime = time + 0.2;
};
that last function sets up the first part of the in menu selection. basically if your in a certain menu and "inp" the number of the key you press. it does that action.

now your finished in menu.qc

our next step is to go to moddefs.qc.

if you want you can just paste all this in there.

Code: Select all

// Function defs
void() PutObserverInServer;
void(float inp) Select;
void() SecondImpulse;

// floats and globals
.float menutime;
.float pteam;
.float menu;
all finished here. save and close.

next we'll move on to client.qc.

search for SelectSpawnPoint.

replace that entire function with this:

Code: Select all

/*
============
SelectSpawnPoint

Returns the entity to spawn at
============
*/
entity() SelectSpawnPoint =
{
	local	entity spot;
	local	entity thing;
	local	float  pcount;
	
// testinfo_player_start is only found in regioned levels
	spot = find (world, classname, "testplayerstart");
	if (spot)
		return spot;
		
	
	if (deathmatch && self.pteam == 1) // if the game is deathmatch and your team is team 1. do stuff.
	{
		spot = lastspawn;
		while (1)
		{
			spot = find(spot, classname, "info_player_deathmatch"); // sets the classname "name of the mapping entitie" to this.
			if (spot != world)
			{
				if (spot == lastspawn)
					return lastspawn;
				pcount = 0;
				thing = findradius(spot.origin, 32);
				while(thing)
				{
					if (thing.classname == "player")
						pcount = pcount + 1;
					thing = thing.chain;
				}
				if (pcount == 0)
				{
					lastspawn = spot;
					return spot;
				}
			}
		}
	}

	else if (deathmatch && self.pteam == 2) // if the game type is deathmatch and your team is team 2. do stuff.
	{
		spot = lastspawn;
		while (1)
		{
			spot = find(spot, classname, "info_player_deathmatch2"); // new classname for team two's spawn point.
			if (spot != world)
			{
				if (spot == lastspawn)
					return lastspawn;
				pcount = 0;
				thing = findradius(spot.origin, 32);
				while(thing)
				{
					if (thing.classname == "player")
						pcount = pcount + 1;
					thing = thing.chain;
				}
				if (pcount == 0)
				{
					lastspawn = spot;
					return spot;
				}
			}
		}
	}

	else if (coop && self.pteam == 1) // if the gametype is coop and your team is team 1. do stuff. (we only have it set for team 1 for a reason.)
	{
		spot = lastspawn;
		while (1)
		{
			spot = find(spot, classname, "info_player_coop"); // same as the rest. uses an already existing classname.
			if (spot != world)
			{
				if (spot == lastspawn)
					return lastspawn;
				pcount = 0;
				thing = findradius(spot.origin, 32);
				while(thing)
				{
					if (thing.classname == "player")
						pcount = pcount + 1;
					thing = thing.chain;
				}
				if (pcount == 0)
				{
					lastspawn = spot;
					return spot;
				}
			}
		}
	}

	
	else if (!self.pteam) // else if you dont have a team.
	{
		spot = lastspawn;
		while (1)
		{
			spot = find(spot, classname, "info_player_observer"); // new classname for a mapping entitie.
			if (spot != world)
			{
				if (spot == lastspawn)
					return lastspawn;
				pcount = 0;
				thing = findradius(spot.origin, 32);
				while(thing)
				{
					if (thing.classname == "observer") // and your class name is OBSERVER  not player. (this is important)
						pcount = pcount + 1;
					thing = thing.chain;
				}
				if (pcount == 0)
				{
					lastspawn = spot;
					return spot;
				}
			}
		}
	}
	
	
	
	if (serverflags)
	{	// return with a rune to start
		spot = find (world, classname, "info_player_start2");
		if (spot)
			return spot;
	}
	
	spot = find (world, classname, "info_player_start");
	if (!spot)
		error ("PutClientInServer: no info_player_start on level");
	
	return spot;
};
most of that code is literally just copys of the existing code thats already there so i didnt take as much time commenting it.

our next step is in PutClientInServer so scroll down to there or do a search for it. should be right after what we just did.

edit it to look like so:

Code: Select all

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

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


void() PutClientInServer =
{
	if (!self.pteam) // if you dont have a team.
	{
		PutObserverInServer(); // spawn as an observer instead.
		return;
	}
	
	
	
	local	entity spot;

	spot = SelectSpawnPoint ();

	self.classname = "player";
	self.health = 100;
	self.takedamage = DAMAGE_AIM;
	self.solid = SOLID_SLIDEBOX;
	self.movetype = self.movetype;
	self.show_hostile = 0;
	self.max_health = 100;
	self.flags = FL_CLIENT;
	self.air_finished = time + 12;
	self.dmg = 2;   		// initial water damage
	self.super_damage_finished = 0;
	self.radsuit_finished = 0;
	self.invisible_finished = 0;
	self.invincible_finished = 0;
	self.effects = 0;
	self.invincible_time = 0;




	DecodeLevelParms ();
	
	W_SetCurrentAmmo ();

	self.attack_finished = time;
	self.th_pain = player_pain;
	self.th_die = PlayerDie;
	
	self.deadflag = DEAD_NO;
// paustime is set by teleporters to keep the player from moving a while
	self.pausetime = 0;
	
//	spot = SelectSpawnPoint ();

//	self.origin = spot.origin + '0 0 1';
	self.origin = self.oldorigin = spot.origin + '0 0 1';	// 1998-07-21 Respawning where player died fix by Robert Field
	self.angles = spot.angles;
	self.fixangle = TRUE;		// turn this way immediately

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

	setsize (self, VEC_HULL_MIN, VEC_HULL_MAX);
	
	self.view_ofs = '0 0 22';
	
	self.velocity = '0 0 0';	// 1998-07-21 Player moves after respawn fix by Xian

	player_stand1 ();
	
	if (deathmatch || coop)
	{
		makevectors(self.angles);
		spawn_tfog (self.origin + v_forward*20);
	}

	spawn_tdeath (self.origin, self);
};
next right below this function add two new functions like the other you see below it like this:

Code: Select all

void() info_player_deathmatch2 =
{
};

void() info_player_observer =
{
};


and our last stop in client.qc is in ClientConnect so go there now.

edit it like this:


















Code: Select all

/*
===========
ClientConnect

called when a player connects to a server
============
*/
void() ClientConnect =
{
	bprint (self.netname);
	bprint (" entered the game\n");
	if (deathmatch) // if the gametype is deathmatch, do this.
	{
		self.pteam = 0; // on connecting you start with out a team.
		self.menu = 1; // opens up your menu.
	}
	else if (coop) // else if the gametype is coop.
	{
		self.pteam = 1; // your team is automatically set to one, since its a coop game you only need 1 team.
		self.menu = 0; // makes sure the menu is off, since you dont need to select a team.
	}
	
	
// a client connecting during an intermission can cause problems
	if (intermission_running)
		ExitIntermission ();
};
OK! now we're all finished in client.qc lets move on to weapons.qc i promise this will be quicker. there's only 1 or 2 steps to this part.

once your in weapons.qc scroll down or search for impulse commands.

edit it to look like this:

Code: Select all

/*
============
ImpulseCommands

============
*/
void() ImpulseCommands =
{
	
   if (self.menu < 1) // if menu equals less than 1.
    {
        if (self.impulse >= 1 && self.impulse <= 8)
            W_ChangeWeapon ();

        if (self.impulse == 9)
            CheatCommand ();
        if (self.impulse == 10)
            CycleWeaponCommand ();
        if (self.impulse == 11)
            ServerflagsCommand ();
        if (self.impulse == 12)
            CycleWeaponReverseCommand ();

        if (self.impulse == 255)
            QuadCheat ();
		
	self.impulse = 0;
	}
	else // otherwise ...
	{
        SecondImpulse(); // call special impulse commands below
	}
};
its basically the same just slightly modified.

then add this right below that function:




Code: Select all

void() SecondImpulse =
// this is the second part of the menu select code its a special set of impulse commands. if you add on to the menu just simply add another impulse to the list.
{
	if (self.impulse == 1)
        Select(1); 
	if (self.impulse == 2)
        Select(2); 
};
ok almost finished 2 more steps left and your DONE!

next we'll make a new .qc file called "observer.qc". in this file put this:


Code: Select all

					/*Observer*/
//========================================================================================

void() PutObserverInServer =
{
	if (self.team >= 1) // if you have a team.
	{
		PutClientInServer();  // do this.
	}
	
		local	entity spot;

	spot = SelectSpawnPoint ();
	
	self.classname = "observer"; // sets the players classname to observer.
	self.health = 100;
	self.takedamage = DAMAGE_NO;
	self.solid = SOLID_NOT;
	self.movetype = MOVETYPE_NONE;
	self.show_hostile = 0;
	self.max_health = 100;
	self.flags = FL_CLIENT;
	self.air_finished = time + 12;
	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.pteam = 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.deadflag = DEAD_NO;
// paustime is set by teleporters to keep the player from moving a while
	self.pausetime = 0;
	
//	spot = SelectSpawnPoint ();

//	self.origin = spot.origin + '0 0 1';
	self.origin = self.oldorigin = spot.origin + '0 0 1';	// 1998-07-21 Respawning where player died fix by Robert Field
	self.angles = spot.angles;
	self.fixangle = TRUE;		// turn this way immediately

// oh, this is a hack!  these set the observers model to the "eye.mdl" model.
	setmodel (self, "progs/eyes.mdl");
	modelindex_eyes = self.modelindex;

	setmodel (self, "progs/eyes.mdl");
	modelindex_player = self.modelindex;

	setsize (self, VEC_HULL_MIN, VEC_HULL_MAX);
	
	self.view_ofs = '0 0 22';
	
	self.velocity = '0 0 0';	// 1998-07-21 Player moves after respawn fix by Xian

	player_stand1 ();
	
	if (deathmatch || coop)
	{
		makevectors(self.angles);
		spawn_tfog (self.origin + v_forward*20);
	}
};
this is basically just a modified putclientinserver. pretty simple.

ok and the last part is to add all the new .qc files in to the progs.src.

it should look like this:



Code: Select all

../progs.dat

defs.qc
moddefs.qc
subs.qc
fight.qc
ai.qc
combat.qc
items.qc
weapons.qc
world.qc
client.qc
observer.qc
player.qc
monsters.qc
doors.qc
buttons.qc
triggers.qc
plats.qc
misc.qc

ogre.qc
demon.qc
shambler.qc
knight.qc
soldier.qc
wizard.qc
dog.qc
zombie.qc
boss.qc

menu.qc

tarbaby.qc		// registered
hknight.qc		// registered
fish.qc			// registered
shalrath.qc		// registered
enforcer.qc		// registered
oldone.qc		// registered
there you go! your all finished! compile and try it out!

there's also a plus that all of this should work in vanilla quake.

also here's a player.mdl with two skins on it all ready setup to use!
Download

Merry Christmas! :P
Post Reply