The cCharController Class
The brains of the character operation is the cCharController class,
which
is probably the biggest non-game core class you’ll work with.
The cCharController class
maintains a list of active characters, each character
being stored within a sCharacter structure. For each type of character, there is
a
matching entry into an array of sMeshAnim structures (and a matching
sCharAnimInfo structure).
A macro is defined at the
beginning of the Char.h file.
// Number of
characters in file
#define NUM_CHARACTER_DEF 256
Following this definition are
the macros you’ve already seen—character types, artificial
intelligence types, and status ailments. The macros after that trio are ones you
haven’t seen, but you should understand them by now; they are the actions that a
character can perform (and the matching animations). Take a look at the macros:
// Character
types
#define CHAR_PC 0
#define CHAR_NPC 1
#define CHAR_MONSTER 2
// AI types
#define CHAR_STAND 0
#define CHAR_WANDER 1
#define CHAR_ROUTE 2
#define CHAR_FOLLOW 3
#define CHAR_EVADE 4
// Action/Animation types
#define CHAR_IDLE 0
#define CHAR_MOVE 1
#define CHAR_ATTACK 2
#define CHAR_SPELL 3
#define CHAR_ITEM 4
#define CHAR_HURT 5
#define CHAR_DIE 6
#define CHAR_TALK 7
// Status ailments
#define AILMENT_POISON 0x0001
#define AILMENT_SLEEP 0x0002
#define AILMENT_PARALYZE 0x0004
#define AILMENT_WEAK 0x0008
#define AILMENT_STRONG 0x0010
#define AILMENT_ENCHANTED 0x0020
#define AILMENT_BARRIER 0x0040
#define AILMENT_DUMBFOUNDED 0x0080
#define AILMENT_CLUMSY 0x0100
#define AILMENT_SUREFOOTED 0x0200
#define AILMENT_SLOW 0x0400
#define AILMENT_FAST 0x0800
#define AILMENT_BLIND 0x1000
#define AILMENT_HAWKEYE 0x2000
#define AILMENT_SILENCED 0x4000
/************************************************************************************************/
typedef struct sRoutePoint
{
float pos_x, pos_y, pos_z;
} *sRoutePointPtr;
typedef struct sCharAnimInfo
{
char name[32]; // name of animation
bool is_loop;
} *sCharAnimInfoPtr;
From here, it’s all left up to
the controller class.
typedef class cCharController
{
private:
cSpellController* m_spell_controller;
cFrustum* m_frustum;
char m_def_file[MAX_PATH];
sItem* m_mil; // master item list
sSpell* m_msl; // master spell list
long m_num_char;
sCharacter* m_root_char;
long m_num_mesh_anim;
sMeshAnim* m_mesh_anim;
char m_weapon_mesh_path[MAX_PATH];
char m_texture_path[MAX_PATH];
long m_num_char_anim;
sCharAnimInfo* m_char_anim;
ID3DXFont* m_font;
/////////////////////////////////////////////////////////////////////////////////////////////
You use the frustum object as you
use the one in the spell controller.
Next comes the filename of the MCL, the pointers to the MIL and MSL, and finally
a pointer to the spell controller.
As characters are added to the
game, a counter (m_num_char) keeps track of how
many are in use. Following the counter is the pointer to the parent (root)
sCharacter
structure in the linked list of structures:
You use a list of mesh and
animation structures much like you use the spell controller.
This time, in addition to storing the texture path, you also create a directory
path where the meshes are located. Why use a mesh directory? In the case of
attaching weapons to a character, the sItem structure stores only the filename,
not
the path. This means that weapon meshes must be located in the same directory
as the character meshes.
That wraps up the internal data
of the sCharController class. Now, you can turn
your attention to the private functions. You use the first function,
get_xz_radius, to calculate
the maximum bounding radius along the X- and Z-axes.
float get_xz_radius(sCharacter* character)
{
if(character == NULL)
return 0.0f;
float min_x, max_x, min_z, max_z;
character->object.get_bounds(&min_x, NULL, &min_z, &max_x, NULL, &max_z, NULL);
float x = max(fabs(min_x), fabs(max_x));
float z = max(fabs(min_z), fabs(max_z));
return max(x, z);
}
You use the X/Z radius to enhance
the reliability of bounding sphere collision detection.
To see what I mean, take a look at Figure 16.16.
Taller characters in the game
have the unfortunate side effect of having large
bounding spheres. To remedy this, only the farthest point of the character in
the
X- and Z-axes is used to compute the bounding sphere size, because those two
axes
represent the character’s width and depth, not height.
Getting on with the functions,
you insert a virtual function that is used to play a sound
effect whenever an action is initiated. It’s your job to derive the
cCharController
class in order to override the function to make it do something useful:
virtual
void play_action_sound(sCharacter* character)
{
}