1 // SuperTuxKart - a fun racing game with go-kart
2 // Copyright (C) 2006-2015 SuperTuxKart-Team
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 3
7 // of the License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18 #include "modes/standard_race.hpp"
19
20 #include "challenges/unlock_manager.hpp"
21 #include "items/powerup_manager.hpp"
22 #include "karts/abstract_kart.hpp"
23 #include "karts/controller/controller.hpp"
24 #include "karts/controller/ghost_controller.hpp"
25 #include "network/network_config.hpp"
26
27 //-----------------------------------------------------------------------------
StandardRace()28 StandardRace::StandardRace() : LinearWorld()
29 {
30 WorldStatus::setClockMode(CLOCK_CHRONO);
31 } // StandardRace
32
33 //-----------------------------------------------------------------------------
34 /** Returns true if the race is finished, i.e. all player karts are finished.
35 */
isRaceOver()36 bool StandardRace::isRaceOver()
37 {
38 if (RaceManager::get()->isWatchingReplay())
39 {
40 return dynamic_cast<GhostController*>
41 (m_karts[0]->getController())->isReplayEnd();
42 }
43 // The race is over if all players have finished the race. Remaining
44 // times for AI opponents will be estimated in enterRaceOverState
45 return RaceManager::get()->allPlayerFinished();
46 } // isRaceOver
47
48 //-----------------------------------------------------------------------------
getDefaultCollectibles(int * collectible_type,int * amount)49 void StandardRace::getDefaultCollectibles(int *collectible_type, int *amount)
50 {
51 // in time trial mode, give zippers
52 if(RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL &&
53 !RaceManager::get()->isWatchingReplay())
54 {
55 *collectible_type = PowerupManager::POWERUP_ZIPPER;
56 *amount = RaceManager::get()->getNumLaps();
57 }
58 else World::getDefaultCollectibles(collectible_type, amount);
59 } // getDefaultCollectibles
60
61 //-----------------------------------------------------------------------------
62 /** Returns if this mode supports bonus boxes or not.
63 */
haveBonusBoxes()64 bool StandardRace::haveBonusBoxes()
65 {
66 // in time trial mode, don't use bonus boxes
67 return RaceManager::get()->getMinorMode() != RaceManager::MINOR_MODE_TIME_TRIAL;
68 } // haveBonusBoxes
69
70 //-----------------------------------------------------------------------------
71 /** Returns an identifier for this race.
72 */
getIdent() const73 const std::string& StandardRace::getIdent() const
74 {
75 if(RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL)
76 return IDENT_TTRIAL;
77 else
78 return IDENT_STD;
79 } // getIdent
80
81 //-----------------------------------------------------------------------------
82 /** Ends the race early and places still active player karts at the back.
83 * The race immediately goes to the result stage, estimating the time for the
84 * karts still in the race. Still active player karts get a penalty in time
85 * as well as being placed at the back. Players that already finished keep
86 * their position.
87 *
88 * End time for the punished players is calculated as follows :
89 *
90 * 1) Intended for races without auto-end where a finish before all
91 * karts arrived means a player abandoned.
92 * end_time = current_time + (estimated_time - current_time)
93 * + (estimated_time_for_last - current_time)
94 * = estimated_time + estimated_time_for_last - current_time
95 * This will put them at the end at all times. The further you (and the last in
96 * the race) are from the finish line, the harsher the punishment will be.
97 *
98 * 2) When there is no AI. Intended for online races with auto-end.
99 * end_time = current_time + 2*(estimated_time - current_time)
100 * = 2*estimated_time - current_time
101 */
endRaceEarly()102 void StandardRace::endRaceEarly()
103 {
104 const unsigned int kart_amount = (unsigned int)m_karts.size();
105 std::vector<int> active_players;
106 // Required for debugging purposes
107 beginSetKartPositions();
108 float worse_finish_time = 0.0f;
109
110 for (unsigned int i = 1; i <= kart_amount; i++)
111 {
112 int kartid = m_position_index[i-1];
113 AbstractKart* kart = m_karts[kartid].get();
114
115 if (kart->hasFinishedRace())
116 {
117 if (kart->getFinishTime() > worse_finish_time)
118 worse_finish_time = kart->getFinishTime();
119
120 // Have to do this to keep endSetKartPosition happy
121 setKartPosition(kartid, kart->getPosition());
122 }
123
124 else
125 {
126 float estimated_finish_time = estimateFinishTimeForKart(kart);
127 if (estimated_finish_time > worse_finish_time)
128 worse_finish_time = estimated_finish_time;
129
130 // Keep active players apart for now
131 if (kart->getController()->isPlayerController())
132 {
133 active_players.push_back(kartid);
134 }
135 // AI karts finish
136 else
137 {
138 setKartPosition(kartid, i - (unsigned int) active_players.size());
139 kart->finishedRace(estimated_finish_time);
140 }
141 }
142 } // i <= kart_amount
143
144 // Now make the active players finish
145 for (unsigned int i = 0; i < active_players.size(); i++)
146 {
147 int kartid = active_players[i];
148 int position = getNumKarts() - (int) active_players.size() + 1 + i;
149 setKartPosition(kartid, position);
150 // Compute the finish time, with a different formula for networked races
151 // to avoid making auto-end too punishing
152 float punished_time = estimateFinishTimeForKart(m_karts[kartid].get());
153 if (!isNetworkWorld())
154 punished_time += worse_finish_time - WorldStatus::getTime();
155 else
156 punished_time = (punished_time * 2) - WorldStatus::getTime();
157
158 m_karts[kartid]->finishedRace(punished_time);
159
160 // In networked races, endRaceEarly will be called if a player
161 // takes too much time to finish, so don't mark him as eliminated
162 if (!isNetworkWorld())
163 m_karts[kartid]->eliminate();
164 } // Finish the active players
165 endSetKartPositions();
166 setPhase(RESULT_DISPLAY_PHASE);
167 if (!isNetworkWorld() || NetworkConfig::get()->isServer())
168 terminateRace();
169 if (!isNetworkWorld())
170 m_ended_early = true;
171 } // endRaceEarly
172