stk-code_catmod/src/modes/standard_race.cpp
2018-07-24 14:22:20 +08:00

157 lines
6.0 KiB
C++

// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2006-2015 SuperTuxKart-Team
//
// 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/standard_race.hpp"
#include "challenges/unlock_manager.hpp"
#include "items/powerup_manager.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/controller/controller.hpp"
#include "karts/controller/ghost_controller.hpp"
#include "network/network_config.hpp"
//-----------------------------------------------------------------------------
StandardRace::StandardRace() : LinearWorld()
{
WorldStatus::setClockMode(CLOCK_CHRONO);
} // StandardRace
//-----------------------------------------------------------------------------
/** Returns true if the race is finished, i.e. all player karts are finished.
*/
bool StandardRace::isRaceOver()
{
if (race_manager->isWatchingReplay())
{
return dynamic_cast<GhostController*>
(m_karts[0]->getController())->isReplayEnd();
}
// The race is over if all players have finished the race. Remaining
// times for AI opponents will be estimated in enterRaceOverState
return race_manager->allPlayerFinished();
} // isRaceOver
//-----------------------------------------------------------------------------
void StandardRace::getDefaultCollectibles(int *collectible_type, int *amount)
{
// in time trial mode, give zippers
if(race_manager->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL &&
!race_manager->isWatchingReplay())
{
*collectible_type = PowerupManager::POWERUP_ZIPPER;
*amount = race_manager->getNumLaps();
}
else World::getDefaultCollectibles(collectible_type, amount);
} // getDefaultCollectibles
//-----------------------------------------------------------------------------
/** Returns if this mode supports bonus boxes or not.
*/
bool StandardRace::haveBonusBoxes()
{
// in time trial mode, don't use bonus boxes
return race_manager->getMinorMode() != RaceManager::MINOR_MODE_TIME_TRIAL;
} // haveBonusBoxes
//-----------------------------------------------------------------------------
/** Returns an identifier for this race.
*/
const std::string& StandardRace::getIdent() const
{
if(race_manager->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL)
return IDENT_TTRIAL;
else
return IDENT_STD;
} // getIdent
//-----------------------------------------------------------------------------
/** Ends the race early and places still active player karts at the back.
* The race immediately goes to the result stage, estimating the time for the
* karts still in the race. Still active player karts get a penalty in time
* as well as being placed at the back. Players that already finished keep
* their position.
*
* End time for the punished players is calculated as follows
* end_time = current_time + (estimated_time - current_time)
* + (estimated_time_for_last - current_time)
* = estimated_time + estimated_time_for_last - current_time
* This will put them at the end at all times. The further you (and the last in
* the race) are from the finish line, the harsher the punishment will be.
*/
void StandardRace::endRaceEarly()
{
const unsigned int kart_amount = (unsigned int)m_karts.size();
std::vector<int> active_players;
// Required for debugging purposes
beginSetKartPositions();
float worse_finish_time = 0.0f;
for (unsigned int i = 1; i <= kart_amount; i++)
{
int kartid = m_position_index[i-1];
AbstractKart* kart = m_karts[kartid].get();
if (kart->hasFinishedRace())
{
if (kart->getFinishTime() > worse_finish_time)
worse_finish_time = kart->getFinishTime();
// Have to do this to keep endSetKartPosition happy
setKartPosition(kartid, kart->getPosition());
}
else
{
float estimated_finish_time = estimateFinishTimeForKart(kart);
if (estimated_finish_time > worse_finish_time)
worse_finish_time = estimated_finish_time;
// Keep active players apart for now
if (kart->getController()->isPlayerController())
{
active_players.push_back(kartid);
}
// AI karts finish
else
{
setKartPosition(kartid, i - (unsigned int) active_players.size());
kart->finishedRace(estimated_finish_time);
}
}
} // i <= kart_amount
// Now make the active players finish
for (unsigned int i = 0; i < active_players.size(); i++)
{
int kartid = active_players[i];
int position = getNumKarts() - (int) active_players.size() + 1 + i;
setKartPosition(kartid, position);
float punished_time = estimateFinishTimeForKart(m_karts[kartid].get())
+ worse_finish_time - WorldStatus::getTime();
m_karts[kartid]->finishedRace(punished_time);
// In networked races, endRaceEarly will be called if a player
// takes too much time to finish, so don't mark him as eliminated
if (!isNetworkWorld())
m_karts[kartid]->eliminate();
} // Finish the active players
endSetKartPositions();
setPhase(RESULT_DISPLAY_PHASE);
if (!isNetworkWorld() || NetworkConfig::get()->isServer())
terminateRace();
} // endRaceEarly