Initial work on supporting real race with ghost karts

To test, you need to pass --ghost to supertuxkart with choosing a
track that has replay recorded, you can record one with artist
debug mode.

TODO: correct position handling
This commit is contained in:
Benau
2016-02-06 14:52:50 +08:00
parent 647f42e984
commit c3f589561c
21 changed files with 228 additions and 94 deletions

View File

@@ -226,7 +226,8 @@ void Flyable::getClosestKart(const AbstractKart **minKart,
// it is not considered a target anymore.
if(kart->isEliminated() || kart == m_owner ||
kart->isInvulnerable() ||
kart->getKartAnimation() ) continue;
kart->getKartAnimation() ||
kart->isGhostKart() ) continue;
const SoccerWorld* sw = dynamic_cast<SoccerWorld*>(World::getWorld());
if (sw)
@@ -480,6 +481,7 @@ void Flyable::explode(AbstractKart *kart_hit, PhysicalObject *object,
->getKartTeam(m_owner->getWorldKartId()))
continue;
}
if (kart->isGhostKart()) continue;
// If no secondary hits should be done, only hit the
// direct hit kart.

View File

@@ -295,7 +295,7 @@ void Powerup::use()
for(unsigned int i = 0 ; i < world->getNumKarts(); ++i)
{
AbstractKart *kart=world->getKart(i);
if(kart->isEliminated()) continue;
if(kart->isEliminated() || kart->isGhostKart()) continue;
if(kart == m_owner) continue;
if(kart->getPosition() == 1)
{
@@ -329,7 +329,7 @@ void Powerup::use()
for(unsigned int i = 0 ; i < world->getNumKarts(); ++i)
{
AbstractKart *kart=world->getKart(i);
if(kart->isEliminated() || kart== m_owner) continue;
if(kart->isEliminated() || kart== m_owner || kart->isGhostKart()) continue;
if(kart->isShielded())
{
kart->decreaseShieldTime();

View File

@@ -243,7 +243,7 @@ void Swatter::chooseTarget()
{
AbstractKart *kart = world->getKart(i);
// TODO: isSwatterReady(), isSquashable()?
if(kart->isEliminated() || kart==m_kart)
if(kart->isEliminated() || kart == m_kart || kart->isGhostKart())
continue;
// don't squash an already hurt kart
if (kart->isInvulnerable() || kart->isSquashed())

View File

@@ -454,6 +454,9 @@ public:
// ------------------------------------------------------------------------
/** Returns whether this kart wins or loses. */
virtual bool getRaceResult() const = 0;
// ------------------------------------------------------------------------
/** Returns whether this kart is a ghost (replay) kart. */
virtual bool isGhostKart() const = 0;
}; // AbstractKart

View File

@@ -1757,7 +1757,7 @@ void SkiddingAI::checkCrashes(const Vec3& pos )
{
const AbstractKart* kart = m_world->getKart(j);
// Ignore eliminated karts
if(kart==m_kart||kart->isEliminated()) continue;
if(kart==m_kart||kart->isEliminated()||kart->isGhostKart()) continue;
const AbstractKart *other_kart = m_world->getKart(j);
// Ignore karts ahead that are faster than this kart.
if(m_kart->getVelocityLC().getZ() < other_kart->getVelocityLC().getZ())

View File

@@ -24,9 +24,11 @@
#include "LinearMath/btQuaternion.h"
#include "utils/log.hpp"
GhostKart::GhostKart(const std::string& ident)
: Kart(ident, /*world kart id*/99999,
/*position*/-1, btTransform(), PLAYER_DIFFICULTY_NORMAL)
GhostKart::GhostKart(const std::string& ident, unsigned int world_kart_id,
int position)
: Kart(ident, world_kart_id,
position, btTransform(btQuaternion(0, 0, 0, 1)),
PLAYER_DIFFICULTY_NORMAL)
{
m_all_times.clear();
m_all_transform.clear();
@@ -131,5 +133,7 @@ void GhostKart::update(float dt)
m_all_physic_info[m_current_transform].m_speed,
m_current_transform);
Vec3 front(0, 0, getKartLength()*0.5f);
m_xyz_front = getTrans()(front);
getKartGFX()->update(dt);
} // update

View File

@@ -51,7 +51,8 @@ private:
unsigned int m_current_transform;
public:
GhostKart(const std::string& ident);
GhostKart(const std::string& ident,
unsigned int world_kart_id, int position);
virtual void update (float dt);
virtual void reset();
// ------------------------------------------------------------------------
@@ -72,5 +73,8 @@ public:
const ReplayBase::PhysicInfo &pi,
const ReplayBase::KartReplayEvent &kre);
// ------------------------------------------------------------------------
/** Returns whether this kart is a ghost (replay) kart. */
virtual bool isGhostKart() const { return true; }
}; // GhostKart
#endif

View File

@@ -512,7 +512,8 @@ void Kart::setController(Controller *controller)
*/
void Kart::setPosition(int p)
{
m_controller->setPosition(p);
if (m_controller)
m_controller->setPosition(p);
m_race_position = p;
} // setPosition
@@ -834,14 +835,15 @@ void Kart::finishedRace(float time)
if(m_finished_race) return;
m_finished_race = true;
m_finish_time = time;
m_controller->finishedRace(time);
if(!isGhostKart())
m_controller->finishedRace(time);
m_kart_model->finishedRace();
race_manager->kartFinishedRace(this, time);
if ((race_manager->getMinorMode() == RaceManager::MINOR_MODE_NORMAL_RACE ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER)
&& m_controller->isPlayerController())
&& (isGhostKart() ? false : m_controller->isPlayerController()))
{
RaceGUIBase* m = World::getWorld()->getRaceGUI();
if (m)
@@ -869,10 +871,13 @@ void Kart::finishedRace(float time)
{
// Save for music handling in race result gui
setRaceResult();
setController(new EndController(this, m_controller->getPlayer(),
m_controller));
if(!isGhostKart())
{
setController(new EndController(this, m_controller->getPlayer(),
m_controller));
}
// Skip animation if this kart is eliminated
if (m_eliminated) return;
if (m_eliminated || isGhostKart()) return;
m_kart_model->setAnimation(m_race_result ?
KartModel::AF_WIN_START : KartModel::AF_LOSE_START);
@@ -885,8 +890,9 @@ void Kart::setRaceResult()
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_NORMAL_RACE ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL)
{
if (m_controller->isLocalPlayerController()) // if player is on this computer
if (isGhostKart() ? false : m_controller->isPlayerController())
{
// if player is on this computer
PlayerProfile *player = PlayerManager::getCurrentPlayer();
const ChallengeStatus *challenge = player->getCurrentChallengeStatus();
// In case of a GP challenge don't make the end animation depend
@@ -1487,7 +1493,7 @@ void Kart::showZipperFire()
*/
void Kart::setSquash(float time, float slowdown)
{
if (isInvulnerable()) return;
if (isInvulnerable() || isGhostKart()) return;
if (isShielded())
{

View File

@@ -68,6 +68,10 @@ protected:
/** Offset of the graphical kart chassis from the physical chassis. */
float m_graphical_y_offset;
/** The coordinates of the front of the kart, used to determine when a
* new lap is triggered. */
Vec3 m_xyz_front;
private:
/** Handles speed increase and capping due to powerup, terrain, ... */
MaxSpeed *m_max_speed;
@@ -106,10 +110,6 @@ private:
/** Current race position (1-num_karts). */
int m_race_position;
/** The coordinates of the front of the kart, used to determine when a
* new lap is triggered. */
Vec3 m_xyz_front;
/** True if the kart wins, false otherwise. */
bool m_race_result;
@@ -448,6 +448,9 @@ public:
// ------------------------------------------------------------------------
/** Set this kart race result. */
void setRaceResult();
// ------------------------------------------------------------------------
/** Returns whether this kart is a ghost (replay) kart. */
virtual bool isGhostKart() const { return false; }
}; // Kart

View File

@@ -806,7 +806,7 @@ void KartModel::update(float dt, float distance, float steer, float speed,
if (!m_kart || !m_wheel_node[i]) continue;
#ifdef DEBUG
if (UserConfigParams::m_physics_debug &&
!dynamic_cast<GhostKart*>(m_kart) )
!m_kart->isGhostKart())
{
const btWheelInfo &wi = m_kart->getVehicle()->getWheelInfo(i);
// Make wheels that are not touching the ground invisible

View File

@@ -23,7 +23,6 @@
#endif
#include "achievements/achievement_info.hpp"
#include "config/player_manager.hpp"
#include "karts/ghost_kart.hpp"
#include "karts/kart.hpp"
#include "karts/kart_gfx.hpp"
#include "karts/kart_properties.hpp"
@@ -82,7 +81,7 @@ void Skidding::reset()
btVector3 rot(0, 0, 0);
// Only access the vehicle if the kart is not a ghost
if (dynamic_cast<GhostKart*>(m_kart)==NULL)
if (!m_kart->isGhostKart())
m_kart->getVehicle()->setTimedRotation(0, rot);
} // reset

View File

@@ -245,20 +245,24 @@ void LinearWorld::newLap(unsigned int kart_index)
{
KartInfo &kart_info = m_kart_info[kart_index];
AbstractKart *kart = m_karts[kart_index];
const bool is_gk = kart->isGhostKart();
// Reset reset-after-lap achievements
StateManager::ActivePlayer *c = kart->getController()->getPlayer();
PlayerProfile *p = PlayerManager::getCurrentPlayer();
if (c && c->getConstProfile() == p)
if (!is_gk)
{
p->getAchievementsStatus()->onLapEnd();
StateManager::ActivePlayer *c = kart->getController()->getPlayer();
PlayerProfile *p = PlayerManager::getCurrentPlayer();
if (c && c->getConstProfile() == p)
{
p->getAchievementsStatus()->onLapEnd();
}
}
// Only update the kart controller if a kart that has already finished
// the race crosses the start line again. This avoids 'fastest lap'
// messages if the end controller does a fastest lap, but especially
// allows the end controller to switch end cameras
if(kart->hasFinishedRace())
if(kart->hasFinishedRace() && !is_gk)
{
kart->getController()->newLap(kart_info.m_race_lap);
return;
@@ -374,7 +378,8 @@ void LinearWorld::newLap(unsigned int kart_index)
} // end if new fastest lap
kart_info.m_lap_start_time = getTime();
kart->getController()->newLap(kart_info.m_race_lap);
if (!is_gk)
kart->getController()->newLap(kart_info.m_race_lap);
} // newLap
//-----------------------------------------------------------------------------
@@ -840,6 +845,8 @@ void LinearWorld::updateRacePosition()
*/
void LinearWorld::checkForWrongDirection(unsigned int i, float dt)
{
if (m_karts[i]->isGhostKart()) return;
if (!m_karts[i]->getController()->isLocalPlayerController())
return;

View File

@@ -102,10 +102,11 @@ void StandardRace::endRaceEarly()
continue;
}
if (kart->getController()->isPlayerController())
if (!kart->isGhostKart())
{
// Keep active players apart for now
active_players.push_back(kartid);
if (kart->getController()->isPlayerController())
active_players.push_back(kartid);
}
else
{

View File

@@ -146,6 +146,9 @@ void World::init()
m_eliminated_karts = 0;
m_eliminated_players = 0;
m_num_players = 0;
unsigned int gk = 0;
if (ReplayPlay::get())
gk = ReplayPlay::get()->getNumGhostKart();
// Create the race gui before anything else is attached to the scene node
// (which happens when the track is loaded). This allows the race gui to
@@ -179,8 +182,16 @@ void World::init()
// karts can be positioned properly on (and not in) the tracks.
m_track->loadTrackModel(race_manager->getReverseTrack());
if (gk > 0)
{
ReplayPlay::get()->load();
for (unsigned int k = 0; k < gk; k++)
m_karts.push_back(ReplayPlay::get()->getGhostKart(k));
}
for(unsigned int i=0; i<num_karts; i++)
{
if (race_manager->getKartType(i) == RaceManager::KT_GHOST) continue;
std::string kart_ident = history->replayHistory()
? history->getKartIdent(i)
: race_manager->getKartIdent(i);
@@ -201,11 +212,8 @@ void World::init()
// Must be called after all karts are created
m_race_gui->init();
if(ReplayPlay::get())
ReplayPlay::get()->Load();
powerup_manager->updateWeightsForRace(num_karts);
if (UserConfigParams::m_weather_effects)
{
m_weather = new Weather(m_track->getWeatherLightning(),
@@ -382,16 +390,6 @@ World::~World()
{
irr_driver->onUnloadWorld();
if(ReplayPlay::get())
{
// Destroy the old replay object, which also stored the ghost
// karts, and create a new one (which means that in further
// races the usage of ghosts will still be enabled).
ReplayPlay::destroy();
ReplayPlay::create();
}
// In case that a race is aborted (e.g. track not found) m_track is 0.
if(m_track)
m_track->cleanup();
@@ -416,9 +414,22 @@ World::~World()
delete m_weather;
for ( unsigned int i = 0 ; i < m_karts.size() ; i++ )
{
// Let ReplayPlay destroy the ghost karts
if (m_karts[i]->isGhostKart()) continue;
delete m_karts[i];
}
if(ReplayPlay::get())
{
// Destroy the old replay object, which also stored the ghost
// karts, and create a new one (which means that in further
// races the usage of ghosts will still be enabled).
ReplayPlay::destroy();
ReplayPlay::create();
}
m_karts.clear();
Camera::removeAllCameras();
projectile_manager->cleanup();
@@ -447,6 +458,7 @@ void World::onGo()
// from sliding downhill)
for(unsigned int i=0; i<m_karts.size(); i++)
{
if (m_karts[i]->isGhostKart()) continue;
m_karts[i]->getVehicle()->setAllBrakes(0);
}
} // onGo
@@ -508,6 +520,7 @@ void World::terminateRace()
}
for(unsigned int i = 0; i < kart_amount; i++)
{
if (m_karts[i]->isGhostKart()) continue;
// Retrieve the current player
StateManager::ActivePlayer* p = m_karts[i]->getController()->getPlayer();
if (p && p->getConstProfile() == PlayerManager::getCurrentPlayer())
@@ -533,6 +546,7 @@ void World::terminateRace()
{
for(unsigned int i = 0; i < kart_amount; i++)
{
if (m_karts[i]->isGhostKart()) continue;
// Retrieve the current player
StateManager::ActivePlayer* p = m_karts[i]->getController()->getPlayer();
if (p && p->getConstProfile() == PlayerManager::getCurrentPlayer())
@@ -593,12 +607,13 @@ void World::resetAllKarts()
getPhysics()->getPhysicsWorld()->resetLocalTime();
// If track checking is requested, check all rescue positions if
// they are heigh enough.
// they are high enough.
if(UserConfigParams::m_track_debug)
{
// Loop over all karts, in case that some karts are dfferent
for(unsigned int kart_id=0; kart_id<(unsigned int)m_karts.size(); kart_id++)
{
if (m_karts[kart_id]->isGhostKart()) continue;
for(unsigned int rescue_pos=0;
rescue_pos<getNumberOfRescuePositions();
rescue_pos++)
@@ -626,6 +641,7 @@ void World::resetAllKarts()
//that at least one of its wheel will be on the surface of the track
for ( KartList::iterator i=m_karts.begin(); i!=m_karts.end(); i++)
{
if ((*i)->isGhostKart()) continue;
Vec3 xyz = (*i)->getXYZ();
//start projection from top of kart
Vec3 up_offset(0, 0.5f * ((*i)->getKartHeight()), 0);
@@ -674,6 +690,7 @@ void World::resetAllKarts()
all_finished=true;
for ( KartList::iterator i=m_karts.begin(); i!=m_karts.end(); i++)
{
if ((*i)->isGhostKart()) continue;
if(!(*i)->isInRest())
{
Vec3 normal;
@@ -716,6 +733,7 @@ void World::resetAllKarts()
for ( KartList::iterator i=m_karts.begin(); i!=m_karts.end(); i++)
{
if ((*i)->isGhostKart()) continue;
(*i)->kartIsInRestNow();
}
@@ -1073,6 +1091,7 @@ void World::updateHighscores(int* best_highscore_rank, int* best_finish_time,
for (unsigned int pos=0; pos<kart_amount; pos++)
{
if (m_karts[index[pos]]->isGhostKart()) continue;
if(index[pos] == 999)
{
// no kart claimed to be in this position, most likely means
@@ -1137,11 +1156,14 @@ AbstractKart *World::getPlayerKart(unsigned int n) const
unsigned int count=-1;
for(unsigned int i=0; i<m_karts.size(); i++)
{
if(m_karts[i]->isGhostKart()) continue;
if(m_karts[i]->getController()->isPlayerController())
{
count++;
if(count==n) return m_karts[i];
}
}
return NULL;
} // getPlayerKart
@@ -1162,6 +1184,7 @@ AbstractKart *World::getLocalPlayerKart(unsigned int n) const
void World::eliminateKart(int kart_id, bool notify_of_elimination)
{
AbstractKart *kart = m_karts[kart_id];
if (kart->isGhostKart()) return;
// Display a message about the eliminated kart in the race guia
if (notify_of_elimination)
@@ -1238,6 +1261,7 @@ void World::unpause()
for(unsigned int i=0; i<m_karts.size(); i++)
{
if (m_karts[i]->isGhostKart()) continue;
// Note that we can not test for isPlayerController here, since
// an EndController will also return 'isPlayerController' if the
// kart belonged to a player.

View File

@@ -47,6 +47,7 @@
#include "network/network_config.hpp"
#include "network/network_world.hpp"
#include "network/protocols/start_game_protocol.hpp"
#include "replay/replay_play.hpp"
#include "scriptengine/property_animator.hpp"
#include "states_screens/grand_prix_cutscene.hpp"
#include "states_screens/grand_prix_lose.hpp"
@@ -308,6 +309,13 @@ void RaceManager::computeRandomKartList()
*/
void RaceManager::startNew(bool from_overworld)
{
unsigned int gk = 0;
if (ReplayPlay::get())
{
ReplayPlay::get()->loadKartInfo();
gk = ReplayPlay::get()->getNumGhostKart();
}
m_started_from_overworld = from_overworld;
m_saved_gp = NULL; // There will be checks for this being NULL done later
@@ -359,16 +367,30 @@ void RaceManager::startNew(bool from_overworld)
// Create the kart status data structure to keep track of scores, times, ...
// ==========================================================================
m_kart_status.clear();
Log::verbose("RaceManager", "Nb of karts=%u, ai:%lu players:%lu\n",
(unsigned int) m_num_karts, m_ai_kart_list.size(), m_player_karts.size());
if (gk > 0)
m_num_karts += gk;
assert((unsigned int)m_num_karts == m_ai_kart_list.size()+m_player_karts.size());
Log::verbose("RaceManager", "Nb of karts=%u, ghost karts:%u ai:%lu players:%lu\n",
(unsigned int) m_num_karts, gk, m_ai_kart_list.size(), m_player_karts.size());
// First add the AI karts (randomly chosen)
assert((unsigned int)m_num_karts == gk+m_ai_kart_list.size()+m_player_karts.size());
// First add the ghost karts (if any)
// ----------------------------------------
// GP ranks start with -1 for the leader.
int init_gp_rank = getMinorMode()==MINOR_MODE_FOLLOW_LEADER ? -1 : 0;
if (gk > 0)
{
for(unsigned int i = 0; i < gk; i++)
{
m_kart_status.push_back(KartStatus(ReplayPlay::get()->getGhostKartName(i),
i, -1, -1, init_gp_rank, KT_GHOST, PLAYER_DIFFICULTY_NORMAL));
init_gp_rank ++;
}
}
// Then add the AI karts (randomly chosen)
// ----------------------------------------
const unsigned int ai_kart_count = m_ai_kart_list.size();
for(unsigned int i = 0; i < ai_kart_count; i++)
{
@@ -382,7 +404,7 @@ void RaceManager::startNew(bool from_overworld)
}
}
// Then add the players, which start behind the AI karts
// Finally add the players, which start behind the AI karts
// -----------------------------------------------------
for(unsigned int i = 0; i < m_player_karts.size(); i++)
{
@@ -836,6 +858,7 @@ void RaceManager::kartFinishedRace(const AbstractKart *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->isGhostKart()) return;
if(kart->getController()->isPlayerController())
m_num_finished_players++;
} // kartFinishedRace

View File

@@ -35,7 +35,8 @@ ReplayPlay *ReplayPlay::m_replay_play = NULL;
*/
ReplayPlay::ReplayPlay()
{
m_next = 0;
m_next = 0;
m_ghost_karts_list.clear();
} // ReplayPlay
//-----------------------------------------------------------------------------
@@ -44,21 +45,13 @@ ReplayPlay::~ReplayPlay()
{
} // ~Replay
//-----------------------------------------------------------------------------
/** Starts replay from the replay file in the current directory.
*/
void ReplayPlay::init()
{
m_next = 0;
Load();
} // init
//-----------------------------------------------------------------------------
/** Resets all ghost karts back to start position.
*/
void ReplayPlay::reset()
{
m_next = 0;
m_ghost_karts_list.clear();
for(unsigned int i=0; i<(unsigned int)m_ghost_karts.size(); i++)
{
m_ghost_karts[i].reset();
@@ -77,10 +70,43 @@ void ReplayPlay::update(float dt)
} // update
//-----------------------------------------------------------------------------
/** Loads the ghost karts info in the replay file, required by race manager.
*/
void ReplayPlay::loadKartInfo()
{
char s[1024];
FILE *fd = openReplayFile(/*writeable*/false);
if(!fd)
{
Log::error("Replay", "Can't read '%s', ghost replay disabled.",
getReplayFilename().c_str());
destroy();
return;
}
Log::info("Replay", "Reading ghost karts info");
while(true)
{
if (fgets(s, 1023, fd) == NULL)
Log::fatal("Replay", "Could not read '%s'.", getReplayFilename().c_str());
std::string is_end = std::string(s);
if (is_end == "kart_list_end\n" || is_end == "kart_list_end\r\n") break;
char s1[1024];
if (sscanf(s,"kart: %s", s1) != 1)
Log::fatal("Replay", "Could not read ghost karts info!");
m_ghost_karts_list.push_back(std::string(s1));
}
fclose(fd);
} // loadKartInfo
//-----------------------------------------------------------------------------
/** Loads a replay data from file called 'trackname'.replay.
*/
void ReplayPlay::Load()
void ReplayPlay::load()
{
m_ghost_karts.clearAndDeleteAll();
char s[1024], s1[1024];
@@ -95,12 +121,21 @@ void ReplayPlay::Load()
}
Log::info("Replay", "Reading replay file '%s'.", getReplayFilename().c_str());
if (fgets(s, 1023, fd) == NULL)
Log::fatal("Replay", "Could not read '%s'.", getReplayFilename().c_str());
for (unsigned int i = 0; i < m_ghost_karts_list.size(); i++)
{
if (fgets(s, 1023, fd) == NULL)
Log::fatal("Replay", "Could not read '%s'.", getReplayFilename().c_str());
// Skip kart info which is already read.
}
if (fgets(s, 1023, fd) == NULL)
Log::fatal("Replay", "Could not read '%s'.", getReplayFilename().c_str());
// Skip kart_list_end
unsigned int version;
if (sscanf(s,"Version: %u", &version) != 1)
if (sscanf(s,"version: %u", &version) != 1)
Log::fatal("Replay", "No Version information found in replay file (bogus replay file).");
if (version != getReplayVersion())
@@ -113,7 +148,7 @@ void ReplayPlay::Load()
if (fgets(s, 1023, fd) == NULL)
Log::fatal("Replay", "Could not read '%s'.", getReplayFilename().c_str());
int n;
int n;
if(sscanf(s, "difficulty: %d", &n) != 1)
Log::fatal("Replay", " No difficulty found in replay file.");
@@ -130,7 +165,7 @@ void ReplayPlay::Load()
unsigned int num_laps;
fgets(s, 1023, fd);
if(sscanf(s, "Laps: %u", &num_laps) != 1)
if(sscanf(s, "laps: %u", &num_laps) != 1)
Log::fatal("Replay", "No number of laps found in replay file.");
race_manager->setNumLaps(num_laps);
@@ -142,11 +177,11 @@ void ReplayPlay::Load()
if(fgets(s, 1023, fd)==NULL) // eof reached
break;
readKartData(fd, s);
} // for k<num_ghost_karts
}
fprintf(fd, "Replay file end.\n");
fclose(fd);
} // Load
} // load
//-----------------------------------------------------------------------------
/** Reads all data from a replay file for a specific kart.
@@ -155,16 +190,13 @@ void ReplayPlay::Load()
void ReplayPlay::readKartData(FILE *fd, char *next_line)
{
char s[1024];
if(sscanf(next_line, "model: %s", s)!=1)
Log::fatal("Replay", "No model information for kart %d found.",
m_ghost_karts.size());
const unsigned int kart_num = m_ghost_karts.size();
m_ghost_karts.push_back(new GhostKart(m_ghost_karts_list.at(kart_num),
kart_num, kart_num + 1));
m_ghost_karts[kart_num].init(RaceManager::KT_GHOST);
m_ghost_karts.push_back(new GhostKart(std::string(s)));
m_ghost_karts[m_ghost_karts.size()-1].init(RaceManager::KT_GHOST);
fgets(s, 1023, fd);
unsigned int size;
if(sscanf(s,"size: %u",&size)!=1)
if(sscanf(next_line,"size: %u",&size)!=1)
Log::fatal("Replay", "Number of records not found in replay file "
"for kart %d.",
m_ghost_karts.size()-1);

View File

@@ -19,6 +19,7 @@
#ifndef HEADER_REPLAY__PLAY_HPP
#define HEADER_REPLAY__PLAY_HPP
#include "karts/ghost_kart.hpp"
#include "replay/replay_base.hpp"
#include "utils/ptr_vector.hpp"
@@ -38,6 +39,8 @@ private:
/** Points to the next free entry. */
unsigned int m_next;
std::vector<std::string> m_ghost_karts_list;
/** All ghost karts. */
PtrVector<GhostKart> m_ghost_karts;
@@ -45,20 +48,30 @@ private:
~ReplayPlay();
void readKartData(FILE *fd, char *next_line);
public:
void init();
void update(float dt);
void reset();
void Load();
void load();
void loadKartInfo();
// ------------------------------------------------------------------------
GhostKart* getGhostKart(int n) { return m_ghost_karts.get(n); }
// ------------------------------------------------------------------------
const unsigned int getNumGhostKart() const
{ return m_ghost_karts_list.size(); }
// ------------------------------------------------------------------------
const std::string& getGhostKartName(int n) const
{ return m_ghost_karts_list.at(n); }
// ------------------------------------------------------------------------
/** Creates a new instance of the replay object. */
static void create() { m_replay_play = new ReplayPlay(); }
static void create() { m_replay_play = new ReplayPlay(); }
// ------------------------------------------------------------------------
/** Returns the instance of the replay object. */
static ReplayPlay *get() { return m_replay_play; }
static ReplayPlay *get() { return m_replay_play; }
// ------------------------------------------------------------------------
/** Delete the instance of the replay object. */
static void destroy() { delete m_replay_play; m_replay_play=NULL; }
static void destroy()
{ delete m_replay_play; m_replay_play = NULL; }
// ------------------------------------------------------------------------
}; // Replay
#endif

View File

@@ -104,6 +104,7 @@ void ReplayRecorder::update(float dt)
for(unsigned int i=0; i<num_karts; i++)
{
const AbstractKart *kart = world->getKart(i);
if (kart->isGhostKart()) continue;
#ifdef DEBUG
m_count++;
#endif
@@ -195,16 +196,24 @@ void ReplayRecorder::Save()
World *world = World::getWorld();
unsigned int num_karts = world->getNumKarts();
fprintf(fd, "Version: %d\n", getReplayVersion());
for (unsigned int real_karts = 0; real_karts < num_karts; real_karts++)
{
if (world->getKart(real_karts)->isGhostKart()) continue;
fprintf(fd, "kart: %s\n",
world->getKart(real_karts)->getIdent().c_str());
}
fprintf(fd, "kart_list_end\n");
fprintf(fd, "version: %d\n", getReplayVersion());
fprintf(fd, "difficulty: %d\n", race_manager->getDifficulty());
fprintf(fd, "track: %s\n", world->getTrack()->getIdent().c_str());
fprintf(fd, "Laps: %d\n", race_manager->getNumLaps());
fprintf(fd, "laps: %d\n", race_manager->getNumLaps());
unsigned int max_frames = (unsigned int)( stk_config->m_replay_max_time
/ stk_config->m_replay_dt );
for (unsigned int k = 0; k < num_karts; k++)
{
fprintf(fd, "model: %s\n", world->getKart(k)->getIdent().c_str());
if (world->getKart(k)->isGhostKart()) continue;
fprintf(fd, "size: %d\n", m_count_transforms[k]);
unsigned int num_transforms = std::min(max_frames,

View File

@@ -378,9 +378,9 @@ void RaceGUI::drawGlobalMiniMap()
// int marker_height = m_marker->getSize().Height;
core::rect<s32> source(core::position2di(0, 0), icon->getSize());
int marker_half_size = (kart->getController()->isLocalPlayerController()
? m_minimap_player_size
: m_minimap_ai_size )>>1;
int marker_half_size = (kart->isGhostKart() ? m_minimap_ai_size :
(kart->getController()->isLocalPlayerController() ? m_minimap_player_size :
m_minimap_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),

View File

@@ -802,13 +802,14 @@ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin)
// draw icon
video::ITexture *icon =
kart->getKartProperties()->getIconMaterial()->getTexture();
int w = kart->getController()
int w = kart->isGhostKart() ? ICON_WIDTH : (kart->getController()
->isLocalPlayerController() ? ICON_PLAYER_WIDTH
: ICON_WIDTH;
: ICON_WIDTH);
const core::rect<s32> pos(x, y, x+w, y+w);
//to bring to light the player's icon: add a background
if (kart->getController()->isLocalPlayerController())
if (kart->isGhostKart() ?
false : kart->getController()->isLocalPlayerController())
{
video::SColor colors[4];
for (unsigned int i=0;i<4;i++)

View File

@@ -89,6 +89,7 @@ void RaceResultGUI::init()
for (unsigned int kart_id = 0; kart_id < num_karts; kart_id++)
{
const AbstractKart *kart = World::getWorld()->getKart(kart_id);
if (kart->isGhostKart()) continue;
if (kart->getController()->isPlayerController())
human_win = human_win && kart->getRaceResult();
}
@@ -474,7 +475,8 @@ void RaceResultGUI::determineTableLayout()
// Save a pointer to the current row_info entry
RowInfo *ri = &(m_all_row_infos[position-first_position]);
ri->m_is_player_kart = kart->getController()->isLocalPlayerController();
ri->m_is_player_kart = kart->isGhostKart() ? false :
kart->getController()->isLocalPlayerController();
// Identify Human player, if so display real name other than kart name
const int rm_id = kart->getWorldKartId() -
@@ -860,7 +862,8 @@ void RaceResultGUI::determineGPLayout()
else
ri->m_kart_name = translations->fribidize(kart->getName());
ri->m_is_player_kart = kart->getController()->isLocalPlayerController();
ri->m_is_player_kart = kart->isGhostKart() ? false :
kart->getController()->isLocalPlayerController();
ri->m_player = ri->m_is_player_kart
? kart->getController()->getPlayer() : NULL;