Fr/Fixing AI in multiplayer

=Introduction=
Cet article est une mise-à-jour de l'article Activer IA en Coop.
Bien que l'ancienne version expiquait comment activer l'IA dans une partie multijoueurs, il s'avérait que les NPCs de type humanoïdes tels que les Combine Soldiers ne pouvaient pas se déplacer, ni tirer sur le joueur, et c'est frustrant!
Eh bien voilà, c'est une histoire an cienne à présent, je vous propose que l'on étudie tout celà ensembe...
Avant que l'on aille plus loin, j'aimerais juste remercier au passage Garry Newman BunkMug, FoxFire, pour leurs tuyaux ainsi que d'autres personnes sur divers forums.
=Tabes des Relations=
==Création de la Table==
D'abord, on doit définir cette table en définissant les différentes classes des personnages présents dans le jeu.
Ouvrez le fichier BaseEntity.h, puis dans la définition de enum Class_T, après le bloc #elif defined( CSTRIKE_DLL ) ajoutez ceci:
//***THOMAS*** INITIALISE LA TABLE DES RELATIONS DES NPC ( =PNJ ) POUR HL2DM
#elif defined ( HL2MP_DLL || HL2MP )
// For CLASSIFY
enum Class_T
{
	CLASS_NONE=0,				
	CLASS_PLAYER,			
	CLASS_PLAYER_ALLY,
	CLASS_PLAYER_ALLY_VITAL,
	CLASS_ANTLION,
	CLASS_BARNACLE,
	CLASS_BULLSEYE,
	CLASS_CITIZEN_PASSIVE,	
	CLASS_CITIZEN_REBEL,
	CLASS_COMBINE,
	CLASS_COMBINE_GUNSHIP,
	CLASS_CONSCRIPT,
	CLASS_HEADCRAB,
	CLASS_MANHACK,
	CLASS_METROPOLICE,		
	CLASS_MILITARY,		
	CLASS_SCANNER,		
	CLASS_STALKER,		
	CLASS_VORTIGAUNT,
	CLASS_ZOMBIE,
	CLASS_PROTOSNIPER,
	CLASS_MISSILE,
	CLASS_FLARE,
	CLASS_EARTH_FAUNA,
	NUM_AI_CLASSES
};
//***
==Utilisation de la Table==
Super! Maintenant il nous faut dire aux Game Rules d'utiliser cette Table de Relations dans le jeu lorque l'on commence une partie de HL2DM.
Ouvrez le fichier h2mp_gamerules.h. En dessous de l'onglet public:, ajoutez ceci:
//***THOMAS*** INITIALISE LA TABLE DES RELATIONS DES NPC ( =PNJ ) POUR HL2DM
#ifndef CLIENT_DLL 
void InitDefaultAIRelationships( void );
#endif
//***
A présent, ouvrez le fichier h2mp_gamerules.cpp. Ensuite tout en bas du fichier .cpp ajoutez tout ce bout de code:
//------------------------------------------------------------------------------
// Purpose : Initialize all default class relationships
// Input :
// Output :
//------------------------------------------------------------------------------
//***THOMAS*** INITIALISE LA TABLE DES RELATIONS DES NPC ( =PNJ ) POUR HL2DM
#ifndef CLIENT_DLL 
void CHL2MPRules::InitDefaultAIRelationships( void )
{
	//Copiez le contenu of de hl2_gamerules InitDefaultAIRelationships ici
}
#endif
//***
Terminons en ajoutant cette petite instruction dans la fonction CHL2MPRules::CHL2MPRules():
//***THOMAS*** INITIALISE LA TABLE DES RELATIONS DES NPC ( =PNJ ) POUR HL2DM
InitDefaultAIRelationships();
//***
=Faire fonctionner les NPCs humanoides=
==Animations des Armes==
Pour chaque arme, prendre le code SP de l'arme, et copier la liste des animations contenues dans la structure acttable_t.
Procédez comme ci-dessous:
acttable_t	CWeaponSMG1::m_acttable[] = 
		{
			//DEPART ANIMATIONS HL2DM
			{ ACT_HL2MP_IDLE,		ACT_HL2MP_IDLE_SMG1,		false },
			( ... )
			{ ACT_RANGE_ATTACK1,		ACT_RANGE_ATTACK_SMG1,		false },
			//FIN ANIMATIONS HL2DM
			//DEPART ANIMATIONS HL2
			{ ACT_RANGE_ATTACK1,		ACT_RANGE_ATTACK_SMG1,		true },
			{ ACT_RELOAD,			ACT_RELOAD_SMG1,		true },
			{ ACT_IDLE,			ACT_IDLE_SMG1,			true },
			(...)
			{ ACT_GESTURE_RELOAD,		ACT_GESTURE_RELOAD_SMG1,	true },
			//FIN ANIMATIONS HL2
		}
==Autres modifications sur les armes==
Il faut à présent insérer le code qui permet à l'IA de controller son arme, en l'occurrence de tirer avec, de la recharger etc. Pour ce faire, il faut déclarer ces deux fonctions, côté serveur seulement:
#ifndef CLIENT_DLL
		//PERMET AUX NPC D'UTILISER CETTE ARME
		int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; }
		//GESTION DES ACTIONS DE L'IA
		void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator );
#endif
==Suggestions de Garry Newman==
Garry Newman suggérait sur des forums de modifier la fonction SetActivity().
Ainsi, dans le fichier basecombatweapon_shared.cpp, recherchez la fonction SetActivity(), et juste après e commentaire d'Adrien:
//Adrian: Oh man...
remplacez
#if !defined( CLIENT_DLL ) && defined( HL2MP )
	SetModel( GetWorldModel() );
#endif
par
	if ( GetOwner()->IsPlayer() )
		SetModel( GetWorldModel() );
Procédez de même après l'autre commentaire, vous remplacerez donc:
	//Adrian: Oh man again...
#if !defined( CLIENT_DLL ) && defined( HL2MP )
	SetModel( GetViewModel() );
#endif
par
	//Adrian: Oh man again...
	if ( GetOwner()->IsPlayer() )
		SetModel( GetViewModel() );
Bon, je ne sais pas si ça change queque chose ou pas, mais Garry insistait sur ce code, alors je suggère qu'en en tienne compte. Si quequ'un à le courage de tester pour voir s'il y a une différence ou pas avec ce code, qu'il m'en fasse part, merci.
==Ammotypes==
Après avoir activé mes NPCs humanoides, ces derniers me tuaient en une seule balle. Si vous avec le même problème, je vous suggère de trouver la fonction CAmmoDef *GetAmmoDef() dans le fichier hl2mp_gamerules.cpp, et de la remplacer entièrement par ce qui suit.
CAmmoDef *GetAmmoDef()
	{
		static CAmmoDef def;
		static bool bInitted = false;
	
		if ( !bInitted )
		{
			bInitted = true;
			//THOMAS: HL2SP DEFINITIONS
			def.AddAmmoType("AR2",				DMG_BULLET,					TRACER_LINE_AND_WHIZ,		"sk_plr_dmg_ar2",		"sk_npc_dmg_ar2",			"sk_max_ar2",			BULLET_IMPULSE(200, 1225), 0 );
			def.AddAmmoType("AlyxGun",			DMG_BULLET,					TRACER_LINE,			"sk_plr_dmg_alyxgun",		"sk_npc_dmg_alyxgun",		"sk_max_alyxgun",			BULLET_IMPULSE(200, 1225), 0 );
			def.AddAmmoType("Pistol",			DMG_BULLET,					TRACER_LINE_AND_WHIZ,		"sk_plr_dmg_pistol",		"sk_npc_dmg_pistol",		"sk_max_pistol",			BULLET_IMPULSE(200, 1225), 0 );
			def.AddAmmoType("SMG1",				DMG_BULLET,					TRACER_LINE_AND_WHIZ,		"sk_plr_dmg_smg1",		"sk_npc_dmg_smg1",			"sk_max_smg1",			BULLET_IMPULSE(200, 1225), 0 );
			def.AddAmmoType("357",				DMG_BULLET,					TRACER_LINE_AND_WHIZ,	"	sk_plr_dmg_357",		"sk_npc_dmg_357",			"sk_max_357",			BULLET_IMPULSE(800, 5000), 0 );
			def.AddAmmoType("XBowBolt",			DMG_BULLET,					TRACER_LINE,			"sk_plr_dmg_crossbow",		"sk_npc_dmg_crossbow",		"sk_max_crossbow",			BULLET_IMPULSE(800, 8000), 0 );
			def.AddAmmoType("Buckshot",			DMG_BULLET | DMG_BUCKSHOT,			TRACER_LINE,			"sk_plr_dmg_buckshot",		"sk_npc_dmg_buckshot",		"sk_max_buckshot",			BULLET_IMPULSE(400, 1200), 0 );
			def.AddAmmoType("RPG_Round",			DMG_BURN,					TRACER_NONE,			"sk_plr_dmg_rpg_round",		"sk_npc_dmg_rpg_round",		"sk_max_rpg_round",		0, 0 );
			def.AddAmmoType("SMG1_Grenade",			DMG_BURN,					TRACER_NONE,			"sk_plr_dmg_smg1_grenade",	"sk_npc_dmg_smg1_grenade",	"sk_max_smg1_grenade",	0, 0 );
			def.AddAmmoType("Grenade",			DMG_BURN,					TRACER_NONE,			"sk_plr_dmg_grenade",		"sk_npc_dmg_grenade",		"sk_max_grenade",		0, 0);
			def.AddAmmoType("Thumper",			DMG_SONIC,					TRACER_NONE,			10, 10, 2, 0, 0 );
			def.AddAmmoType("Gravity",			DMG_CLUB,					TRACER_NONE,			0,	0, 8, 0, 0 );
			def.AddAmmoType("Battery",			DMG_CLUB,					TRACER_NONE,			NULL, NULL, NULL, 0, 0 );
			def.AddAmmoType("GaussEnergy",			DMG_SHOCK,					TRACER_NONE,			"sk_jeep_gauss_damage",		"sk_jeep_gauss_damage", "sk_max_gauss_round", BULLET_IMPULSE(650, 8000), 0 ); // hit like a 10kg weight at 400 in/s
			def.AddAmmoType("CombineCannon",		DMG_BULLET,					TRACER_LINE,			"sk_npc_dmg_gunship_to_plr", 	"sk_npc_dmg_gunship", NULL, 1.5 * 750 * 12, 0 ); // hit like a 1.5kg weight at 750 ft/s
			def.AddAmmoType("AirboatGun",			DMG_AIRBOAT,					TRACER_LINE,			"sk_plr_dmg_airboat",		"sk_npc_dmg_airboat",		NULL,					BULLET_IMPULSE(10, 600), 0 );
			def.AddAmmoType("StriderMinigun",		DMG_BULLET,					TRACER_LINE,			5, 5, 15, 1.0 * 750 * 12, AMMO_FORCE_DROP_IF_CARRIED ); // hit like a 1.0kg weight at 750 ft/s
			def.AddAmmoType("HelicopterGun",		DMG_BULLET,					TRACER_LINE_AND_WHIZ,		"sk_npc_dmg_helicopter_to_plr", "sk_npc_dmg_helicopter",	"sk_max_smg1",	BULLET_IMPULSE(400, 1225), AMMO_FORCE_DROP_IF_CARRIED | AMMO_INTERPRET_PLRDAMAGE_AS_DAMAGE_TO_PLAYER );
			def.AddAmmoType("AR2AltFire",			DMG_DISSOLVE,					TRACER_NONE,			0, 0, "sk_max_ar2_altfire", 0, 0 );
			//***
		}
		return &def;
	}
=Pour terminer=
==Le fichier skill.cfg==
On m'a fait remarquer que les NPC ne causent pas de dommages au joueur, et en plus ils meurent très facilement. Ceci est simplement liée à l'absence du fichier skill.cfg dans le répertoire \cfg du mod.
pour corriger l'erreur, il vous faut copier/coller le fichier skill.cfg du jeu Half-Life 2 dans le répertoire
\cfg de votre mod.
==Animations des Models==
Pour en finir avec ces NPCs, il vous faut remplacer les animations des NPCs par celles d'origine. 
En clair, il faut remplacer les animations de HL2DM par celles de HL2, ce qui permettra à l'IA de fonctionner 
correctement car ee pourra utiliser ses propres animations, et non pas celles de H2DM qui sont différentes!
Il faut évidement utiliser d'autres animations pour les joueurs afin de ne pas corrompre leurs propres animations ( celles utiisées par HL2DM ).
Pour éviter de recompiler les models, il est préférable de les éditer avec un éditeur hexadécimal ( models de joueurs & noms des animations ). Pour vous faire gagner du temps, je vous suggère d'utiliser des models déjà hex-édités, par exemples ceux du Garry's mod.
Je vous ai préparé un petit package qui contient ce dont vous avez besoin pour faire fonctionner les Combine Soldiers et les Citoyens. En fait j'ai emprunté les models de Garry. Vous pouvez avec le ien vers le tléchargement à cette page.
	
Au fait, n'oubliez pas de prendre en compte ce changement dans le code, au niveau de la liste des models de joueurs dans le fichier hl2mp_player.cpp.