//=========================================================
// Victory dance!
//=========================================================
Task_t tlGruntVictoryDance[] =
{
{ TASK_STOP_MOVING, (float)0 },
{ TASK_FACE_ENEMY, (float)0 },
{ TASK_WAIT, (float)1.5 },
{ TASK_GET_PATH_TO_ENEMY_CORPSE,(float)0 },
{ TASK_WALK_PATH, (float)0 },
{ TASK_WAIT_FOR_MOVEMENT, (float)0 },
{ TASK_FACE_ENEMY, (float)0 },
{ TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE },
};
Schedule_t slGruntVictoryDance[] =
{
{
tlGruntVictoryDance,
ARRAYSIZE ( tlGruntVictoryDance ),
bits_COND_NEW_ENEMY |
bits_COND_LIGHT_DAMAGE |
bits_COND_HEAVY_DAMAGE,
0,
"GruntVictoryDance"
},
};
The slGruntVictoryDance[] is the actual schedule, the tlGruntVictoryDance[] is the list of tasks for that schedule. The stuff after ARRAYSIZE ( tlGruntVictoryDance ), are the different things that can interrupt this schedule. In this case, seeing a new enemy, and taking light or heavy damage. The 0, are different sounds that can stop the schedule. No sounds can stop this schedule. The "GruntVictoryDance" is the name of the schedule, if you do impulse 103 while looking at a monster it will give you a report on its current AI status. Now looking at tlGruntVictoryDance[] you'll see a list of tasks to be performed for this schedule. The (float)0 followed by each task is the value of its data. Most tasks don't need anything here except (float)0 but some need a value, for example: TASK_PLAY_SEQUENCE need an ACT to play, most sequences in a monster a linked to specific ACTs, like ACT_DIE, ACT_RELOAD, etc. Farther down in the hgrunt.cpp file you'll see this:
DEFINE_CUSTOM_SCHEDULES( CHGrunt )
{
slGruntFail,
slGruntCombatFail,
slGruntVictoryDance,
slGruntEstablishLineOfFire,
slGruntFoundEnemy,
slGruntCombatFace,
slGruntSignalSuppress,
slGruntSuppress,
slGruntWaitInCover,
slGruntTakeCover,
slGruntGrenadeCover,
slGruntTossGrenadeCover,
slGruntTakeCoverFromBestSound,
slGruntHideReload,
slGruntSweep,
slGruntRangeAttack1A,
slGruntRangeAttack1B,
slGruntRangeAttack2,
slGruntRepel,
slGruntRepelAttack,
slGruntRepelLand,
};
IMPLEMENT_CUSTOM_SCHEDULES( CHGrunt, CSquadMonster );
That code defines all the custum schedules and implements them into the monster so it can use them.//=========================================================
// monster-specific schedule types
//=========================================================
enum
{
SCHED_GRUNT_SUPPRESS = LAST_COMMON_SCHEDULE + 1,
SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE,
SCHED_GRUNT_COVER_AND_RELOAD,
SCHED_GRUNT_SWEEP,
SCHED_GRUNT_FOUND_ENEMY,
SCHED_GRUNT_REPEL,
SCHED_GRUNT_REPEL_ATTACK,
SCHED_GRUNT_REPEL_LAND,
SCHED_GRUNT_WAIT_FACE_ENEMY,
SCHED_GRUNT_TAKECOVER_FAILED,
SCHED_GRUNT_ELOF_FAIL,
};
That declares all the schedule types which will be used later. It is important that after the first schedule you have the = LAST_COMMON_SCHEDULE + 1, otherwise your schedule numbers will be all wrong and your monsters will be doing stuff they shouldn't. The next part of code looks like this.
//=========================================================
// monster-specific tasks
//=========================================================
enum
{
TASK_GRUNT_FACE_TOSS_DIR = LAST_COMMON_TASK + 1,
TASK_GRUNT_SPEAK_SENTENCE,
TASK_GRUNT_CHECK_FIRE,
};
That peice of code declares all the task types. Again its important to have = LAST_COMMON_TASK + 1, after the first task. Now, moving on.if ( HasConditions( bits_COND_ENEMY_DEAD )
&& LookupActivity( ACT_VICTORY_DANCE ) != ACTIVITY_NOT_AVAILABLE )
{
return GetScheduleOfType ( SCHED_VICTORY_DANCE );
}
And back in hgrunt.cpp, GetScheduleOfType function.
case SCHED_VICTORY_DANCE:
{
if ( InSquad() )
{
if ( !IsLeader() )
{
return &slGruntFail[ 0 ];
}
}
return &slGruntVictoryDance[ 0 ];
}
Now heres what that code does. In the GetSchedule code the game checks to see if the monsters enemy is dead and if it has a sequence linked the the ACT_VICTORY_DANCE activity. If it does have the act, then it calls the monsters GetScheduleOfType with SCHED_VICTORY_DANCE. In the hgrunts GetScheduleOfType it looks through all the schedule types. If the grunt is in a squad and is the leader then it returns the fail schedule, otherwise it returns the victory dance schedule (seen farther up this page).case TASK_GRUNT_FACE_TOSS_DIR:
break;
As you can see this task doesn't need to do anything when it first starts. Not all tasks use StartTask or RunTask (although they have to use one). Now in RunTask.
case TASK_GRUNT_FACE_TOSS_DIR:
{
// project a point along the toss vector and turn to face that point.
MakeIdealYaw( pev->origin + m_vecTossVelocity * 64 );
ChangeYaw( pev->yaw_speed );
if ( FacingIdeal() )
{
m_iTaskStatus = TASKSTATUS_COMPLETE;
}
break;
}
Now this code is called every time the monster thinks. It tells the grunt to turn towards where he wants to throw a grenade and when he finally faces the right direction the task is complete. The code that tells the schedule that this task is complete is the m_iTaskStatus = TASKSTATUS_COMPLETE;. You can also use TaskComplete() and TaskIsComplete(). TaskComplete checks to see if the task failed or not, TaskIsComplete just returns m_iTaskStatus = TASKSTATUS_COMPLETE;. It is important that you complete the task somehow, otherwise the monster just performs the task for an infinate amount of time (aka it becomes a vegie).You must log in to post a comment. You can login or register a new account.