コード例 #1
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 successfully calculated a plan in the generic monster movement function, begin executing it.
    if (!plans.empty() && (plans[0].x != g->u.posx || plans[0].y != g->u.posy) &&
            (can_move_to(plans[0].x, plans[0].y) ||
             (has_flag(MF_BASHES) && g->m.bash_rating(bash_skill(), plans[0].x, plans[0].y) > 0))) {
        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;
        }
    }
}
コード例 #2
0
ファイル: monmove.cpp プロジェクト: fuchs87/Cataclysm-DDA
int monster::bash_estimate()
{
    int estimate = bash_skill();
    if( has_flag(MF_GROUP_BASH) ) {
        // Right now just give them a boost so they try to bash a lot of stuff.
        // TODO: base it on number of nearby friendlies.
        estimate += 20;
    }
    return estimate;
}
コード例 #3
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;
}
コード例 #4
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);
    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) ||
                  (has_flag(MF_BASHES) && g->m.bash_rating(bash_skill(), nx, ny) > 0))) {
                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.empty()) {
        int nextsq = rng(0, smoves.size() - 1);
        next = smoves[nextsq];
    }
    return next;
}
コード例 #5
0
ファイル: monmove.cpp プロジェクト: AlecWhite/Cataclysm-DDA
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 );
}
コード例 #6
0
ファイル: monmove.cpp プロジェクト: BromIDE/Cataclysm-DDA
int monster::group_bash_skill( point target )
{
    if( !has_flag(MF_GROUP_BASH) ) {
        return bash_skill();
    }
    int bashskill = 0;

    // 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( target, pos(), max_helper_depth );

    for( point candidate : bzone ) {
        // Drawing this line backwards excludes the target and includes the candidate.
        std::vector<point> path_to_target = line_to( target.x, target.y,
                                                     candidate.x, candidate.y, 0);
        bool connected = true;
        int mondex = -1;
        for( point in_path : path_to_target ) {
            // If any point in the line from zombie to target is not a cooperating zombie,
            // it can't contribute.
            mondex = g->mon_at( in_path );
            if( mondex == -1 ) {
                connected = false;
                break;
            }
            monster &helpermon = g->zombie( mondex );
            if( !helpermon.has_flag(MF_GROUP_BASH) ) {
                connected = false;
                break;
            }
        }
        if( !connected ) {
            continue;
        }
        // If we made it here, the last monster checked was the candidate.
        monster &helpermon = g->zombie( mondex );
        // Contribution falls off rapidly with distance from target.
        bashskill += helpermon.bash_skill() / rl_dist( candidate, target );
    }

    return bashskill;
}
コード例 #7
0
int monster::group_bash_skill( const tripoint &target )
{
    if( !has_flag( MF_GROUP_BASH ) ) {
        return bash_skill();
    }
    int bashskill = 0;

    // pileup = more bash skill, but only help bashing mob directly in front of target
    const int max_helper_depth = 5;
    const std::vector<tripoint> bzone = get_bashing_zone( target, pos(), max_helper_depth );

    for( const tripoint &candidate : bzone ) {
        // Drawing this line backwards excludes the target and includes the candidate.
        std::vector<tripoint> path_to_target = line_to( target, candidate, 0, 0 );
        bool connected = true;
        monster *mon = nullptr;
        for( const tripoint &in_path : path_to_target ) {
            // If any point in the line from zombie to target is not a cooperating zombie,
            // it can't contribute.
            mon = g->critter_at<monster>( in_path );
            if( !mon ) {
                connected = false;
                break;
            }
            monster &helpermon = *mon;
            if( !helpermon.has_flag( MF_GROUP_BASH ) || helpermon.is_hallucination() ) {
                connected = false;
                break;
            }
        }
        if( !connected || !mon ) {
            continue;
        }
        // If we made it here, the last monster checked was the candidate.
        monster &helpermon = *mon;
        // Contribution falls off rapidly with distance from target.
        bashskill += helpermon.bash_skill() / rl_dist( candidate, target );
    }

    return bashskill;
}
コード例 #8
0
ファイル: monmove.cpp プロジェクト: AlecWhite/Cataclysm-DDA
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;
}
コード例 #9
0
ファイル: monmove.cpp プロジェクト: AlecWhite/Cataclysm-DDA
// 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( nullptr );
        return;
    }

    //The monster can consume objects it stands on. Check if there are any.
    //If there are. Consume them.
    if( !is_hallucination() && has_flag( MF_ABSORBS ) && !g->m.has_flag( TFLAG_SEALED, pos() ) &&
        g->m.has_items( pos() ) ) {
        if( g->u.sees( *this ) ) {
            add_msg( _( "The %s flows around the objects on the floor and they are quickly dissolved!" ),
                     name().c_str() );
        }
        static const auto volume_per_hp = units::from_milliliter( 250 );
        for( auto &elem : g->m.i_at( pos() ) ) {
            hp += elem.volume() / volume_per_hp; // Yeah this means it can get more HP than normal.
        }
        g->m.i_clear( pos() );
    }

    const bool pacified = has_effect( effect_pacified );

    // First, use the special attack, if we can!
    // The attack may change `monster::special_attacks` (e.g. by transforming
    // this into another monster type). Therefor we can not iterate over it
    // directly and instead iterate over the map from the monster type
    // (properties of monster types should never change).
    for( const auto &sp_type : type->special_attacks ) {
        const std::string &special_name = sp_type.first;
        const auto local_iter = special_attacks.find( special_name );
        if( local_iter == special_attacks.end() ) {
            continue;
        }
        mon_special_attack &local_attack_data = local_iter->second;
        if( !local_attack_data.enabled ) {
            continue;
        }

        if( local_attack_data.cooldown > 0 ) {
            local_attack_data.cooldown--;
        }

        if( local_attack_data.cooldown == 0 && !pacified && !is_hallucination() ) {
            if( !sp_type.second->call( *this ) ) {
                continue;
            }

            // `special_attacks` might have changed at this point. Sadly `reset_special`
            // doesn't check the attack name, so we need to do it here.
            if( special_attacks.count( special_name ) == 0 ) {
                continue;
            }
            reset_special( special_name );
        }
    }

    // The monster can sometimes hang in air due to last fall being blocked
    const bool can_fly = has_flag( MF_FLIES );
    if( !can_fly && g->m.has_flag( TFLAG_NO_FLOOR, pos() ) ) {
        g->m.creature_on_trap( *this, false );
    }

    if( moves < 0 ) {
        return;
    }

    // TODO: Move this to attack_at/move_to/etc. functions
    bool attacking = false;
    if( !move_effects( attacking ) ) {
        moves = 0;
        return;
    }
    if( has_flag( MF_IMMOBILE ) ) {
        moves = 0;
        return;
    }
    if( has_effect( effect_stunned ) ) {
        stumble();
        moves = 0;
        return;
    }
    if( friendly > 0 ) {
        --friendly;
    }

    // Set attitude to attitude to our current target
    monster_attitude current_attitude = attitude( nullptr );
    if( !wander() ) {
        if( goal == g->u.pos() ) {
            current_attitude = attitude( &( g->u ) );
        } else {
            for( auto &i : g->active_npc ) {
                if( goal == i->pos() ) {
                    current_attitude = attitude( i );
                }
            }
        }
    }

    if( current_attitude == MATT_IGNORE ||
        ( current_attitude == MATT_FOLLOW && rl_dist( pos(), goal ) <= MONSTER_FOLLOW_DIST ) ) {
        moves -= 100;
        stumble();
        return;
    }

    bool moved = false;
    tripoint destination;

    // If true, don't try to greedily avoid locally bad paths
    bool pathed = false;
    if( !wander() ) {
        while( !path.empty() && path.front() == pos() ) {
            path.erase( path.begin() );
        }

        const auto &pf_settings = get_pathfinding_settings();
        if( pf_settings.max_dist >= rl_dist( pos(), goal ) &&
            ( path.empty() || rl_dist( pos(), path.front() ) >= 2 || path.back() != goal ) ) {
            // We need a new path
            path = g->m.route( pos(), goal, pf_settings, get_path_avoid() );
        }

        // Try to respect old paths, even if we can't pathfind at the moment
        if( !path.empty() && path.back() == goal ) {
            destination = path.front();
            moved = true;
            pathed = true;
        } else {
            // Straight line forward, probably because we can't pathfind (well enough)
            destination = goal;
            moved = true;
        }
    }
    if( !moved && 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.
        unset_dest();
        tripoint tmp = scent_move();
        if( tmp.x != -1 ) {
            destination = tmp;
            moved = true;
        }
    }
    if( wandf > 0 && !moved ) { // No LOS, no scent, so as a fall-back follow sound
        unset_dest();
        if( wander_pos != pos() ) {
            destination = wander_pos;
            moved = true;
        }
    }

    if( !g->m.has_zlevels() ) {
        // Otherwise weird things happen
        destination.z = posz();
    }

    tripoint next_step;
    const bool staggers = has_flag( MF_STUMBLES );
    if( moved ) {
        // Implement both avoiding obstacles and staggering.
        moved = false;
        float switch_chance = 0.0;
        const bool can_bash = bash_skill() > 0;
        // This is a float and using trig_dist() because that Does the Right Thing(tm)
        // in both circular and roguelike distance modes.
        const float distance_to_target = trig_dist( pos(), destination );
        for( const tripoint &candidate : squares_closer_to( pos(), destination ) ) {
            if( candidate.z != posz() ) {
                bool can_z_move = true;
                if( !g->m.valid_move( pos(), candidate, false, true ) ) {
                    // Can't phase through floor
                    can_z_move = false;
                }

                if( can_z_move && !can_fly && candidate.z > posz() && !g->m.has_floor_or_support( candidate ) ) {
                    // Can't "jump" up a whole z-level
                    can_z_move = false;
                }

                // Last chance - we can still do the z-level stair teleport bullshit that isn't removed yet
                // @todo Remove z-level stair bullshit teleport after aligning all stairs
                if( !can_z_move &&
                    posx() / ( SEEX * 2 ) == candidate.x / ( SEEX * 2 ) &&
                    posy() / ( SEEY * 2 ) == candidate.y / ( SEEY * 2 ) ) {
                    const tripoint &upper = candidate.z > posz() ? candidate : pos();
                    const tripoint &lower = candidate.z > posz() ? pos() : candidate;
                    if( g->m.has_flag( TFLAG_GOES_DOWN, upper ) && g->m.has_flag( TFLAG_GOES_UP, lower ) ) {
                        can_z_move = true;
                    }
                }

                if( !can_z_move ) {
                    continue;
                }
            }

            // A flag to allow non-stumbling critters to stumble when the most direct choice is bad.
            bool bad_choice = false;

            const Creature *target = g->critter_at( candidate, is_hallucination() );
            if( target != nullptr ) {
                const Creature::Attitude att = attitude_to( *target );
                if( att == A_HOSTILE ) {
                    // When attacking an adjacent enemy, we're direct.
                    moved = true;
                    next_step = candidate;
                    break;
                } else if( att == A_FRIENDLY && ( target->is_player() || target->is_npc() ) ) {
                    continue; // Friendly firing the player or an NPC is illegal for gameplay reasons
                } else if( !has_flag( MF_ATTACKMON ) && !has_flag( MF_PUSH_MON ) ) {
                    // Bail out if there's a non-hostile monster in the way and we're not pushy.
                    continue;
                }
                // Friendly fire and pushing are always bad choices - they take a lot of time
                bad_choice = true;
            }

            // Bail out if we can't move there and we can't bash.
            if( !pathed && !can_move_to( candidate ) ) {
                if( !can_bash ) {
                    continue;
                }

                const int estimate = g->m.bash_rating( bash_estimate(), candidate );
                if( estimate <= 0 ) {
                    continue;
                }

                if( estimate < 5 ) {
                    bad_choice = true;
                }
            }

            const float progress = distance_to_target - trig_dist( candidate, destination );
            // The x2 makes the first (and most direct) path twice as likely,
            // since the chance of switching is 1/1, 1/4, 1/6, 1/8
            switch_chance += progress * 2;
            // Randomly pick one of the viable squares to move to weighted by distance.
            if( moved == false || x_in_y( progress, switch_chance ) ) {
                moved = true;
                next_step = candidate;
                // If we stumble, pick a random square, otherwise take the first one,
                // which is the most direct path.
                // Except if the direct path is bad, then check others
                // Or if the path is given by pathfinder
                if( !staggers && ( !bad_choice || pathed ) ) {
                    break;
                }
            }
        }
    }
    // 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
        const bool did_something =
            ( !pacified && attack_at( next_step ) ) ||
            ( !pacified && bash_at( next_step ) ) ||
            ( !pacified && push_to( next_step, 0, 0 ) ) ||
            move_to( next_step, false, get_stagger_adjust( pos(), destination, next_step ) );

        if( !did_something ) {
            moves -= 100; // If we don't do this, we'll get infinite loops.
        }
    } else {
        moves -= 100;
        stumble();
        path.clear();
    }
}
コード例 #10
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;
    }
    int bashskill = bash_skill();
    if (xbest) {
        if (can_move_to(x, y) || (x == g->u.posx && y == g->u.posy) ||
              (has_flag(MF_BASHES) && g->m.bash_rating(bashskill, x, y) > 0)) {
            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.bash_rating(bashskill, x, y2) > 0)) {
            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.bash_rating(bashskill, x2, y) > 0)) {
            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.bash_rating(bashskill, x, y3) > 0)) {
            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.bash_rating(bashskill, x3, y) > 0)) {
            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.bash_rating(bashskill, x, y) > 0)) {
            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.bash_rating(bashskill, x2, y) > 0)) {
            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.bash_rating(bashskill, x, y2) > 0)) {
            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.bash_rating(bashskill, x3, y) > 0)) {
            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.bash_rating(bashskill, x, y3) > 0)) {
            next.x = x;
            next.y = y3;
        }
    }
    return next;
}
コード例 #11
0
// General movement.
// Currently, priority goes:
// 1) Special Attack
// 2) Sight-based tracking
// 3) Scent-based tracking
// 4) Sound-based tracking
void monster::move()
{
    // We decrement wandf no matter what.  We'll save our wander_to plans until
    // after we finish out set_dest plans, UNLESS they time out first.
    if (wandf > 0) {
        wandf--;
    }

    //Hallucinations have a chance of disappearing each turn
    if (is_hallucination() && one_in(25)) {
        die( nullptr );
        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)) {
                add_msg(m_warning, _("The %s is visibly regenerating!"), name().c_str());
            }
            hp += 50;
            if(hp > type->hp) {
                hp = type->hp;
            }
        }
    }
    if (has_flag(MF_REGENERATES_10)) {
        if (hp < type->hp) {
            if (one_in(2)) {
                add_msg(m_warning, _("The %s seems a little healthier."), name().c_str());
            }
            hp += 10;
            if(hp > type->hp) {
                hp = type->hp;
            }
        }
    }

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

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

    // If this critter dies in sunlight, check & assess damage.
    if (g->is_in_sunlight(posx(), posy()) && has_flag(MF_SUNDEATH)) {
        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)) &&
        !has_effect("pacified") ) {
        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 (friendly != 0) {
        if (friendly > 0) {
            friendly--;
        }
        friendly_move();
        return;
    }

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

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

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

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

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

    // If we're close to our target, we get focused and don't stumble
    if ((has_flag(MF_STUMBLES) && (plans.size() > 3 || plans.empty())) ||
          !moved) {
        stumble(moved);
    }
}