Monsters Programming - The Concepts of Half-Life's AI Last edited 2 years ago2021-04-10 14:07:13 UTC

You are viewing an older revision of this wiki page. The current revision may be more detailed and up-to-date. Click here to see the current revision of this page.

Half-Life Programming

Now that weapons and entities in general are covered. This chapter is about exploring another kind of entities: monsters.

Creating a monster or NPC (shortcut for "Non-Player Character") is one of the most difficult entities to program because they have to behave in a certain way to make a game fun. Artificial Intelligence ("AI") is the component responsible for manipulating that behavior.

We will explore in this page the concepts of Half-Life's AI before we get to create a monster itself. AI and monsters programming always happen in the server project, you will likely never touch the client project. For the sake of clarity, important terminology is in bold.

Classes

Let us do the same thing as we did for the weapons: look at the class declaration of scientist (CScientist), the human grunt (CHGrunt) and the headcrab (CHeadcrab) and determine the class hierarchy.

The reasons we are looking at those three monsters is that they have distinct traits: one is a "simple" monster, another one is a talkative friend and the last one has the ability to be part and communicate within a squad.

With the knowledge and experience you gained in the previous chapters of this book and/or from looking at the HL SDK by yourself, you should be able to do this on your own.

Need some help? The files you should be looking at are scientist.cpp, hgrunt.cpp and headcrab.cpp. Some IDEs like Visual Studio on Windows provide a "class viewer" to navigate through classes rather than files and this could help you when navigating through the code.

Have you finished? Here is the class hierarchy you should have found:
CHGrunt
└ CSquadMonster
CScientist
└ CTalkMonster
  CHeadcrab
  └ CBaseMonster
    └ CBaseToggle
      └ CBaseAnimating
        └ CBaseDelay
          └ CBaseEntity
If you found the correct hierarchy, congratulations! If not, do not get discouraged, learn from your mistakes and try again, that's how you gain knowledge and experience.

We can determine that those three monsters uses three different bases:
Did you know?
The player (CBasePlayer in player.h) is also a "monster", its class is based on CBaseMonster.

It may be weird but if you think about it, players and monsters share some traits like bleeding, dying, moving, attacking, be attacked and so on.

This means that a monster does not always involves an AI. For those who are familiar with Unreal Engine 4: this is a bit like having an APlayerController or AAIController possessing an ACharacter (or anything derived from APawn).

Everything has its beginning

Whenever a monster spawn through Spawn(), a call to MonsterInit() is made which is responsible of checking if the monster is allowed to spawn using the returned value of BOOL CGameRules::FAllowMonsters() which returns TRUE or FALSE depending on the game rules implementation used in g_pGameRules.

The monster remove itself immediately if that is not the case. Otherwise, it initialize some variables which some of them are described in this page.

That same method make a call to StartMonster() which is more interesting because it initialize the monster's "thinking" to MonsterThink() creating a loop of calls to RunAI() until the monster dies (IsAlive() returns FALSE).

RunAI() has several responsibilities such as: These functions are responsible of toggling on and off condition bits of the monster.

Condition bits

You might have seen in the code of monsters constants like bits_COND_LIGHT_DAMAGE. Those are conditions bits, they are used as a way to give information to the AI like "this monster has taken light damage" for example.

They are either disabled or enabled in the monster's m_afConditions attribute. This is done through bitwise operation using the OR operator (|).

In the previous section, we said that several methods such as Look() and GetEnemy() toggle on and/or off these conditions bits. Look() handle all condition bits that involves sighting such like bits_COND_SEE_FEAR meaning "I see an enemy that I'm afraid of".

GetEnemy() also does it with bits_COND_NEW_ENEMY for "I have a new enemy to deal with" for example. Likewise, CheckAmmo() does it with bits_COND_NO_AMMO_LOADED ("my firearm is empty").

Here is a list of condition bits common to all monsters, keep in mind that a monster may use its own (schedule.h):
ConstantValueWhat does it mean for the monster
bits_COND_NO_AMMO_LOADED(1 << 0)My firearm is empty.
bits_COND_SEE_HATE(1 << 1)I see an enemy that I hate (more on this later).
bits_COND_SEE_FEAR(1 << 2)I see an enemy that I am afraid of (more on this later).
bits_COND_SEE_DISLIKE(1 << 3)I see an enemy that I dislike (more on this later).
bits_COND_SEE_ENEMY(1 << 4)I see an enemy.
bits_COND_ENEMY_OCCLUDED(1 << 5)Enemy is occluded by the world.
bits_COND_SMELL_FOOD(1 << 6)I smell food.
bits_COND_ENEMY_TOOFAR(1 << 7)My enemy is too far.
bits_COND_LIGHT_DAMAGE(1 << 8)I took light damage, just a flesh wound.
bits_COND_HEAVY_DAMAGE(1 << 9)I took heavy damage, that hurts!
bits_COND_CAN_RANGE_ATTACK1(1 << 10)I can perform one kind of ranged attack.
bits_COND_CAN_MELEE_ATTACK1(1 << 11)I can perform one kind of melee attack.
bits_COND_CAN_RANGE_ATTACK2(1 << 12)I can perform another kind of ranged attack.
bits_COND_CAN_MELEE_ATTACK2(1 << 13)I can perform another kind of melee attack.
bits_COND_PROVOKED(1 << 15)A friendly (likely the player) provoked me.
bits_COND_NEW_ENEMY(1 << 16)I have a new enemy.
bits_COND_HEAR_SOUND(1 << 17)I hear something interesting.
bits_COND_SMELL(1 << 18)I smell something interesting.
bits_COND_ENEMY_FACING_ME(1 << 19)My enemy is facing me.
bits_COND_ENEMY_DEAD(1 << 20)My enemy is dead.
bits_COND_SEE_CLIENT(1 << 21)I see a client (a bot or a player).
bits_COND_SEE_NEMESIS(1 << 22)I see an enemy which I consider as my nemesis (more on this later).
bits_COND_SPECIAL1(1 << 28)First special condition specific to my type of monster.
bits_COND_SPECIAL2(1 << 29)Second special condition specific to my type of monster.
bits_COND_TASK_FAILED(1 << 30)I failed to perform a task (more on this later).
bits_COND_SCHEDULE_DONE(1 << 31)I completed a schedule (more on this later).
Watch out for the gaps in the values
The constant using the value (1 << 14) is commented and not implemented which is why it is not present in the table above.

According to that comment, it was supposed to be a third kind of ranged attack (bits_COND_CAN_RANGE_ATTACK3).

Likewise, there is no constants between (1 << 23) and (1 << 27).
It is worth mentioning that bits_COND_ALL_SPECIAL exists and is a shortcut to include both special conditions ((bits_COND_SPECIAL1 | bits_COND_SPECIAL2)).

Same with bits_COND_CAN_ATTACK which regroup all kinds of attack ((bits_COND_CAN_RANGE_ATTACK1 | bits_COND_CAN_MELEE_ATTACK1 | bits_COND_CAN_RANGE_ATTACK2 | bits_COND_CAN_MELEE_ATTACK2)).

These are common methods used to handle condition bits with an added description of what they do:
// Returns the conditions ignored by this monster (useful to ignore flinching while attacking for example)
virtual int IgnoreConditions();

// Set the condition(s) specified in "iConditions" for this monster
inline void SetConditions( int iConditions )
{
    m_afConditions |= iConditions;
}

// Clear the condition(s) specified in "iConditions" for this monster
inline void ClearConditions( int iConditions )
{
    m_afConditions &= ~iConditions;
}

// Returns TRUE if any condition in "iConditions" is met for this monster, FALSE otherwise
inline BOOL HasConditions( int iConditions )
{
    if ( m_afConditions & iConditions )
        return TRUE;

    return FALSE;
}

// Returns TRUE if all conditions in "iConditions" are met for this monster, FALSE otherwise
inline BOOL HasAllConditions( int iConditions )
{
    if ( (m_afConditions & iConditions) == iConditions )
        return TRUE;

    return FALSE;
}

Sound bits

The goal of the Listen() method (called by RunAI()) is to make the monster hear sounds he could be interested in and classify them using sound bits such as bits_COND_HEAR_DANGER meaning "I heard a dangerous sound". They work in the same fashion as condition bits.
What is that stench?
You likely know that monsters can "smell" and react to it like Barney and scientists will comment on bad scents and bullsquids can eat headcrab carcasses.

Valve made the choice of relying the "smell" stuff on the sound stuff because they kinda work the same way hence why this is also done in Listen().
Here is a table of common sound bits (and "smell bits") and what they mean (soundent.h):
ConstantValueWhat does it mean for the monster
bits_SOUND_NONE0Nothing worth needing my attention.
bits_SOUND_COMBAT(1 << 0)I hear combat, could be explosions or gunshots.
bits_SOUND_WORLD(1 << 1)I hear doors opening/closing, glass breaking.
bits_SOUND_PLAYER(1 << 2)I hear the player making noise (running, walking, jumping...)
bits_SOUND_CARCASS(1 << 3)I smell a dead body.
bits_SOUND_MEAT(1 << 4)I smell gibs or pork chop.
bits_SOUND_DANGER(1 << 5)I hear danger such as a grenade bouncing, barrel about to explode...
bits_SOUND_GARBAGE(1 << 6)I smell trash cans, banana peels, old fast food bags.
Likewise, remember that monsters may use their own.

One particular method to note here is int ISoundMask() which returns all the bits that the monster should care about that could influence its behavior. There is also BOOL CSound::FIsSound() and BOOL CSound::FIsScent() to distinguish between sound and smell bits.

You also might have seen calls to CSoundEnt::InsertSound( int iType, const Vector &vecOrigin, int iVolume, float flDuration ) in various places of the HL SDK that is used to insert sounds (and smells) in the world for monsters to notice.

Memory

All monsters have a m_afMemory attribute which act as memory for the monster to remember various events. They also work like condition and sound/smell bits.

Again, here is a table of common memory bits (monsters.h):
ConstantValueWhat does it mean for the monster
bits_MEMORY_PROVOKED(1 << 0)A friendly (likely the player) provoked me.
bits_MEMORY_INCOVER(1 << 1)I am in a spot for cover.
bits_MEMORY_SUSPICIOUS(1 << 2)A friendly (likely the player) accidently or tried to provoke me, I should be careful.
bits_MEMORY_PATH_FINISHED(1 << 3)I finished moving on a specific path (used by Big Momma only).
bits_MEMORY_ON_PATH(1 << 4)I'm moving on a specific path.
bits_MEMORY_MOVE_FAILED(1 << 5)I already failed to move somewhere.
bits_MEMORY_FLINCHED(1 << 6)I already flinched.
bits_MEMORY_KILLED(1 << 7)I'm already dead (Valve recognized this as a programming hack).
bits_MEMORY_CUSTOM4(1 << 8)Fourth special memory specific to my type of monster.
bits_MEMORY_CUSTOM3(1 << 9)Third special memory specific to my type of monster.
bits_MEMORY_CUSTOM2(1 << 10)Second special memory specific to my type of monster.
bits_MEMORY_CUSTOM1(1 << 11)First special memory specific to my type of monster.
You probably guessed already: monsters may have their own as well.

Barney is going to help us demonstrate the usage of memory for monsters: if he is not in combat and you hurt him, he's going to remember that you provoked him (bits_MEMORY_PROVOKED) making him stopping following you and try to kill you.

If he is in combat and you "accidentally" hurt him, he is going to get suspicious of you (bits_MEMORY_SUSPICIOUS) and remind you he's a friendly. If you do it again, he will get provoked (bits_MEMORY_PROVOKED) and you already know what is going to happen.

As usual, there are methods to manipulate all of this (with added description):
// Store the bit(s) of memory specified in "iMemory" for this monster
inline void Remember( int iMemory )
{
    m_afMemory |= iMemory;
}

// Remove the bit(s) of memory specified in "iMemory" for this monster
inline void Forget( int iMemory )
{
    m_afMemory &= ~iMemory;
}

// Returns TRUE if this monster remember anything in "iConditions", FALSE otherwise
inline BOOL HasMemory( int iMemory )
{
    if ( m_afMemory & iMemory )
        return TRUE;

    return FALSE;
}

// Returns TRUE if this monster remember everything in "iConditions", FALSE otherwise
inline BOOL HasAllMemories( int iMemory )
{
    if ( (m_afMemory & iMemory) == iMemory )
        return TRUE;

    return FALSE;
}

Capabilities

A capability basically describe what a monster can and can't do. It is stored in the m_afCapability attribute and works the same as the other kinds of bits.

Here is a table of the common ones (cbase.h):
ConstantValueWhat does it mean for the monster
bits_CAP_DUCK(1 << 0)I can duck.
bits_CAP_JUMP(1 << 1)I can jump.
bits_CAP_STRAFE(1 << 2)I can strafe.
bits_CAP_SQUAD(1 << 3)I can join, form and communicate in a squad.
bits_CAP_SWIM(1 << 4)I can swim.
bits_CAP_CLIMB(1 << 5)I can climb ladders, ropes.
bits_CAP_USE(1 << 6)I can open doors, push buttons, pull levers.
bits_CAP_HEAR(1 << 7)I can hear sounds.
bits_CAP_AUTO_DOORS(1 << 8)I can use automated doors.
bits_CAP_OPEN_DOORS(1 << 9)I can open "manual" doors.
bits_CAP_TURN_HEAD(1 << 10)I can turn my head around (if my bone controller at index 0 is correct).
bits_CAP_RANGE_ATTACK1(1 << 11)I can perform one kind of ranged attack.
bits_CAP_RANGE_ATTACK2(1 << 12)I can perform another kind of ranged attack.
bits_CAP_MELEE_ATTACK1(1 << 13)I can perform one kind of melee attack.
bits_CAP_MELEE_ATTACK2(1 << 14)I can perform another kind of melee attack.
bits_CAP_FLY(1 << 15)I can fly.
Again, a monster might have its own capabilities. There is also bits_CAP_DOORS_GROUP available that is a shortcut for bits_CAP_USE, bits_CAP_AUTO_DOORS and bits_CAP_OPEN_DOORS together.

However, there are no methods to manipulate those. You basically set m_afCapability in the monster's Spawn() method. If for some reason you need to query if the monster is capable of something, you check directly if its bit is set (if ( m_afCapability & bits_CAP_SOMETHING )).

Monster state

All monster have a monster state stored in m_monsterState describing in general their current state.

That state impact several things such as tasks and schedules selection, some generic behavior, behavior when performing scripted events ((ai)scripted_sequence) and so on.

Here is a table of possible states and what they mean (util.h):
ConstantValueWhat does the monster do?
MONSTERSTATE_NONE0Nothing.
MONSTERSTATE_IDLE1Idling.
MONSTERSTATE_COMBAT2In combat.
MONSTERSTATE_ALERT3Staying alert.
MONSTERSTATE_HUNT4On the hunt (doesn't seems to be used).
MONSTERSTATE_PRONE5Being grabbed by a barnacle or repeling down a rope (human grunt).
MONSTERSTATE_SCRIPT6Performing a script ((ai)scripted_sequence).
MONSTERSTATE_PLAYDEAD7Play dead (doesn't seems to be used).
MONSTERSTATE_DEAD8Dying/dead.

Activities

An activity allow tying sequences/animations in the model (MDL) with an action in the game code like idling, running, walking and so on.

If you look at any monster model with Solokiller's Half-Life Model Viewer (decompiling the model and looking at the QC also works), you can see when browsing the sequences/animations that they may or may not be tied to a specific activity. Idle sequences/animations are likely tied to ACT_IDLE, walking sequences/animations are likely tied to ACT_WALK and so on.

Sometimes, they are not tied to an activity but they may be used by the game code in a different way, if you look at certain monsters code, you can see the usage of int LookupSequence( const char *label ) instead of int LookupActivity( int activity ). Sequences/animations not tied to an activity are also likely used for scripting purposes ((ai)scripted_sequence).

It is important to note that activities are tied to an index through an "activity map" (activitymap.h). Here is a list of common activities (activity.h):
ConstantValueWhat does it mean for the monster
ACT_RESET0Set m_Activity to this invalid value to force a reset to m_IdealActivity.
ACT_IDLE1Normal idling.
ACT_GUARD2Idling in a guarding stance.
ACT_WALK3Normal walking.
ACT_RUN4Normal running.
ACT_FLY5Fly (and flap if appropriate).
ACT_SWIM6Swim.
ACT_HOP7Do a vertical jump.
ACT_LEAP8Do a long forward jump.
ACT_FALL9Fall.
ACT_LAND10Land from falling or after a jump.
ACT_STRAFE_LEFT11Strafe to the left.
ACT_STRAFE_RIGHT12Strafe to the right.
ACT_ROLL_LEFT13Tuck and roll to the left.
ACT_ROLL_RIGHT14Tuck and roll to the right.
ACT_TURN_LEFT15Turn quickly to the left while being stationary.
ACT_TURN_RIGHT16Turn quickly to the right while being stationary.
ACT_CROUCH17Crouch down from a standing position.
ACT_CROUCHIDLE18Stay crouched (loops).
ACT_STAND19Stand up from a crouched position.
ACT_USE20Use something.
ACT_SIGNAL121First way of making a signal.
ACT_SIGNAL222Second way of making a signal.
ACT_SIGNAL323Third way of making a signal.
ACT_TWITCH24Twitch.
ACT_COWER25Cower.
ACT_SMALL_FLINCH26Do a small flinch after a light damage.
ACT_BIG_FLINCH27Do a big flinch after a heavy damage.
ACT_RANGE_ATTACK128Do the first type of ranged attack.
ACT_RANGE_ATTACK229Do the second type of ranged attack.
ACT_MELEE_ATTACK130Do the first type of melee attack.
ACT_MELEE_ATTACK231Do the second type of melee attack.
ACT_RELOAD32Reload the firearm.
ACT_ARM33Draw the firearm.
ACT_DISARM34Holster the firearm.
ACT_EAT35Eat some tasty food (loop).
ACT_DIESIMPLE36Simple death.
ACT_DIEBACKWARD37Die and fall backward.
ACT_DIEFORWARD38Die and fall forward.
ACT_DIEVIOLENT39Violent death.
ACT_BARNACLE_HIT40Barnacle tongue hits a monster.
ACT_BARNACLE_PULL41Barnacle is lifting the monster (loop).
ACT_BARNACLE_CHOMP42Barnacle latches on to the monster.
ACT_BARNACLE_CHEW43Barnacle is holding the monster in its mouth (loop).
ACT_SLEEP44Take a nap.
ACT_INSPECT_FLOOR45Look at something on or near the floor.
ACT_INSPECT_WALL46Look at something directly ahead of you (doesn't have to be a wall or on a wall).
ACT_IDLE_ANGRY47Pissed off idling (loop).
ACT_WALK_HURT48Wounded walking (loop).
ACT_RUN_HURT49Wounded running (loop).
ACT_HOVER50Idle while in flight.
ACT_GLIDE51Fly (don't flap).
ACT_FLY_LEFT52Turn left in flight.
ACT_FLY_RIGHT53Turn right in flight.
ACT_DETECT_SCENT54Smells a scent carried by the air.
ACT_SNIFF55Sniff something in front of the monster.
ACT_BITE56Eat something in a single bite (the difference with ACT_EAT is that this one does not loop).
ACT_THREAT_DISPLAY57Demonstrate without attacking that I'm angry (yelling for example).
ACT_FEAR_DISPLAY58Just saw something scary of feared.
ACT_EXCITED59Show excitement (like seeing some very tasty food to eat).
ACT_SPECIAL_ATTACK160First special attack.
ACT_SPECIAL_ATTACK261Second special attack.
ACT_COMBAT_IDLE62Agitated idle.
ACT_WALK_SCARED63Scared walking.
ACT_RUN_SCARED64Scared running.
ACT_VICTORY_DANCE65Do a victory dance (after killing a player for example).
ACT_DIE_HEADSHOT66Die from a hit in the head.
ACT_DIE_CHESTSHOT67Die from a hit in the chest.
ACT_DIE_GUTSHOT68Die from a hit in the gut.
ACT_DIE_BACKSHOT69Die from a hit in the back.
ACT_FLINCH_HEAD70Flinch from a hit in the head.
ACT_FLINCH_CHEST71Flinch from a hit in the chest.
ACT_FLINCH_STOMACH72Flinch from a hit in the stomach.
ACT_FLINCH_LEFTARM73Flinch from a hit in the left arm.
ACT_FLINCH_RIGHTARM74Flinch from a hit in the right arm.
ACT_FLINCH_LEFTLEG75Flinch from a hit in the left leg.
ACT_FLINCH_RIGHTLEG76Flinch from a hit in the right leg.
We have an intruder
ACT_RESET is the only activity that is not part of the activity map due to its special behavior.
Surprisingly, no monsters have their own activities.

It is possible to have multiple sequences/animations tied to a same activity. In that scenario, the algorithm for choosing a sequence (simplified) is: An use case for this is choosing a random idle sequence/animation from three possible choices whenever a monster is idling.

All monsters have three attributes named m_Activity, m_IdealActivity and m_movementActivity. They indicate the current activity, ideal next one and the one to use for movement respectively. For methods, there are SetActivity( Activity newActivity ) and Stop() that you might find useful.
It is very dangerous to manipulate activities
They are common to all monsters and if for whatever reason you need to add, edit and delete one or many of them, exercise extreme caution.

You have to treat them and the activity map in the same way you would treat an "exposed C++ interface". If you add a new activity in the middle of the map for example, you will break all monsters unless you update everything including all monsters MDL files. The same applies for deleting an entry and/or changing the order in the map.

Updating the game code will not be enough, you will also be required to build and use a custom version of StudioMDL, the tool to compile QC files into MDLs. If you do not do that, StudioMDL will not be "aware" of the changes you made.

In a nutshell, making any change to activities and the activity map is "extraordinary", only do it if you really think it is really worth it and if you are aware of the consequences.

Animation events

A model can contains events allowing executing specific things such as showing a muzzle flash, play a sound and more. For monsters, it is a way to communicate with the game code and it is an important aspect to consider.

If you look at the scientist model (models/scientist.mdl), more specifically the give_shot animation, we can see some valuable information: there is a tie to the ACT_MELEE_ATTACK1 activity and there is one animation event tied to this sequence/animation. If we look at the event's information, it is triggered on the 17th frame and has the ID #1.
Wait, "ACT_MELEE_ATTACK1" for giving a syringe shot?
Yes, the name of the activity does not always match the real context of the sequence/animation.

The reason for this and you probably have guessed it is because of activities. Why would you risk breaking all monsters because you added an extra activity (and updated the activity map) only to match the context of a single action for only one kind of monster instead of using an existing activity that the same monster isn't already using?

So do not be surprised if you see things like ACT_EAT being used for a sequence/animation that does not involves "eating".
HandleAnimEvent( MonsterEvent_t *pEvent ) is the method responsible for handling animation events. If we look at the scientist's version, we can see this code:
// For the context:
// "SCIENTIST_AE_HEAL"      = 1
// "SCIENTIST_AE_NEEDLEON"  = 2
// "SCIENTIST_AE_NEEDLEOFF" = 3
// "NUM_SCIENTIST_HEADS"    = 4

void CScientist::HandleAnimEvent( MonsterEvent_t *pEvent )
{
    switch ( pEvent->event )
    {
    case SCIENTIST_AE_HEAL: // Heal my target (if within range)
        Heal();
        break;
    case SCIENTIST_AE_NEEDLEON:
        {
        int oldBody = pev->body;
        pev->body = (oldBody % NUM_SCIENTIST_HEADS) + NUM_SCIENTIST_HEADS * 1;
        }
        break;
    case SCIENTIST_AE_NEEDLEOFF:
        {
        int oldBody = pev->body;
        pev->body = (oldBody % NUM_SCIENTIST_HEADS) + NUM_SCIENTIST_HEADS * 0;
        }
        break;
    default:
        CTalkMonster::HandleAnimEvent( pEvent );
    }
}
Taking again our event as example, it will call HandleAnimEvent( MonsterEvent_t *pEvent ) with a value of "1" for pEvent->event thus calling Heal() to heal the player (if conditions are still met). You can also note that there are animation events to show and hide the syringe itself as well. Other examples includes shooting firearms, shooting projectiles, throwing grenades.

Animation events may be common to several entities or specific to a particular one.

Other attributes

There are other attributes common to all monsters that are not mentioned (yet for some of them) in this page. However, they do deserve to be known but they don't need a dedicated section to explain what they actually do.

Here is a table of some of them (basemonster.h):
AttributeDescription
EHANDLE m_hEnemyCurrent enemy.
EHANDLE m_hTargetEntCurrent target (to follow or to move to).
EHANDLE m_hOldEnemy[MAX_OLD_ENEMIES]Old enemies (MAX_OLD_ENEMIES = 4).
Vector m_vecOldEnemy[MAX_OLD_ENEMIES]Position of old enemies.
float m_flFieldOfViewField of view of the monster (not in degrees, 0.5f is 180 degrees to give an idea of the scale)
float m_flWaitFinishedGame time to tell the monster that the wait is over (defined by waiting tasks).
float m_flMoveWaitFinishedGame time to tell the monster that the wait before doing any kind of movement is over.
int m_LastHitGroupLast hitgroup being hit (head, chest, stomach...)
Schedule_t *m_pScheduleCurrent schedule.
Vector m_vecEnemyLKPLast known position of the current enemy.
int m_cAmmoLoadedHow much ammo the firearm has.
float m_flNextAttackTime before making a new attack.
int m_bloodColorColor of the blood when being hurt (red, yellow...)
int m_failScheduleSchedule to use in case of a task failure.
float m_flHungryTimeTime before eating something again.
float m_flDistTooFarRange in units to determine that the monster is too far from its enemy (see condition bit bits_COND_ENEMY_TOO_FAR).
float m_flDistLookRange in units to acquire enemies.
int m_iTriggerConditionFor (ai)scripted_sequence, this is the condition to trigger something (half health, see a player...)
string_t m_iszTriggerTargetFor (ai)scripted_sequence, this is the name of the entity to trigger.
Vector m_HackedGunPosThe position of the firearm relative to the monster's origin.

Recap

This page has a lot of information to digest so you might want a recap of everything we have seen so far: Here is also a simplified flow chart of the lifecycle of a monster:
User posted image

Comments

You must log in to post a comment. You can login or register a new account.