ARRANGEMENT forums
  • Home
  • Help
  • Search
  • Login
  • Register
 
Welcome, Guest. Please login or register.

Login with username, password and session length
News: Something to say? Contact the main developers directly! Do so at: hello@setentia.com.ar
 
Pages: [1]   Go Down
  Send this topic  |  Print  
Author Topic: [C++] Make a CTF Gameplay for your MP Mod  (Read 767 times)
Shepard
Betatester
Hero Member
***

KARMA: 1029
Posts: 662


GiveNamedItem("xp"); - *Trollface* - Problem ?


WWW
« on: September 17, 2008, 10:56:11 AM »

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 entity
In a new header file (ctf_gameplay.h), put this :
Code:
// 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 :
Code:
bool m_fHasObject;
In a new .cpp file (ctf_gameplay.cpp), put the following :
Code:
// 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);
}
Code:
// 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 :
Code:
    // 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 :
Code:
if ( pClient )
{
    CBasePlayer *pPlayer = (CBasePlayer *)CBaseEntity::Instance( pClient );
Put this :
Code:
    // 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:
Code:
    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:
Code:
    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:
Code:
    extern DLL_GLOBAL unsigned short g_usObject; // object capture/stolen event
#2 - Creating Capture Point Entity
Open "ctf_gameplay.h" and put at the bottom :
Code:
// 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;
Code:
    bool m_bInCapture;
In the bottom of ctf_gameplay.cpp, put this :
Code:
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
}

Code:
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 FGD
Open up hl_events.cpp and find the following:
void EV_TripmineFire( struct event_args_s *args );
and put this after it :
Code:
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 :
Code:
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 :
Code:
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 :
Code:
#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 :
Code:
@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!
Logged


Saxon
BETATESTER - 2D/3D ARTIST
Betatester
Hero Member
***

KARMA: 208
Posts: 718



« Reply #1 on: September 17, 2008, 01:10:35 PM »

Interesting.  Personally I'm not too much of a fan of CTF gameplay but if it can be added easily enough... then why not? Smiley

BTW you know what would be really cool to add to AM Multiplayer?  REALLY REALLY freakin cool?  JailBreak game modew00t
I've always been a huge fan of Jailbreak mods, but I've always disliked the s***ty unrealistic weapons which appear in Jailbreak mods.  Realistic weapons would change all that...
Logged
Pages: [1]   Go Up
  Send this topic  |  Print  
 
Jump to: