天行健 君子当自强而不息

Controlling Players and Characters(33)

The next two functions to come along work in conjunction with the update functionality
of the class. At every frame that a character needs updating, a specific
function is called to update the character’s actions. This specific function is dependent
on the type of character being updated; for PCs, that specific function is
pc_update (which is overridden by you in order to control your player).

virtual void pc_update(sCharacter* character, long elaspsed,
                       float* x_move, float* y_move, float* z_move)
{
}

For NPCs and monsters, the update function called is npc_monster_update. pc_update does
nothing at this point because you need to write the code in the main application
to control the player. Monsters and NPCs already have their AIs sets, and the controller
knows how to handle them with npc_monster_update.

///////////////////////////////////////////////////////////////////////////////////////////////////

static bool char_can_spell_health(const sCharacter* character, const sSpell* spell, int spell_index)
{
    
return spell->name[0] && spell->effect == ALTER_HEALTH && spell->value[0] > 0.0f &&
           character->mana_points >= spell->cost &&
           (character->char_def.magic_spell[spell_index / 32] & (1 << (spell_index & 31)));
}

static bool char_can_spell_dispel(const sCharacter* character, const sSpell* spell, int spell_index)
{
    
return spell->name[0] && spell->effect == DISPEL_MAGIC && character->mana_points >= spell->cost &&
           (character->char_def.magic_spell[spell_index / 32] & (1 << (spell_index & 31)));
}

static bool char_can_spell_ailment(const sCharacter* character, const sSpell* spell, int spell_index)
{
    
return spell->name[0] && spell->effect == CAUSE_AILMENT && character->mana_points >= spell->cost &&
           (character->char_def.magic_spell[spell_index / 32] & (1 << (spell_index & 31)));
}

static bool char_can_spell(const sCharacter* character, const sSpell* spell, int spell_index)
{
    
return spell->name[0] && character->mana_points >= spell->cost &&
           (character->char_def.magic_spell[spell_index / 32] & (1 << (spell_index & 31)));
}

static bool is_char_ailment(const sCharacter* character)
{
    
return character->ailments & AILMENT_POISON ||
           character->ailments & AILMENT_SLEEP ||             
           character->ailments & AILMENT_PARALYZE ||          
           character->ailments & AILMENT_ENCHANTED ||         
           character->ailments & AILMENT_DUMBFOUNDED ||       
           character->ailments & AILMENT_SLOW ||              
           character->ailments & AILMENT_BLIND ||             
           character->ailments & AILMENT_SILENCED;
}

static bool is_enhanced_ailment(long ailment)
{
    
return ailment & AILMENT_STRONG ||      
           ailment & AILMENT_BARRIER ||     
           ailment & AILMENT_SUREFOOTED ||  
           ailment & AILMENT_FAST ||        
           ailment & AILMENT_HAWKEYE;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
 
void cCharController::npc_monster_update(sCharacter* character, long elapsed,
                                         
float* x_move, float* y_move, float* z_move)
{
    
if(character == NULL)
        
return;

    
float move_x, move_y, move_z;
    move_x = move_y = move_z = 0.0f;

    
float x_diff, y_diff, z_diff, dist, radius;

    
float speed = elapsed/1000.0f * get_speed(character);

    
// move character based on their type
    switch(character->ai)
    {
    
case CHAR_STAND:
        
break;

    
case CHAR_WANDER:
        
// calculate new distance and direction if needed

        character->distance -= elapsed;

        
if(character->distance <= 0.0f)
        {
            character->distance  = rand()%2000 + 2000.0f;
            character->direction = (rand()%360) * 0.01744444f;
        }

        
// process walk or stand still
        if(character->distance > 1000.0f)
        {
            move_x = sin(character->direction) * speed;
            move_z = cos(character->direction) * speed;

            character->action = CHAR_MOVE;
        }
        
else
        {
            
// stand still for one second
            character->action = CHAR_IDLE;
        }

        
break;

    
case CHAR_ROUTE:
      {
        sRoutePoint* route = &character->route[character->cur_point];

        
// determine if character has reached point

        x_diff = fabs(character->pos_x - route->pos_x);
        y_diff = fabs(character->pos_y - route->pos_y);
        z_diff = fabs(character->pos_z - route->pos_z);

        dist = x_diff * x_diff + y_diff * y_diff + z_diff * z_diff;
        radius = get_xz_radius(character) * 0.25f;

        
// goto next point if reached
        if(dist < (radius * radius))
        {
            
if(++character->cur_point >= character->num_points)
                character->cur_point = 0;

            route = &character->route[character->cur_point];

            
// calculate new differences and distance 

            x_diff = fabs(character->pos_x - route->pos_x);
            y_diff = fabs(character->pos_y - route->pos_y);
            z_diff = fabs(character->pos_z - route->pos_z);

            dist = x_diff * x_diff + y_diff * y_diff + z_diff * z_diff;
        }

        
// setup movement towards target
        dist = sqrt(dist);

        
if(speed > dist)
            speed = dist;

        move_x = (route->pos_x - character->pos_x) / dist * speed;
        move_z = (route->pos_z - character->pos_z) / dist * speed;

        character->direction = atan2(move_x, move_z);
        character->action    = CHAR_MOVE;

        
break;
      }

    
case CHAR_FOLLOW:    
        
// check distance between characters

        x_diff = fabs(character->pos_x - character->target_char->pos_x);
        y_diff = fabs(character->pos_y - character->target_char->pos_y);
        z_diff = fabs(character->pos_z - character->target_char->pos_z);

        dist = x_diff * x_diff + y_diff * y_diff + z_diff * z_diff;

        
// update if further than target
        if(dist > (character->distance * character->distance))
        {
            
// setup movement towards target
            dist = sqrt(dist);

            
if(speed > dist)
                speed = dist;

            move_x = (character->target_char->pos_x - character->pos_x) / dist * speed;
            move_z = (character->target_char->pos_z - character->pos_z) / dist * speed;
            
            character->direction = atan2(move_x, move_z);
            character->action    = CHAR_MOVE;
        }

        
break;

    
case CHAR_EVADE:
        
// check distance between characters

        x_diff = fabs(character->pos_x - character->target_char->pos_x);
        y_diff = fabs(character->pos_y - character->target_char->pos_y);
        z_diff = fabs(character->pos_z - character->target_char->pos_z);

        dist = x_diff * x_diff + y_diff * y_diff + z_diff * z_diff;

        
// update if closer than distance
        if(dist < (character->distance * character->distance))
        {
            
// setup movement away from target
            dist = sqrt(dist);

            
if(speed > dist)
                speed = dist;

            move_x = -(character->target_char->pos_x - character->pos_x) / dist * speed;
            move_z = -(character->target_char->pos_z - character->pos_z) / dist * speed;

            character->direction = atan2(move_x, move_z);
            character->action = CHAR_MOVE;
        }

        
break;
    }

    sCharacter* char_ptr;

    
// process monster actions if at full charge
    if(character->type == CHAR_MONSTER && character->charge >= 100.0f)
    {
        
// determine chance of attacking
        if(rand()%100 <= character->char_def.to_attack)
        {
            
// scan through list and pick a character
            for(char_ptr = m_root_char; char_ptr != NULL; char_ptr = char_ptr->next)
            {
                
// randomly pick update enabled PC, and make sure the target is not hurt or dead.
                if(char_ptr != character && char_ptr->type == CHAR_PC && char_ptr->update_enable &&
                   char_ptr->action != CHAR_DIE && char_ptr->action != CHAR_HURT && rand()%100 < 50)
                {
                    
// get distance to target
                    x_diff = fabs(character->pos_x - char_ptr->pos_x);
                    y_diff = fabs(character->pos_y - char_ptr->pos_y);
                    z_diff = fabs(character->pos_z - char_ptr->pos_z);

                    dist = x_diff * x_diff + y_diff * y_diff + z_diff * z_diff;

                    
// make sure in range to attack                    
                    radius = get_xz_radius(character) + character->char_def.attack_range;

                    
// attack only in range
                    if((radius * radius) >= dist)
                    {                        
                        character->victim  = char_ptr;
                        char_ptr->attacker = character;

                        
// clear movement
                        move_x = move_y = move_z = 0.0f;

                        
// point towards target character
                        x_diff = char_ptr->pos_x - character->pos_x;
                        z_diff = char_ptr->pos_z - character->pos_z;
                        character->direction = atan2(x_diff, z_diff);

                        set_char_action(character, CHAR_ATTACK, 0);
                        
break;
                    }
                }
            }
        }   
        
else if(rand()%100 <= character->char_def.to_magic)
        {
            
// determine chance of spell casting

            
bool is_spell_cast = false;
            
long spell_index;

            
// if health is less than half, then there is 50% chance of healing (if monster knows any heal spells).
            if(character->health_points <= character->char_def.health_points / 2)
            {
                
// search for a known heal spell
                for(long i = 0; i < NUM_SPELL_DEF; i++)
                {
                    
// this is the spell, determine chance to heal.
                    if(char_can_spell_health(character, &m_msl[i], i) && rand()%100 < 50)
                    {                        
                        character->victim      = character;
                        character->attacker    = character;
                        character->spell_index = i;
                        character->target_type = CHAR_MONSTER;                     
                        character->target_x    = character->pos_x;
                        character->target_y    = character->pos_y;
                        character->target_z    = character->pos_z;

                        
// clear movement
                        move_x = move_y = move_z = 0.0f;
                       
                        set_char_action(character, CHAR_SPELL, 0);
                        is_spell_cast = 
true;
                        
break;
                    }
                }
            }

            
// if there are bad status ailments, then there is 50% chance of dispeling magic.
            if(is_char_ailment(character) && !is_spell_cast)
            {
                
// search for a known dispell spell
                for(long i = 0; i < NUM_SPELL_DEF; i++)
                {
                    
// this is the spell, determine chance to dispel.
                    if(char_can_spell_dispel(character, &m_msl[i], i) && rand()%100 < 50)
                    {                        
                        character->victim      = character;
                        character->attacker    = character;
                        character->spell_index = i;
                        character->target_type = CHAR_MONSTER;                     
                        character->target_x    = character->pos_x;
                        character->target_y    = character->pos_y;
                        character->target_z    = character->pos_z;

                        
// clear movement
                        move_x = move_y = move_z = 0.0f;
                       
                        set_char_action(character, CHAR_SPELL, 0);
                        is_spell_cast = 
true;
                        
break;
                    }
                }
            }

            
// if no spells already cast, then pick a random spell to attack with.
            if(!is_spell_cast)
            {
                spell_index = rand() % NUM_SPELL_DEF;

                
// scan through list until a spell is found that monster can cast.
                for(long i = 0; i < NUM_SPELL_DEF; i++)
                {
                    
if(char_can_spell(character, &m_msl[spell_index], spell_index) && rand()%100 < 50)
                    {
                        
// scan through list and pick a character
                        for(char_ptr = m_root_char; char_ptr != NULL; char_ptr = char_ptr->next)
                        {
                            
// randomly pick an update enabled PC, and make sure the target is not hurt or dead.
                            // also, do not cast self-targeting spells here.
                            if(char_ptr != character && m_msl[spell_index].target != TARGET_SELF &&
                                char_ptr->type == CHAR_PC && char_ptr->update_enable &&
                                char_ptr->action != CHAR_DIE && char_ptr->action != CHAR_HURT &&
                                rand()%100 < 50)
                            {
                                
float y1, y2;

                                
// get heights of attacker and target for line of sight checking
                                character->object.get_bounds(NULL, NULL, NULL, NULL, &y1, NULL, NULL);
                                y1 = (y1 * 0.5f) + character->pos_y;

                                char_ptr->
object.get_bounds(NULL, NULL, NULL, NULL, &y2, NULL, NULL);
                                y2 = (y2 * 0.5f) + char_ptr->pos_y;

                                
// get distance to target

                                x_diff = fabs(character->pos_x - char_ptr->pos_x);
                                y_diff = fabs(character->pos_y - char_ptr->pos_y);
                                z_diff = fabs(character->pos_z - char_ptr->pos_z);
                
                                dist = x_diff * x_diff + y_diff * y_diff + z_diff * z_diff;
                                dist = sqrt(dist) - get_xz_radius(char_ptr);
                               
                                
bool in_line_sight = line_of_sight(character, char_ptr,
                                                                   character->pos_x, y1, character->pos_z,
                                                                   char_ptr->pos_x, y2, char_ptr->pos_z);

                                
// make sure target is in range and in line sight
                                if(in_line_sight && dist <= m_msl[spell_index].max_dist)
                                {
                                    character->victim      = char_ptr;
                                    char_ptr->attacker     = character;
                                    character->spell_index = spell_index;
                                    character->target_type = CHAR_PC;
                                    character->target_x    = char_ptr->pos_x;
                                    character->target_y    = char_ptr->pos_y;
                                    character->target_z    = char_ptr->pos_z;

                                    
// face toward target (only if not self)
                                    x_diff = char_ptr->pos_x - character->pos_x;
                                    z_diff = char_ptr->pos_z - character->pos_z;

                                    character->direction = atan2(x_diff, z_diff);

                                    move_x = move_y = move_z = 0.0f;    
// clear movement

                                    set_char_action(character, CHAR_SPELL, 0);
                                    is_spell_cast = 
true;
                                    
break;
                                }
                            }
                        }

                        
break;
                    }

                    
// go to next spelll
                    spell_index = (spell_index + 1) % NUM_SPELL_DEF;
                }
            }

            
// if still no spell cast, try casting a known self-enhancing ailment-effecting spell.
            if(!is_spell_cast)
            {
                
for(long i = 0; i < NUM_SPELL_DEF; i++)
                {
                    
// make sure it is self-enhancing
                    if(char_can_spell_ailment(character, &m_msl[i], i) && is_enhanced_ailment(m_msl[i].value[0]) &&
                       rand()%100 < 10)
                    {
                        
// make sure ailment not already set
                        if(!(character->ailments & (long)m_msl[i].value[0]))
                        {
                            character->victim      = character;
                            character->attacker    = character;
                            character->spell_index = i;
                            character->target_type = CHAR_MONSTER;
                            character->target_x    = character->pos_x;
                            character->target_y    = character->pos_y;
                            character->target_z    = character->pos_z;

                            move_x = move_y = move_z = 0.0f;    
// clear movement

                            set_char_action(character, CHAR_SPELL, 0);
                            
break;
                        }
                    }
                }
            }
        }
    }

    
// store movement
    *x_move = move_x;
    *y_move = move_y;
    *z_move = move_z;
}

The steps taken for initiating an update and processing all of a character’s actions
is a long one. First, you iterate each character in the list of active characters. The
appropriate character’s update function is called (pc_update or npc_monster_update).

posted on 2007-12-04 16:29 lovedday 阅读(298) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论