1 //  SuperTuxKart - a fun racing game with go-kart
2 //  Copyright (C) 2004-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/overworld.hpp"
19 
20 #include "audio/music_manager.hpp"
21 #include "challenges/unlock_manager.hpp"
22 #include "config/player_manager.hpp"
23 #include "config/user_config.hpp"
24 #include "graphics/irr_driver.hpp"
25 #include "input/device_manager.hpp"
26 #include "input/input.hpp"
27 #include "input/input_manager.hpp"
28 #include "input/keyboard_device.hpp"
29 #include "karts/abstract_kart.hpp"
30 #include "karts/kart_properties.hpp"
31 #include "karts/kart_properties_manager.hpp"
32 #include "karts/rescue_animation.hpp"
33 #include "physics/btKart.hpp"
34 #include "physics/physics.hpp"
35 #include "states_screens/dialogs/select_challenge.hpp"
36 #include "states_screens/offline_kart_selection.hpp"
37 #include "states_screens/race_gui_overworld.hpp"
38 #include "tracks/track.hpp"
39 #include "tracks/track_object_manager.hpp"
40 
41 //-----------------------------------------------------------------------------
OverWorld()42 OverWorld::OverWorld() : World()
43 {
44     m_return_to_garage            = false;
45     m_stop_music_when_dialog_open = false;
46     m_play_track_intro_sound      = false;
47 }   // Overworld
48 
49 //-----------------------------------------------------------------------------
~OverWorld()50 OverWorld::~OverWorld()
51 {
52     Vec3 kart_xyz = getKart(0)->getXYZ();
53     RaceManager::get()->setKartLastPositionOnOverworld(kart_xyz);
54 }   // ~OverWorld
55 
56 //-----------------------------------------------------------------------------
57 /** Function to simplify the start process */
enterOverWorld()58 void OverWorld::enterOverWorld()
59 {
60     // update point count and the list of locked/unlocked stuff
61     PlayerManager::getCurrentPlayer()->computeActive();
62 
63     RaceManager::get()->setNumPlayers(1);
64     RaceManager::get()->setMajorMode (RaceManager::MAJOR_MODE_SINGLE);
65     RaceManager::get()->setMinorMode (RaceManager::MINOR_MODE_OVERWORLD);
66     RaceManager::get()->setNumKarts( 1 );
67     RaceManager::get()->setTrack( "overworld" );
68 
69     if (PlayerManager::getCurrentPlayer()->isLocked("difficulty_best"))
70     {
71         RaceManager::get()->setDifficulty(RaceManager::DIFFICULTY_HARD);
72     }
73     else
74     {
75         RaceManager::get()->setDifficulty(RaceManager::DIFFICULTY_BEST);
76     }
77 
78     // Use keyboard 0 by default (FIXME: let player choose?)
79     InputDevice* device = input_manager->getDeviceManager()->getKeyboard(0);
80 
81     // Create player and associate player with keyboard
82     StateManager::get()->createActivePlayer(PlayerManager::getCurrentPlayer(),
83                                             device);
84 
85     if (!kart_properties_manager->getKart(UserConfigParams::m_default_kart))
86     {
87         Log::warn("[overworld]", "cannot find kart '%s', "
88                   "will revert to default",
89                   UserConfigParams::m_default_kart.c_str());
90 
91         UserConfigParams::m_default_kart.revertToDefaults();
92     }
93     RaceManager::get()->setPlayerKart(0, UserConfigParams::m_default_kart);
94 
95     // ASSIGN should make sure that only input from assigned devices
96     // is read.
97     input_manager->getDeviceManager()->setAssignMode(ASSIGN);
98     input_manager->getDeviceManager()
99         ->setSinglePlayer( StateManager::get()->getActivePlayer(0) );
100 
101     StateManager::get()->enterGameState();
102     RaceManager::get()->setupPlayerKartInfo();
103     RaceManager::get()->startNew(false);
104     if(RaceManager::get()->haveKartLastPositionOnOverworld()){
105             OverWorld *ow = (OverWorld*)World::getWorld();
106             ow->getKart(0)->setXYZ(RaceManager::get()->getKartLastPositionOnOverworld());
107             ow->moveKartAfterRescue(ow->getKart(0));
108         }
109     irr_driver->showPointer(); // User should be able to click on the minimap
110 
111 }   // enterOverWorld
112 
113 //-----------------------------------------------------------------------------
114 /** General update function called once per frame.
115  *  \param ticks Number of physics time steps - should be 1.
116  */
update(int ticks)117 void OverWorld::update(int ticks)
118 {
119     // Skip annoying waiting without a purpose
120     // Make sure to do all things that would normally happen in the
121     // update() method of the base classes.
122     if (getPhase() < GO_PHASE)
123     {
124         setPhase(RACE_PHASE);
125         // Normally done in WorldStatus::update(), during phase SET_PHASE,
126         // so we have to start music 'manually', since we skip all phases.
127         Track::getCurrentTrack()->startMusic();
128 
129         if (UserConfigParams::m_music)
130             music_manager->startMusic();
131         m_karts[0]->startEngineSFX();
132     }
133     World::update(ticks);
134     World::updateTrack(ticks);
135     const unsigned int kart_amount  = (unsigned int)m_karts.size();
136 
137     // isn't it cool, on the overworld nitro is free!
138     for(unsigned int n=0; n<kart_amount; n++)
139     {
140         m_karts[n]->setEnergy(100.0f);
141     }
142 
143     /*
144     TrackObjectManager* tom = getTrack()->getTrackObjectManager();
145     PtrVector<TrackObject>& objects = tom->getObjects();
146     for(unsigned int i=0; i<objects.size(); i++)
147     {
148         TrackObject* obj = objects.get(i);
149         if(!obj->isGarage())
150             continue;
151 
152         float m_distance = obj->getDistance();
153         Vec3 m_garage_pos = obj->getPosition();
154         Vec3 m_kart_pos = getKart(0)->getXYZ();
155 
156         if ((m_garage_pos-m_kart_pos).length_2d() > m_distance)
157         {
158             obj->reset();
159         }
160     }
161     */
162 
163     if (m_return_to_garage)
164     {
165         m_return_to_garage = false;
166         RaceManager::get()->exitRace();
167         KartSelectionScreen* s = OfflineKartSelectionScreen::getInstance();
168         s->setMultiplayer(false);
169         s->setFromOverworld(true);
170         StateManager::get()->resetAndGoToScreen(s);
171         throw AbortWorldUpdateException();
172     }
173 }   // update
174 
175 // ----------------------------------------------------------------------------
176 /** Finds the starting position which is closest to the kart.
177  *  \param kart The kart for which a rescue position needs to be determined.
178  */
getRescuePositionIndex(AbstractKart * kart)179 unsigned int OverWorld::getRescuePositionIndex(AbstractKart *kart)
180 {
181     // find closest point to drop kart on
182     const int start_spots_amount = getNumberOfRescuePositions();
183     assert(start_spots_amount > 0);
184 
185     int closest_id = -1;
186     float closest_distance = 999999999.0f;
187 
188     for (int n=0; n<start_spots_amount; n++)
189     {
190         const btTransform &s = getStartTransform(n);
191         const Vec3 &v = s.getOrigin();
192 
193         float abs_distance = (v - kart->getXYZ()).length();
194 
195         if (abs_distance < closest_distance)
196         {
197             closest_distance = abs_distance;
198             closest_id = n;
199         }
200     }
201 
202     assert(closest_id != -1);
203     return closest_id;
204 }   // getRescuePositionIndex
205 
206 //-----------------------------------------------------------------------------
207 /** This function is not used in the overworld race gui.
208  */
getKartsDisplayInfo(std::vector<RaceGUIBase::KartIconDisplayInfo> * info)209 void OverWorld::getKartsDisplayInfo(
210                        std::vector<RaceGUIBase::KartIconDisplayInfo> *info)
211 {
212     assert(false);
213 }   // getKartsDisplayInfo
214 
215 //-----------------------------------------------------------------------------
216 
createRaceGUI()217 void OverWorld::createRaceGUI()
218 {
219     m_race_gui = new RaceGUIOverworld();
220 }   // createRaceGUI
221 
222 //-----------------------------------------------------------------------------
223 
onFirePressed(Controller * who)224 void OverWorld::onFirePressed(Controller* who)
225 {
226     const std::vector<OverworldChallenge>& challenges =
227                                   Track::getCurrentTrack()->getChallengeList();
228 
229     AbstractKart* k = getKart(0);
230     Vec3 kart_xyz = k->getXYZ();
231     if (dynamic_cast<RescueAnimation*>(k->getKartAnimation()) != NULL)
232     {
233         // you can't start a race while being rescued
234         return;
235     }
236 
237     for (unsigned int n=0; n<challenges.size(); n++)
238     {
239         if ( (kart_xyz - Vec3(challenges[n].m_position)).length2_2d()
240               < CHALLENGE_DISTANCE_SQUARED)
241         {
242             if (challenges[n].m_challenge_id == "tutorial")
243             {
244                 scheduleTutorial();
245                 return;
246             }
247             else
248             {
249                 const ChallengeData* challenge = unlock_manager->getChallengeData(challenges[n].m_challenge_id);
250                 if (challenge == NULL)
251                 {
252                     Log::error("track", "Cannot find challenge named '%s'\n",
253                         challenges[n].m_challenge_id.c_str());
254                     continue;
255                 }
256 
257                 const unsigned int val = challenge->getNumTrophies();
258 // Mobile STK may have less challenges available than the main version
259 #ifdef MOBILE_STK
260                 bool enough_challenges = true;
261 #else
262                 const unsigned int val2 = challenge->getNumChallenges();
263                 bool enough_challenges = (PlayerManager::getCurrentPlayer()->getNumCompletedChallenges() >= val2);
264 #endif
265                 bool unlocked = enough_challenges && (PlayerManager::getCurrentPlayer()->getPoints() >= val);
266 
267                 if (UserConfigParams::m_unlock_everything > 0)
268                     unlocked = true;
269 
270                 if (unlocked)
271                 {
272                     RaceManager::get()->setKartLastPositionOnOverworld(kart_xyz);
273                     new SelectChallengeDialog(0.9f, 0.9f,
274                         challenges[n].m_challenge_id);
275                 }
276             }
277         } // end if
278     } // end for
279 }   // onFirePressed
280 
281 //-----------------------------------------------------------------------------
282 /** Called when a mouse click happens. If the click happened while the mouse
283  *  was hovering on top of a challenge, the kart will be teleported to
284  *  the challenge.
285  *  \param x,y Mouse coordinates.
286  */
onMouseClick(int x,int y)287 void OverWorld::onMouseClick(int x, int y)
288 {
289     const OverworldChallenge *challenge =
290         ((RaceGUIOverworld*)getRaceGUI())->getCurrentChallenge();
291 
292     if(challenge)
293     {
294         // Use the 'get closest start point' rescue function
295         // from World by setting the kart's position to
296         // be the location of the challenge bubble.
297         AbstractKart* kart = getKart(0);
298         kart->setXYZ(challenge->m_position);
299         kart->getVehicle()->setMaxSpeed(0);
300 
301         unsigned int index   = getRescuePositionIndex(kart);
302         btTransform s        = getRescueTransform(index);
303         const btVector3 &xyz = s.getOrigin();
304         float angle          = atan2(challenge->m_position.X - xyz[0],
305                                      challenge->m_position.Z - xyz[2]);
306         s.setRotation( btQuaternion(btVector3(0.0f, 1.0f, 0.0f), angle) );
307         moveKartTo(kart, s);
308         return;
309     }
310 }  // onMouseClick
311