276 lines
6.7 KiB
C++
276 lines
6.7 KiB
C++
//=========== (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"
|
|
#include "UserMessages.h"
|
|
|
|
// 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 = NULL;
|
|
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) != 0)
|
|
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) != 0)
|
|
{
|
|
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) != 0) //&& pev->iuser1 != OBS_ROAMING )
|
|
{
|
|
Observer_FindNextPlayer(false);
|
|
|
|
m_flNextObserverInput = gpGlobals->time + 0.2;
|
|
}
|
|
|
|
// Attack2 moves to the prev player
|
|
if ((m_afButtonPressed & IN_ATTACK2) != 0) // && 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 == NULL)
|
|
{
|
|
Observer_FindNextPlayer(false);
|
|
|
|
if (m_hObserverTarget == NULL)
|
|
{
|
|
// 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 != NULL)
|
|
{
|
|
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 != NULL)
|
|
{
|
|
CBaseEntity* pEnt = m_hObserverTarget;
|
|
|
|
if ((pEnt == this) || (pEnt == NULL))
|
|
m_hObserverTarget = NULL;
|
|
else if (((CBasePlayer*)pEnt)->IsObserver() || (pEnt->pev->effects & EF_NODRAW) != 0)
|
|
m_hObserverTarget = NULL;
|
|
}
|
|
|
|
// set spectator mode
|
|
pev->iuser1 = iMode;
|
|
|
|
// if we are not roaming, we need a valid target to track
|
|
if ((iMode != OBS_ROAMING) && (m_hObserverTarget == NULL))
|
|
{
|
|
Observer_FindNextPlayer(false);
|
|
|
|
// if we didn't find a valid target switch to roaming
|
|
if (m_hObserverTarget == NULL)
|
|
{
|
|
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;
|
|
}
|