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); }
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); }
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 } }
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); }
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; } }
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); }
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); }
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; }
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); }; }
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; }
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 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::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 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); }