Beispiel #1
0
bool CRSFTelemetry::send_attitude()
{
	vehicle_attitude_s vehicle_attitude;

	if (orb_copy(ORB_ID(vehicle_attitude), _vehicle_attitude_sub, &vehicle_attitude) != 0) {
		return false;
	}

	matrix::Eulerf attitude = matrix::Quatf(vehicle_attitude.q);
	int16_t pitch = attitude(1) * 1e4f;
	int16_t roll = attitude(0) * 1e4f;
	int16_t yaw = attitude(2) * 1e4f;
	return crsf_send_telemetry_attitude(_uart_fd, pitch, roll, yaw);
}
Beispiel #2
0
Creature::Attitude monster::attitude_to( const Creature &other ) const
{
    const auto m = dynamic_cast<const monster *>( &other );
    const auto p = dynamic_cast<const player *>( &other );
    if( m != nullptr ) {
        if( friendly != 0 && friendly != 0 ) {
            // Currently friendly means "friendly to the player" (on same side as player),
            // so if both monsters are friendly (towards the player), they are friendly towards
            // each other.
            return A_FRIENDLY;
        }
        // For now monsters are neutral (not hostile!) to all other monsters.
        return A_NEUTRAL;
    } else if( p != nullptr ) {
        switch( attitude( const_cast<player *>( p ) ) ) {
            case MATT_FRIEND:
                return A_FRIENDLY;
            case MATT_FPASSIVE:
            case MATT_FLEE:
            case MATT_IGNORE:
            case MATT_FOLLOW:
            case MATT_ZLAVE:
                return A_NEUTRAL;
            case MATT_ATTACK:
                return A_HOSTILE;
            case MATT_NULL:
            case NUM_MONSTER_ATTITUDES:
                break;
        }
    }
    // Should not happen!, creature should be either player or monster
    return A_NEUTRAL;
}
Beispiel #3
0
PyObject *PyLink::getRotation()
{
    boost::python::list retval;
    const hrp::Matrix33 R = attitude();
    Matrix33ToPyList(R, retval);
    return boost::python::incref(retval.ptr());
}
Beispiel #4
0
bool monster::can_move_to(game *g, int x, int y)
{
    if (g->m.move_cost(x, y) == 0 &&
     (!has_flag(MF_DESTROYS) || !g->m.is_destructable(x, y)) &&
     ((!has_flag(MF_AQUATIC) && !has_flag(MF_SWIMS)) ||
      !g->m.has_flag(swimmable, x, y)))
        return false;
    if (has_flag(MF_DIGS) && !g->m.has_flag(diggable, x, y))
        return false;
    if (has_flag(MF_AQUATIC) && !g->m.has_flag(swimmable, x, y))
    return false;

    // various animal behaviours
    if (has_flag(MF_ANIMAL))
    {
        // don't enter sharp terrain unless tiny, or attacking
        if (g->m.has_flag(sharp, x, y) && !(attitude(&(g->u)) == MATT_ATTACK || type->size == MS_TINY))
            return false;

        // don't enter open pits ever unless tiny or can fly
        if (!(type->size == MS_TINY || has_flag(MF_FLIES)) && (g->m.ter(x, y) == t_pit || g->m.ter(x, y) == t_pit_spiked))
            return false;

        // don't enter lava ever
        if (g->m.ter(x, y) == t_lava)
            return false;

        // don't enter fire or electricity ever
        if (g->m.field_at(x, y).findField(fd_fire) || g->m.field_at(x, y).findField(fd_electricity))
            return false;
    }
    return true;
}
Beispiel #5
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;
}
void monster::get_Attitude(nc_color &color, std::string &text)
{
    switch (attitude(&(g->u))) {
        case MATT_FRIEND:
            color = h_white;
            text = _("Friendly ");
            break;
        case MATT_FPASSIVE:
            color = h_white;
            text = _("Passive ");
            break;
        case MATT_FLEE:
            color = c_green;
            text = _("Fleeing! ");
            break;
        case MATT_IGNORE:
            color = c_ltgray;
            text = _("Ignoring ");
            break;
        case MATT_FOLLOW:
            color = c_yellow;
            text = _("Tracking ");
            break;
        case MATT_ATTACK:
            color = c_red;
            text = _("Hostile! ");
            break;
        default:
            color = h_red;
            text = "BUG: Behavior unnamed. (monster.cpp:get_Attitude)";
            break;
    }
}
Beispiel #7
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(game *g, 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;

 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;
}
Beispiel #8
0
/*
Runs the kinematics model on the state vector and returns a vector with the
derivative of each components (except for the accelerations, which must be
calculated directly using a dynamics model).
Contents are as follows:
    - Rate of change in position (3-vector, m/s, NED frame)
    - Rate of change in linear velocity (3-vector, m/s^2, NED frame)
    - Rate of change in attitude (quaternion (x, y, z, w), 1/s, body frame)
    - Rate of change in angular velocity (3-vector, rad/s^2, body frame)
*/
const StateVectorDerivative State::model(ControlVector c, DynamicsModel *d) {
    StateVectorDerivative output;

    AccelerationVector a = d->evaluate(*this, c);

    /* Calculate change in position. */
    output.segment<3>(0) << velocity();

    /* Calculate change in velocity. */
    Quaternionr attitude_q = Quaternionr(attitude());
    output.segment<3>(3) = attitude_q.conjugate() * a.segment<3>(0);

    /* Calculate change in attitude. */
    Eigen::Matrix<real_t, 4, 1> omega_q;
    omega_q << angular_velocity(), 0;

    attitude_q = Quaternionr(omega_q).conjugate() * attitude_q;
    output.segment<4>(6) << attitude_q.vec(), attitude_q.w();
    output.segment<4>(6) *= 0.5;

    /* Calculate change in angular velocity (just angular acceleration). */
    output.segment<3>(10) = a.segment<3>(3);

    return output;
}
bool monster::is_fleeing(player &u)
{
 if (has_effect("run"))
  return true;
 monster_attitude att = attitude(&u);
 return (att == MATT_FLEE ||
         (att == MATT_FOLLOW && rl_dist(_posx, _posy, u.posx, u.posy) <= 4));
}
Beispiel #10
0
bool monster::can_move_to( const tripoint &p ) const
{
    const bool can_climb = has_flag( MF_CLIMBS ) || has_flag( MF_FLIES );
    if( g->m.move_cost( p ) == 0 && !( can_climb && g->m.has_flag( "CLIMBABLE", p ) ) ) {
        return false;
    }

    if( !can_submerge() && g->m.has_flag( TFLAG_DEEP_WATER, p ) ) {
        return false;
    }
    if( has_flag( MF_DIGS ) && !g->m.has_flag( "DIGGABLE", p ) ) {
        return false;
    }
    if( has_flag( MF_AQUATIC ) && !g->m.has_flag( "SWIMMABLE", p ) ) {
        return false;
    }

    if( has_flag( MF_SUNDEATH ) && g->is_in_sunlight( p ) ) {
        return false;
    }

    // various animal behaviours
    if( has_flag( MF_ANIMAL ) ) {
        // don't enter sharp terrain unless tiny, or attacking
        if( g->m.has_flag( "SHARP", p ) && !( attitude( &( g->u ) ) == MATT_ATTACK || type->size == MS_TINY || has_flag( MF_FLIES )) ) {
            return false;
        }

        // Don't enter open pits ever unless tiny, can fly or climb well
        if( !( type->size == MS_TINY || can_climb ) &&
            ( g->m.ter( p ) == t_pit || g->m.ter( p ) == t_pit_spiked || g->m.ter( p ) == t_pit_glass ) ) {
            return false;
        }

        // don't enter lava ever
        if( g->m.ter( p ) == t_lava ) {
            return false;
        }

        // don't enter fire or electricity ever
        const field &local_field = g->m.field_at( p );
        if( local_field.findField( fd_fire ) || local_field.findField( fd_electricity ) ) {
            return false;
        }

        if( g->m.has_flag( TFLAG_NO_FLOOR, p ) && !has_flag( MF_FLIES ) ) {
            return false;
        }
    }

    return true;
}
Beispiel #11
0
mf_attitude monfaction::attitude( const mfaction_id &other ) const
{
    const auto &found = attitude_map.find( other );
    if( found != attitude_map.end() ) {
        return found->second;
    }

    const auto base = other.obj().base_faction;
    if( other != base ) {
        return attitude( base );
    }

    // Shouldn't happen
    debugmsg( "Invalid faction relations (no relation found): %s -> %s",
              id.c_str(), other.obj().id.c_str() );
    return MFA_FRIENDLY;
}
Beispiel #12
0
bool monster::can_move_to(int x, int y)
{
    if (g->m.move_cost(x, y) == 0 &&
            (!has_flag(MF_DESTROYS) || !g->m.is_destructable(x, y))) {
        return false;
    }
    if (!can_submerge() && g->m.has_flag(TFLAG_DEEP_WATER, x, y)) {
        return false;
    }
    if (has_flag(MF_DIGS) && !g->m.has_flag("DIGGABLE", x, y)) {
        return false;
    }
    if (has_flag(MF_AQUATIC) && !g->m.has_flag("SWIMMABLE", x, y)) {
        return false;
    }

    if (has_flag(MF_SUNDEATH) && g->is_in_sunlight(x, y)) {
        return false;
    }

    // various animal behaviours
    if (has_flag(MF_ANIMAL))
    {
        // don't enter sharp terrain unless tiny, or attacking
        if (g->m.has_flag("SHARP", x, y) && !(attitude(&(g->u)) == MATT_ATTACK ||
                                              type->size == MS_TINY))
            return false;

        // don't enter open pits ever unless tiny or can fly
        if (!(type->size == MS_TINY || has_flag(MF_FLIES)) &&
                (g->m.ter(x, y) == t_pit || g->m.ter(x, y) == t_pit_spiked))
            return false;

        // don't enter lava ever
        if (g->m.ter(x, y) == t_lava)
            return false;

        // don't enter fire or electricity ever
        field &local_field = g->m.field_at(x, y);
        if (local_field.findField(fd_fire) || local_field.findField(fd_electricity))
            return false;
    }
    return true;
}
Beispiel #13
0
Creature::Attitude monster::attitude_to( const Creature &other ) const
{
    const auto m = dynamic_cast<const monster *>( &other );
    const auto p = dynamic_cast<const player *>( &other );
    if( m != nullptr ) {
        if( m == this ) {
            return A_FRIENDLY;
        }
        auto faction_att = faction->attitude( m->faction );
        if( ( friendly != 0 && m->friendly != 0 ) ||
            ( friendly == 0 && m->friendly == 0 && faction_att == MFA_FRIENDLY ) ) {
            // Friendly (to player) monsters are friendly to each other
            // Unfriendly monsters go by faction attitude
            return A_FRIENDLY;
        } else if( ( friendly == 0 && m->friendly == 0 && faction_att == MFA_NEUTRAL ) || 
                     morale < 0 || anger < 10 ) {
            // Stuff that won't attack is neutral to everything
            return A_NEUTRAL;
        } else {
            return A_HOSTILE;
        }
    } else if( p != nullptr ) {
        switch( attitude( const_cast<player *>( p ) ) ) {
            case MATT_FRIEND:
                return A_FRIENDLY;
            case MATT_FPASSIVE:
            case MATT_FLEE:
            case MATT_IGNORE:
            case MATT_FOLLOW:
            case MATT_ZLAVE:
                return A_NEUTRAL;
            case MATT_ATTACK:
                return A_HOSTILE;
            case MATT_NULL:
            case NUM_MONSTER_ATTITUDES:
                break;
        }
    }
    // Should not happen!, creature should be either player or monster
    return A_NEUTRAL;
}
/* 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;
}
Beispiel #15
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;
}
Beispiel #16
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;
    }

    //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();
    }
}
Beispiel #17
0
void monster::plan(const mfactions &factions)
{
    // Bots are more intelligent than most living stuff
    bool electronic = has_flag( MF_ELECTRONIC );
    Creature *target = nullptr;
    // 8.6f is rating for tank drone 60 tiles away, moose 16 or boomer 33
    float dist = !electronic ? 1000 : 8.6f;
    int bresenham_slope = 0;
    int selected_slope = 0;
    bool fleeing = false;
    bool docile = has_flag( MF_VERMIN ) || ( friendly != 0 && has_effect( "docile" ) );
    bool angers_hostile_weak = type->anger.find( MTRIG_HOSTILE_WEAK ) != type->anger.end();
    int angers_hostile_near = ( type->anger.find( MTRIG_HOSTILE_CLOSE ) != type->anger.end() ) ? 5 : 0;
    int fears_hostile_near = ( type->fear.find( MTRIG_HOSTILE_CLOSE ) != type->fear.end() ) ? 5 : 0;
    bool group_morale = has_flag( MF_GROUP_MORALE ) && morale < type->morale;
    bool swarms = has_flag( MF_SWARMS );
    auto mood = attitude();

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

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

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

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

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

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

        point dest = target->pos();
        auto att_to_target = attitude_to( *target );
        if( att_to_target == Attitude::A_HOSTILE && !fleeing ) {
            set_dest( dest.x, dest.y, selected_slope );
        } else if( fleeing ) {
            set_dest( posx() * 2 - dest.x, posy() * 2 - dest.y, selected_slope );
        }
        if( angers_hostile_weak && att_to_target != Attitude::A_FRIENDLY ) {
            int hp_per = target->hp_percentage();
            if( hp_per <= 70 ) {
                anger += 10 - int( hp_per / 10 );
            }
        }
    } else if( friendly > 0 && one_in(3)) {
            // Grow restless with no targets
            friendly--;
    } else if( friendly < 0 && sees( g->u, bresenham_slope ) ) {
        if( rl_dist( pos(), g->u.pos() ) > 2 ) {
            set_dest(g->u.posx(), g->u.posy(), bresenham_slope);
        } else {
            plans.clear();
        }
    }
    // If we're not adjacent to the start of our plan path, don't act on it.
    // This is to catch when we had pre-existing invalid plans and
    // made it through the function without changing them.
    if( !plans.empty() && square_dist(pos().x, pos().y,
                                      plans.front().x, plans.front().y ) > 1 ) {
        plans.clear();
    }
}
Beispiel #18
0
void monster::print_info(game *g, WINDOW* w)
{
// First line of w is the border; the next two are terrain info, and after that
// is a blank line. w is 13 characters tall, and we can't use the last one
// because it's a border as well; so we have lines 4 through 11.
// w is also 48 characters wide - 2 characters for border = 46 characters for us
 mvwprintz(w, 6, 1, c_white, "%s ", type->name.c_str());
 switch (attitude(&(g->u))) {
  case MATT_FRIEND:
   wprintz(w, h_white, "Friendly! ");
   break;
  case MATT_FLEE:
   wprintz(w, c_green, "Fleeing! ");
   break;
  case MATT_IGNORE:
   wprintz(w, c_ltgray, "Ignoring ");
   break;
  case MATT_FOLLOW:
   wprintz(w, c_yellow, "Tracking ");
   break;
  case MATT_ATTACK:
   wprintz(w, c_red, "Hostile! ");
   break;
  default:
   wprintz(w, h_red, "BUG: Behavior unnamed ");
   break;
 }
 if (has_effect(ME_DOWNED))
  wprintz(w, h_white, "On ground");
 else if (has_effect(ME_STUNNED))
  wprintz(w, h_white, "Stunned");
 else if (has_effect(ME_BEARTRAP))
  wprintz(w, h_white, "Trapped");
 std::string damage_info;
 nc_color col;
 if (hp == type->hp) {
  damage_info = "It is uninjured";
  col = c_green;
 } else if (hp >= type->hp * .8) {
  damage_info = "It is lightly injured";
  col = c_ltgreen;
 } else if (hp >= type->hp * .6) {
  damage_info = "It is moderately injured";
  col = c_yellow;
 } else if (hp >= type->hp * .3) {
  damage_info = "It is heavily injured";
  col = c_yellow;
 } else if (hp >= type->hp * .1) {
  damage_info = "It is severly injured";
  col = c_ltred;
 } else {
  damage_info = "it is nearly dead";
  col = c_red;
 }
 mvwprintz(w, 7, 1, col, damage_info.c_str());

 std::string tmp = type->description;
 std::string out;
 size_t pos;
 int line = 8;
 do {
  pos = tmp.find_first_of('\n');
  out = tmp.substr(0, pos);
  mvwprintz(w, line, 1, c_white, out.c_str());
  tmp = tmp.substr(pos + 1);
  line++;
 } while (pos != std::string::npos && line < 12);
}
Beispiel #19
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);
    }
}
Beispiel #20
0
void monster::plan(game *g)
{
 int sightrange = g->light_level();
 int closest = -1;
 int dist = 1000;
 int tc, stc;
 bool fleeing = false;
 if (friendly != 0) {	// Target monsters, not the player!
  for (int i = 0; i < g->z.size(); i++) {
   monster *tmp = &(g->z[i]);
   if (tmp->friendly == 0 && rl_dist(posx, posy, tmp->posx, tmp->posy) < dist &&
       g->m.sees(posx, posy, tmp->posx, tmp->posy, sightrange, tc)) {
    closest = i;
    dist = rl_dist(posx, posy, tmp->posx, tmp->posy);
    stc = tc;
   }
  }
  if (has_effect(ME_DOCILE))
   closest = -1;
  if (closest >= 0)
   set_dest(g->z[closest].posx, g->z[closest].posy, stc);
  else if (friendly > 0 && one_in(3))	// Grow restless with no targets
   friendly--;
  else if (friendly < 0 && g->sees_u(posx, posy, tc)) {
   if (rl_dist(posx, posy, g->u.posx, g->u.posy) > 2)
    set_dest(g->u.posx, g->u.posy, tc);
   else
    plans.clear();
  }
  return;
 }
 if (is_fleeing(g->u) && can_see() && g->sees_u(posx, posy, tc)) {
  fleeing = true;
  wandx = posx * 2 - g->u.posx;
  wandy = posy * 2 - g->u.posy;
  wandf = 40;
  dist = rl_dist(posx, posy, g->u.posx, g->u.posy);
 }
// If we can see, and we can see a character, start moving towards them
 if (!is_fleeing(g->u) && can_see() && g->sees_u(posx, posy, tc)) {
  dist = rl_dist(posx, posy, g->u.posx, g->u.posy);
  closest = -2;
  stc = tc;
 }
 for (int i = 0; i < g->active_npc.size(); i++) {
  npc *me = (g->active_npc[i]);
  int medist = rl_dist(posx, posy, me->posx, me->posy);
  if ((medist < dist || (!fleeing && is_fleeing(*me))) &&
      (can_see() &&
       g->m.sees(posx, posy, me->posx, me->posy, sightrange, tc))) {
   if (is_fleeing(*me)) {
    fleeing = true;
    wandx = posx * 2 - me->posx;
    wandy = posy * 2 - me->posy;
    wandf = 40;
    dist = medist;
   } else if (can_see() &&
              g->m.sees(posx, posy, me->posx, me->posy, sightrange, tc)) {
    dist = rl_dist(posx, posy, me->posx, me->posy);
    closest = i;
    stc = tc;
   }
  }
 }
 if (!fleeing) {
  fleeing = attitude() == MATT_FLEE;
  for (int i = 0; i < g->z.size(); i++) {
   monster *mon = &(g->z[i]);
   int mondist = rl_dist(posx, posy, mon->posx, mon->posy);
   if (mon->friendly != 0 && mondist < dist && can_see() &&
       g->m.sees(posx, posy, mon->posx, mon->posy, sightrange, tc)) {
    dist = mondist;
    if (fleeing) {
     wandx = posx * 2 - mon->posx;
     wandy = posy * 2 - mon->posy;
     wandf = 40;
    } else {
     closest = -3 - i;
     stc = tc;
    }
   }
  }
 }
 if (!fleeing) {
  if (closest == -2)
   set_dest(g->u.posx, g->u.posy, stc);
  else if (closest <= -3)
   set_dest(g->z[-3 - closest].posx, g->z[-3 - closest].posy, stc);
  else if (closest >= 0)
   set_dest(g->active_npc[closest]->posx, g->active_npc[closest]->posy, stc);
 }
}
Beispiel #21
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);
    }
}
Beispiel #22
0
void camera::update_view_matrix() const {
  view = attitude() * glm::translate(glm::mat4(), -position);
  view_dirty = false;
}
Beispiel #23
0
bool monster::can_move_to( const tripoint &p ) const
{
    const bool can_climb = has_flag( MF_CLIMBS ) || has_flag( MF_FLIES );
    if( g->m.impassable( p ) && !( can_climb && g->m.has_flag( "CLIMBABLE", p ) ) ) {
        return false;
    }

    if( !can_submerge() && g->m.has_flag( TFLAG_DEEP_WATER, p ) ) {
        return false;
    }
    if( has_flag( MF_DIGS ) && !g->m.has_flag( "DIGGABLE", p ) ) {
        return false;
    }
    if( has_flag( MF_AQUATIC ) && !g->m.has_flag( "SWIMMABLE", p ) ) {
        return false;
    }

    if( has_flag( MF_SUNDEATH ) && g->is_in_sunlight( p ) ) {
        return false;
    }

    // Various avoiding behaviors
    if( has_flag( MF_AVOID_DANGER_1 ) || has_flag( MF_AVOID_DANGER_2 ) ) {
        const ter_id target = g->m.ter( p );
        // Don't enter lava ever
        if( target == t_lava ) {
            return false;
        }
        // Don't ever throw ourselves off cliffs
        if( !g->m.has_floor( p ) && !has_flag( MF_FLIES ) ) {
            return false;
        }

        // Don't enter open pits ever unless tiny, can fly or climb well
        if( !( type->size == MS_TINY || can_climb ) &&
            ( target == t_pit || target == t_pit_spiked || target == t_pit_glass ) ) {
            return false;
        }

        // The following behaviors are overridden when attacking
        if( attitude( &( g->u ) ) != MATT_ATTACK ) {
            if( g->m.has_flag( "SHARP", p ) &&
                !( type->size == MS_TINY || has_flag( MF_FLIES ) ) ) {
                return false;
            }
        }

        const field &target_field = g->m.field_at( p );

        // Differently handled behaviors
        if( has_flag( MF_AVOID_DANGER_2 ) ) {
            const trap &target_trap = g->m.tr_at( p );
            // Don't enter any dangerous fields
            if( is_dangerous_fields( target_field ) ) {
                return false;
            }
            // Don't step on any traps (if we can see)
            if( has_flag( MF_SEES ) && !target_trap.is_benign() && g->m.has_floor( p ) ) {
                return false;
            }
        } else if( has_flag( MF_AVOID_DANGER_1 ) ) {
            // Don't enter fire or electricity ever (other dangerous fields are fine though)
            if( target_field.findField( fd_fire ) || target_field.findField( fd_electricity ) ) {
                return false;
            }
        }
    }

    return true;
}
Beispiel #24
0
glm::vec3 camera::right() const {
  glm::vec4 right = glm::inverse(attitude()) * glm::vec4(1, 0, 0, 1);
  return glm::vec3(right);
}
Beispiel #25
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;
    }

    //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 ) ) {
        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());
            for( auto &elem : g->m.i_at(posx(), posy()) ) {
                hp += elem.volume(); // Yeah this means it can get more HP than normal.
            }
            g->m.i_clear(posx(), posy());
        }
    }

    // First, use the special attack, if we can!
    for (size_t i = 0; i < sp_timeout.size(); ++i) {
        if (sp_timeout[i] > 0) {
            sp_timeout[i]--;
        }

        if( sp_timeout[i] == 0 && !has_effect("pacified") && !is_hallucination() ) {
            type->sp_attack[i](this, i);
        }
    }
    if (moves < 0) {
        return;
    }
    if (!move_effects()) {
        moves = 0;
        return;
    }
    if (has_flag(MF_IMMOBILE)) {
        moves = 0;
        return;
    }
    if (has_effect("stunned")) {
        stumble(false);
        moves = 0;
        return;
    }
    if (friendly != 0) {
        if (friendly > 0) {
            friendly--;
        }
        friendly_move();
        return;
    }

    bool moved = false;
    point next;

    // Set attitude to attitude to our current target
    monster_attitude current_attitude = attitude( nullptr );
    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;
    }

    int mondex = !plans.empty() ? g->mon_at( plans[0].x, plans[0].y ) : -1;
    auto mon_att = mondex != -1 ? attitude_to( g->zombie( mondex ) ) : A_HOSTILE;

    if( !plans.empty() &&
        ( mon_att == A_HOSTILE || 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 ) || has_flag( MF_BORES ) ) &&
          g->m.bash_rating( bash_estimate(), 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);
    }
}
Beispiel #26
0
int monster::print_info(game *g, WINDOW* w, int vStart, int vLines, int column)
{
// First line of w is the border; the next two are terrain info, and after that
// is a blank line. w is 13 characters tall, and we can't use the last one
// because it's a border as well; so we have lines 4 through 11.
// w is also 48 characters wide - 2 characters for border = 46 characters for us
// vStart added because 'help' text in targeting win makes helpful info hard to find
// at a glance.

 const int vEnd = vStart + vLines;

 mvwprintz(w, vStart++, column, c_white, "%s ", type->name.c_str());
 switch (attitude(&(g->u))) {
  case MATT_FRIEND:
   wprintz(w, h_white, _("Friendly! "));
   break;
  case MATT_FLEE:
   wprintz(w, c_green, _("Fleeing! "));
   break;
  case MATT_IGNORE:
   wprintz(w, c_ltgray, _("Ignoring "));
   break;
  case MATT_FOLLOW:
   wprintz(w, c_yellow, _("Tracking "));
   break;
  case MATT_ATTACK:
   wprintz(w, c_red, _("Hostile! "));
   break;
  default:
   wprintz(w, h_red, "BUG: Behavior unnamed. (monster.cpp:print_info)");
   break;
 }
 if (has_effect(ME_DOWNED))
  wprintz(w, h_white, _("On ground"));
 else if (has_effect(ME_STUNNED))
  wprintz(w, h_white, _("Stunned"));
 else if (has_effect(ME_BEARTRAP))
  wprintz(w, h_white, _("Trapped"));
 std::string damage_info;
 nc_color col;
 if (hp >= type->hp) {
  damage_info = _("It is uninjured");
  col = c_green;
 } else if (hp >= type->hp * .8) {
  damage_info = _("It is lightly injured");
  col = c_ltgreen;
 } else if (hp >= type->hp * .6) {
  damage_info = _("It is moderately injured");
  col = c_yellow;
 } else if (hp >= type->hp * .3) {
  damage_info = _("It is heavily injured");
  col = c_yellow;
 } else if (hp >= type->hp * .1) {
  damage_info = _("It is severly injured");
  col = c_ltred;
 } else {
  damage_info = _("it is nearly dead");
  col = c_red;
 }
 mvwprintz(w, vStart++, column, col, damage_info.c_str());

    std::vector<std::string> lines = foldstring(type->description, getmaxx(w) - 1 - column);
    int numlines = lines.size();
    for (int i = 0; i < numlines && vStart <= vEnd; i++)
        mvwprintz(w, vStart++, column, c_white, lines[i].c_str());

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

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

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

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

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

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

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

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

// If we're close to our target, we get focused and don't stumble
 if ((has_flag(MF_STUMBLES) && (plans.size() > 3 || plans.size() == 0)) ||
     !moved)
  stumble(g, moved);
}
Beispiel #28
0
glm::vec3 camera::forward() const {
  glm::vec4 forward = glm::inverse(attitude()) * glm::vec4(0, 0, -1, 1);
  return glm::vec3(forward);
}
Beispiel #29
0
// General movement.
// Currently, priority goes:
// 1) Special Attack
// 2) Sight-based tracking
// 3) Scent-based tracking
// 4) Sound-based tracking
void monster::move(game *g)
{
// We decrement wandf no matter what.  We'll save our wander_to plans until
// after we finish out set_dest plans, UNLESS they time out first.
 if (wandf > 0)
  wandf--;

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

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

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

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

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

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

// If we're close to our target, we get focused and don't stumble
 if ((has_flag(MF_STUMBLES) && (plans.size() > 3 || plans.size() == 0)) ||
     !moved)
  stumble(g, moved);
}
Beispiel #30
0
glm::vec3 camera::up() const {
  glm::vec4 up = glm::inverse(attitude()) * glm::vec4(0, 1, 0, 1);
  return glm::vec3(up);
}