Big code refactoring: instead of having auto_kart, player_kart, the karts

now use a 'controller' object which is responsible for its steering, e.g.
a default_ai_controller, player_controller etc. This makes it easier to
switch a player kart to use a end-of-race computer controller to drive
the kart once the race is finished (though that's not implemented yet).
I expect some bugs with restarting race, and perhaps end of race
behaviour at this stage.


git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@4714 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
hikerstk
2010-02-15 00:54:28 +00:00
parent 7a2d4a2a0e
commit da3de6d131
44 changed files with 1567 additions and 776 deletions

View File

@@ -151,6 +151,17 @@ supertuxkart_SOURCES = \
items/rubber_band.cpp \
items/rubber_band.hpp \
karts/auto_kart.hpp \
karts/controller/controller.cpp \
karts/controller/controller.hpp \
karts/controller/default_ai_controller.cpp \
karts/controller/default_ai_controller.hpp \
karts/controller/end_controller.cpp \
karts/controller/end_controller.hpp \
karts/controller/kart_control.hpp \
karts/controller/new_ai_controller.cpp \
karts/controller/new_ai_controller.hpp \
karts/controller/player_controller.cpp \
karts/controller/player_controller.hpp \
karts/kart.cpp \
karts/kart.hpp \
karts/kart_control.hpp \
@@ -248,10 +259,6 @@ supertuxkart_SOURCES = \
replay/replay_player.hpp \
replay/replay_recorder.cpp \
replay/replay_recorder.hpp \
robots/default_robot.cpp \
robots/default_robot.hpp \
robots/new_ai.cpp \
robots/new_ai.hpp \
states_screens/challenges.cpp \
states_screens/challenges.hpp \
states_screens/credits.cpp \

View File

@@ -23,7 +23,6 @@
#include "karts/kart.hpp"
#include "karts/kart_properties_manager.hpp"
#include "karts/player_kart.hpp"
#include "lisp/lisp.hpp"
#include "lisp/parser.hpp"
#include "modes/linear_world.hpp"
@@ -269,7 +268,7 @@ bool ChallengeData::raceFinished()
if(track_name!=m_track_name ) return false; // wrong track
if((int)world->getNumKarts()<m_num_karts ) return false; // not enough AI karts
PlayerKart* kart = world->getPlayerKart(0);
Kart* kart = world->getPlayerKart(0);
if(m_energy>0 && kart->getEnergy() <m_energy ) return false; // not enough energy
if(m_position>0 && kart->getPosition()>m_position) return false; // too far behind

View File

@@ -1,7 +1,7 @@
#include "config/player.hpp"
#include "karts/player_kart.hpp"
#include "karts/controller/player_controller.hpp"
#include "modes/world.hpp"
ActivePlayer::ActivePlayer(PlayerProfile* player, InputDevice *device)
@@ -47,20 +47,3 @@ void ActivePlayer::setDevice(InputDevice* device)
if (device != NULL) device->setPlayer(this);
} // setDevice
// ----------------------------------------------------------------------------
PlayerKart* ActivePlayer::getKart()
{
World *world = World::getWorld();
const int num_karts = world->getNumKarts();
for (int p=0; p<num_karts; p++)
{
Kart *kart = world->getKart(p);
if(kart->isPlayerKart() && ((PlayerKart*)kart)->getPlayer() == this)
{
return (PlayerKart*)kart;
}
}
std::cout << "ActivePlayer::getKart() failed to find player named " << m_player->getName() << std::endl;
return NULL;
} // getKart

View File

@@ -25,7 +25,7 @@
#include "input/input_device.hpp"
class InputDevice;
class PlayerKart;
class Kart;
/**
* class for managing player profiles (name, control configuration, etc.)
@@ -36,13 +36,11 @@ class PlayerProfile
{
private:
// for saving to config file
/** For saving to config file. */
GroupUserConfigParam m_player_group;
StringUserConfigParam m_name;
// int m_last_kart_id;
public:
PlayerProfile(const char* name) : m_player_group("Player", "Represents one human player"),
@@ -66,8 +64,11 @@ public:
*/
class ActivePlayer
{
PlayerProfile* m_player;
InputDevice* m_device;
PlayerProfile *m_player;
InputDevice *m_device;
/** Pointer to the kart of this player, only valid during the game. */
Kart *m_kart;
public:
/** ID of this player within the list of active players */
int m_id;
@@ -80,8 +81,10 @@ public:
InputDevice* getDevice() const;
void setDevice(InputDevice* device);
PlayerKart* getKart();
/** Sets the kart for this player. */
void setKart(Kart *kart) { m_kart = kart; }
/** Returns the kart of this player. Only valid while world exists. */
Kart* getKart() { return m_kart; }
};
#endif

View File

@@ -23,7 +23,7 @@
#include "audio/sound_manager.hpp"
#include "config/user_config.hpp"
#include "graphics/irr_driver.hpp"
#include "karts/player_kart.hpp"
#include "karts/kart.hpp"
#include "modes/world.hpp"
#include "race/race_manager.hpp"
#include "tracks/track.hpp"

View File

@@ -39,7 +39,6 @@
#include "items/attachment_manager.hpp"
#include "items/projectile_manager.hpp"
#include "karts/kart_properties_manager.hpp"
#include "karts/player_kart.hpp"
#include "main_loop.hpp"
#include "modes/world.hpp"
#include "states_screens/dialogs/confirm_resolution_dialog.hpp"
@@ -860,7 +859,7 @@ void IrrDriver::update(float dt)
Kart *kart=world->getKart(i);
if(kart->getCamera())
{
kart->activateCamera();
kart->getCamera()->activate();
m_scene_manager->drawAll();
} // if kart->Camera
} // for i<world->getNumKarts()

View File

@@ -142,7 +142,7 @@ void SkidMarks::update(float dt)
// not turning enough, don't draw skidmarks if kart is going straight ahead
// this is even stricter for Ai karts, since they tend to use LOTS of skidding
const float min_skid_angle = m_kart.isPlayerKart() ? 0.55f : 0.95f;
const float min_skid_angle = m_kart.getController()->isPlayerController() ? 0.55f : 0.95f;
if( fabsf(m_kart.getSteerPercent()) < min_skid_angle) return;
// Start new skid marks

View File

@@ -481,18 +481,6 @@
>
</File>
</Filter>
<Filter
Name="robots"
>
<File
RelativePath="../../../src\robots\default_robot.cpp"
>
</File>
<File
RelativePath="..\..\robots\new_ai.cpp"
>
</File>
</Filter>
<Filter
Name="challenges"
>
@@ -688,10 +676,30 @@
RelativePath="..\..\karts\moveable.cpp"
>
</File>
<File
RelativePath="..\..\karts\player_kart.cpp"
<Filter
Name="controller"
>
</File>
<File
RelativePath="..\..\karts\controller\controller.cpp"
>
</File>
<File
RelativePath="..\..\karts\controller\default_ai_controller.cpp"
>
</File>
<File
RelativePath="..\..\karts\controller\end_controller.cpp"
>
</File>
<File
RelativePath="..\..\karts\controller\new_ai_controller.cpp"
>
</File>
<File
RelativePath="..\..\karts\controller\player_controller.cpp"
>
</File>
</Filter>
</Filter>
<Filter
Name="graphics"
@@ -1063,6 +1071,10 @@
RelativePath="..\..\states_screens\dialogs\enter_player_name_dialog.cpp"
>
</File>
<File
RelativePath="..\..\states_screens\dialogs\gp_info_dialog.cpp"
>
</File>
<File
RelativePath="..\..\states_screens\dialogs\player_info_dialog.cpp"
>
@@ -1199,18 +1211,6 @@
>
</File>
</Filter>
<Filter
Name="robots"
>
<File
RelativePath="../../../src\robots\default_robot.hpp"
>
</File>
<File
RelativePath="..\..\robots\new_ai.hpp"
>
</File>
</Filter>
<Filter
Name="challenges"
>
@@ -1522,10 +1522,30 @@
RelativePath="..\..\karts\moveable.hpp"
>
</File>
<File
RelativePath="..\..\karts\player_kart.hpp"
<Filter
Name="controller"
>
</File>
<File
RelativePath="..\..\karts\controller\controller.hpp"
>
</File>
<File
RelativePath="..\..\karts\controller\default_ai_controller.hpp"
>
</File>
<File
RelativePath="..\..\karts\controller\end_controller.hpp"
>
</File>
<File
RelativePath="..\..\karts\controller\new_ai_controller.hpp"
>
</File>
<File
RelativePath="..\..\karts\controller\player_controller.hpp"
>
</File>
</Filter>
</Filter>
<Filter
Name="graphics"
@@ -1861,6 +1881,10 @@
RelativePath="..\..\states_screens\dialogs\enter_player_name_dialog.hpp"
>
</File>
<File
RelativePath="..\..\states_screens\dialogs\gp_info_dialog.hpp"
>
</File>
<File
RelativePath="..\..\states_screens\dialogs\player_info_dialog.hpp"
>

View File

@@ -3,7 +3,7 @@
#include "guiengine/abstract_state_manager.hpp"
#include "input/input.hpp"
#include "input/input_device.hpp"
#include "karts/player_kart.hpp"
#include "karts/controller/player_controller.hpp"
#include "modes/world.hpp"
#include "race/race_manager.hpp"
#include "states_screens/state_manager.hpp"
@@ -80,6 +80,7 @@ GamePadDevice::GamePadDevice(const int irrIndex, const std::string name, const i
for(int n=0; n<SEvent::SJoystickEvent::NUMBER_OF_BUTTONS; n++)
m_buttonPressed[n] = false;
} // GamePadDevice
// -----------------------------------------------------------------------------
bool GamePadDevice::isButtonPressed(const int i)
@@ -92,12 +93,14 @@ void GamePadDevice::setButtonPressed(const int i, bool isButtonPressed)
}
// -----------------------------------------------------------------------------
void GamePadDevice::resetAxisDirection(const int axis, Input::AxisDirection direction, ActivePlayer* player)
void GamePadDevice::resetAxisDirection(const int axis,
Input::AxisDirection direction,
ActivePlayer* player)
{
KeyBinding bind;
if (StateManager::get()->getGameState() != GUIEngine::GAME) return; // ignore this while in menus
PlayerKart* pk = player->getKart();
Kart* pk = player->getKart();
if (pk == NULL)
{
std::cerr << "Error, trying to reset axis for an unknown player\n";
@@ -109,7 +112,7 @@ void GamePadDevice::resetAxisDirection(const int axis, Input::AxisDirection dire
bind = m_configuration->getBinding(n);
if(bind.id == axis && bind.dir == direction)
{
pk->action((PlayerAction)n, 0);
((PlayerController*)(pk->getController()))->action((PlayerAction)n, 0);
return;
}
}

View File

@@ -37,8 +37,8 @@
#include "input/input.hpp"
#include "items/item_manager.hpp"
#include "items/projectile_manager.hpp"
#include "karts/controller/player_controller.hpp"
#include "karts/kart.hpp"
#include "karts/player_kart.hpp"
#include "modes/world.hpp"
#include "race/history.hpp"
#include "race/race_manager.hpp"
@@ -368,7 +368,7 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID, int btnID,
if (StateManager::get()->getGameState() == GUIEngine::GAME && !GUIEngine::ModalDialog::isADialogActive())
{
// Find the corresponding PlayerKart from our ActivePlayer instance
PlayerKart* pk;
Kart* pk;
if (player == NULL)
{
@@ -384,7 +384,7 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID, int btnID,
return;
}
pk->action(action, abs(value));
((PlayerController*)pk->getController())->action(action, abs(value));
}
// ... when in menus
else

View File

@@ -20,7 +20,7 @@
#include "graphics/camera.hpp"
#include "io/xml_node.hpp"
#include "items/bowling.hpp"
#include "karts/player_kart.hpp"
#include "karts/kart.hpp"
float Bowling::m_st_max_distance; // maximum distance for a bowling ball to be attracted
float Bowling::m_st_max_distance_squared;

View File

@@ -26,7 +26,7 @@
#include "io/xml_node.hpp"
#include "items/rubber_band.hpp"
#include "items/projectile_manager.hpp"
#include "karts/player_kart.hpp"
#include "karts/kart.hpp"
#include "modes/world.hpp"
#include "physics/physical_object.hpp"
#include "tracks/track.hpp"

View File

@@ -178,7 +178,7 @@ void Powerup::use()
// or the kart "throwing" the anvil? Ideally it should be both.
// Meanwhile, don't play it near AI karts since they obviously
// don't hear anything
if(kart->isPlayerKart())
if(kart->getController()->isPlayerController())
m_sound_use->position(kart->getXYZ());
else
m_sound_use->position(m_owner->getXYZ());
@@ -204,7 +204,7 @@ void Powerup::use()
{
kart->attach(ATTACH_PARACHUTE, stk_config->m_parachute_time_other);
if(kart->isPlayerKart())
if(kart->getController()->isPlayerController())
player_kart = kart;
}
}
@@ -213,7 +213,7 @@ void Powerup::use()
// or the kart "throwing" the anvil? Ideally it should be both.
// Meanwhile, don't play it near AI karts since they obviously
// don't hear anything
if(m_owner->isPlayerKart())
if(m_owner->getController()->isPlayerController())
m_sound_use->position(m_owner->getXYZ());
else if(player_kart)
m_sound_use->position(player_kart->getXYZ());

View File

@@ -29,9 +29,9 @@
class AutoKart : public Kart
{
public:
AutoKart(const std::string& kart_name, int position,
AutoKart(const std::string& ident, int position,
const btTransform& init_pos) :
Kart(kart_name, position, init_pos) {}
Kart(ident, position, init_pos) {}
bool isPlayerKart() const {return false;}
}; // AutoKart

View File

@@ -0,0 +1,43 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2010 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//The AI debugging works best with just 1 AI kart, so set the number of karts
//to 2 in main.cpp with quickstart and run supertuxkart with the arg -N.
#include "karts/controller/controller.hpp"
#include "karts/kart.hpp"
/** Constructor, saves the kart pointer and a pointer to the KartControl
* of the kart.
*/
Controller::Controller(Kart *kart)
{
m_controls = &(kart->getControls());
m_kart = kart;
} // Controller
// ----------------------------------------------------------------------------
const irr::core::stringw& Controller::getNamePostfix() const
{
// Static to avoid returning the address of a temporary stringq
static irr::core::stringw name("");
return name;
} // getNamePostfix
// ----------------------------------------------------------------------------

View File

@@ -0,0 +1,60 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2010 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_CONTROLLER_HPP
#define HEADER_CONTROLLER_HPP
#include "irrlicht.h"
using namespace irr;
#include "karts/controller/kart_control.hpp"
class Kart;
class Item;
/** This is the base class for kart controller - that can be a player
* or a a robot.
*/
class Controller
{
protected:
/** Pointer to the kart that is controlled by this controller. */
Kart *m_kart;
/** A pointer to the main controller, from which the kart takes
* it commands. */
KartControl *m_controls;
public:
Controller (Kart *kart);
~Controller () {};
virtual void reset () {};
virtual void update (float dt) {};
virtual void handleZipper () {};
virtual void collectedItem (const Item &item, int add_info=-1,
float previous_energy=0) {};
virtual void crashed () {};
virtual void setPosition (int p) {};
virtual void finishedRace (float time) {};
virtual bool isPlayerController () const {return false;}
virtual bool isNetworkController() const {return false;}
virtual const irr::core::stringw& getNamePostfix() const;
}; // Controller
#endif
/* EOF */

View File

@@ -22,7 +22,7 @@
//to 2 in main.cpp with quickstart and run supertuxkart with the arg -N.
#undef AI_DEBUG
#include "robots/default_robot.hpp"
#include "karts/controller/default_ai_controller.hpp"
#ifdef AI_DEBUG
# include "irrlicht.h"
@@ -37,7 +37,6 @@
#ifdef AI_DEBUG
#include "graphics/irr_driver.hpp"
#endif
#include "karts/player_kart.hpp"
#include "modes/linear_world.hpp"
#include "network/network_manager.hpp"
#include "race/race_manager.hpp"
@@ -45,13 +44,11 @@
#include "tracks/track.hpp"
#include "utils/constants.hpp"
DefaultRobot::DefaultRobot(const std::string& kart_name,
int position, const btTransform& init_pos,
const Track *track) :
AutoKart( kart_name, position, init_pos )
DefaultAIController::DefaultAIController(Kart *kart) : Controller(kart)
{
m_kart_length = m_kart_properties->getKartModel()->getLength();
m_kart_width = m_kart_properties->getKartModel()->getWidth();
m_kart = kart;
m_kart_length = m_kart->getKartProperties()->getKartModel()->getLength();
m_kart_width = m_kart->getKartProperties()->getKartModel()->getWidth();
m_world = dynamic_cast<LinearWorld*>(World::getWorld());
m_track = m_world->getTrack();
m_quad_graph = &m_track->getQuadGraph();
@@ -153,33 +150,41 @@ DefaultRobot::DefaultRobot(const std::string& kart_name,
#ifdef AI_DEBUG
m_debug_sphere = irr_driver->getSceneManager()->addSphereSceneNode(1);
#endif
} // DefaultRobot
} // DefaultAIController
//-----------------------------------------------------------------------------
/** The destructor deletes the shared TrackInfo objects if no more DefaultRobot
/** The destructor deletes the shared TrackInfo objects if no more DefaultAIController
* instances are around.
*/
DefaultRobot::~DefaultRobot()
DefaultAIController::~DefaultAIController()
{
#ifdef AI_DEBUG
irr_driver->removeNode(m_debug_sphere);
#endif
} // ~DefaultRobot
} // ~DefaultAIController
//-----------------------------------------------------------------------------
const irr::core::stringw& DefaultAIController::getNamePostfix() const
{
// Static to avoid returning the address of a temporary stringq
static irr::core::stringw name="(default)";
return name;
} // getNamePostfix
//-----------------------------------------------------------------------------
//TODO: if the AI is crashing constantly, make it move backwards in a straight
//line, then move forward while turning.
void DefaultRobot::update(float dt)
void DefaultAIController::update(float dt)
{
// This is used to enable firing an item backwards.
m_controls.m_look_back = false;
m_controls.m_nitro = false;
m_controls->m_look_back = false;
m_controls->m_nitro = false;
// Update the current node:
if(m_track_node!=QuadGraph::UNKNOWN_SECTOR)
{
int old_node = m_track_node;
m_quad_graph->findRoadSector(getXYZ(), &m_track_node,
m_quad_graph->findRoadSector(m_kart->getXYZ(), &m_track_node,
&m_all_look_aheads[m_track_node]);
// IF the AI is off track (or on a branch of the track it did not
// select to be on), keep the old position.
@@ -189,19 +194,19 @@ void DefaultRobot::update(float dt)
}
if(m_track_node==QuadGraph::UNKNOWN_SECTOR)
{
m_track_node = m_quad_graph->findOutOfRoadSector(getXYZ());
m_track_node = m_quad_graph->findOutOfRoadSector(m_kart->getXYZ());
}
// The client does not do any AI computations.
if(network_manager->getMode()==NetworkManager::NW_CLIENT)
{
AutoKart::update(dt);
Controller::update(dt);
return;
}
if( m_world->isStartPhase() )
{
handleRaceStart();
AutoKart::update(dt);
Controller::update(dt);
return;
}
@@ -212,18 +217,18 @@ void DefaultRobot::update(float dt)
steps = calcSteps();
computeNearestKarts();
checkCrashes( steps, getXYZ() );
checkCrashes( steps, m_kart->getXYZ() );
findCurve();
// Special behaviour if we have a bomb attach: try to hit the kart ahead
// of us.
bool commands_set = false;
if(m_handle_bomb && getAttachment()->getType()==ATTACH_BOMB &&
if(m_handle_bomb && m_kart->getAttachment()->getType()==ATTACH_BOMB &&
m_kart_ahead )
{
// Use nitro if the kart is far ahead, or faster than this kart
m_controls.m_nitro = m_distance_ahead>10.0f ||
m_kart_ahead->getSpeed() > getSpeed();
m_controls->m_nitro = m_distance_ahead>10.0f ||
m_kart_ahead->getSpeed() > m_kart->getSpeed();
// If we are close enough, try to hit this kart
if(m_distance_ahead<=10)
{
@@ -231,10 +236,10 @@ void DefaultRobot::update(float dt)
// If we are faster, try to predict the point where we will hit
// the other kart
if(m_kart_ahead->getSpeed() < getSpeed())
if(m_kart_ahead->getSpeed() < m_kart->getSpeed())
{
float time_till_hit = m_distance_ahead
/ (getSpeed()-m_kart_ahead->getSpeed());
/ (m_kart->getSpeed()-m_kart_ahead->getSpeed());
target += m_kart_ahead->getVelocity()*time_till_hit;
}
float steer_angle = steerToPoint(m_kart_ahead->getXYZ(),
@@ -253,13 +258,13 @@ void DefaultRobot::update(float dt)
handleRescue(dt);
handleBraking();
// If a bomb is attached, nitro might already be set.
if(!m_controls.m_nitro)
if(!m_controls->m_nitro)
handleNitroAndZipper();
}
// If we are supposed to use nitro, but have a zipper,
// use the zipper instead
if(m_controls.m_nitro && m_powerup.getType()==POWERUP_ZIPPER &&
getSpeed()>1.0f && m_zipper_time_left<=0)
if(m_controls->m_nitro && m_kart->getPowerup()->getType()==POWERUP_ZIPPER &&
m_kart->getSpeed()>1.0f && m_kart->getZipperTimeLeft()<=0)
{
// Make sure that not all AI karts use the zipper at the same
// time in time trial at start up, so during the first 5 seconds
@@ -267,26 +272,26 @@ void DefaultRobot::update(float dt)
if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_TIME_TRIAL ||
(m_world->getTime()<3.0f && rand()%50==1) )
{
m_controls.m_nitro = false;
m_controls.m_fire = true;
m_controls->m_nitro = false;
m_controls->m_fire = true;
}
}
/*And obviously general kart stuff*/
AutoKart::update(dt);
Controller::update(dt);
m_collided = false;
} // update
//-----------------------------------------------------------------------------
void DefaultRobot::handleBraking()
void DefaultAIController::handleBraking()
{
// In follow the leader mode, the kart should brake if they are ahead of
// the leader (and not the leader, i.e. don't have initial position 1)
if(race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER &&
getPosition() < m_world->getKart(0)->getPosition() &&
getInitialPosition()>1 )
m_kart->getPosition() < m_world->getKart(0)->getPosition() &&
m_kart->getInitialPosition()>1 )
{
m_controls.m_brake = true;
m_controls->m_brake = true;
return;
}
@@ -294,13 +299,13 @@ void DefaultRobot::handleBraking()
//We may brake if we are about to get out of the road, but only if the
//kart is on top of the road, and if we won't slow down below a certain
//limit.
if (m_crashes.m_road && getVelocityLC().getY() > MIN_SPEED &&
m_world->isOnRoad(getWorldKartId()) )
if (m_crashes.m_road && m_kart->getVelocityLC().getY() > MIN_SPEED &&
m_world->isOnRoad(m_kart->getWorldKartId()) )
{
float kart_ang_diff =
m_quad_graph->getAngleToNext(m_track_node,
m_successor_index[m_track_node])
- getHPR().getHeading();
- m_kart->getHPR().getHeading();
kart_ang_diff = normalizeAngle(kart_ang_diff);
kart_ang_diff = fabsf(kart_ang_diff);
@@ -314,21 +319,21 @@ void DefaultRobot::handleBraking()
//if the curve angle is bigger than what the kart can steer, brake
//even if we are in the inside, because the kart would be 'thrown'
//out of the curve.
if(!(m_world->getDistanceToCenterForKart(getWorldKartId())
if(!(m_world->getDistanceToCenterForKart(m_kart->getWorldKartId())
> m_quad_graph->getNode(m_track_node).getPathWidth() *
-CURVE_INSIDE_PERC || m_curve_angle > RAD_TO_DEGREE*getMaxSteerAngle()))
-CURVE_INSIDE_PERC || m_curve_angle > RAD_TO_DEGREE*m_kart->getMaxSteerAngle()))
{
m_controls.m_brake = false;
m_controls->m_brake = false;
return;
}
}
else if( m_curve_angle < -MIN_TRACK_ANGLE ) //Next curve is right
{
if(!(m_world->getDistanceToCenterForKart( getWorldKartId() )
if(!(m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() )
< m_quad_graph->getNode(m_track_node).getPathWidth() *
CURVE_INSIDE_PERC || m_curve_angle < -RAD_TO_DEGREE*getMaxSteerAngle()))
CURVE_INSIDE_PERC || m_curve_angle < -RAD_TO_DEGREE*m_kart->getMaxSteerAngle()))
{
m_controls.m_brake = false;
m_controls->m_brake = false;
return;
}
}
@@ -336,23 +341,23 @@ void DefaultRobot::handleBraking()
//Brake if the kart's speed is bigger than the speed we need
//to go through the curve at the widest angle, or if the kart
//is not going straight in relation to the road.
if(getVelocityLC().getY() > m_curve_target_speed ||
if(m_kart->getVelocityLC().getY() > m_curve_target_speed ||
kart_ang_diff > MIN_TRACK_ANGLE )
{
#ifdef AI_DEBUG
std::cout << "BRAKING" << std::endl;
#endif
m_controls.m_brake = true;
m_controls->m_brake = true;
return;
}
}
m_controls.m_brake = false;
m_controls->m_brake = false;
} // handleBraking
//-----------------------------------------------------------------------------
void DefaultRobot::handleSteering(float dt)
void DefaultAIController::handleSteering(float dt)
{
const int next = m_next_node_index[m_track_node];
@@ -362,7 +367,7 @@ void DefaultRobot::handleSteering(float dt)
*finite state machine.
*/
//Reaction to being outside of the road
if( fabsf(m_world->getDistanceToCenterForKart( getWorldKartId() )) >
if( fabsf(m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() )) >
0.5f* m_quad_graph->getNode(m_track_node).getPathWidth()+0.5f )
{
steer_angle = steerToPoint(m_quad_graph->getQuad(next).getCenter(),
@@ -391,7 +396,7 @@ void DefaultRobot::handleSteering(float dt)
}
else
{
if(m_world->getDistanceToCenterForKart( getWorldKartId() ) >
if(m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() ) >
m_world->getDistanceToCenterForKart( m_crashes.m_kart ))
{
steer_angle = steerToAngle(next, -M_PI*0.5f );
@@ -454,10 +459,10 @@ void DefaultRobot::handleSteering(float dt)
} // handleSteering
//-----------------------------------------------------------------------------
void DefaultRobot::handleItems( const float DELTA, const int STEPS )
void DefaultAIController::handleItems( const float DELTA, const int STEPS )
{
m_controls.m_fire = false;
if(isRescue() || m_powerup.getType() == POWERUP_NOTHING ) return;
m_controls->m_fire = false;
if(m_kart->isRescue() || m_kart->getPowerup()->getType() == POWERUP_NOTHING ) return;
m_time_since_last_shot += DELTA;
@@ -467,7 +472,7 @@ void DefaultRobot::handleItems( const float DELTA, const int STEPS )
{
if( m_time_since_last_shot > 10.0f )
{
m_controls.m_fire = true;
m_controls->m_fire = true;
m_time_since_last_shot = 0.0f;
}
return;
@@ -475,7 +480,7 @@ void DefaultRobot::handleItems( const float DELTA, const int STEPS )
// Tactic 2: calculate
// -------------------
switch( m_powerup.getType() )
switch( m_kart->getPowerup()->getType() )
{
case POWERUP_ZIPPER:
// Do nothing. Further up a zipper is used if nitro should be selected,
@@ -490,7 +495,7 @@ void DefaultRobot::handleItems( const float DELTA, const int STEPS )
// kart as well? I.e. only drop if the kart behind is faster? Otoh
// this approach helps preventing an overtaken kart to overtake us
// again.
m_controls.m_fire = (m_distance_behind < 15.0f &&
m_controls->m_fire = (m_distance_behind < 15.0f &&
m_distance_behind > 3.0f ) ||
m_time_since_last_shot>10.0f;
if(m_distance_behind < 10.0f && m_distance_behind > 2.0f )
@@ -500,7 +505,7 @@ void DefaultRobot::handleItems( const float DELTA, const int STEPS )
// towards m_kart_ahead. And some of them can fire backwards, too - which
// isn't yet supported for AI karts.
case POWERUP_CAKE:
m_controls.m_fire = (m_kart_ahead && m_distance_ahead < 20.0f) ||
m_controls->m_fire = (m_kart_ahead && m_distance_ahead < 20.0f) ||
m_time_since_last_shot > 10.0f;
break;
case POWERUP_BOWLING:
@@ -513,11 +518,11 @@ void DefaultRobot::handleItems( const float DELTA, const int STEPS )
!m_kart_ahead;
float distance = fire_backwards ? m_distance_behind
: m_distance_ahead;
m_controls.m_fire = (fire_backwards && distance < 30.0f) ||
m_controls->m_fire = (fire_backwards && distance < 30.0f) ||
(!fire_backwards && distance <10.0f) ||
m_time_since_last_shot > 10.0f;
if(m_controls.m_fire)
m_controls.m_look_back = fire_backwards;
if(m_controls->m_fire)
m_controls->m_look_back = fire_backwards;
break;
}
case POWERUP_PLUNGER:
@@ -529,26 +534,27 @@ void DefaultRobot::handleItems( const float DELTA, const int STEPS )
!m_kart_ahead;
float distance = fire_backwards ? m_distance_behind
: m_distance_ahead;
m_controls.m_fire = distance < 30.0f ||
m_controls->m_fire = distance < 30.0f ||
m_time_since_last_shot > 10.0f;
if(m_controls.m_fire)
m_controls.m_look_back = fire_backwards;
if(m_controls->m_fire)
m_controls->m_look_back = fire_backwards;
break;
}
case POWERUP_ANVIL:
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER)
{
m_controls.m_fire = m_world->getTime()<1.0f && getPosition()>2;
m_controls->m_fire = m_world->getTime()<1.0f &&
m_kart->getPosition()>2;
}
else
{
m_controls.m_fire = m_time_since_last_shot > 3.0f &&
getPosition()>1;
m_controls->m_fire = m_time_since_last_shot > 3.0f &&
m_kart->getPosition()>1;
}
default:
m_controls.m_fire = true;
m_controls->m_fire = true;
}
if(m_controls.m_fire) m_time_since_last_shot = 0.0f;
if(m_controls->m_fire) m_time_since_last_shot = 0.0f;
return;
} // handleItems
@@ -557,10 +563,10 @@ void DefaultRobot::handleItems( const float DELTA, const int STEPS )
* '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 DefaultRobot::computeNearestKarts()
void DefaultAIController::computeNearestKarts()
{
bool need_to_check = false;
int my_position = getPosition();
int my_position = m_kart->getPosition();
// See if the kart ahead has changed:
if( ( m_kart_ahead && m_kart_ahead->getPosition()+1!=my_position ) ||
(!m_kart_ahead && my_position>1 ) )
@@ -573,11 +579,11 @@ void DefaultRobot::computeNearestKarts()
m_kart_behind = m_kart_ahead = NULL;
m_distance_ahead = m_distance_behind = 9999999.9f;
float my_dist = m_world->getDistanceDownTrackForKart(getWorldKartId());
float my_dist = m_world->getDistanceDownTrackForKart(m_kart->getWorldKartId());
for(unsigned int i=0; i<m_world->getNumKarts(); i++)
{
Kart *k = m_world->getKart(i);
if(k->isEliminated() || k==this) continue;
if(k->isEliminated() || k==m_kart) continue;
if(k->getPosition()==my_position+1)
{
m_kart_behind = k;
@@ -597,28 +603,28 @@ void DefaultRobot::computeNearestKarts()
} // computeNearestKarts
//-----------------------------------------------------------------------------
void DefaultRobot::handleAcceleration( const float DELTA )
void DefaultAIController::handleAcceleration( const float DELTA )
{
//Do not accelerate until we have delayed the start enough
if( m_time_till_start > 0.0f )
{
m_time_till_start -= DELTA;
m_controls.m_accel = 0.0f;
m_controls->m_accel = 0.0f;
return;
}
if( m_controls.m_brake == true )
if( m_controls->m_brake == true )
{
m_controls.m_accel = 0.0f;
m_controls->m_accel = 0.0f;
return;
}
if(hasViewBlockedByPlunger())
if(m_kart->hasViewBlockedByPlunger())
{
if(!(getSpeed() > getMaxSpeedOnTerrain() / 2))
m_controls.m_accel = 0.05f;
if(!(m_kart->getSpeed() > m_kart->getMaxSpeedOnTerrain() / 2))
m_controls->m_accel = 0.05f;
else
m_controls.m_accel = 0.0f;
m_controls->m_accel = 0.0f;
return;
}
@@ -627,7 +633,7 @@ void DefaultRobot::handleAcceleration( const float DELTA )
//Find if any player is ahead of this kart
bool player_winning = false;
for(unsigned int i = 0; i < race_manager->getNumPlayers(); ++i )
if( getPosition() > m_world->getPlayerKart(i)->getPosition() )
if( m_kart->getPosition() > m_world->getPlayerKart(i)->getPosition() )
{
player_winning = true;
break;
@@ -635,16 +641,16 @@ void DefaultRobot::handleAcceleration( const float DELTA )
if( player_winning )
{
m_controls.m_accel = m_max_handicap_accel;
m_controls->m_accel = m_max_handicap_accel;
return;
}
}
m_controls.m_accel = 1.0f;
m_controls->m_accel = 1.0f;
} // handleAcceleration
//-----------------------------------------------------------------------------
void DefaultRobot::handleRaceStart()
void DefaultAIController::handleRaceStart()
{
//FIXME: make karts able to get a penalty for accelerating too soon
//like players, should happen to about 20% of the karts in easy,
@@ -660,15 +666,15 @@ void DefaultRobot::handleRaceStart()
} // handleRaceStart
//-----------------------------------------------------------------------------
void DefaultRobot::handleRescue(const float DELTA)
void DefaultAIController::handleRescue(const float DELTA)
{
// check if kart is stuck
if(getSpeed()<2.0f && !isRescue() && !m_world->isStartPhase())
if(m_kart->getSpeed()<2.0f && !m_kart->isRescue() && !m_world->isStartPhase())
{
m_time_since_stuck += DELTA;
if(m_time_since_stuck > 2.0f)
{
forceRescue();
m_kart->forceRescue();
m_time_since_stuck=0.0f;
} // m_time_since_stuck > 2.0f
}
@@ -681,36 +687,36 @@ void DefaultRobot::handleRescue(const float DELTA)
//-----------------------------------------------------------------------------
/** Decides wether to use nitro or not.
*/
void DefaultRobot::handleNitroAndZipper()
void DefaultAIController::handleNitroAndZipper()
{
m_controls.m_nitro = false;
m_controls->m_nitro = false;
// If we are already very fast, save nitro.
if(getSpeed() > 0.95f*getMaxSpeedOnTerrain())
if(m_kart->getSpeed() > 0.95f*m_kart->getMaxSpeedOnTerrain())
return;
// Don't use nitro when the AI has a plunger in the face!
if(hasViewBlockedByPlunger()) return;
if(m_kart->hasViewBlockedByPlunger()) return;
// Don't use nitro if the kart doesn't have any or is not on ground.
if(!isOnGround() || hasFinishedRace()) return;
if(!m_kart->isOnGround() || m_kart->hasFinishedRace()) return;
// Don't compute nitro usage if we don't have nitro or are not supposed
// to use it, and we don't have a zipper or are not supposed to use
// it (calculated).
if( (getEnergy()==0 || m_nitro_level==NITRO_NONE) &&
(m_powerup.getType()!=POWERUP_ZIPPER || m_item_tactic==IT_TEN_SECONDS) )
if( (m_kart->getEnergy()==0 || m_nitro_level==NITRO_NONE) &&
(m_kart->getPowerup()->getType()!=POWERUP_ZIPPER || m_item_tactic==IT_TEN_SECONDS) )
return;
// If a parachute or anvil is attached, the nitro doesn't give much
// benefit. Better wait till later.
const bool has_slowdown_attachment =
m_attachment.getType()==ATTACH_PARACHUTE ||
m_attachment.getType()==ATTACH_ANVIL;
m_kart->getAttachment()->getType()==ATTACH_PARACHUTE ||
m_kart->getAttachment()->getType()==ATTACH_ANVIL;
if(has_slowdown_attachment) return;
// If the kart is very slow (e.g. after rescue), use nitro
if(getSpeed()<5)
if(m_kart->getSpeed()<5)
{
m_controls.m_nitro = true;
m_controls->m_nitro = true;
return;
}
@@ -718,9 +724,9 @@ void DefaultRobot::handleNitroAndZipper()
// (i.e. more than 2) nitro, use it.
// -------------------------------------------------
const unsigned int num_karts = m_world->getCurrentNumKarts();
if(getPosition()== (int)num_karts && getEnergy()>2.0f)
if(m_kart->getPosition()== (int)num_karts && m_kart->getEnergy()>2.0f)
{
m_controls.m_nitro = true;
m_controls->m_nitro = true;
return;
}
@@ -728,13 +734,13 @@ void DefaultRobot::handleNitroAndZipper()
// anyway. Since the kart is faster with nitro, estimate a 50% time
// decrease (additionally some nitro will be saved when top speed
// is reached).
if(m_world->getLapForKart(getWorldKartId())==race_manager->getNumLaps()-1 &&
if(m_world->getLapForKart(m_kart->getWorldKartId())==race_manager->getNumLaps()-1 &&
m_nitro_level == NITRO_ALL)
{
float finish = m_world->getEstimatedFinishTime(getWorldKartId());
if( 1.5f*getEnergy() >= finish - m_world->getTime() )
float finish = m_world->getEstimatedFinishTime(m_kart->getWorldKartId());
if( 1.5f*m_kart->getEnergy() >= finish - m_world->getTime() )
{
m_controls.m_nitro = true;
m_controls->m_nitro = true;
return;
}
}
@@ -746,35 +752,35 @@ void DefaultRobot::handleNitroAndZipper()
// Try to overtake a kart that is close ahead, except
// when we are already much faster than that kart
// --------------------------------------------------
if(m_kart_ahead &&
m_distance_ahead < overtake_distance &&
m_kart_ahead->getSpeed()+5.0f > getSpeed() )
if(m_kart_ahead &&
m_distance_ahead < overtake_distance &&
m_kart_ahead->getSpeed()+5.0f > m_kart->getSpeed() )
{
m_controls.m_nitro = true;
m_controls->m_nitro = true;
return;
}
if(m_kart_behind &&
m_distance_behind < overtake_distance &&
m_kart_behind->getSpeed() > getSpeed() )
if(m_kart_behind &&
m_distance_behind < overtake_distance &&
m_kart_behind->getSpeed() > m_kart->getSpeed() )
{
// Only prevent overtaking on highest level
m_controls.m_nitro = m_nitro_level==NITRO_ALL;
m_controls->m_nitro = m_nitro_level==NITRO_ALL;
return;
}
} // handleNitroAndZipper
//-----------------------------------------------------------------------------
float DefaultRobot::steerToAngle(const size_t SECTOR, const float ANGLE)
float DefaultAIController::steerToAngle(const size_t SECTOR, const float ANGLE)
{
float angle = m_quad_graph->getAngleToNext(SECTOR,
m_successor_index[SECTOR]);
//Desired angle minus current angle equals how many angles to turn
float steer_angle = angle - getHPR().getHeading();
float steer_angle = angle - m_kart->getHPR().getHeading();
if(hasViewBlockedByPlunger())
if(m_kart->hasViewBlockedByPlunger())
steer_angle += ANGLE/5;
else
steer_angle += ANGLE;
@@ -791,20 +797,20 @@ float DefaultRobot::steerToAngle(const size_t SECTOR, const float ANGLE)
* \param point Point to steer towards.
* \param dt Time step.
*/
float DefaultRobot::steerToPoint(const Vec3 &point, float dt)
float DefaultAIController::steerToPoint(const Vec3 &point, float dt)
{
// No sense steering if we are not driving.
if(getSpeed()==0) return 0.0f;
const float dx = point.getX() - getXYZ().getX();
const float dy = point.getY() - getXYZ().getY();
if(m_kart->getSpeed()==0) return 0.0f;
const float dx = point.getX() - m_kart->getXYZ().getX();
const float dy = point.getY() - m_kart->getXYZ().getY();
/** Angle from the kart position to the point in world coordinates. */
float theta = -atan2(dx, dy);
// Angle is the point is relative to the heading - but take the current
// angular velocity into account, too. The value is multiplied by two
// to avoid 'oversteering' - experimentally found.
float angle_2_point = theta - getHPR().getHeading()
- dt*m_body->getAngularVelocity().getZ()*2.0f;
float angle_2_point = theta - m_kart->getHPR().getHeading()
- dt*m_kart->getBody()->getAngularVelocity().getZ()*2.0f;
angle_2_point = normalizeAngle(angle_2_point);
if(fabsf(angle_2_point)<0.1) return 0.0f;
@@ -820,8 +826,8 @@ float DefaultRobot::steerToPoint(const Vec3 &point, float dt)
* computed, and from that the turn radius, and then the steer angle.
* (note: the 2*M_PI can be removed from the computations)
*/
float radius = dt*getSpeed()/angle_2_point;
float sin_steer_angle = m_kart_properties->getWheelBase()/radius;
float radius = dt*m_kart->getSpeed()/angle_2_point;
float sin_steer_angle = m_kart->getKartProperties()->getWheelBase()/radius;
#ifdef DEBUG_OUTPUT
printf("theta %f a2p %f angularv %f radius %f ssa %f\n",
theta, angle_2_point, m_body->getAngularVelocity().getZ(),
@@ -829,14 +835,16 @@ float DefaultRobot::steerToPoint(const Vec3 &point, float dt)
#endif
// Add 0.1 since rouding errors will otherwise result in the kart
// not using drifting.
if(sin_steer_angle <= -1.0f) return -getMaxSteerAngle()*m_skidding_threshold-0.1f;
if(sin_steer_angle >= 1.0f) return getMaxSteerAngle()*m_skidding_threshold+0.1f;
if(sin_steer_angle <= -1.0f)
return -m_kart->getMaxSteerAngle()*m_skidding_threshold-0.1f;
if(sin_steer_angle >= 1.0f)
return m_kart->getMaxSteerAngle()*m_skidding_threshold+0.1f;
float steer_angle = asin(sin_steer_angle);
return steer_angle;
} // steerToPoint
//-----------------------------------------------------------------------------
void DefaultRobot::checkCrashes( const int STEPS, const Vec3& pos )
void DefaultAIController::checkCrashes( const int STEPS, const Vec3& pos )
{
//Right now there are 2 kind of 'crashes': with other karts and another
//with the track. The sight line is used to find if the karts crash with
@@ -848,7 +856,7 @@ void DefaultRobot::checkCrashes( const int STEPS, const Vec3& pos )
const size_t NUM_KARTS = m_world->getNumKarts();
//Protection against having vel_normal with nan values
const Vec3 &VEL = getVelocity();
const Vec3 &VEL = m_kart->getVelocity();
Vec3 vel_normal(VEL.getX(), VEL.getY(), 0.0);
float speed=vel_normal.length();
// If the velocity is zero, no sense in checking for crashes in time
@@ -871,10 +879,10 @@ void DefaultRobot::checkCrashes( const int STEPS, const Vec3& pos )
for( unsigned int j = 0; j < NUM_KARTS; ++j )
{
const Kart* kart = m_world->getKart(j);
if(kart==this||kart->isEliminated()) continue; // ignore eliminated karts
if(kart==m_kart||kart->isEliminated()) continue; // ignore eliminated karts
const Kart *other_kart = m_world->getKart(j);
// Ignore karts ahead that are faster than this kart.
if(getVelocityLC().getY() < other_kart->getVelocityLC().getY())
if(m_kart->getVelocityLC().getY() < other_kart->getVelocityLC().getY())
continue;
Vec3 other_kart_xyz = other_kart->getXYZ() + other_kart->getVelocity()*(i*dt);
float kart_distance = (step_coord - other_kart_xyz).length_2d();
@@ -904,7 +912,7 @@ void DefaultRobot::checkCrashes( const int STEPS, const Vec3& pos )
* the two edges of the track is closest to the next curve after wards,
* and return the position of that edge.
*/
void DefaultRobot::findNonCrashingPoint(Vec3 *result)
void DefaultAIController::findNonCrashingPoint(Vec3 *result)
{
unsigned int sector = m_next_node_index[m_track_node];
int target_sector;
@@ -922,7 +930,8 @@ void DefaultRobot::findNonCrashingPoint(Vec3 *result)
target_sector = m_next_node_index[sector];
//direction is a vector from our kart to the sectors we are testing
direction = m_quad_graph->getQuad(target_sector).getCenter() - getXYZ();
direction = m_quad_graph->getQuad(target_sector).getCenter()
- m_kart->getXYZ();
float len=direction.length_2d();
steps = int( len / m_kart_length );
@@ -937,7 +946,7 @@ void DefaultRobot::findNonCrashingPoint(Vec3 *result)
//Test if we crash if we drive towards the target sector
for( int i = 2; i < steps; ++i )
{
step_coord = getXYZ()+direction*m_kart_length * float(i);
step_coord = m_kart->getXYZ()+direction*m_kart_length * float(i);
m_quad_graph->spatialToTrack(&step_track_coord, step_coord,
sector );
@@ -957,11 +966,11 @@ void DefaultRobot::findNonCrashingPoint(Vec3 *result)
} // findNonCrashingPoint
//-----------------------------------------------------------------------------
void DefaultRobot::reset()
void DefaultAIController::reset()
{
m_time_since_last_shot = 0.0f;
m_start_kart_crash_direction = 0;
m_curve_target_speed = getMaxSpeedOnTerrain();
m_curve_target_speed = m_kart->getMaxSpeedOnTerrain();
m_curve_angle = 0.0;
m_time_till_start = -1.0f;
m_crash_time = 0.0f;
@@ -972,20 +981,20 @@ void DefaultRobot::reset()
m_kart_behind = NULL;
m_distance_behind = 0.0f;
AutoKart::reset();
Controller::reset();
m_track_node = QuadGraph::UNKNOWN_SECTOR;
m_quad_graph->findRoadSector(getXYZ(), &m_track_node);
m_quad_graph->findRoadSector(m_kart->getXYZ(), &m_track_node);
if(m_track_node==QuadGraph::UNKNOWN_SECTOR)
{
fprintf(stderr, "Invalid starting position for '%s' - not on track - can be ignored.\n",
getIdent().c_str());
m_track_node = m_quad_graph->findOutOfRoadSector(getXYZ());
m_kart->getIdent().c_str());
m_track_node = m_quad_graph->findOutOfRoadSector(m_kart->getXYZ());
}
} // reset
//-----------------------------------------------------------------------------
inline float DefaultRobot::normalizeAngle(float angle)
inline float DefaultAIController::normalizeAngle(float angle)
{
while( angle > 2*M_PI ) angle -= 2*M_PI;
while( angle < -2*M_PI ) angle += 2*M_PI;
@@ -1001,9 +1010,9 @@ inline float DefaultRobot::normalizeAngle(float angle)
* and gets the number of steps to use for the sight line of the kart.
* The calling sequence guarantees that m_future_sector is not UNKNOWN.
*/
int DefaultRobot::calcSteps()
int DefaultAIController::calcSteps()
{
int steps = int( getVelocityLC().getY() / m_kart_length );
int steps = int( m_kart->getVelocityLC().getY() / m_kart_length );
if( steps < m_min_steps ) steps = m_min_steps;
//Increase the steps depending on the width, if we steering hard,
@@ -1013,7 +1022,7 @@ int DefaultRobot::calcSteps()
// for more steps if we hit another kart?? If we steer hard,
// the approximation used (pos + velocity*dt) will be even
// worse, since it doesn't take steering into account.
if( fabsf(m_controls.m_steer) > 0.95 )
if( fabsf(m_controls->m_steer) > 0.95 )
{
const int WIDTH_STEPS =
(int)( m_quad_graph->getNode(m_future_sector).getPathWidth()
@@ -1036,32 +1045,32 @@ int DefaultRobot::calcSteps()
* \param angle Steering angle.
* \param dt Time step.
*/
void DefaultRobot::setSteering(float angle, float dt)
void DefaultAIController::setSteering(float angle, float dt)
{
float steer_fraction = angle / getMaxSteerAngle();
m_controls.m_drift = fabsf(steer_fraction)>=m_skidding_threshold;
if(hasViewBlockedByPlunger()) m_controls.m_drift = false;
float old_steer = m_controls.m_steer;
float steer_fraction = angle / m_kart->getMaxSteerAngle();
m_controls->m_drift = fabsf(steer_fraction)>=m_skidding_threshold;
if(m_kart->hasViewBlockedByPlunger()) m_controls->m_drift = false;
float old_steer = m_controls->m_steer;
if (steer_fraction > 1.0f) steer_fraction = 1.0f;
else if(steer_fraction < -1.0f) steer_fraction = -1.0f;
if(hasViewBlockedByPlunger())
if(m_kart->hasViewBlockedByPlunger())
{
if (steer_fraction > 0.5f) steer_fraction = 0.5f;
else if(steer_fraction < -0.5f) steer_fraction = -0.5f;
}
// The AI has its own 'time full steer' value (which is the time
float max_steer_change = dt/m_kart_properties->getTimeFullSteerAI();
float max_steer_change = dt/m_kart->getKartProperties()->getTimeFullSteerAI();
if(old_steer < steer_fraction)
{
m_controls.m_steer = (old_steer+max_steer_change > steer_fraction)
m_controls->m_steer = (old_steer+max_steer_change > steer_fraction)
? steer_fraction : old_steer+max_steer_change;
}
else
{
m_controls.m_steer = (old_steer-max_steer_change < steer_fraction)
m_controls->m_steer = (old_steer-max_steer_change < steer_fraction)
? steer_fraction : old_steer-max_steer_change;
}
} // setSteering
@@ -1073,11 +1082,11 @@ void DefaultRobot::setSteering(float angle, float dt)
*
* The number of sectors that form the curve is dependant on the kart's speed.
*/
void DefaultRobot::findCurve()
void DefaultAIController::findCurve()
{
float total_dist = 0.0f;
int i;
for(i = m_track_node; total_dist < getVelocityLC().getY();
for(i = m_track_node; total_dist < m_kart->getVelocityLC().getY();
i = m_next_node_index[i])
{
total_dist += m_quad_graph->getDistanceToNext(i, m_successor_index[i]);
@@ -1089,5 +1098,5 @@ void DefaultRobot::findCurve()
-m_quad_graph->getAngleToNext(m_track_node,
m_successor_index[m_track_node]) );
m_curve_target_speed = getMaxSpeedOnTerrain();
m_curve_target_speed = m_kart->getMaxSpeedOnTerrain();
} // findCurve

View File

@@ -1,6 +1,7 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2004-2005 Steve Baker <sjbaker1@airmail.net>
// Copyright (C) 2006-2007 Eduardo Hernandez Munoz
// Copyright (C) 2010 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
@@ -16,14 +17,14 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_DEFAULT_HPP
#define HEADER_DEFAULT_HPP
#ifndef HEADER_DEFAULT_SI_CONTROLLER_HPP
#define HEADER_DEFAULT_AI_CONTROLLER_HPP
#include "karts/auto_kart.hpp"
#include "modes/profile_world.hpp"
#include "utils/vec3.hpp"
#include "karts/controller/controller.hpp"
/* third coord won't be used */
//#include "karts/auto_kart.hpp"
//#include "modes/profile_world.hpp"
//#include "utils/vec3.hpp"
class Track;
class LinearWorld;
@@ -37,7 +38,7 @@ namespace irr
}
}
class DefaultRobot : public AutoKart
class DefaultAIController : public Controller
{
private:
enum FallbackTactic
@@ -194,22 +195,12 @@ private:
void findCurve();
public:
DefaultRobot(const std::string& kart_name, int position,
const btTransform& init_pos, const Track *track);
~DefaultRobot();
void update (float delta) ;
void reset ();
DefaultAIController(Kart *kart);
~DefaultAIController();
virtual void update (float delta) ;
virtual void reset ();
virtual void crashed (Kart *k) {if(k) m_collided = true;};
virtual const irr::core::stringw& getName() const
{
// Static to avoid returning the address of a temporary stringq
static irr::core::stringw name=m_kart_properties->getName();
// Add the name of the AI in case of profile mode, to make it
// easier to compare AIs
if(ProfileWorld::isProfileMode())
name+="(default)";
return name;
}
virtual const irr::core::stringw& getNamePostfix() const;
};
#endif

View File

@@ -0,0 +1,551 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2004-2005 Steve Baker <sjbaker1@airmail.net>
// Copyright (C) 2006-2007 Eduardo Hernandez Munoz
// Copyright (C) 2008 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//The AI debugging works best with just 1 AI kart, so set the number of karts
//to 2 in main.cpp with quickstart and run supertuxkart with the arg -N.
#undef AI_DEBUG
#include "karts/controller/end_controller.hpp"
#ifdef AI_DEBUG
# include "irrlicht.h"
using namespace irr;
#endif
#include <cstdlib>
#include <ctime>
#include <cstdio>
#include <iostream>
#ifdef AI_DEBUG
#include "graphics/irr_driver.hpp"
#endif
#include "modes/linear_world.hpp"
#include "network/network_manager.hpp"
#include "race/race_manager.hpp"
#include "tracks/quad_graph.hpp"
#include "tracks/track.hpp"
#include "utils/constants.hpp"
EndController::EndController(Kart *kart) : Controller(kart)
{
m_kart_length = m_kart->getKartProperties()->getKartModel()->getLength();
m_kart_width = m_kart->getKartProperties()->getKartModel()->getWidth();
m_world = dynamic_cast<LinearWorld*>(World::getWorld());
m_track = m_world->getTrack();
m_quad_graph = &m_track->getQuadGraph();
m_next_node_index.reserve(m_quad_graph->getNumNodes());
m_successor_index.reserve(m_quad_graph->getNumNodes());
// Initialise the fields with -1
for(unsigned int i=0; i<m_quad_graph->getNumNodes(); i++)
{
m_next_node_index.push_back(-1);
m_successor_index.push_back(-1);
}
// For now pick one part on random, which is not adjusted during the run
std::vector<unsigned int> next;
int count=0;
int current_node=0;
while (1)
{
next.clear();
m_quad_graph->getSuccessors(current_node, next);
int indx = rand() % next.size();
m_successor_index[current_node] = indx;
m_next_node_index[current_node] = next[indx];
current_node = next[indx];
if(current_node==0) break;
count++;
if(count>(int)m_quad_graph->getNumNodes())
{
fprintf(stderr, "AI can't find a loop going back to node 0, aborting.\n");
exit(-1);
}
};
const unsigned int look_ahead=10;
// Now compute for each node in the graph the list of the next 'look_ahead'
// graph nodes. This is the list of node that is tested in checkCrashes.
// If the look_ahead is too big, the AI can skip loops (see
// QuadGraph::findRoadSector for details), if it's too short the AI won't
// find too good a driveline. Note that in general this list should
// be computed recursively, but since the AI for now is using only
// (randomly picked) path this is fine
m_all_look_aheads.reserve(m_quad_graph->getNumNodes());
for(unsigned int i=0; i<m_quad_graph->getNumNodes(); i++)
{
std::vector<int> l;
int current = i;
for(unsigned int j=0; j<look_ahead; j++)
{
int next = m_next_node_index[current];
if(next==-1) break;
l.push_back(m_next_node_index[current]);
current = m_next_node_index[current];
} // for j<look_ahead
m_all_look_aheads.push_back(l);
}
// Reset must be called after m_quad_graph etc. is set up
reset();
m_max_handicap_accel = 1.0f;
m_min_steps = 2;
m_skidding_threshold = 1.3f;
#ifdef AI_DEBUG
m_debug_sphere = irr_driver->getSceneManager()->addSphereSceneNode(1);
#endif
} // EndController
//-----------------------------------------------------------------------------
/** The destructor deletes the shared TrackInfo objects if no more EndController
* instances are around.
*/
EndController::~EndController()
{
#ifdef AI_DEBUG
irr_driver->removeNode(m_debug_sphere);
#endif
} // ~EndController
//-----------------------------------------------------------------------------
//TODO: if the AI is crashing constantly, make it move backwards in a straight
//line, then move forward while turning.
void EndController::update(float dt)
{
// This is used to enable firing an item backwards.
m_controls->m_look_back = false;
m_controls->m_nitro = false;
// Update the current node:
if(m_track_node!=QuadGraph::UNKNOWN_SECTOR)
{
int old_node = m_track_node;
m_quad_graph->findRoadSector(m_kart->getXYZ(), &m_track_node,
&m_all_look_aheads[m_track_node]);
// IF the AI is off track (or on a branch of the track it did not
// select to be on), keep the old position.
if(m_track_node==QuadGraph::UNKNOWN_SECTOR ||
m_next_node_index[m_track_node]==-1)
m_track_node = old_node;
}
if(m_track_node==QuadGraph::UNKNOWN_SECTOR)
{
m_track_node = m_quad_graph->findOutOfRoadSector(m_kart->getXYZ());
}
// The client does not do any AI computations.
if(network_manager->getMode()==NetworkManager::NW_CLIENT)
{
Controller::update(dt);
return;
}
/*Get information that is needed by more than 1 of the handling funcs*/
//Detect if we are going to crash with the track and/or kart
int steps = 0;
steps = calcSteps();
findCurve();
/*Response handling functions*/
handleAcceleration(dt);
handleSteering(dt);
handleRescue(dt);
handleBraking();
/*And obviously general kart stuff*/
Controller::update(dt);
} // update
//-----------------------------------------------------------------------------
void EndController::handleBraking()
{
// In follow the leader mode, the kart should brake if they are ahead of
// the leader (and not the leader, i.e. don't have initial position 1)
if(race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER &&
m_kart->getPosition() < m_world->getKart(0)->getPosition() &&
m_kart->getInitialPosition()>1 )
{
m_controls->m_brake = true;
return;
}
const float MIN_SPEED = 5.0f;
//We may brake if we are about to get out of the road, but only if the
//kart is on top of the road, and if we won't slow down below a certain
//limit.
m_controls->m_brake = false;
} // handleBraking
//-----------------------------------------------------------------------------
void EndController::handleSteering(float dt)
{
const int next = m_next_node_index[m_track_node];
float steer_angle = 0.0f;
/*The AI responds based on the information we just gathered, using a
*finite state machine.
*/
//Reaction to being outside of the road
if( fabsf(m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() )) >
0.5f* m_quad_graph->getNode(m_track_node).getPathWidth()+0.5f )
{
steer_angle = steerToPoint(m_quad_graph->getQuad(next).getCenter(),
dt );
#ifdef AI_DEBUG
m_debug_sphere->setPosition(m_quad_graph->getQuad(next).getCenter().toIrrVector());
std::cout << "- Outside of road: steer to center point." <<
std::endl;
#endif
}
//If we are going to crash against a kart, avoid it if it doesn't
//drives the kart out of the road
else
{
m_start_kart_crash_direction = 0;
Vec3 straight_point;
findNonCrashingPoint(&straight_point);
#ifdef AI_DEBUG
m_debug_sphere->setPosition(straight_point.toIrrVector());
#endif
steer_angle = steerToPoint(straight_point, dt);
}
#ifdef AI_DEBUG
std::cout << "- Fallback." << std::endl;
#endif
setSteering(steer_angle, dt);
} // handleSteering
//-----------------------------------------------------------------------------
void EndController::handleAcceleration( const float DELTA )
{
//Do not accelerate until we have delayed the start enough
if( m_time_till_start > 0.0f )
{
m_time_till_start -= DELTA;
m_controls->m_accel = 0.0f;
return;
}
if( m_controls->m_brake == true )
{
m_controls->m_accel = 0.0f;
return;
}
if(m_kart->hasViewBlockedByPlunger())
{
if(!(m_kart->getSpeed() > m_kart->getMaxSpeedOnTerrain() / 2))
m_controls->m_accel = 0.05f;
else
m_controls->m_accel = 0.0f;
return;
}
m_controls->m_accel = 1.0f;
} // handleAcceleration
//-----------------------------------------------------------------------------
void EndController::handleRescue(const float DELTA)
{
// check if kart is stuck
if(m_kart->getSpeed()<2.0f && !m_kart->isRescue() && !m_world->isStartPhase())
{
m_time_since_stuck += DELTA;
if(m_time_since_stuck > 2.0f)
{
m_kart->forceRescue();
m_time_since_stuck=0.0f;
} // m_time_since_stuck > 2.0f
}
else
{
m_time_since_stuck = 0.0f;
}
} // handleRescue
//-----------------------------------------------------------------------------
float EndController::steerToAngle(const size_t SECTOR, const float ANGLE)
{
float angle = m_quad_graph->getAngleToNext(SECTOR,
m_successor_index[SECTOR]);
//Desired angle minus current angle equals how many angles to turn
float steer_angle = angle - m_kart->getHPR().getHeading();
if(m_kart->hasViewBlockedByPlunger())
steer_angle += ANGLE/5;
else
steer_angle += ANGLE;
steer_angle = normalizeAngle( steer_angle );
return steer_angle;
} // steerToAngle
//-----------------------------------------------------------------------------
/** Computes the steering angle to reach a certain point. Note that the
* steering angle depends on the velocity of the kart (simple setting the
* steering angle towards the angle the point has is not correct: a slower
* kart will obviously turn less in one time step than a faster kart).
* \param point Point to steer towards.
* \param dt Time step.
*/
float EndController::steerToPoint(const Vec3 &point, float dt)
{
// No sense steering if we are not driving.
if(m_kart->getSpeed()==0) return 0.0f;
const float dx = point.getX() - m_kart->getXYZ().getX();
const float dy = point.getY() - m_kart->getXYZ().getY();
/** Angle from the kart position to the point in world coordinates. */
float theta = -atan2(dx, dy);
// Angle is the point is relative to the heading - but take the current
// angular velocity into account, too. The value is multiplied by two
// to avoid 'oversteering' - experimentally found.
float angle_2_point = theta - m_kart->getHPR().getHeading()
- dt*m_kart->getBody()->getAngularVelocity().getZ()*2.0f;
angle_2_point = normalizeAngle(angle_2_point);
if(fabsf(angle_2_point)<0.1) return 0.0f;
/** To understand this code, consider how a given steering angle determines
* the angle the kart is facing after one timestep:
* sin(steer_angle) = wheel_base / radius; --> compute radius of turn
* circumference = radius * 2 * M_PI; --> circumference of turn circle
* The kart drives dt*V units during a timestep of size dt. So the ratio
* of the driven distance to the circumference is the same as the angle
* the whole circle, or:
* angle / (2*M_PI) = dt*V / circumference
* Reversly, if the angle to drive to is given, the circumference can be
* computed, and from that the turn radius, and then the steer angle.
* (note: the 2*M_PI can be removed from the computations)
*/
float radius = dt*m_kart->getSpeed()/angle_2_point;
float sin_steer_angle = m_kart->getKartProperties()->getWheelBase()/radius;
#ifdef DEBUG_OUTPUT
printf("theta %f a2p %f angularv %f radius %f ssa %f\n",
theta, angle_2_point, m_body->getAngularVelocity().getZ(),
radius, sin_steer_angle);
#endif
// Add 0.1 since rouding errors will otherwise result in the kart
// not using drifting.
if(sin_steer_angle <= -1.0f) return -m_kart->getMaxSteerAngle()*m_skidding_threshold-0.1f;
if(sin_steer_angle >= 1.0f) return m_kart->getMaxSteerAngle()*m_skidding_threshold+0.1f;
float steer_angle = asin(sin_steer_angle);
return steer_angle;
} // steerToPoint
//-----------------------------------------------------------------------------
/** Find the sector that at the longest distance from the kart, that can be
* driven to without crashing with the track, then find towards which of
* the two edges of the track is closest to the next curve after wards,
* and return the position of that edge.
*/
void EndController::findNonCrashingPoint(Vec3 *result)
{
unsigned int sector = m_next_node_index[m_track_node];
int target_sector;
Vec3 direction;
Vec3 step_track_coord;
float distance;
int steps;
//We exit from the function when we have found a solution
while( 1 )
{
//target_sector is the sector at the longest distance that we can drive
//to without crashing with the track.
target_sector = m_next_node_index[sector];
//direction is a vector from our kart to the sectors we are testing
direction = m_quad_graph->getQuad(target_sector).getCenter() - m_kart->getXYZ();
float len=direction.length_2d();
steps = int( len / m_kart_length );
if( steps < 3 ) steps = 3;
//Protection against having vel_normal with nan values
if(len>0.0f) {
direction*= 1.0f/len;
}
Vec3 step_coord;
//Test if we crash if we drive towards the target sector
for( int i = 2; i < steps; ++i )
{
step_coord = m_kart->getXYZ()+direction*m_kart_length * float(i);
m_quad_graph->spatialToTrack(&step_track_coord, step_coord,
sector );
distance = fabsf(step_track_coord[0]);
//If we are outside, the previous sector is what we are looking for
if ( distance + m_kart_width * 0.5f
> m_quad_graph->getNode(sector).getPathWidth() )
{
*result = m_quad_graph->getQuad(sector).getCenter();
return;
}
}
sector = target_sector;
}
} // findNonCrashingPoint
//-----------------------------------------------------------------------------
void EndController::reset()
{
m_start_kart_crash_direction = 0;
m_curve_target_speed = m_kart->getMaxSpeedOnTerrain();
m_curve_angle = 0.0;
m_time_till_start = -1.0f;
m_crash_time = 0.0f;
m_time_since_stuck = 0.0f;
Controller::reset();
m_track_node = QuadGraph::UNKNOWN_SECTOR;
m_quad_graph->findRoadSector(m_kart->getXYZ(), &m_track_node);
if(m_track_node==QuadGraph::UNKNOWN_SECTOR)
{
fprintf(stderr, "Invalid starting position for '%s' - not on track - can be ignored.\n",
m_kart->getIdent().c_str());
m_track_node = m_quad_graph->findOutOfRoadSector(m_kart->getXYZ());
}
} // reset
//-----------------------------------------------------------------------------
inline float EndController::normalizeAngle(float angle)
{
while( angle > 2*M_PI ) angle -= 2*M_PI;
while( angle < -2*M_PI ) angle += 2*M_PI;
if( angle > M_PI ) angle -= 2*M_PI;
else if( angle < -M_PI ) angle += 2*M_PI;
return angle;
}
//-----------------------------------------------------------------------------
/** calc_steps() divides the velocity vector by the lenght of the kart,
* and gets the number of steps to use for the sight line of the kart.
* The calling sequence guarantees that m_future_sector is not UNKNOWN.
*/
int EndController::calcSteps()
{
int steps = int( m_kart->getVelocityLC().getY() / m_kart_length );
if( steps < m_min_steps ) steps = m_min_steps;
//Increase the steps depending on the width, if we steering hard,
//mostly for curves.
#if 0
// FIXME: I don't understand this: if we are steering hard, we check
// for more steps if we hit another kart?? If we steer hard,
// the approximation used (pos + velocity*dt) will be even
// worse, since it doesn't take steering into account.
if( fabsf(m_controls->m_steer) > 0.95 )
{
const int WIDTH_STEPS =
(int)( m_quad_graph->getNode(m_future_sector).getPathWidth()
/( m_kart_length * 2.0 ) );
steps += WIDTH_STEPS;
}
#endif
return steps;
} // calcSteps
//-----------------------------------------------------------------------------
/** Converts the steering angle to a lr steering in the range of -1 to 1.
* If the steering angle is too great, it will also trigger skidding. This
* function uses a 'time till full steer' value specifying the time it takes
* for the wheel to reach full left/right steering similar to player karts
* when using a digital input device. This is done to remove shaking of
* AI karts (which happens when the kart frequently changes the direction
* of a turn). The parameter is defined in the kart properties.
* \param angle Steering angle.
* \param dt Time step.
*/
void EndController::setSteering(float angle, float dt)
{
float steer_fraction = angle / m_kart->getMaxSteerAngle();
m_controls->m_drift = fabsf(steer_fraction)>=m_skidding_threshold;
if(m_kart->hasViewBlockedByPlunger()) m_controls->m_drift = false;
float old_steer = m_controls->m_steer;
if (steer_fraction > 1.0f) steer_fraction = 1.0f;
else if(steer_fraction < -1.0f) steer_fraction = -1.0f;
if(m_kart->hasViewBlockedByPlunger())
{
if (steer_fraction > 0.5f) steer_fraction = 0.5f;
else if(steer_fraction < -0.5f) steer_fraction = -0.5f;
}
// The AI has its own 'time full steer' value (which is the time
float max_steer_change = dt/m_kart->getKartProperties()->getTimeFullSteerAI();
if(old_steer < steer_fraction)
{
m_controls->m_steer = (old_steer+max_steer_change > steer_fraction)
? steer_fraction : old_steer+max_steer_change;
}
else
{
m_controls->m_steer = (old_steer-max_steer_change < steer_fraction)
? steer_fraction : old_steer-max_steer_change;
}
} // setSteering
//-----------------------------------------------------------------------------
/**FindCurve() gathers info about the closest sectors ahead: the curve
* angle, the direction of the next turn, and the optimal speed at which the
* curve can be travelled at it's widest angle.
*
* The number of sectors that form the curve is dependant on the kart's speed.
*/
void EndController::findCurve()
{
float total_dist = 0.0f;
int i;
for(i = m_track_node; total_dist < m_kart->getVelocityLC().getY();
i = m_next_node_index[i])
{
total_dist += m_quad_graph->getDistanceToNext(i, m_successor_index[i]);
}
m_curve_angle =
normalizeAngle(m_quad_graph->getAngleToNext(i, m_successor_index[i])
-m_quad_graph->getAngleToNext(m_track_node,
m_successor_index[m_track_node]) );
m_curve_target_speed = m_kart->getMaxSpeedOnTerrain();
} // findCurve

View File

@@ -0,0 +1,138 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2004-2005 Steve Baker <sjbaker1@airmail.net>
// Copyright (C) 2006-2007 Eduardo Hernandez Munoz
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_END_KART_HPP
#define HEADER_END_KART_HPP
#include "karts/controller/controller.hpp"
#include "modes/profile_world.hpp"
#include "utils/vec3.hpp"
class Track;
class LinearWorld;
class QuadGraph;
namespace irr
{
namespace scene
{
class ISceneNode;
}
}
class EndController : public Controller
{
private:
int m_min_steps; //Minimum number of steps to check. If 0, the AI doesn't
//even has check around the kart, if 1, it checks around
//the kart always, and more than that will check the
//remaining number of steps in front of the kart, always
float m_max_handicap_accel; //The allowed maximum speed, in percentage,
//from 0.0 to 1.0. Used only when
//m_wait_for_players == true.
/*General purpose variables*/
//The crash percentage is how much of the time the AI has been crashing,
//if the AI has been crashing for some time, use the rescue.
float m_crash_time;
float m_time_till_start; //Used to simulate a delay at the start of the
//race, since human players don't accelerate
//at the same time and rarely repeat the a
//previous timing.
float m_curve_target_speed;
float m_curve_angle;
/** Keep a pointer to the track to reduce calls */
Track *m_track;
/** Keep a pointer to world. */
LinearWorld *m_world;
/** The current node the kart is on. This can be different from the value
* in LinearWorld, since it takes the chosen path of the AI into account
* (e.g. the closest point in LinearWorld might be on a branch not
* chosen by the AI). */
int m_track_node;
/** The graph of qudas of this track. */
const QuadGraph *m_quad_graph;
/** Which of the successors of a node was selected by the AI. */
std::vector<int> m_successor_index;
/** For each node in the graph this list contains the chosen next node.
* For normal lap track without branches we always have
* m_next_node_index[i] = (i+1) % size;
* but if a branch is possible, the AI will select one option here.
* If the node is not used, m_next_node_index will be -1. */
std::vector<int> m_next_node_index;
/** For each graph node this list contains a list of the next X
* graph nodes. */
std::vector<std::vector<int> > m_all_look_aheads;
float m_time_since_stuck;
int m_start_kart_crash_direction; //-1 = left, 1 = right, 0 = no crash.
/** Length of the kart, storing it here saves many function calls. */
float m_kart_length;
/** Cache width of kart. */
float m_kart_width;
/** For debugging purpose: a sphere indicating where the AI
* is targeting at. */
irr::scene::ISceneNode *m_debug_sphere;
/** The minimum steering angle at which the AI adds skidding. Lower values
* tend to improve the line the AI is driving. This is used to adjust for
* different AI levels.
*/
float m_skidding_threshold;
/*Functions called directly from update(). They all represent an action
*that can be done, and end up setting their respective m_controls
*variable, except handle_race_start() that isn't associated with any
*specific action (more like, associated with inaction).
*/
void handleAcceleration(const float DELTA);
void handleSteering(float dt);
void handleRescue(const float DELTA);
void handleBraking();
/*Lower level functions not called directly from update()*/
float steerToAngle(const size_t SECTOR, const float ANGLE);
float steerToPoint(const Vec3 &point, float dt);
void checkCrashes(const int STEPS, const Vec3& pos);
void findNonCrashingPoint(Vec3 *result);
float normalizeAngle(float angle);
int calcSteps();
void setSteering(float angle, float dt);
void findCurve();
public:
EndController(Kart *kart);
~EndController();
virtual void update (float delta) ;
virtual void reset ();
}; // EndKart
#endif
/* EOF */

View File

@@ -22,7 +22,7 @@
//to 2 in main.cpp with quickstart and run supertuxkart with the arg -N.
#undef AI_DEBUG
#include "robots/new_ai.hpp"
#include "karts/controller/new_ai_controller.hpp"
#ifdef AI_DEBUG
# include "irrlicht.h"
@@ -37,7 +37,6 @@
#ifdef AI_DEBUG
#include "graphics/irr_driver.hpp"
#endif
#include "karts/player_kart.hpp"
#include "modes/linear_world.hpp"
#include "network/network_manager.hpp"
#include "race/race_manager.hpp"
@@ -45,13 +44,11 @@
#include "tracks/track.hpp"
#include "utils/constants.hpp"
NewAI::NewAI(const std::string& kart_name,
int position, const btTransform& init_pos,
const Track *track) :
AutoKart( kart_name, position, init_pos )
NewAIController::NewAIController(Kart *kart) : Controller(kart)
{
m_kart_length = m_kart_properties->getKartModel()->getLength();
m_kart_width = m_kart_properties->getKartModel()->getWidth();
m_kart = kart;
m_kart_length = m_kart->getKartProperties()->getKartModel()->getLength();
m_kart_width = m_kart->getKartProperties()->getKartModel()->getWidth();
m_world = dynamic_cast<LinearWorld*>(World::getWorld());
m_track = m_world->getTrack();
m_quad_graph = &m_track->getQuadGraph();
@@ -155,35 +152,35 @@ NewAI::NewAI(const std::string& kart_name,
m_debug_left = irr_driver->getSceneManager()->addSphereSceneNode(1);
m_debug_right = irr_driver->getSceneManager()->addSphereSceneNode(1);
#endif
} // NewAI
} // NewAIController
//-----------------------------------------------------------------------------
/** The destructor deletes the shared TrackInfo objects if no more NewAI
/** The destructor deletes the shared TrackInfo objects if no more NewAIController
* instances are around.
*/
NewAI::~NewAI()
NewAIController::~NewAIController()
{
#ifdef AI_DEBUG
irr_driver->removeNode(m_debug_sphere);
irr_driver->removeNode(m_debug_left );
irr_driver->removeNode(m_debug_right );
#endif
} // ~NewAI
} // ~NewAIController
//-----------------------------------------------------------------------------
//TODO: if the AI is crashing constantly, make it move backwards in a straight
//line, then move forward while turning.
void NewAI::update(float dt)
void NewAIController::update(float dt)
{
// This is used to enable firing an item backwards.
m_controls.m_look_back = false;
m_controls.m_nitro = false;
m_controls->m_look_back = false;
m_controls->m_nitro = false;
// Update the current node:
if(m_track_node!=QuadGraph::UNKNOWN_SECTOR)
{
int old_node = m_track_node;
m_quad_graph->findRoadSector(getXYZ(), &m_track_node,
m_quad_graph->findRoadSector(m_kart->getXYZ(), &m_track_node,
&m_all_look_aheads[m_track_node]);
// IF the AI is off track (or on a branch of the track it did not
// select to be on), keep the old position.
@@ -193,19 +190,19 @@ void NewAI::update(float dt)
}
if(m_track_node==QuadGraph::UNKNOWN_SECTOR)
{
m_track_node = m_quad_graph->findOutOfRoadSector(getXYZ());
m_track_node = m_quad_graph->findOutOfRoadSector(m_kart->getXYZ());
}
// The client does not do any AI computations.
if(network_manager->getMode()==NetworkManager::NW_CLIENT)
{
AutoKart::update(dt);
Controller::update(dt);
return;
}
if( m_world->isStartPhase() )
{
handleRaceStart();
AutoKart::update(dt);
Controller::update(dt);
return;
}
@@ -216,18 +213,18 @@ void NewAI::update(float dt)
steps = calcSteps();
computeNearestKarts();
checkCrashes( steps, getXYZ() );
checkCrashes( steps, m_kart->getXYZ() );
findCurve();
// Special behaviour if we have a bomb attach: try to hit the kart ahead
// of us.
bool commands_set = false;
if(m_handle_bomb && getAttachment()->getType()==ATTACH_BOMB &&
if(m_handle_bomb && m_kart->getAttachment()->getType()==ATTACH_BOMB &&
m_kart_ahead )
{
// Use nitro if the kart is far ahead, or faster than this kart
m_controls.m_nitro = m_distance_ahead>10.0f ||
m_kart_ahead->getSpeed() > getSpeed();
m_controls->m_nitro = m_distance_ahead>10.0f ||
m_kart_ahead->getSpeed() > m_kart->getSpeed();
// If we are close enough, try to hit this kart
if(m_distance_ahead<=10)
{
@@ -235,10 +232,10 @@ void NewAI::update(float dt)
// If we are faster, try to predict the point where we will hit
// the other kart
if(m_kart_ahead->getSpeed() < getSpeed())
if(m_kart_ahead->getSpeed() < m_kart->getSpeed())
{
float time_till_hit = m_distance_ahead
/ (getSpeed()-m_kart_ahead->getSpeed());
/ (m_kart->getSpeed()-m_kart_ahead->getSpeed());
target += m_kart_ahead->getVelocity()*time_till_hit;
}
float steer_angle = steerToPoint(m_kart_ahead->getXYZ(),
@@ -257,13 +254,13 @@ void NewAI::update(float dt)
handleRescue(dt);
handleBraking();
// If a bomb is attached, nitro might already be set.
if(!m_controls.m_nitro)
if(!m_controls->m_nitro)
handleNitroAndZipper();
}
// If we are supposed to use nitro, but have a zipper,
// use the zipper instead
if(m_controls.m_nitro && m_powerup.getType()==POWERUP_ZIPPER &&
getSpeed()>1.0f && m_zipper_time_left<=0)
if(m_controls->m_nitro && m_kart->getPowerup()->getType()==POWERUP_ZIPPER &&
m_kart->getSpeed()>1.0f && m_kart->getZipperTimeLeft()<=0)
{
// Make sure that not all AI karts use the zipper at the same
// time in time trial at start up, so during the first 5 seconds
@@ -271,26 +268,26 @@ void NewAI::update(float dt)
if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_TIME_TRIAL ||
(m_world->getTime()<3.0f && rand()%50==1) )
{
m_controls.m_nitro = false;
m_controls.m_fire = true;
m_controls->m_nitro = false;
m_controls->m_fire = true;
}
}
/*And obviously general kart stuff*/
AutoKart::update(dt);
Controller::update(dt);
m_collided = false;
} // update
//-----------------------------------------------------------------------------
void NewAI::handleBraking()
void NewAIController::handleBraking()
{
// In follow the leader mode, the kart should brake if they are ahead of
// the leader (and not the leader, i.e. don't have initial position 1)
if(race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER &&
getPosition() < m_world->getKart(0)->getPosition() &&
getInitialPosition()>1 )
m_kart->getPosition() < m_world->getKart(0)->getPosition() &&
m_kart->getInitialPosition()>1 )
{
m_controls.m_brake = true;
m_controls->m_brake = true;
return;
}
@@ -298,13 +295,13 @@ void NewAI::handleBraking()
//We may brake if we are about to get out of the road, but only if the
//kart is on top of the road, and if we won't slow down below a certain
//limit.
if (m_crashes.m_road && getVelocityLC().getY() > MIN_SPEED &&
m_world->isOnRoad(getWorldKartId()) )
if (m_crashes.m_road && m_kart->getVelocityLC().getY() > MIN_SPEED &&
m_world->isOnRoad(m_kart->getWorldKartId()) )
{
float kart_ang_diff =
m_quad_graph->getAngleToNext(m_track_node,
m_successor_index[m_track_node])
- getHPR().getHeading();
- m_kart->getHPR().getHeading();
kart_ang_diff = normalizeAngle(kart_ang_diff);
kart_ang_diff = fabsf(kart_ang_diff);
@@ -318,21 +315,21 @@ void NewAI::handleBraking()
//if the curve angle is bigger than what the kart can steer, brake
//even if we are in the inside, because the kart would be 'thrown'
//out of the curve.
if(!(m_world->getDistanceToCenterForKart(getWorldKartId())
if(!(m_world->getDistanceToCenterForKart(m_kart->getWorldKartId())
> m_quad_graph->getNode(m_track_node).getPathWidth() *
-CURVE_INSIDE_PERC || m_curve_angle > RAD_TO_DEGREE*getMaxSteerAngle()))
-CURVE_INSIDE_PERC || m_curve_angle > RAD_TO_DEGREE*m_kart->getMaxSteerAngle()))
{
m_controls.m_brake = false;
m_controls->m_brake = false;
return;
}
}
else if( m_curve_angle < -MIN_TRACK_ANGLE ) //Next curve is right
{
if(!(m_world->getDistanceToCenterForKart( getWorldKartId() )
if(!(m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() )
< m_quad_graph->getNode(m_track_node).getPathWidth() *
CURVE_INSIDE_PERC || m_curve_angle < -RAD_TO_DEGREE*getMaxSteerAngle()))
CURVE_INSIDE_PERC || m_curve_angle < -RAD_TO_DEGREE*m_kart->getMaxSteerAngle()))
{
m_controls.m_brake = false;
m_controls->m_brake = false;
return;
}
}
@@ -340,23 +337,23 @@ void NewAI::handleBraking()
//Brake if the kart's speed is bigger than the speed we need
//to go through the curve at the widest angle, or if the kart
//is not going straight in relation to the road.
if(getVelocityLC().getY() > m_curve_target_speed ||
kart_ang_diff > MIN_TRACK_ANGLE )
if(m_kart->getVelocityLC().getY() > m_curve_target_speed ||
kart_ang_diff > MIN_TRACK_ANGLE )
{
#ifdef AI_DEBUG
std::cout << "BRAKING" << std::endl;
#endif
m_controls.m_brake = true;
m_controls->m_brake = true;
return;
}
}
m_controls.m_brake = false;
m_controls->m_brake = false;
} // handleBraking
//-----------------------------------------------------------------------------
void NewAI::handleSteering(float dt)
void NewAIController::handleSteering(float dt)
{
const int next = m_next_node_index[m_track_node];
@@ -366,7 +363,7 @@ void NewAI::handleSteering(float dt)
*finite state machine.
*/
//Reaction to being outside of the road
if( fabsf(m_world->getDistanceToCenterForKart( getWorldKartId() )) >
if( fabsf(m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() )) >
0.5f* m_quad_graph->getNode(m_track_node).getPathWidth()+1.0f )
{
steer_angle = steerToPoint(m_quad_graph->getQuad(next).getCenter(),
@@ -395,7 +392,7 @@ void NewAI::handleSteering(float dt)
}
else
{
if(m_world->getDistanceToCenterForKart( getWorldKartId() ) >
if(m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() ) >
m_world->getDistanceToCenterForKart( m_crashes.m_kart ))
{
steer_angle = steerToAngle(next, -M_PI*0.5f );
@@ -450,10 +447,10 @@ void NewAI::handleSteering(float dt)
} // handleSteering
//-----------------------------------------------------------------------------
void NewAI::handleItems( const float DELTA, const int STEPS )
void NewAIController::handleItems( const float DELTA, const int STEPS )
{
m_controls.m_fire = false;
if(isRescue() || m_powerup.getType() == POWERUP_NOTHING ) return;
m_controls->m_fire = false;
if(m_kart->isRescue() || m_kart->getPowerup()->getType() == POWERUP_NOTHING ) return;
m_time_since_last_shot += DELTA;
@@ -463,7 +460,7 @@ void NewAI::handleItems( const float DELTA, const int STEPS )
{
if( m_time_since_last_shot > 10.0f )
{
m_controls.m_fire = true;
m_controls->m_fire = true;
m_time_since_last_shot = 0.0f;
}
return;
@@ -471,7 +468,7 @@ void NewAI::handleItems( const float DELTA, const int STEPS )
// Tactic 2: calculate
// -------------------
switch( m_powerup.getType() )
switch(m_kart->getPowerup()->getType() )
{
case POWERUP_ZIPPER:
// Do nothing. Further up a zipper is used if nitro should be selected,
@@ -486,7 +483,7 @@ void NewAI::handleItems( const float DELTA, const int STEPS )
// kart as well? I.e. only drop if the kart behind is faster? Otoh
// this approach helps preventing an overtaken kart to overtake us
// again.
m_controls.m_fire = (m_distance_behind < 15.0f &&
m_controls->m_fire = (m_distance_behind < 15.0f &&
m_distance_behind > 3.0f ) ||
m_time_since_last_shot>10.0f;
if(m_distance_behind < 10.0f && m_distance_behind > 2.0f )
@@ -496,7 +493,7 @@ void NewAI::handleItems( const float DELTA, const int STEPS )
// towards m_kart_ahead. And some of them can fire backwards, too - which
// isn't yet supported for AI karts.
case POWERUP_CAKE:
m_controls.m_fire = (m_kart_ahead && m_distance_ahead < 20.0f) ||
m_controls->m_fire = (m_kart_ahead && m_distance_ahead < 20.0f) ||
m_time_since_last_shot > 10.0f;
break;
case POWERUP_BOWLING:
@@ -509,11 +506,11 @@ void NewAI::handleItems( const float DELTA, const int STEPS )
!m_kart_ahead;
float distance = fire_backwards ? m_distance_behind
: m_distance_ahead;
m_controls.m_fire = (fire_backwards && distance < 30.0f) ||
(!fire_backwards && distance <10.0f) ||
m_time_since_last_shot > 10.0f;
if(m_controls.m_fire)
m_controls.m_look_back = fire_backwards;
m_controls->m_fire = (fire_backwards && distance < 30.0f) ||
(!fire_backwards && distance <10.0f) ||
m_time_since_last_shot > 10.0f;
if(m_controls->m_fire)
m_controls->m_look_back = fire_backwards;
break;
}
case POWERUP_PLUNGER:
@@ -525,26 +522,26 @@ void NewAI::handleItems( const float DELTA, const int STEPS )
!m_kart_ahead;
float distance = fire_backwards ? m_distance_behind
: m_distance_ahead;
m_controls.m_fire = distance < 30.0f ||
m_time_since_last_shot > 10.0f;
if(m_controls.m_fire)
m_controls.m_look_back = fire_backwards;
m_controls->m_fire = distance < 30.0f ||
m_time_since_last_shot > 10.0f;
if(m_controls->m_fire)
m_controls->m_look_back = fire_backwards;
break;
}
case POWERUP_ANVIL:
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER)
{
m_controls.m_fire = m_world->getTime()<1.0f && getPosition()>2;
m_controls->m_fire = m_world->getTime()<1.0f && m_kart->getPosition()>2;
}
else
{
m_controls.m_fire = m_time_since_last_shot > 3.0f &&
getPosition()>1;
m_controls->m_fire = m_time_since_last_shot > 3.0f &&
m_kart->getPosition()>1;
}
default:
m_controls.m_fire = true;
m_controls->m_fire = true;
}
if(m_controls.m_fire) m_time_since_last_shot = 0.0f;
if(m_controls->m_fire) m_time_since_last_shot = 0.0f;
return;
} // handleItems
@@ -553,10 +550,10 @@ void NewAI::handleItems( const float DELTA, const int STEPS )
* '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 NewAI::computeNearestKarts()
void NewAIController::computeNearestKarts()
{
bool need_to_check = false;
int my_position = getPosition();
int my_position = m_kart->getPosition();
// See if the kart ahead has changed:
if( ( m_kart_ahead && m_kart_ahead->getPosition()+1!=my_position ) ||
(!m_kart_ahead && my_position>1 ) )
@@ -569,11 +566,11 @@ void NewAI::computeNearestKarts()
m_kart_behind = m_kart_ahead = NULL;
m_distance_ahead = m_distance_behind = 9999999.9f;
float my_dist = m_world->getDistanceDownTrackForKart(getWorldKartId());
float my_dist = m_world->getDistanceDownTrackForKart(m_kart->getWorldKartId());
for(unsigned int i=0; i<m_world->getNumKarts(); i++)
{
Kart *k = m_world->getKart(i);
if(k->isEliminated() || k==this) continue;
if(k->isEliminated() || k==m_kart) continue;
if(k->getPosition()==my_position+1)
{
m_kart_behind = k;
@@ -593,28 +590,28 @@ void NewAI::computeNearestKarts()
} // computeNearestKarts
//-----------------------------------------------------------------------------
void NewAI::handleAcceleration( const float DELTA )
void NewAIController::handleAcceleration( const float DELTA )
{
//Do not accelerate until we have delayed the start enough
if( m_time_till_start > 0.0f )
{
m_time_till_start -= DELTA;
m_controls.m_accel = 0.0f;
m_controls->m_accel = 0.0f;
return;
}
if( m_controls.m_brake == true )
if( m_controls->m_brake == true )
{
m_controls.m_accel = 0.0f;
m_controls->m_accel = 0.0f;
return;
}
if(hasViewBlockedByPlunger())
if(m_kart->hasViewBlockedByPlunger())
{
if(!(getSpeed() > getMaxSpeedOnTerrain() / 2))
m_controls.m_accel = 0.05f;
if(!(m_kart->getSpeed() > m_kart->getMaxSpeedOnTerrain() / 2))
m_controls->m_accel = 0.05f;
else
m_controls.m_accel = 0.0f;
m_controls->m_accel = 0.0f;
return;
}
@@ -623,7 +620,7 @@ void NewAI::handleAcceleration( const float DELTA )
//Find if any player is ahead of this kart
bool player_winning = false;
for(unsigned int i = 0; i < race_manager->getNumPlayers(); ++i )
if( getPosition() > m_world->getPlayerKart(i)->getPosition() )
if( m_kart->getPosition() > m_world->getPlayerKart(i)->getPosition() )
{
player_winning = true;
break;
@@ -631,16 +628,16 @@ void NewAI::handleAcceleration( const float DELTA )
if( player_winning )
{
m_controls.m_accel = m_max_handicap_accel;
m_controls->m_accel = m_max_handicap_accel;
return;
}
}
m_controls.m_accel = 1.0f;
m_controls->m_accel = 1.0f;
} // handleAcceleration
//-----------------------------------------------------------------------------
void NewAI::handleRaceStart()
void NewAIController::handleRaceStart()
{
//FIXME: make karts able to get a penalty for accelerating too soon
//like players, should happen to about 20% of the karts in easy,
@@ -656,15 +653,15 @@ void NewAI::handleRaceStart()
} // handleRaceStart
//-----------------------------------------------------------------------------
void NewAI::handleRescue(const float DELTA)
void NewAIController::handleRescue(const float DELTA)
{
// check if kart is stuck
if(getSpeed()<2.0f && !isRescue() && !m_world->isStartPhase())
if(m_kart->getSpeed()<2.0f && !m_kart->isRescue() && !m_world->isStartPhase())
{
m_time_since_stuck += DELTA;
if(m_time_since_stuck > 2.0f)
{
forceRescue();
m_kart->forceRescue();
m_time_since_stuck=0.0f;
} // m_time_since_stuck > 2.0f
}
@@ -677,36 +674,36 @@ void NewAI::handleRescue(const float DELTA)
//-----------------------------------------------------------------------------
/** Decides wether to use nitro or not.
*/
void NewAI::handleNitroAndZipper()
void NewAIController::handleNitroAndZipper()
{
m_controls.m_nitro = false;
m_controls->m_nitro = false;
// If we are already very fast, save nitro.
if(getSpeed() > 0.95f*getMaxSpeedOnTerrain())
if(m_kart->getSpeed() > 0.95f*m_kart->getMaxSpeedOnTerrain())
return;
// Don't use nitro when the AI has a plunger in the face!
if(hasViewBlockedByPlunger()) return;
if(m_kart->hasViewBlockedByPlunger()) return;
// Don't use nitro if the kart doesn't have any or is not on ground.
if(!isOnGround() || hasFinishedRace()) return;
if(!m_kart->isOnGround() || m_kart->hasFinishedRace()) return;
// Don't compute nitro usage if we don't have nitro or are not supposed
// to use it, and we don't have a zipper or are not supposed to use
// it (calculated).
if( (getEnergy()==0 || m_nitro_level==NITRO_NONE) &&
(m_powerup.getType()!=POWERUP_ZIPPER || m_item_tactic==IT_TEN_SECONDS) )
if( (m_kart->getEnergy()==0 || m_nitro_level==NITRO_NONE) &&
(m_kart->getPowerup()->getType()!=POWERUP_ZIPPER || m_item_tactic==IT_TEN_SECONDS) )
return;
// If a parachute or anvil is attached, the nitro doesn't give much
// benefit. Better wait till later.
const bool has_slowdown_attachment =
m_attachment.getType()==ATTACH_PARACHUTE ||
m_attachment.getType()==ATTACH_ANVIL;
m_kart->getAttachment()->getType()==ATTACH_PARACHUTE ||
m_kart->getAttachment()->getType()==ATTACH_ANVIL;
if(has_slowdown_attachment) return;
// If the kart is very slow (e.g. after rescue), use nitro
if(getSpeed()<5)
if(m_kart->getSpeed()<5)
{
m_controls.m_nitro = true;
m_controls->m_nitro = true;
return;
}
@@ -714,9 +711,9 @@ void NewAI::handleNitroAndZipper()
// (i.e. more than 2) nitro, use it.
// -------------------------------------------------
const unsigned int num_karts = m_world->getCurrentNumKarts();
if(getPosition()== (int)num_karts && getEnergy()>2.0f)
if(m_kart->getPosition()== (int)num_karts && m_kart->getEnergy()>2.0f)
{
m_controls.m_nitro = true;
m_controls->m_nitro = true;
return;
}
@@ -724,13 +721,13 @@ void NewAI::handleNitroAndZipper()
// anyway. Since the kart is faster with nitro, estimate a 50% time
// decrease (additionally some nitro will be saved when top speed
// is reached).
if(m_world->getLapForKart(getWorldKartId())==race_manager->getNumLaps()-1 &&
if(m_world->getLapForKart(m_kart->getWorldKartId())==race_manager->getNumLaps()-1 &&
m_nitro_level == NITRO_ALL)
{
float finish = m_world->getEstimatedFinishTime(getWorldKartId());
if( 1.5f*getEnergy() >= finish - m_world->getTime() )
float finish = m_world->getEstimatedFinishTime(m_kart->getWorldKartId());
if( 1.5f*m_kart->getEnergy() >= finish - m_world->getTime() )
{
m_controls.m_nitro = true;
m_controls->m_nitro = true;
return;
}
}
@@ -744,33 +741,33 @@ void NewAI::handleNitroAndZipper()
// --------------------------------------------------
if(m_kart_ahead &&
m_distance_ahead < overtake_distance &&
m_kart_ahead->getSpeed()+5.0f > getSpeed() )
m_kart_ahead->getSpeed()+5.0f > m_kart->getSpeed() )
{
m_controls.m_nitro = true;
m_controls->m_nitro = true;
return;
}
if(m_kart_behind &&
m_distance_behind < overtake_distance &&
m_kart_behind->getSpeed() > getSpeed() )
m_kart_behind->getSpeed() > m_kart->getSpeed() )
{
// Only prevent overtaking on highest level
m_controls.m_nitro = m_nitro_level==NITRO_ALL;
m_controls->m_nitro = m_nitro_level==NITRO_ALL;
return;
}
} // handleNitroAndZipper
//-----------------------------------------------------------------------------
float NewAI::steerToAngle(const size_t SECTOR, const float ANGLE)
float NewAIController::steerToAngle(const size_t SECTOR, const float ANGLE)
{
float angle = m_quad_graph->getAngleToNext(SECTOR,
m_successor_index[SECTOR]);
//Desired angle minus current angle equals how many angles to turn
float steer_angle = angle - getHPR().getHeading();
float steer_angle = angle - m_kart->getHPR().getHeading();
if(hasViewBlockedByPlunger())
if(m_kart->hasViewBlockedByPlunger())
steer_angle += ANGLE/5;
else
steer_angle += ANGLE;
@@ -787,20 +784,20 @@ float NewAI::steerToAngle(const size_t SECTOR, const float ANGLE)
* \param point Point to steer towards.
* \param dt Time step.
*/
float NewAI::steerToPoint(const Vec3 &point, float dt)
float NewAIController::steerToPoint(const Vec3 &point, float dt)
{
// No sense steering if we are not driving.
if(getSpeed()==0) return 0.0f;
const float dx = point.getX() - getXYZ().getX();
const float dy = point.getY() - getXYZ().getY();
if(m_kart->getSpeed()==0) return 0.0f;
const float dx = point.getX() - m_kart->getXYZ().getX();
const float dy = point.getY() - m_kart->getXYZ().getY();
/** Angle from the kart position to the point in world coordinates. */
float theta = -atan2(dx, dy);
// Angle is the point is relative to the heading - but take the current
// angular velocity into account, too. The value is multiplied by two
// to avoid 'oversteering' - experimentally found.
float angle_2_point = theta - getHPR().getHeading()
- dt*m_body->getAngularVelocity().getZ()*2.0f;
float angle_2_point = theta - m_kart->getHPR().getHeading()
- dt*m_kart->getBody()->getAngularVelocity().getZ()*2.0f;
angle_2_point = normalizeAngle(angle_2_point);
if(fabsf(angle_2_point)<0.1) return 0.0f;
@@ -816,8 +813,8 @@ float NewAI::steerToPoint(const Vec3 &point, float dt)
* computed, and from that the turn radius, and then the steer angle.
* (note: the 2*M_PI can be removed from the computations)
*/
float radius = dt*getSpeed()/angle_2_point;
float sin_steer_angle = m_kart_properties->getWheelBase()/radius;
float radius = dt*m_kart->getSpeed()/angle_2_point;
float sin_steer_angle = m_kart->getKartProperties()->getWheelBase()/radius;
#ifdef DEBUG_OUTPUT
printf("theta %f a2p %f angularv %f radius %f ssa %f\n",
theta, angle_2_point, m_body->getAngularVelocity().getZ(),
@@ -825,14 +822,14 @@ float NewAI::steerToPoint(const Vec3 &point, float dt)
#endif
// Add 0.1 since rouding errors will otherwise result in the kart
// not using drifting.
if(sin_steer_angle <= -1.0f) return -getMaxSteerAngle()*m_skidding_threshold-0.1f;
if(sin_steer_angle >= 1.0f) return getMaxSteerAngle()*m_skidding_threshold+0.1f;
if(sin_steer_angle <= -1.0f) return -m_kart->getMaxSteerAngle()*m_skidding_threshold-0.1f;
if(sin_steer_angle >= 1.0f) return m_kart->getMaxSteerAngle()*m_skidding_threshold+0.1f;
float steer_angle = asin(sin_steer_angle);
return steer_angle;
} // steerToPoint
//-----------------------------------------------------------------------------
void NewAI::checkCrashes( const int STEPS, const Vec3& pos )
void NewAIController::checkCrashes( const int STEPS, const Vec3& pos )
{
//Right now there are 2 kind of 'crashes': with other karts and another
//with the track. The sight line is used to find if the karts crash with
@@ -844,7 +841,7 @@ void NewAI::checkCrashes( const int STEPS, const Vec3& pos )
const size_t NUM_KARTS = m_world->getNumKarts();
//Protection against having vel_normal with nan values
const Vec3 &VEL = getVelocity();
const Vec3 &VEL = m_kart->getVelocity();
Vec3 vel_normal(VEL.getX(), VEL.getY(), 0.0);
float speed=vel_normal.length();
// If the velocity is zero, no sense in checking for crashes in time
@@ -867,10 +864,10 @@ void NewAI::checkCrashes( const int STEPS, const Vec3& pos )
for( unsigned int j = 0; j < NUM_KARTS; ++j )
{
const Kart* kart = m_world->getKart(j);
if(kart==this||kart->isEliminated()) continue; // ignore eliminated karts
if(kart==m_kart||kart->isEliminated()) continue; // ignore eliminated karts
const Kart *other_kart = m_world->getKart(j);
// Ignore karts ahead that are faster than this kart.
if(getVelocityLC().getY() < other_kart->getVelocityLC().getY())
if(m_kart->getVelocityLC().getY() < other_kart->getVelocityLC().getY())
continue;
Vec3 other_kart_xyz = other_kart->getXYZ() + other_kart->getVelocity()*(i*dt);
float kart_distance = (step_coord - other_kart_xyz).length_2d();
@@ -895,10 +892,10 @@ void NewAI::checkCrashes( const int STEPS, const Vec3& pos )
} // checkCrashes
//-----------------------------------------------------------------------------
float NewAI::findNonCrashingAngle()
float NewAIController::findNonCrashingAngle()
{
unsigned int current_sector = m_track_node;
const Vec3 &xyz = getXYZ();
const Vec3 &xyz = m_kart->getXYZ();
const Quad &q = m_quad_graph->getQuad(current_sector);
const Vec3 &right = q[2];
const Vec3 &left = q[3];
@@ -907,10 +904,10 @@ float NewAI::findNonCrashingAngle()
float very_right = -atan2(right.getX()-xyz.getX(),
right.getY()-xyz.getY())
- getHPR().getHeading();
- m_kart->getHPR().getHeading();
float very_left = -atan2(left.getX()-xyz.getX(),
left.getY()-xyz.getY())
- getHPR().getHeading();
- m_kart->getHPR().getHeading();
very_left = normalizeAngle(very_left);
very_right = normalizeAngle(very_right);
float dist = 0;
@@ -923,10 +920,10 @@ float NewAI::findNonCrashingAngle()
float angle_right = -atan2(right.getX()-xyz.getX(),
right.getY()-xyz.getY())
- getHPR().getHeading();
- m_kart->getHPR().getHeading();
float angle_left = -atan2(left.getX()-xyz.getX(),
left.getY()-xyz.getY())
- getHPR().getHeading();
- m_kart->getHPR().getHeading();
angle_left = normalizeAngle(angle_left);
angle_right = normalizeAngle(angle_right);
@@ -964,7 +961,7 @@ float NewAI::findNonCrashingAngle()
* the two edges of the track is closest to the next curve after wards,
* and return the position of that edge.
*/
void NewAI::findNonCrashingPoint(Vec3 *result)
void NewAIController::findNonCrashingPoint(Vec3 *result)
{
unsigned int sector = m_next_node_index[m_track_node];
int target_sector;
@@ -982,7 +979,7 @@ void NewAI::findNonCrashingPoint(Vec3 *result)
target_sector = m_next_node_index[sector];
//direction is a vector from our kart to the sectors we are testing
direction = m_quad_graph->getQuad(target_sector).getCenter() - getXYZ();
direction = m_quad_graph->getQuad(target_sector).getCenter() - m_kart->getXYZ();
float len=direction.length_2d();
steps = int( len / m_kart_length );
@@ -997,7 +994,7 @@ void NewAI::findNonCrashingPoint(Vec3 *result)
//Test if we crash if we drive towards the target sector
for( int i = 2; i < steps; ++i )
{
step_coord = getXYZ()+direction*m_kart_length * float(i);
step_coord = m_kart->getXYZ()+direction*m_kart_length * float(i);
m_quad_graph->spatialToTrack(&step_track_coord, step_coord,
sector );
@@ -1017,11 +1014,11 @@ void NewAI::findNonCrashingPoint(Vec3 *result)
} // findNonCrashingPoint
//-----------------------------------------------------------------------------
void NewAI::reset()
void NewAIController::reset()
{
m_time_since_last_shot = 0.0f;
m_start_kart_crash_direction = 0;
m_curve_target_speed = getMaxSpeedOnTerrain();
m_curve_target_speed = m_kart->getMaxSpeedOnTerrain();
m_curve_angle = 0.0;
m_time_till_start = -1.0f;
m_crash_time = 0.0f;
@@ -1032,19 +1029,19 @@ void NewAI::reset()
m_kart_behind = NULL;
m_distance_behind = 0.0f;
m_track_node = QuadGraph::UNKNOWN_SECTOR;
AutoKart::reset();
m_quad_graph->findRoadSector(getXYZ(), &m_track_node);
Controller::reset();
m_quad_graph->findRoadSector(m_kart->getXYZ(), &m_track_node);
if(m_track_node==QuadGraph::UNKNOWN_SECTOR)
{
fprintf(stderr, "Invalid starting position for '%s' - not on track - can be ignored.\n",
getIdent().c_str());
m_track_node = m_quad_graph->findOutOfRoadSector(getXYZ());
m_kart->getIdent().c_str());
m_track_node = m_quad_graph->findOutOfRoadSector(m_kart->getXYZ());
}
} // reset
//-----------------------------------------------------------------------------
inline float NewAI::normalizeAngle(float angle)
inline float NewAIController::normalizeAngle(float angle)
{
while( angle > 2*M_PI ) angle -= 2*M_PI;
while( angle < -2*M_PI ) angle += 2*M_PI;
@@ -1060,9 +1057,9 @@ inline float NewAI::normalizeAngle(float angle)
* and gets the number of steps to use for the sight line of the kart.
* The calling sequence guarantees that m_future_sector is not UNKNOWN.
*/
int NewAI::calcSteps()
int NewAIController::calcSteps()
{
int steps = int( getVelocityLC().getY() / m_kart_length );
int steps = int( m_kart->getVelocityLC().getY() / m_kart_length );
if( steps < m_min_steps ) steps = m_min_steps;
//Increase the steps depending on the width, if we steering hard,
@@ -1072,7 +1069,7 @@ int NewAI::calcSteps()
// for more steps if we hit another kart?? If we steer hard,
// the approximation used (pos + velocity*dt) will be even
// worse, since it doesn't take steering into account.
if( fabsf(m_controls.m_steer) > 0.95 )
if( fabsf(m_controls->m_steer) > 0.95 )
{
const int WIDTH_STEPS =
(int)( m_quad_graph->getNode(m_future_sector).getPathWidth()
@@ -1095,32 +1092,32 @@ int NewAI::calcSteps()
* \param angle Steering angle.
* \param dt Time step.
*/
void NewAI::setSteering(float angle, float dt)
void NewAIController::setSteering(float angle, float dt)
{
float steer_fraction = angle / getMaxSteerAngle();
m_controls.m_drift = fabsf(steer_fraction)>=m_skidding_threshold;
if(hasViewBlockedByPlunger()) m_controls.m_drift = false;
float old_steer = m_controls.m_steer;
float steer_fraction = angle / m_kart->getMaxSteerAngle();
m_controls->m_drift = fabsf(steer_fraction)>=m_skidding_threshold;
if(m_kart->hasViewBlockedByPlunger()) m_controls->m_drift = false;
float old_steer = m_controls->m_steer;
if (steer_fraction > 1.0f) steer_fraction = 1.0f;
else if(steer_fraction < -1.0f) steer_fraction = -1.0f;
if(hasViewBlockedByPlunger())
if(m_kart->hasViewBlockedByPlunger())
{
if (steer_fraction > 0.5f) steer_fraction = 0.5f;
else if(steer_fraction < -0.5f) steer_fraction = -0.5f;
}
// The AI has its own 'time full steer' value (which is the time
float max_steer_change = dt/m_kart_properties->getTimeFullSteerAI();
float max_steer_change = dt/m_kart->getKartProperties()->getTimeFullSteerAI();
if(old_steer < steer_fraction)
{
m_controls.m_steer = (old_steer+max_steer_change > steer_fraction)
m_controls->m_steer = (old_steer+max_steer_change > steer_fraction)
? steer_fraction : old_steer+max_steer_change;
}
else
{
m_controls.m_steer = (old_steer-max_steer_change < steer_fraction)
m_controls->m_steer = (old_steer-max_steer_change < steer_fraction)
? steer_fraction : old_steer-max_steer_change;
}
} // setSteering
@@ -1132,11 +1129,11 @@ void NewAI::setSteering(float angle, float dt)
*
* The number of sectors that form the curve is dependant on the kart's speed.
*/
void NewAI::findCurve()
void NewAIController::findCurve()
{
float total_dist = 0.0f;
int i;
for(i = m_track_node; total_dist < getVelocityLC().getY();
for(i = m_track_node; total_dist < m_kart->getVelocityLC().getY();
i = m_next_node_index[i])
{
total_dist += m_quad_graph->getDistanceToNext(i, m_successor_index[i]);
@@ -1148,5 +1145,5 @@ void NewAI::findCurve()
-m_quad_graph->getAngleToNext(m_track_node,
m_successor_index[m_track_node]) );
m_curve_target_speed = getMaxSpeedOnTerrain();
m_curve_target_speed = m_kart->getMaxSpeedOnTerrain();
} // findCurve

View File

@@ -16,10 +16,10 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_NEWAI_HPP
#define HEADER_NEWAI_HPP
#ifndef HEADER_NEWAI_CONTROLLER_HPP
#define HEADER_NEWAI_CONTROLLER_HPP
#include "karts/auto_kart.hpp"
#include "karts/controller/controller.hpp"
#include "utils/vec3.hpp"
/* third coord won't be used */
@@ -35,7 +35,7 @@ namespace irr
}
}
class NewAI : public AutoKart
class NewAIController : public Controller
{
private:
enum FallbackTactic
@@ -194,15 +194,14 @@ private:
void findCurve();
public:
NewAI(const std::string& kart_name, int position,
const btTransform& init_pos, const Track *track);
~NewAI();
void update (float delta) ;
void reset ();
NewAIController(Kart *kart);
virtual ~NewAIController();
virtual void update (float delta) ;
virtual void reset ();
virtual void crashed (Kart *k) {if(k) m_collided = true;};
virtual const irr::core::stringw& getName() const
virtual const irr::core::stringw& getN() const
{
static irr::core::stringw name = Kart::getName()+irr::core::stringw("(NewAI)");
static irr::core::stringw name("(NewAI)");
return name;
} // getName
};

View File

@@ -18,7 +18,7 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "karts/player_kart.hpp"
#include "karts/controller/player_controller.hpp"
#include "audio/sfx_base.hpp"
#include "audio/sfx_manager.hpp"
@@ -38,17 +38,17 @@
* \param position The starting position (1 to n).
* \param player The player to which this kart belongs.
* \param init_pos The start coordinates and heading of the kart.
* \param player_index Index of the player akrt.
* \param player_index Index of the player kart.
*/
PlayerKart::PlayerKart(const std::string& kart_name, int position,
ActivePlayer *player, const btTransform& init_pos,
unsigned int player_index) :
Kart(kart_name, position, init_pos)
PlayerController::PlayerController(Kart *kart, ActivePlayer *player,
unsigned int player_index)
: Controller(kart)
{
m_player = player;
m_player->setKart(kart);
m_penalty_time = 0.0f;
setCamera(new Camera(player_index, this));
m_camera->setMode(Camera::CM_NORMAL);
kart->setCamera(new Camera(player_index, kart));
kart->getCamera()->setMode(Camera::CM_NORMAL);
m_bzzt_sound = sfx_manager->newSFX(SFXManager::SOUND_BZZT );
m_wee_sound = sfx_manager->newSFX(SFXManager::SOUND_WEE );
@@ -57,25 +57,24 @@ PlayerKart::PlayerKart(const std::string& kart_name, int position,
m_full_sound = sfx_manager->newSFX(SFXManager::SOUND_FULL );
reset();
} // PlayerKart
} // PlayerController
//-----------------------------------------------------------------------------
/** Destructor for a player kart.
*/
PlayerKart::~PlayerKart()
PlayerController::~PlayerController()
{
delete m_camera;
sfx_manager->deleteSFX(m_bzzt_sound);
sfx_manager->deleteSFX(m_wee_sound );
sfx_manager->deleteSFX(m_ugh_sound );
sfx_manager->deleteSFX(m_grab_sound);
sfx_manager->deleteSFX(m_full_sound);
} // ~PlayerKart
} // ~PlayerController
//-----------------------------------------------------------------------------
/** Resets the player kart for a new or restarted race.
*/
void PlayerKart::reset()
void PlayerController::reset()
{
m_steer_val_l = 0;
m_steer_val_r = 0;
@@ -83,8 +82,7 @@ void PlayerKart::reset()
m_prev_brake = 0;
m_prev_accel = 0;
m_penalty_time = 0;
Kart::reset();
m_camera->reset();
Controller::reset();
} // reset
// ----------------------------------------------------------------------------
@@ -92,7 +90,7 @@ void PlayerKart::reset()
* avoid that any keys pressed at the time the menu is opened are still
* considered to be pressed.
*/
void PlayerKart::resetInputState()
void PlayerController::resetInputState()
{
m_steer_val_l = 0;
m_steer_val_r = 0;
@@ -114,7 +112,7 @@ void PlayerKart::resetInputState()
* and if it's 0 it indicates that the corresponding button
* was released.
*/
void PlayerKart::action(PlayerAction action, int value)
void PlayerController::action(PlayerAction action, int value)
{
switch (action)
{
@@ -138,42 +136,42 @@ void PlayerKart::action(PlayerAction action, int value)
m_prev_accel = value;
if(value)
{
m_controls.m_accel = value/32768.0f;
m_controls.m_brake = false;
m_controls->m_accel = value/32768.0f;
m_controls->m_brake = false;
}
else
{
m_controls.m_accel = 0.0f;
m_controls.m_brake = m_prev_brake;
m_controls->m_accel = 0.0f;
m_controls->m_brake = m_prev_brake;
}
break;
case PA_BRAKE:
m_prev_brake = value!=0;
if(value)
{
m_controls.m_brake = true;
m_controls.m_accel = 0.0f;
m_controls->m_brake = true;
m_controls->m_accel = 0.0f;
}
else
{
m_controls.m_brake = false;
m_controls.m_accel = m_prev_accel/32768.0f;
m_controls->m_brake = false;
m_controls->m_accel = m_prev_accel/32768.0f;
}
break;
case PA_NITRO:
m_controls.m_nitro = (value!=0);
m_controls->m_nitro = (value!=0);
break;
case PA_RESCUE:
m_controls.m_rescue = (value!=0);
m_controls->m_rescue = (value!=0);
break;
case PA_FIRE:
m_controls.m_fire = (value!=0);
m_controls->m_fire = (value!=0);
break;
case PA_LOOK_BACK:
m_controls.m_look_back = (value!=0);
m_controls->m_look_back = (value!=0);
break;
case PA_DRIFT:
m_controls.m_drift = (value!=0);
m_controls->m_drift = (value!=0);
break;
default: assert(false);
}
@@ -183,57 +181,56 @@ void PlayerKart::action(PlayerAction action, int value)
//-----------------------------------------------------------------------------
/** Handles steering for a player kart.
*/
void PlayerKart::steer(float dt, int steer_val)
void PlayerController::steer(float dt, int steer_val)
{
if(UserConfigParams::m_gamepad_debug)
{
printf("steering: steer_val %d ", steer_val);
}
const float STEER_CHANGE = dt/getTimeFullSteer(); // amount the steering is changed
const float STEER_CHANGE = dt/m_kart->getTimeFullSteer(); // amount the steering is changed
if (steer_val < 0)
{
// If we got analog values do not cumulate.
if (steer_val > -32767)
m_controls.m_steer = -steer_val/32767.0f;
m_controls->m_steer = -steer_val/32767.0f;
else
m_controls.m_steer += STEER_CHANGE;
m_controls->m_steer += STEER_CHANGE;
}
else if(steer_val > 0)
{
// If we got analog values do not cumulate.
if (steer_val < 32767)
m_controls.m_steer = -steer_val/32767.0f;
m_controls->m_steer = -steer_val/32767.0f;
else
m_controls.m_steer -= STEER_CHANGE;
m_controls->m_steer -= STEER_CHANGE;
}
else
{ // no key is pressed
if(m_controls.m_steer>0.0f)
if(m_controls->m_steer>0.0f)
{
m_controls.m_steer -= STEER_CHANGE;
if(m_controls.m_steer<0.0f) m_controls.m_steer=0.0f;
m_controls->m_steer -= STEER_CHANGE;
if(m_controls->m_steer<0.0f) m_controls->m_steer=0.0f;
}
else
{ // m_controls.m_steer<=0.0f;
m_controls.m_steer += STEER_CHANGE;
if(m_controls.m_steer>0.0f) m_controls.m_steer=0.0f;
} // if m_controls.m_steer<=0.0f
{ // m_controls->m_steer<=0.0f;
m_controls->m_steer += STEER_CHANGE;
if(m_controls->m_steer>0.0f) m_controls->m_steer=0.0f;
} // if m_controls->m_steer<=0.0f
} // no key is pressed
if(UserConfigParams::m_gamepad_debug)
{
printf(" set to: %f\n", m_controls.m_steer);
printf(" set to: %f\n", m_controls->m_steer);
}
m_controls.m_steer = std::min(1.0f, std::max(-1.0f, m_controls.m_steer));
m_controls->m_steer = std::min(1.0f, std::max(-1.0f, m_controls->m_steer));
} // steer
//-----------------------------------------------------------------------------
/** Updates the player kart, called once each timestep.
*/
void PlayerKart::update(float dt)
void PlayerController::update(float dt)
{
m_camera->update(dt);
// Don't do steering if it's replay. In position only replay it doesn't
// matter, but if it's physics replay the gradual steering causes
// incorrect results, since the stored values are already adjusted.
@@ -242,8 +239,8 @@ void PlayerKart::update(float dt)
if(World::getWorld()->isStartPhase())
{
if(m_controls.m_accel || m_controls.m_brake ||
m_controls.m_fire || m_controls.m_nitro || m_controls.m_drift)
if(m_controls->m_accel || m_controls->m_brake ||
m_controls->m_fire || m_controls->m_nitro || m_controls->m_drift)
{
if(m_penalty_time == 0.0)//eliminates machine-gun-effect for SOUND_BZZT
{
@@ -251,8 +248,7 @@ void PlayerKart::update(float dt)
RaceGUI* m=World::getWorld()->getRaceGUI();
if(m)
{
m->addMessage(_("Penalty time!!"),
this, 2.0f, 60);
m->addMessage(_("Penalty time!!"), m_kart, 2.0f, 60);
}
m_bzzt_sound->play();
} // if penalty_time = 0
@@ -262,7 +258,7 @@ void PlayerKart::update(float dt)
// The call to update is necessary here (even though the kart
// shouldn't actually change) to update m_transform. Otherwise
// the camera gets the wrong position.
Kart::update(dt);
Controller::update(dt);
}
return;
} // if isStartPhase
@@ -273,52 +269,45 @@ void PlayerKart::update(float dt)
return;
}
if ( m_controls.m_fire && !isRescue())
if ( m_controls->m_fire && !m_kart->isRescue())
{
if (m_powerup.getType()==POWERUP_NOTHING)
Kart::beep();
if (m_kart->getPowerup()->getType()==POWERUP_NOTHING)
m_kart->beep();
}
// Camera looks back when reversing
if(m_camera->getMode()!=Camera::CM_FINAL)
if(m_kart->getCamera()->getMode()!=Camera::CM_FINAL)
{
// look backward when the player requests or
// if automatic reverse camera is active
if (m_controls.m_look_back || (UserConfigParams::m_reverse_look_threshold>0 && Kart::getSpeed()<-UserConfigParams::m_reverse_look_threshold))
m_camera->setMode(Camera::CM_REVERSE);
if (m_controls->m_look_back || (UserConfigParams::m_reverse_look_threshold>0 &&
m_kart->getSpeed()<-UserConfigParams::m_reverse_look_threshold))
m_kart->getCamera()->setMode(Camera::CM_REVERSE);
else
m_camera->setMode(Camera::CM_NORMAL);
m_kart->getCamera()->setMode(Camera::CM_NORMAL);
}
// We can't restrict rescue to fulfil isOnGround() (which would be more like
// MK), since e.g. in the City track it is possible for the kart to end
// up sitting on a brick wall, with all wheels in the air :((
if ( m_controls.m_rescue )
if ( m_controls->m_rescue )
{
forceRescue();
m_controls.m_rescue=false;
m_kart->forceRescue();
m_controls->m_rescue=false;
}
if (isRescue() && m_attachment.getType() != ATTACH_TINYTUX)
if (m_kart->isRescue() && m_kart->getAttachment()->getType() != ATTACH_TINYTUX)
{
m_bzzt_sound->play();
}
Kart::update(dt);
Controller::update(dt);
} // update
//-----------------------------------------------------------------------------
/** Marks the kart as having had a crash.
*/
void PlayerKart::crashed(Kart *kart)
{
Kart::crashed(kart);
} // crashed
//-----------------------------------------------------------------------------
/** Checks if the kart was overtaken, and if so plays a sound
*/
void PlayerKart::setPosition(int p)
void PlayerController::setPosition(int p)
{
if(getPosition()<p)
if(m_kart->getPosition()<p)
{
World *world = World::getWorld();
//have the kart that did the passing beep.
@@ -333,36 +322,33 @@ void PlayerKart::setPosition(int p)
}
}
}
Kart::setPosition(p);
} // setPosition
//-----------------------------------------------------------------------------
/** Called when a kart finishes race.
* /param time Finishing time for this kart.
*/
void PlayerKart::raceFinished(float time)
void PlayerController::finishedRace(float time)
{
Kart::raceFinished(time);
// Set race over camera (but not in follow the leader mode, since the kart
// will most likely not be at the starting line at the end of the race
if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_FOLLOW_LEADER)
m_camera->setMode(Camera::CM_FINAL);
m_kart->getCamera()->setMode(Camera::CM_FINAL);
RaceGUI* m=World::getWorld()->getRaceGUI();
if(m)
{
m->addMessage(getPosition()==1 ? _("You won the race!")
: _("You finished the race!") ,
this, 2.0f, 60);
m->addMessage(m_kart->getPosition()==1 ? _("You won the race!")
: _("You finished the race!") ,
m_kart, 2.0f, 60);
}
} // raceFinished
} // finishedRace
//-----------------------------------------------------------------------------
/** Called when a kart hits or uses a zipper.
*/
void PlayerKart::handleZipper()
void PlayerController::handleZipper()
{
Kart::handleZipper();
m_wee_sound->play();
} // handleZipper
@@ -376,14 +362,10 @@ void PlayerKart::handleZipper()
* let the server determine the powerup/attachment for
* the clients.
*/
void PlayerKart::collectedItem(const Item &item, int add_info)
void PlayerController::collectedItem(const Item &item, int add_info, float old_energy)
{
// FIXME - how does the old item relate to the total amount of items?
const float old_energy= getEnergy();
Kart::collectedItem(item, add_info);
if(old_energy < MAX_ITEMS_COLLECTED &&
getEnergy() == MAX_ITEMS_COLLECTED)
m_kart->getEnergy() == MAX_ITEMS_COLLECTED)
{
m_full_sound->play();
}

View File

@@ -23,14 +23,14 @@
#define HEADER_PLAYERKART_HPP
#include "config/player.hpp"
#include "karts/kart.hpp"
#include "karts/controller/controller.hpp"
class SFXBase;
class Player;
/** PlayerKart manages control events from the player and moves
them to the Kart */
class PlayerKart : public Kart
class PlayerController : public Controller
{
private:
int m_steer_val, m_steer_val_l, m_steer_val_r;
@@ -48,22 +48,20 @@ private:
void steer(float, int);
public:
PlayerKart(const std::string& kart_name,
int position, ActivePlayer *_player,
const btTransform& init_pos,
unsigned int player_index);
~PlayerKart ();
PlayerController (Kart *kart, ActivePlayer *_player,
unsigned int player_index);
~PlayerController ();
ActivePlayer *getPlayer () { return m_player; }
PlayerProfile *getPlayerProfile () { return m_player->getProfile(); }
void update (float);
void action (PlayerAction action, int value);
void handleZipper ();
void collectedItem (const Item &item, int add_info=-1);
virtual void crashed (Kart *k);
void collectedItem (const Item &item, int add_info=-1,
float previous_energy=0);
virtual void setPosition (int p);
virtual void raceFinished (float time);
bool isPlayerKart () const {return true;}
void reset ();
virtual void finishedRace (float time);
bool isPlayerController() const {return true;}
virtual void reset ();
void resetInputState ();
};

View File

@@ -57,7 +57,7 @@
# pragma warning(disable:4355)
#endif
Kart::Kart (const std::string& kart_name, int position,
Kart::Kart (const std::string& ident, int position,
const btTransform& init_transform)
: TerrainInfo(1),
Moveable(), m_powerup(this), m_attachment(this)
@@ -66,11 +66,10 @@ Kart::Kart (const std::string& kart_name, int position,
# pragma warning(1:4355)
#endif
{
m_kart_properties = kart_properties_manager->getKart(kart_name);
m_kart_properties = kart_properties_manager->getKart(ident);
assert(m_kart_properties != NULL);
m_initial_position = position;
m_collected_energy = 0;
m_eliminated = false;
m_finished_race = false;
m_finish_time = 0.0f;
m_slipstream_time = 0.0f;
@@ -84,6 +83,8 @@ Kart::Kart (const std::string& kart_name, int position,
m_skidmarks = NULL;
m_animated_node = NULL;
m_camera = NULL;
m_controller = NULL;
m_saved_controller = NULL;
m_view_blocked_by_plunger = 0;
@@ -100,12 +101,12 @@ Kart::Kart (const std::string& kart_name, int position,
m_max_speed_reverse_ratio = m_kart_properties->getMaxSpeedReverseRatio();
m_speed = 0.0f;
// Setting rescue to false is important! If rescue is set when reset() is
// called, it is assumed that this was triggered by a restart, and that
// the vehicle must be added back to the physics world. Since reset() is
// also called at the very start, it must be guaranteed that rescue is
// not set.
m_rescue = false;
// Re-setting kart mode is important! If the mode should be rescue when
// reset() is called, it is assumed that this was triggered by a restart,
// and that the vehicle must be added back to the physics world. Since
// reset() is also called at the very start, it must be guaranteed that
// rescue is not set.
m_kart_mode = KM_RACE;
m_wheel_rotation = 0;
// Create SFXBase for each custom sound
@@ -142,6 +143,19 @@ Kart::Kart (const std::string& kart_name, int position,
reset();
} // Kart
// -----------------------------------------------------------------------------
/** Saves the old controller in m_saved_controller and stores a new
* controller. The save controller is needed in case of a reset.
* \param controller The new controller to use (atm it's always an
* end controller).
*/
void Kart::setController(Controller *controller)
{
assert(m_saved_controller==NULL);
m_saved_controller = m_controller;
m_controller = controller;
} // setController
// -----------------------------------------------------------------------------
btTransform Kart::getKartHeading(const float customPitch)
@@ -302,17 +316,19 @@ Kart::~Kart()
} // ~Kart
//-----------------------------------------------------------------------------
/** Eliminates a kart from the race. It removes the kart from the physics
* world, and makes the scene node invisible.
*/
void Kart::eliminate()
{
m_eliminated = true;
if (!m_rescue)
if (m_kart_mode!=KM_RESCUE)
{
World::getWorld()->getPhysics()->removeKart(this);
}
m_kart_mode = KM_ELIMINATED;
// make the kart invisible by placing it way under the track
Vec3 hell(0, 0, -10000.0f);
getNode()->setPosition(hell.toIrrVector());
getNode()->setVisible(false);
} // eliminate
//-----------------------------------------------------------------------------
@@ -350,23 +366,35 @@ void Kart::updatedWeight()
} // updatedWeight
//-----------------------------------------------------------------------------
/** Reset before a new race.
*/
void Kart::reset()
{
// If the kart was eliminated or rescued, the body was removed from the
// physics world. Add it again.
if(m_eliminated || m_rescue)
if(m_kart_mode==KM_ELIMINATED || m_kart_mode==KM_RESCUE)
{
World::getWorld()->getPhysics()->addKart(this);
}
if(m_node)
m_node->setVisible(true); // In case that the kart was eliminated
if(m_camera)
m_camera->reset();
// If the controller was replaced (e.g. replaced by end controller),
// restore the original controller.
if(m_saved_controller)
{
m_controller = m_saved_controller;
m_saved_controller = NULL;
}
m_view_blocked_by_plunger = 0.0;
m_attachment.clear();
m_powerup.reset();
m_race_position = 9;
m_finished_race = false;
m_eliminated = false;
m_rescue = false;
m_kart_mode = KM_RACE;
m_finish_time = 0.0f;
m_zipper_time_left = 0.0f;
m_collected_energy = 0;
@@ -417,16 +445,19 @@ void Kart::reset()
* player kart have finished the race and all AI karts get
* an estimated finish time set.
*/
void Kart::raceFinished(float time)
void Kart::finishedRace(float time)
{
m_finished_race = true;
m_finish_time = time;
m_kart_mode = KM_END_ANIM;
m_controller->finishedRace(time);
race_manager->RaceFinished(this, time);
} // raceFinished
} // finishedRace
//-----------------------------------------------------------------------------
void Kart::collectedItem(const Item &item, int add_info)
{
float old_energy = m_collected_energy;
const Item::ItemType type = item.getType();
switch (type)
@@ -467,6 +498,7 @@ void Kart::collectedItem(const Item &item, int add_info)
if ( m_collected_energy > MAX_ITEMS_COLLECTED )
m_collected_energy = MAX_ITEMS_COLLECTED;
m_controller->collectedItem(item, add_info, old_energy);
} // collectedItem
@@ -563,6 +595,9 @@ void Kart::handleExplosion(const Vec3& pos, bool direct_hit)
//-----------------------------------------------------------------------------
void Kart::update(float dt)
{
m_controller->update(dt);
if(m_camera)
m_camera->update(dt);
// if its view is blocked by plunger, decrease remaining time
if(m_view_blocked_by_plunger > 0) m_view_blocked_by_plunger -= dt;
@@ -598,7 +633,7 @@ void Kart::update(float dt)
m_wheel_rotation += m_speed*dt / m_kart_properties->getWheelRadius();
m_wheel_rotation=fmodf(m_wheel_rotation, 2*M_PI);
if ( m_rescue )
if ( m_kart_mode==KM_RESCUE )
{
// Let the kart raise 2m in the 2 seconds of the rescue
const float rescue_time = 2.0f;
@@ -618,7 +653,7 @@ void Kart::update(float dt)
-m_rescue_pitch*dt/rescue_time*M_PI/180.0f);
setXYZRotation(getXYZ()+Vec3(0, 0, rescue_height*dt/rescue_time),
getRotation()*q_roll*q_pitch);
} // if m_rescue
} // if rescue mode
m_attachment.update(dt);
//smoke drawing control point
@@ -723,12 +758,12 @@ void Kart::update(float dt)
// is rescued isOnGround might still be true, since the kart rigid
// body was removed from the physics, but still retain the old
// values for the raycasts).
if( (!isOnGround() || m_rescue) && m_shadow_enabled)
if( (!isOnGround() || m_kart_mode==KM_RESCUE) && m_shadow_enabled)
{
m_shadow_enabled = false;
m_shadow->disableShadow();
}
if(!m_shadow_enabled && isOnGround() && !m_rescue)
if(!m_shadow_enabled && isOnGround() && m_kart_mode!=KM_RESCUE)
{
m_shadow->enableShadow();
m_shadow_enabled = true;
@@ -753,6 +788,7 @@ void Kart::handleZipper()
m_vehicle->activateZipper(speed);
// Play custom character sound (weee!)
playCustomSFX(SFXManager::CUSTOM_ZIPPER);
m_controller->handleZipper();
} // handleZipper
//-----------------------------------------------------------------------------
@@ -890,9 +926,14 @@ void Kart::resetBrakes()
{
for(int i=0; i<4; i++) m_vehicle->setBrake(0.0f, i);
} // resetBrakes
// -----------------------------------------------------------------------------
/** Called when the kart crashes against the track (k=NULL) or another kart.
* \params k Either a kart if a kart was hit, or NULL if the track was hit.
*/
void Kart::crashed(Kart *k)
{
m_controller->crashed();
/** If a kart is crashing against the track, the collision is often
* reported more than once, resulting in a machine gun effect, and too
* long disabling of the engine. Therefore, this reaction is disabled
@@ -1009,7 +1050,7 @@ void Kart::updatePhysics (float dt)
// order to make them more competitive (this might be removed once
// the AI is better).
if(m_controls.m_drift &&
(race_manager->getDifficulty()==RaceManager::RD_EASY || isPlayerKart()) )
(race_manager->getDifficulty()==RaceManager::RD_EASY || m_controller->isPlayerController()) )
engine_power *= 0.5f;
m_vehicle->applyEngineForce(engine_power, 2);
m_vehicle->applyEngineForce(engine_power, 3);
@@ -1205,16 +1246,19 @@ void Kart::updatePhysics (float dt)
} // updatePhysics
//-----------------------------------------------------------------------------
/** Sets the mode of the kart to being rescued.
*/
void Kart::forceRescue()
{
m_rescue=true;
m_kart_mode=KM_RESCUE;
} // forceRescue
//-----------------------------------------------------------------------------
/** Drops a kart which was rescued back on the track.
*/
void Kart::endRescue()
{
m_rescue = false ;
m_kart_mode = KM_RACE;
World::getWorld()->getPhysics()->addKart(this);

View File

@@ -28,7 +28,8 @@
#include "items/powerup.hpp"
#include "karts/moveable.hpp"
#include "karts/kart_properties.hpp"
#include "karts/kart_control.hpp"
#include "karts/controller/controller.hpp"
#include "karts/controller/kart_control.hpp"
#include "karts/kart_model.hpp"
#include "tracks/terrain_info.hpp"
@@ -46,6 +47,14 @@ class btVehicleTuning;
class Quad;
class Stars;
/** The main kart class. All type of karts are of this object, but with
* different controllers. The controllers are what turn a kart into a
* player kart (i.e. the controller handle input), or an AI kart (the
* controller runs the AI code to set steering etc).
* Kart has two base classes: the most important one is moveable (which
* is an object that is moved on the track, and has position and rotations)
* and TerrainInfo, which manages the terrain the kart is on.
*/
class Kart : public TerrainInfo, public Moveable
{
private:
@@ -56,8 +65,20 @@ private:
/** Accumulated skidding factor. */
float m_skidding;
int m_initial_position; // initial position of kart
int m_race_position; // current race position (1-numKarts)
/** The main controller of this object, used for driving. This
* controller is used to run the kart. It will be replaced
* with an end kart controller when the kart finishes the race. */
Controller *m_controller;
/** This saves the original controller when the end controller is
* used. This is an easy solution for restarting the race, since
* the controller do not need to be reinitialised. */
Controller *m_saved_controller;
/** Initial rank of the kart. */
int m_initial_position;
/** Current race position (1-num_karts). */
int m_race_position;
/** The camera for each kart. Not all karts have cameras (e.g. AI karts
* usually don't), but there are exceptions: e.g. after the end of a
@@ -91,7 +112,8 @@ private:
btKart *m_vehicle;
btUprightConstraint *m_uprightConstraint;
/** The amount of energy collected by hitting coins. */
/** The amount of energy collected by hitting coins. Note that it
* must be float, since dt is subtraced in each timestep. */
float m_collected_energy;
// Graphical effects
@@ -153,8 +175,11 @@ private:
float m_view_blocked_by_plunger;
float m_speed;
float m_current_gear_ratio;
bool m_rescue;
bool m_eliminated;
/** Different kart modes: normal racing, being rescued, showing end
* animation, explosions, kart eliminated. */
enum {KM_RACE, KM_RESCUE, KM_END_ANIM, KM_EXPLOSION,
KM_ELIMINATED}
m_kart_mode;
std::vector<SFXBase*> m_custom_sounds;
SFXBase *m_beep_sound;
@@ -173,7 +198,7 @@ protected:
public:
Kart(const std::string& kart_name, int position,
Kart(const std::string& ident, int position,
const btTransform& init_transform);
virtual ~Kart();
unsigned int getWorldKartId() const { return m_world_kart_id; }
@@ -199,18 +224,22 @@ public:
void setPowerup (PowerupType t, int n)
{
m_powerup.set(t, n);
}
} // setPowerup
// ------------------------------------------------------------------------
/** Sets the position in race this kart has (1<=p<=n). */
virtual void setPosition(int p)
{
m_race_position = p;
}
m_controller->setPosition(p);
} // setPosition
// ------------------------------------------------------------------------
Attachment *getAttachment()
{
return &m_attachment;
}
} // getAttachment
// ------------------------------------------------------------------------
void setAttachmentType(attachmentType t, float time_left=0.0f, Kart*k=NULL)
{
@@ -218,25 +247,30 @@ public:
}
// ------------------------------------------------------------------------
/** Returns the camera of this kart (or NULL if no camera is attached
* to this kart. */
Camera* getCamera () {return m_camera;}
* to this kart). */
Camera* getCamera () {return m_camera;}
/** Returns the camera of this kart (or NULL if no camera is attached
* to this kart) - const version. */
const Camera* getCamera () const {return m_camera;}
/** Sets the camera for this kart. */
void setCamera(Camera *camera) {m_camera=camera; }
/** Sets viewport etc. for the camera of this kart. */
void activateCamera () {m_camera->activate(); }
/** Returns the viewport of the camera of this kart. */
const core::recti& getViewport() const {return m_camera->getViewport(); }
/** Returns the scaling in x/y direction for the camera of this kart. */
const core::vector2df& getScaling() const {return m_camera->getScaling(); }
const Powerup *getPowerup () const { return &m_powerup; }
Powerup *getPowerup () { return &m_powerup; }
int getNumPowerup () const { return m_powerup.getNum();}
float getEnergy () const { return m_collected_energy;}
int getPosition () const { return m_race_position; }
int getInitialPosition () const { return m_initial_position; }
float getFinishTime () const { return m_finish_time; }
bool hasFinishedRace () const { return m_finished_race; }
/** Returns the number of powerups. */
int getNumPowerup () const { return m_powerup.getNum(); }
/** Returns the time left for a zipper. */
float getZipperTimeLeft () const { return m_zipper_time_left; }
/** Returns the remaining collected energy. */
float getEnergy () const { return m_collected_energy; }
/** Returns the current position of this kart in the race. */
int getPosition () const { return m_race_position; }
/** Returns the initial position of this kart. */
int getInitialPosition () const { return m_initial_position; }
/** Returns the finished time for a kart. */
float getFinishTime () const { return m_finish_time; }
/** Returns true if this kart has finished the race. */
bool hasFinishedRace () const { return m_finished_race; }
void endRescue ();
void getClosestKart (float *cdist, int *closest);
@@ -270,6 +304,8 @@ public:
const Vec3& getGravityCenterShift () const
{return m_kart_properties->getGravityCenterShift(); }
float getSteerPercent () const {return m_controls.m_steer; }
KartControl&
getControls () {return m_controls; }
const KartControl&
getControls () const {return m_controls; }
/** Sets the kart controls. Used e.g. by replaying history. */
@@ -303,20 +339,24 @@ public:
void setSuspensionLength();
float handleNitro (float dt);
float getActualWheelForce();
/** True if the wheels are touching the ground. */
bool isOnGround () const;
/** Returns true if the kart is close to the ground, used to dis/enable
* the upright constraint to allow for more realistic explosions. */
bool isNearGround () const;
bool isEliminated () const {return m_eliminated;}
/** Returns true if the kart is eliminated. */
bool isEliminated () const {return m_kart_mode==KM_ELIMINATED;}
/** Returns true if the kart is being rescued. */
bool isRescue () const {return m_kart_mode==KM_RESCUE;}
void eliminate ();
bool isRescue () const {return m_rescue;}
void resetBrakes ();
void adjustSpeed (float f);
void updatedWeight ();
void forceRescue ();
void handleExplosion (const Vec3& pos, bool direct_hit);
/** Returns a name to be displayed for this kart. */
virtual const irr::core::stringw& getName() const {return m_kart_properties->getName();}
const std::string& getIdent () const {return m_kart_properties->getIdent();}
virtual bool isPlayerKart () const {return false; }
virtual bool isNetworkKart () const { return true; }
// addMessages gets called by world to add messages to the gui
virtual void addMessages () {};
virtual void collectedItem (const Item &item, int random_attachment);
@@ -325,9 +365,16 @@ public:
virtual void crashed (Kart *k);
virtual void update (float dt);
virtual void raceFinished (float time);
virtual void finishedRace (float time);
void beep ();
bool playCustomSFX (unsigned int type);
/** Returns the start transform, i.e. position and rotation. */
const btTransform getResetTransform() const {return m_reset_transform;}
/** Returns the controller of this kart. */
Controller* getController() { return m_controller; }
/** Returns the controller of this kart (const version). */
const Controller* getController() const { return m_controller; }
void setController(Controller *controller);
};

View File

@@ -24,7 +24,6 @@
#include "graphics/irr_driver.hpp"
#include "graphics/material.hpp"
#include "graphics/material_manager.hpp"
#include "karts/player_kart.hpp"
#include "utils/coord.hpp"
Moveable::Moveable()

View File

@@ -89,13 +89,13 @@ void LinearWorld::restartRace()
World::restartRace();
const unsigned int kart_amount = m_karts.size();
for(unsigned int n=0; n<kart_amount; n++)
for(unsigned int i=0; i<kart_amount; i++)
{
KartInfo& info = m_kart_info[n];
KartInfo& info = m_kart_info[i];
info.m_track_sector = QuadGraph::UNKNOWN_SECTOR;
info.m_last_valid_sector = QuadGraph::UNKNOWN_SECTOR;
info.m_lap_start_time = 0;
m_track->getQuadGraph().findRoadSector(m_karts[n]->getXYZ(),
m_track->getQuadGraph().findRoadSector(m_karts[i]->getXYZ(),
&info.m_track_sector);
//If m_track_sector == UNKNOWN_SECTOR, then the kart is not on top of
@@ -104,19 +104,23 @@ void LinearWorld::restartRace()
if (!info.m_on_road)
{
info.m_track_sector =
m_track->getQuadGraph().findOutOfRoadSector(m_karts[n]->getXYZ(),
m_track->getQuadGraph().findOutOfRoadSector(m_karts[i]->getXYZ(),
QuadGraph::UNKNOWN_SECTOR );
}
m_track->getQuadGraph().spatialToTrack(&info.m_curr_track_coords,
m_karts[n]->getXYZ(),
m_karts[i]->getXYZ(),
info.m_track_sector );
info.m_race_lap = -1;
info.m_lap_start_time = -0;
info.m_time_at_last_lap = 99999.9f;
updateRacePosition(m_karts[n], info);
} // next kart
// First all kart infos must be updated before the kart position can be
// recomputed, since otherwise 'new' (initialised) valued will be compared
// with old values.
for(unsigned int i=0; i<kart_amount; i++)
updateRacePosition(m_karts[i], m_kart_info[i]);
} // restartRace
//-----------------------------------------------------------------------------
@@ -236,7 +240,10 @@ void LinearWorld::newLap(unsigned int kart_index)
// receive a message from the server. So a client does not do
// anything here.
if(network_manager->getMode()!=NetworkManager::NW_CLIENT)
kart->raceFinished(getTime());
{
kart->finishedRace(getTime());
createEndKart(kart_index);
}
}
{
float time_per_lap;
@@ -416,7 +423,8 @@ void LinearWorld::terminateRace()
if(!m_karts[i]->hasFinishedRace() && !m_karts[i]->isEliminated())
{
const float est_finish_time = m_kart_info[i].m_estimated_finish;
m_karts[i]->raceFinished(est_finish_time);
m_karts[i]->finishedRace(est_finish_time);
createEndKart(i);
} // if !hasFinishedRace
} // for i
} // terminateRace
@@ -563,7 +571,6 @@ void LinearWorld::updateRacePosition ( Kart* kart, KartInfo& kart_info )
p++;
}
} //next kart
kart->setPosition(p);
// Switch on faster music if not already done so, if the
// first kart is doing its last lap, and if the estimated
@@ -587,7 +594,7 @@ void LinearWorld::updateRacePosition ( Kart* kart, KartInfo& kart_info )
*/
void LinearWorld::checkForWrongDirection(unsigned int i)
{
if(!m_karts[i]->isPlayerKart()) return;
if(!m_karts[i]->getController()->isPlayerController()) return;
if(!m_kart_info[i].m_on_road) return;
// FIXME: Don't do this if the in-game option menu is on the screen!

View File

@@ -21,8 +21,6 @@
#include "graphics/camera.hpp"
#include "graphics/irr_driver.hpp"
#include "robots/default_robot.hpp"
ProfileWorld::ProfileType ProfileWorld::m_profile_mode=PROFILE_NONE;
int ProfileWorld::m_num_laps = 0;
@@ -83,17 +81,15 @@ Kart *ProfileWorld::createKart(const std::string &kart_ident, int index,
{
// Create a camera for the last kart (since this way more of the
// karts can be seen.
Kart *newkart = loadRobot(kart_ident, index+1, init_pos);
Kart *new_kart = new Kart(kart_ident, index+1, init_pos);
Controller *controller = loadAIController(new_kart);
new_kart->setController(controller);
if (index == (int)getNumKarts()-1)
{
// The pointer to the camera does not have to be stored, since it
// the camera for robots is not modified.
// FIXME: this is broken now, where do we store the camera in case
// of profile mode???
new Camera(index, newkart);
new_kart->setCamera(new Camera(index, new_kart));
}
return newkart;
return new_kart;
} // createKart
//-----------------------------------------------------------------------------

View File

@@ -57,7 +57,10 @@ void StandardRace::update(float dt)
for(unsigned int i = 0; i < kart_amount ; i++)
{
if(!m_karts[i]->hasFinishedRace())
m_karts[i]->raceFinished(estimateFinishTimeForKart(m_karts[i]));
{
m_karts[i]->finishedRace(estimateFinishTimeForKart(m_karts[i]));
createEndKart(i);
}
} // i<kart_amount
// Set delay mode to have time for camera animation, and

View File

@@ -71,7 +71,8 @@ void ThreeStrikesBattle::terminateRace()
{
if(!m_karts[i]->hasFinishedRace())
{
m_karts[i]->raceFinished(WorldStatus::getTime());
m_karts[i]->finishedRace(WorldStatus::getTime());
createEndKart(i);
} // if !hasFinishedRace
} // for i
@@ -92,8 +93,10 @@ void ThreeStrikesBattle::kartHit(const int kart_id)
// check if kart is 'dead'
if(m_kart_info[kart_id].m_lives < 1)
{
m_karts[kart_id]->raceFinished(WorldStatus::getTime());
m_karts[kart_id]->finishedRace(WorldStatus::getTime());
removeKart(kart_id);
// FIXME - what about end camera here??
// createEndCamera(kart_id)
}
const unsigned int NUM_KARTS = getNumKarts();

View File

@@ -36,7 +36,10 @@
#include "io/file_manager.hpp"
#include "items/projectile_manager.hpp"
#include "karts/auto_kart.hpp"
#include "karts/player_kart.hpp"
#include "karts/controller/default_ai_controller.hpp"
#include "karts/controller/new_ai_controller.hpp"
#include "karts/controller/player_controller.hpp"
#include "karts/controller/end_controller.hpp"
#include "karts/kart_properties_manager.hpp"
#include "network/network_manager.hpp"
#include "network/race_state.hpp"
@@ -44,8 +47,6 @@
#include "race/highscore_manager.hpp"
#include "race/history.hpp"
#include "race/race_manager.hpp"
#include "robots/default_robot.hpp"
#include "robots/new_ai.hpp"
#include "tracks/track.hpp"
#include "tracks/track_manager.hpp"
#include "utils/constants.hpp"
@@ -151,42 +152,44 @@ Kart *World::createKart(const std::string &kart_ident, int index,
int local_player_id, int global_player_id,
const btTransform &init_pos)
{
Kart *newkart = NULL;
int position = index+1;
int position = index+1;
Kart *new_kart = new Kart(kart_ident, position, init_pos);
Controller *controller = NULL;
switch(race_manager->getKartType(index))
{
case RaceManager::KT_PLAYER:
std::cout << "===== World : creating player kart for kart #" << index << " which has local_player_id " << local_player_id << " ===========\n";
newkart = new PlayerKart(kart_ident, position,
StateManager::get()->getActivePlayer(local_player_id),
init_pos, local_player_id);
controller = new PlayerController(new_kart,
StateManager::get()->getActivePlayer(local_player_id),
local_player_id);
m_num_players ++;
break;
case RaceManager::KT_NETWORK_PLAYER:
newkart = new NetworkKart(kart_ident, position, init_pos,
global_player_id);
m_num_players++;
break;
//case RaceManager::KT_NETWORK_PLAYER:
//controller = new NetworkController(kart_ident, position, init_pos,
// global_player_id);
//m_num_players++;
//break;
case RaceManager::KT_AI:
std::cout << "===== World : creating AI kart for #" << index << "===========\n";
newkart = loadRobot(kart_ident, position, init_pos);
controller = loadAIController(new_kart);
break;
case RaceManager::KT_GHOST:
break;
case RaceManager::KT_LEADER:
break;
}
return newkart;
new_kart->setController(controller);
return new_kart;
} // createKart
//-----------------------------------------------------------------------------
Kart* World::loadRobot(const std::string& kart_name, int position,
const btTransform& init_pos)
/** Creates an AI controller for the kart.
* \param kart The kart to be controlled by an AI.
*/
Controller* World::loadAIController(Kart *kart)
{
Kart* currentRobot;
Controller *controller;
// const int NUM_ROBOTS = 1;
// For now: instead of random switching, use each
// robot in turns: switch(m_random.get(NUM_ROBOTS))
@@ -198,19 +201,19 @@ Kart* World::loadRobot(const std::string& kart_name, int position,
switch(turn)
{
case 0:
currentRobot = new DefaultRobot(kart_name, position, init_pos, m_track);
controller = new DefaultAIController(kart);
break;
case 1:
currentRobot = new NewAI(kart_name, position, init_pos, m_track);
controller = new NewAIController(kart);
break;
default:
std::cerr << "Warning: Unknown robot, using default." << std::endl;
currentRobot = new DefaultRobot(kart_name, position, init_pos, m_track);
controller = new DefaultAIController(kart);
break;
}
return currentRobot;
} // loadRobot
return controller;
} // loadAIController
//-----------------------------------------------------------------------------
World::~World()
@@ -440,17 +443,18 @@ void World::updateHighscores()
}
// Only record times for player karts and only if they finished the race
if(!m_karts[index[pos]]->isPlayerKart()) continue;
if(!m_karts[index[pos]]->getController()->isPlayerController()) continue;
if (!m_karts[index[pos]]->hasFinishedRace()) continue;
assert(index[pos] >= 0);
assert(index[pos] < m_karts.size());
PlayerKart *k = (PlayerKart*)m_karts[index[pos]];
Kart *k = (Kart*)m_karts[index[pos]];
HighscoreEntry* highscores = getHighscores();
PlayerController *controller = (PlayerController*)(k->getController());
if(highscores->addData(k->getIdent(),
k->getPlayer()->getProfile()->getName(),
controller->getPlayer()->getProfile()->getName(),
k->getFinishTime())>0 )
{
highscore_manager->Save();
@@ -465,15 +469,15 @@ void World::updateHighscores()
* so it shouldn't be called inside of loops.
* \param n Index of player kart to return.
*/
PlayerKart *World::getPlayerKart(unsigned int n) const
Kart *World::getPlayerKart(unsigned int n) const
{
unsigned int count=-1;
for(unsigned int i=0; i<m_karts.size(); i++)
if(m_karts[i]->isPlayerKart())
if(m_karts[i]->getController()->isPlayerController())
{
count++;
if(count==n) return (PlayerKart*)m_karts[i];
if(count==n) return m_karts[i];
}
return NULL;
} // getPlayerKart
@@ -482,15 +486,15 @@ PlayerKart *World::getPlayerKart(unsigned int n) const
/** Returns the nth local player kart, i.e. a player kart that has a camera.
* \param n Index of player kart to return.
*/
PlayerKart *World::getLocalPlayerKart(unsigned int n) const
Kart *World::getLocalPlayerKart(unsigned int n) const
{
unsigned int count=-1;
for(unsigned int i=0; i<m_karts.size(); i++)
{
if(m_karts[i]->getCamera() && m_karts[i]->isPlayerKart())
if(m_karts[i]->getCamera() && m_karts[i]->getController()->isPlayerController())
{
count++;
if(count==n) return (PlayerKart*)m_karts[i];
if(count==n) return m_karts[i];
}
}
return NULL;
@@ -518,11 +522,11 @@ void World::removeKart(int kart_number)
*i, 2.0f, 60);
}
} // for i in kart
if(kart->isPlayerKart())
if(kart->getController()->isPlayerController())
{
// Change the camera so that it will be attached to the leader
// and facing backwards.
Camera* camera=((PlayerKart*)kart)->getCamera();
Camera* camera=kart->getCamera();
camera->setMode(Camera::CM_LEADER_MODE);
m_eliminated_players++;
}
@@ -580,13 +584,23 @@ void World::pause()
} // pause
//-----------------------------------------------------------------------------
void World::unpause()
void World::unpause()
{
sound_manager->resumeMusic() ;
sfx_manager->resumeAll();
WorldStatus::unpause();
for(unsigned int i=0; i<m_karts.size(); i++)
if(m_karts[i]->isPlayerKart()) ((PlayerKart*)m_karts[i])->resetInputState();
if(m_karts[i]->getController()->isPlayerController())
((PlayerController*)(m_karts[i]->getController()))->resetInputState();
} // pause
//-----------------------------------------------------------------------------
/** Replaces the kart with index i with an EndKart, i.e. a kart that shows the
* end animation, and does not use any items anymore.
* \param i Index of the kart to be replaced.
*/
void World::createEndKart(unsigned int i)
{
m_karts[i]->setController(new EndController(m_karts[i]));
} // createEndKart
/* EOF */

View File

@@ -31,7 +31,6 @@
class btRigidBody;
class Kart;
class PlayerKart;
class SFXBase;
class Track;
@@ -109,8 +108,8 @@ protected:
void updateHighscores ();
void resetAllKarts ();
void removeKart (int kart_number);
Kart* loadRobot (const std::string& kart_name, int position,
const btTransform& init_pos);
Controller*
loadAIController (Kart *kart);
void estimateFinishTimes();
virtual Kart *createKart(const std::string &kart_ident, int index,
@@ -147,8 +146,8 @@ public:
void disableRace(); // Put race into limbo phase
/** Returns a pointer to the race gui. */
RaceGUI *getRaceGUI() const { return m_race_gui; }
PlayerKart *getPlayerKart(unsigned int player) const;
PlayerKart *getLocalPlayerKart(unsigned int n) const;
Kart *getPlayerKart(unsigned int player) const;
Kart *getLocalPlayerKart(unsigned int n) const;
unsigned int getNumKarts() const { return m_karts.size(); }
Kart *getKart(int kartId) const { assert(kartId >= 0 &&
kartId < int(m_karts.size()));
@@ -226,7 +225,7 @@ public:
/** Called by the race result GUI at the end of the race to know the final order
(fill in the 'order' array) */
virtual void raceResultOrder( int* order ) = 0;
void createEndKart(unsigned int i);
};
#endif

View File

@@ -19,7 +19,6 @@
#include "network/kart_control_message.hpp"
#include "karts/player_kart.hpp"
#include "modes/world.hpp"
#include "network/network_kart.hpp"
@@ -53,7 +52,7 @@ KartControlMessage::KartControlMessage(ENetPacket* pkt, int kart_id_offset,
{
KartControl kc(this);
Kart *kart = World::getWorld()->getKart(i);
if(kart->isNetworkKart())
if(kart->getController()->isNetworkController())
{
((NetworkKart*)kart)->setControl(kc);
}

View File

@@ -55,7 +55,8 @@ RaceResultMessage::RaceResultMessage(ENetPacket* pkt)
float time = getFloat();
char position = getChar();
kart->setPosition(position);
kart->raceFinished(time);
kart->finishedRace(time);
world->createEndKart(i);
}
} // RaceResultMessage

View File

@@ -24,8 +24,8 @@
#include "items/flyable.hpp"
#include "items/item.hpp"
#include "karts/controller/kart_control.hpp"
#include "karts/kart.hpp"
#include "karts/kart_control.hpp"
#include "modes/world.hpp"
#include "network/flyable_info.hpp"
#include "network/item_info.hpp"

View File

@@ -24,7 +24,7 @@
#include "LinearMath/btQuaternion.h"
#include "karts/kart_control.hpp"
#include "karts/controller/kart_control.hpp"
#include "utils/vec3.hpp"
class Kart;

View File

@@ -391,7 +391,7 @@ void RaceManager::RaceFinished(const Kart *kart, float time)
m_kart_status[id].m_overall_time += time;
m_kart_status[id].m_last_time = time;
m_num_finished_karts ++;
if(kart->isPlayerKart()) m_num_finished_players++;
if(kart->getController()->isPlayerController()) m_num_finished_players++;
} // raceFinished
//-----------------------------------------------------------------------------

View File

@@ -29,7 +29,7 @@
#include "utils/translation.hpp"
class Kart;
class PlayerKart;
class Kart;
class Track;
/** The race manager has two functions:

View File

@@ -1,71 +0,0 @@
/* This is a template to avoid writing boring stuff again and again when
* creating a new robot.
*
* Don't forget to replace the copyright in the fourth line of the header in
* this file and the header file, with the current year and your name, the
* #include to <name of your robot>_robot.hpp, the class name references in
* this file and in empty_robot.hpp to <robot name>Robot and the #ifdef at
* beginning of the header file.
*
* You should also delete these intructions. Oh, and if you use this template
* as a robot, it does nothing.
*/
#if 0
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2004-2005 Steve Baker <sjbaker1@airmail.net>
// Copyright (C) 2006-2007 Eduardo Hernandez Munoz
// Copyright (C) <insert year here> <insert name here>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "modes/world.hpp"
#include "robot/empty_robot.hpp"
EmptyRobot::EmptyRobot(const KartProperties *kart_properties, int position,
sgCoord init_pos) :
AutoKart(kart_properties, position, init_pos)
{
//This is called just once per *competition*
reset();
}
//-----------------------------------------------------------------------------
void EmptyRobot::update (float delta)
{
/*General kart stuff*/
AutoKart::update(delta);
}
//-----------------------------------------------------------------------------
void EmptyRobot::reset()
{
//This function is called at the beginning of *each race*
m_controls.lr = 0.0;
m_controls.accel = false;
m_controls.brake = false;
m_controls.brake = false;
m_controls.wheelie = false;
m_controls.jump = false;
m_controls.rescue = false;
m_controls.fire = false;
/*General kart stuff*/
AutoKart::reset();
}
#endif

View File

@@ -1,37 +0,0 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2004-2005 Steve Baker <sjbaker1@airmail.net>
// Copyright (C) 2006-2007 Eduardo Hernandez Munoz
// Copyright (C) <insert year here> <insert name here>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_EMPTY_HPP
#define HEADER_EMPTY_HPP
#include "karts/auto_kart.hpp"
class EmptyRobot : public AutoKart
{
public:
EmptyRobot(const KartProperties *kart_properties, int position,
sgCoord init_pos);
void update (float delta);
void reset ();
};
#endif
/* EOF */

View File

@@ -34,7 +34,6 @@ using namespace irr;
#include "input/input.hpp"
#include "input/input_manager.hpp"
#include "karts/kart_properties_manager.hpp"
#include "karts/player_kart.hpp"
#include "modes/world.hpp"
#include "race/race_manager.hpp"
#include "tracks/track.hpp"
@@ -223,8 +222,8 @@ void RaceGUI::renderGlobal(float dt)
*/
void RaceGUI::renderPlayerView(const Kart *kart)
{
const core::recti &viewport = kart->getViewport();
const core::vector2df &scaling = kart->getScaling();
const core::recti &viewport = kart->getCamera()->getViewport();
const core::vector2df &scaling = kart->getCamera()->getScaling();
drawAllMessages (kart, viewport, scaling);
if(!World::getWorld()->isRacePhase()) return;
@@ -309,8 +308,9 @@ void RaceGUI::drawGlobalMiniMap()
// int marker_height = m_marker->getOriginalSize().Height;
core::rect<s32> source(i *m_marker_rendered_size, 0,
(i+1)*m_marker_rendered_size, m_marker_rendered_size);
int marker_half_size = (kart->isPlayerKart() ? m_marker_player_size
: m_marker_ai_size )>>1;
int marker_half_size = (kart->getController()->isPlayerController()
? m_marker_player_size
: m_marker_ai_size )>>1;
core::rect<s32> position(m_map_left+(int)(draw_at.getX()-marker_half_size),
lower_y -(int)(draw_at.getY()+marker_half_size),
m_map_left+(int)(draw_at.getX()+marker_half_size),
@@ -373,8 +373,10 @@ void RaceGUI::drawGlobalPlayerIcons(const KartIconDisplayInfo* info)
}
// draw icon
video::ITexture *icon = kart->getKartProperties()->getIconMaterial()->getTexture();
int w = kart->isPlayerKart() ? ICON_PLAYER_WIDTH : ICON_WIDTH;
video::ITexture *icon =
kart->getKartProperties()->getIconMaterial()->getTexture();
int w = kart->getController()->isPlayerController() ? ICON_PLAYER_WIDTH
: ICON_WIDTH;
const core::rect<s32> pos(x, y, x+w, y+w);
// Fixes crash bug, why are certain icons not showing up?

View File

@@ -24,7 +24,6 @@
#include "graphics/camera.hpp"
#include "io/xml_node.hpp"
#include "karts/player_kart.hpp"
#include "modes/world.hpp"
#include "race/race_manager.hpp"
#include "tracks/track.hpp"
@@ -70,7 +69,7 @@ void AmbientLightSphere::update(float dt)
const video::SColor &def = track->getDefaultAmbientColor();
color = m_ambient_color.getInterpolated(def, f);
}
((PlayerKart*)kart)->getCamera()->setAmbientLight(color);
kart->getCamera()->setAmbientLight(color);
} // if active
} // for i<num_karts
} // update
@@ -86,6 +85,6 @@ void AmbientLightSphere::update(float dt)
bool AmbientLightSphere::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos,
int indx)
{
if(!World::getWorld()->getKart(indx)->isPlayerKart()) return false;
if(!World::getWorld()->getKart(indx)->getCamera()) return false;
return CheckSphere::isTriggered(old_pos, new_pos, indx);
} // isTriggered