This commit is contained in:
hiker 2016-05-25 21:35:17 +10:00
commit 3ec56386df
27 changed files with 1036 additions and 394 deletions

View File

@ -527,6 +527,9 @@ namespace UserConfigParams
/** True if fps should be printed each frame. */
PARAM_PREFIX bool m_fps_debug PARAM_DEFAULT(false);
/** True if arena (battle/soccer) ai profiling. */
PARAM_PREFIX bool m_arena_ai_stats PARAM_DEFAULT(false);
/** True if slipstream debugging is activated. */
PARAM_PREFIX bool m_slipstream_debug PARAM_DEFAULT( false );

View File

@ -299,6 +299,7 @@ void Skybox::generateSpecularCubemap()
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &fbo);
glActiveTexture(GL_TEXTURE0);
} // generateSpecularCubemap

View File

@ -283,13 +283,13 @@ void AIBaseController::crashed(const Material *m)
} // crashed(Material)
//-----------------------------------------------------------------------------
void AIBaseController::checkPosition(const Vec3 &point,
posData *pos_data,
Vec3 *lc) const
void AIBaseController::checkPosition(const Vec3 &point, posData *pos_data,
Vec3 *lc, bool use_front_xyz) const
{
// Convert to local coordinates from the point of view of current kart
btQuaternion q(btVector3(0, 1, 0), -m_kart->getHeading());
Vec3 p = point - m_kart->getXYZ();
Vec3 p = point -
(use_front_xyz ? m_kart->getFrontXYZ() : m_kart->getXYZ());
Vec3 local_coordinates = quatRotate(q, p);
// Save local coordinates for later use if needed

View File

@ -77,7 +77,9 @@ protected:
/** This can be called to detect if the kart is stuck (i.e. repeatedly
* hitting part of the track). */
bool isStuck() const { return m_stuck; }
void checkPosition(const Vec3&, posData*, Vec3* lc = NULL) const;
void checkPosition(const Vec3&, posData*,
Vec3* lc = NULL,
bool use_front_xyz = false) const;
public:
AIBaseController(AbstractKart *kart);

View File

@ -50,6 +50,7 @@ void ArenaAI::reset()
m_cur_kart_pos_data = {0};
m_is_stuck = false;
m_is_uturn = false;
m_avoid_eating_banana = false;
m_target_point = Vec3(0, 0, 0);
m_time_since_last_shot = 0.0f;
m_time_since_driving = 0.0f;
@ -76,7 +77,10 @@ void ArenaAI::update(float dt)
// Don't do anything if there is currently a kart animations shown.
if (m_kart->getKartAnimation())
{
resetAfterStop();
return;
}
if (isWaiting())
{
@ -101,6 +105,7 @@ void ArenaAI::update(float dt)
if (m_is_uturn)
{
resetAfterStop();
handleArenaUTurn(dt);
}
else
@ -155,6 +160,7 @@ void ArenaAI::checkIfStuck(const float dt)
*/
void ArenaAI::handleArenaAcceleration(const float dt)
{
if (m_controls->m_brake)
{
m_controls->m_accel = 0.0f;
@ -202,6 +208,7 @@ bool ArenaAI::handleArenaUnstuck(const float dt)
{
if (!m_is_stuck || m_is_uturn) return false;
resetAfterStop();
setSteering(0.0f, dt);
if (fabsf(m_kart->getSpeed()) >
@ -234,9 +241,10 @@ void ArenaAI::handleArenaSteering(const float dt)
if (current_node == BattleGraph::UNKNOWN_POLY ||
m_target_node == BattleGraph::UNKNOWN_POLY) return;
if (m_target_node == current_node)
if (m_target_node == current_node || directDrive())
{
// Very close to the item, steer directly
m_path_corners.clear();
checkPosition(m_target_point, &m_cur_kart_pos_data);
#ifdef AI_DEBUG
m_debug_sphere->setPosition(m_target_point.toIrrVector());
@ -263,7 +271,8 @@ void ArenaAI::handleArenaSteering(const float dt)
checkPosition(m_target_point, &m_cur_kart_pos_data);
#ifdef AI_DEBUG
if (m_path_corners.size() > 2)
m_debug_sphere->setPosition(m_path_corners[0].toIrrVector());
/*if (m_path_corners.size() > 2)
{
m_debug_sphere->setVisible(true);
m_debug_sphere_next->setVisible(true);
@ -274,7 +283,7 @@ void ArenaAI::handleArenaSteering(const float dt)
{
m_debug_sphere->setVisible(false);
m_debug_sphere_next->setVisible(false);
}
}*/
#endif
if (m_cur_kart_pos_data.behind)
{
@ -300,6 +309,8 @@ void ArenaAI::handleArenaSteering(const float dt)
//-----------------------------------------------------------------------------
void ArenaAI::handleArenaBanana()
{
m_avoid_eating_banana = false;
if (m_is_uturn) return;
const std::vector< std::pair<const Item*, int> >& item_list =
@ -321,10 +332,7 @@ void ArenaAI::handleArenaBanana()
banana_lc = (banana_pos.lhs ? banana_lc + Vec3 (2, 0, 0) :
banana_lc - Vec3 (2, 0, 0));
m_target_point = m_kart->getTrans()(banana_lc);
m_target_node = BattleGraph::get()
->pointToNode(getCurrentNode(), m_target_point,
false/*ignore_vertical*/);
m_avoid_eating_banana = true;
// Handle one banana only
break;
}
@ -498,6 +506,7 @@ void ArenaAI::handleArenaBraking()
if (forceBraking() && m_kart->getSpeed() > MIN_SPEED)
{
// Brake now
m_controls->m_brake = true;
return;
}
@ -506,6 +515,8 @@ void ArenaAI::handleArenaBraking()
if (getCurrentNode() == BattleGraph::UNKNOWN_POLY ||
m_target_node == BattleGraph::UNKNOWN_POLY) return;
if (m_path_corners.empty()) return;
float current_curve_radius = determineTurnRadius(m_kart->getXYZ(),
m_path_corners[0], (m_path_corners.size() >= 2 ? m_path_corners[1] :
m_path_corners[0]));

View File

@ -64,6 +64,9 @@ protected:
/** The target point. */
Vec3 m_target_point;
/** For directDrive() to work */
bool m_avoid_eating_banana;
void collectItemInArena(Vec3*, int*) const;
private:
/** Used by handleArenaUTurn, it tells whether to do left or right
@ -118,9 +121,11 @@ private:
void stringPull(const Vec3&, const Vec3&);
virtual int getCurrentNode() const = 0;
virtual bool isWaiting() const = 0;
virtual void resetAfterStop() {};
virtual void findClosestKart(bool use_difficulty) = 0;
virtual void findTarget() = 0;
virtual bool forceBraking() { return false; }
virtual bool forceBraking() { return false; }
virtual bool directDrive() { return m_avoid_eating_banana; }
public:
ArenaAI(AbstractKart *kart);
virtual ~ArenaAI() {};

View File

@ -33,6 +33,10 @@ using namespace irr;
using namespace std;
#endif
#ifdef BALL_AIM_DEBUG
#include "graphics/camera.hpp"
#endif
SoccerAI::SoccerAI(AbstractKart *kart)
: ArenaAI(kart)
{
@ -47,9 +51,21 @@ SoccerAI::SoccerAI(AbstractKart *kart)
m_debug_sphere_next = irr_driver->addSphere(1.0f, col_debug_next);
m_debug_sphere_next->setVisible(true);
#endif
#ifdef BALL_AIM_DEBUG
video::SColor red(128, 128, 0, 0);
video::SColor blue(128, 0, 0, 128);
m_red_sphere = irr_driver->addSphere(1.0f, red);
m_red_sphere->setVisible(false);
m_blue_sphere = irr_driver->addSphere(1.0f, blue);
m_blue_sphere->setVisible(false);
#endif
m_world = dynamic_cast<SoccerWorld*>(World::getWorld());
m_track = m_world->getTrack();
m_cur_team = m_world->getKartTeam(m_kart->getWorldKartId());
m_opp_team = (m_cur_team == SOCCER_TEAM_BLUE ?
SOCCER_TEAM_RED : SOCCER_TEAM_BLUE);
// Don't call our own setControllerName, since this will add a
// billboard showing 'AIBaseController' to the kart.
@ -65,6 +81,12 @@ SoccerAI::~SoccerAI()
irr_driver->removeNode(m_debug_sphere);
irr_driver->removeNode(m_debug_sphere_next);
#endif
#ifdef BALL_AIM_DEBUG
irr_driver->removeNode(m_red_sphere);
irr_driver->removeNode(m_blue_sphere);
#endif
} // ~SoccerAI
//-----------------------------------------------------------------------------
@ -75,18 +97,27 @@ void SoccerAI::reset()
ArenaAI::reset();
AIBaseController::reset();
m_saving_ball = false;
m_overtake_ball = false;
m_force_brake = false;
m_steer_with_ball = false;
} // reset
//-----------------------------------------------------------------------------
void SoccerAI::update(float dt)
{
m_saving_ball = false;
#ifdef BALL_AIM_DEBUG
Vec3 red = m_world->getBallAimPosition(SOCCER_TEAM_RED);
Vec3 blue = m_world->getBallAimPosition(SOCCER_TEAM_BLUE);
m_red_sphere->setPosition(red.toIrrVector());
m_blue_sphere->setPosition(blue.toIrrVector());
#endif
m_force_brake = false;
m_steer_with_ball = false;
if (World::getWorld()->getPhase() == World::GOAL_PHASE)
{
resetAfterStop();
m_controls->m_brake = false;
m_controls->m_accel = 0.0f;
AIBaseController::update(dt);
@ -135,132 +166,300 @@ void SoccerAI::findClosestKart(bool use_difficulty)
//-----------------------------------------------------------------------------
void SoccerAI::findTarget()
{
// Check whether any defense is needed
if ((m_world->getBallPosition() - m_world->getGoalLocation(m_cur_team,
CheckGoal::POINT_CENTER)).length_2d() < 50.0f &&
m_world->getDefender(m_cur_team) == (signed)m_kart->getWorldKartId())
// Check if this AI kart is the one who will chase the ball
if (m_world->getBallChaser(m_cur_team) == (signed)m_kart->getWorldKartId())
{
m_target_node = m_world->getBallNode();
m_target_point = correctBallPosition(m_world->getBallPosition());
m_target_point = determineBallAimingPosition();
m_target_node = m_world->getBallNode();
return;
}
// Find a suitable target to drive to, either ball or powerup
if ((m_world->getBallPosition() - m_kart->getXYZ()).length_2d() > 20.0f &&
(m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING &&
m_kart->getAttachment()->getType() != Attachment::ATTACH_SWATTER))
// Always reset this flag,
// in case the ball chaser lost the ball somehow
m_overtake_ball = false;
if (m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING &&
m_kart->getAttachment()->getType() != Attachment::ATTACH_SWATTER)
{
collectItemInArena(&m_target_point , &m_target_node);
}
else if (m_world->getAttacker(m_cur_team) == (signed)m_kart
->getWorldKartId())
{
// This AI will attack the other team ball chaser
int id = m_world->getBallChaser(m_cur_team == SOCCER_TEAM_BLUE ?
SOCCER_TEAM_RED : SOCCER_TEAM_BLUE);
m_target_point = m_world->getKart(id)->getXYZ();
m_target_node = m_world->getKartNode(id);
}
else
{
m_target_node = m_world->getBallNode();
m_target_point = correctBallPosition(m_world->getBallPosition());
m_target_point = m_closest_kart_point;
m_target_node = m_closest_kart_node;
}
} // findTarget
//-----------------------------------------------------------------------------
Vec3 SoccerAI::correctBallPosition(const Vec3& orig_pos)
Vec3 SoccerAI::determineBallAimingPosition()
{
// Notice: Build with AI_DEBUG and change camera target to an AI kart,
// to debug or see how AI steer with the ball
#ifdef BALL_AIM_DEBUG
// Choose your favourite team to watch
if (m_world->getKartTeam(m_kart->getWorldKartId()) == SOCCER_TEAM_BLUE)
{
Camera *cam = Camera::getActiveCamera();
cam->setMode(Camera::CM_NORMAL);
cam->setKart(m_kart);
}
#endif
const Vec3& ball_aim_pos = m_world->getBallAimPosition(m_opp_team);
const Vec3& orig_pos = m_world->getBallPosition();
const float ball_diameter = m_world->getBallDiameter();
posData ball_pos = {0};
posData aim_pos = {0};
Vec3 ball_lc;
checkPosition(orig_pos, &ball_pos, &ball_lc);
Vec3 aim_lc;
checkPosition(orig_pos, &ball_pos, &ball_lc, true/*use_front_xyz*/);
checkPosition(ball_aim_pos, &aim_pos, &aim_lc, true/*use_front_xyz*/);
// Too far / behind from the ball,
// Too far from the ball,
// use path finding from arena ai to get close
if (!(ball_pos.distance < 3.0f) || ball_pos.behind) return orig_pos;
// ie no extra braking is needed
if (aim_pos.distance > 10.0f) return ball_aim_pos;
// Save the opposite team
SoccerTeam opp_team = (m_cur_team == SOCCER_TEAM_BLUE ?
SOCCER_TEAM_RED : SOCCER_TEAM_BLUE);
// Prevent lost control when steering with ball
const bool need_braking = ball_pos.angle > 0.1f &&
m_kart->getSpeed() > 9.0f && ball_pos.distance <2.5f;
// Goal / own goal detection first, as different aiming method will be used
const bool likely_to_goal =
ball_pos.angle < 0.2f && isLikelyToGoal(opp_team);
if (likely_to_goal)
if (m_overtake_ball)
{
if (need_braking)
Vec3 overtake_lc;
const bool can_overtake = determineOvertakePosition(ball_lc, aim_lc,
ball_pos, &overtake_lc);
if (!can_overtake)
{
m_controls->m_brake = true;
m_force_brake = true;
// Prevent keep pushing by aiming a nearer point
return m_kart->getTrans()(ball_lc - Vec3(0, 0, 1));
m_overtake_ball = false;
return ball_aim_pos;
}
// Keep pushing the ball straight to the goal
return orig_pos;
}
const bool likely_to_own_goal =
ball_pos.angle < 0.2f && isLikelyToGoal(m_cur_team);
if (likely_to_own_goal)
{
// It's getting likely to own goal, apply more
// offset for skidding, to save the ball from behind
// scored.
if (m_cur_difficulty == RaceManager::DIFFICULTY_HARD ||
m_cur_difficulty == RaceManager::DIFFICULTY_BEST)
m_saving_ball = true;
return m_kart->getTrans()(ball_pos.lhs ?
ball_lc - Vec3(2, 0, 0) + Vec3(0, 0, 2) :
ball_lc + Vec3(2, 0, 2));
}
// Now try to make the ball face towards the goal
// Aim at upper/lower left/right corner of ball depends on location
posData goal_pos = {0};
checkPosition(m_world->getGoalLocation(opp_team, CheckGoal::POINT_CENTER),
&goal_pos);
Vec3 corrected_pos;
if (ball_pos.lhs && goal_pos.lhs)
{
corrected_pos = ball_lc + Vec3(1, 0, 1);
}
else if (!ball_pos.lhs && !goal_pos.lhs)
{
corrected_pos = ball_lc - Vec3(1, 0, 0) + Vec3(0, 0, 1);
}
else if (!ball_pos.lhs && goal_pos.lhs)
{
corrected_pos = ball_lc + Vec3(1, 0, 0) - Vec3(0, 0, 1);
else
return m_kart->getTrans()(Vec3(overtake_lc));
}
else
{
corrected_pos = ball_lc - Vec3(1, 0, 1);
}
if (need_braking)
{
m_controls->m_brake = true;
m_force_brake = true;
}
return m_kart->getTrans()(corrected_pos);
// Check whether the aim point is non-reachable
// ie the ball is in front of the kart, which the aim position
// is behind the ball , if so m_overtake_ball is true
if (aim_lc.z() > 0 && aim_lc.z() > ball_lc.z())
{
if (isOvertakable(ball_lc, ball_pos))
{
m_overtake_ball = true;
return ball_aim_pos;
}
else
{
// Stop a while to wait for overtaking, prevent own goal too
// Only do that if the ball is moving
if (!m_world->ballNotMoving())
m_force_brake = true;
return ball_aim_pos;
}
}
} // correctBallPosition
// Otherwise use the aim position calculated by soccer world
// Prevent lost control when steering with ball
m_force_brake = ball_pos.angle > 0.15f &&
m_kart->getSpeed() > 9.0f && ball_pos.distance < ball_diameter;
if (aim_pos.behind && aim_pos.distance < (ball_diameter / 2))
{
// Reached aim point, aim forward
m_steer_with_ball = true;
return m_world->getBallAimPosition(m_opp_team, true/*reverse*/);
}
return ball_aim_pos;
}
// Make compiler happy
return ball_aim_pos;
} // determineBallAimingPosition
//-----------------------------------------------------------------------------
bool SoccerAI::isLikelyToGoal(SoccerTeam team) const
bool SoccerAI::isOvertakable(const Vec3& ball_lc, const posData& ball_pos)
{
// Use local coordinate for easy compare
Vec3 first_pos;
Vec3 last_pos;
checkPosition(m_world->getGoalLocation(team, CheckGoal::POINT_FIRST),
NULL, &first_pos);
checkPosition(m_world->getGoalLocation(team, CheckGoal::POINT_LAST),
NULL, &last_pos);
// No overtake if ball is behind
if (ball_lc.z() < 0.0f) return false;
// If the kart lies between the first and last pos, and faces
// in front of them, than it's likely to goal
if ((first_pos.z() > 0.0f && last_pos.z() > 0.0f) &&
((first_pos.x() < 0.0f && last_pos.x() > 0.0f) ||
(last_pos.x() < 0.0f && first_pos.x() > 0.0f)))
return true;
// Circle equation: (x-a)2 + (y-b)2 = r2
const float r2 = (ball_pos.distance / 2) * (ball_pos.distance / 2);
const float a = ball_lc.x();
const float b = ball_lc.z();
// Check first if the kart is lies inside the circle, if so no tangent
// can be drawn ( so can't overtake), minus 0.1 as epslion
const float test_radius_2 = ((a * a) + (b * b)) - 0.1f;
if (test_radius_2 < r2)
{
return false;
}
return true;
} // isOvertakable
//-----------------------------------------------------------------------------
bool SoccerAI::determineOvertakePosition(const Vec3& ball_lc,
const Vec3& aim_lc,
const posData& ball_pos,
Vec3* overtake_lc)
{
// This done by drawing a circle using the center of ball local coordinates
// and the distance / 2 from kart to ball center as radius (which allows more
// offset for overtaking), then find tangent line from kart (0, 0, 0) to the
// circle. The intercept point will be used as overtake position
// Check if overtakable at current location
if (!isOvertakable(ball_lc, ball_pos)) return false;
// Otherwise calculate the tangent
// As all are local coordinates, so center is 0,0 which is y = mx for the
// tangent equation, and the m (slope) can be calculated by puting y = mx
// into the general form of circle equation x2 + y2 + Dx + Ey + F = 0
// This means: x2 + m2x2 + Dx + Emx + F = 0
// (1+m2)x2 + (D+Em)x +F = 0
// As only one root for it, so discriminant b2 - 4ac = 0
// So: (D+Em)2 - 4(1+m2)(F) = 0
// D2 + 2DEm +E2m2 - 4F - 4m2F = 0
// (E2 - 4F)m2 + (2DE)m + (D2 - 4F) = 0
// Now solve the above quadratic equation using
// x = -b (+/-) sqrt(b2 - 4ac) / 2a
// Circle equation: (x-a)2 + (y-b)2 = r2
const float r = ball_pos.distance / 2;
const float r2 = r * r;
const float a = ball_lc.x();
const float b = ball_lc.z();
const float d = -2 * a;
const float e = -2 * b;
const float f = (d * d / 4) + (e * e / 4) - r2;
const float discriminant = (2 * 2 * d * d * e * e) -
(4 * ((e * e) - (4 * f)) * ((d * d) - (4 * f)));
assert(discriminant > 0.0f);
float t_slope_1 = (-(2 * d * e) + sqrtf(discriminant)) /
(2 * ((e * e) - (4 * f)));
float t_slope_2 = (-(2 * d * e) - sqrtf(discriminant)) /
(2 * ((e * e) - (4 * f)));
assert(!std::isnan(t_slope_1));
assert(!std::isnan(t_slope_2));
// Make the slopes in correct order, allow easier rotate later
float slope_1 = 0.0f;
float slope_2 = 0.0f;
if ((t_slope_1 > 0 && t_slope_2 > 0) || (t_slope_1 < 0 && t_slope_2 < 0))
{
if (t_slope_1 > t_slope_2)
{
slope_1 = t_slope_1;
slope_2 = t_slope_2;
}
else
{
slope_1 = t_slope_2;
slope_2 = t_slope_1;
}
}
else
{
if (t_slope_1 > t_slope_2)
{
slope_1 = t_slope_2;
slope_2 = t_slope_1;
}
else
{
slope_1 = t_slope_1;
slope_2 = t_slope_2;
}
}
// Calculate two intercept points, as we already put y=mx into circle
// equation and know that only one root for each slope, so x can be
// calculated easily with -b / 2a
// From (1+m2)x2 + (D+Em)x +F = 0:
const float x1 = -(d + (e * slope_1)) / (2 * (1 + (slope_1 * slope_1)));
const float x2 = -(d + (e * slope_2)) / (2 * (1 + (slope_2 * slope_2)));
const float y1 = slope_1 * x1;
const float y2 = slope_2 * x2;
const Vec3 point1(x1, 0, y1);
const Vec3 point2(x2, 0, y2);
const float d1 = (point1 - aim_lc).length_2d();
const float d2 = (point2 - aim_lc).length_2d();
// Use the tangent closest to the ball aiming position to aim
const bool use_tangent_one = d1 < d2;
// Adjust x and y if r < ball diameter,
// which will likely push to ball forward
// Notice: we cannot increase the radius before, as the kart location
// will likely lie inside the enlarged circle
if (r < m_world->getBallDiameter())
{
// Constuctor a equation using y = (rotateSlope(old_m)) x which is
// a less steep or steeper line, and find out the new adjusted position
// using the distance to the original point * 2 at new line
// Determine if the circle is drawn around the side of kart
// ie point1 or point2 z() < 0, if so reverse the below logic
const float m = ((point1.z() < 0 || point2.z() < 0) ?
(use_tangent_one ? rotateSlope(slope_1, false/*rotate_up*/) :
rotateSlope(slope_2, true/*rotate_up*/)) :
(use_tangent_one ? rotateSlope(slope_1, true/*rotate_up*/) :
rotateSlope(slope_2, false/*rotate_up*/)));
// Calculate new distance from kart to new adjusted position
const float dist = (use_tangent_one ? point1 : point2).length_2d() * 2;
const float dist2 = dist * dist;
// x2 + y2 = dist2
// so y = m * sqrtf (dist2 - y2)
// y = sqrtf(m2 * dist2 / (1 + m2))
const float y = sqrtf((m * m * dist2) / (1 + (m * m)));
const float x = y / m;
*overtake_lc = Vec3(x, 0, y);
}
else
{
// Use the calculated position depends on distance to aim position
if (use_tangent_one)
*overtake_lc = point1;
else
*overtake_lc = point2;
}
return true;
} // determineOvertakePosition
//-----------------------------------------------------------------------------
float SoccerAI::rotateSlope(float old_slope, bool rotate_up)
{
const float theta = atan(old_slope) + (old_slope < 0 ? M_PI : 0);
float new_theta = theta + (rotate_up ? M_PI / 6 : -M_PI /6);
if (new_theta > ((M_PI / 2) - 0.02f) && new_theta < ((M_PI / 2) + 0.02f))
{
// Avoid almost tan 90
new_theta = (M_PI / 2) - 0.02f;
}
// Check if over-rotated
if (new_theta > M_PI)
new_theta = M_PI - 0.1f;
else if (new_theta < 0)
new_theta = 0.1f;
return tan(new_theta);
} // rotateSlope
return false;
} // isLikelyToGoal
//-----------------------------------------------------------------------------
int SoccerAI::getCurrentNode() const
{

View File

@ -21,9 +21,13 @@
#include "karts/controller/arena_ai.hpp"
#undef BALL_AIM_DEBUG
#ifdef BALL_AIM_DEBUG
#include "graphics/irr_driver.hpp"
#endif
class SoccerWorld;
class Vec3;
class Item;
/** The actual soccer AI.
* \ingroup controller
@ -31,22 +35,43 @@ class Item;
class SoccerAI : public ArenaAI
{
private:
#ifdef BALL_AIM_DEBUG
irr::scene::ISceneNode *m_red_sphere;
irr::scene::ISceneNode *m_blue_sphere;
#endif
/** Keep a pointer to world. */
SoccerWorld *m_world;
SoccerTeam m_cur_team;
bool m_saving_ball;
bool m_force_brake;
SoccerTeam m_opp_team;
Vec3 correctBallPosition(const Vec3&);
bool isLikelyToGoal(SoccerTeam team) const;
/** Define which way to handle to ball, either steer with it,
* or overtake it (Denfense).
*/
bool m_overtake_ball;
bool m_force_brake;
bool m_steer_with_ball;
Vec3 determineBallAimingPosition();
bool isOvertakable(const Vec3& ball_lc, const posData& ball_pos);
bool determineOvertakePosition(const Vec3& ball_lc, const Vec3& aim_lc,
const posData& ball_pos, Vec3* overtake_lc);
float rotateSlope(float old_slope, bool rotate_up);
virtual void findClosestKart(bool use_difficulty);
virtual void findTarget();
virtual void resetAfterStop() OVERRIDE { m_overtake_ball = false; }
virtual int getCurrentNode() const;
virtual bool isWaiting() const;
virtual bool canSkid(float steer_fraction) { return m_saving_ball; }
virtual bool forceBraking() OVERRIDE { return m_force_brake; }
virtual bool canSkid(float steer_fraction) { return false; }
virtual bool forceBraking() OVERRIDE { return m_force_brake; }
virtual bool directDrive() OVERRIDE
{
return m_avoid_eating_banana || m_overtake_ball || m_steer_with_ball;
}
public:
SoccerAI(AbstractKart *kart);
~SoccerAI();

View File

@ -57,6 +57,8 @@ RescueAnimation::RescueAnimation(AbstractKart *kart, bool is_auto_rescue)
{
ThreeStrikesBattle *world=(ThreeStrikesBattle*)World::getWorld();
world->kartHit(m_kart->getWorldKartId());
if (UserConfigParams::m_arena_ai_stats)
world->increaseRescueCount();
}
}; // RescueAnimation

View File

@ -774,6 +774,39 @@ int handleCmdLine()
AIBaseController::setTestAI(n);
if (CommandLine::has("--fps-debug"))
UserConfigParams::m_fps_debug = true;
if(CommandLine::has("--soccer-ai-stats"))
{
UserConfigParams::m_arena_ai_stats=true;
race_manager->setMinorMode(RaceManager::MINOR_MODE_SOCCER);
std::vector<std::string> l;
for (int i = 0; i < 9; i++)
l.push_back("tux");
race_manager->setDefaultAIKartList(l);
race_manager->setNumKarts(9);
race_manager->setMaxGoal(30);
race_manager->setTrack("soccer_field");
race_manager->setDifficulty(RaceManager::Difficulty(3));
UserConfigParams::m_no_start_screen = true;
UserConfigParams::m_race_now = true;
UserConfigParams::m_sfx = false;
UserConfigParams::m_music = false;
}
if(CommandLine::has("--battle-ai-stats"))
{
UserConfigParams::m_arena_ai_stats=true;
race_manager->setMinorMode(RaceManager::MINOR_MODE_3_STRIKES);
std::vector<std::string> l;
for (int i = 0; i < 8; i++)
l.push_back("tux");
race_manager->setDefaultAIKartList(l);
race_manager->setTrack("temple");
race_manager->setNumKarts(8);
race_manager->setDifficulty(RaceManager::Difficulty(3));
UserConfigParams::m_no_start_screen = true;
UserConfigParams::m_race_now = true;
UserConfigParams::m_sfx = false;
UserConfigParams::m_music = false;
}
if(UserConfigParams::m_artist_debug_mode)
{

View File

@ -62,7 +62,8 @@ MainLoop::~MainLoop()
float MainLoop::getLimitedDt()
{
// In profile mode without graphics, run with a fixed dt of 1/60
if (ProfileWorld::isProfileMode() && ProfileWorld::isNoGraphics())
if ((ProfileWorld::isProfileMode() && ProfileWorld::isNoGraphics()) ||
UserConfigParams::m_arena_ai_stats)
{
return 1.0f/60.0f;
}

View File

@ -17,8 +17,10 @@
#include "modes/soccer_world.hpp"
#include "main_loop.hpp"
#include "audio/music_manager.hpp"
#include "audio/sfx_base.hpp"
#include "config/user_config.hpp"
#include "io/file_manager.hpp"
#include "graphics/irr_driver.hpp"
#include "karts/abstract_kart.hpp"
@ -30,7 +32,6 @@
#include "karts/controller/soccer_ai.hpp"
#include "physics/physics.hpp"
#include "states_screens/race_gui_base.hpp"
#include "tracks/check_manager.hpp"
#include "tracks/track.hpp"
#include "tracks/track_object_manager.hpp"
#include "utils/constants.hpp"
@ -42,23 +43,25 @@
*/
SoccerWorld::SoccerWorld() : WorldWithRank()
{
if(race_manager->hasTimeTarget())
if (race_manager->hasTimeTarget())
{
WorldStatus::setClockMode(WorldStatus::CLOCK_COUNTDOWN, race_manager->getTimeTarget());
m_count_down_reached_zero = false;
WorldStatus::setClockMode(WorldStatus::CLOCK_COUNTDOWN,
race_manager->getTimeTarget());
}
else
{
WorldStatus::setClockMode(CLOCK_CHRONO);
}
m_frame_count = 0;
m_start_time = irr_driver->getRealTime();
m_use_highscores = false;
m_red_ai = 0;
m_blue_ai = 0;
} // SoccerWorld
//-----------------------------------------------------------------------------
/** The destructor frees al data structures.
/** The destructor frees all data structures.
*/
SoccerWorld::~SoccerWorld()
{
@ -77,39 +80,10 @@ void SoccerWorld::init()
m_display_rank = false;
m_goal_timer = 0.0f;
m_ball_hitter = -1;
m_red_check_goal = NULL;
m_blue_check_goal = NULL;
m_ball = NULL;
m_ball_body = NULL;
m_goal_target = race_manager->getMaxGoal();
m_goal_sound = SFXManager::get()->createSoundSource("goal_scored");
initGoal();
} // init
//-----------------------------------------------------------------------------
/** Called when a soccer game is restarted.
*/
void SoccerWorld::reset()
{
WorldWithRank::reset();
if(race_manager->hasTimeTarget())
{
WorldStatus::setClockMode(WorldStatus::CLOCK_COUNTDOWN, race_manager->getTimeTarget());
m_count_down_reached_zero = false;
}
else WorldStatus::setClockMode(CLOCK_CHRONO);
m_can_score_points = true;
m_red_goal = 0;
m_blue_goal = 0;
m_red_scorers.clear();
m_red_score_times.clear();
m_blue_scorers.clear();
m_blue_score_times.clear();
m_ball_hitter = -1;
m_ball = NULL;
m_red_defender = -1;
m_blue_defender = -1;
m_ball_invalid_timer = 0.0f;
TrackObjectManager* tom = getTrack()->getTrackObjectManager();
assert(tom);
@ -120,12 +94,44 @@ void SoccerWorld::reset()
if(!obj->isSoccerBall())
continue;
m_ball = obj;
m_ball_body = m_ball->getPhysicalObject()->getBody();
// Handle one ball only
break;
}
if (!m_ball)
Log::fatal("SoccerWorld","Ball is missing in soccer field, abort.");
m_bgd.init(m_ball->getPhysicalObject()->getRadius());
} // init
//-----------------------------------------------------------------------------
/** Called when a soccer game is restarted.
*/
void SoccerWorld::reset()
{
WorldWithRank::reset();
if (race_manager->hasTimeTarget())
{
WorldStatus::setClockMode(WorldStatus::CLOCK_COUNTDOWN,
race_manager->getTimeTarget());
}
else
{
WorldStatus::setClockMode(CLOCK_CHRONO);
}
m_count_down_reached_zero = false;
m_red_scorers.clear();
m_red_score_times.clear();
m_blue_scorers.clear();
m_blue_score_times.clear();
m_ball_hitter = -1;
m_red_kdm.clear();
m_blue_kdm.clear();
m_ball_heading = 0.0f;
m_ball_invalid_timer = 0.0f;
if (m_goal_sound != NULL &&
m_goal_sound->getStatus() == SFXBase::SFX_PLAYING)
{
@ -134,7 +140,12 @@ void SoccerWorld::reset()
initKartList();
resetAllPosition();
resetBall();
m_ball->reset();
m_bgd.reset();
// Make the player kart in profiling mode up
// ie make this kart less likely to affect gaming result
if (UserConfigParams::m_arena_ai_stats)
getKart(8)->flyUp();
} // reset
@ -152,19 +163,17 @@ const std::string& SoccerWorld::getIdent() const
*/
void SoccerWorld::update(float dt)
{
World *world = World::getWorld();
WorldWithRank::update(dt);
WorldWithRank::updateTrack(dt);
updateBallPosition(dt);
if (m_track->hasNavMesh())
{
updateKartNodes();
updateDefenders();
updateAIData();
}
if (world->getPhase() == World::GOAL_PHASE)
WorldWithRank::update(dt);
WorldWithRank::updateTrack(dt);
if (getPhase() == World::GOAL_PHASE)
{
if (m_goal_timer == 0.0f)
{
@ -176,16 +185,20 @@ void SoccerWorld::update(float dt)
if (m_goal_timer > 3.0f)
{
world->setPhase(WorldStatus::RACE_PHASE);
setPhase(WorldStatus::RACE_PHASE);
m_goal_timer = 0.0f;
if (!isRaceOver())
{
// Reset all karts
for (unsigned int i = 0; i < m_karts.size(); i++)
moveKartAfterRescue(m_karts[i]);
if (UserConfigParams::m_arena_ai_stats)
getKart(8)->flyUp();
}
}
}
if (UserConfigParams::m_arena_ai_stats)
m_frame_count++;
} // update
@ -195,62 +208,52 @@ void SoccerWorld::onCheckGoalTriggered(bool first_goal)
if (isRaceOver() || isStartPhase())
return;
if (m_can_score_points)
setPhase(WorldStatus::GOAL_PHASE);
m_goal_sound->play();
if (m_ball_hitter != -1)
{
(first_goal ? m_red_goal++ : m_blue_goal++);
ScorerData sd;
sd.m_id = m_ball_hitter;
sd.m_correct_goal = isCorrectGoal(m_ball_hitter, first_goal);
World *world = World::getWorld();
world->setPhase(WorldStatus::GOAL_PHASE);
m_goal_sound->play();
if (m_ball_hitter != -1)
if (sd.m_correct_goal)
{
ScorerData sd;
sd.m_id = m_ball_hitter;
sd.m_correct_goal = isCorrectGoal(m_ball_hitter, first_goal);
m_karts[m_ball_hitter]->getKartModel()
->setAnimation(KartModel::AF_WIN_START, true/* play_non_loop*/);
}
if (sd.m_correct_goal)
{
m_karts[m_ball_hitter]->getKartModel()
->setAnimation(KartModel::AF_WIN_START, true/* play_non_loop*/);
}
else if (!sd.m_correct_goal)
{
m_karts[m_ball_hitter]->getKartModel()
->setAnimation(KartModel::AF_LOSE_START, true/* play_non_loop*/);
}
else if (!sd.m_correct_goal)
if (first_goal)
{
// Notice: true first_goal means it's blue goal being shoot,
// so red team can score
m_red_scorers.push_back(sd);
if (race_manager->hasTimeTarget())
{
m_karts[m_ball_hitter]->getKartModel()
->setAnimation(KartModel::AF_LOSE_START, true/* play_non_loop*/);
}
if (first_goal)
{
// Notice: true first_goal means it's blue goal being shoot,
// so red team can score
m_red_scorers.push_back(sd);
if(race_manager->hasTimeTarget())
{
m_red_score_times.push_back(race_manager
->getTimeTarget() - world->getTime());
}
else
m_red_score_times.push_back(world->getTime());
m_red_score_times.push_back(race_manager->getTimeTarget()
- getTime());
}
else
m_red_score_times.push_back(getTime());
}
else
{
m_blue_scorers.push_back(sd);
if (race_manager->hasTimeTarget())
{
m_blue_scorers.push_back(sd);
if (race_manager->hasTimeTarget())
{
m_blue_score_times.push_back(race_manager
->getTimeTarget() - world->getTime());
}
else
m_blue_score_times.push_back(world->getTime());
m_blue_score_times.push_back(race_manager->getTimeTarget()
- getTime());
}
else
m_blue_score_times.push_back(getTime());
}
}
resetBall();
//Resetting the ball triggers the goal check line one more time.
//This ensures that only one goal is counted, and the second is ignored.
m_can_score_points = !m_can_score_points;
m_ball->reset();
} // onCheckGoalTriggered
@ -282,17 +285,6 @@ bool SoccerWorld::isRaceOver()
} // isRaceOver
//-----------------------------------------------------------------------------
/** Called when the race finishes, i.e. after playing (if necessary) an
* end of race animation. It updates the time for all karts still racing,
* and then updates the ranks.
*/
void SoccerWorld::terminateRace()
{
m_can_score_points = false;
WorldWithRank::terminateRace();
} // terminateRace
//-----------------------------------------------------------------------------
/** Called when the match time ends.
*/
@ -447,25 +439,31 @@ void SoccerWorld::updateBallPosition(float dt)
{
if (isRaceOver()) return;
m_ball_position = m_ball->getPresentation<TrackObjectPresentationMesh>()
->getNode()->getPosition();
if (!ballNotMoving())
{
// Only update heading if the ball is moving
m_ball_heading = atan2f(m_ball_body->getLinearVelocity().getX(),
m_ball_body->getLinearVelocity().getZ());
}
if (m_track->hasNavMesh())
{
m_ball_on_node = BattleGraph::get()->pointToNode(m_ball_on_node,
m_ball_position, true/*ignore_vertical*/);
getBallPosition(), true/*ignore_vertical*/);
if (m_ball_on_node == BattleGraph::UNKNOWN_POLY &&
World::getWorld()->getPhase() == RACE_PHASE)
getPhase() == RACE_PHASE)
{
m_ball_invalid_timer += dt;
// Reset the ball and karts if out of navmesh after 2 seconds
if (m_ball_invalid_timer >= 2.0f)
{
m_ball_invalid_timer = 0.0f;
resetBall();
m_ball->reset();
for (unsigned int i = 0; i < m_karts.size(); i++)
moveKartAfterRescue(m_karts[i]);
if (UserConfigParams::m_arena_ai_stats)
getKart(8)->flyUp();
}
}
else
@ -474,54 +472,21 @@ void SoccerWorld::updateBallPosition(float dt)
} // updateBallPosition
//-----------------------------------------------------------------------------
void SoccerWorld::initGoal()
{
if (!m_track->hasNavMesh()) return;
unsigned int n = CheckManager::get()->getCheckStructureCount();
for (unsigned int i = 0; i < n; i++)
{
CheckGoal* goal =
dynamic_cast<CheckGoal*>(CheckManager::get()->getCheckStructure(i));
if (goal)
{
if (goal->getTeam())
{
m_blue_check_goal = goal;
}
else
{
m_red_check_goal = goal;
}
}
}
} // initGoal
//-----------------------------------------------------------------------------
void SoccerWorld::resetAllPosition()
{
m_kart_on_node.clear();
m_kart_on_node.resize(m_karts.size());
for(unsigned int n=0; n<m_karts.size(); n++)
m_kart_on_node[n] = BattleGraph::UNKNOWN_POLY;
m_kart_on_node.resize(m_karts.size(), BattleGraph::UNKNOWN_POLY);
m_ball_on_node = BattleGraph::UNKNOWN_POLY;
m_ball_position = Vec3(0, 0, 0);
} // resetAllPosition
//-----------------------------------------------------------------------------
SoccerTeam SoccerWorld::getKartTeam(unsigned int kart_id) const
{
std::map<int, SoccerTeam>::const_iterator n = m_kart_team_map.find(kart_id);
if (n != m_kart_team_map.end())
{
return n->second;
}
// Fallback
Log::warn("SoccerWorld", "Unknown team, using blue default.");
return SOCCER_TEAM_BLUE;
std::map<int, SoccerTeam>::const_iterator n =
m_kart_team_map.find(kart_id);
assert(n != m_kart_team_map.end());
return n->second;
} // getKartTeam
//-----------------------------------------------------------------------------
@ -542,53 +507,68 @@ bool SoccerWorld::isCorrectGoal(unsigned int kart_id, bool first_goal) const
} // isCorrectGoal
//-----------------------------------------------------------------------------
void SoccerWorld::updateDefenders()
void SoccerWorld::updateAIData()
{
if (isRaceOver()) return;
float distance = 99999.9f;
int defender = -1;
// Fill the kart distance map
m_red_kdm.clear();
m_blue_kdm.clear();
// Check for red team
for (unsigned int i = 0; i < (unsigned)m_karts.size(); ++i)
for (unsigned int i = 0; i < m_karts.size(); ++i)
{
if (m_karts[i]->getController()->isPlayerController() ||
getKartTeam(m_karts[i]->getWorldKartId()) != SOCCER_TEAM_RED)
if (UserConfigParams::m_arena_ai_stats &&
m_karts[i]->getController()->isPlayerController())
continue;
Vec3 d = this->getGoalLocation(SOCCER_TEAM_RED,
CheckGoal::POINT_CENTER) - m_karts[i]->getXYZ();
if (d.length_2d() <= distance)
if (getKartTeam(m_karts[i]->getWorldKartId()) == SOCCER_TEAM_RED)
{
defender = i;
distance = d.length_2d();
Vec3 rd = m_karts[i]->getXYZ() - getBallPosition();
m_red_kdm.push_back(KartDistanceMap(i, rd.length_2d()));
}
else
{
Vec3 bd = m_karts[i]->getXYZ() - getBallPosition();
m_blue_kdm.push_back(KartDistanceMap(i, bd.length_2d()));
}
}
if (defender != -1) m_red_defender = defender;
// Sort the vectors, so first vector will have the min distance
std::sort(m_red_kdm.begin(), m_red_kdm.end());
std::sort(m_blue_kdm.begin(), m_blue_kdm.end());
distance = 99999.9f;
defender = -1;
// Fill Ball and goals data
m_bgd.updateBallAndGoal(getBallPosition(), getBallHeading());
// Check for blue team
for (unsigned int i = 0; i < (unsigned)m_karts.size(); ++i)
} // updateAIData
//-----------------------------------------------------------------------------
int SoccerWorld::getAttacker(SoccerTeam team) const
{
if (team == SOCCER_TEAM_BLUE && m_blue_kdm.size() > 1)
{
if (m_karts[i]->getController()->isPlayerController() ||
getKartTeam(m_karts[i]->getWorldKartId()) != SOCCER_TEAM_BLUE)
continue;
Vec3 d = this->getGoalLocation(SOCCER_TEAM_BLUE,
CheckGoal::POINT_CENTER) - m_karts[i]->getXYZ();
if (d.length_2d() <= distance)
for (unsigned int i = 1; i < m_blue_kdm.size(); i++)
{
defender = i;
distance = d.length_2d();
// Only AI will do the attack job
if (getKart(m_blue_kdm[i].m_kart_id)
->getController()->isPlayerController())
continue;
return m_blue_kdm[i].m_kart_id;
}
}
else if (team == SOCCER_TEAM_RED && m_red_kdm.size() > 1)
{
for (unsigned int i = 1; i < m_red_kdm.size(); i++)
{
if (getKart(m_red_kdm[i].m_kart_id)
->getController()->isPlayerController())
continue;
return m_red_kdm[i].m_kart_id;
}
}
if (defender != -1) m_blue_defender = defender;
} // updateDefenders
// No attacker
return -1;
} // getAttacker
//-----------------------------------------------------------------------------
int SoccerWorld::getTeamNum(SoccerTeam team) const
@ -609,22 +589,58 @@ unsigned int SoccerWorld::getRescuePositionIndex(AbstractKart *kart)
{
std::map<int, unsigned int>::const_iterator n =
m_kart_position_map.find(kart->getWorldKartId());
if (n != m_kart_position_map.end())
{
return n->second;
}
// Fallback
Log::warn("SoccerWorld", "Unknown kart, using default starting position.");
return 0;
assert (n != m_kart_position_map.end());
return n->second;
} // getRescuePositionIndex
//-----------------------------------------------------------------------------
void SoccerWorld::resetBall()
void SoccerWorld::enterRaceOverState()
{
m_ball->reset();
m_ball->getPhysicalObject()->reset();
} // resetBall
if (UserConfigParams::m_arena_ai_stats)
{
float runtime = (irr_driver->getRealTime()-m_start_time)*0.001f;
Log::verbose("Soccer AI profiling", "Number of frames: %d, Average FPS: %f",
m_frame_count, (float)m_frame_count/runtime);
Log::verbose("Soccer AI profiling", "Time for a team to have 30 goals: %f",
runtime);
// Goal calculation
int red_own_goal = 0;
int blue_own_goal = 0;
for (unsigned i = 0; i < m_red_scorers.size(); i++)
{
// Notice: if a team has own goal, the score will end up in the
// opposite team
if (!m_red_scorers[i].m_correct_goal)
blue_own_goal++;
}
for (unsigned i = 0; i < m_blue_scorers.size(); i++)
{
if (!m_blue_scorers[i].m_correct_goal)
red_own_goal++;
}
int red_goal = ((m_red_scorers.size() - blue_own_goal) >= 0 ?
m_red_scorers.size() - blue_own_goal : 0);
int blue_goal = ((m_blue_scorers.size() - red_own_goal) >= 0 ?
m_blue_scorers.size() - red_own_goal : 0);
Log::verbose("Soccer AI profiling", "Red goal: %d, Red own goal: %d,"
"Blue goal: %d, Blue own goal: %d", red_goal, red_own_goal,
blue_goal, blue_own_goal);
if (getScore(SOCCER_TEAM_BLUE) >= m_goal_target)
Log::verbose("Soccer AI profiling", "Blue team wins");
else
Log::verbose("Soccer AI profiling", "Red team wins");
delete this;
main_loop->abort();
}
else
WorldStatus::enterRaceOverState();
} // enterRaceOverState
//-----------------------------------------------------------------------------
void SoccerWorld::setAITeam()
@ -640,7 +656,15 @@ void SoccerWorld::setAITeam()
for (int i = 0; i < total_player; i++)
{
SoccerTeam team = race_manager->getKartInfo(i).getSoccerTeam();
assert(team != SOCCER_TEAM_NONE);
// Happen in profiling mode
if (team == SOCCER_TEAM_NONE)
{
race_manager->setKartSoccerTeam(i, SOCCER_TEAM_BLUE);
team = SOCCER_TEAM_BLUE;
continue;
}
team == SOCCER_TEAM_BLUE ? blue_player++ : red_player++;
}
@ -666,11 +690,3 @@ void SoccerWorld::setAITeam()
Log::debug("SoccerWorld","blue AI: %d red AI: %d", m_blue_ai, m_red_ai);
} // setAITeam
//-----------------------------------------------------------------------------
const Vec3& SoccerWorld::getGoalLocation(SoccerTeam team,
CheckGoal::PointLocation point) const
{
return (team == SOCCER_TEAM_BLUE ? m_blue_check_goal->getPoint(point) :
m_red_check_goal->getPoint(point));
} // getGoalLocation

View File

@ -23,6 +23,7 @@
#include "states_screens/race_gui_base.hpp"
#include "karts/abstract_kart.hpp"
#include "tracks/check_goal.hpp"
#include "tracks/check_manager.hpp"
#include <IMesh.h>
#include <string>
@ -54,16 +55,213 @@ protected:
PerPlayerDifficulty difficulty) OVERRIDE;
private:
class KartDistanceMap
{
public:
/** World ID of kart. */
unsigned int m_kart_id;
/** Distance to ball from kart */
float m_distance;
bool operator < (const KartDistanceMap& r) const
{
return m_distance < r.m_distance;
}
KartDistanceMap(unsigned int kart_id = 0, float distance = 0.0f)
{
m_kart_id = kart_id;
m_distance = distance;
}
}; // KartDistanceMap
class BallGoalData
{
// These data are used by AI to determine ball aiming angle
private:
// Radius of the ball
float m_radius;
// Slope of the line from ball to the center point of goals
float m_red_goal_slope;
float m_blue_goal_slope;
// The transform only takes the ball heading into account,
// ie no hpr of ball which allowing setting aim point easier
btTransform m_trans;
// Two goals
CheckGoal* m_blue_check_goal;
CheckGoal* m_red_check_goal;
// Location to red/blue goal points from the ball heading point of view
Vec3 m_red_goal_1;
Vec3 m_red_goal_2;
Vec3 m_red_goal_3;
Vec3 m_blue_goal_1;
Vec3 m_blue_goal_2;
Vec3 m_blue_goal_3;
public:
void reset()
{
m_red_goal_1 = Vec3(0, 0, 0);
m_red_goal_2 = Vec3(0, 0, 0);
m_red_goal_3 = Vec3(0, 0, 0);
m_blue_goal_1 = Vec3(0, 0, 0);
m_blue_goal_2 = Vec3(0, 0, 0);
m_blue_goal_3 = Vec3(0, 0, 0);
m_red_goal_slope = 1.0f;
m_blue_goal_slope = 1.0f;
m_trans = btTransform(btQuaternion(0, 0, 0, 1), Vec3(0, 0, 0));
} // reset
float getDiameter() const
{
return m_radius * 2;
} // getDiameter
void init(float ball_radius)
{
m_radius = ball_radius;
assert(m_radius > 0.0f);
// Save two goals
unsigned int n = CheckManager::get()->getCheckStructureCount();
for (unsigned int i = 0; i < n; i++)
{
CheckGoal* goal = dynamic_cast<CheckGoal*>
(CheckManager::get()->getCheckStructure(i));
if (goal)
{
if (goal->getTeam())
m_blue_check_goal = goal;
else
m_red_check_goal = goal;
}
}
if (m_blue_check_goal == NULL || m_red_check_goal == NULL)
{
Log::error("SoccerWorld", "Goal(s) is missing!");
}
} // init
void updateBallAndGoal(const Vec3& ball_pos, float heading)
{
btQuaternion quat(Vec3(0, 1, 0), -heading);
m_trans = btTransform(btQuaternion(Vec3(0, 1, 0), heading),
ball_pos);
// Red goal
m_red_goal_1 = quatRotate(quat, m_red_check_goal
->getPoint(CheckGoal::POINT_FIRST) - ball_pos);
m_red_goal_2 = quatRotate(quat, m_red_check_goal
->getPoint(CheckGoal::POINT_CENTER) - ball_pos);
m_red_goal_3 = quatRotate(quat, m_red_check_goal
->getPoint(CheckGoal::POINT_LAST) - ball_pos);
// Blue goal
m_blue_goal_1 = quatRotate(quat, m_blue_check_goal
->getPoint(CheckGoal::POINT_FIRST) - ball_pos);
m_blue_goal_2 = quatRotate(quat, m_blue_check_goal
->getPoint(CheckGoal::POINT_CENTER) - ball_pos);
m_blue_goal_3 = quatRotate(quat, m_blue_check_goal
->getPoint(CheckGoal::POINT_LAST) - ball_pos);
// Update the slope:
// Use y = mx + c as an equation from goal center to ball
// As the line always intercept in (0,0) which is the ball location,
// so y(z)/x is the slope , it is used for determine aiming position
// of ball later
m_red_goal_slope = m_red_goal_2.z() / m_red_goal_2.x();
m_blue_goal_slope = m_blue_goal_2.z() / m_blue_goal_2.x();
} // updateBallAndGoal
bool isApproachingGoal(SoccerTeam team) const
{
// If the ball lies between the first and last pos, and faces
// in front of either of them, (inside angular size of goal)
// than it's likely to goal
if (team == SOCCER_TEAM_BLUE)
{
if ((m_blue_goal_1.z() > 0.0f || m_blue_goal_3.z() > 0.0f) &&
((m_blue_goal_1.x() < 0.0f && m_blue_goal_3.x() > 0.0f) ||
(m_blue_goal_3.x() < 0.0f && m_blue_goal_1.x() > 0.0f)))
return true;
}
else
{
if ((m_red_goal_1.z() > 0.0f || m_red_goal_3.z() > 0.0f) &&
((m_red_goal_1.x() < 0.0f && m_red_goal_3.x() > 0.0f) ||
(m_red_goal_3.x() < 0.0f && m_red_goal_1.x() > 0.0f)))
return true;
}
return false;
} // isApproachingGoal
Vec3 getAimPosition(SoccerTeam team, bool reverse) const
{
// If it's likely to goal already, aim the ball straight behind
// should do the job
if (isApproachingGoal(team))
return m_trans(Vec3(0, 0, -1));
// Otherwise do the below:
// This is done by using Pythagorean Theorem and solving the
// equation from ball to goal center (y = (m_***_goal_slope) x)
// We aim behind the ball from the center of the ball to its
// diameter, so 2*m_radius = sqrt (x2 + y2),
// which is next x = sqrt (2*m_radius - y2)
// And than we have x = y / m(m_***_goal_slope)
// After put that in the slope equation, we have
// y = sqrt(2*m_radius*m2 / (1+m2))
float x = 0.0f;
float y = 0.0f;
if (team == SOCCER_TEAM_BLUE)
{
y = sqrt((m_blue_goal_slope * m_blue_goal_slope * m_radius*2) /
(1 + (m_blue_goal_slope * m_blue_goal_slope)));
if (m_blue_goal_2.x() == 0.0f ||
(m_blue_goal_2.x() > 0.0f && m_blue_goal_2.z() > 0.0f) ||
(m_blue_goal_2.x() < 0.0f && m_blue_goal_2.z() > 0.0f))
{
// Determine when y should be negative
y = -y;
}
x = y / m_blue_goal_slope;
}
else
{
y = sqrt((m_red_goal_slope * m_red_goal_slope * m_radius*2) /
(1 + (m_red_goal_slope * m_red_goal_slope)));
if (m_red_goal_2.x() == 0.0f ||
(m_red_goal_2.x() > 0.0f && m_red_goal_2.z() > 0.0f) ||
(m_red_goal_2.x() < 0.0f && m_red_goal_2.z() > 0.0f))
{
y = -y;
}
x = y / m_red_goal_slope;
}
assert (!std::isnan(x));
assert (!std::isnan(y));
// Return the world coordinates
return (reverse ? m_trans(Vec3(-x, 0, -y)) :
m_trans(Vec3(x, 0, y)));
} // getAimPosition
}; // BallGoalData
std::vector<KartDistanceMap> m_red_kdm;
std::vector<KartDistanceMap> m_blue_kdm;
BallGoalData m_bgd;
/** Keep a pointer to the track object of soccer ball */
TrackObject* m_ball;
btRigidBody* m_ball_body;
/** Number of goals needed to win */
int m_goal_target;
bool m_count_down_reached_zero;
/** Whether or not goals can be scored (they are disabled when a point is scored
and re-enabled when the next game can be played)*/
bool m_can_score_points;
SFXBase *m_goal_sound;
/** Timer for displaying goal text*/
@ -72,8 +270,6 @@ private:
int m_ball_hitter;
/** Goals data of each team scored */
int m_red_goal;
int m_blue_goal;
std::vector<ScorerData> m_red_scorers;
std::vector<float> m_red_score_times;
std::vector<ScorerData> m_blue_scorers;
@ -85,34 +281,29 @@ private:
/** Data generated from navmesh */
std::vector<int> m_kart_on_node;
int m_ball_on_node;
Vec3 m_ball_position;
CheckGoal* m_red_check_goal;
CheckGoal* m_blue_check_goal;
int m_red_defender;
int m_blue_defender;
int m_red_ai;
int m_blue_ai;
float m_ball_heading;
/** Set the team for the karts */
void initKartList();
/** Function to save check goal for the getting of coordinate */
void initGoal();
/** Function to update the locations of all karts on the polygon map */
void updateKartNodes();
/** Function to update the location the ball on the polygon map */
void updateBallPosition(float dt);
/** Clean up */
void resetAllPosition();
/** Reset the ball to original starting position. */
void resetBall();
/** Function to update the AI which is the closest to its goal to defend. */
void updateDefenders();
/** Function to update data for AI usage. */
void updateAIData();
/** Get number of teammates in a team, used by starting position assign. */
int getTeamNum(SoccerTeam team) const;
/** Profiling usage */
int m_frame_count;
int m_start_time;
public:
SoccerWorld();
@ -122,7 +313,6 @@ public:
// clock events
virtual bool isRaceOver() OVERRIDE;
virtual void terminateRace() OVERRIDE;
virtual void countdownReachedZero() OVERRIDE;
// overriding World methods
@ -132,10 +322,12 @@ public:
virtual bool useFastMusicNearEnd() const OVERRIDE { return false; }
virtual void getKartsDisplayInfo(
std::vector<RaceGUIBase::KartIconDisplayInfo> *info) OVERRIDE {}
std::vector<RaceGUIBase::KartIconDisplayInfo> *info) OVERRIDE {}
virtual bool raceHasLaps() OVERRIDE { return false; }
virtual void enterRaceOverState() OVERRIDE;
virtual const std::string& getIdent() const OVERRIDE;
virtual void update(float dt) OVERRIDE;
@ -150,8 +342,11 @@ public:
/** Get the team of kart in soccer world (including AIs) */
SoccerTeam getKartTeam(unsigned int kart_id) const;
// ------------------------------------------------------------------------
const int getScore(SoccerTeam team) const
{ return (team == SOCCER_TEAM_BLUE ? m_blue_goal : m_red_goal); }
int getScore(SoccerTeam team) const
{
return (team == SOCCER_TEAM_BLUE ? m_blue_scorers.size() :
m_red_scorers.size());
}
// ------------------------------------------------------------------------
const std::vector<ScorerData>& getScorers(SoccerTeam team) const
{ return (team == SOCCER_TEAM_BLUE ? m_blue_scorers : m_red_scorers); }
@ -162,26 +357,47 @@ public:
m_blue_score_times : m_red_score_times);
}
// ------------------------------------------------------------------------
const int getKartNode(unsigned int kart_id) const
{ return m_kart_on_node[kart_id]; }
int getKartNode(unsigned int kart_id) const
{ return m_kart_on_node[kart_id]; }
// ------------------------------------------------------------------------
const int getBallNode() const
{ return m_ball_on_node; }
int getBallNode() const
{ return m_ball_on_node; }
// ------------------------------------------------------------------------
const Vec3& getBallPosition() const
{ return m_ball_position; }
{ return (Vec3&)m_ball_body->getCenterOfMassTransform().getOrigin(); }
// ------------------------------------------------------------------------
bool ballNotMoving() const
{
return (m_ball_body->getLinearVelocity().x() == 0.0f ||
m_ball_body->getLinearVelocity().z() == 0.0f);
}
// ------------------------------------------------------------------------
float getBallHeading() const
{ return m_ball_heading; }
// ------------------------------------------------------------------------
float getBallDiameter() const
{ return m_bgd.getDiameter(); }
// ------------------------------------------------------------------------
bool ballApproachingGoal(SoccerTeam team) const
{ return m_bgd.isApproachingGoal(team); }
// ------------------------------------------------------------------------
Vec3 getBallAimPosition(SoccerTeam team, bool reverse = false) const
{ return m_bgd.getAimPosition(team, reverse); }
// ------------------------------------------------------------------------
bool isCorrectGoal(unsigned int kart_id, bool first_goal) const;
// ------------------------------------------------------------------------
const int getDefender(SoccerTeam team) const
int getBallChaser(SoccerTeam team) const
{
return (team == SOCCER_TEAM_BLUE ? m_blue_defender : m_red_defender);
// Only AI call this function, so each team should have at least a kart
assert(m_blue_kdm.size() > 0 && m_red_kdm.size() > 0);
return (team == SOCCER_TEAM_BLUE ? m_blue_kdm[0].m_kart_id :
m_red_kdm[0].m_kart_id);
}
// ------------------------------------------------------------------------
void setAITeam();
/** Get the AI who will attack the other team ball chaser. */
int getAttacker(SoccerTeam team) const;
// ------------------------------------------------------------------------
const Vec3& getGoalLocation(SoccerTeam team,
CheckGoal::PointLocation point) const;
void setAITeam();
// ------------------------------------------------------------------------
}; // SoccerWorld

View File

@ -20,6 +20,7 @@
#include <string>
#include <IMeshSceneNode.h>
#include "main_loop.hpp"
#include "audio/music_manager.hpp"
#include "config/user_config.hpp"
#include "graphics/camera.hpp"
@ -46,6 +47,12 @@ ThreeStrikesBattle::ThreeStrikesBattle() : WorldWithRank()
m_tire = irr_driver->getMesh(file_manager->getAsset(FileManager::MODEL,
"tire.b3d") );
irr_driver->grabAllTextures(m_tire);
m_total_rescue = 0;
m_frame_count = 0;
m_start_time = irr_driver->getRealTime();
m_total_hit = 0;
} // ThreeStrikesBattle
//-----------------------------------------------------------------------------
@ -160,8 +167,12 @@ void ThreeStrikesBattle::kartHit(const unsigned int kart_id)
if (isRaceOver()) return;
assert(kart_id < m_karts.size());
// make kart lose a life
m_kart_info[kart_id].m_lives--;
// make kart lose a life, ignore if in profiling mode
if (!UserConfigParams::m_arena_ai_stats)
m_kart_info[kart_id].m_lives--;
if (UserConfigParams::m_arena_ai_stats)
m_total_hit++;
// record event
BattleEvent evt;
@ -369,6 +380,9 @@ void ThreeStrikesBattle::update(float dt)
m_tires.push_back(tire_obj);
} // while
if (UserConfigParams::m_arena_ai_stats)
m_frame_count++;
} // update
//-----------------------------------------------------------------------------
@ -429,6 +443,9 @@ void ThreeStrikesBattle::updateKartRanks()
*/
bool ThreeStrikesBattle::isRaceOver()
{
if (UserConfigParams::m_arena_ai_stats)
return (irr_driver->getRealTime()-m_start_time)*0.001f > 20.0f;
// for tests : never over when we have a single player there :)
if (race_manager->getNumberOfKarts()==1 &&
getCurrentNumKarts()==1 &&
@ -515,3 +532,20 @@ void ThreeStrikesBattle::getKartsDisplayInfo(
}
} // getKartsDisplayInfo
//-----------------------------------------------------------------------------
void ThreeStrikesBattle::enterRaceOverState()
{
if (UserConfigParams::m_arena_ai_stats)
{
float runtime = (irr_driver->getRealTime()-m_start_time)*0.001f;
Log::verbose("Battle AI profiling", "Number of frames: %d, Average FPS: %f",
m_frame_count, (float)m_frame_count/runtime);
Log::verbose("Battle AI profiling", "Total rescue: %d , hits %d in %f seconds",
m_total_rescue, m_total_hit, runtime);
delete this;
main_loop->abort();
}
else
WorldStatus::enterRaceOverState();
} // enterRaceOverState

View File

@ -74,6 +74,12 @@ private:
/** Function to update the locations of all karts on the polygon map */
void updateKartNodes();
/** Profiling usage */
int m_total_rescue;
int m_frame_count;
int m_start_time;
int m_total_hit;
public:
/** Used to show a nice graph when battle is over */
@ -108,10 +114,12 @@ public:
virtual void update(float dt);
virtual void kartAdded(AbstractKart* kart, scene::ISceneNode* node);
virtual void enterRaceOverState() OVERRIDE;
int getKartNode(unsigned int kart_id) const;
void updateKartRanks();
void increaseRescueCount() { m_total_rescue++; }
}; // ThreeStrikesBattles

View File

@ -226,6 +226,8 @@ public:
/** Add body to dynamic world */
void addBody();
// ------------------------------------------------------------------------
float getRadius() const { return m_radius; }
// ------------------------------------------------------------------------
const std::string& getOnKartCollisionFunction() const { return m_on_kart_collision; }
// ------------------------------------------------------------------------
const std::string& getOnItemCollisionFunction() const { return m_on_item_collision; }

View File

@ -75,6 +75,8 @@ RaceManager::RaceManager()
m_coin_target = 0;
m_started_from_overworld = false;
m_have_kart_last_position_on_overworld = false;
setMaxGoal(0);
setTimeTarget(0.0f);
setReverseTrack(false);
setRecordRace(false);
setRaceGhostKarts(false);

View File

@ -332,7 +332,6 @@ private:
unsigned int m_num_finished_karts;
unsigned int m_num_finished_players;
int m_coin_target;
bool m_has_time_target;
float m_time_target;
int m_goal_target;
@ -417,9 +416,13 @@ public:
void computeRandomKartList();
// ------------------------------------------------------------------------
bool hasTimeTarget() const { return m_has_time_target; }
bool hasTimeTarget() const { return m_time_target > 0.0f; }
// ------------------------------------------------------------------------
void setMaxGoal(int maxGoal){ m_goal_target = maxGoal; }
void setMaxGoal(int max_goal)
{
m_time_target = 0.0f;
m_goal_target = max_goal;
} // setMaxGoal
// ------------------------------------------------------------------------
int getMaxGoal(){ return m_goal_target; }
// ------------------------------------------------------------------------
@ -458,9 +461,8 @@ public:
void setMajorMode(MajorRaceModeType mode) { m_major_mode = mode; }
// ------------------------------------------------------------------------
void setMinorMode(MinorRaceModeType mode)
{
{
m_minor_mode = mode;
m_has_time_target = false;
} // setMinorMode
// ------------------------------------------------------------------------
void setNumKarts(int num)
@ -470,10 +472,10 @@ public:
m_ai_superpower = SUPERPOWER_NONE;
} // setNumKarts
// ------------------------------------------------------------------------
void setTimeTarget(float num)
void setTimeTarget(float time)
{
m_has_time_target = true;
m_time_target = num;
m_goal_target = 0;
m_time_target = time;
} // setTimeTarget
// ------------------------------------------------------------------------
const RemoteKartInfo& getKartInfo(unsigned int n) const
@ -495,7 +497,7 @@ public:
MinorRaceModeType getMinorMode() const { return m_minor_mode; }
// ------------------------------------------------------------------------
unsigned int getNumPlayers() const
{
{
return (unsigned int) m_player_karts.size();
} // getNumPlayers
// ------------------------------------------------------------------------

View File

@ -397,10 +397,10 @@ void RaceGUI::drawGlobalMiniMap()
irr_driver->getTexture(FileManager::GUI, "soccer_ball_normal.png");
core::rect<s32> source(core::position2di(0, 0), icon->getSize());
core::rect<s32> position(m_map_left+(int)(draw_at.getX()-(m_minimap_ai_size>>2)),
lower_y -(int)(draw_at.getY()+(m_minimap_ai_size>>2)),
m_map_left+(int)(draw_at.getX()+(m_minimap_ai_size>>2)),
lower_y -(int)(draw_at.getY()-(m_minimap_ai_size>>2)));
core::rect<s32> position(m_map_left+(int)(draw_at.getX()-(m_minimap_player_size/2.5f)),
lower_y -(int)(draw_at.getY()+(m_minimap_player_size/2.5f)),
m_map_left+(int)(draw_at.getX()+(m_minimap_player_size/2.5f)),
lower_y -(int)(draw_at.getY()-(m_minimap_player_size/2.5f)));
draw2DImage(icon, position, source, NULL, NULL, true);
}

View File

@ -23,7 +23,9 @@
#include <IMeshSceneNode.h>
#include "config/user_config.hpp"
#include "io/xml_node.hpp"
#include "items/item_manager.hpp"
#include "race/race_manager.hpp"
#include "tracks/navmesh.hpp"
#include "utils/log.hpp"
@ -33,7 +35,8 @@ BattleGraph * BattleGraph::m_battle_graph = NULL;
/** Constructor, Creates a navmesh, builds a graph from the navmesh and
* then runs shortest path algorithm to find and store paths to be used
* by the AI. */
BattleGraph::BattleGraph(const std::string &navmesh_file_name)
BattleGraph::BattleGraph(const std::string &navmesh_file_name,
const XMLNode& node)
{
m_items_on_graph.clear();
@ -41,6 +44,9 @@ BattleGraph::BattleGraph(const std::string &navmesh_file_name)
m_navmesh_file = navmesh_file_name;
buildGraph(NavMesh::get());
computeFloydWarshall();
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER)
loadGoalNodes(node);
} // BattleGraph
// -----------------------------------------------------------------------------
@ -122,7 +128,6 @@ void BattleGraph::computeFloydWarshall()
} // computeFloydWarshall
// -----------------------------------------------------------------------------
/** Maps items on battle graph */
void BattleGraph::findItemsOnGraphNodes()
{
@ -152,7 +157,6 @@ void BattleGraph::findItemsOnGraphNodes()
} // findItemsOnGraphNodes
// -----------------------------------------------------------------------------
int BattleGraph::pointToNode(const int cur_node,
const Vec3& cur_point,
bool ignore_vertical) const
@ -215,10 +219,64 @@ int BattleGraph::pointToNode(const int cur_node,
} // pointToNode
// -----------------------------------------------------------------------------
const int BattleGraph::getNextShortestPathPoly(int i, int j) const
{
if (i == BattleGraph::UNKNOWN_POLY || j == BattleGraph::UNKNOWN_POLY)
return BattleGraph::UNKNOWN_POLY;
return m_parent_poly[j][i];
} // getNextShortestPathPoly
// -----------------------------------------------------------------------------
const bool BattleGraph::differentNodeColor(int n, NodeColor* c) const
{
std::set<int>::iterator it;
it = m_red_node.find(n);
if (it != m_red_node.end())
{
*c = COLOR_RED;
return true;
}
it = m_blue_node.find(n);
if (it != m_blue_node.end())
{
*c = COLOR_BLUE;
return true;
}
return false;
} // differentNodeColor
// -----------------------------------------------------------------------------
void BattleGraph::loadGoalNodes(const XMLNode& node)
{
m_red_node.clear();
m_blue_node.clear();
const XMLNode *check_node = node.getNode("checks");
for (unsigned int i = 0; i < check_node->getNumNodes(); i++)
{
const XMLNode *goal = check_node->getNode(i);
if (goal->getName() =="goal")
{
Vec3 p1, p2;
bool first_goal = false;
goal->get("first_goal", &first_goal);
goal->get("p1", &p1);
goal->get("p2", &p2);
int first = pointToNode(/*cur_node*/-1, p1, true);
int last = pointToNode(/*cur_node*/-1, p2, true);
first_goal ? m_blue_node.insert(first) : m_red_node.insert(first);
first_goal ? m_blue_node.insert(last) : m_red_node.insert(last);
while (first != last)
{
// Find all the nodes which connect the two points of
// goal, notice: only work if it's a straight line
first = getNextShortestPathPoly(first, last);
first_goal ? m_blue_node.insert(first) :
m_red_node.insert(first);
}
}
}
} // loadGoalNodes

View File

@ -19,9 +19,9 @@
#ifndef HEADER_BATTLE_GRAPH_HPP
#define HEADER_BATTLE_GRAPH_HPP
#include <vector>
#include <string>
#include <set>
#include <vector>
#include "tracks/graph_structure.hpp"
#include "tracks/navmesh.hpp"
@ -30,6 +30,7 @@ class GraphStructure;
class Item;
class ItemManager;
class Navmesh;
class XMLNode;
/**
* \ingroup tracks
@ -57,10 +58,14 @@ private:
std::vector< std::pair<const Item*, int> > m_items_on_graph;
std::set<int> m_red_node;
std::set<int> m_blue_node;
void buildGraph(NavMesh*);
void computeFloydWarshall();
void loadGoalNodes(const XMLNode& node);
BattleGraph(const std::string &navmesh_file_name);
BattleGraph(const std::string &navmesh_file_name, const XMLNode& node);
~BattleGraph(void);
// ------------------------------------------------------------------------
@ -79,6 +84,8 @@ private:
// ------------------------------------------------------------------------
virtual const bool hasLapLine() const
{ return false; }
// ------------------------------------------------------------------------
virtual const bool differentNodeColor(int n, NodeColor* c) const;
public:
static const int UNKNOWN_POLY;
@ -88,10 +95,11 @@ public:
// ----------------------------------------------------------------------
/** Asserts that no BattleGraph instance exists. Then
* creates a BattleGraph instance. */
static void create(const std::string &navmesh_file_name)
static void create(const std::string &navmesh_file_name,
const XMLNode& node)
{
assert(m_battle_graph==NULL);
m_battle_graph = new BattleGraph(navmesh_file_name);
m_battle_graph = new BattleGraph(navmesh_file_name, node);
} // create
// ----------------------------------------------------------------------

View File

@ -55,8 +55,7 @@ void CheckGoal::update(float dt)
if (world)
{
const Vec3 &xyz = world->getBallPosition();
if (isTriggered(m_previous_ball_position, xyz, -1))
if (isTriggered(m_previous_ball_position, world->getBallPosition(), -1))
{
if (UserConfigParams::m_check_debug)
{
@ -65,7 +64,7 @@ void CheckGoal::update(float dt)
}
trigger(0);
}
m_previous_ball_position = xyz;
m_previous_ball_position = world->getBallPosition();
}
} // update
@ -108,8 +107,7 @@ void CheckGoal::reset(const Track &track)
if (world)
{
const Vec3 &xyz = world->getBallPosition();
m_previous_ball_position = xyz;
m_previous_ball_position = world->getBallPosition();
}
} // reset

View File

@ -152,8 +152,12 @@ void GraphStructure::createMesh(bool show_invisible,
c.setBlue((i%2) ? 0 : 255);
}
NodeColor nc = COLOR_RED;
const bool different_color = differentNodeColor(count, &nc);
// Transfer the 4 points of the current quad to the list of vertices
set3DVerticesOfGraph(count, new_v+4*i, c);
set3DVerticesOfGraph(count, new_v+4*i, (different_color ?
(nc == COLOR_RED ? video::SColor(255, 255, 0, 0) :
video::SColor(255, 0, 0, 255)) : c));
// Set up the indices for the triangles
// (note, afaik with opengl we could use quads directly, but the code

View File

@ -46,6 +46,13 @@ class GraphStructure : public NoCopy
{
protected:
/** Used by soccer field with navmesh to draw goal line. */
enum NodeColor
{
COLOR_BLUE,
COLOR_RED
};
void cleanupDebugMesh();
void destroyRTT();
@ -77,6 +84,7 @@ private:
virtual const bool isNodeInvisible(int n) const = 0;
virtual const bool isNodeInvalid(int n) const = 0;
virtual const bool hasLapLine() const = 0;
virtual const bool differentNodeColor(int n, NodeColor* c) const = 0;
public:
GraphStructure();

View File

@ -90,6 +90,9 @@ private:
// ------------------------------------------------------------------------
virtual const bool hasLapLine() const
{ return true; }
// ------------------------------------------------------------------------
virtual const bool differentNodeColor(int n, NodeColor* c) const
{ return false; }
public:
static const int UNKNOWN_SECTOR;

View File

@ -670,9 +670,9 @@ void Track::startMusic() const
/** Loads the polygon graph for battle, i.e. the definition of all polys, and the way
* they are connected to each other. Input file name is hardcoded for now
*/
void Track::loadBattleGraph()
void Track::loadBattleGraph(const XMLNode &node)
{
BattleGraph::create(m_root+"navmesh.xml");
BattleGraph::create(m_root+"navmesh.xml", node);
if(BattleGraph::get()->getNumNodes()==0)
{
@ -1635,25 +1635,9 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id)
(void)e;
}
// Load the graph only now: this function is called from world, after
// the race gui was created. The race gui is needed since it stores
// the information about the size of the texture to render the mini
// map to.
if (!m_is_arena && !m_is_soccer && !m_is_cutscene) loadQuadGraph(mode_id, reverse_track);
else if ((m_is_arena || m_is_soccer) && !m_is_cutscene && m_has_navmesh)
loadBattleGraph();
ItemManager::create();
// Set the default start positions. Node that later the default
// positions can still be overwritten.
float forwards_distance = 1.5f;
float sidewards_distance = 3.0f;
float upwards_distance = 0.1f;
int karts_per_row = 2;
// Start building the scene graph
// Soccer field with navmesh requires it
// for two goal line to be drawn them in minimap
std::string path = m_root + m_all_modes[mode_id].m_scene;
XMLNode *root = file_manager->createXMLTree(path);
@ -1667,6 +1651,23 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id)
throw std::runtime_error(msg.str());
}
// Load the graph only now: this function is called from world, after
// the race gui was created. The race gui is needed since it stores
// the information about the size of the texture to render the mini
// map to.
if (!m_is_arena && !m_is_soccer && !m_is_cutscene) loadQuadGraph(mode_id, reverse_track);
else if ((m_is_arena || m_is_soccer) && !m_is_cutscene && m_has_navmesh)
loadBattleGraph(*root);
ItemManager::create();
// Set the default start positions. Node that later the default
// positions can still be overwritten.
float forwards_distance = 1.5f;
float sidewards_distance = 3.0f;
float upwards_distance = 0.1f;
int karts_per_row = 2;
const XMLNode *default_start = root->getNode("default-start");
if (default_start)
{

View File

@ -381,7 +381,7 @@ private:
void loadTrackInfo();
void loadQuadGraph(unsigned int mode_id, const bool reverse);
void loadBattleGraph();
void loadBattleGraph(const XMLNode &node);
void convertTrackToBullet(scene::ISceneNode *node);
bool loadMainTrack(const XMLNode &node);
void loadMinimap();