Exemplo n.º 1
0
point monster::scent_move(game *g)
{
 plans.clear();
 std::vector<point> smoves;

 int maxsmell = 2; // Squares with smell 0 are not eligable targets
 if (has_flag(MF_KEENNOSE))
 {
     maxsmell = 1;
 }
 int minsmell = 9999;
 point pbuff, next(-1, -1);
 unsigned int smell;
 for (int x = -1; x <= 1; x++) {
  for (int y = -1; y <= 1; y++) {
   smell = g->scent(posx + x, posy + y);
   int mon = g->mon_at(posx + x, posy + y);
   if ((mon == -1 || g->z[mon].friendly != 0 || has_flag(MF_ATTACKMON)) &&
       (can_move_to(g, posx + x, posy + y) ||
        (posx + x == g->u.posx && posx + y == g->u.posy) ||
        (g->m.has_flag(bashable, posx + x, posy + y) && has_flag(MF_BASHES)))) {
    if ((!is_fleeing(g->u) && smell > maxsmell) ||
        ( is_fleeing(g->u) && smell < minsmell)   ) {
     smoves.clear();
     pbuff.x = posx + x;
     pbuff.y = posy + y;
     smoves.push_back(pbuff);
     maxsmell = smell;
     minsmell = smell;
    } else if ((!is_fleeing(g->u) && smell == maxsmell) ||
               ( is_fleeing(g->u) && smell == minsmell)   ) {
     pbuff.x = posx + x;
     pbuff.y = posy + y;
     smoves.push_back(pbuff);
    }
   }
  }
 }
 if (smoves.size() > 0) {
  int nextsq = rng(0, smoves.size() - 1);
  next = smoves[nextsq];
 }
 return next;
}
Exemplo n.º 2
0
void monster::print_info(game *g, WINDOW* w)
{
// First line of w is the border; the next two are terrain info, and after that
// is a blank line. w is 13 characters tall, and we can't use the last one
// because it's a border as well; so we have lines 4 through 11.
// w is also 48 characters wide - 2 characters for border = 46 characters for us
    mvwprintz(w, 6, 1, type->color, type->name.c_str());
    if (friendly != 0) {
        wprintz(w, c_white, " ");
        wprintz(w, h_white, "Friendly!");
    }
    std::string damage_info;
    nc_color col;
    if (hp == type->hp) {
        damage_info = "It is uninjured";
        col = c_green;
    } else if (hp >= type->hp * .8) {
        damage_info = "It is lightly injured";
        col = c_ltgreen;
    } else if (hp >= type->hp * .6) {
        damage_info = "It is moderately injured";
        col = c_yellow;
    } else if (hp >= type->hp * .3) {
        damage_info = "It is heavily injured";
        col = c_yellow;
    } else if (hp >= type->hp * .1) {
        damage_info = "It is severly injured";
        col = c_ltred;
    } else {
        damage_info = "it is nearly dead";
        col = c_red;
    }
    mvwprintz(w, 7, 1, col, damage_info.c_str());
    if (is_fleeing(g->u))
        wprintz(w, c_white, ", and it is fleeing.");
    else
        wprintz(w, col, ".");


    std::string tmp = type->description;
    std::string out;
    size_t pos;
    int line = 8;
    do {
        pos = tmp.find_first_of('\n');
        out = tmp.substr(0, pos);
        mvwprintz(w, line, 1, c_white, out.c_str());
        tmp = tmp.substr(pos + 1);
        line++;
    } while (pos != std::string::npos && line < 12);
}
Exemplo n.º 3
0
point monster::scent_move()
{
    std::vector<point> smoves;

    int maxsmell = 10; // Squares with smell 0 are not eligible targets.
    int smell_threshold = 200; // Squares at or above this level are ineligible.
    if (has_flag(MF_KEENNOSE)) {
        maxsmell = 1;
        smell_threshold = 400;
    }
    int minsmell = 9999;
    point pbuff, next(-1, -1);
    unsigned int smell;
    const bool fleeing = is_fleeing(g->u);
    if( !fleeing && g->scent( posx(), posy() ) > smell_threshold ) {
        return next;
    }
    for (int x = -1; x <= 1; x++) {
        for (int y = -1; y <= 1; y++) {
            const int nx = posx() + x;
            const int ny = posy() + y;
            smell = g->scent(nx, ny);
            int mon = g->mon_at(nx, ny);
            if ((mon == -1 || g->zombie(mon).friendly != 0 || has_flag(MF_ATTACKMON)) &&
                    (can_move_to(nx, ny) ||
                     (nx == g->u.posx && ny == g->u.posy) ||
                     (g->m.has_flag("BASHABLE", nx, ny) && has_flag(MF_BASHES)))) {
                if ((!fleeing && smell > maxsmell ) ||
                        ( fleeing && smell < minsmell )   ) {
                    smoves.clear();
                    pbuff.x = nx;
                    pbuff.y = ny;
                    smoves.push_back(pbuff);
                    maxsmell = smell;
                    minsmell = smell;
                } else if ((!fleeing && smell == maxsmell ) ||
                           ( fleeing && smell == minsmell )   ) {
                    pbuff.x = nx;
                    pbuff.y = ny;
                    smoves.push_back(pbuff);
                }
            }
        }
    }
    if (smoves.size() > 0) {
        int nextsq = rng(0, smoves.size() - 1);
        next = smoves[nextsq];
    }
    return next;
}
Exemplo n.º 4
0
tripoint monster::scent_move()
{
    // @todo Remove when scentmap is 3D
    if( abs( posz() - g->get_levz() ) > 1 ) {
        return { -1, -1, INT_MIN };
    }

    std::vector<tripoint> smoves;

    int bestsmell = 10; // Squares with smell 0 are not eligible targets.
    int smell_threshold = 200; // Squares at or above this level are ineligible.
    if( has_flag( MF_KEENNOSE ) ) {
        bestsmell = 1;
        smell_threshold = 400;
    }

    const bool fleeing = is_fleeing( g->u );
    if( fleeing ) {
        bestsmell = g->scent.get( pos() );
    }

    tripoint next( -1, -1, posz() );
    if( ( !fleeing && g->scent.get( pos() ) > smell_threshold ) ||
        ( fleeing && bestsmell == 0 ) ) {
        return next;
    }
    const bool can_bash = bash_skill() > 0;
    for( const auto &dest : g->m.points_in_radius( pos(), 1, 1 ) ) {
        int smell = g->scent.get( dest );
        if( g->m.valid_move( pos(), dest, can_bash, true ) &&
            ( can_move_to( dest ) || ( dest == g->u.pos() ) ||
              ( can_bash && g->m.bash_rating( bash_estimate(), dest ) > 0 ) ) ) {
            if( ( !fleeing && smell > bestsmell ) || ( fleeing && smell < bestsmell ) ) {
                smoves.clear();
                smoves.push_back( dest );
                bestsmell = smell;
            } else if( ( !fleeing && smell == bestsmell ) || ( fleeing && smell == bestsmell ) ) {
                smoves.push_back( dest );
            }
        }
    }

    return random_entry( smoves, next );
}
Exemplo n.º 5
0
tripoint monster::scent_move()
{
    std::vector<tripoint> smoves;

    int bestsmell = 10; // Squares with smell 0 are not eligible targets.
    int smell_threshold = 200; // Squares at or above this level are ineligible.
    if( has_flag( MF_KEENNOSE ) ) {
        bestsmell = 1;
        smell_threshold = 400;
    }

    const bool fleeing = is_fleeing( g->u );
    if( fleeing ) {
        bestsmell = g->scent( pos() );
    }

    tripoint next( -1, -1, posz() );
    if( ( !fleeing && g->scent( pos() ) > smell_threshold ) ||
        ( fleeing && bestsmell == 0 ) ) {
        return next;
    }
    const bool can_bash = has_flag( MF_BASHES ) || has_flag( MF_BORES );
    for( const auto &dest : g->m.points_in_radius( pos(), 1 ) ) {
        int smell = g->scent( dest );
        int mon = g->mon_at( dest );
        if( ( mon == -1 || g->zombie( mon ).friendly != 0 || has_flag( MF_ATTACKMON ) ) &&
            ( can_move_to( dest ) || ( dest == g->u.pos3() ) ||
              ( can_bash && g->m.bash_rating( bash_estimate(), dest ) >= 0 ) ) ) {
            if( ( !fleeing && smell > bestsmell ) || ( fleeing && smell < bestsmell ) ) {
                smoves.clear();
                smoves.push_back( dest );
                bestsmell = smell;
            } else if( ( !fleeing && smell == bestsmell ) || ( fleeing && smell == bestsmell ) ) {
                smoves.push_back( dest );
            }
        }
    }

    return random_entry( smoves, next );
}
Exemplo n.º 6
0
void monster::plan(const std::vector<int> &friendlies)
{
    int sightrange = g->light_level();
    int closest = -1;
    int dist = 1000;
    int tc = 0;
    int stc = 0;
    bool fleeing = false;
    if (friendly != 0) { // Target monsters, not the player!
        for (int i = 0, numz = g->num_zombies(); i < numz; i++) {
            monster *tmp = &(g->zombie(i));
            if (tmp->friendly == 0) {
                int d = rl_dist(posx(), posy(), tmp->posx(), tmp->posy());
                if (d < dist && g->m.sees(posx(), posy(), tmp->posx(), tmp->posy(), sightrange, tc)) {
                    closest = i;
                    dist = d;
                    stc = tc;
                }
            }
        }

        if (has_effect("docile")) {
            closest = -1;
        }

        if (closest >= 0) {
            set_dest(g->zombie(closest).posx(), g->zombie(closest).posy(), stc);
        } else if (friendly > 0 && one_in(3)) {
            // Grow restless with no targets
            friendly--;
        } else if (friendly < 0 &&  sees_player( tc ) ) {
            if (rl_dist(posx(), posy(), g->u.posx, g->u.posy) > 2) {
                set_dest(g->u.posx, g->u.posy, tc);
            } else {
                plans.clear();
            }
        }
        return;
    }

    // If we can see, and we can see a character, move toward them or flee.
    if (can_see() && sees_player( tc ) ) {
        dist = rl_dist(posx(), posy(), g->u.posx, g->u.posy);
        if (is_fleeing(g->u)) {
            // Wander away.
            fleeing = true;
            set_dest(posx() * 2 - g->u.posx, posy() * 2 - g->u.posy, tc);
        } else {
            // Chase the player.
            closest = -2;
            stc = tc;
        }
    }

    for (int i = 0; i < g->active_npc.size(); i++) {
        npc *me = (g->active_npc[i]);
        int medist = rl_dist(posx(), posy(), me->posx, me->posy);
        if ((medist < dist || (!fleeing && is_fleeing(*me))) &&
                (can_see() &&
                 g->m.sees(posx(), posy(), me->posx, me->posy, sightrange, tc))) {
            if (is_fleeing(*me)) {
                fleeing = true;
                set_dest(posx() * 2 - me->posx, posy() * 2 - me->posy, tc);
                \
            } else {
                closest = i;
                stc = tc;
            }
            dist = medist;
        }
    }

    if (!fleeing) {
        fleeing = attitude() == MATT_FLEE;
        if (can_see()) {
            for (int f = 0, numf = friendlies.size(); f < numf; f++) {
                const int i = friendlies[f];
                monster *mon = &(g->zombie(i));
                int mondist = rl_dist(posx(), posy(), mon->posx(), mon->posy());
                if (mondist < dist &&
                        g->m.sees(posx(), posy(), mon->posx(), mon->posy(), sightrange, tc)) {
                    dist = mondist;
                    if (fleeing) {
                        wandx = posx() * 2 - mon->posx();
                        wandy = posy() * 2 - mon->posy();
                        wandf = 40;
                    } else {
                        closest = -3 - i;
                        stc = tc;
                    }
                }
            }
        }

        if (closest == -2) {
            if (one_in(2)) {//random for the diversity of the trajectory
                ++stc;
            } else {
                --stc;
            }
            set_dest(g->u.posx, g->u.posy, stc);
        }
        else if (closest <= -3)
            set_dest(g->zombie(-3 - closest).posx(), g->zombie(-3 - closest).posy(), stc);
        else if (closest >= 0)
            set_dest(g->active_npc[closest]->posx, g->active_npc[closest]->posy, stc);
    }
}
Exemplo n.º 7
0
void monster::plan(game *g)
{
 int sightrange = g->light_level();
 int closest = -1;
 int dist = 1000;
 int tc, stc;
 bool fleeing = false;
 if (friendly != 0) {	// Target monsters, not the player!
  for (int i = 0; i < g->z.size(); i++) {
   monster *tmp = &(g->z[i]);
   if (tmp->friendly == 0 && rl_dist(posx, posy, tmp->posx, tmp->posy) < dist &&
       g->m.sees(posx, posy, tmp->posx, tmp->posy, sightrange, tc)) {
    closest = i;
    dist = rl_dist(posx, posy, tmp->posx, tmp->posy);
    stc = tc;
   }
  }
  if (has_effect(ME_DOCILE))
   closest = -1;
  if (closest >= 0)
   set_dest(g->z[closest].posx, g->z[closest].posy, stc);
  else if (friendly > 0 && one_in(3))	// Grow restless with no targets
   friendly--;
  else if (friendly < 0 && g->sees_u(posx, posy, tc)) {
   if (rl_dist(posx, posy, g->u.posx, g->u.posy) > 2)
    set_dest(g->u.posx, g->u.posy, tc);
   else
    plans.clear();
  }
  return;
 }
 if (is_fleeing(g->u) && can_see() && g->sees_u(posx, posy, tc)) {
  fleeing = true;
  wandx = posx * 2 - g->u.posx;
  wandy = posy * 2 - g->u.posy;
  wandf = 40;
  dist = rl_dist(posx, posy, g->u.posx, g->u.posy);
 }
// If we can see, and we can see a character, start moving towards them
 if (!is_fleeing(g->u) && can_see() && g->sees_u(posx, posy, tc)) {
  dist = rl_dist(posx, posy, g->u.posx, g->u.posy);
  closest = -2;
  stc = tc;
 }
 for (int i = 0; i < g->active_npc.size(); i++) {
  npc *me = (g->active_npc[i]);
  int medist = rl_dist(posx, posy, me->posx, me->posy);
  if ((medist < dist || (!fleeing && is_fleeing(*me))) &&
      (can_see() &&
       g->m.sees(posx, posy, me->posx, me->posy, sightrange, tc))) {
   if (is_fleeing(*me)) {
    fleeing = true;
    wandx = posx * 2 - me->posx;
    wandy = posy * 2 - me->posy;
    wandf = 40;
    dist = medist;
   } else if (can_see() &&
              g->m.sees(posx, posy, me->posx, me->posy, sightrange, tc)) {
    dist = rl_dist(posx, posy, me->posx, me->posy);
    closest = i;
    stc = tc;
   }
  }
 }
 if (!fleeing) {
  fleeing = attitude() == MATT_FLEE;
  for (int i = 0; i < g->z.size(); i++) {
   monster *mon = &(g->z[i]);
   int mondist = rl_dist(posx, posy, mon->posx, mon->posy);
   if (mon->friendly != 0 && mondist < dist && can_see() &&
       g->m.sees(posx, posy, mon->posx, mon->posy, sightrange, tc)) {
    dist = mondist;
    if (fleeing) {
     wandx = posx * 2 - mon->posx;
     wandy = posy * 2 - mon->posy;
     wandf = 40;
    } else {
     closest = -3 - i;
     stc = tc;
    }
   }
  }
 }
 if (!fleeing) {
  if (closest == -2)
   set_dest(g->u.posx, g->u.posy, stc);
  else if (closest <= -3)
   set_dest(g->z[-3 - closest].posx, g->z[-3 - closest].posy, stc);
  else if (closest >= 0)
   set_dest(g->active_npc[closest]->posx, g->active_npc[closest]->posy, stc);
 }
}
Exemplo n.º 8
0
// General movement.
// Currently, priority goes:
// 1) Special Attack
// 2) Sight-based tracking
// 3) Scent-based tracking
// 4) Sound-based tracking
void monster::move(game *g)
{
// We decrement wandf no matter what.  We'll save our wander_to plans until
// after we finish out set_dest plans, UNLESS they time out first.
 if (wandf > 0)
  wandf--;

// First, use the special attack, if we can!
 if (sp_timeout > 0)
  sp_timeout--;
 if (sp_timeout == 0 && (friendly == 0 || has_flag(MF_FRIENDLY_SPECIAL))) {
  mattack ma;
  (ma.*type->sp_attack)(g, this);
 }
 if (moves < 0)
  return;
 if (has_flag(MF_IMMOBILE)) {
  moves = 0;
  return;
 }
 if (has_effect(ME_STUNNED)) {
  stumble(g, false);
  moves = 0;
  return;
 }
 if (has_effect(ME_DOWNED)) {
  moves = 0;
  return;
 }
 if (has_effect(ME_BOULDERING)){
  moves -= 20;
  if (moves < 0)
   moves = 0;
  return;
 }
 if (friendly != 0) {
  if (friendly > 0)
   friendly--;
  friendly_move(g);
  return;
 }

 bool moved = false;
 point next;
 int mondex = (plans.size() > 0 ? g->mon_at(plans[0].x, plans[0].y) : -1);

 monster_attitude current_attitude = attitude();
 if (friendly == 0)
  current_attitude = attitude(&(g->u));
// If our plans end in a player, set our attitude to consider that player
 if (plans.size() > 0) {
  if (plans.back().x == g->u.posx && plans.back().y == g->u.posy)
   current_attitude = attitude(&(g->u));
  else {
   for (int i = 0; i < g->active_npc.size(); i++) {
    if (plans.back().x == g->active_npc[i]->posx &&
        plans.back().y == g->active_npc[i]->posy)
     current_attitude = attitude((g->active_npc[i]));
   }
  }
 }

 if (current_attitude == MATT_IGNORE ||
     (current_attitude == MATT_FOLLOW && plans.size() <= MONSTER_FOLLOW_DIST)) {
  moves -= 100;
  stumble(g, false);
  return;
 }

 if (plans.size() > 0 && !is_fleeing(g->u) &&
     (mondex == -1 || g->z[mondex].friendly != 0 || has_flag(MF_ATTACKMON)) &&
     (can_move_to(g, plans[0].x, plans[0].y) ||
      (plans[0].x == g->u.posx && plans[0].y == g->u.posy) ||
     (g->m.has_flag(bashable, plans[0].x, plans[0].y) && has_flag(MF_BASHES)))){
  // CONCRETE PLANS - Most likely based on sight
  next = plans[0];
  moved = true;
 } else if (has_flag(MF_SMELLS)) {
// No sight... or our plans are invalid (e.g. moving through a transparent, but
//  solid, square of terrain).  Fall back to smell if we have it.
  point tmp = scent_move(g);
  if (tmp.x != -1) {
   next = tmp;
   moved = true;
  }
 }
 if (wandf > 0 && !moved) { // No LOS, no scent, so as a fall-back follow sound
  point tmp = sound_move(g);
  if (tmp.x != posx || tmp.y != posy) {
   next = tmp;
   moved = true;
  }
 }

// Finished logic section.  By this point, we should have chosen a square to
//  move to (moved = true).
 if (moved) {	// Actual effects of moving to the square we've chosen
  mondex = g->mon_at(next.x, next.y);
  int npcdex = g->npc_at(next.x, next.y);
  if (next.x == g->u.posx && next.y == g->u.posy && type->melee_dice > 0)
   hit_player(g, g->u);
  else if (mondex != -1 && g->z[mondex].type->species == species_hallu) {
   g->kill_mon(mondex);
   moves -= 100;
  } else if (mondex != -1 && type->melee_dice > 0 && this != &(g->z[mondex]) &&
             (g->z[mondex].friendly != 0 || has_flag(MF_ATTACKMON)))
   hit_monster(g, mondex);
  else if (npcdex != -1 && type->melee_dice > 0)
   hit_player(g, *g->active_npc[npcdex]);
  else if ((!can_move_to(g, next.x, next.y) || one_in(3)) &&
             g->m.has_flag(bashable, next.x, next.y) && has_flag(MF_BASHES)) {
   std::string bashsound = "NOBASH"; // If we hear "NOBASH" it's time to debug!
   int bashskill = int(type->melee_dice * type->melee_sides);
   g->m.bash(next.x, next.y, bashskill, bashsound);
   g->sound(next.x, next.y, 18, bashsound);
   moves -= 100;
  } else if (g->m.move_cost(next.x, next.y) == 0 && has_flag(MF_DESTROYS)) {
   g->m.destroy(g, next.x, next.y, true);
   moves -= 250;
  } else if (can_move_to(g, next.x, next.y) && g->is_empty(next.x, next.y))
   move_to(g, next.x, next.y);
  else
   moves -= 100;
 } else
  moves -= 100;

// If we're close to our target, we get focused and don't stumble
 if ((has_flag(MF_STUMBLES) && (plans.size() > 3 || plans.size() == 0)) ||
     !moved)
  stumble(g, moved);
}
Exemplo n.º 9
0
void monster::process_effects()
{
    for( auto effect_it = effects.begin(); effect_it != effects.end(); ++effect_it ) {
        std::string id = effect_it->second.get_id();
        if (id == "nasty_poisoned") {
            mod_speed_bonus( -rng(3, 5) );
            apply_damage( nullptr, bp_torso, rng( 3, 6 ) );
        } if (id == "poisoned") {
            mod_speed_bonus( -rng(0, 3) );
            apply_damage( nullptr, bp_torso, rng( 1, 3 ) );

        // MATERIALS-TODO: use fire resistance
        } else if (id == "onfire") {
            if (made_of("flesh") || made_of("iflesh"))
                apply_damage( nullptr, bp_torso, rng( 3, 8 ) );
            if (made_of("veggy"))
                apply_damage( nullptr, bp_torso, rng( 10, 20 ) );
            if (made_of("paper") || made_of("powder") || made_of("wood") || made_of("cotton") ||
                made_of("wool"))
                apply_damage( nullptr, bp_torso, rng( 15, 40 ) );
        }
    }

    //If this monster has the ability to heal in combat, do it now.
    if( has_flag( MF_REGENERATES_50 ) ) {
        if( hp < type->hp ) {
            if( one_in( 2 ) && g->u.sees( this ) ) {
                add_msg( m_warning, _( "The %s is visibly regenerating!" ), name().c_str() );
            }
            hp += 50;
            if( hp > type->hp ) {
                hp = type->hp;
            }
        }
    }
    if( has_flag( MF_REGENERATES_10 ) ) {
        if( hp < type->hp ) {
            if( one_in( 2 ) && g->u.sees( this ) ) {
                add_msg( m_warning, _( "The %s seems a little healthier." ), name().c_str() );
            }
            hp += 10;
            if( hp > type->hp ) {
                hp = type->hp;
            }
        }
    }

    //Monster will regen morale and aggression if it is on max HP
    //It regens more morale and aggression if is currently fleeing.
    if( has_flag( MF_REGENMORALE ) && hp >= type->hp ) {
        if( is_fleeing( g->u ) ) {
            morale = type->morale;
            anger = type->agro;
        }
        if( morale <= type->morale ) {
            morale += 1;
        }
        if( anger <= type->agro ) {
            anger += 1;
        }
        if( morale < 0 ) {
            morale += 5;
        }
        if( anger < 0 ) {
            anger += 5;
        }
    }

    // If this critter dies in sunlight, check & assess damage.
    if( has_flag( MF_SUNDEATH ) && g->is_in_sunlight( posx(), posy() ) ) {
        if( g->u.sees( this ) ) {
            add_msg( m_good, _( "The %s burns horribly in the sunlight!" ), name().c_str() );
        }
        hp -= 100;
        if( hp < 0 ) {
            hp = 0;
        }
    }

    Creature::process_effects();
}
Exemplo n.º 10
0
// General movement.
// Currently, priority goes:
// 1) Special Attack
// 2) Sight-based tracking
// 3) Scent-based tracking
// 4) Sound-based tracking
void monster::move(game *g)
{
// We decrement wandf no matter what.  We'll save our wander_to plans until
// after we finish out set_dest plans, UNLESS they time out first.
 if (wandf > 0)
  wandf--;

 //Hallucinations have a chance of disappearing each turn
 if (is_hallucination() && one_in(25)) {
   die(g);
   return;
 }

// First, use the special attack, if we can!
 if (sp_timeout > 0) {
   sp_timeout--;
 }
 //If this monster has the ability to heal in combat, do it now.
 if (has_flag(MF_REGENERATES_50)) {
    hp += 50;
    if(hp > type->hp){
      hp = type->hp;
    }
 }
 if (has_flag(MF_REGENERATES_10)) {
    hp += 10;
    if(hp > type->hp){
      hp = type->hp;
    }
 }

 if (sp_timeout == 0 && (friendly == 0 || has_flag(MF_FRIENDLY_SPECIAL))) {
   mattack ma;
   if(!is_hallucination()) {
     (ma.*type->sp_attack)(g, this);
   }
 }
 if (moves < 0)
  return;
 if (has_flag(MF_IMMOBILE)) {
  moves = 0;
  return;
 }
 if (has_effect(ME_STUNNED)) {
  stumble(g, false);
  moves = 0;
  return;
 }
 if (has_effect(ME_DOWNED)) {
  moves = 0;
  return;
 }
 if (has_effect(ME_BOULDERING)){
  moves -= 20;
  if (moves < 0) {
   return;
  }
 }
 if (friendly != 0) {
  if (friendly > 0)
   friendly--;
  friendly_move(g);
  return;
 }

 bool moved = false;
 point next;
 int mondex = (plans.size() > 0 ? g->mon_at(plans[0].x, plans[0].y) : -1);

 monster_attitude current_attitude = attitude();
 if (friendly == 0)
  current_attitude = attitude(&(g->u));
// If our plans end in a player, set our attitude to consider that player
 if (plans.size() > 0) {
  if (plans.back().x == g->u.posx && plans.back().y == g->u.posy)
   current_attitude = attitude(&(g->u));
  else {
   for (int i = 0; i < g->active_npc.size(); i++) {
    if (plans.back().x == g->active_npc[i]->posx &&
        plans.back().y == g->active_npc[i]->posy)
     current_attitude = attitude((g->active_npc[i]));
   }
  }
 }

 if (current_attitude == MATT_IGNORE ||
     (current_attitude == MATT_FOLLOW && plans.size() <= MONSTER_FOLLOW_DIST)) {
  moves -= 100;
  stumble(g, false);
  return;
 }

 if (plans.size() > 0 && !is_fleeing(g->u) &&
     (mondex == -1 || g->zombie(mondex).friendly != 0 || has_flag(MF_ATTACKMON)) &&
     (can_move_to(g, plans[0].x, plans[0].y) ||
      (plans[0].x == g->u.posx && plans[0].y == g->u.posy) ||
     (g->m.has_flag("BASHABLE", plans[0].x, plans[0].y) && has_flag(MF_BASHES)))){
  // CONCRETE PLANS - Most likely based on sight
  next = plans[0];
  moved = true;
 } else if (has_flag(MF_SMELLS)) {
// No sight... or our plans are invalid (e.g. moving through a transparent, but
//  solid, square of terrain).  Fall back to smell if we have it.
  plans.clear();
  point tmp = scent_move(g);
  if (tmp.x != -1) {
   next = tmp;
   moved = true;
  }
 }
 if (wandf > 0 && !moved) { // No LOS, no scent, so as a fall-back follow sound
  plans.clear();
  point tmp = wander_next(g);
  if (tmp.x != posx() || tmp.y != posy()) {
   next = tmp;
   moved = true;
  }
 }

// Finished logic section.  By this point, we should have chosen a square to
//  move to (moved = true).
 if (moved) { // Actual effects of moving to the square we've chosen
  // Note: The below works because C++ in A() || B() won't call B() if A() is true
  int& x = next.x; int& y = next.y; // Define alias for x and y
  bool did_something = attack_at(x, y) || bash_at(x, y) || move_to(g, x, y);
  if(!did_something) {
   moves -= 100; // If we don't do this, we'll get infinite loops.
  }
 } else {
  moves -= 100;
 }

// If we're close to our target, we get focused and don't stumble
 if ((has_flag(MF_STUMBLES) && (plans.size() > 3 || plans.size() == 0)) ||
     !moved)
  stumble(g, moved);
}
Exemplo n.º 11
0
// General movement.
// Currently, priority goes:
// 1) Special Attack
// 2) Sight-based tracking
// 3) Scent-based tracking
// 4) Sound-based tracking
void monster::move()
{
    // We decrement wandf no matter what.  We'll save our wander_to plans until
    // after we finish out set_dest plans, UNLESS they time out first.
    if (wandf > 0) {
        wandf--;
    }

    //Hallucinations have a chance of disappearing each turn
    if (is_hallucination() && one_in(25)) {
        die();
        return;
    }

    // First, use the special attack, if we can!
    if (sp_timeout > 0) {
        sp_timeout--;
    }
    //If this monster has the ability to heal in combat, do it now.
    if (has_flag(MF_REGENERATES_50)) {
        if (hp < type->hp) {
            if (one_in(2)) {
                g->add_msg(_("The %s is visibly regenerating!"), name().c_str());
            }
            hp += 50;
            if(hp > type->hp) {
                hp = type->hp;
            }
        }
    }
    if (has_flag(MF_REGENERATES_10)) {
        if (hp < type->hp) {
            if (one_in(2)) {
                g->add_msg(_("The %s seems a little healthier."), name().c_str());
            }
            hp += 10;
            if(hp > type->hp) {
                hp = type->hp;
            }
        }
    }

    //The monster can consume objects it stands on. Check if there are any.
    //If there are. Consume them.
    if (has_flag(MF_ABSORBS)) {
        if(!g->m.i_at(posx(), posy()).empty()) {
            g->add_msg(_("The %s flows around the objects on the floor and they are quickly dissolved!"), name().c_str());
            std::vector<item> items_absorbed = g->m.i_at(posx(), posy());
            for( size_t i = 0; i < items_absorbed.size(); ++i ) {
                hp += items_absorbed.at(i).volume(); //Yeah this means it can get more HP than normal.
            }
            g->m.i_clear(posx(), posy());
        }
    }

    //Monster will regen morale and agression if it is on max HP
    //It regens more morale and agression if is currently fleeing.
    if(has_flag(MF_REGENMORALE) && hp >= type->hp){
        if(is_fleeing(g->u)){
            morale = type->morale;
            anger = type->agro;
        }
        if(morale <= type->morale)
            morale += 1;
        if(anger <= type->agro)
            anger += 1;
        if(morale < 0)
            morale += 5;
        if(anger < 0)
            anger += 5;
    }

    // If this critter dies in sunlight, check & assess damage.
    if (g->is_in_sunlight(posx(), posy()) && has_flag(MF_SUNDEATH)) {
        g->add_msg(_("The %s burns horribly in the sunlight!"), name().c_str());
        hp -= 100;
        if(hp < 0) {
            hp = 0  ;
        }
    }

    if (sp_timeout == 0 && (friendly == 0 || has_flag(MF_FRIENDLY_SPECIAL))) {
        mattack ma;
        if(!is_hallucination()) {
            (ma.*type->sp_attack)(this);
        }
    }
    if (moves < 0) {
        return;
    }
    if (has_flag(MF_IMMOBILE)) {
        moves = 0;
        return;
    }
    if (has_effect("stunned")) {
        stumble(false);
        moves = 0;
        return;
    }
    if (has_effect("downed")) {
        moves = 0;
        return;
    }
    if (has_effect("bouldering")) {
        moves -= 20;
        if (moves < 0) {
            return;
        }
    }
    if (friendly != 0) {
        if (friendly > 0) {
            friendly--;
        }
        friendly_move();
        return;
    }

    bool moved = false;
    point next;
    int mondex = (!plans.empty() ? g->mon_at(plans[0].x, plans[0].y) : -1);

    monster_attitude current_attitude = attitude();
    if (friendly == 0) {
        current_attitude = attitude(&(g->u));
    }
    // If our plans end in a player, set our attitude to consider that player
    if (!plans.empty()) {
        if (plans.back().x == g->u.posx && plans.back().y == g->u.posy) {
            current_attitude = attitude(&(g->u));
        } else {
            for (int i = 0; i < g->active_npc.size(); i++) {
                if (plans.back().x == g->active_npc[i]->posx &&
                      plans.back().y == g->active_npc[i]->posy) {
                    current_attitude = attitude((g->active_npc[i]));
                }
            }
        }
    }

    if (current_attitude == MATT_IGNORE ||
          (current_attitude == MATT_FOLLOW && plans.size() <= MONSTER_FOLLOW_DIST)) {
        moves -= 100;
        stumble(false);
        return;
    }

    if (!plans.empty() &&
         (mondex == -1 || g->zombie(mondex).friendly != 0 || has_flag(MF_ATTACKMON)) &&
         (can_move_to(plans[0].x, plans[0].y) ||
         (plans[0].x == g->u.posx && plans[0].y == g->u.posy) ||
         (g->m.has_flag("BASHABLE", plans[0].x, plans[0].y) && has_flag(MF_BASHES)))){
        // CONCRETE PLANS - Most likely based on sight
        next = plans[0];
        moved = true;
    } else if (has_flag(MF_SMELLS)) {
        // No sight... or our plans are invalid (e.g. moving through a transparent, but
        //  solid, square of terrain).  Fall back to smell if we have it.
        plans.clear();
        point tmp = scent_move();
        if (tmp.x != -1) {
            next = tmp;
            moved = true;
        }
    }
    if (wandf > 0 && !moved) { // No LOS, no scent, so as a fall-back follow sound
        plans.clear();
        point tmp = wander_next();
        if (tmp.x != posx() || tmp.y != posy()) {
            next = tmp;
            moved = true;
        }
    }

    // Finished logic section.  By this point, we should have chosen a square to
    //  move to (moved = true).
    if (moved) { // Actual effects of moving to the square we've chosen
        // Note: The below works because C++ in A() || B() won't call B() if A() is true
        int& x = next.x; int& y = next.y; // Define alias for x and y
        bool did_something = attack_at(x, y) || bash_at(x, y) || move_to(x, y);
        if(!did_something) {
            moves -= 100; // If we don't do this, we'll get infinite loops.
        }
    } else {
        moves -= 100;
    }

    // If we're close to our target, we get focused and don't stumble
    if ((has_flag(MF_STUMBLES) && (plans.size() > 3 || plans.empty())) ||
          !moved) {
        stumble(moved);
    }
}
Exemplo n.º 12
0
void monster::plan(const mfactions &factions)
{
    // Bots are more intelligent than most living stuff
    bool electronic = has_flag( MF_ELECTRONIC );
    Creature *target = nullptr;
    // 8.6f is rating for tank drone 60 tiles away, moose 16 or boomer 33
    float dist = !electronic ? 1000 : 8.6f;
    int bresenham_slope = 0;
    int selected_slope = 0;
    bool fleeing = false;
    bool docile = has_flag( MF_VERMIN ) || ( friendly != 0 && has_effect( "docile" ) );
    bool angers_hostile_weak = type->anger.find( MTRIG_HOSTILE_WEAK ) != type->anger.end();
    int angers_hostile_near = ( type->anger.find( MTRIG_HOSTILE_CLOSE ) != type->anger.end() ) ? 5 : 0;
    int fears_hostile_near = ( type->fear.find( MTRIG_HOSTILE_CLOSE ) != type->fear.end() ) ? 5 : 0;
    bool group_morale = has_flag( MF_GROUP_MORALE ) && morale < type->morale;
    bool swarms = has_flag( MF_SWARMS );
    auto mood = attitude();

    // If we can see the player, move toward them or flee.
    if( friendly == 0 && sees( g->u, bresenham_slope ) ) {
        dist = rate_target( g->u, bresenham_slope, dist, electronic );
        fleeing = fleeing || is_fleeing( g->u );
        target = &g->u;
        selected_slope = bresenham_slope;
        if( dist <= 5 ) {
            anger += angers_hostile_near;
            morale -= fears_hostile_near;
        }
    } else if( friendly != 0 && !docile ) {
        // Target unfriendly monsters, only if we aren't interacting with the player.
        for( int i = 0, numz = g->num_zombies(); i < numz; i++ ) {
            monster &tmp = g->zombie( i );
            if( tmp.friendly == 0 ) {
                float rating = rate_target( tmp, bresenham_slope, dist, electronic );
                if( rating < dist ) {
                    target = &tmp;
                    dist = rating;
                    selected_slope = bresenham_slope;
                }
            }
        }
    }

    if( !docile ) {
        for( size_t i = 0; i < g->active_npc.size(); i++ ) {
            npc *me = g->active_npc[i];
            float rating = rate_target( *me, bresenham_slope, dist, electronic );
            bool fleeing_from = is_fleeing( *me );
            // Switch targets if closer and hostile or scarier than current target
            if( ( rating < dist && fleeing ) ||
                ( rating < dist && attitude( me ) == MATT_ATTACK ) ||
                ( !fleeing && fleeing_from ) ) {
                    target = me;
                    dist = rating;
                    selected_slope = bresenham_slope;
            }
            fleeing = fleeing || fleeing_from;
            if( rating <= 5 ) {
                anger += angers_hostile_near;
                morale -= fears_hostile_near;
            }
        }
    }

    fleeing = fleeing || ( mood == MATT_FLEE );
    if( friendly == 0 && !docile ) {
        for( const auto &fac : factions ) {
            auto faction_att = faction->attitude( fac.first );
            if( faction_att == MFA_NEUTRAL || faction_att == MFA_FRIENDLY ) {
                continue;
            }

            for( int i : fac.second ) { // mon indices
                monster &mon = g->zombie( i );
                float rating = rate_target( mon, bresenham_slope, dist, electronic );
                if( rating < dist ) {
                    target = &mon;
                    dist = rating;
                    selected_slope = bresenham_slope;
                }
                if( rating <= 5 ) {
                    anger += angers_hostile_near;
                    morale -= fears_hostile_near;
                }
            }
        }
    }

    // Friendly monsters here
    // Avoid for hordes of same-faction stuff or it could get expensive
    const monfaction *actual_faction = friendly == 0 ? faction : GetMFact( "player" );
    auto const &myfaction_iter = factions.find( actual_faction );
    if( myfaction_iter == factions.end() ) {
        DebugLog( D_ERROR, D_GAME ) << disp_name() << " tried to find faction " << 
            ( friendly == 0 ? faction->name : "player" ) << " which wasn't loaded in game::monmove";
        swarms = false;
        group_morale = false;
    }
    swarms = swarms && target == nullptr; // Only swarm if we have no target
    if( group_morale || swarms ) {
        auto const &myfaction = myfaction_iter->second;
        for( int i : myfaction ) {
            monster &mon = g->zombie( i );
            float rating = rate_target( mon, bresenham_slope, dist, electronic );
            if( group_morale && rating <= 10 ) {
                morale += 10 - rating;
            }
            if( swarms ) {
                if( rating < 5 ) { // Too crowded here
                    wandx = posx() * rng( 1, 3 ) - mon.posx();
                    wandy = posy() * rng( 1, 3 ) - mon.posy();
                    wandf = 2;
                    target = nullptr;
                    // Swarm to the furthest ally you can see
                } else if( rating < INT_MAX && rating > dist && wandf <= 0 ) {
                    target = &mon;
                    dist = rating;
                    selected_slope = bresenham_slope;
                }
            }
        }
    }

    if( target != nullptr ) {
        if( one_in( 2 ) ) { // Random for the diversity of the trajectory
            ++selected_slope;
        } else {
            --selected_slope;
        }

        point dest = target->pos();
        auto att_to_target = attitude_to( *target );
        if( att_to_target == Attitude::A_HOSTILE && !fleeing ) {
            set_dest( dest.x, dest.y, selected_slope );
        } else if( fleeing ) {
            set_dest( posx() * 2 - dest.x, posy() * 2 - dest.y, selected_slope );
        }
        if( angers_hostile_weak && att_to_target != Attitude::A_FRIENDLY ) {
            int hp_per = target->hp_percentage();
            if( hp_per <= 70 ) {
                anger += 10 - int( hp_per / 10 );
            }
        }
    } else if( friendly > 0 && one_in(3)) {
            // Grow restless with no targets
            friendly--;
    } else if( friendly < 0 && sees( g->u, bresenham_slope ) ) {
        if( rl_dist( pos(), g->u.pos() ) > 2 ) {
            set_dest(g->u.posx(), g->u.posy(), bresenham_slope);
        } else {
            plans.clear();
        }
    }
    // If we're not adjacent to the start of our plan path, don't act on it.
    // This is to catch when we had pre-existing invalid plans and
    // made it through the function without changing them.
    if( !plans.empty() && square_dist(pos().x, pos().y,
                                      plans.front().x, plans.front().y ) > 1 ) {
        plans.clear();
    }
}
Exemplo n.º 13
0
void monster::plan( const mfactions &factions )
{
    // Bots are more intelligent than most living stuff
    bool smart_planning = has_flag( MF_PRIORITIZE_TARGETS );
    Creature *target = nullptr;
    // 8.6f is rating for tank drone 60 tiles away, moose 16 or boomer 33
    float dist = !smart_planning ? 1000 : 8.6f;
    bool fleeing = false;
    bool docile = friendly != 0 && has_effect( effect_docile );
    bool angers_hostile_weak = type->anger.find( MTRIG_HOSTILE_WEAK ) != type->anger.end();
    int angers_hostile_near =
        ( type->anger.find( MTRIG_HOSTILE_CLOSE ) != type->anger.end() ) ? 5 : 0;
    int fears_hostile_near = ( type->fear.find( MTRIG_HOSTILE_CLOSE ) != type->fear.end() ) ? 5 : 0;
    bool group_morale = has_flag( MF_GROUP_MORALE ) && morale < type->morale;
    bool swarms = has_flag( MF_SWARMS );
    auto mood = attitude();

    // If we can see the player, move toward them or flee.
    if( friendly == 0 && sees( g->u ) ) {
        dist = rate_target( g->u, dist, smart_planning );
        fleeing = fleeing || is_fleeing( g->u );
        target = &g->u;
        if( dist <= 5 ) {
            anger += angers_hostile_near;
            morale -= fears_hostile_near;
        }
    } else if( friendly != 0 && !docile ) {
        // Target unfriendly monsters, only if we aren't interacting with the player.
        for( int i = 0, numz = g->num_zombies(); i < numz; i++ ) {
            monster &tmp = g->zombie( i );
            if( tmp.friendly == 0 ) {
                float rating = rate_target( tmp, dist, smart_planning );
                if( rating < dist ) {
                    target = &tmp;
                    dist = rating;
                }
            }
        }
    }

    if( docile ) {
        if( friendly != 0 && target != nullptr ) {
            set_dest( target->pos() );
        }

        return;
    }

    for( size_t i = 0; i < g->active_npc.size(); i++ ) {
        npc &who = *g->active_npc[i];
        auto faction_att = faction.obj().attitude( who.get_monster_faction() );
        if( faction_att == MFA_NEUTRAL || faction_att == MFA_FRIENDLY ) {
            continue;
        }

        float rating = rate_target( who, dist, smart_planning );
        bool fleeing_from = is_fleeing( who );
        // Switch targets if closer and hostile or scarier than current target
        if( ( rating < dist && fleeing ) ||
            ( rating < dist && attitude( &who ) == MATT_ATTACK ) ||
            ( !fleeing && fleeing_from ) ) {
            target = &who;
            dist = rating;
        }
        fleeing = fleeing || fleeing_from;
        if( rating <= 5 ) {
            anger += angers_hostile_near;
            morale -= fears_hostile_near;
        }
    }

    fleeing = fleeing || ( mood == MATT_FLEE );
    if( friendly == 0 ) {
        for( const auto &fac : factions ) {
            auto faction_att = faction.obj().attitude( fac.first );
            if( faction_att == MFA_NEUTRAL || faction_att == MFA_FRIENDLY ) {
                continue;
            }

            for( int i : fac.second ) { // mon indices
                monster &mon = g->zombie( i );
                float rating = rate_target( mon, dist, smart_planning );
                if( rating < dist ) {
                    target = &mon;
                    dist = rating;
                }
                if( rating <= 5 ) {
                    anger += angers_hostile_near;
                    morale -= fears_hostile_near;
                }
            }
        }
    }

    // Friendly monsters here
    // Avoid for hordes of same-faction stuff or it could get expensive
    const auto actual_faction = friendly == 0 ? faction : mfaction_str_id( "player" );
    auto const &myfaction_iter = factions.find( actual_faction );
    if( myfaction_iter == factions.end() ) {
        DebugLog( D_ERROR, D_GAME ) << disp_name() << " tried to find faction "
                                    << actual_faction.id().str()
                                    << " which wasn't loaded in game::monmove";
        swarms = false;
        group_morale = false;
    }
    swarms = swarms && target == nullptr; // Only swarm if we have no target
    if( group_morale || swarms ) {
        for( const int i : myfaction_iter->second ) {
            monster &mon = g->zombie( i );
            float rating = rate_target( mon, dist, smart_planning );
            if( group_morale && rating <= 10 ) {
                morale += 10 - rating;
            }
            if( swarms ) {
                if( rating < 5 ) { // Too crowded here
                    wander_pos.x = posx() * rng( 1, 3 ) - mon.posx();
                    wander_pos.y = posy() * rng( 1, 3 ) - mon.posy();
                    wandf = 2;
                    target = nullptr;
                    // Swarm to the furthest ally you can see
                } else if( rating < INT_MAX && rating > dist && wandf <= 0 ) {
                    target = &mon;
                    dist = rating;
                }
            }
        }
    }

    if( target != nullptr ) {

        tripoint dest = target->pos();
        auto att_to_target = attitude_to( *target );
        if( att_to_target == Attitude::A_HOSTILE && !fleeing ) {
            set_dest( dest );
        } else if( fleeing ) {
            set_dest( tripoint( posx() * 2 - dest.x, posy() * 2 - dest.y, posz() ) );
        }
        if( angers_hostile_weak && att_to_target != Attitude::A_FRIENDLY ) {
            int hp_per = target->hp_percentage();
            if( hp_per <= 70 ) {
                anger += 10 - int( hp_per / 10 );
            }
        }
    } else if( friendly > 0 && one_in( 3 ) ) {
        // Grow restless with no targets
        friendly--;
    } else if( friendly < 0 && sees( g->u ) ) {
        if( rl_dist( pos(), g->u.pos() ) > 2 ) {
            set_dest( g->u.pos() );
        } else {
            unset_dest();
        }
    }
}
Exemplo n.º 14
0
void monster::process_effects()
{
    // Monster only effects
    int mod = 1;
    for( auto &elem : effects ) {
        for( auto &_effect_it : elem.second ) {
            auto &it = _effect_it.second;
            // Monsters don't get trait-based reduction, but they do get effect based reduction
            bool reduced = has_effect(it.get_resist_effect());

            mod_speed_bonus(it.get_mod("SPEED", reduced));

            int val = it.get_mod("HURT", reduced);
            if (val > 0) {
                if(it.activated(calendar::turn, "HURT", val, reduced, mod)) {
                    apply_damage(nullptr, bp_torso, val);
                }
            }

            std::string id = _effect_it.second.get_id();
            // MATERIALS-TODO: use fire resistance
            if (id == "onfire") {
                if (made_of("flesh") || made_of("iflesh"))
                    apply_damage( nullptr, bp_torso, rng( 3, 8 ) );
                if (made_of("veggy"))
                    apply_damage( nullptr, bp_torso, rng( 10, 20 ) );
                if (made_of("paper") || made_of("powder") || made_of("wood") || made_of("cotton") ||
                    made_of("wool"))
                    apply_damage( nullptr, bp_torso, rng( 15, 40 ) );
            }
        }
    }

    //If this monster has the ability to heal in combat, do it now.
    if( has_flag( MF_REGENERATES_50 ) ) {
        if( hp < type->hp ) {
            if( one_in( 2 ) && g->u.sees( *this ) ) {
                add_msg( m_warning, _( "The %s is visibly regenerating!" ), name().c_str() );
            }
            hp += 50;
            if( hp > type->hp ) {
                hp = type->hp;
            }
        }
    }
    if( has_flag( MF_REGENERATES_10 ) ) {
        if( hp < type->hp ) {
            if( one_in( 2 ) && g->u.sees( *this ) ) {
                add_msg( m_warning, _( "The %s seems a little healthier." ), name().c_str() );
            }
            hp += 10;
            if( hp > type->hp ) {
                hp = type->hp;
            }
        }
    }

    //Monster will regen morale and aggression if it is on max HP
    //It regens more morale and aggression if is currently fleeing.
    if( has_flag( MF_REGENMORALE ) && hp >= type->hp ) {
        if( is_fleeing( g->u ) ) {
            morale = type->morale;
            anger = type->agro;
        }
        if( morale <= type->morale ) {
            morale += 1;
        }
        if( anger <= type->agro ) {
            anger += 1;
        }
        if( morale < 0 ) {
            morale += 5;
        }
        if( anger < 0 ) {
            anger += 5;
        }
    }

    // If this critter dies in sunlight, check & assess damage.
    if( has_flag( MF_SUNDEATH ) && g->is_in_sunlight( posx(), posy() ) ) {
        if( g->u.sees( *this ) ) {
            add_msg( m_good, _( "The %s burns horribly in the sunlight!" ), name().c_str() );
        }
        apply_damage( nullptr, bp_torso, 100 );
        if( hp < 0 ) {
            hp = 0;
        }
    }

    Creature::process_effects();
}
Exemplo n.º 15
0
void monster::plan(game *g)
{
 int sightrange = g->light_level();
 int closest = -1;
 int dist = 1000;
 int tc, stc;
 if (friendly != 0) {	// Target monsters, not the player!
  for (int i = 0; i < g->z.size(); i++) {
   monster *tmp = &(g->z[i]);
   if (tmp->friendly == 0 && rl_dist(posx, posy, tmp->posx, tmp->posy) < dist &&
       g->m.sees(posx, posy, tmp->posx, tmp->posy, sightrange, tc)) {
    closest = i;
    dist = rl_dist(posx, posy, tmp->posx, tmp->posy);
    stc = tc;
   }
  }
  if (closest >= 0)
   set_dest(g->z[closest].posx, g->z[closest].posy, stc);
  else if (friendly > 0 && one_in(3))	// Grow restless with no targets
   friendly--;
  else if (friendly < 0 && g->sees_u(posx, posy, tc)) {
   if (rl_dist(posx, posy, g->u.posx, g->u.posy) > 2)
    set_dest(g->u.posx, g->u.posy, tc);
   else
    plans.clear();
  }
  return;
 }
 if (is_fleeing(g->u) && can_see() && g->sees_u(posx, posy, tc) &&
     (!g->u.has_trait(PF_ANIMALEMPATH) || !has_flag(MF_ANIMAL))) {
  wandx = posx * 2 - g->u.posx;
  wandy = posy * 2 - g->u.posy;
  wandf = 40;
 }
// If we can see, and we can see a character, start moving towards them
 if (!is_fleeing(g->u) && can_see()) {
  if (g->sees_u(posx, posy, tc)) {
   dist = rl_dist(posx, posy, g->u.posx, g->u.posy);
   closest = -2;
   stc = tc;
  }
  for (int i = 0; i < g->active_npc.size(); i++) {
   npc *me = &(g->active_npc[i]);
   if (rl_dist(posx, posy, me->posx, me->posy) < dist &&
       g->m.sees(posx, posy, me->posx, me->posy, sightrange, tc)) {
    dist = rl_dist(posx, posy, me->posx, me->posy);
    closest = i;
    stc = tc;
   }
  }
  for (int i = 0; i < g->z.size(); i++) {
   monster *mon = &(g->z[i]);
   if (mon->friendly != 0 && rl_dist(posx, posy, mon->posx, mon->posy) < dist &&
       g->m.sees(posx, posy, mon->posx, mon->posy, sightrange, tc)) {
    dist = rl_dist(posx, posy, mon->posx, mon->posy);
    closest = -3 - i;
    stc = tc;
   }
  }
  if (closest == -2)
   set_dest(g->u.posx, g->u.posy, stc);
  else if (closest <= -3)
   set_dest(g->z[-3 - closest].posx, g->z[-3 - closest].posy, stc);
  else if (closest >= 0)
   set_dest(g->active_npc[closest].posx, g->active_npc[closest].posy, stc);
 }
}
Exemplo n.º 16
0
void monster::plan(const std::vector<int> &friendlies)
{
    int sightrange = g->light_level();
    int closest = -1;
    int dist = 1000;
    int tc = 0;
    int stc = 0;
    bool fleeing = false;

    if (friendly != 0) { // Target monsters, not the player!
        for (int i = 0, numz = g->num_zombies(); i < numz; i++) {
            monster *tmp = &(g->zombie(i));
            if (tmp->friendly == 0) {
                int d = rl_dist(posx(), posy(), tmp->posx(), tmp->posy());
                if (d < dist && g->m.sees(posx(), posy(), tmp->posx(), tmp->posy(), sightrange, tc)) {
                    closest = i;
                    dist = d;
                    stc = tc;
                }
            }
        }

        if (has_effect("docile")) {
            closest = -1;
        }

        if (closest >= 0) {
            set_dest(g->zombie(closest).posx(), g->zombie(closest).posy(), stc);
        } else if (friendly > 0 && one_in(3)) {
            // Grow restless with no targets
            friendly--;
        } else if (friendly < 0 &&  sees_player( tc ) ) {
            if (rl_dist(posx(), posy(), g->u.posx, g->u.posy) > 2) {
                set_dest(g->u.posx, g->u.posy, tc);
            } else {
                plans.clear();
            }
        }
        return;
    }

    // If we can see, and we can see a character, move toward them or flee.
    if (can_see() && sees_player( tc ) ) {
        dist = rl_dist(posx(), posy(), g->u.posx, g->u.posy);
        if (is_fleeing(g->u)) {
            // Wander away.
            fleeing = true;
            set_dest(posx() * 2 - g->u.posx, posy() * 2 - g->u.posy, tc);
        } else {
            // Chase the player.
            closest = -2;
            stc = tc;
        }
    }

    for (size_t i = 0; i < g->active_npc.size(); i++) {
        npc *me = (g->active_npc[i]);
        int medist = rl_dist(posx(), posy(), me->posx, me->posy);
        if ((medist < dist || (!fleeing && is_fleeing(*me))) &&
                (can_see() &&
                g->m.sees(posx(), posy(), me->posx, me->posy, sightrange, tc))) {
            if (is_fleeing(*me)) {
                fleeing = true;
                set_dest(posx() * 2 - me->posx, posy() * 2 - me->posy, tc);\
            } else {
                closest = i;
                stc = tc;
            }
            dist = medist;
        }
    }

    if (!fleeing) {
        fleeing = attitude() == MATT_FLEE;
        if (can_see()) {
            for( auto &friendlie : friendlies ) {
                const int i = friendlie;
                monster *mon = &(g->zombie(i));
                int mondist = rl_dist(posx(), posy(), mon->posx(), mon->posy());
                if (mondist < dist &&
                        g->m.sees(posx(), posy(), mon->posx(), mon->posy(), sightrange, tc)) {
                    dist = mondist;
                    if (fleeing) {
                        wandx = posx() * 2 - mon->posx();
                        wandy = posy() * 2 - mon->posy();
                        wandf = 40;
                    } else {
                        closest = -3 - i;
                        stc = tc;
                    }
                }
            }
        }

        if (closest == -2) {
            if (one_in(2)) {//random for the diversity of the trajectory
                ++stc;
            } else {
                --stc;
            }
            set_dest(g->u.posx, g->u.posy, stc);
        } else if (closest <= -3) {
            set_dest(g->zombie(-3 - closest).posx(), g->zombie(-3 - closest).posy(), stc);
        } else if (closest >= 0) {
            set_dest(g->active_npc[closest]->posx, g->active_npc[closest]->posy, stc);
        }
    }
    // If we're not adjacent to the start of our plan path, don't act on it.
    // This is to catch when we had pre-existing invalid plans and
    // made it through the function without changing them.
    if( !plans.empty() && square_dist(pos().x, pos().y,
                                      plans.front().x, plans.front().y ) > 1 ) {
        plans.clear();
    }
}