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