コード例 #1
0
ファイル: SpaceMongolsUnitAi.cpp プロジェクト: streibeb/cs409
void UnitAiMoonGuard :: run (const WorldInterface& world)
{
    assert(world.isAlive(getShipId()));

    scan(world);

    Vector3 v = getShip().getVelocity();
    if (nearestEnemyShip != PhysicsObjectId::ID_NOTHING)
    {
        v = chargeAtTarget(world, nearestEnemyShip, v);
        shootAtShip(world, nearestEnemyShip);
    }
    else
    {
        Vector3 moon_pos = world.getPosition(moon);
        double moon_radius = world.getRadius(moon);
        v = steeringBehaviour->patrolSphere(world,
                                            moon_pos,
                                            moon_radius,
                                            PLANETOID_AVOID_DISTANCE);
        //printf("Patrolling...\n");
    }
    v = avoidShips(world, v);
    v = avoidRingParticles(world, v);

    getShipAi().setDesiredVelocity(v);
}
コード例 #2
0
Vector3 UnitAiMoonGuard::avoidShips(const WorldInterface& world, const Vector3& originalVelocity)
{
    Vector3 agentPosition = getShip().getPosition();
    vector<PhysicsObjectId> shipIDs = world.getShipIds(agentPosition, SAFE_DISTANCE);
    unsigned int amount = shipIDs.size();

    if (amount == 1) {
        return originalVelocity;
    }

    PhysicsObjectId closestID = PhysicsObjectId::ID_NOTHING;
    double closestDistance = -1;
    PhysicsObjectId agentID = getShip().getId();
    for (unsigned int i = 0; i < amount; i++) {
        PhysicsObjectId shipID = shipIDs[i];
        if (agentID != shipID) {
            Vector3 shipPosition = world.getPosition(shipIDs[i]);
            double distance = agentPosition.getDistanceSquared(shipPosition);
            if (closestDistance < 0) {
                closestDistance = distance;
                closestID = shipIDs[i];
            }
            else if (distance <= closestDistance) {
                closestDistance = distance;
                closestID = shipIDs[i];
            }
        }
    }
    if (closestDistance == -1) {
        return originalVelocity;
    }
    return 	m_steering_behaviour.evade(world, closestID);
}
コード例 #3
0
void UnitAiMoonGuard::chooseTarget(const WorldInterface& world)
{
    const Vector3& ship_position = getShip().getPosition();
    const Vector3& ship_forward = getShip().getForward();
    unsigned int   ship_fleet = getShipId().m_fleet;

    vector<PhysicsObjectId> v_ids = world.getShipIds(ship_position, SCAN_RADIUS);

    m_id_target = PhysicsObjectId::ID_NOTHING;
    double best_angle = VERY_LARGE_ANGLE;

    for (unsigned int i = 0; i < v_ids.size(); i++)
    {
        PhysicsObjectId other_id = v_ids[i];

        if (other_id.m_fleet != ship_fleet)
        {
            // we found an enemy ship

            Vector3 other_position = world.getPosition(other_id);
            Vector3 direction_to_other = other_position - ship_position;
            double angle_to_other = direction_to_other.getAngleSafe(ship_forward);

            if (angle_to_other < best_angle)
            {
                m_id_target = other_id;
                best_angle = angle_to_other;
            }
        }
        // else ship is part of our fleet, so ignore it
    }
}
コード例 #4
0
Vector3 SteeringBehaviour :: escort (const WorldInterface& world,
                                     const PhysicsObjectId& id_target,
                                     const Vector3& offset)
{
	assert(id_target != PhysicsObjectId::ID_NOTHING);

	// no initialization needed
	m_steering_behaviour = ESCORT;

	//
	//  This is a variation on the arrive steering behaviour.
	//    First we work out the desired position from the
	//    offset.  Then we arrive at that position.  Finally we
	//    add on the velocity of the target and truncate to our
	//    maximum speed.
	//
	//  A more sophisticated implementation would use a form of
	//    arrive based on pursue instead of seek.  Or maybe this
	//    already happens; I'm not sure.  In either case,
	//    slowing down for arrival messes up time predictions.
	//

	if(!world.isAlive(m_id_agent) ||
	   !world.isAlive(  id_target))
	{
		assert(invariant());
		return Vector3::ZERO;
	}

	assert(world.isAlive(m_id_agent ));
	assert(world.isAlive(  id_target));
	Vector3 agent_position  = world.getPosition(m_id_agent);
	Vector3 target_velocity = world.getVelocity(  id_target);
	Vector3 escort_position = calculateEscortPosition(world, id_target, offset);

	if(agent_position == escort_position)
	{
		// we are in the correct position, so just match speeds
		assert(invariant());
		return target_velocity;
	}

	double agent_speed_max    = world.getShipSpeedMax(m_id_agent);
	assert(agent_speed_max >= 0.0);
	double agent_acceleration = world.getShipSpeedMax(m_id_agent);
	assert(agent_acceleration >= 0.0);

	double current_distance = agent_position.getDistance(escort_position);

	double  desired_relative_speed    = calculateMaxSafeSpeed(current_distance, agent_acceleration);
	Vector3 desired_relative_velocity = (escort_position - agent_position).getCopyWithNorm(desired_relative_speed);
	Vector3 desired_agent_velocity    = desired_relative_velocity + target_velocity;

	// limit desired velocity to maximum speed
	assert(invariant());
	return desired_agent_velocity.getTruncated(agent_speed_max);
}
コード例 #5
0
Vector3 SteeringBehaviour :: evade (const WorldInterface& world,
                                    const PhysicsObjectId& id_target)
{
	assert(id_target != PhysicsObjectId::ID_NOTHING);

	// no initialization needed
	m_steering_behaviour = EVADE;

	if(!world.isAlive(m_id_agent) ||
	   !world.isAlive(  id_target))
	{
		assert(invariant());
		return Vector3::ZERO;
	}

	Vector3 agent_position  = world.getPosition(m_id_agent);
	Vector3 target_position = world.getPosition(  id_target);

	if(agent_position == target_position)
	{
		assert(invariant());
		return Vector3::ZERO;
	}

	assert(world.isAlive(m_id_agent ));
	assert(world.isAlive(  id_target));
	Vector3 target_velocity   = world.getVelocity(id_target);
	double  agent_speed_max   = world.getShipSpeedMax(m_id_agent);
	assert(agent_speed_max >= 0.0);
	Vector3 aim_direction = getAimDirection(agent_position,
	                                        agent_speed_max,
	                                        target_position,
	                                        target_velocity);
	if(aim_direction.isZero())
	{
		assert(invariant());
		return (agent_position - target_position).getCopyWithNorm(agent_speed_max);
	}
	else
	{
		assert(invariant());
		return aim_direction * -agent_speed_max;
	}
}
コード例 #6
0
Vector3 UnitAiMoonGuard::avoidPlanets(const WorldInterface& world, const Vector3& originalVelocity)
{
    PhysicsObjectId planetoid_id = world.getNearestPlanetoidId(getShip().getPosition());
    Vector3         planetoid_center = world.getPosition(planetoid_id);
    double          planetoid_radius = world.getRadius(planetoid_id);
    return m_steering_behaviour.avoid(world,
                                      originalVelocity,
                                      planetoid_center,
                                      planetoid_radius,
                                      PLANETOID_CLEARANCE,
                                      PLANETOID_AVOID_DISTANCE);
}
コード例 #7
0
void UnitAiMoonGuard::patrolMoon(const WorldInterface& world, const PhysicsObjectId& moonID)
{
    Vector3         planetoid_center = world.getPosition(moonID);
    double          planetoid_radius = world.getRadius(moonID);
    Vector3 desired_velocity = m_steering_behaviour.patrolSphere(world, planetoid_center,
                               planetoid_radius + PLANETOID_CLEARANCE,
                               planetoid_radius + Const::SHELL_THICKNESS);
    Vector3 avoid_velocity = avoidPlanets(world, desired_velocity);
    avoid_velocity = avoidParticles(world, avoid_velocity);
    avoid_velocity = avoidShips(world, avoid_velocity);
    getShipAi().setDesiredVelocity(avoid_velocity);
}
コード例 #8
0
ファイル: SpaceMongolsUnitAi.cpp プロジェクト: streibeb/cs409
Vector3 UnitAiMoonGuard::avoidPlanetoids(const WorldInterface &world, const Vector3& orig_velocity)
{
    Vector3 planetoid_pos = world.getPosition(nearestPlanetoid);
    double planetoid_radius = world.getRadius(nearestPlanetoid);

    Vector3 v = steeringBehaviour->avoid(world,
                                         orig_velocity,
                                         planetoid_pos,
                                         planetoid_radius,
                                         PLANETOID_CLEARANCE,
                                         PLANETOID_AVOID_DISTANCE);
    //printf("Avoiding Planetoid\n");
    return v;
}
コード例 #9
0
Vector3 SteeringBehaviour :: aim (const WorldInterface& world,
                                  const PhysicsObjectId& id_target,
                                  double shot_speed)
{
	assert(id_target != PhysicsObjectId::ID_NOTHING);
	assert(shot_speed >= 0.0);

	// no initialization needed
	m_steering_behaviour = AIM;

	if(!world.isAlive(m_id_agent) ||
	   !world.isAlive(  id_target))
	{
		assert(invariant());
		return Vector3::ZERO;
	}

	Vector3 agent_position  = world.getPosition(m_id_agent);
	Vector3 target_position = world.getPosition(  id_target);

	if(agent_position == target_position)
	{
		assert(invariant());
		return Vector3::ZERO;
	}

	assert(world.isAlive(m_id_agent ));
	assert(world.isAlive(  id_target));
	double  agent_speed_max = world.getShipSpeedMax(m_id_agent);
	assert(agent_speed_max >= 0.0);
	Vector3 target_velocity = world.getVelocity(id_target);
	Vector3   aim_direction = getAimDirection(agent_position, shot_speed, target_position, target_velocity);

	assert(invariant());
	return aim_direction * agent_speed_max;
}
コード例 #10
0
ファイル: SpaceMongolsUnitAi.cpp プロジェクト: streibeb/cs409
void UnitAiMoonGuard::shootAtShip(const WorldInterface& world, const PhysicsObjectId& target)
{
    if (!world.isAlive(target)) return;

    Vector3 target_pos = world.getPosition(target);
    Vector3 target_vel = world.getVelocity(target);

    Vector3 aim_dir = steeringBehaviour->getAimDirection(getShip().getPosition(),
                      Bullet::SPEED,
                      target_pos,
                      target_vel);
    double angle = getShip().getVelocity().getAngleSafe(aim_dir);
    if (angle <= SHOOT_ANGLE_RADIANS_MAX && getShip().isReloaded())
    {
        getShipAi().markFireBulletDesired();
        //printf("\tShooting at (%f, %f, %f)\n", target_pos.x, target_pos.y, target_pos.z);
    };
}
コード例 #11
0
Vector3 SteeringBehaviour :: flee (const WorldInterface& world,
                                   const PhysicsObjectId& id_target)
{
	assert(id_target != PhysicsObjectId::ID_NOTHING);

	// no initialization needed
	m_steering_behaviour = FLEE;

	if(!world.isAlive(id_target))
	{
		assert(invariant());
		return Vector3::ZERO;
	}

	assert(world.isAlive(id_target));
	Vector3 result = flee(world, world.getPosition(id_target));
	assert(invariant());
	return result;
}
コード例 #12
0
void UnitAiMoonGuard::steerToTargetAndShoot(const WorldInterface& world)
{
    assert(m_id_target != PhysicsObjectId::ID_NOTHING);
    assert(world.isAlive(m_id_target));

    Vector3 agent = getShip().getPosition();
    Vector3 agent_velocity = world.getVelocity(getShip().getId());

    Vector3 target = world.getPosition(m_id_target);
    Vector3 target_velocity = world.getVelocity(m_id_target);

    Vector3 desired_velocity;
    double agent_max_speed = world.getShipSpeedMax(getShip().getId());
    double fromAgentToTarget = agent.getAngle(target);
    double fromAgentToTargetFront = agent.getAngle(target + target_velocity);

    if (fromAgentToTargetFront < fromAgentToTarget) {
        desired_velocity = m_steering_behaviour.seek(world, m_id_target);
    }
    else {
        desired_velocity = m_steering_behaviour.getAimDirection(agent, Bullet::SPEED,
                           target, target_velocity);
        if (desired_velocity == Vector3::ZERO) {
            desired_velocity = m_steering_behaviour.pursue(world, m_id_target);
        }
        else {
            desired_velocity.setNorm(agent_max_speed);
        }
    }

    Vector3 avoid_velocity = avoidPlanets(world, desired_velocity);
    avoid_velocity = avoidParticles(world, avoid_velocity);
    avoid_velocity = avoidShips(world, avoid_velocity);
    getShipAi().setDesiredVelocity(avoid_velocity);

    double angle = agent_velocity.getAngle(desired_velocity);
    double distance = target.getDistanceSquared(agent);

    if (angle <= SHOOT_ANGLE_RADIANS_MAX && distance < FIRE_RANGE)
    {
        getShipAi().markFireBulletDesired();
    }
}
コード例 #13
0
Vector3 SteeringBehaviour :: explore (const WorldInterface& world,
                                      double distance_new_position_min,
                                      double distance_new_position_max)
{
	assert(distance_new_position_min >= 0.0);
	assert(distance_new_position_min <= distance_new_position_max);
	assert(distance_new_position_min >  EXPLORE_DISTANCE_NEW_POSITION);

	double position_distance           = (distance_new_position_max + distance_new_position_min) * 0.5;
	double position_distance_tolerance = (distance_new_position_max - distance_new_position_min) * 0.5;

	bool is_new_position_needed = false;
	if(m_steering_behaviour         != EXPLORE ||
	   m_desired_distance           != position_distance ||
	   m_desired_distance_tolerance != position_distance_tolerance)
	{
		m_steering_behaviour         = EXPLORE;
		m_desired_distance           = position_distance;
		m_desired_distance_tolerance = position_distance_tolerance;
		is_new_position_needed       = true;
	}

	if(!world.isAlive(m_id_agent))
	{
		// the explore position doesn't matter at this point

		assert(invariant());
		return Vector3::ZERO;
	}

	Vector3 agent_position = world.getPosition(m_id_agent);
	double agent_speed_max = world.getShipSpeedMax(m_id_agent);
	assert(agent_speed_max >= 0.0);

	if(agent_position.isDistanceLessThan(m_explore_position, EXPLORE_DISTANCE_NEW_POSITION) ||
	   is_new_position_needed)
	{
		m_explore_position = calculateExplorePosition(agent_position);
	}

	return (m_explore_position - agent_position).getCopyWithNorm(agent_speed_max);
}
コード例 #14
0
Vector3 SteeringBehaviour :: calculateEscortPosition (const WorldInterface& world,
                                                      const PhysicsObjectId& id_target,
                                                      const Vector3& offset) const
{
	assert(m_steering_behaviour == ESCORT);
	assert(id_target != PhysicsObjectId::ID_NOTHING);

	if(!world.isAlive(id_target))
		return Vector3::ZERO;

	Vector3 target_position = world.getPosition(id_target);
	Vector3 target_forward  = world.getForward (id_target);
	Vector3 target_up       = world.getUp      (id_target);
	Vector3 target_right    = world.getRight   (id_target);

	return target_position +
	       target_forward * offset.x +
	       target_up      * offset.y +
	       target_right   * offset.z;
}
コード例 #15
0
ファイル: SpaceMongolsUnitAi.cpp プロジェクト: streibeb/cs409
Vector3 UnitAiMoonGuard::chargeAtTarget(const WorldInterface& world, const PhysicsObjectId& target, const Vector3& orig_velocity)
{
    if (!world.isAlive(target))
    {
        return orig_velocity;
    }

    Vector3 unit_pos = getShip().getPosition();
    Vector3 target_pos = world.getPosition(target);
    Vector3 target_forward = world.getForward(target);


    //Vector3 desired_velocity = steeringBehaviour->seek(world, target);
    Vector3 desired_velocity = steeringBehaviour->aim(world, target, Bullet::SPEED);
    //Vector3 desired_velocity = steeringBehaviour->escort(world, target, -target_forward * 50);
    Vector3 v = avoidPlanetoids(world, desired_velocity);

    //printf("\tRamming\n");
    return v;
}
コード例 #16
0
ファイル: SpaceMongolsUnitAi.cpp プロジェクト: streibeb/cs409
Vector3 UnitAiMoonGuard::avoidShips(const WorldInterface &world, const Vector3& orig_velocity)
{
    if (nearestShip == PhysicsObjectId::ID_NOTHING ||
            !world.isAlive(nearestShip) ||
            nearestShip == nearestEnemyShip)
    {
        return orig_velocity;
    }

    Vector3 target_pos = world.getPosition(nearestShip);
    double target_radius = world.getRadius(nearestShip);

    Vector3 v = steeringBehaviour->avoid(world,
                                         orig_velocity,
                                         target_pos,
                                         target_radius,
                                         SHIP_CLEARANCE,
                                         SHIP_AVOID_DISTANCE);
    //printf("\tAvoiding Ships\n");
    return v;
}
コード例 #17
0
ファイル: SpaceMongolsUnitAi.cpp プロジェクト: streibeb/cs409
void UnitAiMoonGuard::getClosestEnemyShip(const WorldInterface& world)
{
    PhysicsObjectId nearestShip;
    Vector3 ship_pos = getShip().getPosition();

    double min_distance = std::numeric_limits<double>::max();
    nearestShip = PhysicsObjectId::ID_NOTHING;
    for (int i = 0; i < nearbyShips.size(); i++)
    {
        if (!world.isAlive(nearbyShips[i])) continue;
        if (nearbyShips[i].m_fleet == getShipId().m_fleet) continue;

        double temp = ship_pos.getDistanceSquared(world.getPosition(nearbyShips[i]));
        if (temp < min_distance)
        {
            min_distance = temp;
            nearestShip = nearbyShips[i];
        }
    }

    this->nearestEnemyShip = nearestShip;
}
コード例 #18
0
Vector3 SteeringBehaviour :: arrive (const WorldInterface& world,
                                     const Vector3& target_position)
{
	// no initialization needed
	m_steering_behaviour = ARRIVE;

	PhysicsObjectId id_agent = m_id_agent;

	if(!world.isAlive(id_agent))
	{
		assert(invariant());
		return Vector3::ZERO;
	}

	Vector3 agent_position = world.getPosition(id_agent);

	if(agent_position == target_position)
	{
		assert(invariant());
		return Vector3::ZERO;
	}

	double agent_speed = world.getShipSpeedMax(id_agent);
	assert(agent_speed >= 0.0);
	double agent_acceleration = world.getShipAcceleration(id_agent);
	assert(agent_acceleration >= 0.0);

	double distance       = agent_position.getDistance(target_position);
	double max_safe_speed = calculateMaxSafeSpeed(distance, agent_acceleration);
	if(max_safe_speed < agent_speed)
		agent_speed = max_safe_speed;
	assert(agent_speed >= 0.0);
	assert(agent_speed <= max_safe_speed);

	assert(invariant());
	return (target_position - agent_position).getCopyWithNorm(agent_speed);
}
コード例 #19
0
Vector3 SteeringBehaviour :: flee (const WorldInterface& world,
                                   const Vector3& target_position)
{
	// no initialization needed
	m_steering_behaviour = FLEE;

	if(!world.isAlive(m_id_agent))
	{
		assert(invariant());
		return Vector3::ZERO;
	}

	Vector3 agent_position = world.getPosition(m_id_agent);

	if(agent_position == target_position)
	{
		assert(invariant());
		return Vector3::ZERO;
	}

	double agent_speed_max = world.getShipSpeedMax(m_id_agent);
	assert(agent_speed_max >= 0.0);
	return (agent_position - target_position).getCopyWithNorm(agent_speed_max);
}
コード例 #20
0
// avoid only when going to collide
Vector3 SteeringBehaviour :: avoid (const WorldInterface& world,
                                    const Vector3& original_velocity,
                                    const Vector3& sphere_center,
                                    double sphere_radius,
                                    double clearance,
                                    double avoid_distance) const
{
	assert(sphere_radius >= 0.0);
	assert(clearance > 0.0);
	assert(clearance <= avoid_distance);

	if(!world.isAlive(m_id_agent) ||
	   original_velocity.isZero())
	{
		assert(invariant());
		return Vector3::ZERO;
	}

	Vector3 agent_position    = world.getPosition(m_id_agent);
	double  agent_radius      = world.getRadius(m_id_agent);
	double  desired_speed     = world.getShipSpeedMax(m_id_agent);
	Vector3 relative_position = sphere_center - agent_position;
	double  radius_sum        = sphere_radius + agent_radius;

	if(relative_position.isNormGreaterThan(radius_sum + avoid_distance))
	{
		if(DEBUGGING_AVOID)
			cout << "Avoid: Outside avoid distance" << endl;
		assert(invariant());
		return original_velocity.getTruncated(desired_speed);  // too far away to worry about
	}

	Vector3 agent_forward = world.getForward(m_id_agent);

	if(relative_position.dotProduct(agent_forward) < 0.0)
	{
		// past center of object; no cylinder
		if(DEBUGGING_AVOID)
			cout << "Avoid: Departing from object" << endl;

		if(relative_position.isNormLessThan(radius_sum + clearance))
		{
			// we are too close, so flee and slow down

			double distance_fraction = (relative_position.getNorm() - radius_sum) / clearance;
			if(DEBUGGING_AVOID)
				cout << "\tInside panic distance: fraction = " << distance_fraction << endl;
			if(distance_fraction < 0.0)
				distance_fraction = 0.0;

			Vector3 interpolated =  original_velocity.getNormalized() *        distance_fraction +
			                       -relative_position.getNormalized() * (1.0 - distance_fraction);

			if(distance_fraction > AVOID_SPEED_FACTOR_MIN)
				desired_speed *= distance_fraction;
			else
				desired_speed *= AVOID_SPEED_FACTOR_MIN;

			if(original_velocity.isNormLessThan(desired_speed))
				desired_speed = original_velocity.getNorm();

			assert(invariant());
			return interpolated.getCopyWithNorm(desired_speed);
		}
		else
		{
			if(DEBUGGING_AVOID)
				cout << "\tPast object" << endl;
			assert(invariant());
			return original_velocity.getTruncated(desired_speed);  // far enough past object
		}
	}
	else
	{
		// have not reached center of object; check against cylinder
		if(DEBUGGING_AVOID)
			cout << "Avoid: Approaching object" << endl;

		double distance_from_cylinder_center = relative_position.getAntiProjection(agent_forward).getNorm();
		double clearance_fraction            = (distance_from_cylinder_center - radius_sum) / clearance;
		if(DEBUGGING_AVOID)
		{
			cout << "\tTo sphere:         " << relative_position << endl;
			cout << "\tDistance_from_cylinder_center: " << distance_from_cylinder_center << endl;
			cout << "\tRadius_sum:        " << radius_sum << endl;
			cout << "\tClearance:         " << clearance << endl;
			cout << "\tFraction:          " << clearance_fraction << endl;
		}

		if(clearance_fraction < 0.0)
		{
			clearance_fraction = 0.0;
			if(DEBUGGING_AVOID)
				cout << "\tLined up at sphere" << endl;
		}

		if(clearance_fraction > 1.0)
		{
			if(DEBUGGING_AVOID)
				cout << "\tOutside cylinder" << endl;
			assert(invariant());
			return original_velocity;  // outside of danger cylinder
		}
		if(DEBUGGING_AVOID)
			cout << "\tModified fraction: " << clearance_fraction << endl;

		assert(!original_velocity.isZero());
		assert(!agent_forward.isZero());
		Vector3 sideways_vector = -relative_position.getAntiProjection(agent_forward);
		while(sideways_vector.isNormLessThan(AVOID_SIDEWAYS_NORM_MIN))
		{
			// we have almost no sideways, so use a random value
			sideways_vector = Vector3::getRandomUnitVector().getAntiProjection(agent_forward);
			// keep trying until we get a good one
		}
		assert(!original_velocity.isZero());
		assert(!sideways_vector.isZero());
		assert(sideways_vector.isOrthogonal(agent_forward));
		if(DEBUGGING_AVOID)
			cout << "\tSideways: " << sideways_vector << endl;

		Vector3 interpolated = original_velocity.getNormalized() *        clearance_fraction +
		                       sideways_vector  .getNormalized() * (1.0 - clearance_fraction);

		if(original_velocity.isNormLessThan(desired_speed))
			desired_speed = original_velocity.getNorm();

		if(DEBUGGING_AVOID)
			cout << "\tDodging out of cylinder: " << interpolated.getCopyWithNorm(desired_speed) << endl;
		assert(invariant());
		return interpolated.getCopyWithNorm(desired_speed);
	}
}
コード例 #21
0
Vector3 SteeringBehaviour :: patrolSphere (const WorldInterface& world,
                                           const Vector3& sphere_center,
                                           double patrol_radius,
                                           double patrol_radius_tolerance)
{
	assert(patrol_radius >= 0.0);
	assert(patrol_radius_tolerance >= 0.0);

	bool is_new_position_needed = false;
	if(m_steering_behaviour         != PATROL_SPHERE ||
	   m_sphere_center              != sphere_center ||
	   m_desired_distance           != patrol_radius ||
	   m_desired_distance_tolerance != patrol_radius_tolerance)
	{
		m_steering_behaviour         = PATROL_SPHERE;
		m_sphere_center              = sphere_center;
		m_desired_distance           = patrol_radius;
		m_desired_distance_tolerance = patrol_radius_tolerance;
		is_new_position_needed       = true;
	}

	if(!world.isAlive(m_id_agent))
	{
		// the explore position doesn't matter at this point

		assert(invariant());
		return Vector3::ZERO;
	}

	Vector3 agent_position = world.getPosition(m_id_agent);

	if(isNearEnoughPatrolSpherePoint(agent_position) ||
	   is_new_position_needed)
	{
		m_explore_position = calculatePatrolSpherePosition(agent_position);
	}

	double agent_speed_max = world.getShipSpeedMax(m_id_agent);
	assert(agent_speed_max >= 0.0);

	if(DEBUGGING_PATROL_SPHERE)
	{
		cout << "Explore:" << endl;
		cout << "\tAgent position:   " << agent_position << endl;
		cout << "\tSphere position:  " << m_sphere_center << endl;
		cout << "\tExplore position: " << m_explore_position << endl;
	}

	double distance_from_sphere = agent_position.getDistance(m_sphere_center);

	double weight_parallel = 1.0;
	if(m_desired_distance_tolerance > 0.0)
	{
		double distance_outside_optimum = distance_from_sphere - m_desired_distance;

		weight_parallel = distance_outside_optimum / m_desired_distance_tolerance;
		if(weight_parallel > 1.0)
			weight_parallel = 1.0;
		if(weight_parallel < -1.0)
			weight_parallel = -1.0;

		if(DEBUGGING_PATROL_SPHERE)
		{
				cout << "\tSphere distance:  " << distance_from_sphere << endl;
				cout << "\tDesired distance: " << m_desired_distance << endl;
				cout << "\tTolerance:        " << m_desired_distance_tolerance << endl;
				cout << "\tweight_parallel:      " << weight_parallel << endl;
		}

		if(weight_parallel >= 0.0)
			weight_parallel =   weight_parallel * weight_parallel;
		else
			weight_parallel = -(weight_parallel * weight_parallel);

		if(DEBUGGING_PATROL_SPHERE)
				cout << "\t                   => " << weight_parallel << endl;

		assert(weight_parallel <=  1.0);
		assert(weight_parallel >= -1.0);
	}
	assert(weight_parallel <=  1.0);
	assert(weight_parallel >= -1.0);
	double weight_perpendicular = 1.0 - fabs(weight_parallel);
	assert(weight_perpendicular <=  1.0);
	assert(weight_perpendicular >= -1.0);

	if(DEBUGGING_PATROL_SPHERE)
		cout << "\tweight_perpendicular: " << weight_perpendicular << endl;

	Vector3 to_sphere               = m_sphere_center    - agent_position;
	Vector3 to_destination          = m_explore_position - agent_position;
	Vector3 parallel_to_normal      = to_destination.getProjection(to_sphere);
	Vector3 perpendicular_to_normal = to_destination - parallel_to_normal;
	Vector3 desired_vector          = parallel_to_normal      * weight_parallel +
	                                  perpendicular_to_normal * weight_perpendicular;

	assert(invariant());
	return desired_vector.getCopyWithNormSafe(agent_speed_max);
}