Observer Tutorial
So, you want to customise half-life to suit your own needs? Well, you can start by downloading the source code. Go to Wavelength and get it at once!


NOTICE:All the tutorials found on this page are designed to be written and compiled using MS Visual C++ 5 or greater. The learning edition is available at around £40 for students and £70 otherwise. I would recommend you get hold of a copy if you want to edit half-life since Valve's project files were done using Visual C++.

Observer Mode tutorial by Aybara
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 finally 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;
}
First, 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 on Wavelength) 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 :)

Aybara


COPYRIGHT NOTICE: Please do not just 'copy' information from these pages, I would prefer it if you just linked to this page rather than stealing others hard work and effort.