//=========== (C) Copyright 1999 Valve, L.L.C. All rights reserved. =========== // // The copyright to the contents herein is the property of Valve, L.L.C. // The contents may be used and/or copied only with the written permission of // Valve, L.L.C., or in accordance with the terms and conditions stipulated in // the agreement/contract under which the contents have been supplied. // // Purpose: Functionality for the observer chase camera // // $Workfile: $ // $Date: $ // //----------------------------------------------------------------------------- // $Log: $ // // $NoKeywords: $ //============================================================================= #include "extdll.h" #include "util.h" #include "cbase.h" #include "player.h" #include "weapons.h" #include "pm_shared.h" extern int gmsgCurWeapon; extern int gmsgSetFOV; extern int gmsgTeamInfo; extern int g_teamplay; // Find the next client in the game for this player to spectate void CBasePlayer::Observer_FindNextPlayer( bool bReverse ) { // MOD AUTHORS: Modify the logic of this function if you want to restrict the observer to watching // only a subset of the players. e.g. Make it check the target's team. int iStart; if( m_hObserverTarget ) iStart = ENTINDEX( m_hObserverTarget->edict() ); else iStart = ENTINDEX( edict() ); int iCurrent = iStart; m_hObserverTarget = 0; int iDir = bReverse ? -1 : 1; do { iCurrent += iDir; // Loop through the clients if( iCurrent > gpGlobals->maxClients ) iCurrent = 1; if( iCurrent < 1 ) iCurrent = gpGlobals->maxClients; CBaseEntity *pEnt = UTIL_PlayerByIndex( iCurrent ); if( !pEnt ) continue; if( pEnt == this ) continue; // Don't spec observers or players who haven't picked a class yet if( ( (CBasePlayer*)pEnt )->IsObserver() || ( pEnt->pev->effects & EF_NODRAW ) ) continue; // MOD AUTHORS: Add checks on target here. m_hObserverTarget = pEnt; break; }while( iCurrent != iStart ); // Did we find a target? if( m_hObserverTarget ) { // Move to the target UTIL_SetOrigin( pev, m_hObserverTarget->pev->origin ); // ALERT( at_console, "Now Tracking %s\n", STRING( m_hObserverTarget->pev->netname ) ); // Store the target in pev so the physics DLL can get to it if( pev->iuser1 != OBS_ROAMING ) pev->iuser2 = ENTINDEX( m_hObserverTarget->edict() ); } } // Handle buttons in observer mode void CBasePlayer::Observer_HandleButtons() { // Slow down mouse clicks if( m_flNextObserverInput > gpGlobals->time ) return; // Jump changes from modes: Chase to Roaming if( m_afButtonPressed & IN_JUMP ) { if( pev->iuser1 == OBS_CHASE_LOCKED ) Observer_SetMode( OBS_CHASE_FREE ); else if( pev->iuser1 == OBS_CHASE_FREE ) Observer_SetMode( OBS_IN_EYE ); else if( pev->iuser1 == OBS_IN_EYE ) Observer_SetMode( OBS_ROAMING ); else if( pev->iuser1 == OBS_ROAMING ) Observer_SetMode( OBS_MAP_FREE ); else if( pev->iuser1 == OBS_MAP_FREE ) Observer_SetMode( OBS_MAP_CHASE ); else Observer_SetMode( OBS_CHASE_FREE ); // don't use OBS_CHASE_LOCKED anymore m_flNextObserverInput = gpGlobals->time + 0.2f; } // Attack moves to the next player if ( m_afButtonPressed & IN_ATTACK )//&& pev->iuser1 != OBS_ROAMING ) { Observer_FindNextPlayer( false ); m_flNextObserverInput = gpGlobals->time + 0.2f; } // Attack2 moves to the prev player if ( m_afButtonPressed & IN_ATTACK2)// && pev->iuser1 != OBS_ROAMING ) { Observer_FindNextPlayer( true ); m_flNextObserverInput = gpGlobals->time + 0.2f; } } void CBasePlayer::Observer_CheckTarget() { if( pev->iuser1 == OBS_ROAMING ) return; // try to find a traget if we have no current one if( m_hObserverTarget == 0 ) { Observer_FindNextPlayer( false ); if( m_hObserverTarget == 0 ) { // no target found at all int lastMode = pev->iuser1; Observer_SetMode( OBS_ROAMING ); m_iObserverLastMode = lastMode; // don't overwrite users lastmode return; // we still have np target return } } CBasePlayer* target = (CBasePlayer*)( UTIL_PlayerByIndex( ENTINDEX( m_hObserverTarget->edict() ) ) ); if( !target ) { Observer_FindNextPlayer( false ); return; } // check taget if( target->pev->deadflag == DEAD_DEAD ) { if( ( target->m_fDeadTime + 2.0f ) < gpGlobals->time ) { // 3 secs after death change target Observer_FindNextPlayer( false ); return; } } } void CBasePlayer::Observer_CheckProperties() { // try to find a traget if we have no current one if( pev->iuser1 == OBS_IN_EYE && m_hObserverTarget != 0 ) { CBasePlayer* target = (CBasePlayer*)( UTIL_PlayerByIndex( ENTINDEX( m_hObserverTarget->edict() ) ) ); if( !target ) return; int weapon = ( target->m_pActiveItem != NULL ) ? target->m_pActiveItem->m_iId : 0; // use fov of tracked client if( m_iFOV != target->m_iFOV || m_iObserverWeapon != weapon ) { m_iFOV = target->m_iFOV; m_iClientFOV = m_iFOV; // write fov before wepon data, so zoomed crosshair is set correctly MESSAGE_BEGIN( MSG_ONE, gmsgSetFOV, NULL, pev ); WRITE_BYTE( m_iFOV ); MESSAGE_END(); m_iObserverWeapon = weapon; //send weapon update MESSAGE_BEGIN( MSG_ONE, gmsgCurWeapon, NULL, pev ); WRITE_BYTE( 1 ); // 1 = current weapon, not on target WRITE_BYTE( m_iObserverWeapon ); WRITE_BYTE( 0 ); // clip MESSAGE_END(); } } else { m_iFOV = 90; if( m_iObserverWeapon != 0 ) { m_iObserverWeapon = 0; MESSAGE_BEGIN( MSG_ONE, gmsgCurWeapon, NULL, pev ); WRITE_BYTE( 1 ); // 1 = current weapon WRITE_BYTE( m_iObserverWeapon ); WRITE_BYTE( 0 ); // clip MESSAGE_END(); } } } // Attempt to change the observer mode void CBasePlayer::Observer_SetMode( int iMode ) { // Just abort if we're changing to the mode we're already in if( iMode == pev->iuser1 ) return; // is valid mode ? if( iMode < OBS_CHASE_LOCKED || iMode > OBS_MAP_CHASE ) iMode = OBS_IN_EYE; // now it is // verify observer target again if( m_hObserverTarget != 0 ) { CBaseEntity *pEnt = m_hObserverTarget; if( ( pEnt == this ) || ( pEnt == NULL ) ) m_hObserverTarget = 0; else if( ( (CBasePlayer*)pEnt )->IsObserver() || ( pEnt->pev->effects & EF_NODRAW ) ) m_hObserverTarget = 0; } // set spectator mode pev->iuser1 = iMode; // if we are not roaming, we need a valid target to track if( ( iMode != OBS_ROAMING ) && ( m_hObserverTarget == 0 ) ) { Observer_FindNextPlayer( false ); // if we didn't find a valid target switch to roaming if( m_hObserverTarget == 0 ) { ClientPrint( pev, HUD_PRINTCENTER, "#Spec_NoTarget" ); pev->iuser1 = OBS_ROAMING; } } // set target if not roaming if( pev->iuser1 == OBS_ROAMING ) { pev->iuser2 = 0; } else pev->iuser2 = ENTINDEX( m_hObserverTarget->edict() ); pev->iuser3 = 0; // clear second target from death cam // print spepctaor mode on client screen char modemsg[16]; sprintf( modemsg,"#Spec_Mode%i", pev->iuser1 ); ClientPrint( pev, HUD_PRINTCENTER, modemsg ); m_iObserverLastMode = iMode; } void CBasePlayer::StopObserver() { // Turn off spectator pev->iuser1 = pev->iuser2 = 0; m_iHideHUD = 0; GetClassPtr( (CBasePlayer *)pev )->Spawn(); pev->nextthink = -1; // Update Team Status MESSAGE_BEGIN( MSG_ALL, gmsgTeamInfo ); WRITE_BYTE( ENTINDEX( edict() ) ); // index number of primary entity if( g_teamplay ) WRITE_STRING( TeamID() ); else WRITE_STRING( "Players" ); MESSAGE_END(); }