天行健 君子当自强而不息

Controlling Players and Characters(39)

Once characters take enough damage, they die, and when that happens, the following
function is called:

void cCharController::death(sCharacter* attacker, sCharacter* victim)
{
    
// if a PC or NPC dies, then do not remove from list.
    if(victim->type == CHAR_PC || victim->type == CHAR_NPC)
    {        
        victim->update_enable = 
false;

        
if(victim->type == CHAR_PC)
            pc_death(victim);
        
else
            npc_death(victim);
    }
    
else    // victim->type = CHAR_MONSTER
    {
        
// give attacker the victim's experience
        if(attacker && gain_exp(attacker, victim->char_def.exp))
        {
            
char text[128];
            sprintf(text, "+%lu exp.", victim->char_def.exp);
            set_char_msg(attacker, text, 500, COLOR_WHITE);
        }
    }

    
if(m_mil && victim->char_def.money)
        drop_money(victim->pos_x, victim->pos_y, victim->pos_z, victim->char_def.money);

    
if(rand()%100 < victim->char_def.drop_chance)
        drop_item(victim->pos_x, victim->pos_y, victim->pos_z, victim->char_def.drop_item);

    sMeshAnim& mesh_anim = m_mesh_anim[victim->char_def.mesh_index];

    
if(--mesh_anim.count == 0)
    {
        mesh_anim.mesh.free();
        mesh_anim.anim.free();
    }

    
// remove the dead character from list

    
if(victim->prev)
        victim->prev->next = victim->next;
    
else
        m_root_char = victim->next;

    
if(victim->next)
        victim->next->prev = victim->prev;

    victim->prev = victim->next = NULL;
    delete victim;
}

virtual void pc_death(sCharacter* character)
{        

 }

virtual void npc_death(sCharacter* character)
{        
}


Taking the pointer to the victim, the controller is able to handle its death appropriately.
If the victim is a monster, you use the attacking character pointer to apply the
experience points. Also, if a monster dies, the death function determines how much
gold the monster drops and what item (if any) is dropped and calls the appropriate
controller function to handle such dropped items.

Leading into the next function, whenever a PC kills a monster, that PC gains the
experience stored in the monster’s MCL definition. To apply the experience, use
the following function:

    virtual bool gain_exp(sCharacter* character, long amount)
    {
        character->char_def.exp += amount;

        
return true;
    }

Notice that the gain_exp function can be overridden. This can occur when you’re
using a separate battle sequence engine; you don’t want experience added to the
PC until the battle is over. Consequently, you use your own function to keep track
of how much experience to apply when the battle is over.

The overridden function can also occur when the character needs to go up in
experience levels once he gains a certain number of experience points. The
gan_exp function is the place to determine just when a character goes up an
experience level and to take the appropriate actions to increase their abilities.

One note about the gain_exp function: The character controller normally displays
the number of experience points that a PC gains when killing a monster. To stop
the controller from displaying this number (as in the case of the separate battle
sequences), return a value of false from the Experience function.

The next couple of functions are the ones responsible for processing attacks and
spells. Both functions take pointers to the attacking characters (if any) as well as
their intended victims. For spells, a sSpellTracker structure is required to tell the
controller which spell to process, as well as the sSpell structure that contains the
information about the spell effects to use:

bool cCharController::attack(sCharacter* attacker, sCharacter* victim)
{
    
if(attacker == NULL || victim == NULL)
        
return false;

    
// do not attack dead or hurt character
    if(victim->action == CHAR_DIE || victim->action == CHAR_HURT)
        
return false;

    victim->attacker = attacker;
    attacker->victim = victim;
    
    
// return if hit missed
    if(rand()%1000 > get_to_hit(attacker))
    {
        set_char_msg(victim, "Missed!", 500, COLOR_WHITE);
        
return false;
    }

    
// return if hit dodged
    if(rand()%1000 <= get_agility(victim))
    {
        set_char_msg(victim, "Dodged!", 500, COLOR_WHITE);
        
return false;
    }

    
// if character is asleep, randomly wake them up (50% chance).
    if((victim->ailments & AILMENT_SLEEP) && rand()%100 < 50)    
        victim->ailments &= ~AILMENT_SLEEP;    

    
// attack landed, apply damage.
    damage(victim, true, get_attack(attacker), -1, -1);

    
return true;
}

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

bool cCharController::spell(sCharacter* caster, const sSpellTracker* spell_tracker, const sSpell* spells)
{
    
if(caster == NULL || spell_tracker == NULL || spells == NULL)
        
return false;

    
const sSpell* spell_ptr = &spells[spell_tracker->spell_index];

    
// reduce magic
    caster->mana_points -= spell_ptr->cost;
    
if(caster->mana_points < 0)
        caster->mana_points = 0;

    
// can not cast if silenced
    if(caster->ailments & AILMENT_SILENCED)
    {
        set_char_msg(caster, "Silenced!", 500, COLOR_WHITE);
        
return false;
    }

    
// handle self-targeting spells instantly
    if(spell_ptr->target == TARGET_SELF)
    {
        spell_effect(caster, caster, spell_ptr);
        
return true;
    }

    
float spell_dist = spell_ptr->range * spell_ptr->range;

    sCharacter* closest_char = NULL;
    
float closest = 0.0f;

    
// scan through all characters and look for hits
    for(sCharacter* char_ptr = m_root_char; char_ptr != NULL; char_ptr = char_ptr->next)
    {
        
// only bother with characters of allowed types.
        // also, allow a RAISE_DEAD PC spell to affect any character.

        
bool allow = false;

        
if(char_ptr != caster && spell_tracker->affect_type == char_ptr->type)
            allow = 
true;

        
if(char_ptr->type == CHAR_PC && spell_ptr->effect == RAISE_DEAD)
            allow = 
true;

        
if(!allow)
            
continue;

        
// get distance from target to character
        float x_diff = fabs(char_ptr->pos_x - spell_tracker->target_x);
        
float y_diff = fabs(char_ptr->pos_y - spell_tracker->target_y);
        
float z_diff = fabs(char_ptr->pos_z - spell_tracker->target_z);

        
// get x/z and y distances
        float xz_dist = (x_diff * x_diff + z_diff * z_diff) - spell_dist;
        
float y_dist  = (y_diff * y_diff) - spell_dist;

        
// get target x/z and y radius
        float min_x, min_y, min_z, max_x, max_y, max_z;
        char_ptr->
object.get_bounds(&min_x, &min_y, &min_z, &max_x, &max_y, &max_z, NULL);

        
float xz_radius = max(max_x - min_x, max_z - min_z) * 0.5f;
        
float y_radius  = (max_y - min_y) * 0.5f;

        
// check if character in range
        if(xz_dist > (xz_radius * xz_radius) || y_dist > (y_radius * y_radius))
            
continue;

        
// determine what to do if in range
        if(spell_ptr->target == TARGET_SINGLE)
        {
            
// record closest character in range
            float dist = x_diff * x_diff + y_diff * y_diff + z_diff * z_diff;

            
if(closest_char == NULL || dist < closest)
            {
                closest_char = char_ptr;
                closest = dist;
            }           
        }
        
else    // spell hit area targets
            spell_effect(caster, char_ptr, spell_ptr);
    }

    
// process spell on closest character if needed
    if(spell_ptr->target == TARGET_SINGLE && closest_char)
        spell_effect(caster, closest_char, spell_ptr);

    
return true;
}

Each of the preceding functions takes into account the attacking and defending
characters’ abilities and adjust their values accordingly. When an attack connects,
damage is dealt. When a spell is found to have affected the target (remember,
there’s a chance it might fail), the next function is called to process the effects:
bool cCharController::spell_effect(sCharacter* caster, sCharacter* target, const sSpell* spell)
{
    
if(target == NULL || spell == NULL)
        
return false;

    
long chance;

    
// calculate chance of hitting
    if(caster)
    {
        
// a spell always lands if target == caster
        if(caster == target)
            chance = 100;
        
else
            chance = (get_mental(caster)/100.0f + 1.0f) * spell->chance;        
    }
    
else
        chance = spell->chance;

    
// alter chance by target's registance
    if(caster != target)
        chance *= (1.0f - get_resistance(target)/100.0f);

    
// see if spell failed
    if(rand()%100 > chance)
    {
        set_char_msg(target, "Failed!", 500, COLOR_WHITE);
        
return false;
    }
    
    
bool can_hit = true;    // flag character to allow effect

    
if(target->action == CHAR_HURT || target->action == CHAR_DIE)
        can_hit = 
false;

    
// store attacker and victim

    target->attacker = caster;
  
    
if(caster)
        caster->victim = target;

    
char text[64];

    
// process spell effect
    switch(spell->effect)
    {
    
case ALTER_HEALTH:
        
if(can_hit)
        {
            
if(spell->value[0] < 0.0f)      // apply damage
                damage(target, false, -spell->value[0], spell->damage_class, spell->cure_class);
            
else if(spell->value[0] > 0.0f) // cure damage
            {
                target->health_points += spell->value[0];

                
if(target->health_points > target->char_def.health_points)
                    target->health_points = target->char_def.health_points;

                
// display amount healed
                sprintf(text, "+%lu HP", spell->value[0]);
                set_char_msg(target, text, 500, D3DCOLOR_RGBA(0, 64, 255, 255));
            }            
        }

        
break;

    
case ALTER_MANA:
        
if(can_hit)
        {
            target->mana_points += spell->value[0];

            
if(target->mana_points < 0)
                target->mana_points = 0;
            
else if(target->mana_points > target->char_def.mana_points)
                target->mana_points = target->char_def.mana_points;

            
if(spell->value[0] < 0.0f)
                sprintf(text, "%ld MP", spell->value[0]);
            
else if(spell->value[0] > 0.0f)
                sprintf(text, "+%ld MP", spell->value[0]);

            set_char_msg(target, text, 500, D3DCOLOR_RGBA(0, 128, 64, 255));            
        }

        
break;

    
case CURE_AILMENT:
        
if(can_hit)
        {
            
// cure ailment and display message
            target->ailments &= ~(long)spell->value[0];
            set_char_msg(target, "Cure", 500, COLOR_WHITE);            
        }

        
break;

    
case CAUSE_AILMENT:
        
if(can_hit)
        {
            
// cause ailment and display message
            target->ailments |= (long)spell->value[0];
            set_char_msg(target, "Ailment", 500, COLOR_WHITE);            
        }

        
break;

    
case RAISE_DEAD:
        
if(target->action == CHAR_DIE)
        {
            target->health_points = 1;
            target->mana_points   = 0;
            target->action        = CHAR_DIE;
            target->is_lock       = 
false;
            target->action_timer  = 0;
            target->ailments      = 0;
            target->update_enable = 
true;
        }
            
        
break;

    
case INSTANT_KILL:
        
if(can_hit)
            set_char_action(target, CHAR_DIE, 0);

        
break;

    
case DISPEL_MAGIC:
        
if(can_hit)
            target->ailments = 0;

        
break;

    
case TELEPORT:      // teleport PC/NPC/MONSTER
        if(can_hit)
        {
            
if(target->type == CHAR_PC)
                pc_teleport(caster, spell);
            
else
            {
                target->pos_x = spell->value[0];
                target->pos_y = spell->value[1];
                target->pos_z = spell->value[2];
            }
        }

        
break;
    }

    
return true;
}
 

posted on 2007-12-04 19:45 lovedday 阅读(255) 评论(0)  编辑 收藏 引用


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


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论