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.

292 lines
7.3 KiB

//=========== (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( this, 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.2;
}
// Attack moves to the next player
if ( m_afButtonPressed & IN_ATTACK )//&& pev->iuser1 != OBS_ROAMING )
{
Observer_FindNextPlayer( false );
m_flNextObserverInput = gpGlobals->time + 0.2;
}
// Attack2 moves to the prev player
if ( m_afButtonPressed & IN_ATTACK2)// && pev->iuser1 != OBS_ROAMING )
{
Observer_FindNextPlayer( true );
m_flNextObserverInput = gpGlobals->time + 0.2;
}
}
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();
}