L4D2 EMS/GnomeHunter tutorial 6

From Valve Developer Community
Jump to: navigation, search

Detecting the Gnome's Proximity to the Locker

When the player carries the gnome we want the following things to happen:

  • If the player carries the gnome near a locker we want the locker to open
  • If the player carries the gnome away from a locker we want the locker to close
  • If the player drops the gnome in the locker we want the locker to close and the gnome to vanish, and set some state about the gnome being stored in the locker

First, lets add a function that will detect whether the gnome is near the locker or not.

We're going to use the VScript entity function Entities.FindByNameWithin() to determine if our gnome is near the locker:

NOTE: You can learn more about search functions by reading through the script_help documentation. While in L4D2 and in a level type 'script_help' and it will dump all of the VScript function descriptions to the console. Here's what the 'script_help' has to say about Entities.FindByNameWithin():

Fine by name within.jpg

NOTE: There is also a VScript function reference in the Appendix of this wiki. Look for it on the sidebar.


Since we want to frequently check to see if the gnome is near a locker we'll install a Mutation helper function called a slow poll. The slow poll is a Mutation System feature that will call any function you designate on a regular timer. For performance reasons, slow poll functions are called once every 2.5 seconds (hence the name slow poll.)

Add the function call to your OnGameplayStart() function and pass in the name of the function you want to call:

// Set up our slow poll function to check the locker periodically
ScriptedMode_AddSlowPoll( LockerGnomeSlowPoll )

And then add the actual LockerGnomeSlowPoll() function. You can throw in a printl statement to verify that your poll is being called.

function LockerGnomeSlowPoll()
{
	printl(" ** locker gnome slow poll:" + Time() )
}

We're almost ready to search for that gnome! We have a handle to the locker entity in our MutationState table but we don't have a handle to the gnome. Add a variable for the gnome in your MutationState table:

	StartActive = true
	CarryingGnome = false
	LockerEntity = null
   ---->GnomeEntity = null

and then revisit your GnomeSpawnCB() callback function and set the GnomeEntity variable at the top of the function:

// store a handle to the gnome entity
SessionState.GnomeEntity = entity

Performing the Search

Inside the LockerGnomeSlowPoll() function, you'll call Entities.FindByNameWithin() to check around your locker position to see if the gnome is nearby:

// search 256 units around the locker origin for our gnome targetname 
local ent = Entities.FindByNameWithin( null, SessionState.GnomeEntity.GetName(), SessionState.LockerEntity.GetOrigin(), 256 )

If ent is not null that means we found our gnome! Lets fire some entity IO to open and close the locker based on whether the gnome is nearby:

if( ent )
{
	printl(" ** Found the gnome!")
	EntFire( SessionState.LockerEntity.GetName(), "setanimation", "open" )
}
else
{
	printl(" ** No gnome near the locker...")
	EntFire( SessionState.LockerEntity.GetName(), "setanimation", "closed" )
}

If you're feeling fancy go ahead and add some open and close sounds, too.

You can play your Mutation now and see that the locker opens when the gnome gets close, and closes if you carry the gnome away from the locker.

Is the Gnome in the Locker?

We know the gnome is close to the locker. Lets figure out if it is actually in the locker, and do something about it:

  • Instead of doing the FindByNameWithin() function call, get fancy by computing the distance between the gnome's origin and the locker's origin.
    • Hint: there is a .Length() method for Vectors.
  • If the gnome is actually inside the locker (the gnome's origin is 21 units or less from the locker's origin) then do the following:
    • Kill the gnome
    • Close the locker
    • Play the sound "Gallery.GnomeFTW" (Don't forget to precache!)


Spoiler alert!

function LockerGnomeSlowPoll()
{
	printl(" ** locker gnome slow poll:" + Time() )

	// if the gnome is already in the locker return
	if( SessionState.GnomeInLocker )
	{
		return
	}

	local INSIDE_LOCKER_DIST = 21
	local NEAR_LOCKER_DIST = 256

	// Get the actual distance between the Gnome and the Locker
	local dist = ( SessionState.LockerEntity.GetOrigin() - SessionState.GnomeEntity.GetOrigin() ).Length()

	// is the gnome close to the locker?
	if( dist <= NEAR_LOCKER_DIST )
	{
		// is the gnome inside the locker?
		if( dist <= INSIDE_LOCKER_DIST )
		{
			// We're in the locker!  Close it and kill() the gnome
			SessionState.GnomeInLocker = true
			SessionState.GnomeEntity.Kill()

			// play the gnome stored sound
			EmitSoundOn( "Gallery.GnomeFTW", SessionState.LockerEntity )

			EntFire( SessionState.LockerEntity.GetName(), "setanimation", "closed" )
		}
		else
		{
			// No, gnome is outside of locker.
			EntFire( SessionState.LockerEntity.GetName(), "setanimation", "open" )
		}
	}
	else
	{
		// Gnome isn't even near the locker!
		EntFire( SessionState.LockerEntity.GetName(), "setanimation", "closed" )
	}
}

extra credit: Instead of using .Length(), speed up your search by writing your own distance function (or use a vector method) that doesn't use a square root.


Next: Spicing things up with waves of infected.

NEXT -->