In the previous tutorial we took a detailed look at adding a new type of NPC to the Single Player Game code. This was a civilian NPC that only attacked under special circumstances. In the second part of the tutorial we will look at adding a regular monster NPC, a werewolf, who attacks all other non-monsters in the game. This monster will have two types of attack - the closeup melee attack and the far jump attack.
Model Requirements
As with our civilian NPC, we need a model for our werewolf and some support files. The model we will be using, avalible on the Downloads page, is named awolf.md3. The model must be located in the models/npc/awolf directory, along with the necessary support files. Since all NPC models require an animation.txt file we must supply one for the werewolf model. It should look like the following :
// start frame, numframes, looping, frames per second
178 6 0 10 // Death #1
184 6 0 10 // Death #2
190 8 0 10 // Death #3
0 0 0 10 // no animation
160 9 0 10 // far attack
160 9 0 10 // melee attack
135 19 19 10 // stand inactive
135 19 19 10 // stand active
154 6 6 10 // walk
40 6 6 10 // run
0 0 0 10 // no animation
66 2 0 10 // jump
68 4 0 10 // land
169 4 0 10 // pain
This is somewhat different from our civilian NPCs animation.txt file since this monster has two attack modes and can jump, requiring more animations.
Model Sounds
Since an NPC would be incomplete without a set of custom sounds we will be defining them using the sounds.txt file. Besides the death, movement and pain sounds, which we defined for our other NPC, we will also define sounds for the attack and jump animations. The sounds.txt file for the werewolf NPC should look like :
// animation number, starting frame, sound file
1 0 sound/npc/awolf/death1.wav
2 0 sound/npc/awolf/death2.wav
3 0 sound/npc/awolf/death3.wav
5 0 sound/npc/awolf/def2.wav
6 0 sound/npc/awolf/def2.wav
9 6 sound/player/footsteps/boot1.wav
9 13 sound/player/footsteps/boot1.wav
9 6 sound/player/footsteps/boot2.wav
9 13 sound/player/footsteps/boot2.wav
9 6 sound/player/footsteps/boot3.wav
9 13 sound/player/footsteps/boot3.wav
10 0 sound/player/footsteps/boot1.wav
10 4 sound/player/footsteps/boot1.wav
10 0 sound/player/footsteps/boot2.wav
10 4 sound/player/footsteps/boot2.wav
10 0 sound/player/footsteps/boot3.wav
10 4 sound/player/footsteps/boot3.wav
12 0 sound/npc/awolf/jump1.wav
14 0 sound/npc/awolf/pain100_1.wav
14 0 sound/npc/awolf/pain100_2.wav
14 0 sound/npc/awolf/pain75_1.wav
14 0 sound/npc/awolf/pain75_2.wav
-1
Model Configuration
Because it is easier to tweak the NPC using a config.txt file rather than recompiling after every change, we will include this file in our model directory. It would look like :
100 // health
1.0 // pain factor
20 // walking speed
40 // running speed
140 // fov
30 // jump height
20 // walking rotation
20 // running rotation
( -16 -16 -24 ) // min bounding box
( 16 16 40 ) // max bounding box
( 0 0 36 ) // eye coords
The werewolf, from this config file, is not a fast runner but it can jump quite high. This fits well with our monster type.
Entity Definition
Like any entity we add to the game we must provide an entity definition for our level editor. The name of the entity is determined by the name of the directory that contains the model information, which in turn is determined by the name of the model. Therefore, our entity will be called npc_awolf and its definition will be :
/*QUAKED npc_awolf (0.2 0.5 0) (-16 -16 -40) (16 16 8) suspended disabled deaf first_taunt
"shot_factor" likeness of NPC to fire (far weapon), 1.0 means always fire,
0 means never fire, only chase, default is 0.5
*/
Source Code Changes
This NPC only requires changes to the game module but the cgame module must be recompiled as well since there are some changes in the bg_* files.
As with any new NPC we must add the new type to the list of NPC types. In keeping with the naming scheme already in place, our NPC's type will be NPC_AWOLF.
In bg_public.h about line 657 add :
NPC_SEALORD,
NPC_SOLDIER1,
NPC_SOLDIER2,
// npc addon
NPC_MAN,
// end npc addon
NPC_AWOLF,
NPC_NUMNPCS
} npcType_t;
We now add the definition of our NPC and its default values to the bg_npclist array. Some of these values will be used if the config.txt file is missing.
In bg_misc.c at the end of the bg_npclist definition about line 199 add :
// npc addon
{
"npc_man" // classname
NPC_MAN,
100, // health
1.0, // pain factor
20, // walk speed
60, // runningSpeed
180, // fov
20, // jumpHeight
20, // walkingRotSpd
75, // runningRotSpd
110, // melee distance
20, // melee damage
0, // far damage
{0},
{-24,-24,-24}, // bounding box min
{24,24,40}, // bounding box max
{0,0,30}, // eye coords
"", // precache
"" // sounds
},
// end npc addon
{
"npc_awolf" // classname
NPC_AWOLF,
100, // health
1.0, // pain factor
20, // walk speed
60, // runningSpeed
180, // fov
20, // jumpHeight
20, // walkingRotSpd
75, // runningRotSpd
0, // melee distance
20, // melee damage
0, // far damage
{0},
{-24,-24,-24}, // bounding box min
{24,24,40}, // bounding box max
{0,0,30}, // eye coords
"", // precache
"" // sounds
},
{NULL},
};
This completes the basic monster NPC. It will attack all non-monsters in the game and inflict melee damage on them. We want to customize our werewolf AI so it has the jump attack and this requires a few more code changes.
In g_npc.c in function G_RunNPC about line 580 add :
else if (npc->dontMoveTime) // NPC must not move
{
if ((ent->npc->npcType==NPC_METLAR
|| ent->npc->npcType==NPC_AWOLF) && npc->toFire==2) // a jump attack?
{
vec3_t dir,ang;
In g_npcthink.c in function NPC_ThinkMove about line 693 add :
if (ent->npc->npcType==NPC_METLAR) // metlar jumps instead of firing
{
npc->ps.legsAnim=((npc->ps.legsAnim & ANIM_TOGGLEBIT)^ANIM_TOGGLEBIT) | ANPC_ATTACK_FAR;
npc->ps.legsTimer=ent->npc->animTimes[ANPC_ATTACK_FAR];
npc->dontMoveTime=level.time+ent->npc->animTimes[ANPC_ATTACK_FAR]*0.8;
npc->toFire=2;
ucmd->upmove=ent->npc->jumpHeight*0.7;
ucmd->forwardmove=127;
}
else if (ent->npc->npcType==NPC_AWOLF) // awolf jumps instead of firing
{
npc->ps.legsAnim=((npc->ps.legsAnim & ANIM_TOGGLEBIT)^ANIM_TOGGLEBIT) | ANPC_ATTACK_FAR;
npc->ps.legsTimer=ent->npc->animTimes[ANPC_ATTACK_FAR];
npc->dontMoveTime=level.time+ent->npc->animTimes[ANPC_ATTACK_FAR]*0.8;
npc->toFire=2;
ucmd->upmove=ent->npc->jumpHeight*0.9;
ucmd->forwardmove=100;
}
// npc addon
else if (ent->npc->npcType!=NPC_MAN)
In g_npcthink.c in function NPC_ThinkMove about line 759 add :
else if (
(
(
r<npc->shot_factor
&& ent->npc->npcType!=NPC_HULK
&& ent->npc->npcType!=NPC_METLAR
&& ent->npc->npcType!=NPC_AWOLF
)
||
enemy_dist<=ent->npc->melee_dist ||
(enemy_dist<=1.5*ent->npc->melee_dist && ent->npc->npcType==NPC_HULK && !in_earthquake) ||
(r<0.1 && ent->npc->npcType==NPC_HULK && !in_earthquake) ||
(r<0.75 && enemy_dist<=4*ent->npc->melee_dist && ent->npc->npcType==NPC_METLAR) ||
(r<0.75 && enemy_dist<=4*ent->npc->melee_dist && ent->npc->npcType==NPC_AWOLF) ||
(npc->goingBack && enemy_dist<ideal_dist+100)
And this completes our werewolf NPC. On the Downloads page is a package containing the model, support files and sounds used for this NPC. It is a slightly modified Quake 2 player model converted to md3 format and is provided only as an example.