Merge branch 'master' of https://github.com/supertuxkart/stk-code
This commit is contained in:
commit
3ec56386df
@ -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 );
|
||||
|
||||
|
@ -299,6 +299,7 @@ void Skybox::generateSpecularCubemap()
|
||||
}
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glDeleteFramebuffers(1, &fbo);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
} // generateSpecularCubemap
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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]));
|
||||
|
@ -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() {};
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
||||
|
33
src/main.cpp
33
src/main.cpp
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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; }
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
// ------------------------------------------------------------------------
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
// ----------------------------------------------------------------------
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user