Beispiel #1
0
void monster::friendly_move(game *g)
{
 point next;
 bool moved = false;
 moves -= 100;
 if (plans.size() > 0 && (plans[0].x != g->u.posx || plans[0].y != g->u.posy) &&
     (can_move_to(g->m, plans[0].x, plans[0].y) ||
     (g->m.has_flag(bashable, plans[0].x, plans[0].y) && has_flag(MF_BASHES)))){
  next = plans[0];
  plans.erase(plans.begin());
  moved = true;
 } else
  stumble(g, moved);
 if (moved) {
  int mondex = g->mon_at(next.x, next.y);
  int npcdex = g->npc_at(next.x, next.y);
  if (mondex != -1 && g->z[mondex].friendly == 0 && type->melee_dice > 0)
   hit_monster(g, mondex);
  else if (npcdex != -1 && type->melee_dice > 0)
   hit_player(g, g->active_npc[g->npc_at(next.x, next.y)]);
  else if (mondex == -1 && npcdex == -1 && can_move_to(g->m, next.x, next.y))
   move_to(g, next.x, next.y);
  else if ((!can_move_to(g->m, 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);
  } 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;
  }
 }
}
/* 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();
        }
    }
}
Beispiel #3
0
/*
Function: friendly_move
The per-turn movement and action calculation of any friendly monsters.
*/
void monster::friendly_move(game *g)
{
	point next;
	bool moved = false;
	//If we sucessfully calculated a plan in the generic monster movement function, begin executing it.
	if (plans.size() > 0 && (plans[0].x != g->u.posx || plans[0].y != g->u.posy) &&
		(can_move_to(g, plans[0].x, plans[0].y) ||
		(g->m.has_flag(bashable, plans[0].x, plans[0].y) && has_flag(MF_BASHES)))){
			next = plans[0];
			plans.erase(plans.begin());
			moved = true;
	} else {
		//Otherwise just stumble around randomly until we formulate a plan.
		moves -= 100;
		stumble(g, moved);
	}
	if (moved) {
		//We have a plan.
		int mondex = g->mon_at(next.x, next.y);
		int npcdex = g->npc_at(next.x, next.y);
		//If there is an unfriendly mosnter in the target square we want to move into, hit them if we have a melee attack.
		if (mondex != -1 && g->z[mondex].friendly == 0 && type->melee_dice > 0){
			hit_monster(g, mondex);
		}
		//If there is an npc (any npc?) we hit them assuming we have a melee attack.
		else if (npcdex != -1 && type->melee_dice > 0){
			hit_player(g, *g->active_npc[g->npc_at(next.x, next.y)]);
		}
		//If no one is there and its a walkable square, walk there.
		else if (mondex == -1 && npcdex == -1 && can_move_to(g, next.x, next.y)){
			move_to(g, next.x, next.y);
		}
		//If there is a bashable object in our way, bash it down.
		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;
		}
		//If there is a destroyable object in our way, destroy it.
		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;
		}
		//If all else fails in our plan (an issue with pathfinding maybe) stumble around instead.
		else {
			stumble(g, moved);
			moves -= 100;
		}
	}
}
Beispiel #4
0
//	敵機を自機の方にレーン変更
//		一番外側が レーン0,一番内側が レーン4
void change_lane(Car &car)
{
	if( car.m_laneChanged ||		//	2回連続レーンチェンジ不可
		car.m_lane == g_car.m_lane ||
		!can_move_to(car.m_pos + car.m_v) )		//	行き止まりの場合
	{
		car.m_laneChanged = false;
		return;
	}
	if( car.m_lane > g_car.m_lane ) {		//	外側のレーンに移動
		if( car.m_v.second == 0 ) {		//	水平方向に移動している場合
			if( car.m_pos.second < MAP_HT/2 ) {
				if( can_move_to(car.m_pos - Vec2(0, 1)) ) {
					car.m_pos.second -= 2;
					--car.m_lane;
					car.m_laneChanged = true;
				}
			} else {
				if( !g_level ) return;
				if( can_move_to(car.m_pos + Vec2(0, 1)) ) {
					car.m_pos.second += 2;
					--car.m_lane;
					car.m_laneChanged = true;
				}
			}
		} else {
		}
	} else {		//	内側のレーンに移動
		if( car.m_v.second == 0 ) {		//	水平方向に移動している場合
			if( car.m_pos.second < MAP_HT/2 ) {
				if( can_move_to(car.m_pos + Vec2(0, 1)) ) {
					car.m_pos.second += 2;
					++car.m_lane;
					car.m_laneChanged = true;
				}
			} else {
				if( !g_level ) return;
				if( can_move_to(car.m_pos - Vec2(0, 1)) ) {
					car.m_pos.second -= 2;
					++car.m_lane;
					car.m_laneChanged = true;
				}
			}
		} else {
		}
	}
	assert(car.m_lane >= 0 && car.m_lane < 5);
}
Beispiel #5
0
void move_car(Car &car)
{
	Vec2 p = car.m_pos + car.m_v;		//	次の位置
	if( !can_move_to(p) ) {		//	壁にぶつかった場合
		Vec2 v = rot_right90(car.m_v);	//	速度ベクターを90度回転
		p = car.m_pos + v;
		if( can_move_to(p) )
			car.m_v = v;
		else {
			//	行き止まりは無いと仮定
			car.m_v = rot_left90(car.m_v);	//	速度ベクターを90度回転
			p = car.m_pos + car.m_v;
		}
	}
	car.m_pos = p;
}
Beispiel #6
0
int reenter_from_bar(const GameState* state, int player, int d)
{
  if (d)
  {
    assert(d>=1&&d<=6);
    if (state->bar[player])
    {
      MoveResult move = can_move_to(state, player, 24-d);
      if (move)
      {
        DEBUG("P%d entering from bar @ %d\n", player, 24-d);
        DEC(state->bar[player]);
        int dest = PT(player,24-d);
        switch (move)
        {
          case MOVE_UNOCCUPIED:
          case MOVE_MERGE:
            SET(state->points[dest], MAKEPOINT(player, state->points[dest].count+1));
            return 1;
          case MOVE_HITBLOT:
            SET(state->points[dest], MAKEPOINT(player, 1));
            INC(state->bar[player^1]);
            return 1;
          default:
            assert(0);
        }
        return 1;
      }
    }
  }
  return 0;
}
bool monster::bash_at( const tripoint &p )
{
    if( p.z != posz() ) {
        return false; // TODO: Remove this
    }

    if( has_effect( "pacified" ) ) {
        return false;
    }

    //Hallucinations can't bash stuff.
    if( is_hallucination() ) {
        return false;
    }
    bool try_bash = !can_move_to( p ) || one_in( 3 );
    bool can_bash = g->m.is_bashable( p ) && ( has_flag( MF_BASHES ) || has_flag( MF_BORES ) );

    if( try_bash && can_bash ) {
        int bashskill = group_bash_skill( p );
        g->m.bash( p, bashskill );
        moves -= 100;
        return true;
    }
    return false;
}
Beispiel #8
0
/*
Function: friendly_move
The per-turn movement and action calculation of any friendly monsters.
*/
void monster::friendly_move()
{
    point next;
    bool moved = false;
    //If we sucessfully calculated a plan in the generic monster movement function, begin executing it.
    if (plans.size() > 0 && (plans[0].x != g->u.posx || plans[0].y != g->u.posy) &&
            (can_move_to(plans[0].x, plans[0].y) ||
             (g->m.has_flag("BASHABLE", plans[0].x, plans[0].y) && has_flag(MF_BASHES)))) {
        next = plans[0];
        plans.erase(plans.begin());
        moved = true;
    } else {
        //Otherwise just stumble around randomly until we formulate a plan.
        moves -= 100;
        stumble(moved);
    }
    if (moved) {
        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 all else fails in our plan (an issue with pathfinding maybe) stumble around instead.
        if(!did_something) {
            stumble(moved);
            moves -= 100;
        }
    }
}
/*
Function: friendly_move
The per-turn movement and action calculation of any friendly monsters.
*/
void monster::friendly_move()
{
    tripoint p;
    bool moved = false;
    //If we successfully calculated a plan in the generic monster movement function, begin executing it.
    if( !plans.empty() && ( plans[0] != g->u.pos3() ) &&
        ( can_move_to( plans[0] ) ||
          ( ( has_flag( MF_BASHES ) || has_flag( MF_BORES ) ) &&
            g->m.bash_rating( bash_estimate(), plans[0] ) >= 0 ) ) ) {
        p = plans[0];
        plans.erase( plans.begin() );
        moved = true;
    } else {
        //Otherwise just stumble around randomly until we formulate a plan.
        moves -= 100;
        stumble( moved );
    }
    if( moved ) {
        bool did_something = attack_at( p ) || bash_at( p ) || move_to( p );

        //If all else fails in our plan (an issue with pathfinding maybe) stumble around instead.
        if( !did_something ) {
            stumble( moved );
            moves -= 100;
        }
    }
}
Beispiel #10
0
		void
		status_machine_imp::move_to(int status)
		{
			REQUIRE(can_move_to(status));	
            _origin_status = _current_status;
			_current_status = status;
		}
Beispiel #11
0
/**
 * 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 );
}
Beispiel #12
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();
    }
}
Beispiel #13
0
MoveResult is_valid_move(const GameState* state, int player, int index, int d)
{
  assert(index>=0 && index<24);
  assert(state->points[PT(player,index)].occupied == player+1);
  
  if (d == 0 || index-d < -1)
    return 0; // out of bounds
  else if (index-d == -1)
    return MOVE_BEAROFF;

  return can_move_to(state, player, index-d);
}
Beispiel #14
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();
 }
}
Beispiel #15
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;
}
Beispiel #16
0
int monster::bash_at(int x, int y) {
    //Hallucinations can't bash stuff.
    if(is_hallucination()) {
      return 0;
    }
    bool try_bash = !can_move_to(g, x, y) || one_in(3);
    bool can_bash = g->m.has_flag("BASHABLE", x, y) && has_flag(MF_BASHES);
    if(try_bash && can_bash) {
        std::string bashsound = "NOBASH"; // If we hear "NOBASH" it's time to debug!
        int bashskill = int(type->melee_dice * type->melee_sides);

        // pileup = more bashskill, but only help bashing mob directly infront of target
        const int max_helper_depth = 5;
        const std::vector<point> bzone = get_bashing_zone( point(x, y), pos(), max_helper_depth );
        int diffx = pos().x - x;
        int diffy = pos().y - y;
        int mo_bash = 0;
        for( int i = 0; i < bzone.size(); i++ ) {
           if ( g->mon_at( bzone[i] ) != -1 ) {
              monster & helpermon = g->zombie( g->mon_at( bzone[i] ) );
              // trying for the same door and can bash; put on helper hat
              if ( helpermon.wandx == wandx && helpermon.wandy == wandy && helpermon.has_flag(MF_BASHES) ) {
                 // helpers lined up behind primary basher add full strength, so do those at either shoulder, others add 50%
                 //addbash *= ( bzone[i].x == pos().x || bzone[i].y == pos().y ? 2 : 1 );
                 int addbash = int(helpermon.type->melee_dice * helpermon.type->melee_sides); 
                 // helpers lined up behind primary basher add full strength, others 50%
                 addbash *= ( ( diffx == 0 && bzone[i].x == pos().x ) || ( diffy == 0 && bzone[i].y == pos().y ) ) ? 2 : 1;
                 mo_bash += addbash;
                 // g->add_msg("+ bashhelp: %d,%d : +%d = %d", bzone[i].x, bzone[i].y, addbash/2, mo_bash/2 );
              }
           }
        }
        // by our powers combined...
        bashskill += int (mo_bash / 2);

        g->m.bash(x, y, bashskill, bashsound);
        g->sound(x, y, 18, bashsound);
        moves -= 100;
        return 1;
    } else if (g->m.move_cost(x, y) == 0 &&
            !g->m.is_divable(x, y) && //No smashing water into rubble!
            has_flag(MF_DESTROYS)) {
        g->m.destroy(g, x, y, true); //todo: add bash info without BASHABLE flag to walls etc, balanced to these guys
        moves -= 250;
        return 1;
    }
    return 0;
}
Beispiel #17
0
int monster::bash_at(int x, int y) {

    if (has_effect("pacified")) return 0;

    //Hallucinations can't bash stuff.
    if(is_hallucination()) {
        return 0;
    }
    bool try_bash = !can_move_to(x, y) || one_in(3);
    bool can_bash = g->m.is_bashable(x, y) && has_flag(MF_BASHES);
    if(try_bash && can_bash) {
        int bashskill = bash_skill();

        // pileup = more bashskill, but only help bashing mob directly infront of target
        const int max_helper_depth = 5;
        const std::vector<point> bzone = get_bashing_zone( point(x, y), pos(), max_helper_depth );
        int diffx = pos().x - x;
        int diffy = pos().y - y;
        int mo_bash = 0;
        for( size_t i = 0; i < bzone.size(); ++i ) {
            if ( g->mon_at( bzone[i] ) != -1 ) {
                monster & helpermon = g->zombie( g->mon_at( bzone[i] ) );
                // trying for the same door and can bash; put on helper hat
                if ( helpermon.wandx == wandx && helpermon.wandy == wandy &&
                     helpermon.has_flag(MF_BASHES) ) {
                    // helpers lined up behind primary basher add full strength,
                    // so do those at either shoulder, others add 50%
                    int addbash = int(helpermon.type->melee_dice * helpermon.type->melee_sides);
                    if (helpermon.has_flag(MF_DESTROYS)) {
                        addbash *= 2.5;
                    }
                    // helpers lined up behind primary basher add full strength, others 50%
                    addbash *= ( ( diffx == 0 && bzone[i].x == pos().x ) ||
                                 ( diffy == 0 && bzone[i].y == pos().y ) ) ? 2 : 1;
                    mo_bash += addbash;
                }
            }
        }
        // by our powers combined...
        bashskill += int (mo_bash / 2);

        g->m.bash( x, y, bashskill );
        moves -= 100;
        return 1;
    }
    return 0;
}
Beispiel #18
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(game *g, 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++) {
   if (can_move_to(g, posx + i, posy + j) &&
       (g->u.posx != posx + i || g->u.posy != posy + j) &&
       (g->mon_at(posx + i, posy + j) == -1 || (i == 0 && j == 0))) {
    point tmp(posx + i, posy + j);
    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(g, posx, posy, cx, cy);
 posx = cx;
 posy = 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 (g->sees_u(posx, posy, tc))
   set_dest(g->u.posx, g->u.posy, tc);
  else //durr, i'm suddenly calm. what was i doing?
   plans.clear();
 }
}
Beispiel #19
0
void Monster::move_towards(int target_x, int target_y)
{
  Generic_map move_map = GAME.map->get_movement_map(get_intelligence());
  Pathfinder pf(move_map);
// Simple, dumb movement - suitable for zombies at least
  Point move;
  Point to(target_x, target_y), from = get_position();
  switch (get_intelligence()) {
    case INTEL_NULL:
    case INTEL_PLANT:
// Mobile plants just drunken walk.
      move.x = rng(posx - 1, posx + 1);
      move.y = rng(posy - 1, posy + 1);
      break;

    case INTEL_ZOMBIE:
      move = pf.get_step(PATH_LINE, from, to);
      break;

    case INTEL_ANIMAL:
    case INTEL_HUMAN:
      move = pf.get_step(PATH_A_STAR, from, to);
      break;

    default:
      debugmsg("No AI movement coded for Intel_level %s",
               intel_level_name(get_intelligence()).c_str());
  }

// TODO:  Add a "Stumble" flag that occasionally randomly picks, rather than
//        picking the best available.

  if (can_move_to( GAME.map, move.x, move.y )) {
    move_to( GAME.map, move.x, move.y );
// TODO: Add a "smashes terrain" flag, and if we can't move then smash
  } else if (GAME.map->is_smashable(move.x, move.y)) {
    std::string sound = GAME.map->smash(move.x, move.y, 
                                        base_attack().roll_damage());
    GAME.make_sound(sound, move.x, move.y);
    use_ap(100);
  } else {
    pause();
  }
}
Beispiel #20
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 );
}
Beispiel #21
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;
}
Beispiel #22
0
int monster::bash_at(int x, int y) {

    if (has_effect("pacified")) return 0;

    //Hallucinations can't bash stuff.
    if(is_hallucination()) {
       return 0;
    }
    bool try_bash = !can_move_to(x, y) || one_in(3);
    bool can_bash = g->m.is_bashable(x, y) && (has_flag(MF_BASHES) || has_flag(MF_BORES));
    if( try_bash && can_bash ) {
        int bashskill = group_bash_skill( point(x, y) );

        g->m.bash( x, y, bashskill );
        moves -= 100;
        return 1;
    }
    return 0;
}
Beispiel #23
0
bool monster::bash_at( const tripoint &p )
{
    if( p.z != posz() ) {
        return false; // TODO: Remove this
    }

    //Hallucinations can't bash stuff.
    if( is_hallucination() ) {
        return false;
    }
    bool try_bash = !can_move_to( p ) || one_in( 3 );
    bool can_bash = g->m.is_bashable( p ) && bash_skill() > 0;

    if( try_bash && can_bash ) {
        int bashskill = group_bash_skill( p );
        g->m.bash( p, bashskill );
        moves -= 100;
        return true;
    }
    return false;
}
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 );
}
Beispiel #25
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(game *g, bool moved)
{
 std::vector <point> valid_stumbles;
 for (int i = -1; i <= 1; i++) {
  for (int j = -1; j <= 1; j++) {
   if (can_move_to(g->m, posx + i, posy + j) &&
       (g->u.posx != posx + i || g->u.posy != posy + j) && 
       (g->mon_at(posx + i, posy + j) == -1 || (i == 0 && j == 0))) {
    point tmp(posx + i, posy + j);
    valid_stumbles.push_back(tmp);
   }
  }
 }
 if (valid_stumbles.size() > 0 && (one_in(8) || (!moved && one_in(3)))) {
  int choice = rng(0, valid_stumbles.size() - 1);
  posx = valid_stumbles[choice].x;
  posy = valid_stumbles[choice].y;
  if (!has_flag(MF_DIGS) || !has_flag(MF_FLIES))
   moves -= (g->m.move_cost(posx, posy) - 2) * 50;
// Here we have to fix our plans[] list, trying to get back to the last point
// Otherwise the stumble will basically have no effect!
  if (plans.size() > 0) {
   int tc;
   if (g->m.sees(posx, posy, plans[0].x, plans[0].y, -1, tc)) {
// Copy out old plans...
    std::vector <point> plans2;
    for (int i = 0; i < plans.size(); i++)
     plans2.push_back(plans[i]);
// Set plans to a route between where we are now, and where we were
    set_dest(plans[0].x, plans[0].y, tc);
// Append old plans to the new plans
    for (int index = 0; index < plans2.size(); index++)
     plans.push_back(plans2[index]);
   } else
    plans.clear();
  }
 }
}
Beispiel #26
0
int monster::bash_at(int x, int y) {
    //Hallucinations can't bash stuff.
    if(is_hallucination()) {
      return 0;
    }
    bool try_bash = !can_move_to(g, x, y) || one_in(3);
    bool can_bash = g->m.has_flag(bashable, x, y) && has_flag(MF_BASHES);
    if(try_bash && can_bash) {
        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(x, y, bashskill, bashsound);
        g->sound(x, y, 18, bashsound);
        moves -= 100;
        return 1;
    } else if (g->m.move_cost(x, y) == 0 &&
            !g->m.is_divable(x, y) && //No smashing water into rubble!
            has_flag(MF_DESTROYS)) {
        g->m.destroy(g, x, y, true);
        moves -= 250;
        return 1;
    }
    return 0;
}
Beispiel #27
0
void change_lane(Car &car, int &key)
{
	Vec2 p = car.m_pos;
	if( can_move_to(p) && can_move_to(p + car.m_v) )
	{		//	行き止まりでない場合
		if( car.m_v.second == 0 ) {		//	水平方向に移動している場合
			if( key == VK_UP && can_move_to(p - Vec2(0, 1)) ) {
				if( (p.second -= 2) < MAP_HT/2 )
					--car.m_lane;		//	外側のレーンに移動
				else {
					++car.m_lane;
				}
				key = 0;
			} else if( key == VK_DOWN && can_move_to(p + Vec2(0, 1)) ) {
				if( (p.second += 2) > MAP_HT/2 )
					--car.m_lane;		//	外側のレーンに移動
				else
					++car.m_lane;
				key = 0;
			}
		} else {			//	垂直方向に移動している場合
			if( key == VK_LEFT && can_move_to(p - Vec2(1, 0)) ) {
				if( (p.first -= 2) < MAP_WD/2 )
					--car.m_lane;		//	外側のレーンに移動
				else
					++car.m_lane;
				key = 0;
			} else if( key == VK_RIGHT && can_move_to(p + Vec2(1, 0)) ) {
				if( (p.first += 2) > MAP_WD/2 )
					--car.m_lane;		//	外側のレーンに移動
				else
					++car.m_lane;
				key = 0;
			}
		}
	}
	car.m_pos = p;
}
Beispiel #28
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;
}
Beispiel #29
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;
}
Beispiel #30
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);
    }
}