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