UnitAiMoonGuard :: UnitAiMoonGuard (const AiShipReference& ship, const WorldInterface& world, const PhysicsObjectId& id_moon) : UnitAiSuperclass(ship) { assert(ship.isShip()); assert(id_moon.m_type == PhysicsObjectId::TYPE_PLANETOID); assert(world.isAlive(id_moon)); assert(world.isPlanetoidMoon(id_moon)); steeringBehaviour = new FleetName::SteeringBehaviour(ship.getId()); moon = id_moon; scanCount = rand() % SCAN_COUNT_MAX; }
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; }
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); }; }
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(); } }
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); }
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; }
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; }
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; }
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; }
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); }
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); }
// 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); } }
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); }