1 //  SuperTuxKart - a fun racing game with go-kart
2 //  Copyright (C) 2010-2015 Joerg Henrichs
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/world_with_rank.hpp"
19 
20 #include "karts/abstract_kart.hpp"
21 #include "karts/controller/spare_tire_ai.hpp"
22 #include "karts/kart_properties.hpp"
23 #include "race/history.hpp"
24 #include "tracks/graph.hpp"
25 #include "tracks/track.hpp"
26 #include "tracks/track_sector.hpp"
27 #include "utils/log.hpp"
28 
29 #include <iostream>
30 
31 //-----------------------------------------------------------------------------
~WorldWithRank()32 WorldWithRank::~WorldWithRank()
33 {
34     for (unsigned int i = 0; i < m_kart_track_sector.size(); i++)
35     {
36         delete m_kart_track_sector[i];
37     }
38     m_kart_track_sector.clear();
39 }   // ~WorldWithRank
40 
41 //-----------------------------------------------------------------------------
init()42 void WorldWithRank::init()
43 {
44     World::init();
45 
46     m_display_rank = true;
47 
48     m_position_index.resize(m_karts.size());
49 #ifdef DEBUG
50     m_position_used.resize(m_karts.size());
51     m_position_setting_initialised = false;
52 #endif
53     stk_config->getAllScores(&m_score_for_position, getNumKarts());
54 
55     Track *track = Track::getCurrentTrack();
56     // Don't init track sector if navmesh is not found in arena
57     if ((track->isArena() || track->isSoccer()) && !track->hasNavMesh())
58         return;
59 
60     for (unsigned int i = 0; i < m_karts.size(); i++)
61         m_kart_track_sector.push_back(new TrackSector());
62 
63 }   // init
64 
65 //-----------------------------------------------------------------------------
reset(bool restart)66 void WorldWithRank::reset(bool restart)
67 {
68     World::reset(restart);
69     for (unsigned int i = 0; i < m_kart_track_sector.size(); i++)
70     {
71         getTrackSector(i)->reset();
72         getTrackSector(i)->update(m_karts[i]->getXYZ());
73     }
74 }   // reset
75 
76 //-----------------------------------------------------------------------------
77 /** Returns the kart with a given position.
78  *  \param p The position of the kart, 1<=p<=num_karts).
79  */
getKartAtPosition(unsigned int p) const80 AbstractKart* WorldWithRank::getKartAtPosition(unsigned int p) const
81 {
82     if(p<1 || p>m_position_index.size())
83         return NULL;
84 
85     return m_karts[m_position_index[p-1]].get();
86 }   // getKartAtPosition
87 
88 //-----------------------------------------------------------------------------
89 /** This function must be called before starting to set all kart positions
90  *  again. It's mainly used to add some debug support, i.e. detect if the
91  *  same position is set in different karts.
92  */
beginSetKartPositions()93 void WorldWithRank::beginSetKartPositions()
94 {
95 #ifdef DEBUG
96     assert(!m_position_setting_initialised);
97     m_position_setting_initialised = true;
98 
99     for(unsigned int i=0; i<m_position_used.size(); i++)
100         m_position_used[i] = false;
101 #endif
102 }   // beginSetKartPositions
103 
104 //-----------------------------------------------------------------------------
105 /** Sets the position of a kart. This will be saved in this object to allow
106  *  quick lookup of which kart is on a given position, but also in the
107  *  kart objects.
108  *  \param kart_id The index of the kart to set the position for.
109  *  \param position The position of the kart (1<=position<=num karts).
110  *  \return false if this position was already set, i.e. an inconsistency in
111  *          kart positions has occurred. This is used in debug mode only to
112  *          allow the calling function to print debug information.
113  */
setKartPosition(unsigned int kart_id,unsigned int position)114 bool WorldWithRank::setKartPosition(unsigned int kart_id,
115                                     unsigned int position)
116 {
117     m_position_index[position-1] = kart_id;
118     m_karts[kart_id]->setPosition(position);
119 #ifdef DEBUG
120     assert(m_position_setting_initialised);
121     if(m_position_used[position-1])
122     {
123         Log::error("[WorldWithRank]", "== TWO KARTS ARE BEING GIVEN THE SAME POSITION!! ==");
124         for (unsigned int j=0; j < m_position_index.size(); j++)
125         {
126             if (!m_position_used[j])
127             {
128                 Log::warn("WorldWithRank]", "No kart is yet set at position %u", j+1);
129             }
130             else
131             {
132                 Log::warn("WorldWithRank]", "Kart %u is at position %u",
133                             m_position_index[j], j);
134             }
135         }
136         Log::warn("WorldWithRank]", "Kart %u is being given position %u,"
137                     "but this position is already taken",
138                     kart_id, position);
139         return false;
140     }
141     m_position_used[position-1] = true;
142 #endif
143     return true;
144 }   // setKartPosition
145 
146 //-----------------------------------------------------------------------------
147 /** Called once the last position was set. Note that we should not test
148  *  if all positions were set, since e.g. for eliminated and finished karts
149  *  the position won't be set anymore.
150  */
endSetKartPositions()151 void WorldWithRank::endSetKartPositions()
152 {
153 #ifdef DEBUG
154     assert(m_position_setting_initialised);
155     m_position_setting_initialised = false;
156 #endif
157 }   // endSetKartPositions
158 
159 //-----------------------------------------------------------------------------
160 /** Determines the rescue position for a kart. The rescue position is the
161  *  start position which is has the biggest accumulated distance to all other
162  *  karts, and which has no other kart very close. The latter avoids dropping
163  *  a kart on top of another kart. This is the method used
164  *  \param kart The kart that is going to be rescued.
165  *  \returns The index of the start position to which the rescued kart
166  *           should be moved to.
167  */
168 
getRescuePositionIndex(AbstractKart * kart)169 unsigned int WorldWithRank::getRescuePositionIndex(AbstractKart *kart)
170 {
171     const int start_spots_amount =
172                          Track::getCurrentTrack()->getNumberOfStartPositions();
173     assert(start_spots_amount > 0);
174 
175     float largest_accumulated_distance_found = -1;
176     int   furthest_id_found                  = -1;
177 
178     for(int n=0; n<start_spots_amount; n++)
179     {
180         const btTransform &s = getStartTransform(n);
181         const Vec3 &v=s.getOrigin();
182         float accumulated_distance = .0f;
183         bool spawn_point_clear = true;
184 
185         for(unsigned int k=0; k<getCurrentNumKarts(); k++)
186         {
187             if(kart->getWorldKartId()==k) continue;
188             float abs_distance2 = (getKart(k)->getXYZ()-v).length2();
189             const float CLEAR_SPAWN_RANGE2 = 5*5;
190             if( abs_distance2 < CLEAR_SPAWN_RANGE2)
191             {
192                 spawn_point_clear = false;
193                 break;
194             }
195             accumulated_distance += sqrt(abs_distance2);
196         }
197 
198         if(accumulated_distance > largest_accumulated_distance_found &&
199             spawn_point_clear)
200         {
201             furthest_id_found = n;
202             largest_accumulated_distance_found = accumulated_distance;
203         }
204     }
205 
206     assert(furthest_id_found != -1);
207     return furthest_id_found;
208 }   // getRescuePositionIndex
209 
210 //-----------------------------------------------------------------------------
211 /** Returns the number of points for a kart at a specified position.
212  *  \param p Position (starting with 1).
213  */
getScoreForPosition(int p)214 int WorldWithRank::getScoreForPosition(int p)
215 {
216     assert(p-1 >= 0);
217     assert(p - 1 <(int) m_score_for_position.size());
218     return m_score_for_position[p - 1];
219 }   // getScoreForPosition
220 
221 //-----------------------------------------------------------------------------
222 /** Returns true if the kart is on a valid graph quad.
223  *  \param kart_index  Index of the kart.
224  */
isOnRoad(unsigned int kart_index) const225 bool WorldWithRank::isOnRoad(unsigned int kart_index) const
226 {
227     return getTrackSector(kart_index)->isOnRoad();
228 }   // isOnRoad
229 
230 //-----------------------------------------------------------------------------
231 /** Gets the sector a kart is on. This function returns UNKNOWN_SECTOR if the
232  *  kart_id is larger than the current kart sector. This is necessary in the
233  *  case that a collision with the track happens during resetAllKarts: at this
234  *  time m_kart_track_sector is not initialised (and has size 0), so it would
235  *  trigger this assert. While this normally does not happen, it is useful for
236  *  track designers that STK does not crash.
237  *  \param kart Kart for which to return the sector.
238  */
getSectorForKart(const AbstractKart * kart) const239 int WorldWithRank::getSectorForKart(const AbstractKart *kart) const
240 {
241     if (kart->getWorldKartId() >= m_kart_track_sector.size())
242         return Graph::UNKNOWN_SECTOR;
243     return getTrackSector(kart->getWorldKartId())->getCurrentGraphNode();
244 }   // getSectorForKart
245 
246 //-----------------------------------------------------------------------------
247 /** Localize each kart on the graph using its center xyz.
248  */
updateSectorForKarts()249 void WorldWithRank::updateSectorForKarts()
250 {
251     if (isRaceOver()) return;
252 
253     const unsigned int n = getNumKarts();
254     assert(n == m_kart_track_sector.size());
255     for (unsigned int i = 0; i < n; i++)
256     {
257         SpareTireAI* sta =
258             dynamic_cast<SpareTireAI*>(m_karts[i]->getController());
259         if (!m_karts[i]->isEliminated() || (sta && sta->isMoving()))
260             getTrackSector(i)->update(m_karts[i]->getXYZ());
261     }
262 }   // updateSectorForKarts
263