Wavelength Logo
tl.jpg (2351 bytes) blank.gif (837 bytes) tr.jpg (2446 bytes)
blank.gif (837 bytes)
Half-Life Observer Mode Tutorial by Blake Grant
blank.gif (837 bytes)
Well, I have answered this question a few times, so I figured I may as well formalize it and make a real tutorial. The problem is: How do you do a Team Fortress style spawning where the users are stuck looking at the map from an info_intermission until they pick a class, then spawn afterwards? Took me a while to get this to work properly, and you may need to modify it to fit your needs, but here's what I did...

First, I wrote these two functions for CBasePlayer:

void CBasePlayer:: StartObserving( void ) {

if (observerflag)
return;

EnableControl(FALSE);

observerflag = TRUE;
RemoveAllItems( TRUE );
pev->deadflag = DEAD_DEAD;

edict_t *pSpot, *pNewSpot;
int iRand;

if ( pev->view_ofs == g_vecZero )
{
// don't accept subsequent attempts to StartObserving()
return;
}
pSpot = FIND_ENTITY_BY_CLASSNAME( NULL, "info_intermission");
if ( !FNullEnt( pSpot ) )
{
// at least one intermission spot in the world.
iRand = RANDOM_LONG( 0, 3 );

while ( iRand > 0 )
{
pNewSpot = FIND_ENTITY_BY_CLASSNAME( pSpot, "info_intermission");

if ( pNewSpot )
{
pSpot = pNewSpot;
}

iRand--;
}

StartObserver( pSpot->v.origin, pSpot->v.v_angle );
}
else
{
// no intermission spot. Push them up in the air, looking down at their corpse
TraceResult tr;
UTIL_TraceLine( pev->origin, pev->origin + Vector( 0, 0, 128 ), ignore_monsters, edict(), &tr );
StartObserver( tr.vecEndPos, UTIL_VecToAngles( tr.vecEndPos - pev->origin ) );
return;
}
}

void CBasePlayer::StopObserving( void ) {

if (!observerflag)
return;

EnableControl(TRUE);

pev->deadflag = DEAD_NO;
observerflag = FALSE;

pev->button = 0;
m_iRespawnFrames = 0;

respawn(pev, !(m_afPhysicsFlags & PFLAG_OBSERVER) );
pev->nextthink = -1;
}

StartObserving is basically just a copy of StartDeathCam with a couple exceptions. First, I set the player's deadflag to DEAD_DEAD since in StartDeathCam, this has already happened. I'm not sure if this is necessary, but its best to be safe. I think it might make you invisible (best not to have people joining just floating in the world!). Second, I called EnableControl(FALSE). This one took me a long time to figure out. Once you do this, your player will be freezed in his current position, only able to look around, but not to move. The rest of the code is pretty much the same as   StartDeathCam, except I removed the CopyBodyToQue commands (this drops a copy of the player's model into the world).

The next function, StopObserving, sets the player back up to allow him control, visibility, etc.

The observerflag variable both allows me to kick out of Start/StopObserving when it is already in action, and allows me to bypass PlayerDeathThink when in observer mode. I put the following  line right at the beginning of PlayerDeathThink:

if (observerflag)
return;

And don't forget to declare observerflag and initialize it to FALSE as well!

Now, that should be all you need to do to CBasePlayer. Depending on why you're doing this, the rest may vary, but if you're just forcing players to choose a class before being able to run around, call StartObserving from the end of InitHUD with the line:

pPlayer->StartObserving();

Hopefully you've been thinking ahead, and have already created your menu (see the menu tutorial) to allow the class selection, so at the end of your class selection code (in ClientCommand of course), call StopObserving.

Well, that should hopefully do it. Please let me know if there's anything wrong in this tutorial. I wrote it from my code, not the other way around, so I may have missed something :)

- Blake Grant

 

blank.gif (837 bytes)
bl.jpg (2471 bytes) blank.gif (837 bytes) br.jpg (2132 bytes)