The Place to Start


Part Two Capture the Object 
Gavin
Hard


Part Two Capture the Object
Editor: RED is new, YELLOW is old.
This is part 2 of capture the object. In this tutorial I will show you
how to make the capture point, set up the fgd, and show the player when 
they have the flag. The code for the capture point is real basic, and 
you could definatly improve it. Open up the object.h file we made in 
part 1 and at the bottom of the file put the following code:

class CObjectCapture : public CObject
{
public:
 void Spawn( void );
 void Precache( void );
 void Touch( CBaseEntity *pOther );
 void KeyValue( KeyValueData* );
 char TeamCapture[24];
 int  CapturePoints;
};

Those are all the functions and variables that will be in the capture
point. Now create a new file called object_capture.cpp, this will contain 
the code for the actual capture point. Include these files at the top:

#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"

Those are all the header files that we will be using for the capture
point. The first function will be the Spawn function.

void CObjectCapture::Spawn( void )
{
	Precache( );
	pev->movetype = MOVETYPE_TOSS;
	pev->solid = SOLID_TRIGGER;
	UTIL_SetOrigin( pev, pev->origin );
	UTIL_SetSize(pev, Vector(-64, -64, 0), Vector(64, 64, 64));
	SetTouch(Touch);
	if(CapturePoints == NULL)
	 CapturePoints = 5;
}

Notice I did not give the capture point a model, if you want it to have
a model just use

SET_MODEL(ENT(pev), "models/< model name >.mdl");

Be sure to put that after the Precache function is called, and also dont
forget to precache the model. The capture point has a movetype toss, so 
it will drop down to the brush right under it. Solid trigger is so that 
a player can walk right through it but still capture the flag. The size
is bigger than the flag, that way it is easier to capture the flag since
there is no model. NOTE: make sure that when mappers put the capture 
point in they do something to make the capture point stand out! The touch 
is Touch. CapturePoints is the variable we will be using to get the amount
of points a mapper set, if it is NULL (just in case) than a person should 
get 5 points. Next will be the Precache function. This will precache whatever
sounds/models/anything else you may want for the capture point.

void CObjectCapture::Precache( void )
{
PRECACHE_SOUND ("capture.wav");
}

Just replace capture.wav with whatever sound you want, or comment it out
if you dont want a sound. Now for the big part! The touch function, this 
will give the person that captured it their points put the flag back 
where it came from, and say that it was captured. 

void CObjectCapture::Touch( CBaseEntity *pOther )
{
CBasePlayer *pPlayer = (CBasePlayer *)pOther;

if (!pOther->IsPlayer())
 return;

if(!pPlayer->HasObject)
 return;

if (pPlayer->HasObject)
 {
 if(!FStrEq(pPlayer->m_szTeamName, TeamCapture))
  return;
 char text [201];
 sprintf( text, "%s team captured the object!\n", TeamCapture);
 UTIL_ClientPrintAll( HUD_PRINTCENTER, text );
 pPlayer->HasObject = FALSE;
 pPlayer->AddPoints(CapturePoints,FALSE);
 EMIT_SOUND_DYN( ENT(pev), CHAN_STATIC, "capture.wav", 1, ATTN_NONE, 0,
100 );
 ReturnObject();
 return;
 }
}

If it was touched by something other than a player, return. If the
person that touched it does not have the object, than return so you 
dont execute the rest of the code. If for some reason (I have no 
idea how) a person did not have the object, I check to see if they 
have the object again. If they do have it check to see if the persons 
team name matches the capture point for their team (that will be 
later in the tutorial) if it does than print to everyone that that 
team captured the object. Make it so that the player no longer has 
the object, and give them points. Everyone will hear the sound that 
the object was captured, comment out this line if you do not want 
a sound saying that the object was captured. Return the object to 
where the mapper put it. Now to get two capture points, one for 
each team. This took some thinking to find out how without retyping 
the whole capture point over.

class CRedCapture : public CObjectCapture
{
 void Spawn( void )
 {
 strcpy( TeamCapture, "Red" );
 CObjectCapture::Spawn( );
 }
 void KeyValue( KeyValueData* Data )
 {
 if( FStrEq( Data->szKeyName, "CapturePoints" ) )
        {
        CapturePoints = atoi( Data->szValue );
        Data->fHandled = TRUE;
        }
 }
}
};
LINK_ENTITY_TO_CLASS( object_red_capture, CRedCapture );

class CBlueCapture : public CObjectCapture
{
 void Spawn( void )
 {
 strcpy( TeamCapture, "Blue" );
 CObjectCapture::Spawn( );
 }
 void KeyValue( KeyValueData* Data )
 {
 if( FStrEq( Data->szKeyName, "CapturePoints" ) )
        {
        CapturePoints = atoi( Data->szValue );
        Data->fHandled = TRUE;
        }
 }
};
LINK_ENTITY_TO_CLASS( object_blue_capture, CBlueCapture );

The first one spawns a capture point, but it copys the team Red into the
team capture variable which was used in the touch function to check what 
team the player was on. It links the entity object_red_capture to the red 
capture point. The blue capture point is almost the same except that it 
copies Blue as the team that will capture it and links object_blue_capture 
to CBlueCapture. Both of those also have a KeyValue function that gets the 
amount of points the mapper set for a capture. Now for the fgd file. Add 
these lines somewhere in your mods fgd file 

@PointClass = object_blue_capture : "Blue Capture Point"
[
 CapturePoints(integer) : "Capture points" : 5
]
@PointClass = object_red_capture : "Red Capture Point"
[
 CapturePoints(integer) : "Capture points" : 5
]
@PointClass = object_world : "Object" []

Those are all the entities wer made, the blue and red capture points and
the object. Now for the last part, having a sprite be displayed when a 
person has the flag. Open up you client.dll project and make a new file 
called object_sprite.cpp, this file will show a sprite on the persons 
hud when they have the object. First will be the include files:

#include "hud.h"
#include "clutil.h"
#include < string.h > // remove spaces
#include < time.h > // remove spaces
#include < stdio.h > // remove spaces
#include "parsemsg.h"

And the declared message

DECLARE_MESSAGE(m_HasObject, ObjectSprite )

If you want to know what more of this means see 2s tutorial on
Wavelegnth. Next would be the init function, this is like the spawn
function for the client dll.

int CObjectSprite::Init(void)
{
HOOK_MESSAGE( ObjectSprite );

m_iFlags |= HUD_ACTIVE;
gHUD.AddHudElem(this);
return 1;
};

That adds the hud element and and makes it so that it can be seen. Next
is the vid init function. This is going to hold the name of the sprite 
and its size.

int CObjectSprite::VidInit(void)
{
m_hSprite = 0;
int HUD_ObjectSprite = gHUD.GetSpriteIndex( "hasobject" );
SpriteArea = &gHUD.GetSpriteRect(HUD_ObjectSprite);
return 1;
};

That has the size of the sprite and its index. I am not very good with
sprites on the hud :(, but this all works. Now for the draw function, 
this could be considered the touch function for the client dll.

int CObjectSprite::Draw(float fTime)
{
if ( gHUD.m_iHideHUDDisplay & ( HIDEHUD_ALL ) )
 return 1;

if(Icon == 1)
 {
 m_hSprite = LoadSprite("sprites/hasobject.spr");
 SPR_Set( m_hSprite, 64, 64, 64 );
 SPR_DrawAdditive( 0, ScreenWidth / 4, ScreenHeight / 4, SurvFlagArea );

 }
else if(Icon == 2)
 {
 m_hSprite = 0;
 m_iFlags |= ~HUD_ACTIVE;
 }
return 1;
}

That checks to see what number the server dll sent over, if it was 1
then show it, if it was 2 then hide the sprite. Replace sprites/hasobject.spr 
with the sprite that you want displayed. Next function gets the values
that the server dll sent over and puts it into the team variable.

int CObjectSprite::MsgFunc_ObjectSprite(const char *pszName, int iSize,
void *pbuf)
{
BEGIN_READ( pbuf, iSize );
Icon = READ_BYTE();
m_iFlags |= HUD_ACTIVE;
return 1;
}

You can close up that file now. Open up hud.cpp in your client dll
project and go down to the line that says:

m_StatusIcons.Init();

Right under that put

m_HasObject.Init();

Still in hud.cpp go down to the line

m_StatusIcons.VidInit();

Under that put

m_HasObject.VidInit();

We are done in hud.cpp but we still need to declare all the functions
and variables. So open up hud.h and put these lines somewhere in it.

class CObjectSprite : public CHudBase
{
public:
 int Init( void );
 int VidInit( void );
 int Draw(float flTime);
 int MsgFunc_ObjectSprite(const char *pszName, int iSize, void *pbuf);
 int Icon;
private:
 wrect_t *SpriteArea;
 HSPRITE m_hSprite;
};

Those are all the functions and variables that were used for the sprite
to show up. Go down to

CHudStatusIcons m_StatusIcons;

And right under that put

CObjectSprite m_HasObject;

We are now done editing the client.dll project. That will let us show
the sprite on their screen. Now to actually send the message that it 
should show up. Open up the mp.dll project. Go into player.cpp go down
to the line
 
int gmsgStatusValue = 0;

Under that put

int gmsgShowSprite = 0;

Find the line in player.cpp

gmsgStatusValue = REG_USER_MSG("StatusValue", 3);

and under that put

gmsgShowSprite = REG_USER_MSG("ObjectSprite", 1);

That just says that the gmsgShowSprite is linked to the message
ObjectSprite that we declared in the client dll. Go to the bottom 
of the PreThink function in player.cpp and put the lines

if (HasObject)
 {
 MESSAGE_BEGIN( MSG_ONE, gmsgShowSprite, NULL, pev );
  WRITE_BYTE( 1 );
 MESSAGE_END();
 }
else
 {
 MESSAGE_BEGIN( MSG_ONE, gmsgShowSprite, NULL, pev );
  WRITE_BYTE( 2 );
 MESSAGE_END();
 }

If you have the object then it triggers the sprite to show on the hud by
sending the message to the client. If you do not have the sprite then it 
hides the sprite from the hud. Now finally the long tutorial is over. 
You should be bale to make an object that you can pick up, pick it up and
capture it for points, and have mappers put it in their maps, and lastly
put a sprite on the hud showing when they have the flag! All that code was 
just a base for making that happen and can definatly be improved. Good 
luck in making that capture point work :)

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