//++ 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 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) ); } } }