Portable Half-Life SDK. GoldSource and Xash3D. Crossplatform.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

641 lines
16 KiB

//++ muphicks
// AGDomination mode
// Based on the AG CTF code by BulliT
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "player.h"
#include "weapons.h"
#include "gamerules.h"
#include "aggamerules.h"
#include "agglobal.h"
#include "agdom.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
// Use the existing CTF message for now since I don't really want
// to have to make client changes at this time.. Once I get the new
// flag model from aljosa@lovercic@siol.net then I will probably
// make a new message if its required and add some new sounds.
extern int gmsgCTFSound;
enum DOMSound
{
YouHaveFlag = 0,
TeamHaveFlag,
EnemyHaveFlag,
BlueFlagReturned,
RedFlagReturned,
BlueScores,
RedScores,
BlueFlagStolen,
RedFlagStolen,
//not used...
BlueLeads,
RedLeads,
TeamsTied,
SuddenDeath,
Stolen,
Capture,
};
extern int gmsgTeamScore;
FILE_GLOBAL int s_iTeam1Score;
FILE_GLOBAL int s_iTeam2Score;
AgDOM::AgDOM()
{
m_iTeam1Score = 0;
m_iTeam2Score = 0;
s_iTeam1Score = 0;
s_iTeam2Score = 0;
}
AgDOM::~AgDOM()
{
}
typedef list<int> boo;
void AgDOM::PlayerInitHud(CBasePlayer* pPlayer)
{
ASSERT(NULL != pPlayer);
if (!pPlayer)
return;
ASSERT(NULL != pPlayer->pev);
if (!pPlayer->pev)
return;
MESSAGE_BEGIN( MSG_ONE, gmsgTeamScore, NULL, pPlayer->pev );
WRITE_STRING( DOM_TEAM1_NAME);
WRITE_SHORT( m_iTeam1Score );
WRITE_SHORT( 0 );
MESSAGE_END();
MESSAGE_BEGIN( MSG_ONE, gmsgTeamScore, NULL, pPlayer->pev );
WRITE_STRING( DOM_TEAM2_NAME);
WRITE_SHORT( m_iTeam2Score );
WRITE_SHORT( 0 );
MESSAGE_END();
}
void AgDOM::SendControlScores(CBasePlayer* pPlayer)
{
ASSERT(NULL != pPlayer);
if (!pPlayer)
return;
ASSERT(NULL != pPlayer->pev);
if (!pPlayer->pev)
return;
MESSAGE_BEGIN( MSG_ONE, gmsgTeamScore, NULL, pPlayer->pev );
WRITE_STRING( DOM_TEAM1_NAME);
WRITE_SHORT( s_iTeam1Score );
WRITE_SHORT( 0 );
MESSAGE_END();
MESSAGE_BEGIN( MSG_ONE, gmsgTeamScore, NULL, pPlayer->pev );
WRITE_STRING( DOM_TEAM2_NAME);
WRITE_SHORT( s_iTeam2Score );
WRITE_SHORT( 0 );
MESSAGE_END();
}
bool AgDOM::ScoreLimit(void)
{
if (ag_dom_scorelimit.value > 1
&& ( s_iTeam1Score >= ag_dom_scorelimit.value
||s_iTeam2Score >= ag_dom_scorelimit.value))
return true;
return false;
}
void AgDOM::ResetControlPoints(void)
{
// Looping through entity finds !
edict_t *pFind;
// Grab a list of control points
pFind = FIND_ENTITY_BY_CLASSNAME( NULL, "item_dom_controlpoint" );
while ( !FNullEnt( pFind ) )
{
// reset each one back to the NEUTRAL team ie uncaptured state
CBaseEntity *pEnt = CBaseEntity::Instance( pFind );
AgDOMControlPoint *pControlPoint = (AgDOMControlPoint *)pEnt;
pControlPoint->Reset();
pFind = FIND_ENTITY_BY_CLASSNAME( pFind, "object_world" );
}
// reset scores here??
m_iTeam1Score = 0;
m_iTeam2Score = 0;
s_iTeam1Score = 0;
s_iTeam2Score = 0;
}
void AgDOM::Think()
{
if (!g_pGameRules)
return;
// Has there been a new capture? Yes then we'd best see if one
// team controls all the capture points or not.
// Play BLUE/RED TEAM DOMINATE
// send update of scores
if (m_iTeam1Score != s_iTeam1Score
||m_iTeam2Score != s_iTeam2Score)
{
m_iTeam1Score = s_iTeam1Score;
//Send new team score to all clients.
MESSAGE_BEGIN( MSG_ALL, gmsgTeamScore );
WRITE_STRING( DOM_TEAM1_NAME);
WRITE_SHORT( s_iTeam1Score );
WRITE_SHORT( 0 );
MESSAGE_END();
m_iTeam2Score = s_iTeam2Score;
//Send new team score to all clients.
MESSAGE_BEGIN( MSG_ALL, gmsgTeamScore );
WRITE_STRING( CTF_TEAM2_NAME);
WRITE_SHORT( s_iTeam2Score );
WRITE_SHORT( 0 );
MESSAGE_END();
// Could check whether a team has all the control points and if so play DOMINATION sound?
}
m_FileItemCache.Init();
}
void AgDOM::ClientConnected(CBasePlayer* pPlayer)
{
ASSERT(NULL != pPlayer);
if (!pPlayer)
return;
ASSERT(NULL != pPlayer->pev);
if (!pPlayer->pev)
return;
}
void AgDOM::ClientDisconnected(CBasePlayer* pPlayer)
{
ASSERT(NULL != pPlayer);
if (!pPlayer)
return;
ASSERT(NULL != pPlayer->pev);
if (!pPlayer->pev)
return;
// remove player from any active scoring controls
edict_t *pFind;
// Grab a list of control points
pFind = FIND_ENTITY_BY_CLASSNAME( NULL, "item_dom_controlpoint" );
while ( !FNullEnt( pFind ) )
{
// reset each one back to the NEUTRAL team ie uncaptured state
CBaseEntity *pEnt = CBaseEntity::Instance( pFind );
AgDOMControlPoint *pControlPoint = (AgDOMControlPoint *)pEnt;
pControlPoint->ClientDisconnected( pPlayer );
pFind = FIND_ENTITY_BY_CLASSNAME( pFind, "item_dom_controlpoint" );
}
}
///////////////////////////////////////////////////////////////////////////////////
//
// AG DOM Control Point
//
///////////////////////////////////////////////////////////////////////////////////
// most of these are not used, but may as well have them for later :)
enum Flag_Animations
{
ON_GROUND = 0,
NOT_CARRIED,
CARRIED,
WAVE_IDLE,
FLAG_POSITION
};
void AgDOMControlPoint::Capture(CBasePlayer *pPlayer, const char *szTeamName)
{
// if players team already controls this area don't recapture
if (FStrEq(m_szTeamName, szTeamName))
return;
ChangeControllingTeam( szTeamName );
m_fCaptureTime = gpGlobals->time + ag_dom_mincontroltime.value;
m_iConsecutiveScores = 0; // reset score count
pCapturingPlayer = pPlayer;
if ( 0 == strcmp(DOM_TEAM1_NAME,szTeamName))
{
MESSAGE_BEGIN( MSG_ALL, gmsgCTFSound );
WRITE_BYTE( RedFlagStolen );
MESSAGE_END();
}
else if ( 0 == strcmp(DOM_TEAM2_NAME,szTeamName))
{
MESSAGE_BEGIN( MSG_ALL, gmsgCTFSound );
WRITE_BYTE( BlueFlagStolen );
MESSAGE_END();
}
// Inform HLTV of this aweinspiring event :P Since there will be many more captures
// happening in DOM than in CTF don't bother with slow mo for now.
UTIL_SendDirectorMessage( pPlayer->edict(), this->edict(), 10 | DRC_FLAG_DRAMATIC );
// Inform all players that zone has been taken control of by playername
// really need identifiers for each control point!
char szText[300];
sprintf(szText, "%s captures CP at %s!", STRING(pPlayer->pev->netname), m_szLocation);
AgConsole(szText);
UTIL_ClientPrintAll( HUD_PRINTCENTER, szText );
}
void AgDOMControlPoint::ChangeControllingTeam( const char *szTeamName )
{
if (FStrEq(DOM_TEAM1_NAME, szTeamName))
pev->skin = 1;
else if (FStrEq(DOM_TEAM2_NAME, szTeamName))
pev->skin = 2;
else
pev->skin = 3;
if (FStrEq(DOM_TEAM1_NAME, szTeamName))
{
pev->rendercolor.x = 0;
pev->rendercolor.y = 0;
pev->rendercolor.z = 128;
pev->renderamt = 50;
}
else if (FStrEq(DOM_TEAM2_NAME, szTeamName))
{
pev->rendercolor.x = 128;
pev->rendercolor.y = 0;
pev->rendercolor.z = 0;
pev->renderamt = 50;
}
else if (FStrEq(DOM_NEUTRAL_NAME, szTeamName))
{
pev->rendercolor.x = 0; // R
pev->rendercolor.y = 128; // G
pev->rendercolor.z = 0; // B
pev->renderamt = 100;
}
// Change the owner of the control point
strncpy( m_szTeamName, szTeamName, sizeof(m_szTeamName) );
}
void AgDOMControlPoint::Spawn ( void )
{
m_fNextTouch = 0;
Precache( );
SET_MODEL(ENT(pev), "models/flag.mdl");
pev->movetype = MOVETYPE_TOSS;
pev->solid = SOLID_TRIGGER;
UTIL_SetOrigin( pev, pev->origin );
UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 16));
SetThink( &AgDOMControlPoint::Think );
SetTouch( &AgDOMControlPoint::Touch );
pev->nextthink = gpGlobals->time + 0.1f;
// spawn by default under no teams control
ChangeControllingTeam( DOM_NEUTRAL_NAME );
pev->sequence = NOT_CARRIED;
pev->framerate = 1;
pev->renderamt = 50;
pev->renderfx = kRenderFxGlowShell;
}
void AgDOMControlPoint::Reset( void )
{
ChangeControllingTeam( DOM_NEUTRAL_NAME );
m_fCaptureTime = -1;
m_iConsecutiveScores = 0; // reset score count
pCapturingPlayer = NULL;
// Inform HLTV of this event, although its more exciting than an empty room its not that exciting :(
UTIL_SendDirectorMessage( this->edict(), NULL, 1 );
}
void AgDOMControlPoint::Precache( void )
{
PRECACHE_MODEL ("models/flag.mdl");
}
void AgDOMControlPoint::Think( void )
{
// Has the flag been under control long enough to score a team point?
if( !FStrEq( m_szTeamName, DOM_NEUTRAL_NAME ))
if( m_fCaptureTime <= gpGlobals->time )
{
// Team scores
if (FStrEq( m_szTeamName, DOM_TEAM1_NAME ))
s_iTeam1Score += ag_dom_controlpoints.value;
else if (FStrEq( m_szTeamName, DOM_TEAM2_NAME ))
s_iTeam2Score += ag_dom_controlpoints.value;
//Give the player the points
if (pCapturingPlayer && FStrEq(pCapturingPlayer->m_szTeamName,m_szTeamName) )
pCapturingPlayer->AddPoints(ag_dom_controlpoints.value, TRUE);
//pCapturingPlayer->AddPointsToTeam(ag_dom_controlpoints.value, TRUE); // is this required?
else if (pCapturingPlayer)
pCapturingPlayer = NULL;
// Increase score count
m_iConsecutiveScores++;
// reset score timer
m_fCaptureTime = gpGlobals->time + ag_dom_mincontroltime.value;
}
// Have we pased the max capture score limit, if so return control of this flag to
// a neutral state.
if ( m_iConsecutiveScores >= ag_dom_resetscorelimit.value ){
char szText[201];
sprintf(szText, "Neutral CP available at %s", m_szLocation);
AgConsole(szText);
UTIL_ClientPrintAll( HUD_PRINTCENTER, szText );
Reset();
}
// animate the control point
pev->frame += pev->framerate;
if (pev->frame < 0.0f || pev->frame >= 256.0f)
{
pev->frame -= (int)(pev->frame / 256.0f) * 256.0f;
}
pev->nextthink = gpGlobals->time + 0.1f;
}
void AgDOMControlPoint::Touch( CBaseEntity *pOther )
{
// prevent CP changing owner too quickly
if (m_fNextTouch > gpGlobals->time)
return;
else m_fNextTouch = gpGlobals->time + 0.5f;
// if it's not a player, ignore
if ( !pOther->IsPlayer() )
return;
if ( !pOther->IsAlive() )
return;
CBasePlayer *pPlayer = (CBasePlayer *)pOther;
if (FStrEq(pPlayer->m_szTeamName, DOM_TEAM1_NAME))
Capture( pPlayer, DOM_TEAM1_NAME );
else if (FStrEq(pPlayer->m_szTeamName, DOM_TEAM2_NAME))
Capture( pPlayer, DOM_TEAM2_NAME );
}
void AgDOMControlPoint::ClientDisconnected(CBasePlayer* pPlayer)
{
// Is there a better way to handle this? eg before adding on player score
// checking if player still exists?
ASSERT(NULL != pPlayer);
if (!pPlayer)
return;
ASSERT(NULL != pPlayer->pev);
if (!pPlayer->pev)
return;
if (pPlayer == pCapturingPlayer)
pCapturingPlayer = NULL;
}
#ifndef AG_NO_CLIENT_DLL
LINK_ENTITY_TO_CLASS( item_dom_controlpoint, AgDOMControlPoint );
#endif
///////////////////////////////////////////////////////////////////////////////////
//
// AG Item Cache - Allows maps that are none DOM specific to be used as DOM maps
//
///////////////////////////////////////////////////////////////////////////////////
#include "vector.h"
class AgDOMFileItemCache;
AgDOMFileItem::AgDOMFileItem()
{
m_vOrigin = Vector(0,0,0);
m_vAngles = Vector(0,0,0);
m_szName[0] = '\0';
m_szData1[0] = '\0';
}
AgDOMFileItem::~AgDOMFileItem()
{
}
void AgDOMFileItem::Show()
{
CLaserSpot* pSpot = CLaserSpot::CreateSpot();
UTIL_SetOrigin( pSpot->pev, m_vOrigin );
pSpot->LiveForTime(5.0);
}
AgDOMFileItemCache::AgDOMFileItemCache()
{
m_bInitDone = false;
Load();
}
AgDOMFileItemCache::~AgDOMFileItemCache()
{
//Delete all.
for (AgDOMFileItemList::iterator itrFileItems = m_lstFileItems.begin() ;itrFileItems != m_lstFileItems.end(); ++itrFileItems)
delete *itrFileItems;
m_lstFileItems.clear();
}
void AgDOMFileItemCache::Add(const AgString& sFileItem,CBasePlayer* pPlayer)
{
ASSERT(NULL != pPlayer);
if (!pPlayer)
return;
ASSERT(NULL != pPlayer->pev);
if (!pPlayer->pev)
return;
if (0 == sFileItem.size())
return;
AgDOMFileItem* pFileItem = new AgDOMFileItem;
strcpy(pFileItem->m_szName,sFileItem.c_str());
pFileItem->m_vOrigin = pPlayer->pev->origin;
pFileItem->m_vAngles = pPlayer->pev->angles;
m_lstFileItems.push_back(pFileItem);
pFileItem->Show();
Save(pPlayer);
AgConsole(UTIL_VarArgs("Added item %s.",(const char*)sFileItem.c_str()),pPlayer);
}
void AgDOMFileItemCache::Del(CBasePlayer* pPlayer)
{
ASSERT(NULL != pPlayer);
if (!pPlayer)
return;
ASSERT(NULL != pPlayer->pev);
if (!pPlayer->pev)
return;
if (0 == m_lstFileItems.size())
return;
AgDOMFileItem* pFileItem = m_lstFileItems.back();
AgConsole(UTIL_VarArgs("Deleted last item - %s.",pFileItem->m_szName,pPlayer));
m_lstFileItems.pop_back();
Save(pPlayer);
}
void AgDOMFileItemCache::List(CBasePlayer* pPlayer)
{
ASSERT(NULL != pPlayer);
if (!pPlayer)
return;
ASSERT(NULL != pPlayer->pev);
if (!pPlayer->pev)
return;
for (AgDOMFileItemList::iterator itrFileItems = m_lstFileItems.begin() ;itrFileItems != m_lstFileItems.end(); ++itrFileItems)
{
AgConsole(UTIL_VarArgs("%s",(const char*)(*itrFileItems)->m_szName),pPlayer);
(*itrFileItems)->Show();
}
}
void AgDOMFileItemCache::Load(CBasePlayer* pPlayer)
{
for (AgDOMFileItemList::iterator itrFileItems = m_lstFileItems.begin() ;itrFileItems != m_lstFileItems.end(); ++itrFileItems)
delete *itrFileItems;
m_lstFileItems.clear();
char szFile[MAX_PATH];
char szData[20000];
sprintf(szFile, "%s/dom/%s.dom", AgGetDirectory(),STRING(gpGlobals->mapname));
FILE* pFile = fopen(szFile,"r");
if (!pFile)
{
// file error
return;
}
int iRead = fread(szData,sizeof(char),sizeof(szData)-2,pFile);
fclose(pFile);
if (0 >= iRead)
return;
szData[iRead] = '\0';
char* pszCTFString = strtok( szData, "\n");
while (pszCTFString != NULL)
{
AgDOMFileItem* pFileItem = new AgDOMFileItem;
sscanf(pszCTFString,"%s %f %f %f %f %f %f %s\n",pFileItem->m_szName,&pFileItem->m_vOrigin.x,&pFileItem->m_vOrigin.y,&pFileItem->m_vOrigin.z,
&pFileItem->m_vAngles.x,&pFileItem->m_vAngles.y,&pFileItem->m_vAngles.z,
pFileItem->m_szData1 );
m_lstFileItems.push_back(pFileItem);
pszCTFString = strtok( NULL, "\n");
}
}
void AgDOMFileItemCache::Save(CBasePlayer* pPlayer)
{
if (0 == m_lstFileItems.size())
return;
char szFile[MAX_PATH];
sprintf(szFile, "%s/dom/%s.dom", AgGetDirectory(),STRING(gpGlobals->mapname));
FILE* pFile = fopen(szFile,"wb");
if (!pFile)
{
// file error
AgConsole(UTIL_VarArgs("Couldn't create/save FileItem file %s.",szFile),pPlayer);
return;
}
//Loop and write the file.
for (AgDOMFileItemList::iterator itrFileItems = m_lstFileItems.begin() ;itrFileItems != m_lstFileItems.end(); ++itrFileItems)
{
//Append.
AgDOMFileItem* pFileItem = *itrFileItems;
fprintf(pFile,"%s %f %f %f %f %f %f %s\n",pFileItem->m_szName, (double)pFileItem->m_vOrigin.x, (double)pFileItem->m_vOrigin.y, (double)pFileItem->m_vOrigin.z,
(double)pFileItem->m_vAngles.x, (double)pFileItem->m_vAngles.y, (double)pFileItem->m_vAngles.z,
pFileItem->m_szData1);
}
fflush(pFile);
fclose(pFile);
}
void AgDOMFileItemCache::Init()
{
if (m_bInitDone)
return;
m_bInitDone = true;
CBaseEntity *pEnt = NULL;
for (AgDOMFileItemList::iterator itrFileItems = m_lstFileItems.begin() ;itrFileItems != m_lstFileItems.end(); ++itrFileItems)
{
AgDOMFileItem* pFileItem = *itrFileItems;
if (g_pGameRules->IsAllowedToSpawn(pFileItem->m_szName))
pEnt = CBaseEntity::Create(pFileItem->m_szName, pFileItem->m_vOrigin, pFileItem->m_vAngles, INDEXENT(0));
// In addition to the generic entity creation params we wish to load a location param for ControlPoints,
// we parse this here. Unless you can tell me a better place or more generic method in which case I'll use it :)
// An alternative is to add info_dom_location items with a string name for the location however this would
// still involve parsing the datafile for strings so unless we change the save/load routine there is no point
// EG parse item name then deal with special cases or generic case - May change to this later :P
if (FStrEq( "item_dom_controlpoint", pFileItem->m_szName) && pEnt)
{
AgDOMControlPoint *pCP = (AgDOMControlPoint*)pEnt;
strncpy( pCP->m_szLocation, pFileItem->m_szData1, sizeof(pCP->m_szLocation) );
}
}
}