1 //
2 //  SuperTuxKart - a fun racing game with go-kart
3 //  Copyright (C) 2006-2015 Joerg Henrichs
4 //
5 //  This program is free software; you can redistribute it and/or
6 //  modify it under the terms of the GNU General Public License
7 //  as published by the Free Software Foundation; either version 3
8 //  of the License, or (at your option) any later version.
9 //
10 //  This program is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program; if not, write to the Free Software
17 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 #include "items/projectile_manager.hpp"
20 
21 #include "graphics/explosion.hpp"
22 #include "graphics/hit_effect.hpp"
23 #include "items/bowling.hpp"
24 #include "items/cake.hpp"
25 #include "items/plunger.hpp"
26 #include "items/powerup_manager.hpp"
27 #include "items/powerup.hpp"
28 #include "items/rubber_ball.hpp"
29 #include "karts/abstract_kart.hpp"
30 #include "karts/controller/controller.hpp"
31 #include "modes/world.hpp"
32 #include "network/network_config.hpp"
33 #include "network/network_string.hpp"
34 #include "network/rewind_manager.hpp"
35 #include "utils/stk_process.hpp"
36 #include "utils/string_utils.hpp"
37 
38 #include <typeinfo>
39 
40 //=============================================================================================
41 ProjectileManager* g_projectile_manager[PT_COUNT];
42 //---------------------------------------------------------------------------------------------
get()43 ProjectileManager* ProjectileManager::get()
44 {
45     ProcessType type = STKProcess::getType();
46     return g_projectile_manager[type];
47 }   // get
48 
49 //---------------------------------------------------------------------------------------------
create()50 void ProjectileManager::create()
51 {
52     ProcessType type = STKProcess::getType();
53     g_projectile_manager[type] = new ProjectileManager();
54 }   // create
55 
56 //---------------------------------------------------------------------------------------------
destroy()57 void ProjectileManager::destroy()
58 {
59     ProcessType type = STKProcess::getType();
60     delete g_projectile_manager[type];
61     g_projectile_manager[type] = NULL;
62 }   // destroy
63 
64 //---------------------------------------------------------------------------------------------
clear()65 void ProjectileManager::clear()
66 {
67     memset(g_projectile_manager, 0, sizeof(g_projectile_manager));
68 }   // clear
69 
70 //---------------------------------------------------------------------------------------------
loadData()71 void ProjectileManager::loadData()
72 {
73 }   // loadData
74 
75 //-----------------------------------------------------------------------------
removeTextures()76 void ProjectileManager::removeTextures()
77 {
78     cleanup();
79     // Only the explosion is here, all other models are actually managed
80     // by powerup_manager.
81 }   // removeTextures
82 
83 //-----------------------------------------------------------------------------
cleanup()84 void ProjectileManager::cleanup()
85 {
86     m_active_projectiles.clear();
87     for(HitEffects::iterator i  = m_active_hit_effects.begin();
88         i != m_active_hit_effects.end(); ++i)
89     {
90         delete *i;
91     }
92 
93     m_active_hit_effects.clear();
94 }   // cleanup
95 
96 // -----------------------------------------------------------------------------
97 /** Called once per rendered frame. It is used to only update any graphical
98  *  effects, and calls updateGraphics in any flyable objects.
99  *  \param dt Time step size (since last call).
100  */
updateGraphics(float dt)101 void ProjectileManager::updateGraphics(float dt)
102 {
103     for (auto& p : m_active_projectiles)
104         p.second->updateGraphics(dt);
105 }   // updateGraphics
106 
107 // -----------------------------------------------------------------------------
108 /** General projectile update call. */
update(int ticks)109 void ProjectileManager::update(int ticks)
110 {
111     updateServer(ticks);
112 
113     if (RewindManager::get()->isRewinding())
114         return;
115     HitEffects::iterator he = m_active_hit_effects.begin();
116     while(he!=m_active_hit_effects.end())
117     {
118         // While this shouldn't happen, we had one crash because of this
119         if(!(*he))
120         {
121             HitEffects::iterator next = m_active_hit_effects.erase(he);
122             he = next;
123         }
124         // Update this hit effect. If it can be removed, remove it.
125         else if((*he)->updateAndDelete(ticks))
126         {
127             delete *he;
128             HitEffects::iterator next = m_active_hit_effects.erase(he);
129             he = next;
130         }   // if hit effect finished
131         else  // hit effect not finished, go to next one.
132             he++;
133     }   // while hit effect != end
134 }   // update
135 
136 // -----------------------------------------------------------------------------
137 /** Updates all rockets on the server (or no networking). */
updateServer(int ticks)138 void ProjectileManager::updateServer(int ticks)
139 {
140     auto p = m_active_projectiles.begin();
141     while (p != m_active_projectiles.end())
142     {
143         if (!p->second->hasServerState())
144         {
145             p++;
146             continue;
147         }
148         bool can_be_deleted = p->second->updateAndDelete(ticks);
149         if (can_be_deleted)
150         {
151             HitEffect* he = p->second->getHitEffect();
152             if (he)
153                 addHitEffect(he);
154 
155             p->second->onDeleteFlyable();
156             // Flyables will be deleted by computeError in client
157             if (!NetworkConfig::get()->isNetworking() ||
158                 NetworkConfig::get()->isServer())
159                 p = m_active_projectiles.erase(p);
160         }
161         else
162             p++;
163     }   // while p!=m_active_projectiles.end()
164 
165 }   // updateServer
166 
167 // -----------------------------------------------------------------------------
168 /** Creates a new projectile of the given type.
169  *  \param kart The kart which shoots the projectile.
170  *  \param type Type of projectile.
171  */
172 std::shared_ptr<Flyable>
newProjectile(AbstractKart * kart,PowerupManager::PowerupType type)173     ProjectileManager::newProjectile(AbstractKart *kart,
174                                      PowerupManager::PowerupType type)
175 {
176     const std::string& uid = getUniqueIdentity(kart, type);
177     auto it = m_active_projectiles.find(uid);
178     // Flyable has already created before and now rewinding, re-fire it
179     if (it != m_active_projectiles.end())
180     {
181         it->second->onFireFlyable();
182         return it->second;
183     }
184 
185     std::shared_ptr<Flyable> f;
186     switch(type)
187     {
188         case PowerupManager::POWERUP_BOWLING:
189             f = std::make_shared<Bowling>(kart);
190             break;
191         case PowerupManager::POWERUP_PLUNGER:
192             f = std::make_shared<Plunger>(kart);
193             break;
194         case PowerupManager::POWERUP_CAKE:
195             f = std::make_shared<Cake>(kart);
196             break;
197         case PowerupManager::POWERUP_RUBBERBALL:
198             f = std::make_shared<RubberBall>(kart);
199             break;
200         default:
201             return nullptr;
202     }
203     // This cannot be done in constructor because of virtual function
204     f->onFireFlyable();
205     m_active_projectiles[uid] = f;
206     if (RewindManager::get()->isEnabled())
207         f->addForRewind(uid);
208 
209     return f;
210 }   // newProjectile
211 
212 // -----------------------------------------------------------------------------
213 /** Returns true if a projectile is within the given distance of the specified
214  *  kart.
215  *  \param kart The kart for which the test is done.
216  *  \param radius Distance within which the projectile must be.
217 */
projectileIsClose(const AbstractKart * const kart,float radius)218 bool ProjectileManager::projectileIsClose(const AbstractKart * const kart,
219                                          float radius)
220 {
221     float r2 = radius * radius;
222     for (auto i = m_active_projectiles.begin(); i != m_active_projectiles.end(); i++)
223     {
224         if (!i->second->hasServerState())
225             continue;
226         float dist2 = i->second->getXYZ().distance2(kart->getXYZ());
227         if (dist2 < r2)
228             return true;
229     }
230     return false;
231 }   // projectileIsClose
232 
233 // -----------------------------------------------------------------------------
234 /** Returns an int containing the numbers of a given flyable in a given radius
235  *  around the kart
236  *  \param kart The kart for which the test is done.
237  *  \param radius Distance within which the projectile must be.
238  *  \param type The type of projectile checked
239 */
getNearbyProjectileCount(const AbstractKart * const kart,float radius,PowerupManager::PowerupType type,bool exclude_owned)240 int ProjectileManager::getNearbyProjectileCount(const AbstractKart * const kart,
241                                          float radius, PowerupManager::PowerupType type,
242                                          bool exclude_owned)
243 {
244     float r2 = radius * radius;
245     int projectile_count = 0;
246     for (auto i = m_active_projectiles.begin(); i != m_active_projectiles.end(); i++)
247     {
248         if (!i->second->hasServerState())
249             continue;
250         if (i->second->getType() == type)
251         {
252             if (exclude_owned && (i->second->getOwner() == kart))
253                 continue;
254 
255             float dist2 = i->second->getXYZ().distance2(kart->getXYZ());
256             if (dist2 < r2)
257             {
258                 projectile_count++;
259             }
260         }
261     }
262     return projectile_count;
263 }   // getNearbyProjectileCount
264 
265 // -----------------------------------------------------------------------------
getBasketballPositions()266 std::vector<Vec3> ProjectileManager::getBasketballPositions()
267 {
268     std::vector<Vec3> positions;
269     for (auto i = m_active_projectiles.begin(); i != m_active_projectiles.end(); i++)
270     {
271         if (!i->second->hasServerState())
272             continue;
273         if (i->second->getType() == PowerupManager::POWERUP_RUBBERBALL)
274             positions.emplace_back(i->second->getXYZ());
275     } // loop over projectiles
276 
277     return positions;
278 } // getBasketballPositions
279 // -----------------------------------------------------------------------------
getUniqueIdentity(AbstractKart * kart,PowerupManager::PowerupType t)280 std::string ProjectileManager::getUniqueIdentity(AbstractKart* kart,
281                                                  PowerupManager::PowerupType t)
282 {
283     BareNetworkString uid;
284     switch (t)
285     {
286         case PowerupManager::POWERUP_BOWLING:
287         {
288             uid.addUInt8(RN_BOWLING);
289             break;
290         }
291         case PowerupManager::POWERUP_PLUNGER:
292         {
293             uid.addUInt8(RN_PLUNGER);
294             break;
295         }
296         case PowerupManager::POWERUP_CAKE:
297         {
298             uid.addUInt8(RN_CAKE);
299             break;
300         }
301         case PowerupManager::POWERUP_RUBBERBALL:
302         {
303             uid.addUInt8(RN_RUBBERBALL);
304             break;
305         }
306         default:
307             assert(false);
308             return "";
309     }
310     uid.addUInt8((uint8_t)kart->getWorldKartId())
311         .addUInt32(World::getWorld()->getTicksSinceStart());
312     return std::string((char*)uid.getBuffer().data(), uid.getBuffer().size());
313 }   // getUniqueIdentity
314 
315 // -----------------------------------------------------------------------------
316 /* If any flyable is not found in current game state, create it with respect to
317  * its uid as below. */
318 std::shared_ptr<Rewinder>
addRewinderFromNetworkState(const std::string & uid)319           ProjectileManager::addRewinderFromNetworkState(const std::string& uid)
320 {
321     if (uid.size() != 6)
322         return nullptr;
323     BareNetworkString data(uid.data(), (int)uid.size());
324 
325     RewinderName rn = (RewinderName)data.getUInt8();
326     if (!(rn == RN_BOWLING || rn == RN_PLUNGER ||
327         rn == RN_CAKE || rn == RN_RUBBERBALL))
328         return nullptr;
329 
330     AbstractKart* kart = World::getWorld()->getKart(data.getUInt8());
331     int created_ticks = data.getUInt32();
332     std::shared_ptr<Flyable> f;
333     switch (rn)
334     {
335         case RN_BOWLING:
336         {
337             f = std::make_shared<Bowling>(kart);
338             break;
339         }
340         case RN_PLUNGER:
341         {
342             f = std::make_shared<Plunger>(kart);
343             break;
344         }
345         case RN_CAKE:
346         {
347             f = std::make_shared<Cake>(kart);
348             break;
349         }
350         case RN_RUBBERBALL:
351         {
352             f = std::make_shared<RubberBall>(kart);
353             break;
354         }
355         default:
356         {
357             break;
358         }
359     }
360     assert(f);
361     f->setCreatedTicks(created_ticks);
362     f->onFireFlyable();
363     f->addForRewind(uid);
364     Flyable* flyable = f.get();
365     Log::debug("ProjectileManager", "Missed a firing event, "
366         "add the flyable %s by %s created at %d manually.",
367         typeid(*flyable).name(),
368         StringUtils::wideToUtf8(kart->getController()->getName()).c_str(),
369         created_ticks);
370 
371     m_active_projectiles[uid] = f;
372     return f;
373 }   // addProjectileFromNetworkState
374 
375