The Place to Start


Part one of Capture the Object 
Gavin
Hard


Part one of Capture the Object
Editor: RED is new, YELLOW is old.
This tutorial will show you how to make an entity that someone is supposed to 
get and take to a capture point. First create a header file called object.h in 
it put the following:

//
// object.h
//

class CObject : public CBaseEntity
{
public:
 virtual void Spawn( void );
 virtual void Precache( void );
 virtual void ReturnObject( void );
 virtual void Materialize( void );
 virtual void Touch( CBaseEntity *pOther );
 virtual BOOL MyTouch( CBasePlayer *pPlayer );
 virtual void Think( void );
 BOOL HasObject;
 BOOL FallenObject;
};

Those are all the functions that the object will have. Now create the file 
object.cpp, this is going to be the actually object that you can pick up and 
put on the capture point. First put:

#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "weapons.h"
#include "player.h"
#include "soundent.h"
#include "gamerules.h"
#include "animation.h"
#include "object.h"

extern int gmsgItemPickup;

enum Object_Animations
 {
 IDLE = 1
 };


at the top of the file. There are the header files at the top, including
the the new object.h header file we just made. Extern int gmsgItemPickUp is 
for when you touch the flag and pick it up. Object_Animations is the enum 
that will hold the object animations. Next will be the spawn.

void CObject::Spawn ( void )
{
Precache( );
SET_MODEL(ENT(pev), "models/can.mdl");

pev->movetype = MOVETYPE_TOSS;
pev->solid = SOLID_TRIGGER;
UTIL_SetOrigin( pev, pev->origin );
UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 16));

SetThink( Think );
SetTouch( Touch );

pev->sequence = IDLE;
pev->framerate = 1.0;

pev->nextthink = gpGlobals->time + 0.1;
}

It calls the Precache() function to precache all the models and anything else 
you need before you spawn it. The model is just a can, change that to whatever 
you want to capture like a flag. It has a movetype toss so it will fall to a 
brush below it. SOLID_TRIGGER is so you can trigger it by walking through it. 
It sets the think to Think and the touch to Touch, those functions will be created 
later in this tutorial. The sequence right now is IDLE. Next is the Precache 
function.

void CObject::Precache( )
{
PRECACHE_MODEL( "models/can.mdl" );
}

that should be self explanatory. Materialize is the function that will make it 
visible and operable again after we change some of the things around in it.


void CObject::Materialize( void )
{
if ( pev->effects & EF_NODRAW )
 {
 pev->effects &= ~EF_NODRAW;
 pev->effects |= EF_MUZZLEFLASH;
 }
SetTouch( Touch );
SetThink( Think );
}

It makes it visible and sets the think and touch back again. Now for the touch 
function, this does not handle all the picking up information though.


void CObject::Touch( CBaseEntity *pOther )
{
if ( !pOther->IsPlayer() )
 return;

CBasePlayer *pPlayer = (CBasePlayer *)pOther;
if (MyTouch( pPlayer ))
 {
 SUB_UseTargets( pOther, USE_TOGGLE, 0 );
 SetTouch( NULL );
 SetThink( NULL );
 pev->effects |= EF_NODRAW;
 }
}


It calls the MyTouch function to see if we actually did pick up the object. 
If it was picked up this sets the think and touch to NULL and makes it invisible.
Now for the big one! MyTouch handles the picking up of the object. 
*BigGuy: Sorry guys, I goofed on this one the first time.


BOOL CObject::MyTouch( CBasePlayer *pPlayer )
{
char text[256];
CBaseEntity *pEnt;

if ( ( pPlayer->pev->weapons & (1<< WEAPON_SUIT) ) )
 {
 if(pPlayer->HasObject)
  return FALSE;
 pPlayer->HasObject = TRUE;
 CBaseEntity *pEnt = CBaseEntity::Create( "object_follow", pev->origin,
pev->angles, pPlayer->edict() );
 CObjectFollow *pFollow = (CObjectFollow *)pEnt;
 pFollow->Owner = pPlayer;

 if(FStrEq(pPlayer->m_szTeamName, "Red"))
  {
  sprintf( text, "Red team has the object!\n");
  UTIL_ClientPrintAll( HUD_PRINTCENTER, text );
  }
 else if(FStrEq(pPlayer->m_szTeamName, "Blue"))
  {
  sprintf( text, "Blue team has the object!\n");
  UTIL_ClientPrintAll( HUD_PRINTCENTER, text );
  }

 MESSAGE_BEGIN( MSG_ONE, gmsgItemPickup, NULL, pPlayer->pev );
  WRITE_STRING( STRING(pev->classname) );
 MESSAGE_END();
 if(FallenObject)
  UTIL_Remove( this );
 return TRUE;
 }
return FALSE;
}


This is the big thing for the flag, it lets you pick it up. First it checks 
to see if you have the suit. If you already has the object then you cannot 
pick it up again. If you dont have the object it gives you the object. 
It creates the entity that actually follows you, that will be later in this 
tutorial. It sets who the object is supposed to follow, the player that 
picked it up. Next it checks to see what team you are on, just replace 
Red and Blue with whatever your team names are. It then says what team 
picked up the object, either red or blue. It then picks up the object
and says it did by sending a message to the client. If it was fallen
from a person that died or other, then it removes it so there wont be 
more than one object in the world at a time. Return TRUE just says that 
yes, you did pick up the object. The think function is just for animating.


void CObject::Think( void )
{
pev->sequence = IDLE;
pev->nextthink = gpGlobals->time + 0.1;
}

That just sets the sequence to play to IDLE. The last function is the
one that after you capture the flag it will make it be visible and you 
can pick it up again, where the mapper put it.

void CObject::ReturnObject( )
{
edict_t *pFind;
CBaseEntity *pEnt;

pFind = FIND_ENTITY_BY_CLASSNAME( NULL, "object_world" );

while ( !FNullEnt( pFind ) )
 {
 CBaseEntity *pEnt = CBaseEntity::Instance( pFind );
 CObject *pObject = (CObject *)pEnt;
 pObject->Materialize( );
 pFind = FIND_ENTITY_BY_CLASSNAME( pFind, "object_world" );
 }
}


It does a search for the object entity then materializes it there, and
does that for every object that the mapper put in the map. The next thing 
will be to link the actual entity to the class.

LINK_ENTITY_TO_CLASS( object_world, CObject );

That says that object_world (the entity that the mappers will put in the
map) comes from the CObject class. The object that you pick up is almost 
done. Open up player.h and right above:

int m_iPlayerSound;

Put

BOOL HasObject;

Now it should compile with no errors. But it will crash in game when you
try to pick up the flag because the object_follow will not be found. Now 
onto making the entity that will follow the person Go into object.h and 
underneath the CObject stuff, we are going to put the CObjectFollow stuff.

class CObjectFollow : public CObject
{
public:
 virtual void Spawn( void );
 virtual void Precache( void );
 virtual void Think( void );
 CBasePlayer *Owner;
};

The following object is alot simpler than the actual one in the world,
because all it is a model that animates and follows the owner. So in object.cpp 
under everything we just did put:

void CObjectFollow::Spawn( )
{
Precache( );
UTIL_SetOrigin( pev, pev->origin );
pev->movetype = MOVETYPE_NONE;
pev->solid = SOLID_NOT;
pev->effects |= EF_NODRAW;
pev->sequence = IDLE;
pev->framerate = 1.0;
SET_MODEL(ENT(pev), "models/can.mdl");
SetThink( Think );
pev->nextthink = gpGlobals->time + 0.1;
}

That will spawn it, pretty self explanatory. There is another precache
function.

void CObjectFollow::Precache( )
{
PRECACHE_MODEL ("models/can.mdl");
}

That precaches the can model, you could call the one in CObject, but I
wanted to make a new one to give more freedom to my code. The last function 
here is the Think.

void CObjectFollow::Think( )
{
pev->effects &= ~EF_NODRAW;
pev->aiment = ENT(Owner->pev);
pev->movetype = MOVETYPE_FOLLOW;

if (!Owner->IsAlive())
 UTIL_Remove( this );

if ( !Owner->HasObject)
 UTIL_Remove( this );
else
 {
 pev->sequence = IDLE;
 pev->nextthink = gpGlobals->time + 0.1;
 }
}

If the owner is not alive or no longer has the flag then it is removed
from them. If it should still be on them then just animate it. Lastly 
link the entity to the class.

LINK_ENTITY_TO_CLASS( object_follow, CObjectFollow );

Now compile and it should work. Except that when the person that has the
flag dies then there is no more flag, that would definatly ruin the gameplay! 
So go into player.cpp and include the object.h file. Go down to PackDeadPlayerItems(), 
near the end of the function but before RemoveAllItems( TRUE ); put the lines:

if(HasObject)
 {
 char szText [201];
 CBaseEntity *pEnt;

 if(FStrEq(m_szTeamName, "Red"))
  sprintf(szText, "Red lost the object!");
 else if(FStrEq(m_szTeamName, "Blue"))
  sprintf(szText, "Blue lost the object!");
 UTIL_ClientPrintAll( HUD_PRINTCENTER, szText );
 pEnt = CBaseEntity::Create( "object_world", pev->origin,pev->angles,
edict() );
 pEnt->pev->velocity = pev->velocity * 1.2;
 pEnt->pev->angles.x = 0;
 CObject *pObject = (CObject *)pEnt;
 pObject->FallenObject = TRUE;
 HasObject = FALSE;
 }

That makes it so if you die there is a flag in the world the people can
pick up. It also says that it is fallen so that there will not be multiple 
flags in the world. That just says what team dropped the object, creates 
on in the world, and says that it is fallen. The next part will include the
capture point, so for now just run around seeing who can get the flag :)

Any questions or suggestions should be sent to me: british@epix.net