Merge remote-tracking branch 'origin/advanced_soccer_ai' into arena_ai_profiling

This commit is contained in:
Benau
2016-05-21 11:41:02 +08:00
40 changed files with 1231 additions and 704 deletions

View File

@@ -1,6 +1,7 @@
# SuperTuxKart Installation Instructions
Note: If you obtained this source code from Github, you also need to download the game assets from Sourceforge using SVN.
Note: If you obtained this source code from Github, you also need to download
the game assets from Sourceforge using SVN.
`svn checkout https://svn.code.sf.net/p/supertuxkart/code/stk-assets stk-assets`
@@ -26,8 +27,10 @@ First, make sure that you have the following packages installed:
Ubuntu command:
```
sudo apt-get install autoconf automake build-essential cmake libogg-dev libvorbis-dev libopenal-dev libxxf86vm-dev \
libgl1-mesa-dev libglu1-mesa-dev libcurl4-openssl-dev libfribidi-dev libbluetooth-dev libxrandr-dev libfreetype6-dev
sudo apt-get install build-essential cmake libbluetooth-dev \
libcurl4-gnutls-dev libfreetype6-dev libfribidi-dev libgl1-mesa-dev \
libjpeg-dev libogg-dev libopenal-dev libpng-dev libvorbis-dev libxrandr-dev \
mesa-common-dev pkg-config zlib1g-dev
```
Unpack the files from the tarball like this:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -4,7 +4,7 @@ out vec4 FragColor;
void main(void)
{
vec3 eyedir = vec3(gl_FragCoord.xy / screen, 1.);
vec3 eyedir = vec3(mod(gl_FragCoord.xy, screen) / screen, 1.);
eyedir = 2.0 * eyedir - 1.0;
vec4 tmp = (InverseProjectionMatrix * vec4(eyedir, 1.));
tmp /= tmp.w;

View File

@@ -8,28 +8,28 @@
<summary>A racing game</summary>
<description>
<p>
SuperTuxKart is a fun 3D kart racing game.
You can play with up to 4 friends on one PC, racing against each other or
just try to beat the computer.
Supertuxkart is a free 3D kart racing game, with a focus on having fun over
realism. You can play with up to 4 friends on one PC, racing against each
other, or try to beat the computer in single-player mode.
</p>
<p>
See the great lighthouse or drive through the sand and visit the pyramids.
Race underground or in space, watching the stars passing by.
Have some rest under the palms on the beach (watching the other karts
overtaking you :) ).
But don't eat the bananas! Watch for bowling balls, plungers, bubble gum
and cakes thrown by opponents.
Race underground or in space, watching the stars pass by. Or rest under the
palm trees on the beach, watching the other karts overtake you. But don't eat
the bananas! Watch for bowling balls, plungers, bubble gum and cakes thrown by
your opponents.
</p>
<p>
You can do a single race against other karts, compete in one of several
Grand Prix, try to beat the high score in time trials on your own, play
battle mode against your friends, and more!
You can do a single race against other karts, compete in one of several Grand
Prix, try to beat the high score in time trials on your own, play battle mode
against the computer or your friends, and more!
</p>
</description>
<url type="homepage">http://supertuxkart.sourceforge.net/</url>
<screenshots>
<screenshot type="default">http://supertuxkart.sourceforge.net/persistent/images/4/4d/SuperTuxKart_0.8_screenshot.jpg</screenshot>
<screenshot>http://supertuxkart.sourceforge.net/persistent/images/1/1f/SuperTuxKart_0.8_screenshot4.jpg</screenshot>
<screenshot type="default">http://supertuxkart.sourceforge.net/persistent/images/4/4e/Supertuxkart-0.9-screenshot-2.jpg</screenshot>
<screenshot>http://supertuxkart.sourceforge.net/persistent/images/a/a9/Supertuxkart-0.9-screenshot-1.jpg</screenshot>
<screenshot>http://supertuxkart.sourceforge.net/persistent/images/6/63/Supertuxkart-0.9-screenshot-3.jpg</screenshot>
</screenshots>
<updatecontact>supertuxkart-devel@lists.sourceforge.net</updatecontact>
</application>

View File

@@ -16,3 +16,4 @@ add_library(glew STATIC
)
add_definitions(-DGLEW_STATIC)
add_definitions(-DGLEW_NO_GLU)

View File

@@ -283,23 +283,24 @@ 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
if (lc) *lc = local_coordinates;
// on_side: tell whether it's left or right hand side
if (pos_data == NULL) return;
// lhs: tell whether it's left or right hand side
if (local_coordinates.getX() < 0)
pos_data->on_side = true;
pos_data->lhs = true;
else
pos_data->on_side = false;
pos_data->lhs = false;
// behind: tell whether it's behind or not
if (local_coordinates.getZ() < 0)

View File

@@ -65,7 +65,7 @@ protected:
static int m_test_ai;
/** Position info structure of targets. */
struct posData {bool behind; bool on_side; float angle; float distance;};
struct posData {bool behind; bool lhs; float angle; float distance;};
void setControllerName(const std::string &name);
float steerToPoint(const Vec3 &point);
@@ -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

@@ -33,6 +33,7 @@ ArenaAI::ArenaAI(AbstractKart *kart)
: AIBaseController(kart)
{
m_debug_sphere = NULL;
m_debug_sphere_next = NULL;
} // ArenaAI
//-----------------------------------------------------------------------------
@@ -49,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;
@@ -75,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())
{
@@ -100,6 +105,7 @@ void ArenaAI::update(float dt)
if (m_is_uturn)
{
resetAfterStop();
handleArenaUTurn(dt);
}
else
@@ -154,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;
@@ -201,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()) >
@@ -233,16 +241,17 @@ 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());
#endif
if (m_cur_kart_pos_data.behind)
{
m_adjusting_side = m_cur_kart_pos_data.on_side;
m_adjusting_side = m_cur_kart_pos_data.lhs;
m_is_uturn = true;
}
else
@@ -262,11 +271,23 @@ void ArenaAI::handleArenaSteering(const float dt)
checkPosition(m_target_point, &m_cur_kart_pos_data);
#ifdef AI_DEBUG
m_debug_sphere->setPosition(m_target_point.toIrrVector());
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);
m_debug_sphere->setPosition(m_path_corners[1].toIrrVector());
m_debug_sphere_next->setPosition(m_path_corners[2].toIrrVector());
}
else
{
m_debug_sphere->setVisible(false);
m_debug_sphere_next->setVisible(false);
}*/
#endif
if (m_cur_kart_pos_data.behind)
{
m_adjusting_side = m_cur_kart_pos_data.on_side;
m_adjusting_side = m_cur_kart_pos_data.lhs;
m_is_uturn = true;
}
else
@@ -288,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 =
@@ -306,13 +329,10 @@ void ArenaAI::handleArenaBanana()
{
// Check whether it's straight ahead towards a banana
// If so, adjust target point
banana_lc = (banana_pos.on_side ? banana_lc + Vec3 (2, 0, 0) :
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;
}
@@ -478,28 +498,28 @@ void ArenaAI::stringPull(const Vec3& start_pos, const Vec3& end_pos)
*/
void ArenaAI::handleArenaBraking()
{
m_controls->m_brake = false;
if (getCurrentNode() == BattleGraph::UNKNOWN_POLY ||
m_target_node == BattleGraph::UNKNOWN_POLY) return;
// A kart will not brake when the speed is already slower than this
// value. This prevents a kart from going too slow (or even backwards)
// in tight curves.
const float MIN_SPEED = 5.0f;
std::vector<Vec3> points;
if (forceBraking() && m_kart->getSpeed() > MIN_SPEED)
{
// Brake now
m_controls->m_brake = true;
return;
}
points.push_back(m_kart->getXYZ());
points.push_back(m_path_corners[0]);
points.push_back((m_path_corners.size()>=2) ? m_path_corners[1] : m_path_corners[0]);
m_controls->m_brake = false;
float current_curve_radius = determineTurnRadius(points);
if (getCurrentNode() == BattleGraph::UNKNOWN_POLY ||
m_target_node == BattleGraph::UNKNOWN_POLY) return;
Vec3 d1 = m_kart->getXYZ() - m_target_point;
Vec3 d2 = m_kart->getXYZ() - m_path_corners[0];
if (d1.length2_2d() < d2.length2_2d())
current_curve_radius = d1.length_2d();
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]));
float max_turn_speed = m_kart->getSpeedForTurnRadius(current_curve_radius);
@@ -516,55 +536,44 @@ void ArenaAI::handleArenaBraking()
* location of AI, first corner and the second corner. Once the constants are
* computed, a formula is used to find the radius of curvature at the kart's
* current location.
* NOTE: This method does not apply enough braking, should think of something
* else.
*/
float ArenaAI::determineTurnRadius( std::vector<Vec3>& points )
float ArenaAI::determineTurnRadius(const Vec3& p1, const Vec3& p2,
const Vec3& p3)
{
// Declaring variables
float a, b;
irr::core::CMatrix4<float> A;
irr::core::CMatrix4<float> X;
irr::core::CMatrix4<float> B;
// The parabola function is as following: y=ax2+bx+c
// No need to calculate c as after differentiating c will be zero
const float eps = 0.01f;
const float denominator = (p1.x() - p2.x()) * (p1.x() - p3.x()) *
(p2.x() - p3.x());
//Populating matrices
for (unsigned int i = 0; i < 3; i++)
{
A(i, 0) = points[i].x()*points[i].x();
A(i, 1) = points[i].x();
A(i, 2) = 1.0f;
A(i, 3) = 0.0f;
}
A(3, 0) = A(3, 1) = A(3, 2) = 0.0f;
A(3, 3) = 1.0f;
// Avoid nan, this will happen if three values of coordinates x are too
// close together, ie a straight line, return a large radius
// so no braking is needed
if (fabsf(denominator) < eps) return 25.0f;
for (unsigned int i = 0; i < 3; i++)
{
B(i, 0) = points[i].z();
B(i, 1) = 0.0f;
B(i, 2) = 0.0f;
B(i, 3) = 0.0f;
}
B(3, 0) = B(3, 1) = B(3, 2) = B(3, 3) = 0.0f;
const float a = (p3.x() * (p2.z() - p1.z()) +
p2.x() * (p1.z() - p3.z()) +
p1.x() * (p3.z() - p2.z())) / denominator;
//Computing inverse : X = inv(A)*B
irr::core::CMatrix4<float> invA;
if (!A.getInverse(invA))
return -1;
// Should not happen, otherwise y=c which is a straight line
if (fabsf(a) < eps) return 25.0f;
X = invA*B;
a = X(0, 0);
b = X(0, 1);
//c = X(0, 2);
const float b = (p3.x() * p3.x() * (p1.z() - p2.z()) +
p2.x() * p2.x() * (p3.z() - p1.z()) +
p1.x() * p1.x() * (p2.z() - p3.z())) / denominator;
float x = points.front().x();
//float z = a*pow(x, 2) + b*x + c;
float dx_by_dz = 2*a*x + b;
float d2x_by_dz = 2*a;
// Differentiate the function, so y=ax2+bx+c will become y=2ax+b for dy_dx,
// y=2a for d2y_dx2
// Use the p1 (current location of AI) as x
const float dy_dx = 2 * a * p1.x() + b;
const float d2y_dx2 = 2 * a;
float radius = pow(abs(1 + pow(dx_by_dz, 2)), 1.5f)/ abs(d2x_by_dz);
// Calculate the radius of curvature at current location of AI
const float radius = pow(1 + pow(dy_dx, 2), 1.5f) / fabsf(d2y_dx2);
assert(!std::isnan(radius));
return radius;
// Avoid returning too large radius
return (radius > 25.0f ? 25.0f : radius);
} // determineTurnRadius
//-----------------------------------------------------------------------------
@@ -663,7 +672,8 @@ void ArenaAI::handleArenaItems(const float dt)
if (m_time_since_last_shot < 1.0f) break;
if (m_closest_kart_pos_data.distance < 6.0f &&
(difficulty || perfect_aim))
(difficulty || perfect_aim) &&
!m_closest_kart->isInvulnerable())
{
m_controls->m_fire = true;
m_controls->m_look_back = fire_behind;

View File

@@ -56,6 +56,7 @@ protected:
/** For debugging purpose: a sphere indicating where the AI
* is targeting at. */
irr::scene::ISceneNode *m_debug_sphere;
irr::scene::ISceneNode *m_debug_sphere_next;
/** The node(poly) at which the target point lies in. */
int m_target_node;
@@ -63,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
@@ -104,7 +108,8 @@ private:
float m_time_since_uturn;
void checkIfStuck(const float dt);
float determineTurnRadius(std::vector<Vec3>& points);
float determineTurnRadius(const Vec3& p1, const Vec3& p2,
const Vec3& p3);
void findPortals(int start, int end);
void handleArenaAcceleration(const float dt);
void handleArenaBanana();
@@ -116,8 +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 directDrive() { return m_avoid_eating_banana; }
public:
ArenaAI(AbstractKart *kart);
virtual ~ArenaAI() {};

View File

@@ -41,8 +41,11 @@ BattleAI::BattleAI(AbstractKart *kart)
#ifdef AI_DEBUG
video::SColor col_debug(128, 128, 0, 0);
video::SColor col_debug_next(128, 0, 128, 128);
m_debug_sphere = irr_driver->addSphere(1.0f, col_debug);
m_debug_sphere->setVisible(true);
m_debug_sphere_next = irr_driver->addSphere(1.0f, col_debug_next);
m_debug_sphere_next->setVisible(true);
#endif
m_world = dynamic_cast<ThreeStrikesBattle*>(World::getWorld());
m_track = m_world->getTrack();
@@ -59,6 +62,7 @@ BattleAI::~BattleAI()
{
#ifdef AI_DEBUG
irr_driver->removeNode(m_debug_sphere);
irr_driver->removeNode(m_debug_sphere_next);
#endif
} // ~BattleAI
@@ -157,12 +161,12 @@ void BattleAI::findTarget()
}
} // findTarget
// ------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int BattleAI::getCurrentNode() const
{
return m_world->getKartNode(m_kart->getWorldKartId());
} // getCurrentNode
// ------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool BattleAI::isWaiting() const
{
return m_world->isStartPhase();

View File

@@ -140,8 +140,7 @@ SkiddingAI::SkiddingAI(AbstractKart *kart)
} // SkiddingAI
//-----------------------------------------------------------------------------
/** The destructor deletes the shared TrackInfo objects if no more SkiddingAI
* instances are around.
/** Destructor, mostly to clean up debug data structures.
*/
SkiddingAI::~SkiddingAI()
{
@@ -183,10 +182,10 @@ void SkiddingAI::reset()
QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node);
if(m_track_node==QuadGraph::UNKNOWN_SECTOR)
{
Log::error("SkiddingAI",
"Invalid starting position for '%s' - not on track"
" - can be ignored.",
m_kart->getIdent().c_str());
Log::error(getControllerName().c_str(),
"Invalid starting position for '%s' - not on track"
" - can be ignored.",
m_kart->getIdent().c_str());
m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ());
}
@@ -400,8 +399,8 @@ void SkiddingAI::handleBraking()
{
#ifdef DEBUG
if(m_ai_debug)
Log::debug("SkiddingAI", "braking: %s ahead of leader.",
m_kart->getIdent().c_str());
Log::debug(getControllerName().c_str(), "braking: %s ahead of leader.",
m_kart->getIdent().c_str());
#endif
m_controls->m_brake = true;
@@ -420,8 +419,9 @@ void SkiddingAI::handleBraking()
{
#ifdef DEBUG
if(m_ai_debug)
Log::debug("SkiddingAI", "%s not aligned with track.",
m_kart->getIdent().c_str());
Log::debug(getControllerName().c_str(),
"%s not aligned with track.",
m_kart->getIdent().c_str());
#endif
m_controls->m_brake = true;
return;
@@ -439,11 +439,11 @@ void SkiddingAI::handleBraking()
m_controls->m_brake = true;
#ifdef DEBUG
if(m_ai_debug)
Log::debug("SkiddingAI",
"speed %f too tight curve: radius %f ",
m_kart->getSpeed(),
m_kart->getIdent().c_str(),
m_current_curve_radius);
Log::debug(getControllerName().c_str(),
"speed %f too tight curve: radius %f ",
m_kart->getSpeed(),
m_kart->getIdent().c_str(),
m_current_curve_radius);
#endif
}
return;
@@ -484,7 +484,8 @@ void SkiddingAI::handleSteering(float dt)
#ifdef AI_DEBUG
m_debug_sphere[0]->setPosition(QuadGraph::get()->getQuadOfNode(next)
.getCenter().toIrrVector());
Log::debug("skidding_ai","-Outside of road: steer to center point.");
Log::debug(getControllerName().c_str(),
"Outside of road: steer to center point.");
#endif
}
//If we are going to crash against a kart, avoid it if it doesn't
@@ -494,12 +495,12 @@ void SkiddingAI::handleSteering(float dt)
//-1 = left, 1 = right, 0 = no crash.
if( m_start_kart_crash_direction == 1 )
{
steer_angle = steerToAngle(next, -M_PI*0.5f );
steer_angle = steerToAngle(next, M_PI*0.5f );
m_start_kart_crash_direction = 0;
}
else if(m_start_kart_crash_direction == -1)
{
steer_angle = steerToAngle(next, M_PI*0.5f);
steer_angle = steerToAngle(next, -M_PI*0.5f);
m_start_kart_crash_direction = 0;
}
else
@@ -507,18 +508,19 @@ void SkiddingAI::handleSteering(float dt)
if(m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() ) >
m_world->getDistanceToCenterForKart( m_crashes.m_kart ))
{
steer_angle = steerToAngle(next, -M_PI*0.5f );
steer_angle = steerToAngle(next, M_PI*0.5f );
m_start_kart_crash_direction = 1;
}
else
{
steer_angle = steerToAngle(next, M_PI*0.5f );
steer_angle = steerToAngle(next, -M_PI*0.5f );
m_start_kart_crash_direction = -1;
}
}
#ifdef AI_DEBUG
Log::debug("skidding_ai", "- Velocity vector crashes with kart "
Log::debug(getControllerName().c_str(),
"Velocity vector crashes with kart "
"and doesn't crashes with road : steer 90 "
"degrees away from kart.");
#endif
@@ -700,8 +702,8 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
}
if(m_ai_debug)
Log::debug("SkiddingAI", "%s unselects item.",
m_kart->getIdent().c_str());
Log::debug(getControllerName().c_str(), "%s unselects item.",
m_kart->getIdent().c_str());
// Otherwise remove the pre-selected item (and start
// looking for a new item).
m_item_to_collect = NULL;
@@ -770,9 +772,10 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
.toIrrVector());
#endif
if(m_ai_debug)
Log::debug("SkiddingAI", "%s selects item type '%d'.",
m_kart->getIdent().c_str(),
item_to_collect->getType());
Log::debug(getControllerName().c_str(),
"%s selects item type '%d'.",
m_kart->getIdent().c_str(),
item_to_collect->getType());
m_item_to_collect = item_to_collect;
}
else
@@ -793,18 +796,18 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
.toIrrVector());
#endif
if(m_ai_debug)
Log::debug("SkiddingAI",
"%s adjusts to hit type %d angle %f.",
m_kart->getIdent().c_str(),
item_to_collect->getType(), angle);
Log::debug(getControllerName().c_str(),
"%s adjusts to hit type %d angle %f.",
m_kart->getIdent().c_str(),
item_to_collect->getType(), angle);
}
else
{
if(m_ai_debug)
Log::debug("SkiddingAI",
"%s won't hit '%d', angle %f.",
m_kart->getIdent().c_str(),
item_to_collect->getType(), angle);
Log::debug(getControllerName().c_str(),
"%s won't hit '%d', angle %f.",
m_kart->getIdent().c_str(),
item_to_collect->getType(), angle);
}
} // kart will not hit item
} // does hit hit bad item
@@ -1421,9 +1424,9 @@ void SkiddingAI::handleItems(const float dt)
m_controls->m_fire = m_kart_ahead != NULL;
break;
default:
Log::error("SkiddingAI",
"Invalid or unhandled powerup '%d' in default AI.",
m_kart->getPowerup()->getType());
Log::error(getControllerName().c_str(),
"Invalid or unhandled powerup '%d' in default AI.",
m_kart->getPowerup()->getType());
assert(false);
}
if(m_controls->m_fire) m_time_since_last_shot = 0.0f;
@@ -1717,7 +1720,7 @@ void SkiddingAI::checkCrashes(const Vec3& pos )
slip->isSlipstreamReady() &&
slip->getSlipstreamTarget())
{
//Log::debug("skidding_ai", "%s overtaking %s\n",
//Log::debug(getControllerName().c_str(), "%s overtaking %s",
// m_kart->getIdent().c_str(),
// m_kart->getSlipstreamKart()->getIdent().c_str());
// FIXME: we might define a minimum distance, and if the target kart
@@ -1742,9 +1745,9 @@ void SkiddingAI::checkCrashes(const Vec3& pos )
int current_node = m_track_node;
if(steps<1 || steps>1000)
{
Log::warn("SkiddingAI",
"Incorrect STEPS=%d. kart_length %f velocity %f",
steps, m_kart_length, m_kart->getVelocityLC().getZ());
Log::warn(getControllerName().c_str(),
"Incorrect STEPS=%d. kart_length %f velocity %f",
steps, m_kart_length, m_kart->getVelocityLC().getZ());
steps=1000;
}
for(int i = 1; steps > i; ++i)
@@ -2226,8 +2229,9 @@ bool SkiddingAI::canSkid(float steer_fraction)
if(m_ai_debug)
{
if(fabsf(steer_fraction)>=2.5f)
Log::debug("SkiddingAI", "%s stops skidding (%f).",
m_kart->getIdent().c_str(), steer_fraction);
Log::debug(getControllerName().c_str(),
"%s stops skidding (%f).",
m_kart->getIdent().c_str(), steer_fraction);
}
#endif
// If the current turn is not sharp enough, delay releasing
@@ -2246,8 +2250,9 @@ bool SkiddingAI::canSkid(float steer_fraction)
#ifdef DEBUG
if(m_controls->m_skid && m_ai_debug)
{
Log::debug("SkiddingAI", "%s stops skidding on straight.",
m_kart->getIdent().c_str());
Log::debug(getControllerName().c_str(),
"%s stops skidding on straight.",
m_kart->getIdent().c_str());
}
#endif
return false;
@@ -2282,8 +2287,9 @@ bool SkiddingAI::canSkid(float steer_fraction)
if(m_controls->m_skid && duration < 1.0f)
{
if(m_ai_debug)
Log::debug("SkiddingAI", "'%s' too short, stop skid.",
m_kart->getIdent().c_str());
Log::debug(getControllerName().c_str(),
"'%s' too short, stop skid.",
m_kart->getIdent().c_str());
return false;
}
// Test if the AI is trying to skid against track direction. This
@@ -2297,9 +2303,9 @@ bool SkiddingAI::canSkid(float steer_fraction)
{
#ifdef DEBUG
if(m_controls->m_skid && m_ai_debug)
Log::debug("SkiddingAI",
"%s skidding against track direction.",
m_kart->getIdent().c_str());
Log::debug(getControllerName().c_str(),
"%s skidding against track direction.",
m_kart->getIdent().c_str());
#endif
return false;
}
@@ -2309,8 +2315,9 @@ bool SkiddingAI::canSkid(float steer_fraction)
{
#ifdef DEBUG
if(!m_controls->m_skid && m_ai_debug)
Log::debug("SkiddingAI", "%s start skid, duration %f.",
m_kart->getIdent().c_str(), duration);
Log::debug(getControllerName().c_str(),
"%s start skid, duration %f.",
m_kart->getIdent().c_str(), duration);
#endif
return true;
@@ -2318,8 +2325,9 @@ bool SkiddingAI::canSkid(float steer_fraction)
#ifdef DEBUG
if(m_controls->m_skid && m_ai_debug)
Log::debug("SkiddingAI", "%s has no reasons to skid anymore.",
m_kart->getIdent().c_str());
Log::debug(getControllerName().c_str(),
"%s has no reasons to skid anymore.",
m_kart->getIdent().c_str());
#endif
return false;
} // canSkid
@@ -2364,9 +2372,10 @@ void SkiddingAI::setSteering(float angle, float dt)
: SKID_PROBAB_NO_SKID;
#undef PRINT_SKID_STATS
#ifdef PRINT_SKID_STATS
Log::info("SkiddingAI", "%s distance %f prob %d skidding %s\n",
m_kart->getIdent().c_str(), distance, prob,
sc= ? "no" : sc==KartControl::SC_LEFT ? "left" : "right");
Log::info(getControllerName().c_str(),
"%s distance %f prob %d skidding %s",
m_kart->getIdent().c_str(), distance, prob,
sc= ? "no" : sc==KartControl::SC_LEFT ? "left" : "right");
#endif
}
m_controls->m_skid = m_skid_probability_state == SKID_PROBAB_SKID
@@ -2397,8 +2406,9 @@ void SkiddingAI::setSteering(float angle, float dt)
m_controls->m_skid = KartControl::SC_NONE;
#ifdef DEBUG
if(m_ai_debug)
Log::info("SkiddingAI", "'%s' wrong steering, stop skid.",
m_kart->getIdent().c_str());
Log::info(getControllerName().c_str(),
"'%s' wrong steering, stop skid.",
m_kart->getIdent().c_str());
#endif
}
@@ -2412,8 +2422,9 @@ void SkiddingAI::setSteering(float angle, float dt)
{
#ifdef DEBUG
if(m_ai_debug)
Log::info("SkiddingAI", "%s steering too much (%f).",
m_kart->getIdent().c_str(), steer_fraction);
Log::info(getControllerName().c_str(),
"%s steering too much (%f).",
m_kart->getIdent().c_str(), steer_fraction);
#endif
m_controls->m_skid = KartControl::SC_NONE;
}

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)
{
@@ -41,11 +45,27 @@ SoccerAI::SoccerAI(AbstractKart *kart)
#ifdef AI_DEBUG
video::SColor col_debug(128, 128, 0, 0);
video::SColor col_debug_next(128, 0, 128, 128);
m_debug_sphere = irr_driver->addSphere(1.0f, col_debug);
m_debug_sphere->setVisible(true);
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.
@@ -59,7 +79,14 @@ SoccerAI::~SoccerAI()
{
#ifdef AI_DEBUG
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
//-----------------------------------------------------------------------------
@@ -70,34 +97,27 @@ void SoccerAI::reset()
ArenaAI::reset();
AIBaseController::reset();
m_saving_ball = false;
if (race_manager->getNumPlayers() == 1)
{
// Same handle in SoccerWorld::createKart
if (race_manager->getKartInfo(0).getSoccerTeam() == SOCCER_TEAM_RED)
{
m_cur_team = (m_kart->getWorldKartId() % 2 == 0 ?
SOCCER_TEAM_BLUE : SOCCER_TEAM_RED);
}
else
{
m_cur_team = (m_kart->getWorldKartId() % 2 == 0 ?
SOCCER_TEAM_RED : SOCCER_TEAM_BLUE);
}
}
else
{
m_cur_team = (m_kart->getWorldKartId() % 2 == 0 ?
SOCCER_TEAM_BLUE : SOCCER_TEAM_RED);
}
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);
@@ -146,114 +166,225 @@ void SoccerAI::findClosestKart(bool use_difficulty)
//-----------------------------------------------------------------------------
void SoccerAI::findTarget()
{
// Check whether any defense is needed
if ((m_world->getBallPosition() - NavMesh::get()->getNavPoly(m_world
->getGoalNode(m_cur_team)).getCenter()).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 goal_pos = {0};
Vec3 ball_lc(0, 0, 0);
checkPosition(orig_pos, &ball_pos, &ball_lc);
posData aim_pos = {0};
Vec3 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*/);
// opposite team goal
checkPosition(NavMesh::get()->getNavPoly(m_world
->getGoalNode(m_cur_team == SOCCER_TEAM_BLUE ?
SOCCER_TEAM_RED : SOCCER_TEAM_BLUE)).getCenter(), &goal_pos);
// Too far from the ball,
// use path finding from arena ai to get close
// ie no extra braking is needed
if (aim_pos.distance > 10.0f) return ball_aim_pos;
if (goal_pos.behind)
if (m_overtake_ball)
{
if (goal_pos.angle > 0.3f && ball_pos.distance < 3.0f &&
!ball_pos.behind)
Vec3 overtake_wc;
const bool can_overtake = determineOvertakePosition(ball_lc, ball_pos,
&overtake_wc);
if (!can_overtake)
{
// Only steer with ball if same sides for ball and goal
if (ball_pos.on_side && goal_pos.on_side)
{
ball_lc = ball_lc + Vec3 (1, 0, 1);
return m_kart->getTrans()(ball_lc);
}
else if (!ball_pos.on_side && !goal_pos.on_side)
{
ball_lc = ball_lc - Vec3 (1, 0, 0) + Vec3 (0, 0, 1);
return m_kart->getTrans()(ball_lc);
}
else
m_controls->m_brake = true;
m_overtake_ball = false;
return ball_aim_pos;
}
else
{
// This case is facing straight ahead opposite goal
// (which is straight behind itself), apply more
// offset for skidding, to save the ball from behind
// scored.
// Notice: this assume map maker make soccer field
// with two goals facing each other straight
ball_lc = (goal_pos.on_side ? ball_lc - Vec3 (2, 0, 0) +
Vec3 (0, 0, 2) : ball_lc + Vec3 (2, 0, 2));
if (ball_pos.distance < 3.0f &&
(m_cur_difficulty == RaceManager::DIFFICULTY_HARD ||
m_cur_difficulty == RaceManager::DIFFICULTY_BEST))
m_saving_ball = true;
return m_kart->getTrans()(ball_lc);
}
return overtake_wc;
}
if (ball_pos.distance < 3.0f &&
!ball_pos.behind && !goal_pos.behind)
else
{
if (goal_pos.angle < 0.5f)
return orig_pos;
else
// 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())
{
// Same with above
if (ball_pos.on_side && goal_pos.on_side)
const bool can_overtake = determineOvertakePosition(ball_lc,
ball_pos, NULL);
if (can_overtake)
{
ball_lc = ball_lc + Vec3 (1, 0, 1);
return m_kart->getTrans()(ball_lc);
}
else if (!ball_pos.on_side && !goal_pos.on_side)
{
ball_lc = ball_lc - Vec3 (1, 0, 0) + Vec3 (0, 0, 1);
return m_kart->getTrans()(ball_lc);
m_overtake_ball = true;
}
else
m_controls->m_brake = true;
{
// 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;
}
}
}
return orig_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::determineOvertakePosition(const Vec3& ball_lc,
const posData& ball_pos,
Vec3* overtake_wc)
{
// 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
// No overtake if ball is behind
if (ball_lc.z() < 0.0f) return false;
// Circle equation: (x-a)2 + (y-b)2 = r2
const float r2 = (ball_pos.distance / 2) * (ball_pos.distance / 2);
// No overtake if sqrtf(r2) / 2 < ball radius,
// which will likely push to ball forward
if ((sqrtf(r2) / 2) < (m_world->getBallDiameter() / 2))
{
return false;
}
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;
}
// 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
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);
const float slope_1 = (-(2 * d * e) + sqrtf(discriminant)) /
(2 * ((e * e) - (4 * f)));
const float slope_2 = (-(2 * d * e) - sqrtf(discriminant)) /
(2 * ((e * e) - (4 * f)));
assert(!std::isnan(slope_1));
assert(!std::isnan(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)));
// Use the closest point to aim
float x = std::min(fabsf(x1), fabsf(x2));
float y = 0.0f;
if (-x == x1)
{
// x was negative
x = -x;
y = slope_1 * x;
}
else if (x == x1)
{
y = slope_1 * x;
}
else if (-x == x2)
{
x = -x;
y = slope_2 * x;
}
else
{
y = slope_2 * x;
}
if (overtake_wc)
*overtake_wc = m_kart->getTrans()(Vec3(x, 0, y));
return true;
} // determineOvertakePosition
//-----------------------------------------------------------------------------
int SoccerAI::getCurrentNode() const
{
return m_world->getKartNode(m_kart->getWorldKartId());
} // getCurrentNode
// ------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool SoccerAI::isWaiting() const
{
return m_world->isStartPhase();

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,19 +35,41 @@ 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;
SoccerTeam m_opp_team;
Vec3 correctBallPosition(const Vec3&);
/** 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 determineOvertakePosition(const Vec3& ball_lc,
const posData& ball_pos, Vec3* overtake_wc);
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 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

@@ -58,7 +58,13 @@
#include <cstdio>
#include <iostream>
TestAI::TestAI(AbstractKart *kart)
// This define is only used to make the TestAI as similar to the
// SkiddingAI. This way the two AIs can be compared without too many
// false positives.
#define SkiddingAI TestAI
SkiddingAI::SkiddingAI(AbstractKart *kart)
: AIBaseLapController(kart)
{
reset();
@@ -137,13 +143,12 @@ TestAI::TestAI(AbstractKart *kart)
#endif
#endif
} // TestAI
} // SkiddingAI
//-----------------------------------------------------------------------------
/** The destructor deletes the shared TrackInfo objects if no more TestAI
* instances are around.
/** Destructor, mostly to clean up debug data structures.
*/
TestAI::~TestAI()
SkiddingAI::~SkiddingAI()
{
#ifdef AI_DEBUG
for(unsigned int i=0; i<3; i++)
@@ -155,12 +160,12 @@ TestAI::~TestAI()
}
delete [] m_curve;
#endif
} // ~TestAI
} // ~SkiddingAI
//-----------------------------------------------------------------------------
/** Resets the AI when a race is restarted.
*/
void TestAI::reset()
void SkiddingAI::reset()
{
m_time_since_last_shot = 0.0f;
m_start_kart_crash_direction = 0;
@@ -183,10 +188,10 @@ void TestAI::reset()
QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node);
if(m_track_node==QuadGraph::UNKNOWN_SECTOR)
{
Log::error("TestAI",
"Invalid starting position for '%s' - not on track"
" - can be ignored.",
m_kart->getIdent().c_str());
Log::error(getControllerName().c_str(),
"Invalid starting position for '%s' - not on track"
" - can be ignored.",
m_kart->getIdent().c_str());
m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ());
}
@@ -198,7 +203,7 @@ void TestAI::reset()
* This is used in profile mode when comparing different AI implementations
* to be able to distinguish them from each other.
*/
const irr::core::stringw& TestAI::getNamePostfix() const
const irr::core::stringw& SkiddingAI::getNamePostfix() const
{
// Static to avoid returning the address of a temporary string.
static irr::core::stringw name="(default)";
@@ -210,7 +215,7 @@ const irr::core::stringw& TestAI::getNamePostfix() const
* \param index The index of the graph node for which the successor
* is searched.
*/
unsigned int TestAI::getNextSector(unsigned int index)
unsigned int SkiddingAI::getNextSector(unsigned int index)
{
return m_successor_index[index];
} // getNextSector
@@ -220,7 +225,7 @@ unsigned int TestAI::getNextSector(unsigned int index)
* It is called once per frame for each AI and determines the behaviour of
* the AI, e.g. steering, accelerating/braking, firing.
*/
void TestAI::update(float dt)
void SkiddingAI::update(float dt)
{
// This is used to enable firing an item backwards.
m_controls->m_look_back = false;
@@ -389,7 +394,7 @@ void TestAI::update(float dt)
* it will brake in order to make it easier to re-align itself), and
* estimated curve radius (brake to avoid being pushed out of a curve).
*/
void TestAI::handleBraking()
void SkiddingAI::handleBraking()
{
m_controls->m_brake = false;
// In follow the leader mode, the kart should brake if they are ahead of
@@ -400,8 +405,8 @@ void TestAI::handleBraking()
{
#ifdef DEBUG
if(m_ai_debug)
Log::debug("TestAI", "braking: %s ahead of leader.",
m_kart->getIdent().c_str());
Log::debug(getControllerName().c_str(), "braking: %s ahead of leader.",
m_kart->getIdent().c_str());
#endif
m_controls->m_brake = true;
@@ -420,8 +425,9 @@ void TestAI::handleBraking()
{
#ifdef DEBUG
if(m_ai_debug)
Log::debug("TestAI", "%s not aligned with track.",
m_kart->getIdent().c_str());
Log::debug(getControllerName().c_str(),
"%s not aligned with track.",
m_kart->getIdent().c_str());
#endif
m_controls->m_brake = true;
return;
@@ -439,11 +445,11 @@ void TestAI::handleBraking()
m_controls->m_brake = true;
#ifdef DEBUG
if(m_ai_debug)
Log::debug("TestAI",
"speed %f too tight curve: radius %f ",
m_kart->getSpeed(),
m_kart->getIdent().c_str(),
m_current_curve_radius);
Log::debug(getControllerName().c_str(),
"speed %f too tight curve: radius %f ",
m_kart->getSpeed(),
m_kart->getIdent().c_str(),
m_current_curve_radius);
#endif
}
return;
@@ -462,7 +468,7 @@ void TestAI::handleBraking()
* steer direction to arrive at the currently aim-at point.
* \param dt Time step size.
*/
void TestAI::handleSteering(float dt)
void SkiddingAI::handleSteering(float dt)
{
const int next = m_next_node_index[m_track_node];
@@ -484,7 +490,8 @@ void TestAI::handleSteering(float dt)
#ifdef AI_DEBUG
m_debug_sphere[0]->setPosition(QuadGraph::get()->getQuadOfNode(next)
.getCenter().toIrrVector());
Log::debug("skidding_ai","-Outside of road: steer to center point.");
Log::debug(getControllerName().c_str(),
"Outside of road: steer to center point.");
#endif
}
//If we are going to crash against a kart, avoid it if it doesn't
@@ -494,12 +501,12 @@ void TestAI::handleSteering(float dt)
//-1 = left, 1 = right, 0 = no crash.
if( m_start_kart_crash_direction == 1 )
{
steer_angle = steerToAngle(next, -M_PI*0.5f );
steer_angle = steerToAngle(next, M_PI*0.5f );
m_start_kart_crash_direction = 0;
}
else if(m_start_kart_crash_direction == -1)
{
steer_angle = steerToAngle(next, M_PI*0.5f);
steer_angle = steerToAngle(next, -M_PI*0.5f);
m_start_kart_crash_direction = 0;
}
else
@@ -507,18 +514,19 @@ void TestAI::handleSteering(float dt)
if(m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() ) >
m_world->getDistanceToCenterForKart( m_crashes.m_kart ))
{
steer_angle = steerToAngle(next, -M_PI*0.5f );
steer_angle = steerToAngle(next, M_PI*0.5f );
m_start_kart_crash_direction = 1;
}
else
{
steer_angle = steerToAngle(next, M_PI*0.5f );
steer_angle = steerToAngle(next, -M_PI*0.5f );
m_start_kart_crash_direction = -1;
}
}
#ifdef AI_DEBUG
Log::debug("skidding_ai", "- Velocity vector crashes with kart "
Log::debug(getControllerName().c_str(),
"Velocity vector crashes with kart "
"and doesn't crashes with road : steer 90 "
"degrees away from kart.");
#endif
@@ -611,7 +619,7 @@ void TestAI::handleSteering(float dt)
* try to collect an item, this value will be changed.
* \param last_node Index of the graph node on which the aim_point is.
*/
void TestAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
int last_node)
{
#ifdef AI_DEBUG
@@ -700,8 +708,8 @@ void TestAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
}
if(m_ai_debug)
Log::debug("TestAI", "%s unselects item.",
m_kart->getIdent().c_str());
Log::debug(getControllerName().c_str(), "%s unselects item.",
m_kart->getIdent().c_str());
// Otherwise remove the pre-selected item (and start
// looking for a new item).
m_item_to_collect = NULL;
@@ -770,9 +778,10 @@ void TestAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
.toIrrVector());
#endif
if(m_ai_debug)
Log::debug("TestAI", "%s selects item type '%d'.",
m_kart->getIdent().c_str(),
item_to_collect->getType());
Log::debug(getControllerName().c_str(),
"%s selects item type '%d'.",
m_kart->getIdent().c_str(),
item_to_collect->getType());
m_item_to_collect = item_to_collect;
}
else
@@ -793,18 +802,18 @@ void TestAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
.toIrrVector());
#endif
if(m_ai_debug)
Log::debug("TestAI",
"%s adjusts to hit type %d angle %f.",
m_kart->getIdent().c_str(),
item_to_collect->getType(), angle);
Log::debug(getControllerName().c_str(),
"%s adjusts to hit type %d angle %f.",
m_kart->getIdent().c_str(),
item_to_collect->getType(), angle);
}
else
{
if(m_ai_debug)
Log::debug("TestAI",
"%s won't hit '%d', angle %f.",
m_kart->getIdent().c_str(),
item_to_collect->getType(), angle);
Log::debug(getControllerName().c_str(),
"%s won't hit '%d', angle %f.",
m_kart->getIdent().c_str(),
item_to_collect->getType(), angle);
}
} // kart will not hit item
} // does hit hit bad item
@@ -820,7 +829,7 @@ void TestAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
* to be avoided.
* \return True if it would hit any of the bad items.
*/
bool TestAI::hitBadItemWhenAimAt(const Item *item,
bool SkiddingAI::hitBadItemWhenAimAt(const Item *item,
const std::vector<const Item *> &items_to_avoid)
{
core::line2df to_item(m_kart->getXYZ().getX(), m_kart->getXYZ().getZ(),
@@ -845,7 +854,7 @@ bool TestAI::hitBadItemWhenAimAt(const Item *item,
* \param last_node
* \return True if th AI should still aim for the pre-selected item.
*/
bool TestAI::handleSelectedItem(float kart_aim_angle, Vec3 *aim_point)
bool SkiddingAI::handleSelectedItem(float kart_aim_angle, Vec3 *aim_point)
{
// If the item is unavailable keep on testing. It is not necessary
// to test if an item has turned bad, this was tested before this
@@ -881,7 +890,7 @@ bool TestAI::handleSelectedItem(float kart_aim_angle, Vec3 *aim_point)
* into account).
* \return True if steering is necessary to avoid an item.
*/
bool TestAI::steerToAvoid(const std::vector<const Item *> &items_to_avoid,
bool SkiddingAI::steerToAvoid(const std::vector<const Item *> &items_to_avoid,
const core::line2df &line_to_target,
Vec3 *aim_point)
{
@@ -1024,7 +1033,7 @@ bool TestAI::steerToAvoid(const std::vector<const Item *> &items_to_avoid,
* (NULL if no item was avoided so far).
* \param item_to_collect A pointer to a previously selected item to collect.
*/
void TestAI::evaluateItems(const Item *item, float kart_aim_angle,
void SkiddingAI::evaluateItems(const Item *item, float kart_aim_angle,
std::vector<const Item *> *items_to_avoid,
std::vector<const Item *> *items_to_collect)
{
@@ -1136,7 +1145,7 @@ void TestAI::evaluateItems(const Item *item, float kart_aim_angle,
* STATE: shield on -> avoid usage of offensive items (with certain tolerance)
* STATE: swatter on -> avoid usage of shield
*/
void TestAI::handleItems(const float dt)
void SkiddingAI::handleItems(const float dt)
{
m_controls->m_fire = false;
if(m_kart->getKartAnimation() ||
@@ -1421,9 +1430,9 @@ void TestAI::handleItems(const float dt)
m_controls->m_fire = m_kart_ahead != NULL;
break;
default:
Log::error("TestAI",
"Invalid or unhandled powerup '%d' in default AI.",
m_kart->getPowerup()->getType());
Log::error(getControllerName().c_str(),
"Invalid or unhandled powerup '%d' in default AI.",
m_kart->getPowerup()->getType());
assert(false);
}
if(m_controls->m_fire) m_time_since_last_shot = 0.0f;
@@ -1434,7 +1443,7 @@ void TestAI::handleItems(const float dt)
* 'closeness' is for now simply based on the position, i.e. if a kart is
* more than one lap behind or ahead, it is not considered to be closest.
*/
void TestAI::computeNearestKarts()
void SkiddingAI::computeNearestKarts()
{
int my_position = m_kart->getPosition();
@@ -1496,7 +1505,7 @@ void TestAI::computeNearestKarts()
/** Determines if the AI should accelerate or not.
* \param dt Time step size.
*/
void TestAI::handleAcceleration( const float dt)
void SkiddingAI::handleAcceleration( const float dt)
{
//Do not accelerate until we have delayed the start enough
if( m_start_delay > 0.0f )
@@ -1506,13 +1515,15 @@ void TestAI::handleAcceleration( const float dt)
return;
}
// This test appears to be incorrect, since at this stage
// m_brake has not been reset from the previous frame
//if( m_controls->m_brake )
//{
// m_controls->m_accel = 0.0f;
// return;
//}
// FIXME: This test appears to be incorrect, since at this stage
// m_brake has not been reset from the previous frame, which can
// cause too long slow downs. On the other hand removing it appears
// to decrease performance in some narrower tracks
if( m_controls->m_brake )
{
m_controls->m_accel = 0.0f;
return;
}
if(m_kart->getBlockedByPlungerTime()>0)
{
@@ -1528,7 +1539,7 @@ void TestAI::handleAcceleration( const float dt)
} // handleAcceleration
//-----------------------------------------------------------------------------
void TestAI::handleRaceStart()
void SkiddingAI::handleRaceStart()
{
if( m_start_delay < 0.0f )
{
@@ -1556,7 +1567,7 @@ void TestAI::handleRaceStart()
//TODO: if the AI is crashing constantly, make it move backwards in a straight
//line, then move forward while turning.
void TestAI::handleRescue(const float dt)
void SkiddingAI::handleRescue(const float dt)
{
// check if kart is stuck
if(m_kart->getSpeed()<2.0f && !m_kart->getKartAnimation() &&
@@ -1578,7 +1589,7 @@ void TestAI::handleRescue(const float dt)
//-----------------------------------------------------------------------------
/** Decides wether to use nitro or not.
*/
void TestAI::handleNitroAndZipper()
void SkiddingAI::handleNitroAndZipper()
{
m_controls->m_nitro = false;
@@ -1586,9 +1597,10 @@ void TestAI::handleNitroAndZipper()
// is already fast.
// If we are already very fast, save nitro.
//if(m_kart->getSpeed() > 0.95f*m_kart->getCurrentMaxSpeed())
// return;
if(m_kart->getSpeed() > 0.95f*m_kart->getCurrentMaxSpeed())
return;
// About the above: removing this line might enable the AI to better use
// nitro and zippers .
// This works well for some tracks (math, lighthouse
// sandtrack), but worse in others (zen, xr591). The good result
// is caused by long straights in which the AI will now use zippers,
@@ -1734,7 +1746,7 @@ void TestAI::handleNitroAndZipper()
} // handleNitroAndZipper
//-----------------------------------------------------------------------------
void TestAI::checkCrashes(const Vec3& pos )
void SkiddingAI::checkCrashes(const Vec3& pos )
{
int steps = int( m_kart->getVelocityLC().getZ() / m_kart_length );
if( steps < 2 ) steps = 2;
@@ -1757,7 +1769,7 @@ void TestAI::checkCrashes(const Vec3& pos )
slip->isSlipstreamReady() &&
slip->getSlipstreamTarget())
{
//Log::debug("skidding_ai", "%s overtaking %s\n",
//Log::debug(getControllerName().c_str(), "%s overtaking %s",
// m_kart->getIdent().c_str(),
// m_kart->getSlipstreamKart()->getIdent().c_str());
// FIXME: we might define a minimum distance, and if the target kart
@@ -1782,9 +1794,9 @@ void TestAI::checkCrashes(const Vec3& pos )
int current_node = m_track_node;
if(steps<1 || steps>1000)
{
Log::warn("TestAI",
"Incorrect STEPS=%d. kart_length %f velocity %f",
steps, m_kart_length, m_kart->getVelocityLC().getZ());
Log::warn(getControllerName().c_str(),
"Incorrect STEPS=%d. kart_length %f velocity %f",
steps, m_kart_length, m_kart->getVelocityLC().getZ());
steps=1000;
}
for(int i = 1; steps > i; ++i)
@@ -1863,7 +1875,7 @@ void TestAI::checkCrashes(const Vec3& pos )
* driven to in a straight line.
* \param last_node The graph node index in which the aim_position is.
*/
void TestAI::findNonCrashingPointNew(Vec3 *result, int *last_node)
void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node)
{
*last_node = m_next_node_index[m_track_node];
const core::vector2df xz = m_kart->getXYZ().toIrrVector2d();
@@ -1956,7 +1968,7 @@ void TestAI::findNonCrashingPointNew(Vec3 *result, int *last_node)
* driven to in a straight line.
* \param last_node The graph node index in which the aim_position is.
*/
void TestAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
{
#ifdef AI_DEBUG_KART_HEADING
const Vec3 eps(0,0.5f,0);
@@ -2050,7 +2062,7 @@ void TestAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
* \param aim_position On exit contains the point the AI should aim at.
* \param last_node On exit contais the graph node the AI is aiming at.
*/
void TestAI::findNonCrashingPoint(Vec3 *aim_position, int *last_node)
void SkiddingAI::findNonCrashingPoint(Vec3 *aim_position, int *last_node)
{
#ifdef AI_DEBUG_KART_HEADING
const Vec3 eps(0,0.5f,0);
@@ -2137,7 +2149,7 @@ void TestAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
/** Determines the direction of the track ahead of the kart: 0 indicates
* straight, +1 right turn, -1 left turn.
*/
void TestAI::determineTrackDirection()
void SkiddingAI::determineTrackDirection()
{
const QuadGraph *qg = QuadGraph::get();
unsigned int succ = m_successor_index[m_track_node];
@@ -2189,7 +2201,7 @@ void TestAI::determineTrackDirection()
// ----------------------------------------------------------------------------
/** If the kart is at/in a curve, determine the turn radius.
*/
void TestAI::handleCurve()
void SkiddingAI::handleCurve()
{
// Ideally we would like to have a circle that:
// 1) goes through the kart position
@@ -2253,7 +2265,7 @@ void TestAI::handleCurve()
* AIBaseLapController.
* \return True if the kart should skid.
*/
bool TestAI::canSkid(float steer_fraction)
bool SkiddingAI::canSkid(float steer_fraction)
{
if(fabsf(steer_fraction)>1.5f)
{
@@ -2266,8 +2278,9 @@ bool TestAI::canSkid(float steer_fraction)
if(m_ai_debug)
{
if(fabsf(steer_fraction)>=2.5f)
Log::debug("TestAI", "%s stops skidding (%f).",
m_kart->getIdent().c_str(), steer_fraction);
Log::debug(getControllerName().c_str(),
"%s stops skidding (%f).",
m_kart->getIdent().c_str(), steer_fraction);
}
#endif
// If the current turn is not sharp enough, delay releasing
@@ -2286,8 +2299,9 @@ bool TestAI::canSkid(float steer_fraction)
#ifdef DEBUG
if(m_controls->m_skid && m_ai_debug)
{
Log::debug("TestAI", "%s stops skidding on straight.",
m_kart->getIdent().c_str());
Log::debug(getControllerName().c_str(),
"%s stops skidding on straight.",
m_kart->getIdent().c_str());
}
#endif
return false;
@@ -2322,8 +2336,9 @@ bool TestAI::canSkid(float steer_fraction)
if(m_controls->m_skid && duration < 1.0f)
{
if(m_ai_debug)
Log::debug("TestAI", "'%s' too short, stop skid.",
m_kart->getIdent().c_str());
Log::debug(getControllerName().c_str(),
"'%s' too short, stop skid.",
m_kart->getIdent().c_str());
return false;
}
// Test if the AI is trying to skid against track direction. This
@@ -2337,9 +2352,9 @@ bool TestAI::canSkid(float steer_fraction)
{
#ifdef DEBUG
if(m_controls->m_skid && m_ai_debug)
Log::debug("TestAI",
"%s skidding against track direction.",
m_kart->getIdent().c_str());
Log::debug(getControllerName().c_str(),
"%s skidding against track direction.",
m_kart->getIdent().c_str());
#endif
return false;
}
@@ -2349,8 +2364,9 @@ bool TestAI::canSkid(float steer_fraction)
{
#ifdef DEBUG
if(!m_controls->m_skid && m_ai_debug)
Log::debug("TestAI", "%s start skid, duration %f.",
m_kart->getIdent().c_str(), duration);
Log::debug(getControllerName().c_str(),
"%s start skid, duration %f.",
m_kart->getIdent().c_str(), duration);
#endif
return true;
@@ -2358,8 +2374,9 @@ bool TestAI::canSkid(float steer_fraction)
#ifdef DEBUG
if(m_controls->m_skid && m_ai_debug)
Log::debug("TestAI", "%s has no reasons to skid anymore.",
m_kart->getIdent().c_str());
Log::debug(getControllerName().c_str(),
"%s has no reasons to skid anymore.",
m_kart->getIdent().c_str());
#endif
return false;
} // canSkid
@@ -2377,7 +2394,7 @@ bool TestAI::canSkid(float steer_fraction)
* \param angle Steering angle.
* \param dt Time step.
*/
void TestAI::setSteering(float angle, float dt)
void SkiddingAI::setSteering(float angle, float dt)
{
float steer_fraction = angle / m_kart->getMaxSteerAngle();
@@ -2404,9 +2421,10 @@ void TestAI::setSteering(float angle, float dt)
: SKID_PROBAB_NO_SKID;
#undef PRINT_SKID_STATS
#ifdef PRINT_SKID_STATS
Log::info("TestAI", "%s distance %f prob %d skidding %s\n",
m_kart->getIdent().c_str(), distance, prob,
sc= ? "no" : sc==KartControl::SC_LEFT ? "left" : "right");
Log::info(getControllerName().c_str(),
"%s distance %f prob %d skidding %s",
m_kart->getIdent().c_str(), distance, prob,
sc= ? "no" : sc==KartControl::SC_LEFT ? "left" : "right");
#endif
}
m_controls->m_skid = m_skid_probability_state == SKID_PROBAB_SKID
@@ -2437,8 +2455,9 @@ void TestAI::setSteering(float angle, float dt)
m_controls->m_skid = KartControl::SC_NONE;
#ifdef DEBUG
if(m_ai_debug)
Log::info("TestAI", "'%s' wrong steering, stop skid.",
m_kart->getIdent().c_str());
Log::info(getControllerName().c_str(),
"'%s' wrong steering, stop skid.",
m_kart->getIdent().c_str());
#endif
}
@@ -2452,8 +2471,9 @@ void TestAI::setSteering(float angle, float dt)
{
#ifdef DEBUG
if(m_ai_debug)
Log::info("TestAI", "%s steering too much (%f).",
m_kart->getIdent().c_str(), steer_fraction);
Log::info(getControllerName().c_str(),
"%s steering too much (%f).",
m_kart->getIdent().c_str(), steer_fraction);
#endif
m_controls->m_skid = KartControl::SC_NONE;
}
@@ -2496,7 +2516,7 @@ void TestAI::setSteering(float angle, float dt)
* \param[out] center Center point of the circle.
* \param[out] radius Radius of the circle.
*/
void TestAI::determineTurnRadius(const Vec3 &start,
void SkiddingAI::determineTurnRadius(const Vec3 &start,
const Vec3 &tangent,
const Vec3 &end,
Vec3 *center,

View File

@@ -552,6 +552,8 @@ void cmdLineHelp()
" --root=DIR Path to add to the list of STK root directories.\n"
" You can specify more than one by separating them\n"
" with colons (:).\n"
" --cutscene=NAME Launch the specified track as a cutscene.\n"
" This is for internal debugging use only.\n"
"\n"
"You can visit SuperTuxKart's homepage at "
"http://supertuxkart.sourceforge.net\n\n",

View File

@@ -301,6 +301,11 @@ void CutsceneWorld::update(float dt)
rot2.setPitch(rot2.getPitch() + 90.0f);
m_camera->setRotation(rot2.toIrrVector());
irr::core::vector3df up(0.0f, 0.0f, 1.0f);
irr::core::matrix4 matrix = anchorNode->getAbsoluteTransformation();
matrix.rotateVect(up);
m_camera->setUpVector(up);
SFXManager::get()->positionListener(m_camera->getAbsolutePosition(),
m_camera->getTarget() -
m_camera->getAbsolutePosition(),

View File

@@ -32,8 +32,6 @@
#include "karts/controller/soccer_ai.hpp"
#include "physics/physics.hpp"
#include "states_screens/race_gui_base.hpp"
#include "tracks/check_goal.hpp"
#include "tracks/check_manager.hpp"
#include "tracks/track.hpp"
#include "tracks/track_object_manager.hpp"
#include "utils/constants.hpp"
@@ -45,10 +43,10 @@
*/
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
{
@@ -58,10 +56,12 @@ SoccerWorld::SoccerWorld() : WorldWithRank()
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()
{
@@ -80,37 +80,11 @@ void SoccerWorld::init()
m_display_rank = false;
m_goal_timer = 0.0f;
m_ball_hitter = -1;
m_ball = NULL;
m_ball_body = NULL;
m_goal_target = race_manager->getMaxGoal();
m_goal_sound = SFXManager::get()->createSoundSource("goal_scored");
} // 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);
PtrVector<TrackObject>& objects = tom->getObjects();
@@ -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)
{
@@ -133,9 +139,9 @@ void SoccerWorld::reset()
}
initKartList();
resetAllNodes();
initGoalNodes();
resetBall();
resetAllPosition();
m_ball->reset();
m_bgd.reset();
} // reset
@@ -153,19 +159,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)
{
@@ -177,7 +181,7 @@ 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())
{
@@ -198,62 +202,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
@@ -285,17 +279,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.
*/
@@ -361,22 +344,10 @@ AbstractKart *SoccerWorld::createKart(const std::string &kart_ident, int index,
if (kart_type == RaceManager::KT_AI)
{
if (race_manager->getNumPlayers() == 1)
{
// Make AI even when single player choose a different team
if (race_manager->getKartInfo(0).getSoccerTeam() == SOCCER_TEAM_RED)
{
team = (index % 2 == 0 ? SOCCER_TEAM_BLUE : SOCCER_TEAM_RED);
}
else
{
team = (index % 2 == 0 ? SOCCER_TEAM_RED : SOCCER_TEAM_BLUE);
}
}
if (index < m_red_ai)
team = SOCCER_TEAM_RED;
else
{
team = (index % 2 == 0 ? SOCCER_TEAM_BLUE : SOCCER_TEAM_RED);
}
team = SOCCER_TEAM_BLUE;
m_kart_team_map[index] = team;
}
else
@@ -462,23 +433,27 @@ 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]);
}
@@ -490,59 +465,20 @@ void SoccerWorld::updateBallPosition(float dt)
} // updateBallPosition
//-----------------------------------------------------------------------------
/** Localize two goals on the navigation mesh.
*/
void SoccerWorld::initGoalNodes()
{
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_goal_node = BattleGraph::get()->pointToNode(m_blue_goal_node,
goal->convertTo3DCenter(), true/*ignore_vertical*/);
}
else
{
m_red_goal_node = BattleGraph::get()->pointToNode(m_red_goal_node,
goal->convertTo3DCenter(), true/*ignore_vertical*/);
}
}
}
} // initGoalNodes
//-----------------------------------------------------------------------------
void SoccerWorld::resetAllNodes()
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);
m_red_goal_node = BattleGraph::UNKNOWN_POLY;
m_blue_goal_node = BattleGraph::UNKNOWN_POLY;
} // resetAllNodes
} // 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
//-----------------------------------------------------------------------------
@@ -563,55 +499,64 @@ 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)
continue;
Vec3 d = NavMesh::get()->getNavPoly(this
->getGoalNode(SOCCER_TEAM_RED)).getCenter()
- 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 = NavMesh::get()->getNavPoly(this
->getGoalNode(SOCCER_TEAM_BLUE)).getCenter()
- 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
@@ -632,23 +577,11 @@ 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()
{
m_ball->reset();
m_ball->getPhysicalObject()->reset();
} // resetBall
//-----------------------------------------------------------------------------
void SoccerWorld::enterRaceOverState()
{
@@ -683,3 +616,51 @@ void SoccerWorld::enterRaceOverState()
else
WorldStatus::enterRaceOverState();
} // enterRaceOverState
//-----------------------------------------------------------------------------
void SoccerWorld::setAITeam()
{
const int total_player = race_manager->getNumPlayers();
const int total_karts = race_manager->getNumberOfKarts();
// No AI
if ((total_karts - total_player) == 0) return;
int red_player = 0;
int blue_player = 0;
for (int i = 0; i < total_player; i++)
{
SoccerTeam team = race_manager->getKartInfo(i).getSoccerTeam();
// Happen in profiling mode
if (team == SOCCER_TEAM_NONE)
{
race_manager->setKartSoccerTeam(i, SOCCER_TEAM_BLUE);
team = SOCCER_TEAM_BLUE;
}
team == SOCCER_TEAM_BLUE ? blue_player++ : red_player++;
}
int available_ai = total_karts - red_player - blue_player;
while (available_ai > 0)
{
if ((m_red_ai + red_player) > (m_blue_ai + blue_player))
{
m_blue_ai++;
available_ai--;
}
else if ((m_blue_ai + blue_player) > (m_red_ai + red_player))
{
m_red_ai++;
available_ai--;
}
else if ((m_blue_ai + blue_player) == (m_red_ai + red_player))
{
blue_player > red_player ? m_red_ai++ : m_blue_ai++;
available_ai--;
}
}
Log::debug("SoccerWorld","blue AI: %d red AI: %d", m_blue_ai, m_red_ai);
} // setAITeam

View File

@@ -22,6 +22,8 @@
#include "modes/world_with_rank.hpp"
#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>
@@ -53,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*/
@@ -71,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;
@@ -84,27 +281,22 @@ private:
/** Data generated from navmesh */
std::vector<int> m_kart_on_node;
int m_ball_on_node;
Vec3 m_ball_position;
int m_red_goal_node;
int m_blue_goal_node;
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 init the locations of two goals on the polygon map */
void initGoalNodes();
/** 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 resetAllNodes();
/** 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();
void resetAllPosition();
/** 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;
@@ -121,7 +313,6 @@ public:
// clock events
virtual bool isRaceOver() OVERRIDE;
virtual void terminateRace() OVERRIDE;
virtual void countdownReachedZero() OVERRIDE;
// overriding World methods
@@ -131,7 +322,7 @@ 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; }
@@ -151,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); }
@@ -163,26 +357,48 @@ 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(); }
// ------------------------------------------------------------------------
const int getGoalNode(SoccerTeam team) const
bool ballNotMoving() const
{
return (team == SOCCER_TEAM_BLUE ? m_blue_goal_node : m_red_goal_node);
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);
}
// ------------------------------------------------------------------------
/** Get the AI who will attack the other team ball chaser. */
int getAttacker(SoccerTeam team) const;
// ------------------------------------------------------------------------
void setAITeam();
// ------------------------------------------------------------------------
}; // SoccerWorld

View File

@@ -42,6 +42,7 @@
#include "karts/kart_properties_manager.hpp"
#include "modes/overworld.hpp"
#include "modes/profile_world.hpp"
#include "modes/soccer_world.hpp"
#include "network/network_config.hpp"
#include "physics/btKart.hpp"
#include "physics/physics.hpp"
@@ -191,6 +192,11 @@ void World::init()
m_karts.push_back(ReplayPlay::get()->getGhostKart(k));
}
// Assign team of AIs for soccer mode before createKart
SoccerWorld* sw = dynamic_cast<SoccerWorld*>(this);
if (sw)
sw->setAITeam();
for(unsigned int i=0; i<num_karts; i++)
{
if (race_manager->getKartType(i) == RaceManager::KT_GHOST) continue;

View File

@@ -211,6 +211,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

@@ -329,7 +329,7 @@ void GrandPrixData::reload()
throw std::runtime_error("Missing track id");
}
if (number_of_laps < 1)
if (number_of_laps < 1 && !UserConfigParams::m_artist_debug_mode)
{
Log::error("GrandPrixData",
"Track '%s' in the Grand Prix file '%s' should be raced "

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

@@ -148,8 +148,6 @@ EventPropagation FocusDispatcher::focused(const int player_id)
return GUIEngine::EVENT_LET;
} // focused
static FocusDispatcher *g_dispatcher = NULL;
#if 0
#pragma mark -
#pragma mark KartHoverListener
@@ -229,6 +227,7 @@ bool sameKart(const PlayerKartWidget& player1, const PlayerKartWidget& player2)
KartSelectionScreen::KartSelectionScreen(const char* filename) : Screen(filename)
{
m_dispatcher = NULL;
m_removed_widget = NULL;
m_multiplayer_message = NULL;
m_from_overworld = false;
@@ -246,8 +245,8 @@ KartSelectionScreen* KartSelectionScreen::getRunningInstance()
void KartSelectionScreen::loadedFromFile()
{
g_dispatcher = new FocusDispatcher(this);
m_first_widget = g_dispatcher;
m_dispatcher = new FocusDispatcher(this);
m_first_widget = m_dispatcher;
m_game_master_confirmed = false;
m_multiplayer_message = NULL;
// Dynamically add tabs
@@ -305,6 +304,7 @@ void KartSelectionScreen::beforeAddingWidget()
void KartSelectionScreen::init()
{
m_instance_ptr = this;
Screen::init();
m_must_delete_on_back = false;
@@ -318,17 +318,17 @@ void KartSelectionScreen::init()
// FIXME : The reserved id value is -1 when we switch from KSS to NKSS and vice-versa
g_dispatcher->setRootID(placeholder->m_reserved_id);
m_dispatcher->setRootID(placeholder->m_reserved_id);
g_root_id = placeholder->m_reserved_id;
if (!m_widgets.contains(g_dispatcher))
if (!m_widgets.contains(m_dispatcher))
{
m_widgets.push_back(g_dispatcher);
m_widgets.push_back(m_dispatcher);
// this is only needed if the dispatcher wasn't already in
// the list of widgets. If it already was, it was added along
// other widgets.
g_dispatcher->add();
m_dispatcher->add();
}
m_game_master_confirmed = false;
@@ -426,7 +426,7 @@ void KartSelectionScreen::tearDown()
void KartSelectionScreen::unloaded()
{
// these pointer is no more valid (have been deleted along other widgets)
g_dispatcher = NULL;
m_dispatcher = NULL;
}
// ----------------------------------------------------------------------------
@@ -439,7 +439,7 @@ bool KartSelectionScreen::joinPlayer(InputDevice* device)
Log::info("KartSelectionScreen", "joinPlayer() invoked");
if (!m_multiplayer && !first_player) return false;
assert (g_dispatcher != NULL);
assert (m_dispatcher != NULL);
DynamicRibbonWidget* w = getWidget<DynamicRibbonWidget>("karts");
if (w == NULL)

View File

@@ -36,6 +36,7 @@ namespace Online
class OnlineProfile;
}
class FocusDispatcher;
class InputDevice;
class KartHoverListener;
@@ -79,6 +80,8 @@ protected:
/** Message shown in multiplayer mode */
GUIEngine::BubbleWidget* m_multiplayer_message;
FocusDispatcher *m_dispatcher;
KartSelectionScreen(const char* filename);
/** Called when all players selected their kart */

View File

@@ -37,6 +37,7 @@
#include "modes/cutscene_world.hpp"
#include "modes/overworld.hpp"
#include "modes/demo_world.hpp"
#include "network/network_config.hpp"
#include "online/request_manager.hpp"
#include "states_screens/addons_screen.hpp"
#include "states_screens/credits.hpp"
@@ -385,7 +386,8 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name,
#endif
if (selection == "new")
{
KartSelectionScreen* s = OfflineKartSelectionScreen::getInstance(); //FIXME : that was for tests
NetworkConfig::get()->unsetNetworking();
KartSelectionScreen* s = OfflineKartSelectionScreen::getInstance();
s->setMultiplayer(false);
s->setFromOverworld(false);
s->push();
@@ -393,6 +395,7 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name,
else if (selection == "multiplayer")
{
KartSelectionScreen* s = OfflineKartSelectionScreen::getInstance();
NetworkConfig::get()->unsetNetworking();
s->setMultiplayer(true);
s->setFromOverworld(false);
s->push();

View File

@@ -44,7 +44,6 @@ DEFINE_SCREEN_SINGLETON( NetworkKartSelectionScreen );
NetworkKartSelectionScreen::NetworkKartSelectionScreen()
: KartSelectionScreen("karts_online.stkgui")
{
KartSelectionScreen::m_instance_ptr = this;
} // NetworkKartSelectionScreen
// ----------------------------------------------------------------------------

View File

@@ -21,7 +21,6 @@ DEFINE_SCREEN_SINGLETON( OfflineKartSelectionScreen );
OfflineKartSelectionScreen::OfflineKartSelectionScreen() : KartSelectionScreen("karts.stkgui")
{
KartSelectionScreen::m_instance_ptr = this;
}
OfflineKartSelectionScreen::~OfflineKartSelectionScreen()

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

@@ -35,6 +35,8 @@
#include "io/file_manager.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/controller/controller.hpp"
#include "karts/controller/end_controller.hpp"
#include "karts/controller/local_player_controller.hpp"
#include "karts/kart_properties.hpp"
#include "karts/kart_properties_manager.hpp"
#include "modes/cutscene_world.hpp"
@@ -384,10 +386,6 @@ void RaceResultGUI::eventCallback(GUIEngine::Widget* widget,
{
race_manager->exitRace();
race_manager->setAIKartOverride("");
// FIXME: why is this call necessary here? tearDown should be
// automatically called when the screen is left. Note that the
// NetworkKartSelectionScreen::getInstance()->tearDown(); caused #1347
KartSelectionScreen::getRunningInstance()->tearDown();
Screen* newStack[] = { MainMenuScreen::getInstance(),
RaceSetupScreen::getInstance(),
NULL };
@@ -401,11 +399,6 @@ void RaceResultGUI::eventCallback(GUIEngine::Widget* widget,
{
race_manager->exitRace();
race_manager->setAIKartOverride("");
// FIXME: why is this call necessary here? tearDown should be
// automatically called when the screen is left. Note that the
// NetworkKartSelectionScreen::getInstance()->tearDown(); caused #1347
//if (KartSelectionScreen::getRunningInstance() != NULL)
// KartSelectionScreen::getRunningInstance()->tearDown();
StateManager::get()->resetAndGoToScreen(MainMenuScreen::getInstance());
if (race_manager->raceWasStartedFromOverworld())
@@ -492,15 +485,7 @@ void RaceResultGUI::backToLobby()
// Save a pointer to the current row_info entry
RowInfo *ri = &(m_all_row_infos[position - first_position]);
ri->m_is_player_kart = kart->getController()->isLocalPlayerController();
// Identify Human player, if so display real name other than kart name
const int rm_id = kart->getWorldKartId() -
(race_manager->getNumberOfKarts() - race_manager->getNumPlayers());
if (rm_id >= 0 && !race_manager->isWatchingReplay())
ri->m_kart_name = race_manager->getKartInfo(rm_id).getPlayerName();
else
ri->m_kart_name = translations->fribidize(kart->getName());
ri->m_kart_name = getKartDisplayName(kart);
video::ITexture *icon =
kart->getKartProperties()->getIconMaterial()->getTexture();
@@ -865,16 +850,8 @@ void RaceResultGUI::backToLobby()
RowInfo *ri = &(m_all_row_infos[rank]);
ri->m_kart_icon =
kart->getKartProperties()->getIconMaterial()->getTexture();
const int rm_id = kart_id -
(race_manager->getNumberOfKarts() - race_manager->getNumPlayers());
if (rm_id >= 0 && !race_manager->isWatchingReplay())
ri->m_kart_name = race_manager->getKartInfo(rm_id).getPlayerName();
else
ri->m_kart_name = translations->fribidize(kart->getName());
ri->m_is_player_kart = kart->getController()->isLocalPlayerController();
ri->m_kart_name = getKartDisplayName(kart);
// In FTL karts do have a time, which is shown even when the kart
// is eliminated
@@ -925,6 +902,29 @@ void RaceResultGUI::backToLobby()
} // i < num_karts
} // determineGPLayout
//-----------------------------------------------------------------------------
/** Returns a string to display next to a kart. For a player that's the name
* of the player, for an AI kart it's the name of the driver.
*/
core::stringw RaceResultGUI::getKartDisplayName(const AbstractKart *kart) const
{
const EndController *ec =
dynamic_cast<const EndController*>(kart->getController());
// If the race was given up, there is no end controller for the
// players, so this case needs to be handled separately
if(ec && ec->isLocalPlayerController())
return ec->getName();
else
{
// No end controller, check explicitely for a player controller
const PlayerController *pc =
dynamic_cast<const PlayerController*>(kart->getController());
// Check if the kart is a player controller to get the real name
if(pc) return pc->getName();
}
return translations->fribidize(kart->getName());
} // getKartDisplayName
//-----------------------------------------------------------------------------
/** Displays the race results for a single kart.
* \param n Index of the kart to be displayed.
@@ -1102,6 +1102,7 @@ void RaceResultGUI::backToLobby()
if (own_goal)
{
result_text.append(" ");
//I18N: indicates a player that scored in their own goal in result screen
result_text.append(_("(Own Goal)"));
}

View File

@@ -194,6 +194,7 @@ private:
void displayHighScores();
void displaySoccerResults();
void displayScreenShots();
irr::core::stringw getKartDisplayName(const AbstractKart *kart) const;
int getFontHeight () const;

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

@@ -37,12 +37,12 @@ CheckGoal::CheckGoal(const XMLNode &node, unsigned int index)
m_first_goal = false;
node.get("first_goal", &m_first_goal);
Vec3 p1, p2;
node.get("p1", &p1);
node.get("p2", &p2);
node.get("p1", &m_p1);
node.get("p2", &m_p3);
m_line.setLine( core::vector2df(p1.getX(), p1.getZ()),
core::vector2df(p2.getX(), p2.getZ()) );
m_line.setLine( core::vector2df(m_p1.getX(), m_p1.getZ()),
core::vector2df(m_p3.getX(), m_p3.getZ()) );
m_p2 = (m_p1 + m_p3) / 2;
} // CheckGoal
// ----------------------------------------------------------------------------
@@ -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,16 +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
// ----------------------------------------------------------------------------
Vec3 CheckGoal::convertTo3DCenter() const
{
float x = m_line.getMiddle().X;
float y = m_line.getMiddle().Y;
return Vec3(x, 0, y);
}

View File

@@ -24,7 +24,6 @@
#include <line2d.h>
using namespace irr;
class CheckManager;
class XMLNode;
class Track;
class Vec3;
@@ -37,6 +36,14 @@ class Vec3;
*/
class CheckGoal : public CheckStructure
{
public:
/** Used by AIs to test whether the ball is likely to goal. */
enum PointLocation
{
POINT_FIRST,
POINT_CENTER,
POINT_LAST
};
private:
/** Previois ball position. */
Vec3 m_previous_ball_position;
@@ -47,6 +54,11 @@ private:
/** The line that is tested for being crossed. */
core::line2df m_line;
/** Used by AIs to test whether the ball is likely to goal. */
Vec3 m_p1;
Vec3 m_p2;
Vec3 m_p3;
public:
CheckGoal(const XMLNode &node, unsigned int index);
virtual ~CheckGoal() {}
@@ -59,7 +71,11 @@ public:
// ------------------------------------------------------------------------
bool getTeam() const { return m_first_goal; }
// ------------------------------------------------------------------------
Vec3 convertTo3DCenter() const;
}; // CheckLine
const Vec3& getPoint(PointLocation point) const
{
return (point == POINT_LAST ? m_p3 :
(point == POINT_CENTER ? m_p2 : m_p1));
}
}; // CheckGoal
#endif

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();