NOTE: This code will need modifying to suit
your team system,ect so don't take this as a complete C&P (which it
basically is).
#1 - Creating Flag entityIn a new header file (ctf_gameplay.h), put this :
// Status of the flag
#define FLAG_STOLEN 1
#define FLAG_CAPTURE 2
#define FLAG_DROPPED 3
// Our flag class which holds all our functions and variables we're going to use
class CObjectFlag : public CBaseEntity
{
public:
void Spawn( );
void Precache( );
void Touch(CBaseEntity *);
bool m_fIsInPlay;
};
// The Flag entity that is dropped by a player when killed/disconnected/whatnot
class CDroppedFlag : public CBaseEntity
{
public:
void Spawn( );
void Precache( );
void Touch(CBaseEntity *);
};
In player.h put the code below in class CBasePlayer : public CBaseMonster in public :
In a new .cpp file (ctf_gameplay.cpp), put the following :
// includes needed for everything to compile
#include "extdll.h"
#include "decals.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "weapons.h"
#include "nodes.h"
#include "player.h"
#include "soundent.h"
#include "shake.h"
#include "gamerules.h"
#include "ctf_object.h"
#include "teamplay_gamerules.h"
LINK_ENTITY_TO_CLASS( object_flag, CObjectFlag );
void CObjectFlag :: Spawn( )
{
// Calls the precache function to precache any models, sounds,ect you need
//before it spawns
Precache( );
// Setup what model we want to use for the flag, change this to
// whatever you want. Just remember precache it.
SET_MODEL( ENT(pev), "models/flag.mdl" );
UTIL_SetOrigin( pev, pev->origin + Vector(0,0,10) );
UTIL_SetSize( pev, Vector(-16,-16,0), Vector(16,16,16) );
// This is so it falls to the brush below it
pev->movetype = MOVETYPE_TOSS;
// This allows you to trigger it by walking through it
pev->solid = SOLID_TRIGGER;
// This just sets our Touch to Touch, which is down a little further
SetTouch(Touch);
// This just makes a GlowShell around the model, you can
// change the colour of the shell by changing the RGB values,
// which are the XYZ values in this case. So at the moment we have
// a nice bright red colour. Feel free to change this to your needs.
pev->renderfx = kRenderFxGlowShell;
pev->rendercolor.x = 255;
pev->rendercolor.y = 0;
pev->rendercolor.z = 0;
pev->renderamt = 10; // 10 units off the sides of the model
// This just sets our IsInPlay bool to false, because its only just spawned and
// no one has picked it up yet.
m_fIsInPlay = false;
}
// This is the Precache function which is called from the Spawn function.
void CObjectFlag :: Precache( )
{
// Precache's the model
PRECACHE_MODEL( "models/flag.mdl" );
}
// This is the Touch function which was set above, in the Spawn function,
// its basically the stuff that runs when the flag has been touched by a player
void CObjectFlag :: Touch(CBaseEntity *pOther)
{
// If its in play, do nothing
if(m_fIsInPlay)
return;
// Determine if the object that touches it, is a player
// and check if the player is alive.
if(!pOther)
return;
if(!pOther->IsPlayer())
return;
if(!pOther->IsAlive())
return;
CBasePlayer *pPlayer = (CBasePlayer *)pOther;
// Print to the HUD who has taken the flag
UTIL_ClientPrintAll( HUD_PRINTCENTER, UTIL_VarArgs( "%s has the Flag!\n", STRING( pPlayer->pev->netname )));
pPlayer->m_fHasObject = true;
// Set the client attachment using an event
PLAYBACK_EVENT_FULL(0, pPlayer->edict(), g_usObject, 0, (float *)&g_vecZero, (float *)&g_vecZero, 0, 0, FLAG_STOLEN, 0, 0, 0);
m_fIsInPlay = true;
// Give the illusion of the player taking the model by not drawing it
pev->effects = EF_NODRAW;
// Now don't let it accept any more touch's
SetTouch(NULL);
}
// This is the Flag that is dropped by a player when killed,disconnected,ect.
// Its basically the same as CObjectFlag, with a few changes.
LINK_ENTITY_TO_CLASS( dropped_flag, CDroppedFlag );
void CDroppedFlag :: Spawn( )
{
// Calls the precache function to precache any models or sounds,ect
// you need before it spawns.
Precache( );
// Setup what model we want to use for the flag, change this to
// whatever you want. Just remember precache it.
SET_MODEL( ENT(pev), "models/flag.mdl" );
UTIL_SetOrigin( pev, pev->origin );
UTIL_SetSize( pev, Vector(-16,-16,0), Vector(16,16,16) );
// This is so it falls to the brush below it
pev->movetype = MOVETYPE_TOSS;
// This allows you to trigger it by walking through it
pev->solid = SOLID_TRIGGER;
// This just sets our Touch to Touch, which is down a little further
SetTouch(Touch);
// This just makes a GlowShell around the model, you can
// change the colour of the shell by changing the RGB values,
// which are the XYZ values in this case. So at the moment we have
// a nice bright red colour. Feel free to change this to your needs.
pev->effects = EF_BRIGHTFIELD;
pev->renderfx = kRenderFxGlowShell;
pev->rendercolor.x = 255;
pev->rendercolor.y = 0;
pev->rendercolor.z = 0;
pev->renderamt = 10; // 10 units off the sides of the model
pev->avelocity.z = 25;
}
// This is the Precache function which is called from the Spawn function.
void CDroppedFlag :: Precache( )
{
// Precache's the model
PRECACHE_MODEL( "models/flag.mdl" );
}
// This is the Touch function which was set above, in the Spawn function,
// its basically the stuff that runs when the flag has been touched by a player.
void CDroppedFlag :: Touch(CBaseEntity *pOther)
{
// Determine if the object that touches it, is a player
// and check if the player is alive.
if(!pOther)
return;
if(!pOther->IsPlayer())
return;
if(!pOther->IsAlive())
return;
CBasePlayer *pPlayer = (CBasePlayer *)pOther;
// Print to the HUD who has taken the flag
UTIL_ClientPrintAll( HUD_PRINTCENTER, UTIL_VarArgs( "%s has the Flag!\n", STRING( pPlayer->pev->netname )));
// Set it to true because the player has the flag
pPlayer->m_fHasObject = true;
// Set the client attachment using an event
PLAYBACK_EVENT_FULL(0, pPlayer->edict(), g_usObject, 0, (float *)&g_vecZero, (float *)&g_vecZero, 0, 0, FLAG_STOLEN, 0, 0, 0);
// remove this item
SetThink(SUB_Remove);
// next think time to now!
pev->nextthink = gpGlobals->time;
}
In teamplay_gamerules in void CHalfLifeTeamplay :: PlayerKilled, put the following :
// If they have the flag, drop it.
if( pVictim->m_fHasObject )
{
// Print to HUD who has dropped the flag
UTIL_ClientPrintAll( HUD_PRINTNOTIFY, UTIL_VarArgs( "%s has dropped the Flag!\n", STRING( pPlayer->pev->netname )));
// Creates the flag entity in the direction the player is facing
CDroppedFlag *pFlag = (CDroppedFlag *)CBaseEntity::Create("dropped_flag", pVictim->pev->origin, pVictim->pev->angles);
// Player no longer has flag so set the bool to false
pVictim->m_fHasObject = false;
// Update client with what has happened
PLAYBACK_EVENT_FULL(0, pVictim->edict(), g_usObject, 0, (float *)&g_vecZero, (float *)&g_vecZero, 0, 0, FLAG_DROPPED, 0, 0, 0);
}
multiplay_gamerules.cpp, CHalfLifeMultiplay :: ClientDisconnected( edict_t *pClient ) before this :
if ( pClient )
{
CBasePlayer *pPlayer = (CBasePlayer *)CBaseEntity::Instance( pClient );
Put this :
// If they have the flag, drop it
if( pPlayer->m_fHasObject )
{
// Print to HUD who has dropped the flag
UTIL_ClientPrintAll( HUD_PRINTNOTIFY, UTIL_VarArgs( "%s has dropped the Flag!\n", STRING( pPlayer->pev->netname )));
// Creates the flag entity in the direction the player is facing
CDroppedFlag *pFlag = (CDroppedFlag *)CBaseEntity::Create("dropped_flag", pPlayer->pev->origin, pPlayer->pev->angles);
// Player no longer has flag so set the bool to false
pPlayer->m_fHasObject = false;
// Update client with what has happened
PLAYBACK_EVENT_FULL(0, pPlayer->edict(), g_usObject, 0, (float *)&g_vecZero, (float *)&g_vecZero, 0, 0, FLAG_DROPPED, 0, 0, 0);
}
Now in weapons.cpp find, DLL_GLOBAL short g_sModelIndexBloodSpray; and put the following after it:
DLL_GLOBAL unsigned short g_usObject; // object capture/taken event
Now find g_sModelIndexBloodSpray = PRECACHE_MODEL ("sprites/bloodspray.spr");
and put the following after it:
g_usObject = PRECACHE_EVENT(1, "events/object.sc");
Now in weapons.h fine, extern DLL_GLOBAL short g_sModelIndexBloodSpray; and put the following after it:
extern DLL_GLOBAL unsigned short g_usObject; // object capture/stolen event
#2 - Creating Capture Point EntityOpen "ctf_gameplay.h" and put at the bottom :
// Capture point for Team 1
class CCaptureTeam1 : public CBaseEntity
{
public:
void Spawn( );
void Precache( );
void EXPORT Touch(CBaseEntity *);
void EXPORT Think( );
void KeyValue( KeyValueData* );
};
// Capture point for Team 2
class CCaptureTeam2 : public CBaseEntity
{
public:
void Spawn( );
void Precache( );
void EXPORT Touch( CBaseEntity *);
void EXPORT Think( );
void KeyValue( KeyValueData* );
};
In player.h, put the following in class CBasePlayer : public CBaseMonster
in the publics under bool m_fHasObject;
In the bottom of ctf_gameplay.cpp, put this :
LINK_ENTITY_TO_CLASS( capture_team1, CCaptureTeam1 );
void CCaptureTeam1 :: Spawn( )
{
// Calls the precache function to precache any models, sounds,ect
// you need before it spawns
Precache( );
SET_MODEL( ENT(pev), STRING(pev->model) );
UTIL_SetOrigin( pev, pev->origin );
pev->movetype = MOVETYPE_FLY;
// This allows you to trigger it by walking through it
pev->solid = SOLID_TRIGGER;
// This just sets our Touch to Touch and our Think to Think
// which is down a little further.
SetTouch(Touch);
SetThink(Think);
// Sets next think time
pev->nextthink = gpGlobals->time + 0.1;
// Make the entity invisible
pev->rendermode = kRenderTransTexture;
pev->renderamt = 0;
}
// This is the Precache function which is called from the Spawn function.
void CCaptureTeam1 :: Precache( )
{
PRECACHE_MODEL( (char*)STRING(pev->model) );
}
void CCaptureTeam1 :: Think( )
{
// loop through every player and check if they are in the area
for(int i=0; i<gpGlobals->maxClients; i++)
{
CBasePlayer *pPlayer = (CBasePlayer *)UTIL_PlayerByIndex(i);
if(pPlayer && pPlayer->m_iTeam == 1)
{
if((pPlayer->pev->origin.x >= pev->mins.x) && (pPlayer->pev->origin.x <= pev->maxs.x) &&
(pPlayer->pev->origin.y >= pev->mins.y) && (pPlayer->pev->origin.y <= pev->maxs.y) &&
(pPlayer->pev->origin.z >= pev->mins.z) && (pPlayer->pev->origin.z <= pev->maxs.z))
pPlayer->m_bInCapture = true;
else
pPlayer->m_bInCapture = false;
}
}
// Set next think time
pev->nextthink = gpGlobals->time + 0.1;
}
// This is the Touch function which was set above, in the Spawn function,
// its basically the stuff that runs when the capture point has been touched by a player
void CCaptureTeam1 :: Touch( CBaseEntity* pOther )
{
// Determine if the object that touches it, is a player
// and check if the player is alive.
if(!pOther)
return;
if(!pOther->IsPlayer())
return;
if(!pOther->IsAlive())
return;
CBasePlayer *pPlayer = (CBasePlayer *)pOther;
if(pPlayer && pPlayer->m_iTeam == 1)
{
// Check to see if they have the object
if( !pPlayer->m_fHasObject )
return;
else
{
// Print to HUD who has captured the flag
UTIL_ClientPrintAll( HUD_PRINTCENTER, UTIL_VarArgs
( "%s has captured the Flag!\n", STRING( pPlayer->pev->netname )));
// Remove the flag
pPlayer->m_fHasObject = false;
PLAYBACK_EVENT_FULL(0, pPlayer->edict(), g_usObject, 0, (float *)&g_vecZero,
(float *)&g_vecZero, 0, 0, FLAG_CAPTURE, 0, 0, 0);
// Reset the flag
CObjectFlag *pFlag = (CObjectFlag *)UTIL_FindEntityByClassname(NULL, "object_flag");
if(pFlag)
{
pFlag->m_fIsInPlay = false;
// Do a funky effect on the flag when it gets reset
pFlag->pev->effects = EF_BRIGHTFIELD;
}
}
}
else
return; // wrong team
}
void CCaptureTeam1 :: KeyValue( KeyValueData* Data )
{
// This will store the points but its not fully implemented. I'll leave it for you to do.
if( FStrEq( Data->szKeyName, "points" ) )
{
int m_iPoints = atoi(Data->szValue);
Data->fHandled = true;
}
CBaseEntity::KeyValue(Data); // call the parent function
}
LINK_ENTITY_TO_CLASS( capture_team2, CCaptureTeam2 );
void CCaptureTeam2 :: Spawn( )
{
// Calls the precache function to precache any models, sounds,ect
// you need before it spawns.
Precache( );
SET_MODEL( ENT(pev), STRING(pev->model) );
UTIL_SetOrigin( pev, pev->origin );
pev->movetype = MOVETYPE_FLY;
// This allows you to trigger it by walking through it
pev->solid = SOLID_TRIGGER;
// This just sets our Touch to Touch and our Think to Think
// which is down a little further.
SetTouch(Touch);
SetThink(Think);
// Set next think time
pev->nextthink = gpGlobals->time + 0.1;
// Make the entity invisible
pev->rendermode = kRenderTransTexture;
pev->renderamt = 0;
}
void CCaptureTeam2 :: Precache( )
{
PRECACHE_MODEL( (char*)STRING(pev->model) );
}
void CCaptureTeam2 :: Think( )
{
// loop through every player and check if they are in the area
for(int i=0; i<gpGlobals->maxClients; i++)
{
CBasePlayer *pPlayer = (CBasePlayer *)UTIL_PlayerByIndex(i);
if(pPlayer && pPlayer->m_iTeam == 2)
{
if((pPlayer->pev->origin.x >= pev->mins.x) && (pPlayer->pev->origin.x <= pev->maxs.x) &&
(pPlayer->pev->origin.y >= pev->mins.y) && (pPlayer->pev->origin.y <= pev->maxs.y) &&
(pPlayer->pev->origin.z >= pev->mins.z) && (pPlayer->pev->origin.z <= pev->maxs.z))
pPlayer->m_bInCapture = true;
else
pPlayer->m_bInCapture = false;
}
}
// Set the next think time
pev->nextthink = gpGlobals->time + 0.1;
}
void CCaptureTeam2 :: Touch( CBaseEntity* pOther )
{
// Determine if the object that touches it, is a player
// and check if the player is alive.
if(!pOther)
return;
if(!pOther->IsPlayer())
return;
if(!pOther->IsAlive())
return;
CBasePlayer *pPlayer = (CBasePlayer *)pOther;
if(pPlayer && pPlayer->m_iTeam == 2)
{
// Check to see if they have the object
if( !pPlayer->m_fHasObject )
return;
else
{
// Print to HUD who has captured the flag
UTIL_ClientPrintAll( HUD_PRINTCENTER, UTIL_VarArgs
( "%s has captured the Flag!\n", STRING( pPlayer->pev->netname )));
// Remove the object
pPlayer->m_fHasObject = false;
PLAYBACK_EVENT_FULL(0, pPlayer->edict(), g_usObject, 0, (float *)&g_vecZero,
(float *)&g_vecZero, 0, 0, FLAG_CAPTURE, 0, 0, 0);
// Reset the object
CObjectFlag *pFlag = (CObjectFlag *)UTIL_FindEntityByClassname(NULL, "object_flag");
if(pFlag)
{
pFlag->m_fIsInPlay = false;
// Do a funky effect on the flag when it gets reset
pFlag->pev->effects = EF_BRIGHTFIELD;
}
}
}
else
return; // Wrong team
}
void CCaptureTeam2 :: KeyValue( KeyValueData* Data )
{
// This will store the points but its not fully implemented. I'll leave it for you to do.
if( FStrEq( Data->szKeyName, "points" ) )
{
int m_iPoints = atoi(Data->szValue);
Data->fHandled = true;
}
CBaseEntity::KeyValue(Data); // call the parent function
}
#3 - Coding Client Side and FGDOpen up hl_events.cpp and find the following:
void EV_TripmineFire( struct event_args_s *args );
and put this after it :
void EV_Flag( struct event_args_s *args );
Next, in the same file, find:
gEngfuncs.pfnHookEvent( "events/tripfire.sc", EV_TripmineFire );
and after it put this :
gEngfuncs.pfnHookEvent( "events/object.sc", EV_Flag );
Next, in ev_hldm.cpp, find:
void EV_TripmineFire( struct event_args_s *args );
and at the end of that function put this :
void EV_Flag(struct event_args_s *args );
Same file, ev_hldm.cpp, find:
void EV_TripmineFire( event_args_t *args )
and at the end of that function put this :
#define FLAG_STOLEN 1
#define FLAG_CAPTURE 2
#define FLAG_DROPPED 3
void EV_Flag( event_args_t *args )
{
� �int idx = args->entindex;
� �int type = args->iparam1;
� �// Feel free to change the model that is attached to the player
� �int FlagModel = gEngfuncs.pEventAPI->EV_FindModelIndex("models/flag.mdl");
� �switch( type )
� �{
� �case FLAG_STOLEN:
� � � � � �// Attach model to player
� � � � � �gEngfuncs.pEfxAPI->R_AttachTentToPlayer( idx, FlagModel, 52, 9999); // The 52 is the z offset from
� � � � � �the player and the 9999 is how long you want it to be attached for
� � � � � �// Add more stuff here if you like
� � � � � �break;
� �case FLAG_CAPTURE:
� � � � � �// Add stuff here if you like
� �case FLAG_DROPPED:
� � � � � �// Add more stuff here if you like
� � � � � �// This gets rid of the attached model
� � � � � �gEngfuncs.pEfxAPI->R_KillAttachedTents(idx);
� � � � � �break;
� �}
}
Code to add at the bottom of your FGD :
@PointClass = object_flag: "The Flag" []
@SolidClass base(Targetx) = capture_team1: "Where Team 1 brings the Flag"
[
m_iPoints(integer) : "Points" : 1
]
@SolidClass base(Targetx) = capture_team2: "Where Team 2 brings the Flag"
[
m_iPoints(integer) : "Points" : 1
]
Tutorial by DarkNight :
#1 - Creating Flag Entity Original Version : http://articles.thewavelength.net/203/
#2 - Creating Capture Point Entity Original Version : http://articles.thewavelength.net/205/
#3 - Coding in Client Side and FGD Original Version : http://articles.thewavelength.net/206/Enjoy!