
| Deepflame | AI Tutorial #3 |
Welcome to Part 3 of my ai tut for HL.
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); int Save( CSave &save ); int Restore( CRestore &restore ); BOOL m_fPlayerAttacked; // This is to check if he's been attacked by the player static TYPEDESCRIPTION m_SaveData[];This is to check the damage it got and change things with it, and special values to save. Right underneath the LINK_ENTITY add :
TYPEDESCRIPTION CMonster::m_SaveData[] =
{
DEFINE_FIELD( CBullsquid, m_fPlayerAttacked, FIELD_BOOLEAN ),
};
IMPLEMENT_SAVERESTORE( CMonster, CBaseMonster );
That saves the boolean attacked by the player to THAT monster alone.
(If you don't know what a boolean is, it's the value that stores TRUE, FALSE or NULL)
Then go all the way down the code and add :
int CMonster :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
{
if ( pevAttacker->flags & FL_CLIENT )
{
m_fPlayerAttacked = TRUE; // Yup, I am attacked by him, so now I'm pissed
}
else
{
if (m_fPlayerAttacked != TRUE)
m_fPlayerAttacked = FALSE; // He's still my friend! (This is just to make sure it'll go right.
}
return CBaseMonster :: TakeDamage ( pevInflictor, pevAttacker, flDamage, bitsDamageType );
}
Now go up to void CMonster::Classify. Remove return CLASS_PLAYER_ALLY; and add :
if (m_fPlayerAttacked == FALSE || m_fPlayerAttacked == NULL) return CLASS_PLAYER_ALLY; // This is a friendly monster else return CLASS_ALIEN_MONSTER; // Now he's a bad guy =)There you go, if you'll shoot him, he'll fight ya. Else he won't. But did you notice you have to actually touch him to be damaged? That is good for killing headcrabs, but you won't hurt any other creatures with it. I said, in Part 2, that this wasn't the most efficient way of doing it, and it shows here. And ofcourse you want to fix that, and I don't blame you. First go to the class definition, and remove :
void EXPORT DoDamage ( CBaseEntity *pOther ); // Does the damageThen go down and remove the function DoDamage. Then go back to the class definition, and add :
CBaseEntity *Bite( void );And go all the way down, and add :
CBaseEntity *CMonster :: Bite( void )
{
TraceResult tr;
UTIL_MakeVectors( pev->angles );
Vector vecStart = pev->origin;
vecStart.z += pev->size.z * 0.5;
Vector vecEnd = vecStart + (gpGlobals->v_forward * 70);
UTIL_TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr );
if ( tr.pHit )
{
CBaseEntity *pEntity = CBaseEntity::Instance( tr.pHit );
return pEntity;
}
return NULL;
}
This does the damage.
Then go to HandleAnimEvent, to case MELEE_ATTACK1 and remove everything
between the first and the last bracket. Then add :
CBaseEntity *pHurt = Bite();
if ( pHurt )
{
// SOUND HERE!
UTIL_MakeVectors( pev->angles );
pHurt->pev->punchangle.x = 15;
pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_forward * 100 + gpGlobals->v_up * 50;
pHurt->TakeDamage( pev, pev, 25, DMG_CLUB );
}
Incase you missed what I meant with remove everything between the brackets,
it should look like this :
case MELEE_ATTACK1:
{
CBaseEntity *pHurt = Bite();
if ( pHurt )
{
// SOUND HERE!
UTIL_MakeVectors( pev->angles );
pHurt->pev->punchangle.x = 15;
pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_forward * 100 + gpGlobals->v_up * 50;
pHurt->TakeDamage( pev, pev, 25, DMG_CLUB );
}
}
break;
Now, that's step 1.
Time for the last part of the damaging, go to StartTask.
Change case TASK_MELEE_ATTACK1 so that it looks like this :
case TASK_MELEE_ATTACK1:
{
CBaseEntity *pHurt = Bite();
switch ( RANDOM_LONG ( 0, 2 ) )
{
case 0:
EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_attackgrowl.wav", 1, ATTN_NORM );
if ( pHurt )
{
// SOUND HERE!
UTIL_MakeVectors( pev->angles );
pHurt->pev->punchangle.x = 15;
pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_forward * 100 + gpGlobals->v_up * 50;
pHurt->TakeDamage( pev, pev, 25, DMG_CLUB );
}
break;
case 1:
EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_attackgrowl2.wav", 1, ATTN_NORM );
if ( pHurt )
{
// SOUND HERE!
UTIL_MakeVectors( pev->angles );
pHurt->pev->punchangle.x = 15;
pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_forward * 100 + gpGlobals->v_up * 50;
pHurt->TakeDamage( pev, pev, 25, DMG_CLUB );
}
break;
case 2:
EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_attackgrowl3.wav", 1, ATTN_NORM );
if ( pHurt )
{
// SOUND HERE!
UTIL_MakeVectors( pev->angles );
pHurt->pev->punchangle.x = 15;
pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_forward * 100 + gpGlobals->v_up * 50;
pHurt->TakeDamage( pev, pev, 25, DMG_CLUB );
}
break;
}
CBaseMonster :: StartTask ( pTask );
break;
}
If you've done this right, it should compile and work PERFECTLY!
I'll give my monster.cpp up to this point with it, so if you can't
get it right, you can use mine. But do try it yourself first, as
that gives a much better feeling =) (I always get happy when my code works)
------------------------------------------------------------------ Part 2 : Random Chattering according to the monsters current state ------------------------------------------------------------------Ok, let's asume we have world's smartest monster, and he knows how to speak English. You can either record a couple of sounds yourself, or you can just use Barney's sounds. For this tutorial I'll just use Barney's sounds =)
void TalkIdle(); void TalkAlert();in Schedule_t *CMonster :: GetSchedule( void ) under switch ( m_MonsterState )
case MONSTERSTATE_IDLE:
{
TalkIdle();
break;
}
case MONSTERSTATE_ALERT:
{
TalkAlert();
break;
}
And completely under the file :
void CMonster :: TalkAlert ( void )
{
switch ( RANDOM_LONG ( 0, 2 ) )
{
case 0:
EMIT_SOUND( ENT(pev), CHAN_VOICE, "barney/badarea.wav", 1, ATTN_NORM );
break;
case 1:
EMIT_SOUND( ENT(pev), CHAN_VOICE, "barney/badfeeling.wav", 1, ATTN_NORM );
break;
case 2:
EMIT_SOUND( ENT(pev), CHAN_VOICE, "barney/hearsomething.wav", 1, ATTN_NORM );
break;
}
return;
}
void CMonster :: TalkIdle ( void )
{
switch ( RANDOM_LONG ( 0, 2 ) )
{
case 0:
EMIT_SOUND( ENT(pev), CHAN_VOICE, "barney/beertopside.wav", 1, ATTN_NORM );
break;
case 1:
EMIT_SOUND( ENT(pev), CHAN_VOICE, "barney/aintscared.wav", 1, ATTN_NORM );
break;
case 2:
EMIT_SOUND( ENT(pev), CHAN_VOICE, "barney/howdy.wav", 1, ATTN_NORM );
break;
}
return;
}
And now he'll talk, although a bit shocky. To fix that you'll have to set a timer.
But I won't go into detail for that now. For tutorial Part 4 I'll probably fix this timer stuff
and give your monster shooting ability. And if you make a cool monster with special abilities,
or any other cool stuff, be sure to tell me. =)