Fixing AI in multiplayer

This article describes the changes that need to be made to get NPCs to work in the multiplayer SDK, as there are several key features that need to be re-implemented in a multiplayer context. While it covers the use of the SDK, it should be readily convertible to work with any SDK, by referring to the correct GameRules.
Note that a patch is available for the Orange Box HL2DM source that implements all changes described here, it is listed near the end of the article.
Note that a Fork of the codeline on GitHub exists in which these changes have been applied. Cloning this fork might be a good way to get these changes into your project.
== Relationships ==
There is no relationship table set in . Without this table, NPCs won't know which entities to hate/like, and if Combine Soldiers like players, they won't attack them.
This is simple to fix. First go to , under void RestartGame(); add this:
 #ifndef CLIENT_DLL 
 void InitDefaultAIRelationships( void );
 #endif
Next go to and copy the entire InitDefaultAIRelationships() function. Paste it in . 
Don't forget to put the copied functions from hl2_gamerules.cpp between #ifndef CLIENT_DLL and #endif.
And change to 
in hl2mp_gamerules.cpp .
And Voula now you should have working relationships with npcs
== Weapons ==
=== Activities & animation events ===
The weapons have custom activities and animation events used for the AI in HL2. In the HL2DM-versions of the weapons this is removed.
Open the file and add the following code after the includes:
#ifndef CLIENT_DLL
	#include "AI_BaseNPC.h"
#endif
Now open the HL2SP files of the BaseHLBludgeonWeapon, AR2, shotgun, SMG1, crowbar, pistol, stunstick and frag grenade (found in dlls/hl2_dll/). The NPCs in HL2 don't use any other weapons. Each weapon has a Operator_HandleAnimEvent and/or CapabilitiesGet function, which the NPCs use to fire their weapons. Copy these functions and any other functions that are called in Operator_HandleAnimEvent to the HL2DM weaponfiles (found in game_shared/hl2mp/).
Don't forget the headers and put all copied functions between #ifndef CLIENT_DLL and #endif.
Also, you need to copy the activities. Look for m_acttable[] in the HL2SP-variants and copy them to the HL2DM-variants.
=== SetActivity() ===
Open and look for the function SetActivity.
Look for the following code:
//Adrian: Oh man...
#if !defined( CLIENT_DLL ) && defined( HL2MP )
	SetModel( GetWorldModel() );
#endif
int sequence = SelectWeightedSequence( act ); 
	
	// FORCE IDLE on sequences we don't have (which should be many)
	if ( sequence == ACTIVITY_NOT_AVAILABLE )
		sequence = SelectWeightedSequence( ACT_VM_IDLE );
//Adrian: Oh man again...
#if !defined( CLIENT_DLL ) && defined( HL2MP )
	SetModel( GetViewModel() );
#endif
The activities can only be retrieved from weapon world models. However, the weapons of players are viewmodels. So with this little hack, the models are changed to worldmodel, and activities are retrieved and changed back to viewmodel. However considering NPCs don't have to see or work with their viewmodels, this is going to be changed:
	//Adrian: Oh man...
	if ( GetOwner()->IsPlayer() )
		SetModel( GetWorldModel() );
	
	int sequence = SelectWeightedSequence( act ); 
	
	// FORCE IDLE on sequences we don't have (which should be many)
	if ( sequence == ACTIVITY_NOT_AVAILABLE )
		sequence = SelectWeightedSequence( ACT_VM_IDLE );
	//Adrian: Oh man again...
	if ( GetOwner()->IsPlayer() )
		SetModel( GetViewModel() );
== Ammotypes ==
The damage of the weapons need to use the data from . Don't forget to copy 's </code> to your mod's cfg directory.
Also, the code needs to be told to use these values. Open again and copy the entire GetAmmoDef function. Replace the same function in hl2mp_gamerules.cpp with the copied code but add the following line:
def.AddAmmoType("slam", DMG_BURN, TRACER_NONE, 0, 0, 5, 0, 0);
You need to include in too.
Otherwise, you will get two compiler errors C2065: 'DMG_SNIPER': undeclared identifier.
== Model animations ==
Source's humanoid NPC models and deathmatch player models were not designed to be compatible with each other. The player and NPC versions both require different animations, and so depending on the order you mount your game content in (HL2DM before HL2, or vice versa) either humanoid NPCs or player animations will be completely missing, unless you use custom models.
Assuming you don't have new models, then two approaches are open to you.
# The first involves duplicating either the NPC or player models, and using a hex editor to direct the model to use a different animation file (eg create a renamed copy of the npc animations, and change NPC models to use that instead)
# Alternatively, you can try to "remap" the NPC animation names into player names in the code. A brief discussion of this method can be seen here: http://forums.steampowered.com/forums/showthread.php?t=752399
== Function calls ==
Still, much of the AI code is still not designed to be used in multiplayer. All calls to the functions AI_GetSinglePlayer, AI_IsSingleplayer and UTIL_GetLocalPlayer would need fixing. Also pieces of code like if ( gpGlobals->maxClients == 1) and UTIL_PlayerByIndex( 1 ). There are hundreds of these calls, and it's lots of work to fix them all.
== Lag compensation ==
Without lag compensation, when you shoot at a target, you have to take your ping time into account, and aim ahead of it accordingly.
So if your ping is 100ms, you have to aim for where the target's head will be in 100ms, rather than where you see it right now.
Clearly, this is less than ideal, and so HL2DM incorporates lag compensation, which when calculating whether a bullet hits or not, briefly adjusts the position of all players back by the shooter's ping, so that it calculates the bullet collision based upon exactly what the shooter saw when they fired. This effect is missing for NPCs but can be duplicated from the player lag compensation code without too much trouble.
The NPC lag compensation article proved too long to include in this tutorial, it is instead located here.
== Jerky animations ==
When ping times are large enough, NPC animation becomes extremely slow and jerky. There's a built-in mechanism to correct for this, interpolation. Essentially, it shows all NPCs etc slightly "back in time" by a few hundred milliseconds, essentially giving more buffer time for clients. It's controlled by a ConVar, and it would be best to have a slightly higher value by default, so open c_baseentity.cpp, and find
static ConVar cl_interp_npcs( "cl_interp_npcs", "0.0", FCVAR_USERINFO, "Interpolate NPC positions starting this many seconds in past (or cl_interp, if greater)" );
Change the second parameter (default value) to 0.25, and now an unusually high ping will be required to distort animations.
static ConVar cl_interp_npcs( "cl_interp_npcs", "0.25", FCVAR_USERINFO, "Interpolate NPC positions starting this many seconds in past (or cl_interp, if greater)" ); 
That's 250 milliseconds. Experiment with the console command to determine an optimal value, the higher value you provide, the higher latency users can have and still observe smooth animation. For 250 ms cl_interp_npcs, a net_fakelag of 150 ms still results in smooth animation. The higher the value you provide, however, the further NPCs will operate "in the past" - this only really presents a problem with fast zombies and headcrabs, as the user will appear to take damage while the NPC is still jumping towards them. If you are making a multiplayer zombie mod, it is recommended that you only set the ConVar to around 150, as it would decrease the effect of taking damage before being hit.
Instead of relying on interpolation, UseClientSideAnimation(); could be added to the CAI_BaseNPC constructor (in ai_basenpc.cpp) - however, this significantly dampens NPCs animations, and so is not recommended.
== Blood ==
Thanks to the prediction, the blood of NPCs is suppressed. This is because in HL2DM the blood of players is done client-side. This is not the case with NPCs and the prediction needs to "break":
Open and look for the function UTIL_BloodDrips. Add to the top of the function: IPredictionSystem::SuppressHostEvents( NULL );
== Zombies ==
In (Or ai_basenpc.cpp?) find CAI_BaseNPC::Ignite and remove/comment out the #ifdef HL2_EPISODIC section. In npc_BaseZombie.cpp find CNPC_BaseZombie::MakeAISpookySound and remove/comment out its contents.
== Patch ==
Including the 'function calls,' there are an awful lot of changes required to implement AI in a multiplayer mod. This patch file implements all the changes described here, (crucially) including correcting all the function calls and lag compensation code - so it should save you a good deal of dull coding work! See the readme for details of how it replaces calls to functions such as UTIL_GetLocalPlayer() & AI_GetSingleplayer(). Users requiring assistance in applying a patch file are directed here.
Due to Megaupload being closed,
it has been uploaded using mirrorcreator.com which uploads to 8 various file hosting websites
Download Here
Another direct link is Here
=== 15th June 2009 HL2MP Patch ===
The patch (which was made using the original Winston patch via copy & paste) can be downloaded from MegaUpload. Please note that this is the basic AI patch added to the new SDK, it behaves like the old patch.
There is a small bug that crept in on each weapon_*.cpp file. That is the ACT_ items are in #ifndef client_dll sections. This is incorrect, they (and the IMPLEMENT_ACTTABLE) should be outside these. If you don't fix these, then even if you get custom player animations to work, their activities won't be called so they'll continue to be t-posed with no animations.
For example in the weapon_ar2:
ifndef CLIENT_DLL
acttable_t	CWeaponAR2::m_acttable[] = 
{
	{ ACT_MP_STAND_IDLE,				ACT_HL2MP_IDLE_AR2,					false },
Should be:
acttable_t	CWeaponAR2::m_acttable[] = 
{
	{ ACT_MP_STAND_IDLE,				ACT_HL2MP_IDLE_AR2,					false },
== Conclusion ==
While a lot of work is involved, particularly if the patch file is not used, the changes described here should fix most of the AI to a reasonable standard for use in multiplayer. Further changes that will be required, and there is probably room for optimisation in the "function call" fixes, but most NPCs should work well.
Category:Networking
Category:AI Programming
Category:Multiplayer