summaryrefslogtreecommitdiff
path: root/game/server/tfc
diff options
context:
space:
mode:
Diffstat (limited to 'game/server/tfc')
-rw-r--r--game/server/tfc/tfc_bot_temp.cpp470
-rw-r--r--game/server/tfc/tfc_bot_temp.h21
-rw-r--r--game/server/tfc/tfc_building.h73
-rw-r--r--game/server/tfc/tfc_client.cpp155
-rw-r--r--game/server/tfc/tfc_client.h17
-rw-r--r--game/server/tfc/tfc_engineer.cpp73
-rw-r--r--game/server/tfc/tfc_engineer.h21
-rw-r--r--game/server/tfc/tfc_eventlog.cpp56
-rw-r--r--game/server/tfc/tfc_gameinterface.cpp33
-rw-r--r--game/server/tfc/tfc_mapitems.cpp3131
-rw-r--r--game/server/tfc/tfc_mapitems.h405
-rw-r--r--game/server/tfc/tfc_player.cpp1115
-rw-r--r--game/server/tfc/tfc_player.h254
-rw-r--r--game/server/tfc/tfc_playermove.cpp76
-rw-r--r--game/server/tfc/tfc_team.cpp279
-rw-r--r--game/server/tfc/tfc_team.h58
-rw-r--r--game/server/tfc/tfc_timer.cpp117
-rw-r--r--game/server/tfc/tfc_timer.h60
18 files changed, 6414 insertions, 0 deletions
diff --git a/game/server/tfc/tfc_bot_temp.cpp b/game/server/tfc/tfc_bot_temp.cpp
new file mode 100644
index 0000000..6a36d2c
--- /dev/null
+++ b/game/server/tfc/tfc_bot_temp.cpp
@@ -0,0 +1,470 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Basic BOT handling.
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "player.h"
+#include "tfc_player.h"
+#include "in_buttons.h"
+#include "movehelper_server.h"
+
+void ClientPutInServer( edict_t *pEdict, const char *playername );
+void Bot_Think( CTFCPlayer *pBot );
+
+ConVar bot_forcefireweapon( "bot_forcefireweapon", "", 0, "Force bots with the specified weapon to fire." );
+ConVar bot_forceattack2( "bot_forceattack2", "0", 0, "When firing, use attack2." );
+ConVar bot_forceattackon( "bot_forceattackon", "0", 0, "When firing, don't tap fire, hold it down." );
+ConVar bot_flipout( "bot_flipout", "0", 0, "When on, all bots fire their guns." );
+ConVar bot_defend( "bot_defend", "0", 0, "Set to a team number, and that team will all keep their combat shields raised." );
+ConVar bot_changeclass( "bot_changeclass", "0", 0, "Force all bots to change to the specified class." );
+static ConVar bot_mimic( "bot_mimic", "0", 0, "Bot uses usercmd of player by index." );
+static ConVar bot_mimic_yaw_offset( "bot_mimic_yaw_offset", "180", 0, "Offsets the bot yaw." );
+
+static int BotNumber = 1;
+static int g_iNextBotTeam = -1;
+static int g_iNextBotClass = -1;
+
+typedef struct
+{
+ bool backwards;
+
+ float nextturntime;
+ bool lastturntoright;
+
+ float nextstrafetime;
+ float sidemove;
+
+ QAngle forwardAngle;
+ QAngle lastAngles;
+
+ float m_flJoinTeamTime;
+ int m_WantedTeam;
+ int m_WantedClass;
+
+ bool m_bWasDead;
+ float m_flDeadTime;
+} botdata_t;
+
+static botdata_t g_BotData[ MAX_PLAYERS ];
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a new Bot and put it in the game.
+// Output : Pointer to the new Bot, or NULL if there's no free clients.
+//-----------------------------------------------------------------------------
+CBasePlayer *BotPutInServer( bool bFrozen, int iTeam, int iClass )
+{
+ g_iNextBotTeam = iTeam;
+ g_iNextBotClass = iClass;
+
+ char botname[ 64 ];
+ Q_snprintf( botname, sizeof( botname ), "Bot%02i", BotNumber );
+
+ edict_t *pEdict = engine->CreateFakeClient( botname );
+ if (!pEdict)
+ {
+ Msg( "Failed to create Bot.\n");
+ return NULL;
+ }
+
+ // Allocate a CBasePlayer for the bot, and call spawn
+ //ClientPutInServer( pEdict, botname );
+ CTFCPlayer *pPlayer = ((CTFCPlayer *)CBaseEntity::Instance( pEdict ));
+ pPlayer->ClearFlags();
+ pPlayer->AddFlag( FL_CLIENT | FL_FAKECLIENT );
+
+ if ( bFrozen )
+ pPlayer->AddEFlags( EFL_BOT_FROZEN );
+
+ BotNumber++;
+
+ botdata_t *pBot = &g_BotData[ pPlayer->entindex() - 1 ];
+ pBot->m_bWasDead = false;
+ pBot->m_WantedTeam = iTeam;
+ pBot->m_WantedClass = iClass;
+ pBot->m_flJoinTeamTime = gpGlobals->curtime + 0.3;
+
+ return pPlayer;
+}
+
+
+// Handler for the "bot" command.
+CON_COMMAND_F( "bot", "Add a bot.", FCVAR_CHEAT )
+{
+ //CDODPlayer *pPlayer = CDODPlayer::Instance( UTIL_GetCommandClientIndex() );
+
+ // The bot command uses switches like command-line switches.
+ // -count <count> tells how many bots to spawn.
+ // -team <index> selects the bot's team. Default is -1 which chooses randomly.
+ // Note: if you do -team !, then it
+ // -class <index> selects the bot's class. Default is -1 which chooses randomly.
+ // -frozen prevents the bots from running around when they spawn in.
+
+ // Look at -count.
+ int count = args.FindArgInt( "-count", 1 );
+ count = clamp( count, 1, 16 );
+
+ int iTeam = 0;
+ const char *pVal = args.FindArg( "-team" );
+ if ( pVal )
+ {
+ if ( stricmp( pVal, "red" ) == 0 )
+ iTeam = TEAM_RED;
+ else
+ iTeam = TEAM_BLUE;
+ }
+
+ // Look at -frozen.
+ bool bFrozen = !!args.FindArg( "-frozen" );
+
+ // Ok, spawn all the bots.
+ while ( --count >= 0 )
+ {
+ // What class do they want?
+ int iClass = RandomInt( 0, PC_LASTCLASS-1 );
+ pVal = args.FindArg( "-class" );
+ if ( pVal )
+ {
+ for ( int i=0; i < PC_LASTCLASS; i++ )
+ {
+ if ( stricmp( GetTFCClassInfo( i )->m_pClassName, pVal ) == 0 )
+ {
+ iClass = i;
+ break;
+ }
+ }
+ }
+
+ BotPutInServer( bFrozen, iTeam, iClass );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Run through all the Bots in the game and let them think.
+//-----------------------------------------------------------------------------
+void Bot_RunAll( void )
+{
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CTFCPlayer *pPlayer = ToTFCPlayer( UTIL_PlayerByIndex( i ) );
+
+ if ( pPlayer && (pPlayer->GetFlags() & FL_FAKECLIENT) )
+ {
+ Bot_Think( pPlayer );
+ }
+ }
+}
+
+bool RunMimicCommand( CUserCmd& cmd )
+{
+ if ( bot_mimic.GetInt() <= 0 )
+ return false;
+
+ if ( bot_mimic.GetInt() > gpGlobals->maxClients )
+ return false;
+
+
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex( bot_mimic.GetInt() );
+ if ( !pPlayer )
+ return false;
+
+ if ( !pPlayer->GetLastUserCommand() )
+ return false;
+
+ cmd = *pPlayer->GetLastUserCommand();
+ cmd.viewangles[YAW] += bot_mimic_yaw_offset.GetFloat();
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Simulates a single frame of movement for a player
+// Input : *fakeclient -
+// *viewangles -
+// forwardmove -
+// sidemove -
+// upmove -
+// buttons -
+// impulse -
+// msec -
+// Output : virtual void
+//-----------------------------------------------------------------------------
+static void RunPlayerMove( CTFCPlayer *fakeclient, const QAngle& viewangles, float forwardmove, float sidemove, float upmove, unsigned short buttons, byte impulse, float frametime )
+{
+ if ( !fakeclient )
+ return;
+
+ CUserCmd cmd;
+
+ // Store off the globals.. they're gonna get whacked
+ float flOldFrametime = gpGlobals->frametime;
+ float flOldCurtime = gpGlobals->curtime;
+
+ float flTimeBase = gpGlobals->curtime + gpGlobals->frametime - frametime;
+ fakeclient->SetTimeBase( flTimeBase );
+
+ Q_memset( &cmd, 0, sizeof( cmd ) );
+
+ if ( !RunMimicCommand( cmd ) )
+ {
+ VectorCopy( viewangles, cmd.viewangles );
+ cmd.forwardmove = forwardmove;
+ cmd.sidemove = sidemove;
+ cmd.upmove = upmove;
+ cmd.buttons = buttons;
+ cmd.impulse = impulse;
+ cmd.random_seed = random->RandomInt( 0, 0x7fffffff );
+ }
+
+ MoveHelperServer()->SetHost( fakeclient );
+ fakeclient->PlayerRunCommand( &cmd, MoveHelperServer() );
+
+ // save off the last good usercmd
+ fakeclient->SetLastUserCommand( cmd );
+
+ // Clear out any fixangle that has been set
+ fakeclient->pl.fixangle = FIXANGLE_NONE;
+
+ // Restore the globals..
+ gpGlobals->frametime = flOldFrametime;
+ gpGlobals->curtime = flOldCurtime;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Run this Bot's AI for one frame.
+//-----------------------------------------------------------------------------
+void Bot_Think( CTFCPlayer *pBot )
+{
+ // Make sure we stay being a bot
+ pBot->AddFlag( FL_FAKECLIENT );
+
+ botdata_t *botdata = &g_BotData[ ENTINDEX( pBot->edict() ) - 1 ];
+
+ QAngle vecViewAngles;
+ float forwardmove = 0.0;
+ float sidemove = botdata->sidemove;
+ float upmove = 0.0;
+ unsigned short buttons = 0;
+ byte impulse = 0;
+ float frametime = gpGlobals->frametime;
+
+ vecViewAngles = pBot->GetLocalAngles();
+
+
+ // Create some random values
+ if ( pBot->GetTeamNumber() == TEAM_UNASSIGNED && gpGlobals->curtime > botdata->m_flJoinTeamTime )
+ {
+ pBot->HandleCommand_JoinTeam( botdata->m_WantedTeam == TEAM_RED ? "red" : "blue" );
+ }
+ else if ( pBot->GetTeamNumber() != TEAM_UNASSIGNED && pBot->m_Shared.GetPlayerClass() == PC_UNDEFINED )
+ {
+ // If they're on a team but haven't picked a class, choose a random class..
+ pBot->HandleCommand_JoinClass( GetTFCClassInfo( botdata->m_WantedClass )->m_pClassName );
+ }
+ else if ( pBot->IsAlive() && (pBot->GetSolid() == SOLID_BBOX) )
+ {
+ trace_t trace;
+
+ botdata->m_bWasDead = false;
+
+ // Stop when shot
+ if ( !pBot->IsEFlagSet(EFL_BOT_FROZEN) )
+ {
+ if ( pBot->m_iHealth == 100 )
+ {
+ forwardmove = 600 * ( botdata->backwards ? -1 : 1 );
+ if ( botdata->sidemove != 0.0f )
+ {
+ forwardmove *= random->RandomFloat( 0.1, 1.0f );
+ }
+ }
+ else
+ {
+ forwardmove = 0;
+ }
+ }
+
+ // Only turn if I haven't been hurt
+ if ( !pBot->IsEFlagSet(EFL_BOT_FROZEN) && pBot->m_iHealth == 100 )
+ {
+ Vector vecEnd;
+ Vector forward;
+
+ QAngle angle;
+ float angledelta = 15.0;
+
+ int maxtries = (int)360.0/angledelta;
+
+ if ( botdata->lastturntoright )
+ {
+ angledelta = -angledelta;
+ }
+
+ angle = pBot->GetLocalAngles();
+
+ Vector vecSrc;
+ while ( --maxtries >= 0 )
+ {
+ AngleVectors( angle, &forward );
+
+ vecSrc = pBot->GetLocalOrigin() + Vector( 0, 0, 36 );
+
+ vecEnd = vecSrc + forward * 10;
+
+ UTIL_TraceHull( vecSrc, vecEnd, VEC_HULL_MIN_SCALED( pBot ), VEC_HULL_MAX_SCALED( pBot ),
+ MASK_PLAYERSOLID, pBot, COLLISION_GROUP_NONE, &trace );
+
+ if ( trace.fraction == 1.0 )
+ {
+ if ( gpGlobals->curtime < botdata->nextturntime )
+ {
+ break;
+ }
+ }
+
+ angle.y += angledelta;
+
+ if ( angle.y > 180 )
+ angle.y -= 360;
+ else if ( angle.y < -180 )
+ angle.y += 360;
+
+ botdata->nextturntime = gpGlobals->curtime + 2.0;
+ botdata->lastturntoright = random->RandomInt( 0, 1 ) == 0 ? true : false;
+
+ botdata->forwardAngle = angle;
+ botdata->lastAngles = angle;
+
+ }
+
+
+ if ( gpGlobals->curtime >= botdata->nextstrafetime )
+ {
+ botdata->nextstrafetime = gpGlobals->curtime + 1.0f;
+
+ if ( random->RandomInt( 0, 5 ) == 0 )
+ {
+ botdata->sidemove = -600.0f + 1200.0f * random->RandomFloat( 0, 2 );
+ }
+ else
+ {
+ botdata->sidemove = 0;
+ }
+ sidemove = botdata->sidemove;
+
+ if ( random->RandomInt( 0, 20 ) == 0 )
+ {
+ botdata->backwards = true;
+ }
+ else
+ {
+ botdata->backwards = false;
+ }
+ }
+
+ pBot->SetLocalAngles( angle );
+ vecViewAngles = angle;
+ }
+
+ // Is my team being forced to defend?
+ if ( bot_defend.GetInt() == pBot->GetTeamNumber() )
+ {
+ buttons |= IN_ATTACK2;
+ }
+ // If bots are being forced to fire a weapon, see if I have it
+ else if ( bot_forcefireweapon.GetString() )
+ {
+ CBaseCombatWeapon *pWeapon = pBot->Weapon_OwnsThisType( bot_forcefireweapon.GetString() );
+ if ( pWeapon )
+ {
+ // Switch to it if we don't have it out
+ CBaseCombatWeapon *pActiveWeapon = pBot->GetActiveWeapon();
+
+ // Switch?
+ if ( pActiveWeapon != pWeapon )
+ {
+ pBot->Weapon_Switch( pWeapon );
+ }
+ else
+ {
+ // Start firing
+ // Some weapons require releases, so randomise firing
+ if ( bot_forceattackon.GetBool() || (RandomFloat(0.0,1.0) > 0.5) )
+ {
+ buttons |= bot_forceattack2.GetBool() ? IN_ATTACK2 : IN_ATTACK;
+ }
+ }
+ }
+ }
+
+ if ( bot_flipout.GetInt() )
+ {
+ if ( bot_forceattackon.GetBool() || (RandomFloat(0.0,1.0) > 0.5) )
+ {
+ buttons |= bot_forceattack2.GetBool() ? IN_ATTACK2 : IN_ATTACK;
+ }
+ }
+ }
+ else
+ {
+ // Wait for Reinforcement wave
+ if ( !pBot->IsAlive() )
+ {
+ if ( botdata->m_bWasDead )
+ {
+ // Wait for a few seconds before respawning.
+ if ( gpGlobals->curtime - botdata->m_flDeadTime > 3 )
+ {
+ // Respawn the bot
+ buttons |= IN_JUMP;
+ }
+ }
+ else
+ {
+ // Start a timer to respawn them in a few seconds.
+ botdata->m_bWasDead = true;
+ botdata->m_flDeadTime = gpGlobals->curtime;
+ }
+ }
+ }
+
+ if ( bot_flipout.GetInt() >= 2 )
+ {
+
+ QAngle angOffset = RandomAngle( -1, 1 );
+
+ botdata->lastAngles += angOffset;
+
+ for ( int i = 0 ; i < 2; i++ )
+ {
+ if ( fabs( botdata->lastAngles[ i ] - botdata->forwardAngle[ i ] ) > 15.0f )
+ {
+ if ( botdata->lastAngles[ i ] > botdata->forwardAngle[ i ] )
+ {
+ botdata->lastAngles[ i ] = botdata->forwardAngle[ i ] + 15;
+ }
+ else
+ {
+ botdata->lastAngles[ i ] = botdata->forwardAngle[ i ] - 15;
+ }
+ }
+ }
+
+ botdata->lastAngles[ 2 ] = 0;
+
+ pBot->SetLocalAngles( botdata->lastAngles );
+ }
+
+ RunPlayerMove( pBot, pBot->GetLocalAngles(), forwardmove, sidemove, upmove, buttons, impulse, frametime );
+}
+
+
diff --git a/game/server/tfc/tfc_bot_temp.h b/game/server/tfc/tfc_bot_temp.h
new file mode 100644
index 0000000..906ae00
--- /dev/null
+++ b/game/server/tfc/tfc_bot_temp.h
@@ -0,0 +1,21 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TFC_BOT_TEMP_H
+#define TFC_BOT_TEMP_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+// If iTeam or iClass is -1, then a team or class is randomly chosen.
+CBasePlayer *BotPutInServer( bool bFrozen, int iTeam, int iClass );
+
+void Bot_RunAll();
+
+
+#endif // TFC_BOT_TEMP_H
diff --git a/game/server/tfc/tfc_building.h b/game/server/tfc/tfc_building.h
new file mode 100644
index 0000000..401c5bf
--- /dev/null
+++ b/game/server/tfc/tfc_building.h
@@ -0,0 +1,73 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef TFC_BUILDING_H
+#define TFC_BUILDING_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "baseanimating.h"
+#include "tfc_shareddefs.h"
+
+
+class CTFBaseBuilding : public CBaseAnimating
+{
+public:
+ EHANDLE real_owner;
+};
+
+
+class CTFTeleporter : public CTFBaseBuilding
+{
+public:
+ void Spawn(void);
+ void Precache(void);
+
+ void EXPORT Teleporter_Explode( void );
+ void EXPORT TeleporterThink( void );
+ void EXPORT TeleporterTouch( CBaseEntity *pOther );
+
+ Class_T Classify(void) { return CLASS_MACHINE; };
+ int BloodColor( void ) { return DONT_BLEED; }
+
+ void Remove( void );
+
+ void TeamFortress_TakeEMPBlast(CBaseEntity* pevGren);
+ void Finished( void );
+ static CTFTeleporter *CreateTeleporter( Vector vecOrigin, Vector vecAngles, CBaseEntity *pOwner, int type );
+ BOOL EngineerUse( CBasePlayer *pPlayer );
+ void Killed( CBaseEntity *pevInflictor, CBaseEntity *pevAttacker, int iGib );
+ void TeleporterSend( CBasePlayer *pPlayer );
+ void TeleporterReceive( CBasePlayer *pPlayer, float flDelay );
+ void TeleporterKilled( void );
+ BOOL TeleportersReady( void );
+ void TeleporterFadePlayer( int direction );
+ void TeleporterProcessFade( void );
+ void SetTeleporterRings( int state );
+ void SetTeleporterParticles( int state );
+ float GetDamageMultiplier( void );
+ CTFTeleporter* FindMatch( void );
+ const Vector& GetTeamColor( void );
+ bool PlayerIsStandingOnTeleporter( CBaseEntity *pOther );
+
+ CBasePlayer *m_pPlayer; // player being teleported
+
+ float m_flInitialUseDelay;
+
+ int m_iType; // entry or exit
+ int m_iState; // state of the teleporter (idle, ready, sending, etc.)
+ int m_iDestroyed; // has this teleporter been destroyed
+
+ int m_iShardIndex; // Metal shards
+
+ float m_flMyNextThink; // used to control the pace at which the teleporters work
+ float m_flDamageDelay; // damage multiplier that slows the teleporters when they're damaged
+};
+
+
+#endif // TFC_BUILDING_H
diff --git a/game/server/tfc/tfc_client.cpp b/game/server/tfc/tfc_client.cpp
new file mode 100644
index 0000000..af8f2a7
--- /dev/null
+++ b/game/server/tfc/tfc_client.cpp
@@ -0,0 +1,155 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+/*
+
+===== tf_client.cpp ========================================================
+
+ HL2 client/server game specific stuff
+
+*/
+
+#include "cbase.h"
+#include "player.h"
+#include "gamerules.h"
+#include "entitylist.h"
+#include "physics.h"
+#include "game.h"
+#include "ai_network.h"
+#include "ai_node.h"
+#include "ai_hull.h"
+#include "shake.h"
+#include "player_resource.h"
+#include "engine/IEngineSound.h"
+#include "tfc_player.h"
+#include "tfc_gamerules.h"
+#include "tier0/vprof.h"
+#include "tfc_bot_temp.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+extern CBaseEntity *FindPickerEntity( CBasePlayer *pPlayer );
+
+extern bool g_fGameOver;
+
+
+void FinishClientPutInServer( CTFCPlayer *pPlayer )
+{
+ pPlayer->InitialSpawn();
+ pPlayer->Spawn();
+
+ char sName[128];
+ Q_strncpy( sName, pPlayer->GetPlayerName(), sizeof( sName ) );
+
+ // First parse the name and remove any %'s
+ for ( char *pApersand = sName; pApersand != NULL && *pApersand != 0; pApersand++ )
+ {
+ // Replace it with a space
+ if ( *pApersand == '%' )
+ *pApersand = ' ';
+ }
+
+ // notify other clients of player joining the game
+ UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "#Game_connected", sName[0] != 0 ? sName : "<unconnected>" );
+}
+
+/*
+===========
+ClientPutInServer
+
+called each time a player is spawned into the game
+============
+*/
+void ClientPutInServer( edict_t *pEdict, const char *playername )
+{
+ // Allocate a CBaseTFPlayer for pev, and call spawn
+ CTFCPlayer *pPlayer = CTFCPlayer::CreatePlayer( "player", pEdict );
+ pPlayer->SetPlayerName( playername );
+}
+
+
+void ClientActive( edict_t *pEdict, bool bLoadGame )
+{
+ // Can't load games in CS!
+ Assert( !bLoadGame );
+
+ CTFCPlayer *pPlayer = ToTFCPlayer( CBaseEntity::Instance( pEdict ) );
+ FinishClientPutInServer( pPlayer );
+}
+
+
+/*
+===============
+const char *GetGameDescription()
+
+Returns the descriptive name of this .dll. E.g., Half-Life, or Team Fortress 2
+===============
+*/
+const char *GetGameDescription()
+{
+ if ( g_pGameRules ) // this function may be called before the world has spawned, and the game rules initialized
+ return g_pGameRules->GetGameDescription();
+ else
+ return "CounterStrike";
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Precache game-specific models & sounds
+//-----------------------------------------------------------------------------
+void ClientGamePrecache( void )
+{
+ // Materials used by the client effects
+ CBaseEntity::PrecacheModel( "sprites/white.vmt" );
+ CBaseEntity::PrecacheModel( "sprites/physbeam.vmt" );
+}
+
+
+// called by ClientKill and DeadThink
+void respawn( CBaseEntity *pEdict, bool fCopyCorpse )
+{
+ if (gpGlobals->coop || gpGlobals->deathmatch)
+ {
+ if ( fCopyCorpse )
+ {
+ // make a copy of the dead body for appearances sake
+ dynamic_cast< CBasePlayer* >( pEdict )->CreateCorpse();
+ }
+
+ // respawn player
+ pEdict->Spawn();
+ }
+ else
+ { // restart the entire server
+ engine->ServerCommand("reload\n");
+ }
+}
+
+void GameStartFrame( void )
+{
+ VPROF( "GameStartFrame" );
+
+ if ( g_pGameRules )
+ g_pGameRules->Think();
+
+ if ( g_fGameOver )
+ return;
+
+ gpGlobals->teamplay = teamplay.GetInt() ? true : false;
+
+ Bot_RunAll();
+}
+
+//=========================================================
+// instantiate the proper game rules object
+//=========================================================
+void InstallGameRules()
+{
+ CreateGameRulesObject( "CTFCGameRules" );
+}
diff --git a/game/server/tfc/tfc_client.h b/game/server/tfc/tfc_client.h
new file mode 100644
index 0000000..a3a5356
--- /dev/null
+++ b/game/server/tfc/tfc_client.h
@@ -0,0 +1,17 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef TFC_CLIENT_H
+#define TFC_CLIENT_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+void respawn( CBaseEntity *pEdict, bool fCopyCorpse );
+
+
+#endif // TFC_CLIENT_H
diff --git a/game/server/tfc/tfc_engineer.cpp b/game/server/tfc/tfc_engineer.cpp
new file mode 100644
index 0000000..ddbf32d
--- /dev/null
+++ b/game/server/tfc/tfc_engineer.cpp
@@ -0,0 +1,73 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "tfc_player.h"
+#include "tfc_building.h"
+
+
+//=========================================================================
+// Destroys a single Engineer building
+void DestroyBuilding(CTFCPlayer *eng, char *bld)
+{
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, bld );
+ while ( pEnt )
+ {
+ CTFBaseBuilding *pBuilding = dynamic_cast<CTFBaseBuilding*>( pEnt );
+
+ if (pBuilding && pBuilding->real_owner == eng)
+ {
+ // If it's fallen out of the world, give the engineer
+ // some metal back
+ int pos = UTIL_PointContents(pEnt->GetAbsOrigin());
+#ifdef TFCTODO // CONTENTS_SKY doesn't exist in the new engine
+ if (pos == CONTENT_SOLID || pos == CONTENT_SKY)
+#else
+ if (pos == CONTENTS_SOLID)
+#endif
+ {
+ eng->GiveAmmo( 100, TFC_AMMO_CELLS );
+ eng->TeamFortress_CheckClassStats();
+ }
+
+ pEnt->TakeDamage( CTakeDamageInfo( pEnt, pEnt, 500, 0 ) );
+ }
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, bld );
+ }
+}
+
+
+//=========================================================================
+// Destroys a teleporter (determined by type)
+void DestroyTeleporter(CTFCPlayer *eng, int type)
+{
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "building_teleporter" );
+ while ( pEnt )
+ {
+ CTFTeleporter *pTeleporter = dynamic_cast<CTFTeleporter*>( pEnt );
+
+ if (pTeleporter && pTeleporter->real_owner == eng && pTeleporter->m_iType == type )
+ {
+ // If it's fallen out of the world, give the engineer
+ // some metal back
+ int pos = UTIL_PointContents(pEnt->GetAbsOrigin());
+#ifdef TFCTODO // CONTENTS_SKY doesn't exist in the new engine
+ if (pos == CONTENT_SOLID || pos == CONTENT_SKY)
+#else
+ if (pos == CONTENTS_SOLID)
+#endif
+ {
+ eng->GiveAmmo( 100, TFC_AMMO_CELLS );
+ eng->TeamFortress_CheckClassStats();
+ }
+
+ pEnt->TakeDamage( CTakeDamageInfo( pEnt, pEnt, 500, 0 ) );
+ }
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "building_teleporter" );
+ }
+}
diff --git a/game/server/tfc/tfc_engineer.h b/game/server/tfc/tfc_engineer.h
new file mode 100644
index 0000000..0a57dca
--- /dev/null
+++ b/game/server/tfc/tfc_engineer.h
@@ -0,0 +1,21 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef TFC_ENGINEER_H
+#define TFC_ENGINEER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+class CTFCPlayer;
+
+
+void DestroyBuilding(CTFCPlayer *eng, char *bld);
+void DestroyTeleporter(CTFCPlayer *eng, int type);
+
+
+#endif // TFC_ENGINEER_H
diff --git a/game/server/tfc/tfc_eventlog.cpp b/game/server/tfc/tfc_eventlog.cpp
new file mode 100644
index 0000000..a75fa45
--- /dev/null
+++ b/game/server/tfc/tfc_eventlog.cpp
@@ -0,0 +1,56 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "cbase.h"
+#include "../EventLog.h"
+#include "KeyValues.h"
+
+class CTFCEventLog : public CEventLog
+{
+private:
+ typedef CEventLog BaseClass;
+
+public:
+ virtual ~CTFCEventLog() {};
+
+public:
+ bool PrintEvent( KeyValues * event ) // override virtual function
+ {
+ if ( BaseClass::PrintEvent( event ) )
+ {
+ return true;
+ }
+
+ if ( Q_strcmp(event->GetName(), "cstrike_") == 0 )
+ {
+ return PrintCStrikeEvent( event );
+ }
+
+ return false;
+ }
+
+protected:
+
+ bool PrintCStrikeEvent( KeyValues * event ) // print Mod specific logs
+ {
+ // const char * name = event->GetName() + Q_strlen("cstrike_"); // remove prefix
+
+ return false;
+ }
+
+};
+
+CTFCEventLog g_TFCEventLog;
+
+//-----------------------------------------------------------------------------
+// Singleton access
+//-----------------------------------------------------------------------------
+IGameSystem* GameLogSystem()
+{
+ return &g_TFCEventLog;
+}
+
diff --git a/game/server/tfc/tfc_gameinterface.cpp b/game/server/tfc/tfc_gameinterface.cpp
new file mode 100644
index 0000000..67b8766
--- /dev/null
+++ b/game/server/tfc/tfc_gameinterface.cpp
@@ -0,0 +1,33 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "gameinterface.h"
+#include "mapentities.h"
+
+
+// -------------------------------------------------------------------------------------------- //
+// Mod-specific CServerGameClients implementation.
+// -------------------------------------------------------------------------------------------- //
+
+void CServerGameClients::GetPlayerLimits( int& minplayers, int& maxplayers, int &defaultMaxPlayers ) const
+{
+ minplayers = 2; // Force multiplayer.
+ maxplayers = MAX_PLAYERS;
+ defaultMaxPlayers = 32;
+}
+
+
+// -------------------------------------------------------------------------------------------- //
+// Mod-specific CServerGameDLL implementation.
+// -------------------------------------------------------------------------------------------- //
+
+void CServerGameDLL::LevelInit_ParseAllEntities( const char *pMapEntities )
+{
+ MapEntity_ParseAllEntities( pMapEntities, NULL );
+}
+
+
diff --git a/game/server/tfc/tfc_mapitems.cpp b/game/server/tfc/tfc_mapitems.cpp
new file mode 100644
index 0000000..0ca6b87
--- /dev/null
+++ b/game/server/tfc/tfc_mapitems.cpp
@@ -0,0 +1,3131 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "tfc_mapitems.h"
+#include "tfc_shareddefs.h"
+#include "tfc_player.h"
+#include "tfc_gamerules.h"
+#include "tfc_timer.h"
+#include "tfc_team.h"
+
+
+bool ActivateDoResults(CTFGoal *Goal, CTFCPlayer *AP, CTFGoal *ActivatingGoal);
+bool ActivationSucceeded(CTFGoal *Goal, CTFCPlayer *AP, CTFGoal *ActivatingGoal);
+void DoResults(CTFGoal *Goal, CTFCPlayer *AP, BOOL bAddBonuses);
+
+
+// ---------------------------------------------------------------------------------------- //
+// Global helpers.
+// ---------------------------------------------------------------------------------------- //
+
+const char* GetTeamName( int iTeam )
+{
+ if ( iTeam == 0 )
+ {
+ return "SPECTATOR";
+ }
+ else
+ {
+ CTeam *pTeam = GetGlobalTeam( iTeam );
+ if ( pTeam )
+ return pTeam->GetName();
+ else
+ return "UNKNOWN TEAM";
+ }
+}
+
+
+//===========================================
+int GetTeamCheckTeam( const char *pTargetName )
+{
+ CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, pTargetName );
+ if ( pEntity )
+ {
+ if ( !strcmp( pEntity->GetClassname(), "info_tf_teamcheck" ) )
+ return pEntity->GetTeamNumber();
+ }
+
+ return 0;
+}
+
+
+//=========================================================================
+// Displays the state of a GoalItem
+void DisplayItemStatus(CTFGoal *Goal, CTFCPlayer *Player, CTFGoalItem *Item)
+{
+ MDEBUG( Msg( "Displaying Item Status\nItem goal_no : %d\n", Item->goal_no) );
+
+ // If we have a teamcheck entity, use it instead
+ if ( Item->owned_by_teamcheck != NULL_STRING )
+ Item->owned_by = GetTeamCheckTeam( STRING(Item->owned_by_teamcheck) );
+
+ if (Item->goal_state == TFGS_ACTIVE)
+ {
+ MDEBUG( Msg( " Item is ACTIVE\n") );
+
+ if ( (Goal->team_str_carried != NULL_STRING) || (Goal->non_team_str_carried != NULL_STRING) )
+ {
+ CBaseEntity *pOwner = Item->GetOwnerEntity();
+
+ if (Player->GetTeamNumber() == Item->owned_by)
+ {
+ if (Player == Item->GetOwnerEntity())
+ ClientPrint( Player, HUD_PRINTTALK, STRING(Goal->team_str_carried), "you" );
+ else
+ ClientPrint( Player, HUD_PRINTTALK, STRING(Goal->team_str_carried), STRING(pOwner->GetEntityName()) );
+ }
+ else
+ {
+ if (Player == Item->GetOwnerEntity())
+ ClientPrint( Player, HUD_PRINTTALK, STRING(Goal->non_team_str_carried), "you" );
+ else
+ ClientPrint( Player, HUD_PRINTTALK, STRING(Goal->non_team_str_carried), STRING(pOwner->GetEntityName()) );
+ }
+ }
+ }
+ else if (Item->GetAbsOrigin() != Item->oldorigin)
+ {
+ MDEBUG( Msg( " Item has MOVED\n") );
+
+ if ( (Goal->team_str_moved != NULL_STRING) || (Goal->non_team_str_moved != NULL_STRING) )
+ {
+ if (Player->GetTeamNumber() == Item->owned_by)
+ ClientPrint( Player, HUD_PRINTTALK, STRING(Goal->team_str_moved) );
+ else
+ ClientPrint( Player, HUD_PRINTTALK, STRING(Goal->non_team_str_moved) );
+ }
+ }
+ else
+ {
+ MDEBUG( Msg( " Item is AT HOME\n") );
+
+ if ( Goal->team_str_home != NULL_STRING || Goal->non_team_str_home != NULL_STRING )
+ {
+ if (Player->GetTeamNumber() == Item->owned_by)
+ ClientPrint( Player, HUD_PRINTTALK, STRING(Goal->team_str_home) );
+ else
+ ClientPrint( Player, HUD_PRINTTALK, STRING(Goal->non_team_str_home) );
+ }
+ }
+}
+
+
+//=========================================================================
+// Inactivates a Teamspawn point
+void InactivateSpawn(CTFSpawn *Spawn)
+{
+ Spawn->goal_state = TFGS_REMOVED;
+}
+
+//=========================================================================
+// Activates a Teamspawn point
+void ActivateSpawn(CTFSpawn *Spawn)
+{
+ Spawn->goal_state = TFGS_INACTIVE;
+}
+
+
+//=========================================================================
+// Increase the score of a team
+void TeamFortress_TeamIncreaseScore(int tno, int scoretoadd)
+{
+ if ( tno == 0 )
+ return;
+
+ CTeam *pTeam = GetGlobalTeam( tno );
+ if ( !pTeam )
+ return;
+
+ pTeam->AddScore( scoretoadd );
+}
+
+// Returns true if the AP's carrying at least 1 of the items in the group
+bool HasItemFromGroup( CBaseEntity *AP, int iGroupNo )
+{
+ // Find all items in the group
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "item_tfgoal" );
+ while ( pEnt )
+ {
+ CTFGoalItem *pGoal = dynamic_cast<CTFGoalItem*>( pEnt );
+ if ( (pGoal->group_no == iGroupNo) && (pGoal->GetOwnerEntity() == AP) )
+ return true;
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "item_tfgoal" );
+ }
+
+ return false;
+}
+
+//=========================================================================
+// Returns true if all the goals in the specified group are in the specified state
+bool AllGoalsInState( int iGroupNo, int iState )
+{
+ // Find all goals in the group
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "info_tfgoal" );
+ while ( pEnt )
+ {
+ CTFGoal *pGoal = dynamic_cast< CTFGoal* >( pEnt );
+ if ( pGoal )
+ {
+ if (pGoal->group_no == iGroupNo)
+ {
+ // All Goals in the group must be in the specified state
+ if (pGoal->goal_state != iState)
+ return false;
+ }
+ }
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "info_tfgoal" );
+ }
+
+ return true;
+}
+
+
+// Return the item with a goal_no equal to ino
+CTFGoalItem* Finditem(int ino)
+{
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "item_tfgoal" );
+ while ( pEnt )
+ {
+ CTFGoalItem *pGoal = dynamic_cast<CTFGoalItem*>( pEnt );
+ if (pGoal && pGoal->goal_no == ino)
+ return pGoal;
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "item_tfgoal" );
+ }
+
+ // Goal does not exist
+ Warning("Could not find an item with a goal_no of %d.\n", ino);
+ return NULL;
+}
+
+
+//=========================================================================
+// Return the TeamSpawn with a goal_no equal to gno
+CTFSpawn* Findteamspawn(int gno)
+{
+ // Search by netname
+ //TFCTODO: I think FindEntityByClassname will do the same thing.
+ //CBaseEntity *pEnt = UTIL_FindEntityByString( NULL, "netname", "info_player_teamspawn" );
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "info_player_teamspawn" );
+ while ( pEnt )
+ {
+ CTFSpawn *pSpawn = dynamic_cast<CTFSpawn*>( pEnt );
+ if ( pSpawn )
+ {
+ if (pSpawn->goal_no == gno)
+ return pSpawn;
+ }
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "info_player_teamspawn" );
+ }
+
+ // Goal does not exist
+ Warning("Could not find a Teamspawn with a goal_no of %d.\n", gno);
+ return NULL;
+}
+
+
+//=========================================================================
+// Return the goal with a goal_no equal to gno
+CTFGoal* Findgoal(int gno)
+{
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "item_tfgoal" );
+ while ( pEnt )
+ {
+ CTFGoal *pGoal = dynamic_cast<CTFGoal*>( pEnt );
+ if (pGoal && pGoal->goal_no == gno)
+ return pGoal;
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "item_tfgoal" );
+ }
+
+ // Goal does not exist
+ Warning("Could not find a goal with a goal_no of %d.\n", gno);
+ return NULL;
+}
+
+
+//=========================================================================
+// Remove a Timer/Goal
+void RemoveGoal(CTFGoal *pGoal)
+{
+ pGoal->AddSolidFlags( FSOLID_NOT_SOLID );
+ pGoal->goal_state = TFGS_REMOVED;
+ pGoal->AddEffects( EF_NODRAW );
+}
+
+
+//=========================================================================
+// Return true if the player meets the AP criteria
+bool APMeetsCriteria(CTFGoal *Goal, CTFCPlayer *AP)
+{
+ MDEBUG(Warning("==========================\n"));
+ MDEBUG(Warning("AP Criteria Checking\n"));
+ MDEBUG(Warning(UTIL_VarArgs("Goal: %s", STRING(Goal->edict()->netname))));
+
+ CTFGoal *pGoal;
+ CTFGoalItem *pItem;
+
+ if (AP != NULL && AP->Classify() == CLASS_PLAYER)
+ {
+ MDEBUG(Warning(UTIL_VarArgs("\nAP : %s\n", AP->GetPlayerName())));
+
+ // If a player of a specific team can only activate this
+ if (Goal->GetTeamNumber())
+ {
+ MDEBUG(Warning(" Checking team."));
+ if (Goal->GetTeamNumber() != AP->GetTeamNumber())
+ return false;
+ if ( !AP->IsAlive() ) // don't want dead or dying players activating this
+ return false;
+ MDEBUG(Warning("passed.\n"));
+ }
+
+ // If a player in a team specified by a teamcheck entity can activate this
+ if (Goal->teamcheck != NULL_STRING)
+ {
+ MDEBUG(Warning(" Checking teamcheck entity."));
+
+ if ( AP->GetTeamNumber() != GetTeamCheckTeam( STRING(Goal->teamcheck) ) )
+ return false;
+
+ MDEBUG(Warning(" passed.\n"));
+ }
+
+ // If a player of a specific class can only activate this
+ if (Goal->playerclass)
+ {
+ MDEBUG(Warning(" Checking class."));
+ if (Goal->playerclass != AP->m_Shared.GetPlayerClass())
+ return false;
+ MDEBUG(Warning("passed.\n"));
+ }
+
+ // If this activation needs a GoalItem, make sure the player has it
+ if (Goal->items_allowed)
+ {
+ MDEBUG(Warning(" Checking items."));
+ pItem = Finditem(Goal->items_allowed);
+ if (!pItem)
+ return false;
+ if (pItem->GetOwnerEntity() != AP)
+ return false;
+ MDEBUG(Warning("passed.\n"));
+ }
+ }
+
+ // Check Goal states
+ if (Goal->if_goal_is_active)
+ {
+ MDEBUG(Warning(" Checking if_goal_is_active."));
+ pGoal = Findgoal(Goal->if_goal_is_active);
+ if (!pGoal)
+ return false;
+ if (pGoal->goal_state != TFGS_ACTIVE)
+ return false;
+ MDEBUG(Warning("passed.\n"));
+ }
+
+ if (Goal->if_goal_is_inactive)
+ {
+ MDEBUG(Warning(" Checking if_goal_is_inactive."));
+ pGoal = Findgoal(Goal->if_goal_is_inactive);
+ if (!pGoal)
+ return false;
+ if (pGoal->goal_state != TFGS_INACTIVE)
+ return false;
+ MDEBUG(Warning("passed.\n"));
+ }
+
+ if (Goal->if_goal_is_removed)
+ {
+ MDEBUG(Warning(" Checking if_goal_is_removed."));
+ pGoal = Findgoal(Goal->if_goal_is_removed);
+ if (!pGoal)
+ return false;
+ if (pGoal->goal_state != TFGS_REMOVED)
+ return false;
+ MDEBUG(Warning("passed.\n"));
+ }
+
+ // Check Group States
+ if (Goal->if_group_is_active)
+ {
+ MDEBUG(Warning(" Checking if_group_is_active."));
+ if ( !AllGoalsInState(Goal->if_group_is_active, TFGS_ACTIVE) )
+ return false;
+ MDEBUG(Warning("passed.\n"));
+ }
+
+ if (Goal->if_group_is_inactive)
+ {
+ MDEBUG(Warning(" Checking if_group_is_inactive."));
+ if ( !AllGoalsInState(Goal->if_group_is_inactive, TFGS_INACTIVE) )
+ return false;
+ MDEBUG(Warning("passed.\n"));
+ }
+
+ if (Goal->if_group_is_removed)
+ {
+ MDEBUG(Warning(" Checking if_group_is_removed."));
+ if ( !AllGoalsInState(Goal->if_group_is_removed, TFGS_REMOVED) )
+ return false;
+ MDEBUG(Warning("passed.\n"));
+ }
+
+ // Check Item States
+ if (Goal->if_item_has_moved)
+ {
+ MDEBUG(Warning(" Checking if_item_has_moved."));
+ // Find the item
+ pItem = Finditem(Goal->if_item_has_moved);
+ if (!pItem)
+ return false;
+ if (pItem->goal_state != TFGS_ACTIVE && pItem->GetAbsOrigin() == pItem->oldorigin)
+ return false;
+ MDEBUG(Warning("passed.\n"));
+ }
+
+ if (Goal->if_item_hasnt_moved)
+ {
+ MDEBUG(Warning(" Checking if_item_hasnt_moved."));
+ // Find the item
+ pItem = Finditem(Goal->if_item_hasnt_moved);
+ if (!pItem)
+ return false;
+ if (pItem->goal_state == TFGS_ACTIVE || pItem->GetAbsOrigin() != pItem->oldorigin )
+ return false;
+ MDEBUG(Warning("passed.\n"));
+ }
+
+ // Check Items being carried
+ if (AP != NULL && AP->Classify() == CLASS_PLAYER)
+ {
+ if (Goal->has_item_from_group)
+ {
+ MDEBUG(Warning(" Checking has_item_from_group."));
+ if ( !HasItemFromGroup(AP, Goal->has_item_from_group) )
+ return false;
+ MDEBUG(Warning("passed.\n"));
+ }
+
+ if (Goal->hasnt_item_from_group)
+ {
+ MDEBUG(Warning(" Checking hasnt_item_from_group."));
+ if ( HasItemFromGroup(AP, Goal->hasnt_item_from_group) )
+ return false;
+ MDEBUG(Warning("passed.\n"));
+ }
+ }
+
+ MDEBUG(Warning("Criteria passed.\n"));
+ return true;
+}
+
+
+//=========================================================================
+// Return true if the Entity should activate
+bool ShouldActivate(CTFGoal *Goal, CTFCPlayer *AP)
+{
+#ifdef MAP_DEBUG
+ Warning(UTIL_VarArgs("\nDoIActivate: ", Goal->edict()->netname ? STRING(Goal->edict()->netname) : STRING(Goal->edict()->classname)));
+ if (AP)
+ Warning(UTIL_VarArgs(", AP: %s\n", AP->GetPlayerName()));
+#endif
+
+ // Abort if it's already active
+ if (Goal->goal_state == TFGS_ACTIVE)
+ {
+ MDEBUG(Warning("-- Goal already active --\n"));
+ return false;
+ }
+ // Abort if it's been removed
+ if (Goal->goal_state == TFGS_REMOVED)
+ {
+ MDEBUG(Warning("-- Goal is in Removed state --\n"));
+ return false;
+ }
+ // Abort if it's been activated already and its activation's being delayed
+ if (Goal->goal_state == TFGS_DELAYED)
+ {
+ MDEBUG(Warning("-- Goal is being Delayed --\n"));
+ return false;
+ }
+
+ // See if the AP matches the criteria
+ bool bAPMet = APMeetsCriteria(Goal, AP);
+ bool bAct = false;
+ bool bRevAct;
+ if ( FClassnameIs(Goal,"item_tfgoal") )
+ bRevAct = (Goal->goal_activation & TFGI_REVERSE_AP) != 0;
+ else
+ bRevAct = (Goal->goal_activation & TFGA_REVERSE_AP) != 0;
+
+ // Does the AP match the AP Criteria?
+ if (bAPMet)
+ {
+ MDEBUG(Warning("-- Criteria met --\n"));
+ if (!bRevAct)
+ bAct = true;
+ }
+ else
+ {
+ MDEBUG(Warning("-- Criteria not met --\n"));
+ if (bRevAct)
+ {
+ MDEBUG(Warning("Reverse Activation\n"));
+ bAct = true;
+ }
+ }
+
+#ifdef MAP_DEBUG
+ if (bAct)
+ Warning("Activation.\n");
+ else
+ Warning("NO Activation.\n");
+#endif
+
+ return bAct;
+};
+
+
+//=========================================================================
+// Return TRUE if the player is affected by the goal
+BOOL IsAffectedBy(CTFGoal *Goal, CTFCPlayer *Player, CTFCPlayer *AP)
+{
+ // Don't affect anyone who isn't alive or is in Observer mode
+ if (Player->m_Shared.GetPlayerClass() == PC_UNDEFINED)
+ return FALSE;
+
+ // Same Environment Check
+ if (Goal->goal_effects & TFGE_SAME_ENVIRONMENT)
+ {
+ int iEnviron = UTIL_PointContents( Goal->GetAbsOrigin() );
+ if ( UTIL_PointContents( Player->GetAbsOrigin() ) != iEnviron )
+ return FALSE;
+ }
+
+ if (Goal->t_length != 0)
+ {
+ // Within radius?
+ if ((Goal->GetAbsOrigin() - Player->GetAbsOrigin()).Length() <= Goal->t_length)
+ {
+ // Obstructed by walls?
+ if (Goal->goal_effects & TFGE_WALL)
+ {
+ trace_t tr;
+ UTIL_TraceLine ( Goal->GetAbsOrigin(), Player->WorldSpaceCenter(), MASK_SOLID, Goal, COLLISION_GROUP_NONE, &tr );
+ if ( tr.fraction == 1.0 )
+ return TRUE;
+ }
+ else
+ {
+ return TRUE;
+ }
+ }
+ }
+
+ if ( Goal->Classify() != CLASS_TFGOAL_TIMER && AP != NULL )
+ {
+ // Spawnpoints always affect the player who spawns on them
+ if ((Goal->Classify() == CLASS_TFSPAWN) && (Player == AP))
+ return TRUE;
+
+ if ((Goal->goal_effects & TFGE_AP) && (Player == AP))
+ return TRUE;
+
+ if ((Goal->goal_effects & TFGE_AP_TEAM) && (AP->GetTeamNumber() == Player->GetTeamNumber()))
+ return TRUE;
+ }
+
+ if (Goal->goal_effects & TFGE_NOT_AP_TEAM)
+ {
+ if (AP == NULL || AP->GetTeamNumber() != Player->GetTeamNumber())
+ return TRUE;
+ }
+
+ if ((Goal->goal_effects & TFGE_NOT_AP) && (Player != AP))
+ return TRUE;
+
+ if ((Goal->maxammo_shells != 0) && (Player->GetTeamNumber() == Goal->maxammo_shells))
+ return TRUE;
+
+ if ((Goal->maxammo_nails != 0) && (Player->GetTeamNumber() != Goal->maxammo_nails))
+ return TRUE;
+
+ return FALSE;
+}
+
+
+//=========================================================================
+// Do all the checking of Item Groups
+void DoItemGroupWork(CTFGoalItem *Item, CTFCPlayer *AP)
+{
+ if (Item->distance != 0)
+ {
+ if (Item->pain_finished == 0)
+ {
+ // No goal specified in .pain_finished. Print error.
+ Warning( "GoalItem %d has .distance specified, but no .pain_finished\n", Item->goal_no );
+ }
+
+ BOOL bAllCarried = TRUE;
+ // Find all items
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "item_tfgoal" );
+ while ( pEnt && bAllCarried )
+ {
+ CTFGoalItem *pItem = dynamic_cast<CTFGoalItem*>( pEnt );
+ if ( pItem )
+ {
+ if (pItem->group_no == Item->distance && pItem->goal_state != TFGS_ACTIVE)
+ bAllCarried = FALSE;
+ }
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "item_tfgoal" );
+ }
+
+ if (bAllCarried)
+ {
+ CTFGoal *pGoal = Findgoal(Item->pain_finished);
+ if (pGoal)
+ DoResults(pGoal, AP, (Item->goal_result & TFGR_ADD_BONUSES));
+ }
+ }
+
+ if (Item->speed != 0)
+ {
+ if (Item->attack_finished == 0)
+ {
+ // No goal specified in .attack_finished. Print error.
+ Warning( "GoalItem %d has .speed specified, but no .attack_finished\n", Item->goal_no );
+ }
+
+ BOOL bAllCarried = TRUE;
+ CBaseEntity *pCarrier = NULL;
+ // Find all goals
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "item_tfgoal" );
+ while ( pEnt && bAllCarried )
+ {
+ CTFGoalItem *pItem = dynamic_cast<CTFGoalItem*>( pEnt );
+ if ( pItem )
+ {
+ if (pItem->group_no == Item->speed)
+ {
+ if (pItem->goal_state != TFGS_ACTIVE)
+ bAllCarried = FALSE;
+ else if (!pCarrier) // Store Player
+ pCarrier = pItem->GetOwnerEntity();
+ else if (pCarrier != pItem->GetOwnerEntity()) // Need to all be carried by the same player
+ bAllCarried = FALSE;
+ }
+ }
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "item_tfgoal" );
+ }
+
+ if (bAllCarried)
+ {
+ CTFGoal *pGoal = Findgoal(Item->attack_finished);
+ if (pGoal)
+ DoResults(pGoal, AP, (Item->goal_result & TFGR_ADD_BONUSES));
+ }
+ }
+}
+
+
+//=========================================================================
+// Remove any results applied to this player by the Goal
+// Used when a GoalItem is dropped/removed
+void RemoveResults(CTFGoal *Goal, CTFCPlayer *pPlayer)
+{
+ // Only remove the stats if the player has been affected
+ // by this item. This is needed because the player may have
+ // died since being affected
+ if ( FClassnameIs( Goal, "item_tfgoal" ) )
+ {
+ if (!(pPlayer->item_list & Goal->item_list))
+ return;
+
+ if (Goal->goal_activation & TFGI_DONTREMOVERES)
+ return;
+
+ // Remove the affected flag
+ pPlayer->item_list &= ~(Goal->item_list);
+ }
+
+ if (Goal->GetHealth() > 0)
+ pPlayer->TakeDamage( CTakeDamageInfo( Goal, Goal, Goal->GetHealth(), DMG_IGNOREARMOR ) );
+ if (Goal->GetHealth() < 0)
+ pPlayer->TakeHealth( (0 - Goal->GetHealth()), 0 );
+ pPlayer->lives -= Goal->lives;
+ pPlayer->armortype -= Goal->armortype;
+ pPlayer->SetArmorValue( pPlayer->ArmorValue() - Goal->armorvalue );
+ pPlayer->armorclass &= ~(Goal->armorclass);
+
+ if (Goal->frags)
+ {
+ pPlayer->TF_AddFrags(Goal->frags);
+ }
+
+ pPlayer->RemoveAmmo( Goal->ammo_shells, TFC_AMMO_SHELLS );
+ pPlayer->RemoveAmmo( Goal->ammo_nails, TFC_AMMO_NAILS );
+ pPlayer->RemoveAmmo( Goal->ammo_rockets, TFC_AMMO_ROCKETS );
+ pPlayer->RemoveAmmo( Goal->ammo_cells, TFC_AMMO_CELLS );
+ pPlayer->RemoveAmmo( Goal->ammo_medikit, TFC_AMMO_MEDIKIT );
+ pPlayer->RemoveAmmo( Goal->ammo_detpack, TFC_AMMO_DETPACK );
+
+ // Detpacks
+//TFCTODO: this should be handled in the GiveAmmo functions..
+// if (pPlayer->ammo_detpack > pPlayer->maxammo_detpack)
+// pPlayer->ammo_detpack = pPlayer->maxammo_detpack;
+
+ // Grenades
+ pPlayer->RemoveAmmo( Goal->no_grenades_1, TFC_AMMO_GRENADES1 );
+ pPlayer->RemoveAmmo( Goal->no_grenades_2, TFC_AMMO_GRENADES2 );
+
+ // If they had a primed grenade, and they don't have any more of
+ // that type of grenade, unprime it and remove it.
+ if (pPlayer->m_Shared.GetStateFlags() & TFSTATE_GRENPRIMED)
+ {
+ if (pPlayer->GetAmmoCount( TFC_AMMO_GRENADES2 ) <= 0 || pPlayer->GetAmmoCount( TFC_AMMO_GRENADES1 ) <= 0)
+ {
+ pPlayer->m_Shared.RemoveStateFlags( TFSTATE_GRENPRIMED );
+ pPlayer->m_Shared.RemoveStateFlags( TFSTATE_GRENTHROWING );
+ pPlayer->bRemoveGrenade = TRUE;
+ }
+ }
+
+ BOOL puinvin = FALSE;
+ BOOL puinvis = FALSE;
+ BOOL puquad = FALSE;
+ BOOL purad = FALSE;
+ // Make sure we don't remove an effect another Goal is also supplying
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "item_tfgoal" );
+ while ( pEnt )
+ {
+ CTFGoalItem *pItem = dynamic_cast<CTFGoalItem*>( pEnt );
+ if ( pItem )
+ {
+ if ( (pItem->GetOwnerEntity() == pPlayer) && (pEnt != Goal) )
+ {
+ if (pItem->invincible_finished > 0)
+ puinvin = TRUE;
+ if (pItem->invisible_finished > 0)
+ puinvis = TRUE;
+ if (pItem->super_damage_finished > 0)
+ puquad = TRUE;
+ if (pItem->radsuit_finished > 0)
+ purad = TRUE;
+ }
+ }
+ pEnt = gEntList.FindEntityByClassname( pEnt, "item_tfgoal" );
+ }
+
+ // Remove all powerups
+ if ((Goal->invincible_finished > 0) && (!puinvin))
+ {
+ // if its a GoalItem, powerup was permanent, so we remove TFSTATE flag
+ pPlayer->m_Shared.RemoveStateFlags( TFSTATE_INVINCIBLE );
+ pPlayer->m_Shared.AddItemFlags( IT_INVULNERABILITY );
+ pPlayer->invincible_finished = gpGlobals->curtime + Goal->invincible_finished;
+ }
+ if ((Goal->invisible_finished > 0) && (!puinvis))
+ {
+ // if its a GoalItem, powerup was permanent, so we remove TFSTATE flag
+ pPlayer->m_Shared.RemoveStateFlags( TFSTATE_INVISIBLE );
+ pPlayer->m_Shared.AddItemFlags( IT_INVISIBILITY );
+ pPlayer->invisible_finished = gpGlobals->curtime + Goal->invisible_finished;
+ }
+ if ((Goal->super_damage_finished > 0) && (!puquad))
+ {
+ // if its a GoalItem, powerup was permanent, so we remove TFSTATE flag
+ pPlayer->m_Shared.RemoveStateFlags( TFSTATE_QUAD );
+ pPlayer->m_Shared.AddItemFlags( IT_QUAD );
+ pPlayer->super_damage_finished = gpGlobals->curtime + Goal->super_damage_finished;
+ }
+ if ((Goal->radsuit_finished > 0) && (!purad))
+ {
+ // if its a GoalItem, powerup was permanent, so we remove TFSTATE flag
+ pPlayer->m_Shared.RemoveStateFlags( TFSTATE_RADSUIT );
+ pPlayer->m_Shared.AddItemFlags( IT_SUIT );
+ pPlayer->radsuit_finished = gpGlobals->curtime + Goal->radsuit_finished;
+ }
+
+ // Now apply the pev->playerclass limitations & Redisplay Ammo counts
+ pPlayer->TeamFortress_CheckClassStats();
+ //W_SetCurrentAmmo ();
+
+ if (Goal->replacement_model != NULL_STRING)
+ {
+ // is it the same goal that gave us the replacment model?
+ if (pPlayer->replacement_model == Goal->replacement_model)
+ {
+ pPlayer->replacement_model = NULL_STRING;
+ pPlayer->replacement_model_body = 0;
+ pPlayer->replacement_model_skin = 0;
+ pPlayer->replacement_model_flags = 0;
+
+ pPlayer->TeamFortress_SetSkin();
+ }
+ }
+}
+
+
+//=========================================================================
+// Give the GoalItem to a Player.
+void tfgoalitem_GiveToPlayer(CTFGoalItem *Item, CTFCPlayer *AP, CTFGoal *Goal)
+{
+ MDEBUG(Warning( "Giving %s to %s\n", Item->GetEntityName().ToCStr(), AP->GetPlayerName()));
+
+ // Don't let it re-drop
+ if (Item->redrop_count)
+ Item->SetThink( NULL );
+
+ Item->SetOwnerEntity( AP );
+ // Remove it from the map
+ Item->FollowEntity( AP );
+ // Play carry animations
+ if (Item->GetModelName() != NULL_STRING)
+ {
+ Item->RemoveEffects( EF_NODRAW );
+
+ Item->SetSequence( Item->LookupSequence( "carried" ) );
+ if (Item->GetSequence() != -1)
+ {
+ Item->ResetSequenceInfo();
+ Item->m_flCycle = 0;
+ }
+ }
+
+ Item->AddSolidFlags( FSOLID_NOT_SOLID );
+
+ // Do the deeds on the player
+ if (Item->goal_activation & TFGI_GLOW)
+ AP->AddEffects( EF_BRIGHTLIGHT ); //TFCTODO: this used to be EF_BRIGHTFIELD.. make sure it's the same
+ if (Item->goal_activation & TFGI_SLOW)
+ AP->TeamFortress_SetSpeed();
+ if (Item->speed_reduction)
+ AP->TeamFortress_SetSpeed();
+
+ if (Item->goal_activation & TFGI_ITEMGLOWS)
+ {
+ Item->m_nRenderFX = kRenderFxNone;
+ Item->SetRenderColor( 0, 0, 0, 0 );
+ }
+
+ // Light up console icons
+ if (Item->items & IT_KEY1)
+ AP->m_Shared.AddItemFlags( IT_KEY1 );
+ if (Item->items & IT_KEY2)
+ AP->m_Shared.AddItemFlags( IT_KEY2 );
+ if (Item->items & IT_KEY3)
+ AP->m_Shared.AddItemFlags( IT_KEY3 );
+ if (Item->items & IT_KEY4)
+ AP->m_Shared.AddItemFlags( IT_KEY4 );
+
+ // Only do the results if we're allowed to
+ if (Goal != Item)
+ {
+ if (Goal->goal_result & TFGR_NO_ITEM_RESULTS)
+ {
+ Item->goal_state = TFGS_ACTIVE;
+ return;
+ }
+ }
+
+ MDEBUG(Warning("Doing item results...\n"));
+
+ // Prevent the Player from disguising themself if applicable
+ if (Item->goal_result & TFGR_REMOVE_DISGUISE)
+ AP->is_unableto_spy_or_teleport = 1;
+
+ // Do the Results, adding the bonuses
+ DoResults(Item, AP, TRUE);
+
+ // Check the Item Group Stuff
+ DoItemGroupWork(Item, AP);
+}
+
+
+//=========================================================================
+// Drop the item
+void tfgoalitem_drop(CTFGoalItem *Item, BOOL PAlive, CTFCPlayer *P)
+{
+ CBaseEntity *pOwner = Item->GetOwnerEntity();
+
+ // Backup origin for retry at the drop
+ if ( FBitSet( pOwner->GetFlags(), FL_DUCKING ) )
+ Item->redrop_origin = pOwner->GetAbsOrigin() + Vector(0, 0, 26);
+ else
+ Item->redrop_origin = pOwner->GetAbsOrigin() + Vector(0, 0, 8);
+ Item->redrop_count = 0;
+
+ Item->SetTouch( &CTFGoalItem::item_tfgoal_touch );
+ Item->DoDrop( Item->redrop_origin );
+
+ Item->SetOwnerEntity( P );
+ if (PAlive)
+ {
+ Vector vForward, vUp;
+ AngleVectors( P->EyeAngles(), &vForward, NULL, &vUp );
+ Item->SetAbsVelocity( (vForward * 400) + (vUp * 200) );
+
+ Item->SetTouch( NULL );
+ Item->SetThink( &CTFGoalItem::tfgoalitem_droptouch ); // give it 0.75 seconds
+ Item->SetNextThink( gpGlobals->curtime + 0.75 ); // and then set it's touch func
+
+ // Prevent the dropping player from picking it up for longer
+ Item->enemy = P;
+ Item->m_flDroppedAt = gpGlobals->curtime;
+ }
+}
+
+
+//=========================================================================
+// Remove the GoalItem from a Player.
+void tfgoalitem_RemoveFromPlayer(CTFGoalItem *Item, CTFCPlayer *AP, int iMethod)
+{
+ MDEBUG(Warning("Removing %s from %s\n", STRING(Item->pev->netname), STRING(AP->pev->netname)));
+
+ // If we have a teamcheck entity, use it instead
+ if ( Item->owned_by_teamcheck != NULL_STRING )
+ Item->owned_by = GetTeamCheckTeam( STRING(Item->owned_by_teamcheck) );
+
+ BOOL lighton = FALSE;
+ BOOL slowon = FALSE;
+ BOOL key1on = FALSE;
+ BOOL key2on = FALSE;
+ BOOL key3on = FALSE;
+ BOOL key4on = FALSE;
+ BOOL spyoff = FALSE;
+ // Remove the effects from the player
+ // Make sure we don't remove an effect another Goal is also supplying
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "item_tfgoal" );
+ while ( pEnt )
+ {
+ CTFGoalItem *pItem = dynamic_cast<CTFGoalItem*>( pEnt );
+ if ( pItem )
+ {
+ if ( (pItem->GetOwnerEntity() == AP) && (pEnt != Item) )
+ {
+ if (pItem->goal_activation & TFGI_GLOW)
+ lighton = TRUE;
+ if (pItem->goal_activation & TFGI_SLOW)
+ slowon = TRUE;
+
+ if (pItem->items & IT_KEY1)
+ key1on = TRUE;
+ if (pItem->items & IT_KEY2)
+ key2on = TRUE;
+ if (pItem->items & IT_KEY3)
+ key3on = TRUE;
+ if (pItem->items & IT_KEY4)
+ key4on = TRUE;
+
+ if (pItem->goal_result & TFGR_REMOVE_DISGUISE)
+ spyoff = TRUE;
+ }
+ }
+ pEnt = gEntList.FindEntityByClassname( pEnt, "item_tfgoal" );
+ }
+
+ // Check Powerups too
+ if (!lighton)
+ {
+ if (AP->invincible_finished > gpGlobals->curtime + 3)
+ lighton = TRUE;
+ else if (AP->super_damage_finished > gpGlobals->curtime + 3)
+ lighton = TRUE;
+ }
+ if (!lighton)
+ {
+ //TFCTODO: Add support for EF_BRIGHTFIELD if necessary.
+ //AP->RemoveEffects( EF_BRIGHTFIELD );
+ AP->RemoveEffects( EF_BRIGHTLIGHT );
+ }
+ if (Item->goal_activation & TFGI_ITEMGLOWS)
+ {
+ Item->m_nRenderFX = kRenderFxGlowShell;
+
+ if (Item->owned_by > 0 && Item->owned_by <= 4)
+ Item->m_clrRender = Vector255ToRGBColor( rgbcolors[Item->owned_by] );
+ else
+ Item->m_clrRender = Vector255ToRGBColor( rgbcolors[0] );
+ Item->SetRenderColorA( 100 ); // Shell size
+ }
+
+ // Remove the Spy prevention
+ if (!spyoff)
+ AP->is_unableto_spy_or_teleport = FALSE;
+ // Remove the lit console key icons
+ if (!key1on)
+ AP->m_Shared.RemoveItemFlags( IT_KEY1 );
+ if (!key2on)
+ AP->m_Shared.RemoveItemFlags( IT_KEY2 );
+ if (!key3on)
+ AP->m_Shared.RemoveItemFlags( IT_KEY3 );
+ if (!key4on)
+ AP->m_Shared.RemoveItemFlags( IT_KEY4 );
+
+ // Remove AP Modifications
+ // Go through all the players and do any results
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CTFCPlayer *pPlayer = ToTFCPlayer( UTIL_PlayerByIndex( i ) );
+ if ( pPlayer && IsAffectedBy(Item, pPlayer, AP) )
+ RemoveResults(Item, pPlayer);
+ }
+
+ // Setup animations
+ if (Item->GetModelName() != NULL_STRING)
+ {
+ Item->SetSequence( Item->LookupSequence( "not_carried" ) );
+ if (Item->GetSequence() != -1)
+ {
+ Item->ResetSequenceInfo();
+ Item->m_flCycle = 0;
+ }
+ }
+
+ // Return it to the starting point if the flag is set
+ if (iMethod == GI_DROP_PLAYERDEATH || iMethod == GI_DROP_PLAYERDROP)
+ {
+ // Do messages
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
+ if ( !pPlayer )
+ continue;
+
+ if (pPlayer->GetTeamNumber() == Item->owned_by)
+ {
+ if (Item->team_drop != NULL_STRING)
+ UTIL_ShowMessage( STRING(Item->team_drop), pPlayer );
+ if (Item->netname_team_drop != NULL_STRING)
+ ClientPrint( pPlayer, HUD_PRINTNOTIFY, STRING(Item->netname_team_drop), AP->GetPlayerName() );
+ // Old printing
+ if (Item->org_team_drop != NULL_STRING)
+ ClientPrint( pPlayer, HUD_PRINTCENTER, STRING(Item->org_team_drop) );
+ }
+ else // (pPlayer->GetTeamNumber() != Item->owned_by)
+ {
+ if (Item->non_team_drop != NULL_STRING)
+ UTIL_ShowMessage( STRING(Item->non_team_drop), pPlayer );
+ if (Item->netname_non_team_drop != NULL_STRING)
+ ClientPrint( pPlayer, HUD_PRINTNOTIFY, STRING(Item->netname_non_team_drop), AP->GetPlayerName() );
+ // Old printing
+ if (Item->org_non_team_drop != NULL_STRING)
+ ClientPrint( pPlayer, HUD_PRINTCENTER, STRING(Item->org_non_team_drop) );
+ }
+ }
+
+ // Drop it if the flag is set
+ if (Item->goal_activation & TFGI_RETURN_DROP)
+ {
+ CTimer *pTimer = Timer_CreateTimer( Item, TF_TIMER_RETURNITEM );
+ pTimer->m_flNextThink = gpGlobals->curtime + 0.5f;
+ if (iMethod == GI_DROP_PLAYERDEATH)
+ pTimer->weapon = GI_RET_DROP_DEAD;
+ else
+ pTimer->weapon = GI_RET_DROP_LIVING;
+ }
+ else if (Item->goal_activation & TFGI_DROP)
+ {
+ if ( (iMethod == GI_DROP_PLAYERDROP) && (Item->goal_activation & TFGI_CANBEDROPPED) )
+ tfgoalitem_drop(Item, TRUE, AP);
+ else
+ tfgoalitem_drop(Item, FALSE, AP);
+ }
+ else
+ {
+ // Remove the Item
+ Item->SetOwnerEntity( NULL );
+ Item->SetNextThink( gpGlobals->curtime );
+ Item->SetThink( &CBaseEntity::SUB_Remove );
+ AP->TeamFortress_SetSpeed();
+ return;
+ }
+
+ Item->SetOwnerEntity( NULL );
+ Item->RemoveFlag( FL_ONGROUND );
+ UTIL_SetSize(Item, Item->goal_min, Item->goal_max);
+
+ AP->TeamFortress_SetSpeed();
+ }
+ else if (iMethod == GI_DROP_REMOVEGOAL)
+ {
+ Item->SetOwnerEntity( NULL );
+
+ if (Item->goal_activation & TFGI_RETURN_GOAL)
+ {
+ CTimer *pTimer = Timer_CreateTimer( Item, TF_TIMER_RETURNITEM );
+ pTimer->m_flNextThink = gpGlobals->curtime + 0.5;
+ pTimer->weapon = GI_RET_GOAL;
+ AP->TeamFortress_SetSpeed();
+ return;
+ }
+
+ // Don't remove it, since it may be given away again later
+ Item->AddSolidFlags( FSOLID_NOT_SOLID );
+ Item->AddEffects( EF_NODRAW );
+ Item->StopFollowingEntity();
+ AP->TeamFortress_SetSpeed();
+ }
+}
+
+
+//=========================================================================
+// Apply modifications to the Player passed in
+void Apply_Results(CTFGoal *Goal, CTFCPlayer *Player, CTFCPlayer *AP, BOOL bAddBonuses)
+{
+ MDEBUG( Warning("Applying Results from %s to %s\n", STRING(Goal->pev->netname), STRING(Player->pev->netname)) );
+
+ // If this is a goalitem, record the fact that this player
+ // has been affected by it.
+ if ( FClassnameIs(Goal, "item_tfgoal") )
+ Player->item_list |= Goal->item_list;
+
+ if (Player == AP)
+ {
+ // Alter the team score
+ if (Goal->count != 0 && Player->GetTeamNumber() > 0)
+ {
+ TeamFortress_TeamIncreaseScore(Player->GetTeamNumber(), Goal->count);
+ // Display short team scores
+ //TeamFortress_TeamShowScores(FALSE, NULL);
+ }
+ }
+
+ // Apply Stats, only if told to
+ if (bAddBonuses)
+ {
+ MDEBUG( Warning("Adding bonuses.\n") );
+ // Some results are not applied to dead players
+ if ( Player->IsAlive() )
+ {
+ if (Goal->GetHealth() > 0)
+ Player->TakeHealth(Goal->GetHealth(), 0);
+ if (Goal->GetHealth() < 0)
+ {
+ // Make sure we don't gib them, because it creates too many entities if
+ // a lot of players are affected by this Goal.
+ Player->TakeDamage( CTakeDamageInfo( Goal, Goal, (0 - Goal->GetHealth()), DMG_IGNOREARMOR | DMG_NEVERGIB ) );
+ }
+ }
+
+ // The player may be dead now, so check again
+ if ( Player->IsAlive() )
+ {
+ if (Goal->armortype > 0)
+ Player->armortype = Goal->armortype;
+ else if (Goal->armorvalue > 0)
+ Player->armortype = Player->armor_allowed;
+ Player->IncrementArmorValue( Goal->armorvalue );
+ if (Goal->armorclass > 0)
+ Player->armorclass = Goal->armorclass;
+
+ Player->GiveAmmo( Goal->ammo_shells, TFC_AMMO_SHELLS );
+ Player->GiveAmmo( Goal->ammo_nails, TFC_AMMO_NAILS );
+ Player->GiveAmmo( Goal->ammo_rockets, TFC_AMMO_ROCKETS );
+ Player->GiveAmmo( Goal->ammo_cells, TFC_AMMO_CELLS );
+ Player->GiveAmmo( Goal->ammo_medikit, TFC_AMMO_MEDIKIT );
+ Player->GiveAmmo( Goal->ammo_detpack, TFC_AMMO_DETPACK );
+
+#ifdef TFCTODO // do this when grenades are implemented.
+ // Grenades
+ if ( Player->tp_grenades_1 != GR_TYPE_NONE )
+ Player->no_grenades_1 += Goal->no_grenades_1;
+ if ( Player->tp_grenades_2 != GR_TYPE_NONE )
+ Player->no_grenades_2 += Goal->no_grenades_2;
+
+ // If they had a primed grenade, and they don't have any more of
+ // that type of grenade, unprime it and remove it.
+ if (Player->tfstate & TFSTATE_GRENPRIMED)
+ {
+ if ( (Player->m_iPrimedGrenType == 1 && Player->no_grenades_1 <= 0 && Goal->no_grenades_1 < 0) ||
+ (Player->m_iPrimedGrenType == 2 && Player->no_grenades_2 <= 0 && Goal->no_grenades_2 < 0) )
+ {
+ Player->tfstate &= ~TFSTATE_GRENPRIMED;
+ Player->tfstate &= ~TFSTATE_GRENTHROWING;
+ Player->bRemoveGrenade = TRUE;
+ }
+ }
+#endif
+
+ // Apply any powerups
+ if (Goal->invincible_finished > 0)
+ {
+ Player->m_Shared.AddItemFlags( IT_INVULNERABILITY );
+ Player->invincible_finished = gpGlobals->curtime + Goal->invincible_finished;
+ // if its a GoalItem, powerup is permanent, so we use TFSTATE flags
+ if ( FClassnameIs(Goal, "item_tfgoal") )
+ {
+ Player->m_Shared.AddStateFlags( TFSTATE_INVINCIBLE );
+ Player->invincible_finished = gpGlobals->curtime + 666;
+ }
+
+ // Force it to recalculate shell color
+ Player->m_nRenderFX = kRenderFxNone;
+ }
+ if (Goal->invisible_finished > 0)
+ {
+ Player->m_Shared.AddItemFlags( IT_INVISIBILITY );
+ Player->invisible_finished = gpGlobals->curtime + Goal->invisible_finished;
+ // if its a GoalItem, powerup is permanent, so we use TFSTATE flags
+ if ( FClassnameIs(Goal, "item_tfgoal") )
+ {
+ Player->m_Shared.AddStateFlags( TFSTATE_INVISIBLE );
+ Player->invisible_finished = gpGlobals->curtime + 666;
+ }
+
+ // Force it to recalculate shell color
+ Player->m_nRenderFX = kRenderFxNone;
+ }
+ if (Goal->super_damage_finished > 0)
+ {
+ Player->m_Shared.AddItemFlags( IT_QUAD );
+ Player->super_damage_finished = gpGlobals->curtime + Goal->super_damage_finished;
+ // if its a GoalItem, powerup is permanent, so we use TFSTATE flags
+ if ( FClassnameIs(Goal, "item_tfgoal") )
+ {
+ Player->m_Shared.AddStateFlags( TFSTATE_QUAD );
+ Player->super_damage_finished = gpGlobals->curtime + 666;
+ }
+
+ // Force it to recalculate shell color
+ Player->m_nRenderFX = kRenderFxNone;
+ }
+ if (Goal->radsuit_finished > 0)
+ {
+ Player->m_Shared.AddItemFlags( IT_SUIT );
+ Player->radsuit_finished = gpGlobals->curtime + Goal->radsuit_finished;
+ // if its a GoalItem, powerup is permanent, so we use TFSTATE flags
+ if ( FClassnameIs(Goal, "item_tfgoal") )
+ {
+ Player->m_Shared.AddStateFlags( TFSTATE_RADSUIT );
+ Player->radsuit_finished = gpGlobals->curtime + 666;
+ }
+ }
+ }
+
+ // These results are applied to dead and living players
+ Player->lives += Goal->lives;
+
+ if ( Goal->frags != 0 )
+ Player->TF_AddFrags(Goal->frags);
+
+ // Now apply the m_Shared.GetPlayerClass() limitations & Redisplay Ammo counts
+ Player->TeamFortress_CheckClassStats();
+ }
+#ifdef MAP_DEBUG
+ else
+ ALERT( at_console, "NOT Adding bonuses.\n" );
+#endif
+
+ // If the Goal resets Spy skin/color then do it
+ if (Player->m_Shared.GetPlayerClass() == PC_SPY && Goal->goal_result & TFGR_REMOVE_DISGUISE)
+ {
+ //TFCTODO: looks like this isn't actually used anywhere.
+ //Player->immune_to_check = gpGlobals->curtime + 10;
+ Player->Spy_RemoveDisguise();
+ }
+
+ // If there's a GoalItem for this goal, give it to the player
+ // GoalItems use "items" for the console lights... so don't do it for items.
+ if ( Goal->items != 0 && !FClassnameIs(Goal,"item_tfgoal") )
+ {
+ // Find the item
+ CTFGoalItem *pItem = Finditem(Goal->items);
+ // Don't give them the item if it's the item that just affected them
+ if (pItem != NULL && pItem != Goal)
+ tfgoalitem_GiveToPlayer(pItem, Player, Goal);
+ }
+
+ // If this goal removes an item from the player, remove it
+ if (Goal->axhitme != 0)
+ {
+ CTFGoalItem *pItem = Finditem(Goal->axhitme);
+ if (pItem->GetOwnerEntity() == Player)
+ tfgoalitem_RemoveFromPlayer(pItem, Player, GI_DROP_REMOVEGOAL);
+ }
+
+ // if this goal removes a group of items from the player, remove them
+ if (Goal->remove_item_group != 0)
+ {
+ // Find all items in the group
+ CTFGoalItem *pItemToRemove = NULL;
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "item_tfgoal" );
+ while ( pEnt )
+ {
+ CTFGoalItem *pItem = dynamic_cast<CTFGoalItem*>( pEnt );
+ if ( pItem )
+ {
+ if ( (pItem->group_no == Goal->remove_item_group) && (pItem->GetOwnerEntity() == Player) )
+ pItemToRemove = pItem;
+
+ // need to cycle before removing it from the player, because it may be destroyed
+ pEnt = gEntList.FindEntityByClassname( pEnt, "item_tfgoal" );
+ if (pItemToRemove)
+ {
+ tfgoalitem_RemoveFromPlayer(pItemToRemove, Player, GI_DROP_REMOVEGOAL);
+ pItemToRemove = NULL;
+ }
+ }
+ }
+ }
+
+ // Display all the item statuses
+ Player->DisplayLocalItemStatus(Goal);
+
+ // Destroy buildings
+ if (Goal->goal_result & TFGR_DESTROY_BUILDINGS)
+ {
+ Player->no_sentry_message = TRUE;
+ Player->no_dispenser_message = TRUE;
+ Player->no_entry_teleporter_message = TRUE;
+ Player->no_exit_teleporter_message = TRUE;
+ Player->Engineer_RemoveBuildings();
+ Player->TeamFortress_RemoveLiveGrenades();
+ Player->TeamFortress_RemoveRockets();
+ Player->RemovePipebombs();
+
+ // is the player setting a detpack?
+ if ( Player->is_detpacking )
+ {
+ Player->TeamFortress_DetpackStop();
+ }
+ else
+ {
+ // does the player have a detpack in the world?
+ if ( Player->TeamFortress_RemoveDetpacks() )
+ {
+ Player->GiveAmmo( 1, TFC_AMMO_DETPACK );
+ }
+ }
+ }
+
+ // Force respawns
+ if (Goal->goal_result & TFGR_FORCE_RESPAWN)
+ {
+ // Only if they're alive
+ if ( Player->IsAlive() )
+ Player->ForceRespawn();
+ }
+
+ if (Goal->replacement_model != NULL_STRING)
+ {
+ // if we don't already have a replacement_model
+ if ( !Player->replacement_model )
+ {
+ Player->replacement_model = Goal->replacement_model;
+ Player->replacement_model_body = Goal->replacement_model_body;
+ Player->replacement_model_skin = Goal->replacement_model_skin;
+ Player->replacement_model_flags = Goal->replacement_model_flags;
+
+ Player->TeamFortress_SetSkin();
+ }
+ }
+}
+
+
+//=========================================================================
+// Use (Triggered) function for Goals
+void EndRound( CTFGoal *pGoal )
+{
+ // fade everyones screen
+ color32 clr;
+ memset( &clr, 0, sizeof( clr ) );
+ UTIL_ScreenFadeAll( clr, 0.3, pGoal->m_flEndRoundTime, FFADE_MODULATE | FFADE_OUT );
+
+ // Display Long TeamScores to everyone
+ TeamFortress_TeamShowScores(TRUE, NULL);
+
+ int highestScore = -99990;
+ int winningTeam = 1;
+ const char *winnerMsg = "";
+ // Only do team score check if the win one is set
+ if ( pGoal->m_iszEndRoundMsg_Team1_Win != NULL_STRING )
+ {
+ // work out which team won
+ for ( int i = 1; i <= 4; i++ )
+ {
+ int teamScore = TeamFortress_TeamGetScoreFrags( i );
+
+ if ( teamScore > highestScore )
+ {
+ winningTeam = i;
+ highestScore = teamScore;
+ }
+ }
+
+ // work out the winning msg
+ switch ( winningTeam )
+ {
+ case 1: winnerMsg = STRING(pGoal->m_iszEndRoundMsg_Team1_Win); break;
+ case 2: winnerMsg = STRING(pGoal->m_iszEndRoundMsg_Team2_Win); break;
+ case 3: winnerMsg = STRING(pGoal->m_iszEndRoundMsg_Team3_Win); break;
+ case 4: winnerMsg = STRING(pGoal->m_iszEndRoundMsg_Team4_Win); break;
+ };
+ }
+
+ // Prevent players from moving and shooting
+ no_cease_fire_text = TRUE;
+ cease_fire = TRUE;
+
+ // Send out the messages
+ CTFCPlayer *client = NULL;
+ while ( ((client = (CTFCPlayer*)gEntList.FindEntityByClassname( client, "player" )) != NULL) && (!FNullEnt(client->edict())) )
+ {
+ if ( !client )
+ continue;
+
+ // Freeze all the players
+ if ( client->IsObserver() == FALSE )
+ {
+ //TFCTODO implement something for this?
+ // iuser4 stops firing on the clients
+ //client->pev->iuser4 = TRUE;
+
+ //TFCTODO: implement HIDEHUD_WEAPONS, or is it the same as HIDEHUD_WEAPONSELECTION?
+ //client->m_Local.m_iHideHUD |= (HIDEHUD_HEALTH | HIDEHUD_WEAPONS);
+ client->m_Local.m_iHideHUD |= (HIDEHUD_HEALTH | HIDEHUD_WEAPONSELECTION);
+
+ client->m_Shared.AddStateFlags( TFSTATE_CANT_MOVE );
+ }
+ client->TeamFortress_SetSpeed();
+
+ // Owned by and Non owned by take precedence
+ if ( pGoal->m_iszEndRoundMsg_OwnedBy != NULL_STRING && ( client->GetTeamNumber() == pGoal->owned_by ) )
+ {
+ UTIL_ShowMessage( STRING( pGoal->m_iszEndRoundMsg_OwnedBy ), client );
+ }
+ else if ( pGoal->m_iszEndRoundMsg_NonOwnedBy != NULL_STRING && ( client->GetTeamNumber() != pGoal->owned_by ) )
+ {
+ UTIL_ShowMessage( STRING( pGoal->m_iszEndRoundMsg_NonOwnedBy ), client );
+ }
+ else if ( pGoal->m_iszEndRoundMsg_Team1_Win != NULL_STRING && client->GetTeamNumber() == winningTeam )
+ {
+ UTIL_ShowMessage( winnerMsg, client );
+ }
+ else
+ {
+ const char *loserMsg = "";
+ // work out the loser message and send it to them
+ if ( pGoal->m_iszEndRoundMsg_Team1_Win != NULL_STRING )
+ {
+ switch ( client->GetTeamNumber() )
+ {
+ case 1: loserMsg = STRING(pGoal->m_iszEndRoundMsg_Team1_Lose); break;
+ case 2: loserMsg = STRING(pGoal->m_iszEndRoundMsg_Team2_Lose); break;
+ case 3: loserMsg = STRING(pGoal->m_iszEndRoundMsg_Team3_Lose); break;
+ case 4: loserMsg = STRING(pGoal->m_iszEndRoundMsg_Team4_Lose); break;
+ };
+ }
+ else
+ {
+ switch ( client->GetTeamNumber() )
+ {
+ case 1: loserMsg = STRING(pGoal->m_iszEndRoundMsg_Team1); break;
+ case 2: loserMsg = STRING(pGoal->m_iszEndRoundMsg_Team2); break;
+ case 3: loserMsg = STRING(pGoal->m_iszEndRoundMsg_Team3); break;
+ case 4: loserMsg = STRING(pGoal->m_iszEndRoundMsg_Team4); break;
+ };
+ }
+
+ UTIL_ShowMessage( loserMsg, client );
+ }
+ }
+
+ // Create a timer to remove the EndRound in the specified time
+ CTimer *pTimer = Timer_CreateTimer( pGoal, TF_TIMER_ENDROUND );
+ //pTimer->SetThink( &CBaseEntity::EndRoundEnd );
+ pTimer->m_flNextThink = gpGlobals->curtime + pGoal->m_flEndRoundTime;
+}
+
+
+//=========================================================================
+// Inactivate a Timer/Goal
+void InactivateGoal(CTFGoal *Goal)
+{
+ MDEBUG( Warning("Inactivating %s", STRING(Goal->pev->netname)) );
+
+ if (Goal->goal_state == TFGS_ACTIVE)
+ {
+ MDEBUG( Warning("... succeeded.\n") );
+ // Not a timer goal
+ if (Goal->Classify() != CLASS_TFGOAL_TIMER)
+ {
+ if ( Goal->goal_activation & TFGI_SOLID && (Goal->Classify() == CLASS_TFGOAL || Goal->Classify() == CLASS_TFGOAL_ITEM) )
+ Goal->SetSolid( SOLID_BBOX );
+ else
+ Goal->AddSolidFlags( FSOLID_TRIGGER );
+ }
+
+ Goal->goal_state = TFGS_INACTIVE;
+ const char *pModel = STRING( Goal->GetModelName() );
+ if (pModel && pModel[0] != '*')
+ Goal->RemoveEffects( EF_NODRAW );
+ }
+#ifdef MAP_DEBUG
+ else
+ Warning("... failed. Goal is %s\n", g_szStates[Goal->goal_state]);
+#endif
+}
+
+
+//=========================================================================
+// Restores a Timer/Goal
+void RestoreGoal(CTFGoal *Goal)
+{
+ MDEBUG( Warning("Attempting to Restore %s", STRING(Goal->pev->netname)) );
+
+ if (Goal->goal_state == TFGS_REMOVED)
+ {
+ MDEBUG( Warning("... succeeded.\n") );
+
+ // Not a timer goal
+ if (Goal->search_time == 0)
+ {
+ if (Goal->goal_activation & TFGI_SOLID && FClassnameIs(Goal, "item_tfgoal") )
+ Goal->SetSolid( SOLID_BBOX );
+ else
+ Goal->AddSolidFlags( FSOLID_TRIGGER );
+ }
+ else
+ Goal->SetNextThink( gpGlobals->curtime + Goal->search_time );
+
+ Goal->goal_state = TFGS_INACTIVE;
+ const char *pModel = STRING(Goal->GetModelName());
+ if (pModel[0] != '*')
+ Goal->RemoveEffects( EF_NODRAW );
+ }
+#ifdef MAP_DEBUG
+ else
+ Warning("... failed. Goal is %s\n", g_szStates[Goal->goal_state]);
+#endif
+}
+
+
+//=========================================================================
+// Do all the activation/inactivation/etc of Goal Groups
+void DoGroupWork(CTFGoal *Goal, CTFCPlayer *AP)
+{
+#ifdef MAP_DEBUG
+ if (Goal->all_active || Goal->activate_group_no || Goal->inactivate_group_no || Goal->restore_group_no || Goal->remove_group_no)
+ Warning("Doing Groupwork...\n");
+#endif
+
+ // Check all goals activated flag
+ if (Goal->all_active != 0)
+ {
+ if (Goal->last_impulse == 0)
+ {
+ // No goal specified in .last_impulse. Print error.
+ Warning("Goal %d has .all_active specified, but no .last_impulse\n", Goal->goal_no);
+ }
+ else
+ {
+ MDEBUG( Warning("All Active Group Check.\n") );
+
+ BOOL bAllSet = TRUE;
+ // Find all goals
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "info_tfgoal" );
+ while ( pEnt && bAllSet)
+ {
+ CTFGoal *pGoal = dynamic_cast< CTFGoal* >( pEnt );
+ if ( pGoal )
+ {
+ if (pGoal->group_no == Goal->all_active && pGoal->goal_state != TFGS_ACTIVE)
+ bAllSet = FALSE;
+ }
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "info_tfgoal" );
+ }
+
+ // If all goals in this group are activated, do it
+ if (bAllSet)
+ {
+ MDEBUG( Warning("All Active, Activating last_impulse.\n") );
+
+ CTFGoal *pGoal = Findgoal(Goal->last_impulse);
+ if (pGoal)
+ DoResults(pGoal, AP, (Goal->goal_result & TFGR_ADD_BONUSES));
+ }
+ #ifdef MAP_DEBUG
+ else
+ {
+ Warning("Not all Active.\n");
+ }
+ #endif
+ }
+ }
+
+ // Check Activate all in the group flag
+ if (Goal->activate_group_no != 0)
+ {
+ // Find all goals
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "info_tfgoal" );
+ while ( pEnt )
+ {
+ CTFGoal *pGoal = dynamic_cast< CTFGoal* >( pEnt );
+ if ( pGoal )
+ {
+ if (pGoal->group_no == Goal->activate_group_no)
+ ActivateDoResults(pGoal, AP, Goal);
+ }
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "info_tfgoal" );
+ }
+ }
+
+ // Check Inactivate all in the group flag
+ if (Goal->inactivate_group_no != 0)
+ {
+ // Find all goals
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "info_tfgoal" );
+ while ( pEnt )
+ {
+ CTFGoal *pGoal = dynamic_cast< CTFGoal* >( pEnt );
+ if ( pGoal )
+ {
+ if (pGoal->group_no == Goal->inactivate_group_no)
+ InactivateGoal(pGoal);
+ }
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "info_tfgoal" );
+ }
+ }
+
+ // Check Remove all in the group flag
+ if (Goal->remove_group_no != 0)
+ {
+ // Find all goals
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "info_tfgoal" );
+ while ( pEnt )
+ {
+ CTFGoal *pGoal = dynamic_cast< CTFGoal* >( pEnt );
+ if ( pGoal )
+ {
+ if (pGoal->group_no == Goal->remove_group_no)
+ RemoveGoal(pGoal);
+ }
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "info_tfgoal" );
+ }
+ }
+
+ // Check Restore all in the group flag
+ if (Goal->restore_group_no != 0)
+ {
+ // Find all goals
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "info_tfgoal" );
+ while ( pEnt )
+ {
+ CTFGoal *pGoal = dynamic_cast< CTFGoal* >( pEnt );
+ if ( pGoal )
+ {
+ if (pGoal->group_no == Goal->restore_group_no)
+ RestoreGoal(pGoal);
+ }
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "info_tfgoal" );
+ }
+ }
+
+#ifdef MAP_DEBUG
+ if (Goal->remove_spawngroup || Goal->restore_spawngroup)
+ Warning("Doing SpawnGroupwork...\n");
+#endif
+}
+
+
+
+//=========================================================================
+// Do all the activation/inactivation/etc of individual Goals
+void DoGoalWork(CTFGoal *Goal, CTFCPlayer *AP)
+{
+#ifdef MAP_DEBUG
+ if (Goal->activate_goal_no || Goal->inactivate_goal_no || Goal->restore_goal_no || Goal->remove_goal_no || Goal->return_item_no)
+ Warning("Doing Goalwork...\n");
+#endif
+
+ // If another goal should be activated, activate it
+ if (Goal->activate_goal_no != 0)
+ {
+ CTFGoal *pFoundGoal = Findgoal(Goal->activate_goal_no);
+ if (pFoundGoal)
+ ActivateDoResults(pFoundGoal, AP, Goal);
+ }
+
+ // If another goal should be inactivated, inactivate it
+ if (Goal->inactivate_goal_no != 0)
+ {
+ CTFGoal *pFoundGoal = Findgoal(Goal->inactivate_goal_no);
+ if (pFoundGoal)
+ InactivateGoal(pFoundGoal);
+ }
+
+ // If another goal should be restored, restore it
+ if (Goal->restore_goal_no != 0)
+ {
+ CTFGoal *pFoundGoal = Findgoal(Goal->restore_goal_no);
+ if (pFoundGoal)
+ RestoreGoal(pFoundGoal);
+ }
+
+ // If another goal should be removed, remove it
+ if (Goal->remove_goal_no != 0)
+ {
+ CTFGoal *pFoundGoal = Findgoal(Goal->remove_goal_no);
+ if (pFoundGoal)
+ RemoveGoal(pFoundGoal);
+ }
+
+ // If a GoalItem should be returned, return it
+ if (Goal->return_item_no != 0)
+ {
+ CTFGoalItem *pFoundGoal = Finditem(Goal->return_item_no);
+ if (pFoundGoal)
+ {
+ CBaseEntity *pOwner = pFoundGoal->GetOwnerEntity();
+ Assert( dynamic_cast<CTFCPlayer*>( pOwner ) );
+ if (pFoundGoal->goal_state == TFGS_ACTIVE)
+ tfgoalitem_RemoveFromPlayer(pFoundGoal, (CTFCPlayer*)pOwner, GI_DROP_REMOVEGOAL);
+
+ // Setup a ReturnItem timer
+ CTimer *pTimer = Timer_CreateTimer( pFoundGoal, TF_TIMER_RETURNITEM );
+ pTimer->weapon = GI_RET_TIME;
+ pTimer->m_flNextThink = gpGlobals->curtime + 0.1;
+
+ pFoundGoal->AddSolidFlags( FSOLID_NOT_SOLID );
+ }
+ }
+
+#ifdef MAP_DEBUG
+ if (Goal->remove_spawnpoint || Goal->restore_spawnpoint)
+ Warning("Doing Spawnwork...\n");
+#endif
+
+ // Spawnpoint behaviour
+ if (Goal->remove_spawnpoint != 0)
+ {
+ CTFSpawn *pFoundGoal = Findteamspawn(Goal->remove_spawnpoint);
+ if (pFoundGoal)
+ InactivateSpawn(pFoundGoal);
+ }
+
+ if (Goal->restore_spawnpoint != 0)
+ {
+ CTFSpawn *pFoundGoal = Findteamspawn(Goal->restore_spawnpoint);
+ if (pFoundGoal)
+ {
+ if (pFoundGoal->goal_state == TFGS_REMOVED)
+ ActivateSpawn(pFoundGoal);
+ }
+ }
+}
+
+
+//=========================================================================
+// Do all the activation/removal of Quake Triggers
+void DoTriggerWork(CTFGoal *Goal, CTFCPlayer *AP)
+{
+ // remove killtargets
+ if (Goal->killtarget != NULL_STRING)
+ {
+ MDEBUG( Warning("Doing Triggerwork...\n") );
+ MDEBUG( Warning("Killing Target(s): %s\n", STRING(Goal->killtarget)) );
+
+ CBaseEntity *pentKillTarget = gEntList.FindEntityByName( NULL, STRING(Goal->killtarget) );
+ while ( pentKillTarget )
+ {
+ UTIL_Remove( pentKillTarget );
+ pentKillTarget = gEntList.FindEntityByName( pentKillTarget, STRING(Goal->killtarget) );
+ }
+ }
+
+ // fire targets
+ if (Goal->target != NULL_STRING)
+ {
+ MDEBUG( Warning("Doing Triggerwork...\n") );
+ MDEBUG( ALERT( at_console, "Activating Target(s): %s\n", STRING(Goal->pev->target) ) );
+
+ CBaseEntity *pentTarget = gEntList.FindEntityByName( NULL, STRING(Goal->target) );
+ while ( pentTarget )
+ {
+ CBaseEntity *pTarget = pentTarget;
+ if ( !(pTarget->GetFlags() & FL_KILLME) )
+ pTarget->Use( AP, Goal, USE_TOGGLE, 0 );
+ pentTarget = gEntList.FindEntityByName( pentTarget, STRING(Goal->target) );
+ }
+ }
+}
+
+
+//=========================================================================
+// Setup the way this Timer/Goal/Item will respawn
+void SetupRespawn(CTFGoal *pGoal)
+{
+ MDEBUG( Warning("Setting up Respawn...\n") );
+
+ pGoal->m_bAddBonuses = FALSE;
+
+ // Check status of respawn for this goal
+ // Single Activation, do nothing
+ if (pGoal->goal_result & TFGR_SINGLE)
+ {
+ RemoveGoal(pGoal);
+ return;
+ }
+
+ // Timer Goal?
+ if (pGoal->Classify() == CLASS_TFGOAL_TIMER)
+ {
+ InactivateGoal(pGoal);
+ pGoal->SetThink( &CTFGoal::tfgoal_timer_tick );
+ pGoal->SetNextThink( gpGlobals->curtime + pGoal->search_time );
+ return;
+ }
+
+ // Respawn Activation, set up respawn
+ if (pGoal->wait > 0)
+ {
+ pGoal->SetThink(&CTFGoal::DoRespawn);
+ pGoal->SetNextThink( gpGlobals->curtime + pGoal->wait );
+ return;
+ }
+ // Permanently active goal?
+ else if (pGoal->wait == -1)
+ return;
+
+ // Otherwise, it's a Multiple Goal
+ InactivateGoal(pGoal);
+}
+
+
+//=========================================================================
+// Do the results for the Timer/Goal/Item
+void DoResults(CTFGoal *Goal, CTFCPlayer *AP, BOOL bAddBonuses)
+{
+ // Can't activate during PreMatch time
+ if ( (TFCGameRules()->IsInPreMatch()) && (Goal->Classify() != CLASS_TFGOAL_TIMER) )
+ return;
+
+ // Is the goal already activated?
+ // This check is needed for goals which are being activated by other goals
+ if (Goal->goal_state == TFGS_ACTIVE)
+ return;
+
+ // Delayed Activation?
+ if (Goal->delay_time > 0 && Goal->goal_state != TFGS_DELAYED)
+ {
+ MDEBUG( Warning("Delaying Results of %s\n", STRING(Goal->edict()->netname)) );
+
+ Goal->goal_state = TFGS_DELAYED;
+ Timer_CreateTimer( Goal, TF_TIMER_DELAYEDGOAL );
+ Goal->enemy = AP;
+ Goal->SetThink( &CTFGoal::DelayedResult );
+ Goal->SetNextThink( gpGlobals->curtime + Goal->delay_time );
+ Goal->weapon = bAddBonuses;
+ return;
+ }
+
+ // If we have a teamcheck entity, use it instead
+ if ( Goal->owned_by_teamcheck != NULL_STRING )
+ Goal->owned_by = GetTeamCheckTeam( STRING(Goal->owned_by_teamcheck) );
+
+ Goal->goal_state = TFGS_INACTIVE;
+
+
+ // if it's a TF goal, removes it's model
+ if ( Goal->Classify() == CLASS_TFGOAL || Goal->Classify() == CLASS_TFGOAL_TIMER )
+ Goal->AddEffects( EF_NODRAW );
+
+#ifdef MAP_DEBUG
+ Warning("---= Activation =---\n");
+ if (AP)
+ Warning("Goal: %s\nAP : %s\n", STRING(Goal->edict()->netname), AP->GetPlayerName());
+ else
+ Warning("Goal: %s\nAP : NONE\n", STRING(Goal->edict()->netname));
+ if (bAddBonuses)
+ Warning(" adding bonuses\n-=================-\n");
+ else
+ Warning("NOT adding bonuses\n-=================-\n");
+#endif
+
+ // Make the sound
+ if (Goal->noise != NULL_STRING)
+ {
+ Goal->EmitSound( STRING( Goal->noise ) );
+ }
+
+ // Increase scores
+ BOOL bDumpScores = FALSE;
+ int i;
+ for ( i = 0; i <= 3; i++)
+ {
+ if (Goal->increase_team[i] != 0)
+ {
+ TeamFortress_TeamIncreaseScore(i + 1, Goal->increase_team[i]);
+ bDumpScores = TRUE;
+ }
+ }
+
+ // Increase the score of the team that owns this entity
+ if ( ( Goal->increase_team_owned_by != 0 ) && ( Goal->owned_by != 0 ) )
+ {
+ TeamFortress_TeamIncreaseScore( Goal->owned_by, Goal->increase_team_owned_by );
+ bDumpScores = TRUE;
+ }
+
+ // CTF Map support
+ if (TFCGameRules()->CTF_Map == TRUE && AP != NULL)
+ {
+ if (Goal->goal_no == CTF_FLAG1 || Goal->goal_no == CTF_FLAG1 || Goal->goal_no == CTF_DROPOFF1 || Goal->goal_no == CTF_DROPOFF1)
+ {
+ // Do Messaging
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CTFCPlayer *pPlayer = ToTFCPlayer( UTIL_PlayerByIndex( i ) );
+ if ( !pPlayer )
+ continue;
+
+ if ( (pPlayer->GetTeamNumber() == 2 && Goal->goal_no == CTF_FLAG1) || (pPlayer->GetTeamNumber() == 1 && Goal->goal_no == CTF_FLAG2) )
+ {
+ if (pPlayer == AP)
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "You got the enemy flag!\n\nReturn to base!");
+ else
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "Your team GOT the ENEMY flag!!");
+ }
+ else if (Goal->goal_no == CTF_FLAG1 || Goal->goal_no == CTF_FLAG2)
+ {
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "Your flag has been TAKEN!!");
+ }
+ else if ( (pPlayer->GetTeamNumber() == 2 && Goal->goal_no == CTF_DROPOFF1) || (pPlayer->GetTeamNumber() == 1 && Goal->goal_no == CTF_DROPOFF2) )
+ {
+ if (pPlayer == AP)
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "You CAPTURED the FLAG!!");
+ else
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "Your flag was CAPTURED!!");
+ }
+ else if (Goal->goal_no == CTF_DROPOFF1 || Goal->goal_no == CTF_DROPOFF2)
+ {
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "Your team CAPTURED the flag!!");
+ }
+ }
+
+ const char *pTeamName = "SPECTATOR";
+ if ( AP->GetTeamNumber() != 0 )
+ {
+ CTeam *pTeam = GetGlobalTeam( AP->GetTeamNumber() );
+ if ( pTeam )
+ pTeamName = pTeam->GetName();
+ }
+
+ // Console Prints
+ switch(Goal->goal_no)
+ {
+ case CTF_FLAG1:
+ UTIL_ClientPrintAll( HUD_PRINTNOTIFY, UTIL_VarArgs("%s GOT the BLUE flag!", AP->GetPlayerName()) );
+
+ UTIL_LogPrintf("\"%s<%i><%s>\" triggered \"Stole_Blue_Flag\"\n",
+ AP->GetPlayerName(),
+ AP->GetUserID(),
+ pTeamName );
+
+ AP->m_Shared.AddItemFlags( IT_KEY1 );
+ break;
+ case CTF_FLAG2:
+ UTIL_ClientPrintAll( HUD_PRINTNOTIFY, UTIL_VarArgs("%s GOT the RED flag!", AP->GetPlayerName()) );
+
+ UTIL_LogPrintf("\"%s<%i><%s>\" triggered \"Stole_Red_Flag\"\n",
+ AP->GetPlayerName(),
+ AP->GetUserID(),
+ pTeamName );
+
+ AP->m_Shared.AddItemFlags( IT_KEY2 );
+ break;
+ case CTF_DROPOFF1:
+ UTIL_ClientPrintAll( HUD_PRINTNOTIFY, UTIL_VarArgs("%s CAPTURED the RED flag!", AP->GetPlayerName()) );
+
+ UTIL_LogPrintf("\"%s<%i><%s>\" triggered \"Captured_Red_Flag\"\n",
+ AP->GetPlayerName(),
+ AP->GetUserID(),
+ pTeamName );
+
+ AP->m_Shared.RemoveItemFlags( IT_KEY2 );
+ break;
+ case CTF_DROPOFF2:
+ UTIL_ClientPrintAll( HUD_PRINTNOTIFY, UTIL_VarArgs("%s CAPTURED the BLUE flag!", AP->GetPlayerName()) );
+
+ UTIL_LogPrintf("\"%s<%i><%s>\" triggered \"Captured_Blue_Flag\"\n",
+ AP->GetPlayerName(),
+ AP->GetUserID(),
+ pTeamName );
+
+ AP->m_Shared.RemoveItemFlags( IT_KEY1 );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // Do Spawnpoint work before cycling players, so Forced respawn players work correctly.
+ if (Goal->remove_spawngroup != 0)
+ {
+ // Find all goals
+ //TFCTODO: I think FindEntityByClassname will do the same thing.
+ //CBaseEntity *pEnt = UTIL_FindEntityByString( NULL, "netname", "info_player_teamspawn" );
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "info_player_teamspawn" );
+ while ( pEnt )
+ {
+ CTFSpawn *pSpawn = dynamic_cast<CTFSpawn*>( pEnt );
+ if ( pSpawn )
+ {
+ if ( pSpawn->group_no == Goal->remove_spawngroup)
+ InactivateSpawn(pSpawn);
+ }
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "info_player_teamspawn" );
+ }
+ }
+
+ if (Goal->restore_spawngroup != 0)
+ {
+ // Find all goals
+ //TFCTODO: I think FindEntityByClassname will do the same thing.
+ //CBaseEntity *pEnt = UTIL_FindEntityByString( NULL, "netname", "info_player_teamspawn" );
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "info_player_teamspawn" );
+ while ( pEnt )
+ {
+ CTFSpawn *pSpawn = dynamic_cast<CTFSpawn*>( pEnt );
+ if ( pSpawn )
+ {
+ if (pSpawn->group_no == Goal->restore_spawngroup)
+ ActivateSpawn(pSpawn);
+ }
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "info_player_teamspawn" );
+ }
+ }
+
+ // Go through all the players and do any results
+ if ( Goal->broadcast != NULL_STRING && TFCGameRules()->CTF_Map == FALSE )
+ {
+ UTIL_LogPrintf("World triggered \"%s\"\n", STRING(Goal->broadcast) );
+ }
+ if ( Goal->netname_broadcast != NULL_STRING && TFCGameRules()->CTF_Map == FALSE && AP != NULL )
+ {
+ UTIL_LogPrintf("\"%s<%i><%s>\" triggered \"%s\"\n",
+ AP->GetPlayerName(),
+ AP->GetUserID(),
+ ( AP->GetTeamNumber() != 0 ) ? GetTeamName( AP->GetTeamNumber() ) : "SPECTATOR",
+ STRING(Goal->netname_broadcast) );
+ }
+
+ BOOL bGotOne = FALSE;
+ for ( i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CTFCPlayer *pPlayer = ToTFCPlayer( UTIL_PlayerByIndex( i ) );
+ if ( !pPlayer )
+ continue;
+
+ // Centerprinting
+ if (Goal->broadcast != NULL_STRING && TFCGameRules()->CTF_Map == FALSE)
+ UTIL_ShowMessage( STRING(Goal->broadcast), pPlayer );
+ if (Goal->netname_broadcast != NULL_STRING && TFCGameRules()->CTF_Map == FALSE && AP != NULL)
+ ClientPrint( pPlayer, HUD_PRINTNOTIFY, STRING(Goal->netname_broadcast), AP->GetPlayerName() );
+ // Old printing
+ if (Goal->org_broadcast != NULL_STRING && TFCGameRules()->CTF_Map == FALSE)
+ ClientPrint( pPlayer, HUD_PRINTCENTER, STRING(Goal->org_broadcast) );
+
+ // VOX
+ if (Goal->speak != NULL_STRING)
+ pPlayer->ClientHearVox( STRING(Goal->speak) );
+
+ if (AP == pPlayer)
+ {
+ // Spawnpoints handle their own printing elsewhere
+ if (Goal->message != NULL_STRING && Goal->Classify() != CLASS_TFSPAWN)
+ UTIL_ShowMessage( STRING(Goal->message), pPlayer );
+ if (Goal->org_message != NULL_STRING && Goal->Classify() != CLASS_TFSPAWN)
+ ClientPrint( pPlayer, HUD_PRINTCENTER, STRING(Goal->org_message) );
+
+ // VOX
+ if (Goal->AP_speak != NULL_STRING)
+ pPlayer->ClientHearVox( STRING(Goal->AP_speak) );
+ }
+ else if ( (AP != NULL) && (AP->GetTeamNumber() == pPlayer->GetTeamNumber()) )
+ {
+ // Text Printing
+ if (Goal->owners_team_broadcast != NULL_STRING && pPlayer->GetTeamNumber() == Goal->owned_by)
+ UTIL_ShowMessage( STRING(Goal->owners_team_broadcast), pPlayer );
+ else if (Goal->non_owners_team_broadcast != NULL_STRING && pPlayer->GetTeamNumber() != Goal->owned_by)
+ UTIL_ShowMessage( STRING(Goal->non_owners_team_broadcast), pPlayer );
+ else if (Goal->team_broadcast!= NULL_STRING )
+ UTIL_ShowMessage( STRING(Goal->team_broadcast), pPlayer );
+ // Old Text Printing
+ if (Goal->org_owners_team_broadcast != NULL_STRING && pPlayer->GetTeamNumber() == Goal->owned_by)
+ ClientPrint( pPlayer, HUD_PRINTCENTER, STRING(Goal->org_owners_team_broadcast) );
+ else if (Goal->org_non_owners_team_broadcast != NULL_STRING && pPlayer->GetTeamNumber() != Goal->owned_by)
+ ClientPrint( pPlayer, HUD_PRINTCENTER, STRING(Goal->org_non_owners_team_broadcast) );
+ else if (Goal->org_team_broadcast!= NULL_STRING )
+ ClientPrint( pPlayer, HUD_PRINTCENTER, STRING(Goal->org_team_broadcast) );
+
+
+ // VOX
+ if (Goal->owners_team_speak != NULL_STRING && pPlayer->GetTeamNumber() == Goal->owned_by)
+ pPlayer->ClientHearVox( STRING(Goal->owners_team_speak) );
+ else if (Goal->non_owners_team_speak != NULL_STRING && pPlayer->GetTeamNumber() != Goal->owned_by)
+ pPlayer->ClientHearVox( STRING(Goal->non_owners_team_speak) );
+ else if (Goal->team_speak!= NULL_STRING )
+ pPlayer->ClientHearVox( STRING(Goal->team_speak) );
+
+ if (Goal->netname_owners_team_broadcast != NULL_STRING && pPlayer->GetTeamNumber() == Goal->owned_by)
+ ClientPrint( pPlayer, HUD_PRINTNOTIFY, STRING(Goal->netname_owners_team_broadcast), AP->GetPlayerName() );
+ else if (Goal->netname_team_broadcast!= NULL_STRING )
+ ClientPrint( pPlayer, HUD_PRINTNOTIFY, STRING(Goal->netname_team_broadcast), AP->GetPlayerName() );
+ }
+ else
+ {
+ // Text Printing
+ if (Goal->owners_team_broadcast != NULL_STRING && pPlayer->GetTeamNumber() == Goal->owned_by)
+ UTIL_ShowMessage( STRING(Goal->owners_team_broadcast), pPlayer );
+ else if (Goal->non_owners_team_broadcast != NULL_STRING && pPlayer->GetTeamNumber() != Goal->owned_by)
+ UTIL_ShowMessage( STRING(Goal->non_owners_team_broadcast), pPlayer );
+ else if (Goal->non_team_broadcast != NULL_STRING )
+ UTIL_ShowMessage( STRING(Goal->non_team_broadcast), pPlayer );
+ // Old Text Printing
+ if (Goal->org_owners_team_broadcast != NULL_STRING && pPlayer->GetTeamNumber() == Goal->owned_by)
+ ClientPrint( pPlayer, HUD_PRINTCENTER, STRING(Goal->org_owners_team_broadcast) );
+ else if (Goal->org_non_owners_team_broadcast != NULL_STRING && pPlayer->GetTeamNumber() != Goal->owned_by)
+ ClientPrint( pPlayer, HUD_PRINTCENTER, STRING(Goal->org_non_owners_team_broadcast) );
+ else if (Goal->org_non_team_broadcast!= NULL_STRING )
+ ClientPrint( pPlayer, HUD_PRINTCENTER, STRING(Goal->org_non_team_broadcast) );
+
+ // VOX
+ if (Goal->owners_team_speak != NULL_STRING && pPlayer->GetTeamNumber() == Goal->owned_by)
+ pPlayer->ClientHearVox( STRING(Goal->owners_team_speak) );
+ else if (Goal->non_owners_team_speak != NULL_STRING && pPlayer->GetTeamNumber() != Goal->owned_by)
+ pPlayer->ClientHearVox( STRING(Goal->non_owners_team_speak) );
+ else if (Goal->non_team_speak != NULL_STRING )
+ pPlayer->ClientHearVox( STRING(Goal->non_team_speak) );
+
+ if (Goal->netname_owners_team_broadcast != NULL_STRING && pPlayer->GetTeamNumber() == Goal->owned_by && AP != NULL)
+ ClientPrint( pPlayer, HUD_PRINTNOTIFY, STRING(Goal->netname_owners_team_broadcast), AP->GetPlayerName() );
+ else if (Goal->netname_non_team_broadcast != NULL_STRING && AP != NULL)
+ ClientPrint( pPlayer, HUD_PRINTNOTIFY, STRING(Goal->netname_non_team_broadcast), AP->GetPlayerName() );
+ }
+
+ if (IsAffectedBy(Goal, pPlayer, AP))
+ {
+ // If its a Timer Goal, see if it needs to check Criteria again
+ if (Goal->search_time != 0 && Goal->goal_effects & TFGE_TIMER_CHECK_AP)
+ {
+ if (APMeetsCriteria(Goal, pPlayer))
+ {
+ Apply_Results(Goal, (CTFCPlayer*)pPlayer, AP, bAddBonuses);
+ bGotOne = TRUE;
+ }
+ }
+ else
+ {
+ Apply_Results(Goal, (CTFCPlayer*)pPlayer, AP, bAddBonuses);
+ bGotOne = TRUE;
+ }
+ }
+ }
+
+#ifdef MAP_DEBUG
+ if (bGotOne == FALSE)
+ Warning("NO PLAYERS AFFECTED\n");
+#endif
+
+ // Goal is now active
+ // Items are not set to active. They handle their modes.
+ if ( Goal->Classify() == CLASS_TFGOAL_TIMER || Goal->Classify() == CLASS_TFGOAL )
+ Goal->goal_state = TFGS_ACTIVE;
+
+ // EndGame checking
+ if (Goal->goal_result & TFGR_ENDGAME)
+ {
+ // Display Long TeamScores to everyone
+ TeamFortress_TeamShowScores(TRUE, NULL);
+
+ if ( g_pGameRules->IsMultiplayer() )
+ TFCGameRules()->TFCGoToIntermission();
+ return;
+ }
+
+ // EndRound checking
+ if (Goal->m_flEndRoundTime)
+ EndRound( Goal );
+
+ // Do Goal Group checking
+ DoGroupWork(Goal, AP);
+
+ // Do Goal checking
+ DoGoalWork(Goal, AP);
+
+ // Do Quake Trigger actions (Standard entities use SUB_UseTargets())
+ if ( Goal->Classify() == CLASS_TFGOAL_TIMER || Goal->Classify() == CLASS_TFGOAL_ITEM || Goal->Classify() == CLASS_TFGOAL || Goal->Classify() == CLASS_TFSPAWN || Goal->do_triggerwork )
+ DoTriggerWork(Goal, AP);
+
+ // Setup for Respawn
+ // Items, Triggers, and Spawnpoints do their own respawn work
+ if ( Goal->Classify() == CLASS_TFGOAL || Goal->Classify() == CLASS_TFGOAL_TIMER )
+ SetupRespawn(Goal);
+}
+
+
+//=========================================================================
+// Check to see if the Goal should Activate. Handle Else Goals if not.
+// If it does activate, Do the Results. Return true if the Goal activated.
+bool ActivateDoResults(CTFGoal *Goal, CTFCPlayer *AP, CTFGoal *ActivatingGoal)
+{
+ // Check Goal activation. This func handles Else Goals.
+ if ( !ActivationSucceeded(Goal, AP, ActivatingGoal) )
+ return false;
+
+ // Do the Results.
+ if (ActivatingGoal == Goal || Goal->m_bAddBonuses == true)
+ DoResults(Goal, AP, true);
+ else if (ActivatingGoal != NULL)
+ DoResults(Goal, AP, (ActivatingGoal->goal_result & TFGR_ADD_BONUSES));
+ else
+ DoResults(Goal, AP, 0);
+
+ return true;
+}
+
+
+//=========================================================================
+// Return true if the Goal should activate, and handle Else Goals
+bool ActivationSucceeded(CTFGoal *Goal, CTFCPlayer *AP, CTFGoal *ActivatingGoal)
+{
+ // Can't activate during PreMatch time, except for timers
+ if ( (TFCGameRules()->IsInPreMatch()) && (Goal->Classify() != CLASS_TFGOAL_TIMER) )
+ return false;
+
+ // If activation fails, try and activate the Else Goal
+ if ( !ShouldActivate(Goal, AP) )
+ {
+ // If an else goal should be activated, activate it
+ if (Goal->else_goal != 0)
+ {
+ MDEBUG( Warning(" Else Goal.\n") );
+
+ CTFGoal *pElseGoal = Findgoal(Goal->else_goal);
+ if (pElseGoal)
+ ActivateDoResults(pElseGoal, AP, Goal);
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+
+// ---------------------------------------------------------------------------------------- //
+// CTFBaseItem existence.
+// ---------------------------------------------------------------------------------------- //
+
+//===========================================
+// Check whether this entity should exist at this skill
+bool CTFBaseItem::CheckExistence()
+{
+ if (ex_skill_min == -1 && g_iSkillLevel < 0)
+ return FALSE;
+ else if (ex_skill_max == -1 && g_iSkillLevel > 0)
+ return FALSE;
+
+ if ( (ex_skill_min != 0) && (ex_skill_min != -1) && (g_iSkillLevel < ex_skill_min) )
+ return FALSE;
+ else if ( (ex_skill_max != 0) && (ex_skill_max != -1) && (g_iSkillLevel > ex_skill_max) )
+ return FALSE;
+
+ return TRUE;
+}
+
+
+// ---------------------------------------------------------------------------------------- //
+// CTFGoal implementation.
+// ---------------------------------------------------------------------------------------- //
+
+LINK_ENTITY_TO_CLASS(info_tfgoal, CTFGoal);
+
+BEGIN_DATADESC( CTFGoal )
+ DEFINE_FUNCTION( PlaceGoal ),
+ DEFINE_FUNCTION( DelayedResult )
+END_DATADESC()
+
+
+
+//===========================================
+// TF Goal spawn
+void CTFGoal::Spawn( void )
+{
+ if (CheckExistence() == false)
+ {
+ UTIL_Remove(this);
+ return;
+ }
+
+ // Graphic
+ string_t modelName = GetModelName();
+ if ( modelName != NULL_STRING )
+ {
+ // Brush Models need to be invisible
+ const char *pModel = STRING( modelName );
+ if (pModel[0] == '*')
+ AddEffects( EF_NODRAW );
+ }
+
+#ifdef TFCTODO
+ // Activation sound
+ if (pev->noise)
+ PRECACHE_SOUND( (char*)STRING(pev->noise) );
+
+ // For the powerups
+ PRECACHE_SOUND("items/protect.wav");
+ PRECACHE_SOUND("items/protect2.wav");
+ PRECACHE_SOUND("items/protect3.wav");
+ PRECACHE_SOUND("FVox/HEV_logon.wav");
+ PRECACHE_SOUND("FVox/hev_shutdown.wav");
+ PRECACHE_SOUND("items/inv1.wav");
+ PRECACHE_SOUND("items/inv2.wav");
+ PRECACHE_SOUND("items/inv3.wav");
+ PRECACHE_SOUND("items/damage.wav");
+ PRECACHE_SOUND("items/damage2.wav");
+ PRECACHE_SOUND("items/damage3.wav");
+#endif
+
+ // Set initial states
+ AddSolidFlags( FSOLID_TRIGGER );
+ if (goal_state == 0)
+ goal_state = TFGS_INACTIVE;
+
+ // Set Size
+ if (goal_min != vec3_origin && goal_max != vec3_origin)
+ UTIL_SetSize( this, goal_min, goal_max );
+
+ StartGoal();
+}
+
+
+//=========================================================================
+// Respawn the goal
+void CTFGoal::DoRespawn()
+{
+ RestoreGoal(this);
+ InactivateGoal(this);
+}
+
+
+//=========================================================================
+// Timer goal tick
+void CTFGoal::tfgoal_timer_tick()
+{
+ // Check criteria
+ if (goal_state != TFGS_REMOVED)
+ {
+ #ifdef MAP_DEBUG
+ Warning("==========================\n");
+ Warning("Timer Tick for: %s\nChecking Criteria...", GetEntityName().ToCStr());
+ #endif
+
+ // Timers don't fire during prematch.
+ // Instead, they setup to fire the correct amount of time past the prematch
+ if ( TFCGameRules()->IsInPreMatch() )
+ {
+ MDEBUG( Warning("\n PREMATCH IS ON. DELAYING UNTIL AFTER PREMATCH.\n") );
+
+ SetThink( &CTFGoal::tfgoal_timer_tick );
+ SetNextThink( TFCGameRules()->GetPreMatchEndTime() + search_time );
+ return;
+ }
+
+ if (APMeetsCriteria(this, NULL))
+ {
+ DoResults(this, NULL, TRUE);
+ }
+ else
+ {
+ MDEBUG( Warning("\n") );
+ SetThink( &CTFGoal::tfgoal_timer_tick );
+ SetNextThink( gpGlobals->curtime + search_time );
+ }
+ }
+}
+
+
+//=========================================================================
+// Use (Triggered) function for Goals
+void CTFGoal::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
+{
+ // If the Activator isn't a player, pretend there isn't an activator
+ if (pActivator && !pActivator->IsPlayer())
+ {
+ pActivator = NULL;
+ }
+
+ CTFGoal *pCallerGoal = dynamic_cast<CTFGoal*>( pCaller );
+ if ( !pCallerGoal )
+ {
+ // Need to rethink some stuff if this can be called by entities that aren't CTFGoals.
+ Assert( false );
+ return;
+ }
+
+ // Goals are only activatable by players
+ if (!pActivator || pActivator->IsPlayer())
+ {
+ // Force it to add bonuses
+ m_bAddBonuses = true;
+ ActivateDoResults(this, (CTFCPlayer*)pActivator, pCallerGoal);
+ }
+}
+
+
+//===========================================
+// Make Goal's more easy to touch
+void CTFGoal::SetObjectCollisionBox( void )
+{
+ const char *pModel = STRING( GetModelName() );
+ if (pModel[0] != '*')
+ {
+ Vector vMins = WorldAlignMins() + Vector(-24, -24, 0);
+ Vector vMaxs = WorldAlignMaxs() + Vector(24, 24, 16);
+ SetCollisionBounds( vMins, vMaxs );
+ }
+ else
+ {
+// Do we even need to do this? The bmodel should be setup correctly at this point anyway.
+#ifdef TFCTODO
+ // Ripped from ::SetObjectCollisionBox
+ float max, v;
+ int i;
+
+ max = 0;
+ for (i=0 ; i<3 ; i++)
+ {
+ v = fabs( (( float * )pev->mins )[i]);
+ if (v > max)
+ max = v;
+ v = fabs( (( float * )pev->maxs )[i]);
+ if (v > max)
+ max = v;
+ }
+ for (i=0 ; i<3 ; i++)
+ {
+ ((float *)pev->absmin)[i] = (( float * )GetAbsOrigin())[i] - max;
+ ((float *)pev->absmax)[i] = (( float * )GetAbsOrigin())[i] + max;
+ }
+
+ pev->absmin.x -= 1;
+ pev->absmin.y -= 1;
+ pev->absmin.z -= 1;
+ pev->absmax.x += 1;
+ pev->absmax.y += 1;
+ pev->absmax.z += 1;
+#endif
+ }
+}
+
+//===========================================
+// Start the Goal
+void CTFGoal::StartGoal( void )
+{
+ m_bAddBonuses = false;
+ SetThink( &CTFGoal::PlaceGoal );
+ SetNextThink( gpGlobals->curtime + 0.2 ); // goals start after other solids
+
+ if (goal_state == TFGS_REMOVED)
+ RemoveGoal(this);
+};
+
+//===========================================
+// Sets up the Goal's first thoughts
+void CTFGoal::PlaceGoal( void )
+{
+ if ( FClassnameIs(this, "info_tfgoal_timer") )
+ {
+ // Set up the next Timer Tick
+ SetThink( &CTFGoal::tfgoal_timer_tick );
+ SetNextThink( gpGlobals->curtime + search_time );
+ }
+ else
+ {
+ // Only give touch functions to goals that can be activated by touch
+ if (goal_activation & TFGA_TOUCH)
+ SetTouch( &CTFGoal::tfgoal_touch );
+ }
+
+ // So searches for this goal work later on
+ Assert( stricmp( GetClassname(), "info_tfgoal" ) == 0 );
+
+ // Drop to ground
+ if (goal_activation & TFGA_DROPTOGROUND)
+ {
+// Is this right?
+#ifdef TFCTODO
+ SetMoveType( MOVETYPE_TOSS );
+#else
+ SetMoveType( MOVETYPE_FLYGRAVITY );
+#endif
+ SetAbsOrigin( GetAbsOrigin() + Vector( 0, 0, 6 ) );
+
+ if ( UTIL_DropToFloor(this, MASK_SOLID) == 0)
+ {
+ Error("TF Goal %s fell out of level at %f,%f,%f", GetEntityName(), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z);
+ UTIL_Remove( this );
+ return;
+ }
+ }
+
+ SetMoveType( MOVETYPE_NONE );
+ SetAbsVelocity( Vector( 0, 0, 0 ) );
+ oldorigin = GetAbsOrigin(); // So we can return it later
+}
+
+
+// Touch function for Goals
+void CTFGoal::tfgoal_touch( CBaseEntity *pOther )
+{
+ // Can't touch during PreMatch time
+ if ( TFCGameRules()->IsInPreMatch() )
+ return;
+ // If it is not activated in by the player's touch, return
+ if (!(goal_activation & TFGA_TOUCH))
+ return;
+ // Only activatable by a player
+ if (!pOther->IsPlayer())
+ return;
+ if ( pOther->IsAlive() == FALSE )
+ return;
+
+ // If it's already active, don't bother
+ if (goal_state == TFGS_ACTIVE)
+ {
+ MDEBUG( Warning("Goal already active. aborting touch.\n") );
+ return;
+ }
+
+ // CTF Hack to make sure the key is in place. Like the rest of the CTF_Map stuff,
+ // it's not needed... the base scripting could handle it all.
+ if (TFCGameRules()->CTF_Map)
+ {
+ if ((goal_no == CTF_DROPOFF1) && (pOther->GetTeamNumber() == 1))
+ {
+ CTFGoalItem *pFlag = Finditem(CTF_FLAG1);
+ if ((pFlag->goal_state == TFGS_ACTIVE) || (pFlag->GetAbsOrigin() != pFlag->oldorigin))
+ return;
+ }
+ if ((goal_no == CTF_DROPOFF2) && (pOther->GetTeamNumber() == 2))
+ {
+ CTFGoalItem *pFlag = Finditem(CTF_FLAG2);
+ if ((pFlag->goal_state == TFGS_ACTIVE) || (pFlag->GetAbsOrigin() != pFlag->oldorigin))
+ return;
+ }
+ }
+
+ ActivateDoResults(this, ToTFCPlayer( pOther ), this);
+}
+
+//=========================================================================
+// Handles Delayed Activation of Goals
+void CTFGoal::DelayedResult()
+{
+ CBaseEntity *pEnemy = enemy;
+ if (goal_state == TFGS_DELAYED)
+ DoResults(this, ToTFCPlayer( pEnemy ), weapon);
+}
+
+
+// ---------------------------------------------------------------------------------------- //
+// CTFGoalItem implementation.
+// ---------------------------------------------------------------------------------------- //
+
+LINK_ENTITY_TO_CLASS(item_tfgoal, CTFGoalItem);
+
+
+//=========================================================================
+// Spawn a goalitem entity
+void CTFGoalItem::Spawn( void )
+{
+ if (CheckExistence() == false)
+ {
+ UTIL_Remove(this);
+ return;
+ }
+
+ // Set this in case they used abbreviations
+ Assert( stricmp( GetClassname(), "item_tfgoal" ) == 0 );
+
+ // Graphic
+ if ( GetModelName() != NULL_STRING )
+ {
+ // Setup animations
+ SetSequence( LookupSequence( "not_carried" ) );
+ if ( GetSequence() != -1 )
+ {
+ ResetSequenceInfo();
+ m_flCycle = 0;
+ }
+ }
+
+#ifdef TFCTODO
+ // Respawn sound
+ PRECACHE_SOUND("items/itembk2.wav");
+
+ // Activation sound
+ if (pev->noise)
+ PRECACHE_SOUND( (char*)STRING(pev->noise) );
+
+ if (!(pev->netname))
+ pev->netname = MAKE_STRING("goalitem");
+#endif
+
+ if (goal_state == 0)
+ goal_state = TFGS_INACTIVE;
+
+ // Set initial solidity
+ if (goal_activation & TFGI_SOLID)
+ {
+ SetSolid( SOLID_BBOX );
+ // Solid goalitems need a bbox
+ if (goal_min == vec3_origin)
+ goal_min = Vector(-16, -16, -24);
+ if (goal_max == vec3_origin)
+ goal_max = Vector(16, 16, 32);
+ }
+ else
+ {
+ SetSolidFlags( FSOLID_TRIGGER );
+ }
+
+ if (drop_time <= 0)
+ drop_time = 60;
+
+ // Set Size
+ UTIL_SetSize(this, goal_min, goal_max);
+
+ SetTouch( &CTFGoalItem::item_tfgoal_touch );
+ StartItem();
+};
+
+//=========================================================================
+// Start the Goal Item
+void CTFGoalItem::StartItem( void )
+{
+ SetThink( &CTFGoalItem::PlaceItem );
+ SetNextThink( gpGlobals->curtime + 0.2 ); // items start after other solids
+
+ if (goal_state == TFGS_REMOVED)
+ RemoveGoal(this);
+}
+
+//===========================================
+// Place the Goal Item
+void CTFGoalItem::PlaceItem( void )
+{
+ static int item_list_bit = 1; // used to determine what the bit of each new GoalItem will be.
+
+ SetAbsVelocity( vec3_origin );
+
+ // Drop to ground
+ if (goal_activation & TFGA_DROPTOGROUND)
+ {
+#ifdef TFCTODO
+ pev->movetype = MOVETYPE_TOSS;
+#else
+ SetMoveType( MOVETYPE_FLYGRAVITY );
+#endif
+ SetAbsOrigin( GetAbsOrigin() + Vector( 0, 0, 6 ) );
+
+ if ( UTIL_DropToFloor( this, MASK_SOLID ) == 0)
+ {
+ Error("TF GoalItem %s fell out of level at %f,%f,%f", STRING( GetEntityName() ), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z);
+ UTIL_Remove( this );
+ return;
+ }
+ }
+
+ SetMoveType( MOVETYPE_NONE );
+ oldorigin = GetAbsOrigin(); // So we can return it later
+
+ if (goal_activation & TFGI_ITEMGLOWS)
+ {
+ m_nRenderFX = kRenderFxGlowShell;
+
+ // If we have a teamcheck entity, use it instead
+ if ( owned_by_teamcheck != NULL_STRING )
+ owned_by = GetTeamCheckTeam( STRING(owned_by_teamcheck) );
+
+ if (owned_by > 0 && owned_by <= 4)
+ m_clrRender = Vector255ToRGBColor( rgbcolors[owned_by] );
+ else
+ m_clrRender = Vector255ToRGBColor( rgbcolors[0] );
+
+ SetRenderColorA( 100 ); // Shell size
+ }
+
+ // Set the item bit
+ item_list = item_list_bit;
+ item_list_bit *= 2;
+}
+
+
+// Touch function for the goalitem entity
+void CTFGoalItem::item_tfgoal_touch( CBaseEntity *pOther )
+{
+ if (!pOther->IsPlayer())
+ return;
+ if ( pOther->IsAlive() == FALSE )
+ return;
+ // Can't touch during PreMatch time
+ if ( TFCGameRules()->IsInPreMatch() )
+ return;
+
+ // Hack to prevent feigning spies from repicking up flags
+ CTFCPlayer *pPlayer = dynamic_cast<CTFCPlayer*>( pOther );
+ if (pPlayer->is_feigning)
+ return;
+
+ // only let players have one replacement_model goal item at a time
+ if ( replacement_model != NULL_STRING )
+ {
+ if ( pPlayer->replacement_model != NULL_STRING )
+ return;
+ }
+
+ // Prevent the dropping player from picking it up for longer
+ if (enemy.Get() && m_flDroppedAt != 0)
+ {
+ if ( (enemy == pOther) && m_flDroppedAt + 5 > gpGlobals->curtime )
+ return;
+ }
+ m_flDroppedAt = 0;
+
+ ASSERT( pOther != GetOwnerEntity() ); // There is no way in hell this should ever happen, and yet it still does.
+
+ // Prevent picking up flags through thin walls
+ trace_t tr;
+ UTIL_TraceLine ( WorldSpaceCenter(), pOther->WorldSpaceCenter(), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
+ if ( tr.fraction != 1.0 && tr.m_pEnt != pOther )
+ return;
+
+ // CTF Hack to return your key.
+ // Should use the builtin support now.
+ if (TFCGameRules()->CTF_Map == TRUE)
+ {
+ // Flag not at home?
+ if (GetAbsOrigin() != oldorigin)
+ {
+ if (GetTeamNumber() == 1)
+ {
+ if (goal_no == CTF_FLAG1)
+ {
+ UTIL_ClientPrintAll( HUD_PRINTNOTIFY, UTIL_VarArgs("%s RETURNED the BLUE flag!", pPlayer->GetPlayerName()) );
+ UTIL_LogPrintf("\"%s<%i><%s>\" triggered \"Returned_Blue_Flag\"\n",
+ pPlayer->GetPlayerName(),
+ pPlayer->GetUserID(),
+ GetTeamName( pOther->GetTeamNumber() ) );
+ }
+ else
+ {
+ UTIL_ClientPrintAll( HUD_PRINTNOTIFY, UTIL_VarArgs("%s RETURNED the RED flag!", pPlayer->GetPlayerName()) );
+ UTIL_LogPrintf("\"%s<%i><%s>\" triggered \"Returned_Red_Flag\"\n",
+ pPlayer->GetPlayerName(),
+ pPlayer->GetUserID(),
+ GetTeamName( pOther->GetTeamNumber() ) );
+ }
+
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
+ if ( !pPlayer )
+ continue;
+
+ if (pPlayer->GetTeamNumber() == 1 && goal_no == CTF_FLAG1)
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "Your flag was RETURNED!!\n");
+ else if (pPlayer->GetTeamNumber() != 1 && goal_no == CTF_FLAG1)
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "The ENEMY flag was RETURNED!!\n");
+ else if (pPlayer->GetTeamNumber() == 2 && goal_no == CTF_FLAG2)
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "Your flag was RETURNED!!\n");
+ else if (pPlayer->GetTeamNumber() == 2 && goal_no == CTF_FLAG1)
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "The ENEMY flag was RETURNED!!\n");
+ }
+
+ goal_state = TFGS_INACTIVE;
+ SetSolidFlags( FSOLID_TRIGGER );
+ SetTouch( &CTFGoalItem::item_tfgoal_touch );
+ SetAbsOrigin( oldorigin );
+
+ EmitSound( "GoalItem.Touch" );
+ //EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "items/itembk2.wav", 1, ATTN_NORM, 0, 150 );
+ return;
+ }
+ }
+ else
+ {
+ // Ignore touches to our own flag when it's at home
+ if (pOther->GetTeamNumber() == 1 && goal_no == CTF_FLAG1)
+ return;
+ if (pOther->GetTeamNumber() == 2 && goal_no == CTF_FLAG2)
+ return;
+ }
+ }
+
+ // Activate. Handles Else Goals if it fails.
+ if ( ActivationSucceeded( this, ToTFCPlayer( pOther ), NULL) )
+ {
+ // Give it to the player
+ tfgoalitem_GiveToPlayer(this, ToTFCPlayer( pOther ), this);
+ // It may have killed the player, so check:
+ if (pOther->GetHealth() > 0)
+ goal_state = TFGS_ACTIVE;
+ }
+}
+
+
+//=========================================================================
+// Throw the item into the air at the specified position
+void CTFGoalItem::DoDrop( Vector vecOrigin )
+{
+ SetAbsOrigin( vecOrigin );
+
+ StopFollowingEntity();
+ SetMoveType( MOVETYPE_FLYGRAVITY );
+
+ // Just drop it vertically first time to prevent it falling through walls too often
+ Vector vVel = GetAbsVelocity();
+ vVel.z = 400;
+ if (redrop_count > 1)
+ {
+ // Second and third drops try pushing it in other directions
+ vVel.x = RandomFloat(-50, 50);
+ vVel.y = RandomFloat(-50, 50);
+ }
+ SetAbsVelocity( vVel );
+
+ goal_state = TFGS_INACTIVE;
+ SetAbsAngles( QAngle( 0, 0, 0 ) );
+ if (goal_activation & TFGI_SOLID)
+ {
+ SetSolid( SOLID_BBOX );
+ }
+ else
+ {
+ SetSolid( SOLID_BBOX );
+ SetSolidFlags( FSOLID_TRIGGER );
+ }
+
+ RemoveEffects( EF_NODRAW );
+
+ UTIL_SetSize(this, goal_min, goal_max);
+
+ redrop_count++;
+
+ SetThink( &CTFGoalItem::tfgoalitem_dropthink ); // give it five seconds
+ SetNextThink( gpGlobals->curtime + 5.0 ); // and then find where it ended up
+}
+
+
+//=========================================================================
+// Set the GoalItems touch func
+void CTFGoalItem::tfgoalitem_droptouch()
+{
+ SetTouch( &CTFGoalItem::item_tfgoal_touch );
+ SetThink( &CTFGoalItem::tfgoalitem_dropthink ); // give it five seconds since it was dropped
+ SetNextThink( gpGlobals->curtime + 4.25 ); // and then find where it ended up
+}
+
+
+//=========================================================================
+// A quick check to make sure the items is not in a wall
+void CTFGoalItem::tfgoalitem_dropthink()
+{
+ MDEBUG( Msg( "DropThink for %s\n", STRING(pev->netname)) );
+
+ StopFollowingEntity();
+ SetMoveType( MOVETYPE_FLYGRAVITY );
+
+ if (drop_time != 0)
+ {
+ int iEnviron = UTIL_PointContents( GetAbsOrigin() );
+
+ if (iEnviron == CONTENTS_SLIME)
+ {
+ SetNextThink( gpGlobals->curtime + (drop_time / 4) );
+ }
+#ifdef TFCTODO // CONTENTS_LAVA and CONTENTS_SKY don't exist in src.
+ else if (iEnviron == CONTENTS_LAVA)
+ {
+ SetNextThink( gpGlobals->curtime + 5 );
+ }
+ else if (iEnviron == CONTENTS_SOLID || iEnviron == CONTENTS_SKY)
+#else
+ else if (iEnviron == CONTENTS_SOLID)
+#endif
+ {
+ // Its out of the world
+ // Retry a drop from the original position 3 times
+ if (redrop_count < 3)
+ {
+ // Retry the Drop
+ DoDrop( redrop_origin );
+ return;
+ }
+ else
+ {
+ // Fourth time round, just return it
+ SetNextThink( gpGlobals->curtime + 2 );
+ }
+ }
+ else
+ {
+ SetNextThink( gpGlobals->curtime + drop_time );
+ }
+
+ SetThink( &CTFGoalItem::tfgoalitem_remove );
+ }
+}
+
+
+//=========================================================================
+// Remove the item, or Return it if needed
+void CTFGoalItem::tfgoalitem_remove()
+{
+ MDEBUG( Msg( "RemoveItem for %s...", STRING(pev->netname)) );
+
+ // Has someone picked it up?
+ if (goal_state == TFGS_ACTIVE)
+ {
+ MDEBUG( Msg( "Item picked up, exiting.\n") );
+ return;
+ }
+
+ // Should it be returned?
+ if (goal_activation & TFGI_RETURN_REMOVE)
+ {
+ MDEBUG( Msg( "Returned.\n") );
+
+ CTimer *pTimer = Timer_CreateTimer( this, TF_TIMER_RETURNITEM );
+ pTimer->weapon = GI_RET_TIME;
+ pTimer->m_flNextThink = gpGlobals->curtime + 0.1;
+ //pTimer->SetThink( &CBaseEntity::ReturnItem ); this is done by CreateTimer code now.
+ return;
+ }
+
+ MDEBUG( Msg( "Removed.\n") );
+ UTIL_Remove(this);
+}
+
+
+// ---------------------------------------------------------------------------------------------------- //
+// CTFSpawn implementation.
+// ---------------------------------------------------------------------------------------------------- //
+
+LINK_ENTITY_TO_CLASS(info_player_teamspawn, CTFSpawn);
+
+//===========================================
+void CTFSpawn::Spawn( void )
+{
+ if (CheckExistence() == FALSE)
+ {
+ UTIL_Remove(this);
+ return;
+ }
+
+ // Team spawnpoints must have a team associated with them
+ if ( (GetTeamNumber() <= 0 || GetTeamNumber() >= 5) && teamcheck == NULL_STRING)
+ {
+ Warning("Teamspawnpoint with an invalid GetTeamNumber() of %d\n", GetTeamNumber());
+ return;
+ }
+
+
+ // find the highest team number
+
+ if (number_of_teams < GetTeamNumber())
+ number_of_teams = GetTeamNumber();
+
+ // Save out the info_player_teamspawn
+ Assert( stricmp( GetClassname(), "info_player_teamspawn" ) == 0 );
+#ifdef TFCTODO
+ pev->netname = pev->classname = MAKE_STRING( );
+#endif
+}
+
+void CTFSpawn::Activate( void )
+{
+ m_pTeamCheck = NULL;
+
+ // Find the team check entity
+ CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, STRING(teamcheck) );
+
+ if ( pTarget )
+ {
+ if ( !strcmp( pTarget->GetClassname(), "info_tf_teamcheck" ) )
+ m_pTeamCheck = pTarget;
+ }
+}
+
+BOOL CTFSpawn::CheckTeam( int iTeamNo )
+{
+ // First check team number
+ if ( GetTeamNumber() )
+ {
+ return ( iTeamNo == GetTeamNumber() );
+ }
+
+ // Then check the teamcheck
+ if ( m_pTeamCheck )
+ {
+ CTeamCheck *pTeamCheck = dynamic_cast<CTeamCheck*>( m_pTeamCheck.Get() );
+ Assert( pTeamCheck );
+ if ( pTeamCheck->TeamMatches( iTeamNo ) == FALSE )
+ return FALSE;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+// ---------------------------------------------------------------------------------------------------- //
+// CBaseDelay implementation.
+// ---------------------------------------------------------------------------------------------------- //
+
+LINK_ENTITY_TO_CLASS( DelayedUse, CBaseDelay );
+
+void CBaseDelay::SUB_UseTargets( CBaseEntity *pActivator, USE_TYPE useType, float value )
+{
+ // TeamFortress Goal Checking
+ if (!pActivator || pActivator->IsPlayer() )
+ DoResults(this, ToTFCPlayer( pActivator ), TRUE);
+
+ //
+ // exit immediatly if we don't have a target or kill target
+ //
+ if (target == NULL_STRING && !m_iszKillTarget)
+ return;
+
+ //
+ // check for a delay
+ //
+ if (m_flDelay != 0)
+ {
+ // create a temp object to fire at a later time
+ CBaseDelay *pTemp = (CBaseDelay*)CreateEntityByName( "DelayedUse" );
+ Assert( stricmp( pTemp->GetClassname(), "DelayedUse" ) == 0 );
+
+ pTemp->SetNextThink( gpGlobals->curtime + m_flDelay );
+ pTemp->SetThink( &CBaseDelay::DelayThink );
+
+ // Save the useType
+ pTemp->button = (int)useType;
+ pTemp->m_iszKillTarget = m_iszKillTarget;
+ pTemp->m_flDelay = 0; // prevent "recursion"
+ pTemp->target = target;
+ return;
+ }
+
+ //
+ // kill the killtargets
+ //
+
+ if ( m_iszKillTarget != NULL_STRING )
+ {
+ CBaseEntity *pentKillTarget = NULL;
+
+ Msg( "KillTarget: %s\n", STRING(m_iszKillTarget) );
+ pentKillTarget = gEntList.FindEntityByName( NULL, STRING(m_iszKillTarget) );
+ while ( pentKillTarget )
+ {
+ Msg( "killing %s\n", pentKillTarget->GetClassname() );
+
+ CBaseEntity *pNext = gEntList.FindEntityByName( pentKillTarget, STRING(m_iszKillTarget) );
+ UTIL_Remove( pentKillTarget );
+ pentKillTarget = pNext;
+ }
+ }
+
+ //
+ // fire targets
+ //
+ if ( target != NULL_STRING )
+ {
+ FireTargets( STRING(target), pActivator, this, useType, value );
+ }
+}
+
+
+bool CBaseDelay::KeyValue( const char *szKeyName, const char *szValue )
+{
+ if (FStrEq(szKeyName, "delay"))
+ {
+ m_flDelay = atof( szValue );
+ return true;
+ }
+ else if (FStrEq(szKeyName, "killtarget"))
+ {
+ m_iszKillTarget = MAKE_STRING(szValue);
+ return true;
+ }
+ else
+ {
+ return BaseClass::KeyValue( szKeyName, szValue );
+ }
+}
+
+void CBaseDelay::DelayThink( void )
+{
+ // The use type is cached (and stashed) in pev->button
+ SUB_UseTargets( NULL, (USE_TYPE)button, 0 );
+ UTIL_Remove( this );
+}
+
+
+// ---------------------------------------------------------------------------------------------------- //
+// CTeamCheck implementation.
+// ---------------------------------------------------------------------------------------------------- //
+
+LINK_ENTITY_TO_CLASS(info_tf_teamcheck, CTeamCheck );
+
+
+void CTeamCheck::Spawn( void )
+{
+}
+
+void CTeamCheck::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
+{
+ // Either Toggle or get set to a specific team number
+ if ( useType == USE_TOGGLE )
+ {
+ if ( GetTeamNumber() == 1 )
+ ChangeTeam( 2 );
+ else
+ ChangeTeam( 1 );
+ }
+ else if ( useType == USE_SET )
+ {
+ if ( value >= 1 && value <= 4 )
+ ChangeTeam( value );
+ }
+}
+
+BOOL CTeamCheck::TeamMatches( int iTeam )
+{
+ return ( iTeam == GetTeamNumber() );
+}
diff --git a/game/server/tfc/tfc_mapitems.h b/game/server/tfc/tfc_mapitems.h
new file mode 100644
index 0000000..6e6df70
--- /dev/null
+++ b/game/server/tfc/tfc_mapitems.h
@@ -0,0 +1,405 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef TFC_MAPITEMS_H
+#define TFC_MAPITEMS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "tfc_shareddefs.h"
+
+
+class CTFCPlayer;
+
+
+/*==================================================*/
+/* CTF Support defines */
+/*==================================================*/
+#define CTF_FLAG1 1
+#define CTF_FLAG2 2
+#define CTF_DROPOFF1 3
+#define CTF_DROPOFF2 4
+#define CTF_SCORE1 5
+#define CTF_SCORE2 6
+
+
+// Defines for GoalItem Removing from Player Methods
+#define GI_DROP_PLAYERDEATH 0 // Dropped by a dying player
+#define GI_DROP_REMOVEGOAL 1 // Removed by a Goal
+#define GI_DROP_PLAYERDROP 2 // Dropped by a player
+
+
+// Defines for methods of GoalItem returning
+#define GI_RET_DROP_DEAD 0 // Dropped by a dead player
+#define GI_RET_DROP_LIVING 1 // Dropped by a living player
+#define GI_RET_GOAL 2 // Returned by a Goal
+#define GI_RET_TIME 3 // Returned due to timeout
+
+
+// Defines for Goal States
+#define TFGS_ACTIVE 1
+#define TFGS_INACTIVE 2
+#define TFGS_REMOVED 3
+#define TFGS_DELAYED 4
+
+
+// Defines for Goal Result types : goal_result
+#define TFGR_SINGLE 1 // Goal can only be activated once
+#define TFGR_ADD_BONUSES 2 // Any Goals activated by this one give their bonuses
+#define TFGR_ENDGAME 4 // Goal fires Intermission, displays scores, and ends level
+#define TFGR_NO_ITEM_RESULTS 8 // GoalItems given by this Goal don't do results
+#define TFGR_REMOVE_DISGUISE 16 // Prevent/Remove undercover from any Spy
+#define TFGR_FORCE_RESPAWN 32 // Forces the player to teleport to a respawn point
+#define TFGR_DESTROY_BUILDINGS 64 // Destroys this player's buildings, if anys
+
+
+// Defines for Goal Item types, : goal_activation (in items)
+#define TFGI_GLOW 1 // Players carrying this GoalItem will glow
+#define TFGI_SLOW 2 // Players carrying this GoalItem will move at half-speed
+#define TFGI_DROP 4 // Players dying with this item will drop it
+#define TFGI_RETURN_DROP 8 // Return if a player with it dies
+#define TFGI_RETURN_GOAL 16 // Return if a player with it has it removed by a goal's activation
+#define TFGI_RETURN_REMOVE 32 // Return if it is removed by TFGI_REMOVE
+#define TFGI_REVERSE_AP 64 // Only pickup if the player _doesn't_ match AP Details
+#define TFGI_REMOVE 128 // Remove if left untouched for 2 minutes after being dropped
+#define TFGI_KEEP 256 // Players keep this item even when they die
+#define TFGI_ITEMGLOWS 512 // Item glows when on the ground
+#define TFGI_DONTREMOVERES 1024 // Don't remove results when the item is removed
+#define TFGI_DROPTOGROUND 2048 // Drop To Ground when spawning
+#define TFGI_CANBEDROPPED 4096 // Can be voluntarily dropped by players
+#define TFGI_SOLID 8192 // Is solid... blocks bullets, etc
+
+
+// For all these defines, see the tfortmap.txt that came with the zip
+// for complete descriptions.
+// Defines for Goal Activation types : goal_activation (in goals)
+#define TFGA_TOUCH 1 // Activated when touched
+#define TFGA_TOUCH_DETPACK 2 // Activated when touched by a detpack explosion
+#define TFGA_REVERSE_AP 4 // Activated when AP details are _not_ met
+#define TFGA_SPANNER 8 // Activated when hit by an engineer's spanner
+#define TFGA_DROPTOGROUND 2048 // Drop to Ground when spawning
+
+
+// Defines for Goal Effects types : goal_effect
+#define TFGE_AP 1 // AP is affected. Default.
+#define TFGE_AP_TEAM 2 // All of the AP's team.
+#define TFGE_NOT_AP_TEAM 4 // All except AP's team.
+#define TFGE_NOT_AP 8 // All except AP.
+#define TFGE_WALL 16 // If set, walls stop the Radius effects
+#define TFGE_SAME_ENVIRONMENT 32 // If set, players in a different environment to the Goal are not affected
+#define TFGE_TIMER_CHECK_AP 64 // If set, Timer Goals check their critera for all players fitting their effects
+
+
+class CTFBaseItem : public CBaseAnimating
+{
+public:
+
+ bool CheckExistence();
+
+
+public:
+
+ int group_no;
+ int goal_no;
+ int goal_state; // TFGS_
+ // Goal/Timer/GoalItem/Trigger existence checking
+ int ex_skill_min; // Exists when the skill is >= this value
+ int ex_skill_max; // Exists when the skill is <= this value
+ string_t teamcheck; // TeamCheck entity that should be checked
+};
+
+
+class CTFGoal : public CTFBaseItem
+{
+public:
+ DECLARE_CLASS( CTFGoal, CBaseAnimating );
+ DECLARE_DATADESC();
+
+ void Spawn( void );
+ void StartGoal( void );
+ void PlaceGoal( void );
+ void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
+ void tfgoal_touch( CBaseEntity *pOther );
+
+ void DelayedResult();
+
+ Class_T Classify ( void ) { return CLASS_TFGOAL; }
+
+ void SetObjectCollisionBox( void );
+
+ void tfgoal_timer_tick();
+ void DoRespawn();
+
+
+public:
+ //TFCTODO: lots of these variables need to be put in the FGD file.
+ int goal_effects; // TFGE_
+ int goal_result; // TFGR_
+
+ int playerclass; // One of the PC_ defines.
+
+ float t_length; // Goal Criteria radius check
+
+ // NOTE: In CTFGoal, these are overridden to mean: if they're not zero, then this goal only
+ // affects players...
+ int maxammo_shells; // ... with that team number
+ int maxammo_nails; // ... without that team number
+
+ int ammo_shells;
+ int ammo_nails;
+ int ammo_rockets;
+ int ammo_cells;
+ int ammo_medikit;
+ int ammo_detpack;
+ int no_grenades_1;
+ int no_grenades_2;
+
+ // Replacement_Model Stuff
+ string_t replacement_model;
+ int replacement_model_body;
+ int replacement_model_skin;
+ int replacement_model_flags;
+
+ // Item Displaying details
+ int display_item_status[4]; // Goal displays the status of these items
+ string_t team_str_home; // Displayed when the item is at home base
+ string_t team_str_moved; // Displayed when the item has been moved
+ string_t team_str_carried; // Displayed when the item is being carried
+ string_t non_team_str_home; // Displayed when the item is at home base
+ string_t non_team_str_moved; // Displayed when the item has been moved
+ string_t non_team_str_carried; // Displayed when the item is being carried
+
+ float invincible_finished;
+ float invisible_finished;
+ float super_damage_finished;
+ float radsuit_finished;
+
+ int lives;
+ int frags;
+ float wait;
+
+ float search_time; // Timer goal delay
+ int item_list; // Used to keep track of which goalitems are
+ // affecting the player at any time.
+ // GoalItems use it to keep track of their own
+ // mask to apply to a player's item_list
+
+ float drop_time; // Time spent untouched before item return
+ float armortype;
+ int armorvalue;
+ int armorclass; // Type of armor being worn;
+
+ int count; // Change teamscores
+
+ // Goal Size
+ Vector goal_min;
+ Vector goal_max;
+
+ bool m_bAddBonuses;
+
+ int items;
+ int items_allowed;
+
+ int else_goal;
+ int if_goal_is_active;
+ int if_goal_is_inactive;
+ int if_goal_is_removed;
+ int if_group_is_active;
+ int if_group_is_inactive;
+ int if_group_is_removed;
+
+ int speed_reduction;
+
+ int return_item_no;
+ int if_item_has_moved;
+ int if_item_hasnt_moved;
+
+ int has_item_from_group;
+ int hasnt_item_from_group;
+
+ int goal_activation;
+ int delay_time;
+ int weapon;
+ string_t owned_by_teamcheck;
+ int owned_by;
+ string_t noise;
+
+ // Spawnpoint behaviour
+ int remove_spawnpoint;
+ int restore_spawnpoint;
+ int remove_spawngroup;
+ int restore_spawngroup;
+
+ // These are the old centerprinting methods.
+ // They now print using the large fancy text
+ string_t broadcast; // Centerprinted to all, overridden by the next two
+ string_t team_broadcast; // Centerprinted to AP's team members, but not the AP
+ string_t non_team_broadcast; // Centerprinted to non AP's team members
+ string_t owners_team_broadcast; // Centerprinted to the members of the team that own the Goal/Item
+ string_t non_owners_team_broadcast; // Centerprinted to the members of the team that don't own the Goal/Item
+ string_t team_drop; // Centerprinted to item owners team
+ string_t non_team_drop; // Centerprinted to everone not on item owners team
+
+ // These are new fields that print the old fashioned centerprint method
+ string_t org_broadcast; // Centerprinted to all, overridden by the next two
+ string_t org_team_broadcast; // Centerprinted to AP's team members, but not the AP
+ string_t org_non_team_broadcast; // Centerprinted to non AP's team members
+ string_t org_owners_team_broadcast; // Centerprinted to the members of the team that own the Goal/Item
+ string_t org_non_owners_team_broadcast; // Centerprinted to the members of the team that don't own the Goal/Item
+ string_t org_team_drop; // Centerprinted to item owners team
+ string_t org_non_team_drop; // Centerprinted to everone not on item owners team
+ string_t org_message; // Centerprinted to the AP upon activation
+ string_t org_noise3;
+ string_t org_noise4;
+ // These still print the old centerprint fashion
+ string_t netname_broadcast; // same as above, prepended by AP netname and bprinted
+ string_t netname_team_broadcast; // same as above, prepended by AP netname and bprinted
+ string_t netname_non_team_broadcast; // same as above, prepended by AP netname and bprinted
+ string_t netname_owners_team_broadcast; // same as above, prepended by AP netname and bprinted
+ string_t netname_team_drop; // same as above, prepended by AP netname and bprinted
+ string_t netname_non_team_drop; // same as above, prepended by AP netname and bprinted
+ string_t speak; // VOX Spoken to Everyone
+ string_t AP_speak; // VOX Spoken the AP
+ string_t team_speak; // VOX Spoken to AP's team_members, including the AP
+ string_t non_team_speak; // VOX Spoken to non AP's team_members
+ string_t owners_team_speak; // VOX Spoken to members of the team that own this Goal
+ string_t non_owners_team_speak; // VOX Spoken to everyone bit the members of the team that own this Goal
+
+ float m_flEndRoundTime;
+ string_t m_iszEndRoundMsg_Team1_Win;
+ string_t m_iszEndRoundMsg_Team2_Win;
+ string_t m_iszEndRoundMsg_Team3_Win;
+ string_t m_iszEndRoundMsg_Team4_Win;
+ string_t m_iszEndRoundMsg_Team1_Lose;
+ string_t m_iszEndRoundMsg_Team2_Lose;
+ string_t m_iszEndRoundMsg_Team3_Lose;
+ string_t m_iszEndRoundMsg_Team4_Lose;
+ string_t m_iszEndRoundMsg_Team1;
+ string_t m_iszEndRoundMsg_Team2;
+ string_t m_iszEndRoundMsg_Team3;
+ string_t m_iszEndRoundMsg_Team4;
+ string_t m_iszEndRoundMsg_OwnedBy;
+ string_t m_iszEndRoundMsg_NonOwnedBy;
+
+ int all_active;
+ int last_impulse; // The previous impulse command from this player
+
+ int activate_goal_no;
+ int inactivate_goal_no;
+ int remove_goal_no;
+ int restore_goal_no;
+ int activate_group_no;
+ int inactivate_group_no;
+ int remove_group_no;
+ int restore_group_no;
+
+ BOOL do_triggerwork; // Overrides for trigger handling in TF Goals
+ string_t killtarget; // Remove ents with this target
+ string_t target;
+
+ string_t message;
+
+ // Score increases
+ int increase_team[4]; // Increase the scores of teams
+ int increase_team_owned_by; // Increase the score of the team that owns this entity
+
+ EHANDLE enemy;
+
+ Vector oldorigin;
+ int axhitme; // Remove item from AP
+
+ int remove_item_group;
+};
+
+
+class CTFGoalItem : public CTFGoal
+{
+public:
+ void Spawn( void );
+ void StartItem( void );
+ void PlaceItem( void );
+
+ Class_T Classify ( void ) { return CLASS_TFGOAL_ITEM; }
+
+ void item_tfgoal_touch( CBaseEntity *pOther );
+ void tfgoalitem_droptouch();
+ void tfgoalitem_dropthink();
+ void tfgoalitem_remove();
+
+ void DoDrop( Vector vecOrigin );
+
+
+public:
+
+ float m_flDroppedAt;
+
+ float speed;
+ int speed_reduction;
+
+ float distance;
+ float pain_finished;
+ float attack_finished;
+
+ Vector redrop_origin; // Original drop position
+ int redrop_count; // Number of time's we redropped.
+};
+
+
+class CTFTimerGoal : public CTFGoal
+{
+public:
+ void Spawn( void );
+
+ Class_T Classify ( void ) { return CLASS_TFGOAL_TIMER; }
+};
+
+
+class CTFSpawn : public CTFBaseItem
+{
+public:
+ void Spawn( void );
+ void Activate( void );
+ Class_T Classify ( void ) { return CLASS_TFSPAWN; }
+ BOOL CheckTeam( int iTeamNo );
+
+ EHANDLE m_pTeamCheck;
+};
+
+
+class CBaseDelay : public CTFGoal
+{
+public:
+ DECLARE_CLASS( CBaseDelay, CTFGoal );
+
+ void SUB_UseTargets( CBaseEntity *pActivator, USE_TYPE useType, float value );
+ void KeyValue( KeyValueData *pkvd );
+ void DelayThink( void );
+ virtual bool KeyValue( const char *szKeyName, const char *szValue );
+
+public:
+ float m_flDelay;
+ string_t m_iszKillTarget;
+ int button;
+};
+
+
+class CTeamCheck : public CBaseDelay
+{
+public:
+ void Spawn( void );
+ void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
+ BOOL TeamMatches( int iTeam );
+};
+
+
+// Global functions.
+CTFGoalItem* Finditem(int ino);
+void DisplayItemStatus(CTFGoal *Goal, CTFCPlayer *Player, CTFGoalItem *Item);
+
+
+#endif // TFC_MAPITEMS_H
diff --git a/game/server/tfc/tfc_player.cpp b/game/server/tfc/tfc_player.cpp
new file mode 100644
index 0000000..d006efc
--- /dev/null
+++ b/game/server/tfc/tfc_player.cpp
@@ -0,0 +1,1115 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Player for HL1.
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "tfc_player.h"
+#include "tfc_gamerules.h"
+#include "KeyValues.h"
+#include "viewport_panel_names.h"
+#include "client.h"
+#include "team.h"
+#include "weapon_tfcbase.h"
+#include "tfc_client.h"
+#include "tfc_mapitems.h"
+#include "tfc_timer.h"
+#include "tfc_engineer.h"
+#include "tfc_team.h"
+
+
+#define TFC_PLAYER_MODEL "models/player/pyro.mdl"
+
+
+// -------------------------------------------------------------------------------- //
+// Player animation event. Sent to the client when a player fires, jumps, reloads, etc..
+// -------------------------------------------------------------------------------- //
+
+class CTEPlayerAnimEvent : public CBaseTempEntity
+{
+public:
+ DECLARE_CLASS( CTEPlayerAnimEvent, CBaseTempEntity );
+ DECLARE_SERVERCLASS();
+
+ CTEPlayerAnimEvent( const char *name ) : CBaseTempEntity( name )
+ {
+ }
+
+ CNetworkHandle( CBasePlayer, m_hPlayer );
+ CNetworkVar( int, m_iEvent );
+ CNetworkVar( int, m_nData );
+};
+
+IMPLEMENT_SERVERCLASS_ST_NOBASE( CTEPlayerAnimEvent, DT_TEPlayerAnimEvent )
+ SendPropEHandle( SENDINFO( m_hPlayer ) ),
+ SendPropInt( SENDINFO( m_iEvent ), Q_log2( PLAYERANIMEVENT_COUNT ) + 1, SPROP_UNSIGNED )
+ SendPropInt( SENDINFO( m_nData ), 32 )
+END_SEND_TABLE()
+
+static CTEPlayerAnimEvent g_TEPlayerAnimEvent( "PlayerAnimEvent" );
+
+void TE_PlayerAnimEvent( CBasePlayer *pPlayer, PlayerAnimEvent_t event, int nData )
+{
+ CPVSFilter filter( pPlayer->EyePosition() );
+
+ // The player himself doesn't need to be sent his animation events
+ // unless cs_showanimstate wants to show them.
+ if ( !ToolsEnabled() && ( cl_showanimstate.GetInt() == pPlayer->entindex() ) )
+ {
+ filter.RemoveRecipient( pPlayer );
+ }
+
+ g_TEPlayerAnimEvent.m_hPlayer = pPlayer;
+ g_TEPlayerAnimEvent.m_iEvent = event;
+ g_TEPlayerAnimEvent.m_nData = nData;
+ g_TEPlayerAnimEvent.Create( filter, 0 );
+}
+
+
+// -------------------------------------------------------------------------------- //
+// Tables.
+// -------------------------------------------------------------------------------- //
+
+LINK_ENTITY_TO_CLASS( player, CTFCPlayer );
+PRECACHE_REGISTER(player);
+
+IMPLEMENT_SERVERCLASS_ST( CTFCPlayer, DT_TFCPlayer )
+ SendPropExclude( "DT_BaseAnimating", "m_flPoseParameter" ),
+ SendPropExclude( "DT_BaseAnimating", "m_flPlaybackRate" ),
+ SendPropExclude( "DT_BaseAnimating", "m_nSequence" ),
+ SendPropExclude( "DT_BaseEntity", "m_angRotation" ),
+ SendPropExclude( "DT_BaseAnimatingOverlay", "overlay_vars" ),
+
+ // cs_playeranimstate and clientside animation takes care of these on the client
+ SendPropExclude( "DT_ServerAnimationData" , "m_flCycle" ),
+ SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ),
+
+ SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 0), 11 ),
+ SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 1), 11 ),
+
+ SendPropDataTable( SENDINFO_DT( m_Shared ), &REFERENCE_SEND_TABLE( DT_TFCPlayerShared ) )
+END_SEND_TABLE()
+
+
+// -------------------------------------------------------------------------------- //
+
+void cc_CreatePredictionError_f()
+{
+ CBaseEntity *pEnt = CBaseEntity::Instance( 1 );
+ pEnt->SetAbsOrigin( pEnt->GetAbsOrigin() + Vector( 63, 0, 0 ) );
+}
+
+ConCommand cc_CreatePredictionError( "CreatePredictionError", cc_CreatePredictionError_f, "Create a prediction error", FCVAR_CHEAT );
+
+
+CTFCPlayer::CTFCPlayer()
+{
+ m_PlayerAnimState = CreatePlayerAnimState( this );
+ item_list = 0;
+
+ UseClientSideAnimation();
+ m_angEyeAngles.Init();
+ m_pCurStateInfo = NULL;
+ m_lifeState = LIFE_DEAD; // Start "dead".
+
+ SetViewOffset( TFC_PLAYER_VIEW_OFFSET );
+
+ SetContextThink( &CTFCPlayer::TFCPlayerThink, gpGlobals->curtime, "TFCPlayerThink" );
+}
+
+
+void CTFCPlayer::TFCPlayerThink()
+{
+ if ( m_pCurStateInfo && m_pCurStateInfo->pfnThink )
+ (this->*m_pCurStateInfo->pfnThink)();
+
+ SetContextThink( &CTFCPlayer::TFCPlayerThink, gpGlobals->curtime, "TFCPlayerThink" );
+}
+
+
+CTFCPlayer::~CTFCPlayer()
+{
+ m_PlayerAnimState->Release();
+}
+
+
+CTFCPlayer *CTFCPlayer::CreatePlayer( const char *className, edict_t *ed )
+{
+ CTFCPlayer::s_PlayerEdict = ed;
+ return (CTFCPlayer*)CreateEntityByName( className );
+}
+
+
+void CTFCPlayer::PostThink()
+{
+ BaseClass::PostThink();
+
+ QAngle angles = GetLocalAngles();
+ angles[PITCH] = 0;
+ SetLocalAngles( angles );
+
+ // Store the eye angles pitch so the client can compute its animation state correctly.
+ m_angEyeAngles = EyeAngles();
+
+ m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] );
+}
+
+
+void CTFCPlayer::Precache()
+{
+ for ( int i=0; i < PC_LASTCLASS; i++ )
+ PrecacheModel( GetTFCClassInfo( i )->m_pModelName );
+
+ PrecacheScriptSound( "Player.Spawn" );
+
+ BaseClass::Precache();
+}
+
+
+void CTFCPlayer::InitialSpawn( void )
+{
+ BaseClass::InitialSpawn();
+
+ State_Enter( STATE_WELCOME );
+}
+
+
+void CTFCPlayer::Spawn()
+{
+ SetModel( GetTFCClassInfo( m_Shared.GetPlayerClass() )->m_pModelName );
+
+ SetMoveType( MOVETYPE_WALK );
+ m_iLegDamage = 0;
+
+ BaseClass::Spawn();
+
+ // Kind of lame, but CBasePlayer::Spawn resets a lot of the state that we initially want on.
+ // So if we're in the welcome state, call its enter function to reset
+ if ( m_Shared.State_Get() == STATE_WELCOME )
+ {
+ State_Enter_WELCOME();
+ }
+
+ // If they were dead, then they're respawning. Put them in the active state.
+ if ( m_Shared.State_Get() == STATE_DYING )
+ {
+ State_Transition( STATE_ACTIVE );
+ }
+
+ // If they're spawning into the world as fresh meat, give them items and stuff.
+ if ( m_Shared.State_Get() == STATE_ACTIVE )
+ {
+ EmitSound( "Player.Spawn" );
+ GiveDefaultItems();
+ }
+}
+
+
+void CTFCPlayer::ForceRespawn()
+{
+ //TFCTODO: goldsrc tfc has a big function for this.. doing what I'm doing here may not work.
+ respawn( this, false );
+}
+
+
+void CTFCPlayer::GiveDefaultItems()
+{
+ switch( m_Shared.GetPlayerClass() )
+ {
+ case PC_HWGUY:
+ {
+ GiveNamedItem( "weapon_crowbar" );
+
+ GiveNamedItem( "weapon_minigun" );
+ GiveAmmo( 176, TFC_AMMO_SHELLS );
+ }
+ break;
+
+ case PC_PYRO:
+ {
+ GiveNamedItem( "weapon_crowbar" );
+ }
+ break;
+
+ case PC_ENGINEER:
+ {
+ GiveNamedItem( "weapon_spanner" );
+ GiveNamedItem( "weapon_super_shotgun" );
+ GiveAmmo( 20, TFC_AMMO_SHELLS );
+ }
+ break;
+
+ case PC_SCOUT:
+ {
+ GiveNamedItem( "weapon_crowbar" );
+ GiveNamedItem( "weapon_shotgun" );
+ GiveNamedItem( "weapon_nailgun" );
+ GiveAmmo( 25, TFC_AMMO_SHELLS );
+ GiveAmmo( 100, TFC_AMMO_NAILS );
+ }
+ break;
+
+ case PC_SNIPER:
+ {
+ GiveNamedItem( "weapon_crowbar" );
+ }
+ break;
+
+ case PC_SOLDIER:
+ {
+ GiveNamedItem( "weapon_crowbar" );
+ }
+ break;
+
+ case PC_DEMOMAN:
+ {
+ GiveNamedItem( "weapon_crowbar" );
+ }
+ break;
+
+ case PC_SPY:
+ {
+ GiveNamedItem( "weapon_knife" );
+ }
+ break;
+
+ case PC_MEDIC:
+ {
+ GiveNamedItem( "weapon_medikit" );
+ GiveNamedItem( "weapon_super_nailgun" );
+ GiveAmmo( 100, TFC_AMMO_NAILS );
+ }
+ break;
+ }
+}
+
+
+void CTFCPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
+{
+ m_PlayerAnimState->DoAnimationEvent( event, nData );
+ TE_PlayerAnimEvent( this, event, nData ); // Send to any clients who can see this guy.
+}
+
+
+void CTFCPlayer::State_Transition( TFCPlayerState newState )
+{
+ State_Leave();
+ State_Enter( newState );
+}
+
+
+void CTFCPlayer::State_Enter( TFCPlayerState newState )
+{
+ m_Shared.m_iPlayerState = newState;
+ m_pCurStateInfo = State_LookupInfo( newState );
+
+ // Initialize the new state.
+ if ( m_pCurStateInfo && m_pCurStateInfo->pfnEnterState )
+ (this->*m_pCurStateInfo->pfnEnterState)();
+}
+
+
+void CTFCPlayer::State_Leave()
+{
+ if ( m_pCurStateInfo && m_pCurStateInfo->pfnLeaveState )
+ {
+ (this->*m_pCurStateInfo->pfnLeaveState)();
+ }
+}
+
+
+CPlayerStateInfo* CTFCPlayer::State_LookupInfo( TFCPlayerState state )
+{
+ // This table MUST match the
+ static CPlayerStateInfo playerStateInfos[] =
+ {
+ { STATE_ACTIVE, "STATE_ACTIVE", &CTFCPlayer::State_Enter_ACTIVE, NULL, NULL },
+ { STATE_WELCOME, "STATE_WELCOME", &CTFCPlayer::State_Enter_WELCOME, NULL, NULL },
+ { STATE_PICKINGTEAM, "STATE_PICKINGTEAM", &CTFCPlayer::State_Enter_PICKINGTEAM, NULL, NULL },
+ { STATE_PICKINGCLASS, "STATE_PICKINGCLASS", &CTFCPlayer::State_Enter_PICKINGCLASS, NULL, NULL },
+ { STATE_OBSERVER_MODE, "STATE_OBSERVER_MODE", &CTFCPlayer::State_Enter_OBSERVER_MODE, NULL, NULL },
+ { STATE_DYING, "STATE_DYING", &CTFCPlayer::State_Enter_DYING, NULL, NULL }
+ };
+
+ for ( int i=0; i < ARRAYSIZE( playerStateInfos ); i++ )
+ {
+ if ( playerStateInfos[i].m_iPlayerState == state )
+ return &playerStateInfos[i];
+ }
+
+ return NULL;
+}
+
+
+void CTFCPlayer::State_Enter_WELCOME()
+{
+ SetMoveType( MOVETYPE_NONE );
+ AddEffects( EF_NODRAW );
+ AddSolidFlags( FSOLID_NOT_SOLID );
+ PhysObjectSleep();
+
+ // Show info panel (if it's not a simple demo map).
+ KeyValues *data = new KeyValues("data");
+ data->SetString( "title", "Message of the Day" ); // info panel title
+ data->SetString( "type", "3" ); // show a file
+ data->SetString( "msg", "motd.txt" ); // this file
+ data->SetString( "cmd", "joingame" ); // exec this command if panel closed
+
+ ShowViewPortPanel( "info", true, data );
+
+ data->deleteThis();
+}
+
+
+void CTFCPlayer::State_Enter_PICKINGTEAM()
+{
+ ShowViewPortPanel( PANEL_TEAM ); // show the team menu
+}
+
+
+void CTFCPlayer::State_Enter_PICKINGCLASS()
+{
+ // go to spec mode, if dying keep deathcam
+ if ( GetObserverMode() == OBS_MODE_DEATHCAM )
+ {
+ StartObserverMode( OBS_MODE_DEATHCAM );
+ }
+ else
+ {
+ StartObserverMode( OBS_MODE_ROAMING );
+ }
+
+ PhysObjectSleep();
+
+ // show the class menu:
+ ShowViewPortPanel( PANEL_CLASS );
+}
+
+
+void CTFCPlayer::State_Enter_OBSERVER_MODE()
+{
+ StartObserverMode( m_iObserverLastMode );
+ PhysObjectSleep();
+}
+
+
+void CTFCPlayer::State_Enter_ACTIVE()
+{
+ SetMoveType( MOVETYPE_WALK );
+ RemoveEffects( EF_NODRAW );
+ RemoveSolidFlags( FSOLID_NOT_SOLID );
+ m_Local.m_iHideHUD = 0;
+ PhysObjectWake();
+}
+
+
+void CTFCPlayer::State_Enter_DYING()
+{
+ SetMoveType( MOVETYPE_NONE );
+ AddSolidFlags( FSOLID_NOT_SOLID );
+}
+
+
+void CTFCPlayer::PhysObjectSleep()
+{
+ IPhysicsObject *pObj = VPhysicsGetObject();
+ if ( pObj )
+ pObj->Sleep();
+}
+
+
+void CTFCPlayer::PhysObjectWake()
+{
+ IPhysicsObject *pObj = VPhysicsGetObject();
+ if ( pObj )
+ pObj->Wake();
+}
+
+
+void CTFCPlayer::HandleCommand_JoinTeam( const char *pTeamName )
+{
+ int iTeam = TEAM_RED;
+ if ( stricmp( pTeamName, "auto" ) == 0 )
+ {
+ iTeam = RandomInt( 0, 1 ) ? TEAM_RED : TEAM_BLUE;
+ }
+ else if ( stricmp( pTeamName, "spectate" ) == 0 )
+ {
+ iTeam = TEAM_SPECTATOR;
+ }
+ else
+ {
+ for ( int i=0; i < TEAM_MAXCOUNT; i++ )
+ {
+ if ( stricmp( pTeamName, teamnames[i] ) == 0 )
+ {
+ iTeam = i;
+ break;
+ }
+ }
+ }
+
+ if ( iTeam == TEAM_SPECTATOR )
+ {
+ // Prevent this is the cvar is set
+ if ( !mp_allowspectators.GetInt() && !IsHLTV() )
+ {
+ ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Be_Spectator" );
+ return;
+ }
+
+ if ( GetTeamNumber() != TEAM_UNASSIGNED && !IsDead() )
+ {
+ CommitSuicide();
+
+ // add 1 to frags to balance out the 1 subtracted for killing yourself
+ IncrementFragCount( 1 );
+ }
+
+ ChangeTeam( TEAM_SPECTATOR );
+
+ // do we have fadetoblack on? (need to fade their screen back in)
+ if ( mp_fadetoblack.GetInt() )
+ {
+ color32_s clr = { 0,0,0,0 };
+ UTIL_ScreenFade( this, clr, 0.001, 0, FFADE_IN );
+ }
+ }
+ else
+ {
+ ChangeTeam( iTeam );
+ State_Transition( STATE_PICKINGCLASS );
+ }
+}
+
+
+void CTFCPlayer::ChangeTeam( int iTeamNum )
+{
+ if ( !GetGlobalTeam( iTeamNum ) )
+ {
+ Warning( "CCSPlayer::ChangeTeam( %d ) - invalid team index.\n", iTeamNum );
+ return;
+ }
+
+ int iOldTeam = GetTeamNumber();
+
+ // if this is our current team, just abort
+ if ( iTeamNum == iOldTeam )
+ return;
+
+ BaseClass::ChangeTeam( iTeamNum );
+
+ if ( iTeamNum == TEAM_UNASSIGNED )
+ {
+ State_Transition( STATE_OBSERVER_MODE );
+ }
+ else if ( iTeamNum == TEAM_SPECTATOR )
+ {
+ State_Transition( STATE_OBSERVER_MODE );
+ }
+ else // active player
+ {
+ if ( iOldTeam == TEAM_SPECTATOR )
+ {
+ // If they're switching from being a spectator to ingame player
+ GetIntoGame();
+ }
+
+ if ( !IsDead() && iOldTeam != TEAM_UNASSIGNED )
+ {
+ // Kill player if switching teams while alive
+ CommitSuicide();
+ }
+
+ // Put up the class selection menu.
+ State_Transition( STATE_PICKINGCLASS );
+ }
+}
+
+
+void CTFCPlayer::HandleCommand_JoinClass( const char *pClassName )
+{
+ int iClass = RandomInt( 0, PC_LAST_NORMAL_CLASS );
+ if ( stricmp( pClassName, "random" ) != 0 )
+ {
+ for ( int i=0; i < PC_LASTCLASS; i++ )
+ {
+ if ( stricmp( pClassName, GetTFCClassInfo( i )->m_pClassName ) == 0 )
+ {
+ iClass = i;
+ break;
+ }
+ }
+ if ( i == PC_LAST_NORMAL_CLASS )
+ {
+ Warning( "HandleCommand_JoinClass( %s ) - invalid class name.\n", pClassName );
+ }
+ }
+
+ m_Shared.SetPlayerClass( iClass );
+
+ if ( !IsAlive() )
+ GetIntoGame();
+}
+
+
+void CTFCPlayer::GetIntoGame()
+{
+ State_Transition( STATE_ACTIVE );
+ Spawn();
+}
+
+
+bool CTFCPlayer::ClientCommand( const CCommand& args )
+{
+ const char *pcmd = args[0];
+ if ( FStrEq( pcmd, "joingame" ) )
+ {
+ // player just closed MOTD dialog
+ if ( m_Shared.m_iPlayerState == STATE_WELCOME )
+ {
+ State_Transition( STATE_PICKINGTEAM );
+ }
+ return true;
+ }
+ else if ( FStrEq( pcmd, "jointeam" ) )
+ {
+ if ( args.ArgC() >= 2 )
+ {
+ HandleCommand_JoinTeam( args[1] );
+ return true;
+ }
+ }
+ else if ( FStrEq( pcmd, "joinclass" ) )
+ {
+ if ( args.ArgC() < 2 )
+ {
+ Warning( "Player sent bad joinclass syntax\n" );
+ }
+
+ HandleCommand_JoinClass( args[1] );
+ return true;
+ }
+
+ return BaseClass::ClientCommand( args );
+}
+
+
+bool CTFCPlayer::IsAlly( CBaseEntity *pEnt ) const
+{
+ return pEnt->GetTeamNumber() == GetTeamNumber();
+}
+
+
+void CTFCPlayer::TF_AddFrags( int nFrags )
+{
+ // TFCTODO: implement frags
+}
+
+
+void CTFCPlayer::ResetMenu()
+{
+ current_menu = 0;
+}
+
+
+int CTFCPlayer::GetNumFlames() const
+{
+ // TFCTODO: implement flames
+ return 0;
+}
+
+
+void CTFCPlayer::SetNumFlames( int nFlames )
+{
+ // TFCTODO: implement frags
+ Assert( 0 );
+}
+
+int CTFCPlayer::TakeHealth( float flHealth, int bitsDamageType )
+{
+ int bResult = false;
+
+ // If the bit's set, ignore the monster's max health and add over it
+ if ( bitsDamageType & DMG_IGNORE_MAXHEALTH )
+ {
+ int iDamage = g_pGameRules->Damage_GetTimeBased();
+ m_bitsDamageType &= ~(bitsDamageType & ~iDamage);
+ m_iHealth += flHealth;
+ bResult = true;
+ }
+ else
+ {
+ bResult = BaseClass::TakeHealth( flHealth, bitsDamageType );
+ }
+
+ // Leg Healing
+ if (m_iLegDamage > 0)
+ {
+ // Allow even at full health
+ if ( GetHealth() >= (GetMaxHealth() - 5))
+ m_iLegDamage = 0;
+ else
+ m_iLegDamage -= (GetHealth() + flHealth) / 20;
+ if (m_iLegDamage < 1)
+ m_iLegDamage = 0;
+
+ TeamFortress_SetSpeed();
+ bResult = true;
+ }
+
+ return bResult;
+}
+
+
+void CTFCPlayer::TeamFortress_SetSpeed()
+{
+ int playerclass = m_Shared.GetPlayerClass();
+ float maxfbspeed;
+
+ // Spectators can move while in Classic Observer mode
+ if ( IsObserver() )
+ {
+ if ( GetObserverMode() == OBS_MODE_ROAMING )
+ SetMaxSpeed( GetTFCClassInfo( PC_SCOUT )->m_flMaxSpeed );
+ else
+ SetMaxSpeed( 0 );
+
+ return;
+ }
+
+ // Check for any reason why they can't move at all
+ if ( (m_Shared.GetStateFlags() & TFSTATE_CANT_MOVE) || (playerclass == PC_UNDEFINED) )
+ {
+ SetAbsVelocity( vec3_origin );
+ SetMaxSpeed( 1 );
+ return;
+ }
+
+ // First, get their max class speed
+ maxfbspeed = GetTFCClassInfo( playerclass )->m_flMaxSpeed;
+
+ // 2nd, see if any GoalItems are slowing them down
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "item_tfgoal" );
+ while ( pEnt )
+ {
+ CTFGoal *pGoal = dynamic_cast<CTFGoal*>( pEnt );
+ if ( pGoal )
+ {
+ if ( pGoal->GetOwnerEntity() == this )
+ {
+ if (pGoal->goal_activation & TFGI_SLOW)
+ {
+ maxfbspeed = maxfbspeed / 2;
+ }
+ else if (pGoal->speed_reduction)
+ {
+ float flPercent = ((float)pGoal->speed_reduction) / 100.0;
+ maxfbspeed = flPercent * maxfbspeed;
+ }
+ }
+ }
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "item_tfgoal" );
+ }
+
+ // 3rd, See if they're tranquilised
+ if (m_Shared.GetStateFlags() & TFSTATE_TRANQUILISED)
+ {
+ maxfbspeed = maxfbspeed / 2;
+ }
+
+ // 4th, check for leg wounds
+ if (m_iLegDamage)
+ {
+ if (m_iLegDamage > 6)
+ m_iLegDamage = 6;
+
+ // reduce speed by 10% per leg wound
+ maxfbspeed = (maxfbspeed * ((10 - m_iLegDamage) / 10));
+ }
+
+ // 5th, if they're a sniper, and they're aiming, their speed must be 80 or less
+ if (m_Shared.GetStateFlags() & TFSTATE_AIMING)
+ {
+ if (maxfbspeed > 80)
+ maxfbspeed = 80;
+ }
+
+ // Set the speed
+ SetMaxSpeed( maxfbspeed );
+}
+
+
+void CTFCPlayer::Event_Killed( const CTakeDamageInfo &info )
+{
+ DoAnimationEvent( PLAYERANIMEVENT_DIE );
+ State_Transition( STATE_DYING ); // Transition into the dying state.
+
+ // Remove all items..
+ RemoveAllItems( true );
+
+ BaseClass::Event_Killed( info );
+
+ // Don't overflow the value for this.
+ m_iHealth = 0;
+}
+
+
+void CTFCPlayer::ClientHearVox( const char *pSentence )
+{
+ //TFCTODO: implement this.
+}
+
+
+//=========================================================================
+// Check all stats to make sure they're good for this class
+void CTFCPlayer::TeamFortress_CheckClassStats()
+{
+ // Check armor
+ if (armortype > armor_allowed)
+ armortype = armor_allowed;
+
+ if (ArmorValue() > GetClassInfo()->m_iMaxArmor)
+ SetArmorValue( GetClassInfo()->m_iMaxArmor );
+
+ if (ArmorValue() < 0)
+ SetArmorValue( 0 );
+
+ if (armortype < 0)
+ armortype = 0;
+
+ // Check ammo
+ for ( int iAmmoType=0; iAmmoType < TFC_NUM_AMMO_TYPES; iAmmoType++ )
+ {
+ if ( GetAmmoCount( iAmmoType ) > GetClassInfo()->m_MaxAmmo[iAmmoType] )
+ RemoveAmmo( GetAmmoCount( iAmmoType ) - GetClassInfo()->m_MaxAmmo[iAmmoType], iAmmoType );
+ }
+
+ // Check Grenades
+ Assert( GetAmmoCount( TFC_AMMO_GRENADES1 ) >= 0 );
+ Assert( GetAmmoCount( TFC_AMMO_GRENADES2 ) >= 0 );
+
+ // Limit Nails
+ if ( no_grenades_1() > g_nMaxGrenades[tp_grenades_1()] )
+ RemoveAmmo( TFC_AMMO_GRENADES1, no_grenades_1() - g_nMaxGrenades[tp_grenades_1()] );
+
+ if ( no_grenades_2() > g_nMaxGrenades[tp_grenades_2()] )
+ RemoveAmmo( TFC_AMMO_GRENADES2, no_grenades_2() - g_nMaxGrenades[tp_grenades_2()] );
+
+ // Check health
+ if (GetHealth() > GetMaxHealth() && !(m_Shared.GetItemFlags() & IT_SUPERHEALTH))
+ SetHealth( GetMaxHealth() );
+
+ if (GetHealth() < 0)
+ SetHealth( 0 );
+
+ // Update armor picture
+ m_Shared.RemoveItemFlags( IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3 );
+ if (armortype >= 0.8)
+ m_Shared.AddItemFlags( IT_ARMOR3 );
+ else if (armortype >= 0.6)
+ m_Shared.AddItemFlags( IT_ARMOR2 );
+ else if (armortype >= 0.3)
+ m_Shared.AddItemFlags( IT_ARMOR1 );
+}
+
+
+//======================================================================
+// DISGUISE HANDLING
+//======================================================================
+// Reset spy skin and color or remove invisibility
+void CTFCPlayer::Spy_RemoveDisguise()
+{
+ if (m_Shared.GetPlayerClass() == PC_SPY)
+ {
+ if ( undercover_team || undercover_skin )
+ ClientPrint( this, HUD_PRINTCENTER, "#Disguise_Lost" );
+
+ // Set their color
+ undercover_team = 0;
+ undercover_skin = 0;
+
+ immune_to_check = gpGlobals->curtime + 10;
+ is_undercover = 0;
+
+ // undisguise weapon
+ TeamFortress_SetSkin();
+ TeamFortress_SpyCalcName();
+
+ Spy_ResetExternalWeaponModel();
+
+ // get them out of any disguise menus
+ if ( current_menu == MENU_SPY || current_menu == MENU_SPY_SKIN || current_menu == MENU_SPY_COLOR )
+ {
+ ResetMenu();
+ }
+
+ // Remove the Disguise timer
+ CTimer *pTimer = Timer_FindTimer( this, TF_TIMER_DISGUISE );
+ if (pTimer)
+ {
+ ClientPrint( this, HUD_PRINTCENTER, "#Disguise_stop" );
+ Timer_Remove( pTimer );
+ }
+ }
+}
+
+
+// when the spy loses disguise reset his weapon
+void CTFCPlayer::Spy_ResetExternalWeaponModel( void )
+{
+ // we don't show any weapon models if we're feigning
+ if ( is_feigning )
+ return;
+
+#ifdef TFCTODO // spy
+ pev->weaponmodel = MAKE_STRING( m_pszSavedWeaponModel );
+ strcpy( m_szAnimExtention, m_szSavedAnimExtention );
+ m_iCurrentAnimationState = 0; // force the current animation sequence to be recalculated
+#endif
+}
+
+
+//=========================================================================
+// Try and find the player's name who's skin and team closest fit the
+// current disguise of the spy
+void CTFCPlayer::TeamFortress_SpyCalcName()
+{
+ CBaseEntity *last_target = undercover_target;// don't redisguise self as this person
+
+ undercover_target = NULL;
+
+ // Find a player on the team the spy is disguised as to pretend to be
+ if (undercover_team != 0)
+ {
+ CTFCPlayer *pPlayer = NULL;
+
+ // Loop through players
+ int i;
+ for ( i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ pPlayer = ToTFCPlayer( UTIL_PlayerByIndex( i ) );
+ if ( pPlayer )
+ {
+ if ( pPlayer == last_target )
+ {
+ // choose someone else, we're trying to rid ourselves of a disguise as this one
+ continue;
+ }
+
+ // First, try to find a player with same color and skins
+ if (pPlayer->GetTeamNumber() == undercover_team && pPlayer->m_Shared.GetPlayerClass() == undercover_skin)
+ {
+ undercover_target = pPlayer;
+ return;
+ }
+ }
+ }
+
+ for ( i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ pPlayer = ToTFCPlayer( UTIL_PlayerByIndex( i ) );
+
+ if ( pPlayer )
+ {
+ if (pPlayer->GetTeamNumber() == undercover_team)
+ {
+ undercover_target = pPlayer;
+ return;
+ }
+ }
+ }
+ }
+}
+
+
+//=========================================================================
+// Set the skin of a player based on his/her class
+void CTFCPlayer::TeamFortress_SetSkin()
+{
+ immune_to_check = gpGlobals->curtime + 10;
+
+ // Find out whether we should show our actual class or a disguised class
+ int iClassToUse = m_Shared.GetPlayerClass();
+ if (iClassToUse == PC_SPY && undercover_skin != 0)
+ iClassToUse = undercover_skin;
+
+ int iTeamToUse = GetTeamNumber();
+ if (m_Shared.GetPlayerClass() == PC_SPY && undercover_team != 0)
+ iTeamToUse = undercover_team;
+
+// TFCTODO: handle replacement_model here.
+
+ SetModel( GetTFCClassInfo( iClassToUse )->m_pModelName );
+
+ // Skins in the models should be setup using the team IDs in tfc_shareddefs.h, subtracting 1
+ // so they're 0-based.
+ m_nSkin = iTeamToUse - 1;
+
+ if ( FBitSet(GetFlags(), FL_DUCKING) )
+ UTIL_SetSize(this, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX);
+ else
+ UTIL_SetSize(this, VEC_HULL_MIN, VEC_HULL_MAX);
+}
+
+
+
+//=========================================================================
+// Displays the state of the items specified by the Goal passed in
+void CTFCPlayer::DisplayLocalItemStatus( CTFGoal *pGoal )
+{
+ for (int i = 0; i < 4; i++)
+ {
+ if (pGoal->display_item_status[i] != 0)
+ {
+ CTFGoalItem *pItem = Finditem(pGoal->display_item_status[i]);
+ if (pItem)
+ DisplayItemStatus(pGoal, this, pItem);
+ else
+ ClientPrint( this, HUD_PRINTTALK, "#Item_missing" );
+ }
+ }
+}
+
+
+//=========================================================================
+// Removes all the Engineer's buildings
+void CTFCPlayer::Engineer_RemoveBuildings()
+{
+ // If the player's building already, stop
+ if (is_building == 1)
+ {
+ m_Shared.RemoveStateFlags( TFSTATE_CANT_MOVE );
+ TeamFortress_SetSpeed();
+
+ // Remove the timer
+ CTimer *pTimer = Timer_FindTimer( this, TF_TIMER_BUILD );
+ if (pTimer)
+ Timer_Remove(pTimer);
+
+ // Remove the building
+ UTIL_Remove( building );
+ building = NULL;
+ is_building = 0;
+
+ // Stop Build Sound
+ StopSound( "Engineer.Building" );
+ //STOP_SOUND( ENT(pev), CHAN_STATIC, "weapons/building.wav" );
+
+ if ( GetActiveWeapon() )
+ GetActiveWeapon()->Deploy();
+ }
+
+ DestroyBuilding(this, "building_dispenser");
+ DestroyBuilding(this, "building_sentrygun");
+ DestroyTeleporter(this, BUILD_TELEPORTER_ENTRY);
+ DestroyTeleporter(this, BUILD_TELEPORTER_EXIT);
+}
+
+
+//=========================================================================
+// Removes all grenades that persist for a period of time from the world
+void CTFCPlayer::TeamFortress_RemoveLiveGrenades( void )
+{
+ RemoveOwnedEnt( "tf_weapon_napalmgrenade" );
+ RemoveOwnedEnt( "tf_weapon_nailgrenade" );
+ RemoveOwnedEnt( "tf_weapon_gasgrenade" );
+ RemoveOwnedEnt( "tf_weapon_caltrop" );
+}
+
+
+//=========================================================================
+// Removes all rockets the player has fired into the world
+// (this prevents a team kill cheat where players would fire rockets
+// then change teams to kill their own team)
+void CTFCPlayer::TeamFortress_RemoveRockets( void )
+{
+ RemoveOwnedEnt( "tf_rpg_rocket" );
+ RemoveOwnedEnt( "tf_ic_rocket" );
+}
+
+// removes the player's pipebombs with no explosions
+void CTFCPlayer::RemovePipebombs( void )
+{
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "tf_gl_pipebomb" );
+ while ( pEnt )
+ {
+ CTFCPlayer *pOwner = ToTFCPlayer( pEnt->GetOwnerEntity() );
+ if ( pOwner == this )
+ {
+ pOwner->m_iPipebombCount--;
+ pEnt->AddFlag( FL_KILLME );
+ }
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "tf_gl_pipebomb" );
+ }
+}
+
+
+//=========================================================================
+// Stops the setting of the detpack
+void CTFCPlayer::TeamFortress_DetpackStop( void )
+{
+ CTimer *pTimer = Timer_FindTimer( this, TF_TIMER_DETPACKSET );
+
+ if (!pTimer)
+ return;
+
+ ClientPrint( this, HUD_PRINTNOTIFY, "#Detpack_retrieve" );
+
+ // Return the detpack
+ GiveAmmo( 1, TFC_AMMO_DETPACK );
+ Timer_Remove(pTimer);
+
+ // Release player
+ m_Shared.RemoveStateFlags( TFSTATE_CANT_MOVE );
+ is_detpacking = 0;
+ TeamFortress_SetSpeed();
+
+ // Return their weapon
+ if ( GetActiveWeapon() )
+ GetActiveWeapon()->Deploy();
+}
+
+
+//=========================================================================
+// Removes any detpacks the player may have set
+BOOL CTFCPlayer::TeamFortress_RemoveDetpacks( void )
+{
+ // Remove all detpacks owned by the player
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "detpack" );
+ while ( pEnt )
+ {
+ // if the player owns this detpack, remove it
+ if ( pEnt->GetOwnerEntity() == this )
+ {
+ UTIL_Remove( pEnt );
+ return TRUE;
+ }
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "detpack" );
+ }
+
+ return FALSE;
+}
+
+
+//=========================================================================
+// Remove all of an ent owned by this player
+void CTFCPlayer::RemoveOwnedEnt( char *pEntName )
+{
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, pEntName );
+ while ( pEnt )
+ {
+ // if the player owns this entity, remove it
+ if ( pEnt->GetOwnerEntity() == this )
+ pEnt->AddFlag( FL_KILLME );
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, pEntName );
+ }
+}
+
+
diff --git a/game/server/tfc/tfc_player.h b/game/server/tfc/tfc_player.h
new file mode 100644
index 0000000..484aaa3
--- /dev/null
+++ b/game/server/tfc/tfc_player.h
@@ -0,0 +1,254 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Player for HL1.
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TFC_PLAYER_H
+#define TFC_PLAYER_H
+#pragma once
+
+
+#include "player.h"
+#include "server_class.h"
+#include "tfc_playeranimstate.h"
+#include "tfc_shareddefs.h"
+#include "tfc_player_shared.h"
+
+
+class CTFCPlayer;
+class CTFGoal;
+class CTFGoalItem;
+
+
+// Function table for each player state.
+class CPlayerStateInfo
+{
+public:
+ TFCPlayerState m_iPlayerState;
+ const char *m_pStateName;
+
+ void (CTFCPlayer::*pfnEnterState)(); // Init and deinit the state.
+ void (CTFCPlayer::*pfnLeaveState)();
+
+ void (CTFCPlayer::*pfnThink)(); // Called every frame.
+};
+
+
+//=============================================================================
+// >> CounterStrike player
+//=============================================================================
+class CTFCPlayer : public CBasePlayer
+{
+public:
+ DECLARE_CLASS( CTFCPlayer, CBasePlayer );
+ DECLARE_SERVERCLASS();
+
+
+ CTFCPlayer();
+ ~CTFCPlayer();
+
+ static CTFCPlayer *CreatePlayer( const char *className, edict_t *ed );
+ static CTFCPlayer* Instance( int iEnt );
+
+ // This passes the event to the client's and server's CPlayerAnimState.
+ void DoAnimationEvent( PlayerAnimEvent_t event );
+
+ virtual void PostThink();
+ virtual void InitialSpawn();
+ virtual void Spawn();
+ virtual void Precache();
+ virtual bool ClientCommand( const CCommand &args );
+ virtual void ChangeTeam( int iTeamNum ) OVERRIDE;
+ virtual int TakeHealth( float flHealth, int bitsDamageType );
+ virtual void Event_Killed( const CTakeDamageInfo &info );
+
+ void ClientHearVox( const char *pSentence );
+ void DisplayLocalItemStatus( CTFGoal *pGoal );
+
+
+public:
+
+ // Is this entity an ally (on our team)?
+ bool IsAlly( CBaseEntity *pEnt ) const;
+
+ TFCPlayerState State_Get() const; // Get the current state.
+
+ void TF_AddFrags( int nFrags );
+
+ void ResetMenu();
+
+ // On fire..
+ int GetNumFlames() const;
+ void SetNumFlames( int nFlames );
+
+ void ForceRespawn();
+
+ void TeamFortress_SetSpeed();
+ void TeamFortress_CheckClassStats();
+ void TeamFortress_SetSkin();
+ void TeamFortress_RemoveLiveGrenades();
+ void TeamFortress_RemoveRockets();
+ void TeamFortress_DetpackStop( void );
+
+ BOOL TeamFortress_RemoveDetpacks( void );
+ void RemovePipebombs( void );
+ void RemoveOwnedEnt( char *pEntName );
+
+// SPY STUFF
+public:
+
+ void Spy_RemoveDisguise();
+ void TeamFortress_SpyCalcName();
+ void Spy_ResetExternalWeaponModel( void );
+
+
+// ENGINEER STUFF
+public:
+
+ void Engineer_RemoveBuildings();
+
+ // Building
+ BOOL is_building; // TRUE for an ENGINEER if they're building something
+ EHANDLE building; // The building the ENGINEER is using
+ float building_wait; // Used to prevent using a building again immediately
+ EHANDLE real_owner;
+ float has_dispenser; // TRUE if engineer has a dispenser
+ float has_sentry; // TRUE if engineer has a sentry
+ float has_entry_teleporter; // TRUE if engineer has an entry teleporter
+ float has_exit_teleporter; // TRUE if engineer has an exit teleporter
+
+
+// DEMO STUFF
+public:
+
+ int m_iPipebombCount;
+
+
+public:
+
+ // Get the class info associated with us.
+ const CTFCPlayerClassInfo* GetClassInfo() const;
+
+ // Helpers to ease porting...
+ int tp_grenades_1() const { return GetClassInfo()->m_iGrenadeType1; }
+ int tp_grenades_2() const { return GetClassInfo()->m_iGrenadeType2; }
+ int no_grenades_1() const { return GetAmmoCount( TFC_AMMO_GRENADES1 ); }
+ int no_grenades_2() const { return GetAmmoCount( TFC_AMMO_GRENADES2 ); }
+
+
+public:
+
+ CTFCPlayerShared m_Shared;
+
+ int item_list; // Used to keep track of which goalitems are
+ // affecting the player at any time.
+ // GoalItems use it to keep track of their own
+ // mask to apply to a player's item_list
+
+ float armortype;
+ //float armorvalue; // Use CBasePlayer::m_ArmorValue.
+ int armorclass; // Type of armor being worn
+ float armor_allowed;
+
+ float invincible_finished;
+ float invisible_finished;
+ float super_damage_finished;
+ float radsuit_finished;
+
+ int lives; // The number of lives you have left
+ int is_unableto_spy_or_teleport;
+
+ BOOL bRemoveGrenade; // removes the primed grenade if set
+
+ // Replacement_Model Stuff
+ string_t replacement_model;
+ int replacement_model_body;
+ int replacement_model_skin;
+ int replacement_model_flags;
+
+ // Spy
+ int undercover_team; // The team the Spy is pretending to be in
+ int undercover_skin; // The skin the Spy is pretending to have
+ EHANDLE undercover_target; // The player the Spy is pretending to be
+ BOOL is_feigning; // TRUE for a SPY if they're feigning death
+ float immune_to_check;
+ BOOL is_undercover; // TRUE for a SPY if they're undercover
+
+ // TEAMFORTRESS VARIABLES
+ int no_sentry_message;
+ int no_dispenser_message;
+
+ // teleporter variables
+ int no_entry_teleporter_message;
+ int no_exit_teleporter_message;
+
+ BOOL is_detpacking; // TRUE for a DEMOMAN if they're setting a detpack
+
+ float current_menu; // is set to the number of the current menu, is 0 if they are not in a menu
+
+// State management.
+private:
+
+ void State_Transition( TFCPlayerState newState );
+ void State_Enter( TFCPlayerState newState );
+ void State_Leave();
+ CPlayerStateInfo* State_LookupInfo( TFCPlayerState state );
+
+ CPlayerStateInfo *m_pCurStateInfo;
+
+ void State_Enter_WELCOME();
+ void State_Enter_PICKINGTEAM();
+ void State_Enter_PICKINGCLASS();
+ void State_Enter_ACTIVE();
+ void State_Enter_OBSERVER_MODE();
+ void State_Enter_DYING();
+
+
+private:
+
+ friend void Bot_Think( CTFCPlayer *pBot );
+ void HandleCommand_JoinTeam( const char *pTeamName );
+ void HandleCommand_JoinClass( const char *pClassName );
+
+ void GiveDefaultItems();
+
+ void TFCPlayerThink();
+
+ void PhysObjectSleep();
+ void PhysObjectWake();
+
+ void GetIntoGame();
+
+
+private:
+
+ // Copyed from EyeAngles() so we can send it to the client.
+ CNetworkQAngle( m_angEyeAngles );
+
+ ITFCPlayerAnimState *m_PlayerAnimState;
+
+ int m_iLegDamage;
+};
+
+
+inline CTFCPlayer *ToTFCPlayer( CBaseEntity *pEntity )
+{
+ if ( !pEntity || !pEntity->IsPlayer() )
+ return NULL;
+
+#ifdef _DEBUG
+ Assert( dynamic_cast<CTFCPlayer*>( pEntity ) != 0 );
+#endif
+ return static_cast< CTFCPlayer* >( pEntity );
+}
+
+
+inline const CTFCPlayerClassInfo* CTFCPlayer::GetClassInfo() const
+{
+ return GetTFCClassInfo( m_Shared.GetPlayerClass() );
+}
+
+
+#endif // TFC_PLAYER_H
diff --git a/game/server/tfc/tfc_playermove.cpp b/game/server/tfc/tfc_playermove.cpp
new file mode 100644
index 0000000..6ef1a90
--- /dev/null
+++ b/game/server/tfc/tfc_playermove.cpp
@@ -0,0 +1,76 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "player_command.h"
+#include "igamemovement.h"
+#include "in_buttons.h"
+#include "ipredictionsystem.h"
+#include "tfc_player.h"
+
+
+static CMoveData g_MoveData;
+CMoveData *g_pMoveData = &g_MoveData;
+
+IPredictionSystem *IPredictionSystem::g_pPredictionSystems = NULL;
+
+
+//-----------------------------------------------------------------------------
+// Sets up the move data for TF2
+//-----------------------------------------------------------------------------
+class CTFCPlayerMove : public CPlayerMove
+{
+DECLARE_CLASS( CTFCPlayerMove, CPlayerMove );
+
+public:
+ virtual void StartCommand( CBasePlayer *player, CUserCmd *cmd );
+ virtual void SetupMove( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move );
+ virtual void FinishMove( CBasePlayer *player, CUserCmd *ucmd, CMoveData *move );
+};
+
+// PlayerMove Interface
+static CTFCPlayerMove g_PlayerMove;
+
+//-----------------------------------------------------------------------------
+// Singleton accessor
+//-----------------------------------------------------------------------------
+CPlayerMove *PlayerMove()
+{
+ return &g_PlayerMove;
+}
+
+//-----------------------------------------------------------------------------
+// Main setup, finish
+//-----------------------------------------------------------------------------
+
+void CTFCPlayerMove::StartCommand( CBasePlayer *player, CUserCmd *cmd )
+{
+ BaseClass::StartCommand( player, cmd );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: This is called pre player movement and copies all the data necessary
+// from the player for movement. (Server-side, the client-side version
+// of this code can be found in prediction.cpp.)
+//-----------------------------------------------------------------------------
+void CTFCPlayerMove::SetupMove( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move )
+{
+ BaseClass::SetupMove( player, ucmd, pHelper, move );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: This is called post player movement to copy back all data that
+// movement could have modified and that is necessary for future
+// movement. (Server-side, the client-side version of this code can
+// be found in prediction.cpp.)
+//-----------------------------------------------------------------------------
+void CTFCPlayerMove::FinishMove( CBasePlayer *player, CUserCmd *ucmd, CMoveData *move )
+{
+ // Call the default FinishMove code.
+ BaseClass::FinishMove( player, ucmd, move );
+}
diff --git a/game/server/tfc/tfc_team.cpp b/game/server/tfc/tfc_team.cpp
new file mode 100644
index 0000000..c4c7400
--- /dev/null
+++ b/game/server/tfc/tfc_team.cpp
@@ -0,0 +1,279 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Team management class. Contains all the details for a specific team
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tfc_team.h"
+#include "entitylist.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+// Datatable
+IMPLEMENT_SERVERCLASS_ST(CTFCTeam, DT_TFCTeam)
+END_SEND_TABLE()
+
+LINK_ENTITY_TO_CLASS( tfc_team_manager, CTFCTeam );
+
+
+Vector rgbcolors[5];
+team_color_t teamcolors[5][PC_LASTCLASS]; // Colors for each of the 4 teams
+int number_of_teams = 0; // This is incremented for each map as info_player_teamspawn are created.
+const char *teamnames[5] =
+{
+ "spectator",
+ "blue",
+ "red",
+ "yellow",
+ "green"
+};
+
+//========================================================================
+// Set the color for the team corresponding to the no passed in, to team_no
+void TeamFortress_TeamSetColor()
+{
+ // Blue Team
+ teamcolors[1][PC_SCOUT].topColor = 153;
+ teamcolors[1][PC_SCOUT].bottomColor = 139;
+
+ teamcolors[1][PC_SNIPER].topColor = 153;
+ teamcolors[1][PC_SNIPER].bottomColor = 145;
+
+ teamcolors[1][PC_SOLDIER].topColor = 153;
+ teamcolors[1][PC_SOLDIER].bottomColor = 130;
+
+ teamcolors[1][PC_DEMOMAN].topColor = 153;
+ teamcolors[1][PC_DEMOMAN].bottomColor = 145;
+
+ teamcolors[1][PC_MEDIC].topColor = 153;
+ teamcolors[1][PC_MEDIC].bottomColor = 140;
+
+ teamcolors[1][PC_HWGUY].topColor = 148;
+ teamcolors[1][PC_HWGUY].bottomColor = 138;
+
+ teamcolors[1][PC_PYRO].topColor = 140;
+ teamcolors[1][PC_PYRO].bottomColor = 145;
+
+ teamcolors[1][PC_SPY].topColor = 150;
+ teamcolors[1][PC_SPY].bottomColor = 145;
+
+ teamcolors[1][PC_ENGINEER].topColor = 140;
+ teamcolors[1][PC_ENGINEER].bottomColor = 148;
+
+ teamcolors[1][PC_CIVILIAN].topColor = 150;
+ teamcolors[1][PC_CIVILIAN].bottomColor = 140;
+
+#ifdef TFCTODO // sentry colors
+ teamcolors[1][SENTRY_COLOR].topColor = 150;
+ teamcolors[1][SENTRY_COLOR].bottomColor = 0;
+
+ teamcolors[2][SENTRY_COLOR].topColor = 250;
+ teamcolors[2][SENTRY_COLOR].bottomColor = 0;
+
+ teamcolors[3][SENTRY_COLOR].topColor = 45;
+ teamcolors[3][SENTRY_COLOR].bottomColor = 0;
+
+ teamcolors[4][SENTRY_COLOR].topColor = 100;
+ teamcolors[4][SENTRY_COLOR].bottomColor = 0;
+#endif
+
+ // Red Team
+ teamcolors[2][PC_SCOUT].topColor = 255;
+ teamcolors[2][PC_SCOUT].bottomColor = 10;
+
+ teamcolors[2][PC_SNIPER].topColor = 255;
+ teamcolors[2][PC_SNIPER].bottomColor = 10;
+
+ teamcolors[2][PC_SOLDIER].topColor = 250;
+ teamcolors[2][PC_SOLDIER].bottomColor = 28;
+
+ teamcolors[2][PC_DEMOMAN].topColor = 255;
+ teamcolors[2][PC_DEMOMAN].bottomColor = 20;
+
+ teamcolors[2][PC_MEDIC].topColor = 255;
+ teamcolors[2][PC_MEDIC].bottomColor = 250;
+
+ teamcolors[2][PC_HWGUY].topColor = 255;
+ teamcolors[2][PC_HWGUY].bottomColor = 25;
+
+ teamcolors[2][PC_PYRO].topColor = 250;
+ teamcolors[2][PC_PYRO].bottomColor = 25;
+
+ teamcolors[2][PC_SPY].topColor = 250;
+ teamcolors[2][PC_SPY].bottomColor = 240;
+
+ teamcolors[2][PC_ENGINEER].topColor = 5;
+ teamcolors[2][PC_ENGINEER].bottomColor = 250;
+
+ teamcolors[2][PC_CIVILIAN].topColor = 250;
+ teamcolors[2][PC_CIVILIAN].bottomColor = 240;
+
+
+ // Yellow Team
+ teamcolors[3][PC_SCOUT].topColor = 45;
+ teamcolors[3][PC_SCOUT].bottomColor = 35;
+
+ teamcolors[3][PC_SNIPER].topColor = 45;
+ teamcolors[3][PC_SNIPER].bottomColor = 35;
+
+ teamcolors[3][PC_SOLDIER].topColor = 45;
+ teamcolors[3][PC_SOLDIER].bottomColor = 35;
+
+ teamcolors[3][PC_DEMOMAN].topColor = 45;
+ teamcolors[3][PC_DEMOMAN].bottomColor = 35;
+
+ teamcolors[3][PC_MEDIC].topColor = 45;
+ teamcolors[3][PC_MEDIC].bottomColor = 35;
+
+ teamcolors[3][PC_HWGUY].topColor = 45;
+ teamcolors[3][PC_HWGUY].bottomColor = 40;
+
+ teamcolors[3][PC_PYRO].topColor = 45;
+ teamcolors[3][PC_PYRO].bottomColor = 35;
+
+ teamcolors[3][PC_SPY].topColor = 45;
+ teamcolors[3][PC_SPY].bottomColor = 35;
+
+ teamcolors[3][PC_ENGINEER].topColor = 45;
+ teamcolors[3][PC_ENGINEER].bottomColor = 45;
+
+ teamcolors[3][PC_CIVILIAN].topColor = 45;
+ teamcolors[3][PC_CIVILIAN].bottomColor = 35;
+
+ // Green Team
+ teamcolors[4][PC_SCOUT].topColor = 100;
+ teamcolors[4][PC_SCOUT].bottomColor = 90;
+
+ teamcolors[4][PC_SNIPER].topColor = 80;
+ teamcolors[4][PC_SNIPER].bottomColor = 90;
+
+ teamcolors[4][PC_SOLDIER].topColor = 100;
+ teamcolors[4][PC_SOLDIER].bottomColor = 40;
+
+ teamcolors[4][PC_DEMOMAN].topColor = 100;
+ teamcolors[4][PC_DEMOMAN].bottomColor = 90;
+
+ teamcolors[4][PC_MEDIC].topColor = 100;
+ teamcolors[4][PC_MEDIC].bottomColor = 90;
+
+ teamcolors[4][PC_HWGUY].topColor = 100;
+ teamcolors[4][PC_HWGUY].bottomColor = 90;
+
+ teamcolors[4][PC_PYRO].topColor = 100;
+ teamcolors[4][PC_PYRO].bottomColor = 50;
+
+ teamcolors[4][PC_SPY].topColor = 100;
+ teamcolors[4][PC_SPY].bottomColor = 90;
+
+ teamcolors[4][PC_ENGINEER].topColor = 100;
+ teamcolors[4][PC_ENGINEER].bottomColor = 90;
+
+ teamcolors[4][PC_CIVILIAN].topColor = 100;
+ teamcolors[4][PC_CIVILIAN].bottomColor = 90;
+
+ rgbcolors[0] = Vector( 255, 255, 255 ); // White for non-owned
+ rgbcolors[1] = Vector( 0, 0, 255 );
+ rgbcolors[2] = Vector( 255, 0, 0 );
+ rgbcolors[3] = Vector( 255, 255, 30 );
+ rgbcolors[4] = Vector( 0, 255, 0 );
+}
+class CColorInitializer
+{
+public:
+ CColorInitializer()
+ {
+ TeamFortress_TeamSetColor();
+ }
+} g_ColorInitializer;
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Get a pointer to the specified TF team manager
+//-----------------------------------------------------------------------------
+CTFCTeam *GetGlobalTFCTeam( int iIndex )
+{
+ return (CTFCTeam*)GetGlobalTeam( iIndex );
+}
+
+
+// Display all the Team Scores
+void TeamFortress_TeamShowScores(BOOL bLong, CBasePlayer *pPlayer)
+{
+ for (int i = 1; i < g_Teams.Count(); i++)
+ {
+ if (!bLong)
+ {
+ // Dump short scores
+ UTIL_ClientPrintAll( HUD_PRINTNOTIFY, UTIL_VarArgs("%s: %d\n", g_szTeamColors[i], GetGlobalTeam(i)->GetScore()) );
+ }
+ else
+ {
+ // Dump long scores
+ if (pPlayer == NULL)
+ UTIL_ClientPrintAll( HUD_PRINTNOTIFY, UTIL_VarArgs("Team %d (%s): %d\n", i, g_szTeamColors[i], GetGlobalTeam(i)->GetScore()) );
+ else // Print to just one client
+ ClientPrint( pPlayer, HUD_PRINTNOTIFY, UTIL_VarArgs("Team %d (%s): %d\n", i, g_szTeamColors[i], GetGlobalTeam(i)->GetScore()) );
+ }
+ }
+}
+
+
+//=========================================================================
+// Return the score/frags of a team, depending on whether TeamFrags is on
+int TeamFortress_TeamGetScoreFrags(int tno)
+{
+ CTeam *pTeam = GetGlobalTeam( tno );
+ if ( pTeam )
+ {
+ return pTeam->GetScore();
+ }
+ else
+ {
+ Assert( false );
+ return -1;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Needed because this is an entity, but should never be used
+//-----------------------------------------------------------------------------
+void CTFCTeam::Init( const char *pName, int iNumber )
+{
+ BaseClass::Init( pName, iNumber );
+
+ // Only detect changes every half-second.
+ NetworkProp()->SetUpdateInterval( 0.75f );
+}
+
+
+color32 CTFCTeam::GetTeamColor()
+{
+ int i = GetTeamNumber();
+ if ( i >= 0 && i < ARRAYSIZE( rgbcolors ) )
+ {
+ return Vector255ToRGBColor( rgbcolors[i] );
+ }
+ else
+ {
+ Assert( false );
+ color32 x;
+ memset( &x, 0, sizeof( x ) );
+ return x;
+ }
+}
+
+
+color32 Vector255ToRGBColor( const Vector &vColor )
+{
+ color32 ret;
+ ret.a = 0;
+ ret.r = (byte)vColor.x;
+ ret.g = (byte)vColor.y;
+ ret.b = (byte)vColor.z;
+ return ret;
+}
diff --git a/game/server/tfc/tfc_team.h b/game/server/tfc/tfc_team.h
new file mode 100644
index 0000000..79953c7
--- /dev/null
+++ b/game/server/tfc/tfc_team.h
@@ -0,0 +1,58 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Team management class. Contains all the details for a specific team
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TFC_TEAM_H
+#define TFC_TEAM_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "utlvector.h"
+#include "team.h"
+#include "tfc_shareddefs.h"
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Team Manager
+//-----------------------------------------------------------------------------
+class CTFCTeam : public CTeam
+{
+ DECLARE_CLASS( CTFCTeam, CTeam );
+ DECLARE_SERVERCLASS();
+
+public:
+
+ // Initialization
+ virtual void Init( const char *pName, int iNumber );
+ color32 GetTeamColor();
+};
+
+
+extern CTFCTeam *GetGlobalTFCTeam( int iIndex );
+
+void TeamFortress_TeamShowScores(BOOL bLong, CBasePlayer *pPlayer);
+int TeamFortress_TeamGetScoreFrags(int tno);
+
+// Colors for each team.
+typedef struct
+{
+ int topColor;
+ int bottomColor;
+} team_color_t;
+
+extern Vector rgbcolors[5];
+extern team_color_t teamcolors[5][PC_LASTCLASS]; // Colors for each of the 4 teams
+extern int number_of_teams; // This is incremented for each map as info_player_teamspawn are created.
+extern const char *teamnames[5];
+#define g_szTeamColors teamnames
+
+color32 Vector255ToRGBColor( const Vector &vColor );
+
+
+#endif // TF_TEAM_H
diff --git a/game/server/tfc/tfc_timer.cpp b/game/server/tfc/tfc_timer.cpp
new file mode 100644
index 0000000..a518602
--- /dev/null
+++ b/game/server/tfc/tfc_timer.cpp
@@ -0,0 +1,117 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "tfc_timer.h"
+
+
+static CUtlLinkedList<CTimer*,int> g_Timers;
+
+
+// ------------------------------------------------------------------------------------------ //
+// CTimer functions.
+// ------------------------------------------------------------------------------------------ //
+
+CTimer::CTimer()
+{
+ m_iTeamNumber = 0;
+ m_flNextThink = 0;
+}
+
+
+int CTimer::GetTeamNumber() const
+{
+ return m_iTeamNumber;
+}
+
+
+// ------------------------------------------------------------------------------------------ //
+// Global timer functions.
+// ------------------------------------------------------------------------------------------ //
+
+CTimer* Timer_FindTimer( CBaseEntity *pPlayer, TFCTimer_t timerType )
+{
+ FOR_EACH_LL( g_Timers, i )
+ {
+ CTimer *pTimer = g_Timers[i];
+
+ if ( pTimer->m_hOwner == pPlayer )
+ {
+ if ( timerType == TF_TIMER_ANY || pTimer->m_Type == timerType )
+ return pTimer;
+ }
+ }
+ return NULL;
+}
+
+
+CTimer* Timer_CreateTimer( CBaseEntity *pPlayer, TFCTimer_t timerType )
+{
+ Assert( !Timer_FindTimer( pPlayer, timerType ) );
+
+ CTimer *pTimer = new CTimer;
+ pTimer->m_hOwner = pPlayer;
+ pTimer->m_Type = timerType;
+ pTimer->m_iListIndex = g_Timers.AddToTail( pTimer );
+
+ // TFCTODO: Register the think functions here..
+ if ( pTimer->m_Type == TF_TIMER_ROTHEALTH )
+ {
+ pTimer->m_flNextThink = gpGlobals->curtime + 5;
+ }
+ else if ( pTimer->m_Type == TF_TIMER_INFECTION )
+ {
+ pTimer->m_flNextThink = gpGlobals->curtime + 2;
+ }
+
+ // TFCTODO: hook up thinks...
+ // TF_TIMER_RETURNITEM -> CBaseEntity::ReturnItem -> <up to caller>
+ // TF_TIMER_ENDROUND -> CBaseEntity::EndRoundEnd -> <up to caller>
+
+ return pTimer;
+}
+
+
+void Timer_Remove( CTimer *pTimer )
+{
+ g_Timers.Remove( pTimer->m_iListIndex );
+ delete pTimer;
+}
+
+
+void Timer_UpdateAll()
+{
+ int iNext = 0;
+ int i = g_Timers.Head();
+ while ( i != g_Timers.InvalidIndex() )
+ {
+ iNext = g_Timers.Next( i );
+ CTimer *pTimer = g_Timers[i];
+ i = iNext;
+
+ // Get rid of invalid timers.
+ if ( pTimer->m_hOwner.Get() == NULL )
+ {
+ g_Timers.Remove( i );
+ delete pTimer;
+ }
+ else
+ {
+ // Is it time to think for this timer?
+ if ( gpGlobals->curtime >= pTimer->m_flNextThink )
+ {
+ // TFCTODO: think here.
+ }
+ }
+ }
+}
+
+
+void Timer_RemoveAll()
+{
+ g_Timers.PurgeAndDeleteElements();
+}
+
diff --git a/game/server/tfc/tfc_timer.h b/game/server/tfc/tfc_timer.h
new file mode 100644
index 0000000..156eb10
--- /dev/null
+++ b/game/server/tfc/tfc_timer.h
@@ -0,0 +1,60 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef TFC_TIMER_H
+#define TFC_TIMER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "ehandle.h"
+#include "tfc_shareddefs.h"
+
+
+class CTFCPlayer;
+
+
+class CTimer
+{
+public:
+ CTimer();
+
+ int GetTeamNumber() const;
+
+public:
+ EHANDLE m_hOwner;
+ EHANDLE m_hEnemy;
+ TFCTimer_t m_Type; // One of the TF_TIMER_ defines.
+ int m_iTeamNumber;
+ float m_flNextThink;
+ int weapon; // GI_RET_ define.
+
+ // For g_Timers.
+ int m_iListIndex;
+};
+
+
+// This stuff replaces the functions like CBaseEntity::FindTimer, CBaseEntity::CreateTimer,
+// and all the timer handlers in TFC.
+
+// Find an active timer on the specified entity.
+CTimer* Timer_FindTimer( CBaseEntity *pPlayer, TFCTimer_t timerType );
+
+// Create a new timer.
+CTimer* Timer_CreateTimer( CBaseEntity *pPlayer, TFCTimer_t timerType );
+
+// Get rid of a timer.
+void Timer_Remove( CTimer *pTimer );
+
+// Update all timers.
+void Timer_UpdateAll();
+
+// Call at round restart.
+void Timer_RemoveAll();
+
+
+#endif // TFC_TIMER_H