Zh/Condition

条件是NPC用于记录一些关于地图的状态的。条件主要是用于选择或插入修改的，并且每次 都会提醒。
一些条件实例:
* "I can see an enemy"//我可以看见一个敌人
* "I have taken some damage"//我被打中了！
* "My weapon's clip is empty"//我没子弹啦
The engine's set of shared conditions are usually supplemented by others specific to an NPC. For example, drown when in water, so they have a condition of their own that tells them "I am underwater".
== Interrupt conditions ==
As well as being used when selecting a new , conditions validate the current one by acting as "interrupts". Each schedule has an associated list of conditions that will cause it to exit if detected. When this happens, a new schedule is chosen.
For example, an NPC may be running a schedule to "Chase my enemy". This kind of schedule usually specifies the "I have chosen a new enemy" condition as an interrupt, because the NPC shouldn't keep chasing the old enemy if it has found a a newer, more important one.
== Adding new conditions ==
Conditions are normally enumerated inside the NPC class. For example, if our new NPC has a custom condition to reflect "I am hungry", our condition enum should look something like this:
enum 
{
	COND_MYNPC_HUNGRY = BaseClass::NEXT_CONDITION,
	NEXT_CONDITION
};
It's good practice to include the NEXT_CONDITION enum, so that NPCs derived from our NPC can use BaseClass::NEXT_CONDITION (as we do, on the first line) to declare their custom conditions without causing collisions with ours.
Conditions must then be declared inside the NPC's AI_BEGIN_CUSTOM_NPC block. This is done through the DECLARE_CONDITION macro. For the above example, we'd use this line:
AI_BEGIN_CUSTOM_NPC( npc_custom, CNPC_Custom )
	DECLARE_CONDITION( COND_MYNPC_HUNGRY )
AI_END_CUSTOM_NPC()
== Condition functions ==
 stores conditions as s in m_Conditions, but it should not be accessed directly. Instead, condition handling is done with these functions:
; GatherConditions()
: The primary entry point for condition generation, which is called every time the NPC thinks. 
;SetCondition( iCondition )
;ClearCondition( iCondition )
:Sets/clears a condition. Instead of passing an integer, you'll of course make use of your enum.
; HasCondition( iCondition )
:True if the specified condition is currently set. Remember that conditions are thrown away and re-generated on every think.
;BuildScheduleTestBits()
:This function allows you to dynamically modify a schedule's interrupts at any time. It's useful for appending your custom NPC conditions to base NPC or shared schedules. See below for an example.
== Examples ==
===GatherConditions()===
Here's a slightly trimmed version of the 's GatherConditions(). Antlions jump around a lot, and sometimes land on top of other NPCs. They need to know whether they've landed on an NPC when decision making, so they generate a condition when they have. Antlions also need to drown if they ever find themselves in water, so they set a condition when their water level reaches waist high.
void CNPC_Antlion::GatherConditions()
{
	BaseClass::GatherConditions();
	// See if I've landed on another NPC after jumping.
	CBaseEntity *pGroundEnt = GetGroundEntity();
	if ( pGroundEnt && pGroundEnt->GetSolidFlags() & FSOLID_NOT_STANDABLE && GetFlags() & FL_ONGROUND )
		SetCondition( COND_ANTLION_ON_NPC );
	else
		ClearCondition( COND_ANTLION_ON_NPC );
	// See if I've landed in water
	if( m_lifeState == LIFE_ALIVE && GetWaterLevel() > 1 )
		SetCondition( COND_ANTLION_IN_WATER );
}
The else statement uses ClearCondition() here because if the first if statement is true, then the boolean COND_ANTLION_ON_NPC is set to True. When this happens, another piece of code later on is fired, telling the antlion to get off the NPC's head, and hopefully, the antlion does not jump onto another NPC. Once it is no longer standing on an NPC, the condition goes back to being false, so it resumes normal activity.
//On another NPC's head?
if( HasCondition( COND_ANTLION_ON_NPC ) )
{
 // You're on an NPC's head. Get off.
 return SCHED_ANTLION_DISMOUNT_NPC;
}
===BuildScheduleTestBits()===
The allows mappers to specify whether an NPC should divert from their path to fight new enemies or keep running. This kind of dynamic interrupt specification can't be done in the static schedule definitions, and is exactly what BuildScheduleTestBits() is designed for.
void CAI_AssaultBehavior::BuildScheduleTestBits()
{
	BaseClass::BuildScheduleTestBits();
	// If we're allowed to divert, add the appropriate interrupts to our movement schedules
	if ( m_hAssaultPoint && m_hAssaultPoint->m_bAllowDiversion )
	{
		if ( IsCurSchedule( SCHED_MOVE_TO_ASSAULT_POINT ) ||
			 IsCurSchedule( SCHED_MOVE_TO_RALLY_POINT ) || 
			 IsCurSchedule( SCHED_HOLD_RALLY_POINT ) )
		{
			GetOuter()->SetCustomInterruptCondition( COND_NEW_ENEMY );
			GetOuter()->SetCustomInterruptCondition( COND_SEE_ENEMY );
		}
	}
}
== See also ==
* - a list of conditions inherited by all NPCs
* - records whether the NPC is idle, alert, in combat, etc.
Category:AI Programming