/* Random walking even when we've moved
 * To simulate zombie stumbling and ineffective movement
 * Note that this is sub-optimal; stumbling may INCREASE a zombie's speed.
 * Most of the time (out in the open) this effect is insignificant compared to
 * the negative effects, but in a hallway it's perfectly even
 */
void monster::stumble( bool moved )
{
    // don't stumble every turn. every 3rd turn, or 8th when walking.
    if( ( moved && !one_in( 8 ) ) || !one_in( 3 ) ) {
        return;
    }

    std::vector<tripoint> valid_stumbles;
    const bool avoid_water = has_flag( MF_NO_BREATHE ) && !has_flag( MF_SWIMS ) && !has_flag( MF_AQUATIC );
    for( int i = -1; i <= 1; i++ ) {
        for( int j = -1; j <= 1; j++ ) {
            tripoint dest( posx() + i, posy() + j, posz() );
            if( ( i || j ) && can_move_to( dest ) &&
                //Stop zombies and other non-breathing monsters wandering INTO water
                //(Unless they can swim/are aquatic)
                //But let them wander OUT of water if they are there.
                !( avoid_water &&
                   g->m.has_flag( "SWIMMABLE", dest ) &&
                   !g->m.has_flag( "SWIMMABLE", pos3() ) ) &&
                g->critter_at( dest ) == nullptr ) {
                valid_stumbles.push_back( dest );
            }
        }
    }

    if( g->m.has_zlevels() ) {
        tripoint below( posx(), posy(), posz() - 1 );
        tripoint above( posx(), posy(), posz() + 1 );
        if( g->m.valid_move( pos(), below, false, true ) && can_move_to( below ) ) {
            valid_stumbles.push_back( below );
        }
        // More restrictions for moving up
        // It should happen during "shambling around", but not as actual stumbling
        if( !moved && one_in( 5 ) && has_flag( MF_FLIES ) &&
            g->m.valid_move( pos(), above, false, true ) && can_move_to( above ) ) {
            valid_stumbles.push_back( above );
        }
    }

    if( valid_stumbles.empty() ) { //nowhere to stumble?
        return;
    }

    move_to( random_entry( valid_stumbles ), false );

    // Here we have to fix our plans[] list,
    // acquiring a new path to the previous target.
    // target == either end of current plan, or the player.
    int bresenham_slope, junk;
    if( !plans.empty() ) {
        if( g->m.sees( pos3(), plans.back(), -1, bresenham_slope, junk ) ) {
            set_dest( plans.back(), bresenham_slope );
        } else if( sees( g->u, bresenham_slope ) ) {
            set_dest( g->u.pos(), bresenham_slope );
        } else { //durr, i'm suddenly calm. what was i doing?
            plans.clear();
        }
    }
}
Exemple #2
0
/* Random walking even when we've moved
 * To simulate zombie stumbling and ineffective movement
 * Note that this is sub-optimal; stumbling may INCREASE a zombie's speed.
 * Most of the time (out in the open) this effect is insignificant compared to
 * the negative effects, but in a hallway it's perfectly even
 */
void monster::stumble(bool moved)
{
// don't stumble every turn. every 3rd turn, or 8th when walking.
    if((moved && !one_in(8)) || !one_in(3))
    {
        return;
    }

    std::vector <point> valid_stumbles;
    for (int i = -1; i <= 1; i++) {
        for (int j = -1; j <= 1; j++) {
            const int nx = posx() + i;
            const int ny = posy() + j;
            if ((i || j) && can_move_to(nx, ny) &&
                    /* Don't ever stumble into impassable terrain, even if we normally could
                     * smash it, as this is uncoordinated movement (and is forced). */
                    g->m.move_cost(nx, ny) != 0 &&
                    //Stop zombies and other non-breathing monsters wandering INTO water
                    //(Unless they can swim/are aquatic)
                    //But let them wander OUT of water if they are there.
                    !(has_flag(MF_NO_BREATHE) && !has_flag(MF_SWIMS) && !has_flag(MF_AQUATIC)
                      && g->m.has_flag("SWIMMABLE", nx, ny)
                      && !g->m.has_flag("SWIMMABLE", posx(), posy())) &&
                    (g->u.posx != nx || g->u.posy != ny) &&
                    (g->mon_at(nx, ny) == -1)) {
                point tmp(nx, ny);
                valid_stumbles.push_back(tmp);
            }
        }
    }
    if (valid_stumbles.size() == 0) //nowhere to stumble?
    {
        return;
    }

    int choice = rng(0, valid_stumbles.size() - 1);
    int cx = valid_stumbles[choice].x;
    int cy = valid_stumbles[choice].y;

    moves -= calc_movecost(posx(), posy(), cx, cy);
    setpos(cx, cy);

// Here we have to fix our plans[] list,
// acquiring a new path to the previous target.
// target == either end of current plan, or the player.
    int tc;
    if (plans.size() > 0) {
        if (g->m.sees(posx(), posy(), plans.back().x, plans.back().y, -1, tc))
            set_dest(plans.back().x, plans.back().y, tc);
        else if (sees_player( tc ))
            set_dest(g->u.posx, g->u.posy, tc);
        else //durr, i'm suddenly calm. what was i doing?
            plans.clear();
    }
}
int monster::trigger_sum(std::set<monster_trigger> *triggers) const
{
    int ret = 0;
    bool check_terrain = false, check_meat = false, check_fire = false;
    for( const auto &trigger : *triggers ) {
        switch( trigger ) {
            case MTRIG_STALK:
                if (anger > 0 && one_in(20)) {
                    ret++;
                }
                break;

            case MTRIG_MEAT:
                check_terrain = true;
                check_meat = true;
                break;

            case MTRIG_FIRE:
                check_terrain = true;
                check_fire = true;
                break;

            default:
                break; // The rest are handled when the impetus occurs
        }
    }

    if (check_terrain) {
        for (int x = posx() - 3; x <= posx() + 3; x++) {
            for (int y = posy() - 3; y <= posy() + 3; y++) {
                if (check_meat) {
                    auto items = g->m.i_at(x, y);
                    for( auto &item : items ) {
                        if( item.is_corpse() || item.type->id == "meat" ||
                            item.type->id == "meat_cooked" || item.type->id == "human_flesh" ) {
                            ret += 3;
                            check_meat = false;
                        }
                    }
                }
                if (check_fire) {
                    ret += ( 5 * g->m.get_field_strength( point(x, y), fd_fire) );
                }
            }
        }
        if (check_fire) {
            if (g->u.has_amount("torch_lit", 1)) {
                ret += 49;
            }
        }
    }

    return ret;
}
Exemple #4
0
/* Random walking even when we've moved
 * To simulate zombie stumbling and ineffective movement
 * Note that this is sub-optimal; stumbling may INCREASE a zombie's speed.
 * Most of the time (out in the open) this effect is insignificant compared to
 * the negative effects, but in a hallway it's perfectly even
 */
void monster::stumble(bool moved)
{
 // don't stumble every turn. every 3rd turn, or 8th when walking.
 if((moved && !one_in(8)) || !one_in(3))
 {
     return;
 }

 std::vector <point> valid_stumbles;
 for (int i = -1; i <= 1; i++) {
  for (int j = -1; j <= 1; j++) {
   const int nx = posx() + i;
   const int ny = posy() + j;
   if ((i || j) && can_move_to(nx, ny) &&
       //Stop zombies and other non-breathing monsters wandering INTO water
       //(Unless they can swim/are aquatic)
       //But let them wander OUT of water if they are there.
       !(has_flag(MF_NO_BREATHE) && !has_flag(MF_SWIMS) && !has_flag(MF_AQUATIC)
           && g->m.has_flag("SWIMMABLE", nx, ny)
           && !g->m.has_flag("SWIMMABLE", posx(), posy())) &&
       (g->u.posx() != nx || g->u.posy() != ny) &&
       (g->mon_at(nx, ny) == -1) &&
       (g->npc_at(nx, ny) == -1) ) {
    point tmp(nx, ny);
    valid_stumbles.push_back(tmp);
   }
  }
 }
 if (valid_stumbles.empty()) //nowhere to stumble?
 {
     return;
 }

 int choice = rng(0, valid_stumbles.size() - 1);
 int cx = valid_stumbles[choice].x;
 int cy = valid_stumbles[choice].y;

 move_to( cx, cy, false );

 // Here we have to fix our plans[] list,
 // acquiring a new path to the previous target.
 // target == either end of current plan, or the player.
 int bresenham_slope;
 if (!plans.empty()) {
  if (g->m.sees( pos(), plans.back(), -1, bresenham_slope))
   set_dest(plans.back().x, plans.back().y, bresenham_slope);
  else if (sees( g->u, bresenham_slope ))
   set_dest(g->u.posx(), g->u.posy(), bresenham_slope);
  else //durr, i'm suddenly calm. what was i doing?
   plans.clear();
 }
}
Exemple #5
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;
}
void Enemy::Process()
{
    auto obj = GetMap()->GetNearest(pixel_x(), pixel_y());
    if (obj != nullptr)
        ProcessSpeed(obj->posx() * 32, obj->posy() * 32);
    ProcessMove();
    ProcessHealth();
}
Exemple #7
0
int monster::turns_to_reach(int x, int y)
{
    std::vector<point> path = g->m.route(posx(), posy(), x, y, has_flag(MF_BASHES));
    if (path.size() == 0)
        return 999;

    double turns = 0.;
    for (int i = 0; i < path.size(); i++) {
        if (g->m.move_cost(path[i].x, path[i].y) == 0) // We have to bash through
            turns += 5;
        else if (i == 0)
            turns += double(calc_movecost(posx(), posy(), path[i].x, path[i].y)) / speed;
        else
            turns += double(calc_movecost(path[i-1].x, path[i-1].y, path[i].x, path[i].y)) / speed;
    }
    return int(turns + .9); // Round up
}
/**
 * Stumble in a random direction, but with some caveats.
 */
void monster::stumble( )
{
    // Only move every 3rd turn.
    if( !one_in( 3 ) ) {
        return;
    }

    std::vector<tripoint> valid_stumbles;
    const bool avoid_water = has_flag( MF_NO_BREATHE ) &&
      !has_flag( MF_SWIMS ) && !has_flag( MF_AQUATIC );
    for( int i = -1; i <= 1; i++ ) {
        for( int j = -1; j <= 1; j++ ) {
            tripoint dest( posx() + i, posy() + j, posz() );
            if( ( i || j ) && can_move_to( dest ) &&
                //Stop zombies and other non-breathing monsters wandering INTO water
                //(Unless they can swim/are aquatic)
                //But let them wander OUT of water if they are there.
                !( avoid_water &&
                   g->m.has_flag( TFLAG_SWIMMABLE, dest ) &&
                   !g->m.has_flag( TFLAG_SWIMMABLE, pos3() ) ) &&
                ( g->critter_at( dest, is_hallucination() ) == nullptr ) ) {
                valid_stumbles.push_back( dest );
            }
        }
    }

    if( g->m.has_zlevels() ) {
        tripoint below( posx(), posy(), posz() - 1 );
        tripoint above( posx(), posy(), posz() + 1 );
        if( g->m.valid_move( pos(), below, false, true ) && can_move_to( below ) ) {
            valid_stumbles.push_back( below );
        }
        // More restrictions for moving up
        if( one_in( 5 ) && has_flag( MF_FLIES ) &&
            g->m.valid_move( pos(), above, false, true ) && can_move_to( above ) ) {
            valid_stumbles.push_back( above );
        }
    }

    if( valid_stumbles.empty() ) { //nowhere to stumble?
        return;
    }

    move_to( random_entry( valid_stumbles ), false );
}
Exemple #9
0
int monster::turns_to_reach(int x, int y)
{
    std::vector<point> path = g->m.route(posx(), posy(), x, y, false);
    if (path.empty())
        return 999;

    double turns = 0.;
    for (size_t i = 0; i < path.size(); i++) {
        if (g->m.move_cost(path[i].x, path[i].y) == 0) {
            // We have to bash through
            turns += 5;
        } else if (i == 0) {
            turns += double(calc_movecost(posx(), posy(), path[i].x, path[i].y)) / get_speed();
        } else {
            turns += double(calc_movecost(path[i-1].x, path[i-1].y, path[i].x, path[i].y)) / get_speed();
        }
    }
    return int(turns + .9); // Round up
}
void monster::drop_items_on_death()
{
    if(is_hallucination()) {
        return;
    }
    if (type->death_drops.empty()) {
        return;
    }
    g->m.put_items_from_loc( type->death_drops, posx(), posy(), calendar::turn );
}
bool monster::setpos(const int x, const int y, const bool level_change)
{
    if (!level_change && x == posx() && y == posy()) {
        return true;
    }
    bool ret = level_change ? true : g->update_zombie_pos(*this, x, y);
    position.x = x;
    position.y = y;

    return ret;
}
Exemple #12
0
/*
 * Drawing-related functions
 */
void Creature::draw(WINDOW *w, int player_x, int player_y, bool inverted) const
{
    int draw_x = getmaxx(w) / 2 + posx() - player_x;
    int draw_y = getmaxy(w) / 2 + posy() - player_y;
    if(inverted) {
        mvwputch_inv(w, draw_y, draw_x, basic_symbol_color(), symbol());
    } else if(is_symbol_highlighted()) {
        mvwputch_hi(w, draw_y, draw_x, basic_symbol_color(), symbol());
    } else {
        mvwputch(w, draw_y, draw_x, symbol_color(), symbol() );
    }
}
Exemple #13
0
/* will_reach() is used for determining whether we'll get to stairs (and
 * potentially other locations of interest).  It is generally permissive.
 * TODO: Pathfinding;
         Make sure that non-smashing monsters won't "teleport" through windows
         Injure monsters if they're gonna be walking through pits or whatevs
 */
bool monster::will_reach(int x, int y)
{
    monster_attitude att = attitude(&(g->u));
    if (att != MATT_FOLLOW && att != MATT_ATTACK && att != MATT_FRIEND)
        return false;

    if (has_flag(MF_DIGS))
        return false;

    if (has_flag(MF_IMMOBILE) && (posx() != x || posy() != y))
        return false;

    std::vector<point> path = g->m.route(posx(), posy(), x, y, has_flag(MF_BASHES));
    if (path.size() == 0)
        return false;

    if (has_flag(MF_SMELLS) && g->scent(posx(), posy()) > 0 &&
            g->scent(x, y) > g->scent(posx(), posy()))
        return true;

    if (can_hear() && wandf > 0 && rl_dist(wandx, wandy, x, y) <= 2 &&
            rl_dist(posx(), posy(), wandx, wandy) <= wandf)
        return true;

    int t;
    if (can_see() && g->m.sees(posx(), posy(), x, y, g->light_level(), t))
        return true;

    return false;
}
Exemple #14
0
void monster::draw(WINDOW *w, int plx, int ply, bool inv)
{
 int x = getmaxx(w)/2 + posx() - plx;
 int y = getmaxy(w)/2 + posy() - ply;
 nc_color color = type->color;
 if (friendly != 0 && !inv)
  mvwputch_hi(w, y, x, color, type->sym);
 else if (inv)
  mvwputch_inv(w, y, x, color, type->sym);
 else {
  color = color_with_effects();
  mvwputch(w, y, x, color, type->sym);
 }
}
/* will_reach() is used for determining whether we'll get to stairs (and
 * potentially other locations of interest).  It is generally permissive.
 * TODO: Pathfinding;
         Make sure that non-smashing monsters won't "teleport" through windows
         Injure monsters if they're gonna be walking through pits or whatevs
 */
bool monster::will_reach( int x, int y )
{
    monster_attitude att = attitude( &( g->u ) );
    if( att != MATT_FOLLOW && att != MATT_ATTACK && att != MATT_FRIEND && att != MATT_ZLAVE ) {
        return false;
    }

    if( has_flag( MF_DIGS ) || has_flag( MF_AQUATIC ) ) {
        return false;
    }

    if( has_flag( MF_IMMOBILE ) && ( posx() != x || posy() != y ) ) {
        return false;
    }

    std::vector<tripoint> path = g->m.route( pos(), tripoint(x, y, posz()), 0, 100 );
    if( path.empty() ) {
        return false;
    }

    if( has_flag( MF_SMELLS ) && g->scent( pos3() ) > 0 &&
        g->scent( { x, y, posz() } ) > g->scent( pos3() ) ) {
        return true;
    }

    if( can_hear() && wandf > 0 && rl_dist( wander_pos.x, wander_pos.y, x, y ) <= 2 &&
        rl_dist( posx(), posy(), wander_pos.x, wander_pos.y ) <= wandf ) {
        return true;
    }

    int t;
    if( can_see() && g->m.sees( posx(), posy(), x, y, g->light_level(), t ) ) {
        return true;
    }

    return false;
}
Exemple #16
0
/* will_reach() is used for determining whether we'll get to stairs (and
 * potentially other locations of interest).  It is generally permissive.
 * TODO: Pathfinding;
         Make sure that non-smashing monsters won't "teleport" through windows
         Injure monsters if they're gonna be walking through pits or whatevs
 */
bool monster::will_reach( int x, int y )
{
    monster_attitude att = attitude( &( g->u ) );
    if( att != MATT_FOLLOW && att != MATT_ATTACK && att != MATT_FRIEND && att != MATT_ZLAVE ) {
        return false;
    }

    if( has_flag( MF_DIGS ) || has_flag( MF_AQUATIC ) ) {
        return false;
    }

    if( has_flag( MF_IMMOBILE ) && ( posx() != x || posy() != y ) ) {
        return false;
    }

    auto path = g->m.route( pos(), tripoint( x, y, posz() ), get_pathfinding_settings() );
    if( path.empty() ) {
        return false;
    }

    if( has_flag( MF_SMELLS ) && g->scent.get( pos() ) > 0 &&
        g->scent.get( { x, y, posz() } ) > g->scent.get( pos() ) ) {
        return true;
    }

    if( can_hear() && wandf > 0 && rl_dist( wander_pos.x, wander_pos.y, x, y ) <= 2 &&
        rl_dist( posx(), posy(), wander_pos.x, wander_pos.y ) <= wandf ) {
        return true;
    }

    if( can_see() && sees( tripoint( x, y, posz() ) ) ) {
        return true;
    }

    return false;
}
void Creature::draw( const catacurses::window &w, const tripoint &p, bool inverted ) const
{
    if (is_draw_tiles_mode()) {
        return;
    }

    int draw_x = getmaxx(w) / 2 + posx() - p.x;
    int draw_y = getmaxy(w) / 2 + posy() - p.y;
    if(inverted) {
        mvwputch_inv(w, draw_y, draw_x, basic_symbol_color(), symbol());
    } else if(is_symbol_highlighted()) {
        mvwputch_hi(w, draw_y, draw_x, basic_symbol_color(), symbol());
    } else {
        mvwputch(w, draw_y, draw_x, symbol_color(), symbol() );
    }
}
Exemple #18
0
bool Character::i_add_or_drop(item& it, int qty) {
    bool retval = true;
    bool drop = false;
    inv.assign_empty_invlet(it);
    for (int i = 0; i < qty; ++i) {
        if (!drop && (!can_pickWeight(it.weight(), !OPTIONS["DANGEROUS_PICKUPS"])
                      || !can_pickVolume(it.volume()))) {
            drop = true;
        }
        if (drop) {
            retval &= g->m.add_item_or_charges(posx(), posy(), it);
        } else {
            i_add(it);
        }
    }
    return retval;
}
Exemple #19
0
void print_counter()
{
  UBYTE cnt;

  /* Ensure mutual exclusion (not really necessary in this example)... */
  disable_interrupts();
  cnt = tim_cnt;
  enable_interrupts();

  printf(" TIM %u", cnt);
  gotoxy(9, posy());

  /* Ensure mutual exclusion (not really necessary in this example)... */
  disable_interrupts();
  cnt = vbl_cnt;
  enable_interrupts();

  printf("- VBL %u\n", cnt);
}
Exemple #20
0
/** Create or adjust "pos" parameter for a component
 * Assumed that name either equals "x", "y" or "z" otherwise this
 * method will not add or modify "pos" parameter
 * @param comp :: Component
 * @param name :: name of the parameter
 * @param value :: value
 * @param pDescription :: a pointer (may be NULL) to a string, containing
 * parameter's
 * description. If provided, the contents of the string is copied to the
 * parameters
 * memory
  */
void ParameterMap::addPositionCoordinate(
    const IComponent *comp, const std::string &name, const double value,
    const std::string *const pDescription) {
  Parameter_sptr param = get(comp, pos());
  V3D position;
  if (param) {
    // so "pos" already defined
    position = param->value<V3D>();
  } else {
    // so "pos" is not defined - therefore get position from component
    position = comp->getPos();
  }

  // adjust position

  if (name.compare(posx()) == 0)
    position.setX(value);
  else if (name.compare(posy()) == 0)
    position.setY(value);
  else if (name.compare(posz()) == 0)
    position.setZ(value);
  else {
    g_log.warning() << "addPositionCoordinate() called with unrecognized "
                       "coordinate symbol: " << name;
    // set description if one is provided
    if (pDescription) {
      param->setDescription(*pDescription);
    }
    return;
  }

  // clear the position cache
  clearPositionSensitiveCaches();
  // finally add or update "pos" parameter
  addV3D(comp, pos(), position, pDescription);
}
Exemple #21
0
void Bezier()
{
    glClear(GL_COLOR_BUFFER_BIT);
    glLineWidth(5);
    float PuntosdeControl[7][3] = {
        {411.0,posy(249.0),0.0},
        {505.0,posy(274.0),0.0},
        {597.0,posy(239.0),0.0},
        {620.0,posy(137.0),0.0},
        {518.0,posy(84.0),0.0},
        {414.0,posy(150.0),0.0},
        {412.0,posy(248.0),0.0}
    };
    
    glMap1f(GL_MAP1_VERTEX_3,0.0,1.0,3,7,*PuntosdeControl);
    glEnable(GL_MAP1_VERTEX_3);
    glMapGrid1f(100,0.0,1.0);
    glColor3f(1,0,0);
    glEvalMesh1(GL_LINE,0,100);
    glDisable(GL_MAP1_VERTEX_3);
    
    glFlush();
}
bool monster::digging()
{
    return has_flag(MF_DIGS) || (has_flag(MF_CAN_DIG) && g->m.has_flag("DIGGABLE", posx(), posy()));
}
Exemple #23
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);
    }
}
Exemple #24
0
void monster::knock_back_from(int x, int y)
{
    if (x == posx() && y == posy())
        return; // No effect
    point to(posx(), posy());
    if (x < posx())
        to.x++;
    if (x > posx())
        to.x--;
    if (y < posy())
        to.y++;
    if (y > posy())
        to.y--;

    bool u_see = g->u_see(to.x, to.y);

// First, see if we hit another monster
    int mondex = g->mon_at(to.x, to.y);
    if (mondex != -1) {
        monster *z = &(g->zombie(mondex));
        hurt(z->type->size);
        add_effect("stunned", 1);
        if (type->size > 1 + z->type->size) {
            z->knock_back_from(posx(), posy()); // Chain reaction!
            z->hurt(type->size);
            z->add_effect("stunned", 1);
        } else if (type->size > z->type->size) {
            z->hurt(type->size);
            z->add_effect("stunned", 1);
        }

        if (u_see)
            g->add_msg(_("The %s bounces off a %s!"), name().c_str(), z->name().c_str());

        return;
    }

    int npcdex = g->npc_at(to.x, to.y);
    if (npcdex != -1) {
        npc *p = g->active_npc[npcdex];
        hurt(3);
        add_effect("stunned", 1);
        p->hit(this, bp_torso, -1, type->size, 0);
        if (u_see)
            g->add_msg(_("The %s bounces off %s!"), name().c_str(), p->name.c_str());

        return;
    }

// If we're still in the function at this point, we're actually moving a tile!
    if (g->m.ter_at(to.x, to.y).has_flag(TFLAG_DEEP_WATER)) {
        if (g->m.has_flag("LIQUID", to.x, to.y) && can_drown()) {
            hurt(9999);
            if (u_see) {
                g->add_msg(_("The %s drowns!"), name().c_str());
            }

        } else if (has_flag(MF_AQUATIC)) { // We swim but we're NOT in water
            hurt(9999);
            if (u_see) {
                g->add_msg(_("The %s flops around and dies!"), name().c_str());
            }
        }
    }

    if (g->m.move_cost(to.x, to.y) == 0) {

        // It's some kind of wall.
        hurt(type->size);
        add_effect("stunned", 2);
        if (u_see) {
            g->add_msg(_("The %s bounces off a %s."), name().c_str(),
                       g->m.tername(to.x, to.y).c_str());
        }

    } else { // It's no wall
        setpos(to);
    }
}
Exemple #25
0
int monster::move_to(int x, int y, bool force)
{
    // Make sure that we can move there, unless force is true.
    if(!force) if(!g->is_empty(x, y) || !can_move_to(x, y)) {
            return 0;
        }

    if (has_effect("beartrap")) {
        moves = 0;
        return 0;
    }

    if (plans.size() > 0) {
        plans.erase(plans.begin());
    }

    if (!force) {
        moves -= calc_movecost(posx(), posy(), x, y);
    }

    //Check for moving into/out of water
    bool was_water = g->m.is_divable(posx(), posy());
    bool will_be_water = g->m.is_divable(x, y);

    if(was_water && !will_be_water && g->u_see(x, y)) {
        //Use more dramatic messages for swimming monsters
        g->add_msg(_("A %s %s from the %s!"), name().c_str(),
                   has_flag(MF_SWIMS) || has_flag(MF_AQUATIC) ? _("leaps") : _("emerges"),
                   g->m.tername(posx(), posy()).c_str());
    } else if(!was_water && will_be_water && g->u_see(x, y)) {
        g->add_msg(_("A %s %s into the %s!"), name().c_str(),
                   has_flag(MF_SWIMS) || has_flag(MF_AQUATIC) ? _("dives") : _("sinks"),
                   g->m.tername(x, y).c_str());
    }

    setpos(x, y);
    footsteps(x, y);
    if(is_hallucination()) {
        //Hallucinations don't do any of the stuff after this point
        return 1;
    }
    if (type->size != MS_TINY && g->m.has_flag("SHARP", posx(), posy()) && !one_in(4)) {
        hurt(rng(2, 3));
    }
    if (type->size != MS_TINY && g->m.has_flag("ROUGH", posx(), posy()) && one_in(6)) {
        hurt(rng(1, 2));
    }
    if (!digging() && !has_flag(MF_FLIES) &&
            g->m.tr_at(posx(), posy()) != tr_null) { // Monster stepped on a trap!
        trap* tr = g->traps[g->m.tr_at(posx(), posy())];
        if (dice(3, type->sk_dodge + 1) < dice(3, tr->avoidance)) {
            trapfuncm f;
            (f.*(tr->actm))(this, posx(), posy());
        }
    }
    // Diggers turn the dirt into dirtmound
    if (digging()) {
        int factor = 0;
        switch (type->size) {
        case MS_TINY:
            factor = 100;
            break;
        case MS_SMALL:
            factor = 30;
            break;
        case MS_MEDIUM:
            factor = 6;
            break;
        case MS_LARGE:
            factor = 3;
            break;
        case MS_HUGE:
            factor = 1;
            break;
        }
        if (has_flag(MF_VERMIN)) {
            factor *= 100;
        }
        if (one_in(factor)) {
            g->m.ter_set(posx(), posy(), t_dirtmound);
        }
    }
    // Acid trail monsters leave... a trail of acid
    if (has_flag(MF_ACIDTRAIL)) {
        g->m.add_field(posx(), posy(), fd_acid, 3);
    }

    if (has_flag(MF_SLUDGETRAIL)) {
        for (int dx = -1; dx <= 1; dx++) {
            for (int dy = -1; dy <= 1; dy++) {
                const int fstr = 3 - (abs(dx) + abs(dy));
                if (fstr >= 2) {
                    g->m.add_field(posx() + dx, posy() + dy, fd_sludge, fstr);
                }
            }
        }
    }

    return 1;
}
Exemple #26
0
point monster::wander_next()
{
    point next;
    bool xbest = true;
    if (abs(wandy - posy()) > abs(wandx - posx()))// which is more important
        xbest = false;
    next.x = posx();
    next.y = posy();
    int x = posx(), x2 = posx() - 1, x3 = posx() + 1;
    int y = posy(), y2 = posy() - 1, y3 = posy() + 1;
    if (wandx < posx()) {
        x--;
        x2++;
    }
    if (wandx > posx()) {
        x++;
        x2++;
        x3 -= 2;
    }
    if (wandy < posy()) {
        y--;
        y2++;
    }
    if (wandy > posy()) {
        y++;
        y2++;
        y3 -= 2;
    }
    if (xbest) {
        if (can_move_to(x, y) || (x == g->u.posx && y == g->u.posy) ||
                (has_flag(MF_BASHES) && g->m.has_flag("BASHABLE", x, y))) {
            next.x = x;
            next.y = y;
        } else if (can_move_to(x, y2) || (x == g->u.posx && y == g->u.posy) ||
                   (has_flag(MF_BASHES) && g->m.has_flag("BASHABLE", x, y2))) {
            next.x = x;
            next.y = y2;
        } else if (can_move_to(x2, y) || (x == g->u.posx && y == g->u.posy) ||
                   (has_flag(MF_BASHES) && g->m.has_flag("BASHABLE", x2, y))) {
            next.x = x2;
            next.y = y;
        } else if (can_move_to(x, y3) || (x == g->u.posx && y == g->u.posy) ||
                   (has_flag(MF_BASHES) && g->m.has_flag("BASHABLE", x, y3))) {
            next.x = x;
            next.y = y3;
        } else if (can_move_to(x3, y) || (x == g->u.posx && y == g->u.posy) ||
                   (has_flag(MF_BASHES) && g->m.has_flag("BASHABLE", x3, y))) {
            next.x = x3;
            next.y = y;
        }
    } else {
        if (can_move_to(x, y) || (x == g->u.posx && y == g->u.posy) ||
                (has_flag(MF_BASHES) && g->m.has_flag("BASHABLE", x, y))) {
            next.x = x;
            next.y = y;
        } else if (can_move_to(x2, y) || (x == g->u.posx && y == g->u.posy) ||
                   (has_flag(MF_BASHES) && g->m.has_flag("BASHABLE", x2, y))) {
            next.x = x2;
            next.y = y;
        } else if (can_move_to(x, y2) || (x == g->u.posx && y == g->u.posy) ||
                   (has_flag(MF_BASHES) && g->m.has_flag("BASHABLE", x, y2))) {
            next.x = x;
            next.y = y2;
        } else if (can_move_to(x3, y) || (x == g->u.posx && y == g->u.posy) ||
                   (has_flag(MF_BASHES) && g->m.has_flag("BASHABLE", x3, y))) {
            next.x = x3;
            next.y = y;
        } else if (can_move_to(x, y3) || (x == g->u.posx && y == g->u.posy) ||
                   (has_flag(MF_BASHES) && g->m.has_flag("BASHABLE", x, y3))) {
            next.x = x;
            next.y = y3;
        }
    }
    return next;
}
Exemple #27
0
bool Character::move_effects(bool attacking)
{
    if (has_effect("downed")) {
        ///\EFFECT_DEX increases chance to stand up when knocked down

        ///\EFFECT_STR increases chance to stand up when knocked down, slightly
        if (rng(0, 40) > get_dex() + get_str() / 2) {
            add_msg_if_player(_("You struggle to stand."));
        } else {
            add_msg_player_or_npc(m_good, _("You stand up."),
                                    _("<npcname> stands up."));
            remove_effect("downed");
        }
        return false;
    }
    if (has_effect("webbed")) {
        ///\EFFECT_STR increases chance to escape webs
        if (x_in_y(get_str(), 6 * get_effect_int("webbed"))) {
            add_msg_player_or_npc(m_good, _("You free yourself from the webs!"),
                                    _("<npcname> frees themselves from the webs!"));
            remove_effect("webbed");
        } else {
            add_msg_if_player(_("You try to free yourself from the webs, but can't get loose!"));
        }
        return false;
    }
    if (has_effect("lightsnare")) {
        ///\EFFECT_STR increases chance to escape light snare

        ///\EFFECT_DEX increases chance to escape light snare
        if(x_in_y(get_str(), 12) || x_in_y(get_dex(), 8)) {
            remove_effect("lightsnare");
            add_msg_player_or_npc(m_good, _("You free yourself from the light snare!"),
                                    _("<npcname> frees themselves from the light snare!"));
            item string("string_36", calendar::turn);
            item snare("snare_trigger", calendar::turn);
            g->m.add_item_or_charges(posx(), posy(), string);
            g->m.add_item_or_charges(posx(), posy(), snare);
        } else {
            add_msg_if_player(m_bad, _("You try to free yourself from the light snare, but can't get loose!"));
        }
        return false;
    }
    if (has_effect("heavysnare")) {
        ///\EFFECT_STR increases chance to escape heavy snare, slightly

        ///\EFFECT_DEX increases chance to escape light snare
        if(x_in_y(get_str(), 32) || x_in_y(get_dex(), 16)) {
            remove_effect("heavysnare");
            add_msg_player_or_npc(m_good, _("You free yourself from the heavy snare!"),
                                    _("<npcname> frees themselves from the heavy snare!"));
            item rope("rope_6", calendar::turn);
            item snare("snare_trigger", calendar::turn);
            g->m.add_item_or_charges(posx(), posy(), rope);
            g->m.add_item_or_charges(posx(), posy(), snare);
        } else {
            add_msg_if_player(m_bad, _("You try to free yourself from the heavy snare, but can't get loose!"));
        }
        return false;
    }
    if (has_effect("beartrap")) {
        /* Real bear traps can't be removed without the proper tools or immense strength; eventually this should
           allow normal players two options: removal of the limb or removal of the trap from the ground
           (at which point the player could later remove it from the leg with the right tools).
           As such we are currently making it a bit easier for players and NPC's to get out of bear traps.
        */
        ///\EFFECT_STR increases chance to escape bear trap
        if(x_in_y(get_str(), 100)) {
            remove_effect("beartrap");
            add_msg_player_or_npc(m_good, _("You free yourself from the bear trap!"),
                                    _("<npcname> frees themselves from the bear trap!"));
            item beartrap("beartrap", calendar::turn);
            g->m.add_item_or_charges(posx(), posy(), beartrap);
        } else {
            add_msg_if_player(m_bad, _("You try to free yourself from the bear trap, but can't get loose!"));
        }
        return false;
    }
    if (has_effect("crushed")) {
        ///\EFFECT_STR increases chance to escape crushing rubble

        ///\EFFECT_DEX increases chance to escape crushing rubble, slightly
        if(x_in_y(get_str() + get_dex() / 4, 100)) {
            remove_effect("crushed");
            add_msg_player_or_npc(m_good, _("You free yourself from the rubble!"),
                                    _("<npcname> frees themselves from the rubble!"));
        } else {
            add_msg_if_player(m_bad, _("You try to free yourself from the rubble, but can't get loose!"));
        }
        return false;
    }
    // Below this point are things that allow for movement if they succeed

    // Currently we only have one thing that forces movement if you succeed, should we get more
    // than this will need to be reworked to only have success effects if /all/ checks succeed
    if (has_effect("in_pit")) {
        ///\EFFECT_STR increases chance to escape pit

        ///\EFFECT_DEX increases chance to escape pit, slightly
        if (rng(0, 40) > get_str() + get_dex() / 2) {
            add_msg_if_player(m_bad, _("You try to escape the pit, but slip back in."));
            return false;
        } else {
            add_msg_player_or_npc(m_good, _("You escape the pit!"),
                                    _("<npcname> escapes the pit!"));
            remove_effect("in_pit");
        }
    }
    if( has_effect( "grabbed" ) && !attacking ) {
        int zed_number = 0;
        for( auto &&dest : g->m.points_in_radius( pos(), 1, 0 ) ){
            if( g->mon_at( dest ) != -1 &&
                ( g->zombie( g->mon_at( dest ) ).has_flag( MF_GRABS ) ||
                  g->zombie( g->mon_at( dest ) ).type->has_special_attack( "GRAB" ) ) ) {
                zed_number ++;
            }
        }
        if( zed_number == 0 ) {
            add_msg_player_or_npc( m_good, _( "You find yourself no longer grabbed." ),
                                   _( "<npcname> finds themselves no longer grabbed." ) );
            remove_effect( "grabbed" );
        ///\EFFECT_DEX increases chance to escape grab, if >STR

        ///\EFFECT_STR increases chance to escape grab, if >DEX
        } else if( rng( 0, std::max( get_dex(), get_str() ) ) < rng( get_effect_int( "grabbed" ), 8 ) ) {
            // Randomly compare higher of dex or str to grab intensity.
            add_msg_player_or_npc( m_bad, _( "You try break out of the grab, but fail!" ),
                                   _( "<npcname> tries to break out of the grab, but fails!" ) );
            return false;
        } else {
            add_msg_player_or_npc( m_good, _( "You break out of the grab!" ),
                                   _( "<npcname> breaks out of the grab!" ) );
            remove_effect( "grabbed" );
        }
    }
    return Creature::move_effects( attacking );
}
Exemple #28
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;
            }
        }
    }

    // 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.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(false);
        return;
    }

    if (plans.size() > 0 &&
            (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.size() == 0)) ||
            !moved) {
        stumble(moved);
    }
}
Creature *Creature::auto_find_hostile_target( int range, int &boo_hoo, int area )
{
    Creature *target = nullptr;
    player &u = g->u; // Could easily protect something that isn't the player
    constexpr int hostile_adj = 2; // Priority bonus for hostile targets
    const int iff_dist = ( range + area ) * 3 / 2 + 6; // iff check triggers at this distance
    int iff_hangle = 15 + area; // iff safety margin (degrees). less accuracy, more paranoia
    float best_target_rating = -1.0f; // bigger is better
    int u_angle = 0;         // player angle relative to turret
    boo_hoo = 0;         // how many targets were passed due to IFF. Tragically.
    bool self_area_iff = false; // Need to check if the target is near the vehicle we're a part of
    bool area_iff = false;      // Need to check distance from target to player
    bool angle_iff = true;      // Need to check if player is in a cone between us and target
    int pldist = rl_dist( pos(), g->u.pos() );
    vehicle *in_veh = is_fake() ? veh_pointer_or_null( g->m.veh_at( pos() ) ) : nullptr;
    if( pldist < iff_dist && sees( g->u ) ) {
        area_iff = area > 0;
        angle_iff = true;
        // Player inside vehicle won't be hit by shots from the roof,
        // so we can fire "through" them just fine.
        const optional_vpart_position vp = g->m.veh_at( u.pos() );
        if( in_veh && veh_pointer_or_null( vp ) == in_veh && vp->is_inside() ) {
            angle_iff = false; // No angle IFF, but possibly area IFF
        } else if( pldist < 3 ) {
            iff_hangle = (pldist == 2 ? 30 : 60);    // granularity increases with proximity
        }
        u_angle = g->m.coord_to_angle(posx(), posy(), u.posx(), u.posy());
    }

    if( area > 0 && in_veh != nullptr ) {
        self_area_iff = true;
    }

    std::vector<Creature*> targets = g->get_creatures_if( [&]( const Creature &critter ) {
        if( const monster *const mon_ptr = dynamic_cast<const monster*>( &critter ) ) {
            // friendly to the player, not a target for us
            return mon_ptr->friendly == 0;
        }
        if( const npc *const npc_ptr = dynamic_cast<const npc*>( &critter ) ) {
            // friendly to the player, not a target for us
            return npc_ptr->get_attitude() == NPCATT_KILL;
        }
        //@todo: what about g->u?
        return false;
    } );
    for( auto &m : targets ) {
        if( !sees( *m ) ) {
            // can't see nor sense it
            continue;
        }
        int dist = rl_dist( pos(), m->pos() ) + 1; // rl_dist can be 0
        if( dist > range + 1 || dist < area ) {
            // Too near or too far
            continue;
        }
        // Prioritize big, armed and hostile stuff
        float mon_rating = m->power_rating();
        float target_rating = mon_rating / dist;
        if( mon_rating + hostile_adj <= 0 ) {
            // We wouldn't attack it even if it was hostile
            continue;
        }

        if( in_veh != nullptr && veh_pointer_or_null( g->m.veh_at( m->pos() ) ) == in_veh ) {
            // No shooting stuff on vehicle we're a part of
            continue;
        }
        if( area_iff && rl_dist( u.pos(), m->pos() ) <= area ) {
            // Player in AoE
            boo_hoo++;
            continue;
        }
        // Hostility check can be expensive, but we need to inform the player of boo_hoo
        // only when the target is actually "hostile enough"
        bool maybe_boo = false;
        if( angle_iff ) {
            int tangle = g->m.coord_to_angle(posx(), posy(), m->posx(), m->posy());
            int diff = abs(u_angle - tangle);
            // Player is in the angle and not too far behind the target
            if( ( diff + iff_hangle > 360 || diff < iff_hangle ) &&
                ( dist * 3 / 2 + 6 > pldist ) ) {
                maybe_boo = true;
            }
        }
        if( !maybe_boo && ( ( mon_rating + hostile_adj ) / dist <= best_target_rating ) ) {
            // "Would we skip the target even if it was hostile?"
            // Helps avoid (possibly expensive) attitude calculation
            continue;
        }
        if( m->attitude_to( u ) == A_HOSTILE ) {
            target_rating = ( mon_rating + hostile_adj ) / dist;
            if( maybe_boo ) {
                boo_hoo++;
                continue;
            }
        }
        if( target_rating <= best_target_rating || target_rating <= 0 ) {
            continue; // Handle this late so that boo_hoo++ can happen
        }
        // Expensive check for proximity to vehicle
        if( self_area_iff && overlaps_vehicle( in_veh->get_points(), m->pos(), area ) ) {
            continue;
        }

        target = m;
        best_target_rating = target_rating;
    }
    return target;
}
/**
 * Attempts to harm a creature with a projectile.
 *
 * @param source Pointer to the creature who shot the projectile.
 * @param attack A structure describing the attack and its results.
 */
void Creature::deal_projectile_attack( Creature *source, dealt_projectile_attack &attack )
{
    const double missed_by = attack.missed_by;
    if( missed_by >= 1.0 ) {
        // Total miss
        return;
    }

    const projectile &proj = attack.proj;
    dealt_damage_instance &dealt_dam = attack.dealt_dam;
    const auto &proj_effects = proj.proj_effects;

    const bool u_see_this = g->u.sees(*this);

    const int avoid_roll = dodge_roll();
    // Do dice(10, speed) instead of dice(speed, 10) because speed could potentially be > 10000
    const int diff_roll = dice( 10, proj.speed );
    // Partial dodge, capped at [0.0, 1.0], added to missed_by
    const double dodge_rescaled = avoid_roll / static_cast<double>( diff_roll );
    const double goodhit = missed_by + std::max( 0.0, std::min( 1.0, dodge_rescaled ) ) ;

    if( goodhit >= 1.0 ) {
        // "Avoid" rather than "dodge", because it includes removing self from the line of fire
        //  rather than just Matrix-style bullet dodging
        if( source != nullptr && g->u.sees( *source ) ) {
            add_msg_player_or_npc(
                m_warning,
                _("You avoid %s projectile!"),
                _("<npcname> avoids %s projectile."),
                source->disp_name(true).c_str() );
        } else {
            add_msg_player_or_npc(
                m_warning,
                _("You avoid an incoming projectile!"),
                _("<npcname> avoids an incoming projectile.") );
        }

        attack.missed_by = 1.0; // Arbitrary value
        return;
    }

    // Bounce applies whether it does damage or not.
    if( proj.proj_effects.count( "BOUNCE" ) ) {
        add_effect( effect_bounced, 1_turns );
    }

    body_part bp_hit;
    double hit_value = missed_by + rng_float(-0.5, 0.5);
    // Headshots considered elsewhere
    if( hit_value <= 0.4 ) {
        bp_hit = bp_torso;
    } else if (one_in(4)) {
        if( one_in(2)) {
            bp_hit = bp_leg_l;
        } else {
            bp_hit = bp_leg_r;
        }
    } else {
        if( one_in(2)) {
            bp_hit = bp_arm_l;
        } else {
            bp_hit = bp_arm_r;
        }
    }

    double damage_mult = 1.0;

    std::string message = "";
    game_message_type gmtSCTcolor = m_neutral;

    if( goodhit < accuracy_headshot ) {
        message = _("Headshot!");
        gmtSCTcolor = m_headshot;
        damage_mult *= rng_float(1.95, 2.05);
        bp_hit = bp_head; // headshot hits the head, of course

    } else if( goodhit < accuracy_critical ) {
        message = _("Critical!");
        gmtSCTcolor = m_critical;
        damage_mult *= rng_float(1.5, 2.0);

    } else if( goodhit < accuracy_goodhit ) {
        message = _("Good hit!");
        gmtSCTcolor = m_good;
        damage_mult *= rng_float(1, 1.5);

    } else if( goodhit < accuracy_standard ) {
        damage_mult *= rng_float(0.5, 1);

    } else if( goodhit < accuracy_grazing ) {
        message = _("Grazing hit.");
        gmtSCTcolor = m_grazing;
        damage_mult *= rng_float(0, .25);
    }

    if( source != nullptr && !message.empty() ) {
        source->add_msg_if_player(m_good, message.c_str());
    }

    attack.missed_by = goodhit;

    // copy it, since we're mutating
    damage_instance impact = proj.impact;
    if( damage_mult > 0.0f && proj_effects.count( "NO_DAMAGE_SCALING" ) ) {
        damage_mult = 1.0f;
    }

    impact.mult_damage(damage_mult);

    if( proj_effects.count( "NOGIB" ) > 0 ) {
        float dmg_ratio = (float)impact.total_damage() / get_hp_max( player::bp_to_hp( bp_hit ) );
        if( dmg_ratio > 1.25f ) {
            impact.mult_damage( 1.0f / dmg_ratio );
        }
    }

    dealt_dam = deal_damage(source, bp_hit, impact);
    dealt_dam.bp_hit = bp_hit;

    // Apply ammo effects to target.
    if (proj.proj_effects.count("FLAME")) {
        if (made_of( material_id( "veggy" ) ) || made_of( material_id( "cotton" ) ) ||
            made_of( material_id( "wool" ) ) || made_of( material_id( "paper" ) ) ||
            made_of( material_id( "wood" ) ) ) {
            add_effect( effect_onfire, rng( 8_turns, 20_turns ), bp_hit );
        } else if (made_of( material_id( "flesh" ) ) || made_of( material_id( "iflesh" ) ) ) {
            add_effect( effect_onfire, rng( 5_turns, 10_turns ), bp_hit );
        }
    } else if (proj.proj_effects.count("INCENDIARY") ) {
        if (made_of( material_id( "veggy" ) ) || made_of( material_id( "cotton" ) ) ||
            made_of( material_id( "wool" ) ) || made_of( material_id( "paper" ) ) ||
            made_of( material_id( "wood" ) ) ) {
            add_effect( effect_onfire, rng( 2_turns, 6_turns ), bp_hit );
        } else if ( (made_of( material_id( "flesh" ) ) || made_of( material_id( "iflesh" ) ) ) &&
                    one_in(4) ) {
            add_effect( effect_onfire, rng( 1_turns, 4_turns ), bp_hit );
        }
    } else if (proj.proj_effects.count("IGNITE")) {
        if (made_of( material_id( "veggy" ) ) || made_of( material_id( "cotton" ) ) ||
            made_of( material_id( "wool" ) ) || made_of( material_id( "paper" ) ) ||
            made_of( material_id( "wood" ) ) ) {
            add_effect( effect_onfire, 6_turns, bp_hit );
        } else if (made_of( material_id( "flesh" ) ) || made_of( material_id( "iflesh" ) ) ) {
            add_effect( effect_onfire, 10_turns, bp_hit );
        }
    }

    if( bp_hit == bp_head && proj_effects.count( "BLINDS_EYES" ) ) {
        // TODO: Change this to require bp_eyes
        add_env_effect( effect_blind, bp_eyes, 5, rng( 3_turns, 10_turns ) );
    }

    if( proj_effects.count( "APPLY_SAP" ) ) {
        add_effect( effect_sap, 1_turns * dealt_dam.total_damage() );
    }

    int stun_strength = 0;
    if (proj.proj_effects.count("BEANBAG")) {
        stun_strength = 4;
    }
    if (proj.proj_effects.count("LARGE_BEANBAG")) {
        stun_strength = 16;
    }
    if( stun_strength > 0 ) {
        switch( get_size() ) {
        case MS_TINY:
            stun_strength *= 4;
            break;
        case MS_SMALL:
            stun_strength *= 2;
            break;
        case MS_MEDIUM:
        default:
            break;
        case MS_LARGE:
            stun_strength /= 2;
            break;
        case MS_HUGE:
            stun_strength /= 4;
            break;
        }
        add_effect( effect_stunned, 1_turns * rng( stun_strength / 2, stun_strength ) );
    }

    if(u_see_this) {
        if( damage_mult == 0 ) {
            if( source != nullptr ) {
                add_msg( source->is_player() ? _("You miss!") : _("The shot misses!") );
            }
        } else if( dealt_dam.total_damage() == 0 ) {
            //~ 1$ - monster name, 2$ - character's bodypart or monster's skin/armor
            add_msg( _("The shot reflects off %1$s %2$s!"), disp_name(true).c_str(),
                     is_monster() ?
                        skin_name().c_str() :
                        body_part_name_accusative(bp_hit).c_str() );
        } else if( is_player() ) {
                //monster hits player ranged
                //~ Hit message. 1$s is bodypart name in accusative. 2$d is damage value.
                add_msg_if_player(m_bad, _( "You were hit in the %1$s for %2$d damage." ),
                                  body_part_name_accusative(bp_hit).c_str(),
                                  dealt_dam.total_damage());
        } else if( source != nullptr ) {
            if( source->is_player() ) {
                //player hits monster ranged
                SCT.add(posx(), posy(),
                        direction_from(0, 0, posx() - source->posx(), posy() - source->posy()),
                        get_hp_bar(dealt_dam.total_damage(), get_hp_max(), true).first,
                        m_good, message, gmtSCTcolor);

                if (get_hp() > 0) {
                    SCT.add(posx(), posy(),
                            direction_from(0, 0, posx() - source->posx(), posy() - source->posy()),
                            get_hp_bar(get_hp(), get_hp_max(), true).first, m_good,
                            //~ "hit points", used in scrolling combat text
                            _("hp"), m_neutral, "hp");
                } else {
                    SCT.removeCreatureHP();
                }

                add_msg(m_good, _("You hit %s for %d damage."),
                        disp_name().c_str(), dealt_dam.total_damage());
            } else if( u_see_this ) {
                //~ 1$ - shooter, 2$ - target
                add_msg(_("%1$s shoots %2$s."),
                        source->disp_name().c_str(), disp_name().c_str());
            }
        }
    }

    check_dead_state();
    attack.hit_critter = this;
    attack.missed_by = goodhit;
}