1 //  SuperTuxKart - a fun racing game with go-kart
2 //  Copyright (C) 2018 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/capture_the_flag.hpp"
19 #include "audio/sfx_base.hpp"
20 #include "io/file_manager.hpp"
21 #include "items/powerup.hpp"
22 #include "graphics/irr_driver.hpp"
23 #include "guiengine/engine.hpp"
24 #include "karts/abstract_kart.hpp"
25 #include "karts/controller/controller.hpp"
26 #include "karts/kart_model.hpp"
27 #include "modes/ctf_flag.hpp"
28 #include "network/network_config.hpp"
29 #include "network/network_string.hpp"
30 #include "network/protocols/game_events_protocol.hpp"
31 #include "network/server_config.hpp"
32 #include "network/stk_host.hpp"
33 #include "physics/triangle_mesh.hpp"
34 #include "states_screens/race_gui.hpp"
35 #include "tracks/track.hpp"
36 #include "tracks/track_object_manager.hpp"
37 #include "utils/string_utils.hpp"
38 #include "utils/translation.hpp"
39 
40 #include <algorithm>
41 
42 const float g_capture_length = 2.0f;
43 const int g_captured_score = 10;
44 
45 // ----------------------------------------------------------------------------
46 /** Constructor. Sets up the clock mode etc.
47  */
CaptureTheFlag()48 CaptureTheFlag::CaptureTheFlag() : FreeForAll()
49 {
50     m_red_flag_node = m_blue_flag_node = NULL;
51     m_red_flag_indicator = m_blue_flag_indicator = NULL;
52     m_red_flag_mesh = m_blue_flag_mesh = NULL;
53     m_scored_sound = NULL;
54 #ifndef SERVER_ONLY
55     if (GUIEngine::isNoGraphics())
56         return;
57 
58     file_manager->pushTextureSearchPath(
59         file_manager->getAsset(FileManager::MODEL,""), "models");
60     m_red_flag_mesh = irr_driver->getAnimatedMesh
61         (file_manager->getAsset(FileManager::MODEL, "red_flag.spm"));
62     m_blue_flag_mesh = irr_driver->getAnimatedMesh
63         (file_manager->getAsset(FileManager::MODEL, "blue_flag.spm"));
64     assert(m_red_flag_mesh);
65     assert(m_blue_flag_mesh);
66     irr_driver->grabAllTextures(m_red_flag_mesh);
67     irr_driver->grabAllTextures(m_blue_flag_mesh);
68     file_manager->popTextureSearchPath();
69     m_scored_sound = SFXManager::get()->createSoundSource("goal_scored");
70 #endif
71 }   // CaptureTheFlag
72 
73 // ----------------------------------------------------------------------------
~CaptureTheFlag()74 CaptureTheFlag::~CaptureTheFlag()
75 {
76 #ifndef SERVER_ONLY
77     if (GUIEngine::isNoGraphics())
78         return;
79 
80     m_red_flag_node->drop();
81     m_blue_flag_node->drop();
82     irr_driver->dropAllTextures(m_red_flag_mesh);
83     irr_driver->dropAllTextures(m_blue_flag_mesh);
84     irr_driver->removeMeshFromCache(m_red_flag_mesh);
85     irr_driver->removeMeshFromCache(m_blue_flag_mesh);
86     m_scored_sound->deleteSFX();
87 #endif
88 }   // ~CaptureTheFlag
89 
90 // ----------------------------------------------------------------------------
init()91 void CaptureTheFlag::init()
92 {
93     FreeForAll::init();
94     const btTransform& orig_red = Track::getCurrentTrack()->getRedFlag();
95     const btTransform& orig_blue = Track::getCurrentTrack()->getBlueFlag();
96 
97     m_red_flag = std::make_shared<CTFFlag>(FC_RED, orig_red);
98     m_blue_flag = std::make_shared<CTFFlag>(FC_BLUE, orig_blue);
99     if (NetworkConfig::get()->isNetworking())
100     {
101         m_red_flag->rewinderAdd();
102         m_blue_flag->rewinderAdd();
103     }
104 
105 #ifndef SERVER_ONLY
106     if (GUIEngine::isNoGraphics())
107         return;
108 
109     m_red_flag_node = irr_driver->addAnimatedMesh(m_red_flag_mesh, "red_flag");
110     m_blue_flag_node = irr_driver->addAnimatedMesh(m_blue_flag_mesh,
111         "blue_flag");
112     assert(m_red_flag_node);
113     assert(m_blue_flag_node);
114     m_red_flag_node->grab();
115     m_blue_flag_node->grab();
116 
117     std::string red_path =
118         file_manager->getAsset(FileManager::GUI_ICON, "red_arrow.png");
119     std::string blue_path =
120         file_manager->getAsset(FileManager::GUI_ICON, "blue_arrow.png");
121 
122     m_red_flag_indicator = irr_driver->addBillboard(
123         core::dimension2df(1.5f, 1.5f), red_path, NULL);
124     m_red_flag_indicator->setPosition(Vec3(
125         orig_red(Vec3(0.0f, 2.5f, 0.0f))).toIrrVector());
126     m_blue_flag_indicator = irr_driver->addBillboard(
127         core::dimension2df(1.5f, 1.5f), blue_path, NULL);
128     m_blue_flag_indicator->setPosition(Vec3(
129         orig_blue(Vec3(0.0f, 2.5f, 0.0f))).toIrrVector());
130 
131     m_red_flag->initFlagRenderInfo(m_red_flag_node);
132     m_blue_flag->initFlagRenderInfo(m_blue_flag_node);
133 #endif
134 }   // init
135 
136 // ----------------------------------------------------------------------------
reset(bool restart)137 void CaptureTheFlag::reset(bool restart)
138 {
139     // 5 bits for kart id (with -1 and -2 flag status)
140     if (m_karts.size() > 29)
141         Log::fatal("CaptureTheFlag", "Too many karts");
142 
143     FreeForAll::reset(restart);
144     m_red_scores = m_blue_scores = 0;
145     m_swatter_reset_kart_ticks.clear();
146     m_last_captured_flag_ticks = 0;
147     m_red_flag_status = m_blue_flag_status = CTFFlag::IN_BASE;
148     m_red_flag->resetToBase();
149     m_blue_flag->resetToBase();
150 #ifndef SERVER_ONLY
151     if (GUIEngine::isNoGraphics())
152         return;
153 
154     if (m_red_flag_node)
155         m_red_flag->updateFlagGraphics(m_red_flag_node);
156     if (m_blue_flag_node)
157         m_blue_flag->updateFlagGraphics(m_blue_flag_node);
158 #endif
159 }   // reset
160 
161 // ----------------------------------------------------------------------------
updateGraphics(float dt)162 void CaptureTheFlag::updateGraphics(float dt)
163 {
164     FreeForAll::updateGraphics(dt);
165 
166 #ifndef SERVER_ONLY
167     if (m_red_flag_node)
168         m_red_flag->updateFlagGraphics(m_red_flag_node);
169     if (m_blue_flag_node)
170         m_blue_flag->updateFlagGraphics(m_blue_flag_node);
171     if (m_red_flag_indicator)
172         m_red_flag_indicator->setVisible(!m_red_flag->isInBase());
173     if (m_blue_flag_indicator)
174         m_blue_flag_indicator->setVisible(!m_blue_flag->isInBase());
175 
176     core::stringw msg;
177     // Don't show flag has been returned message if
178     // a point has been scored recently
179     const bool scored_recently =
180         getTicksSinceStart() > m_last_captured_flag_ticks &&
181         getTicksSinceStart() - m_last_captured_flag_ticks < stk_config->time2Ticks(2.0f);
182     if (m_red_flag_status != m_red_flag->getStatus())
183     {
184         if (m_red_flag->getHolder() != -1)
185         {
186             AbstractKart* kart = getKart(m_red_flag->getHolder());
187             const core::stringw& name = kart->getController()->getName();
188             // I18N: Show when a player gets the red flag in CTF
189             msg = _("%s has the red flag!", name);
190             if (kart->getController()->isLocalPlayerController())
191                 SFXManager::get()->quickSound("wee");
192         }
193         else if (m_red_flag->isInBase() && !scored_recently)
194         {
195             // I18N: Show when the red flag is returned to its base in CTF
196             msg = _("The red flag has returned!");
197         }
198         m_red_flag_status = m_red_flag->getStatus();
199     }
200     else if (m_blue_flag_status != m_blue_flag->getStatus())
201     {
202         if (m_blue_flag->getHolder() != -1)
203         {
204             AbstractKart* kart = getKart(m_blue_flag->getHolder());
205             const core::stringw& name = kart->getController()->getName();
206             // I18N: Show when a player gets the blue flag in CTF
207             msg = _("%s has the blue flag!", name);
208             if (kart->getController()->isLocalPlayerController())
209                 SFXManager::get()->quickSound("wee");
210         }
211         else if (m_blue_flag->isInBase() && !scored_recently)
212         {
213             // I18N: Show when the blue flag is returned to its base in CTF
214             msg = _("The blue flag has returned!");
215         }
216         m_blue_flag_status = m_blue_flag->getStatus();
217     }
218     if (m_race_gui && !msg.empty())
219         m_race_gui->addMessage(msg, NULL, 1.5f);
220 #endif
221 }   // updateGraphics
222 
223 // ----------------------------------------------------------------------------
update(int ticks)224 void CaptureTheFlag::update(int ticks)
225 {
226     FreeForAll::update(ticks);
227 
228     for (auto it = m_swatter_reset_kart_ticks.begin();
229          it != m_swatter_reset_kart_ticks.end();)
230     {
231         if (it->second < getTicksSinceStart() - stk_config->time2Ticks(8.0f))
232         {
233             it = m_swatter_reset_kart_ticks.erase(it);
234         }
235         else
236         {
237             if (it->second == getTicksSinceStart())
238             {
239                 AbstractKart* kart = m_karts[it->first].get();
240                 if (kart->isEliminated() || !kart->isSquashed())
241                 {
242                     it++;
243                     continue;
244                 }
245                 unsigned int index = getRescuePositionIndex(kart);
246                 btTransform t = getRescueTransform(index);
247                 t.setOrigin(t.getOrigin() + t.getBasis().getColumn(1) * 3.0f);
248                 kart->getBody()->setLinearVelocity(Vec3(0.0f));
249                 kart->getBody()->setAngularVelocity(Vec3(0.0f));
250                 kart->getBody()->proceedToTransform(t);
251                 kart->setTrans(t);
252                 kart->getPowerup()->reset();
253                 static_cast<SmoothNetworkBody*>(kart)->reset();
254             }
255             it++;
256         }
257     }
258 
259     // Update new flags position
260     m_red_flag->update(ticks);
261     m_blue_flag->update(ticks);
262 
263     if (m_red_flag->getHolder() != -1 && m_blue_flag->isInBase() &&
264         (m_blue_flag->getBaseOrigin() - m_red_flag->getOrigin()).length() <
265         g_capture_length)
266     {
267         // Blue team scored
268         if (!NetworkConfig::get()->isNetworking() ||
269             NetworkConfig::get()->isServer())
270         {
271             int red_holder = m_red_flag->getHolder();
272             int new_kart_scores = m_scores.at(red_holder) + g_captured_score;
273             int new_blue_scores = m_blue_scores + 1;
274             m_scores.at(red_holder) = new_kart_scores;
275             if (NetworkConfig::get()->isServer())
276             {
277                 NetworkString p(PROTOCOL_GAME_EVENTS);
278                 p.setSynchronous(true);
279                 p.addUInt8(GameEventsProtocol::GE_CTF_SCORED)
280                     .addUInt8((int8_t)red_holder)
281                     .addUInt8(0/*red_team_scored*/)
282                     .addUInt16((int16_t)new_kart_scores)
283                     .addUInt8((uint8_t)m_red_scores)
284                     .addUInt8((uint8_t)new_blue_scores);
285                 STKHost::get()->sendPacketToAllPeers(&p, true);
286             }
287             ctfScored(red_holder, false/*red_team_scored*/, new_kart_scores,
288                 m_red_scores, new_blue_scores);
289         }
290         m_last_captured_flag_ticks = World::getWorld()->getTicksSinceStart();
291         m_red_flag->resetToBase(RaceManager::get()->getFlagDeactivatedTicks());
292     }
293     else if (m_blue_flag->getHolder() != -1 && m_red_flag->isInBase() &&
294         (m_red_flag->getBaseOrigin() - m_blue_flag->getOrigin()).length() <
295         g_capture_length)
296     {
297         // Red team scored
298         if (!NetworkConfig::get()->isNetworking() ||
299             NetworkConfig::get()->isServer())
300         {
301             int blue_holder = m_blue_flag->getHolder();
302             int new_kart_scores = m_scores.at(blue_holder) + g_captured_score;
303             int new_red_scores = m_red_scores + 1;
304             m_scores.at(blue_holder) = new_kart_scores;
305             if (NetworkConfig::get()->isServer())
306             {
307                 NetworkString p(PROTOCOL_GAME_EVENTS);
308                 p.setSynchronous(true);
309                 p.addUInt8(GameEventsProtocol::GE_CTF_SCORED)
310                     .addUInt8((int8_t)blue_holder)
311                     .addUInt8(1/*red_team_scored*/)
312                     .addUInt16((int16_t)new_kart_scores)
313                     .addUInt8((uint8_t)new_red_scores)
314                     .addUInt8((uint8_t)m_blue_scores);
315                 STKHost::get()->sendPacketToAllPeers(&p, true);
316             }
317             ctfScored(blue_holder, true/*red_team_scored*/, new_kart_scores,
318                 new_red_scores, m_blue_scores);
319         }
320         m_last_captured_flag_ticks = World::getWorld()->getTicksSinceStart();
321         m_blue_flag->resetToBase(RaceManager::get()->getFlagDeactivatedTicks());
322     }
323 
324     // Test if red or blue flag is touched
325     for (auto& k : m_karts)
326     {
327         if (k->isEliminated() || k->getKartAnimation() || k->isSquashed())
328             continue;
329 
330         if (m_red_flag->canBeCaptured() &&
331             (k->getXYZ() - m_red_flag->getOrigin()).length() <
332             g_capture_length)
333         {
334             uint8_t kart_id = (uint8_t)k->getWorldKartId();
335             if (getKartTeam(kart_id) == KART_TEAM_RED)
336             {
337                 if (!m_red_flag->isInBase())
338                 {
339                     // Return the flag
340                     m_red_flag->resetToBase(
341                         RaceManager::get()->getFlagDeactivatedTicks());
342                 }
343             }
344             else
345             {
346                 // Get the flag
347                 m_red_flag->setCapturedByKart(kart_id);
348             }
349         }
350         if (m_blue_flag->canBeCaptured() &&
351             (k->getXYZ() - m_blue_flag->getOrigin()).length() <
352             g_capture_length)
353         {
354             uint8_t kart_id = (uint8_t)k->getWorldKartId();
355             if (getKartTeam(kart_id) == KART_TEAM_BLUE)
356             {
357                 if (!m_blue_flag->isInBase())
358                 {
359                     // Return the flag
360                     m_blue_flag->resetToBase(
361                         RaceManager::get()->getFlagDeactivatedTicks());
362                 }
363             }
364             else
365             {
366                 // Get the flag
367                 m_blue_flag->setCapturedByKart(kart_id);
368             }
369         }
370     }
371 }   // update
372 
373 // ----------------------------------------------------------------------------
getRedHolder() const374 int CaptureTheFlag::getRedHolder() const
375 {
376     return m_red_flag->getHolder();
377 }   // getRedHolder
378 
379 // ----------------------------------------------------------------------------
getBlueHolder() const380 int CaptureTheFlag::getBlueHolder() const
381 {
382     return m_blue_flag->getHolder();
383 }   // getBlueHolder
384 
385 // ----------------------------------------------------------------------------
isRedFlagInBase() const386 bool CaptureTheFlag::isRedFlagInBase() const
387 {
388     return m_red_flag->isInBase();
389 }   // isRedFlagInBase
390 
391 // ----------------------------------------------------------------------------
isBlueFlagInBase() const392 bool CaptureTheFlag::isBlueFlagInBase() const
393 {
394     return m_blue_flag->isInBase();
395 }   // isBlueFlagInBase
396 
397 // ----------------------------------------------------------------------------
getRedFlag() const398 const Vec3& CaptureTheFlag::getRedFlag() const
399 {
400     return m_red_flag->getOrigin();
401 }   // getRedFlag
402 
403 // ----------------------------------------------------------------------------
getBlueFlag() const404 const Vec3& CaptureTheFlag::getBlueFlag() const
405 {
406     return m_blue_flag->getOrigin();
407 }   // getBlueFlag
408 
409 // ----------------------------------------------------------------------------
ctfScored(int kart_id,bool red_team_scored,int new_kart_score,int new_red_score,int new_blue_score)410 void CaptureTheFlag::ctfScored(int kart_id, bool red_team_scored,
411                                int new_kart_score, int new_red_score,
412                                int new_blue_score)
413 {
414     m_scores.at(kart_id) = new_kart_score;
415     AbstractKart* kart = getKart(kart_id);
416     core::stringw scored_msg;
417     const core::stringw& name = kart->getController()->getName();
418     m_red_scores = new_red_score;
419     m_blue_scores = new_blue_score;
420     if (red_team_scored)
421     {
422         scored_msg = _("%s captured the blue flag!", name);
423     }
424     else
425     {
426         scored_msg = _("%s captured the red flag!", name);
427     }
428 #ifndef SERVER_ONLY
429     // Don't set animation and show message if receiving in live join
430     if (isStartPhase() || GUIEngine::isNoGraphics())
431         return;
432     if (m_race_gui)
433         m_race_gui->addMessage(scored_msg, NULL, 3.0f);
434     kart->getKartModel()
435         ->setAnimation(KartModel::AF_WIN_START, true/*play_non_loop*/);
436     m_scored_sound->play();
437 #endif
438 }   // ctfScored
439 
440 // ----------------------------------------------------------------------------
getDroppedFlagTrans(const btTransform & kt,btTransform * out) const441 bool CaptureTheFlag::getDroppedFlagTrans(const btTransform& kt,
442                                          btTransform* out) const
443 {
444     Vec3 from = kt.getOrigin() + kt.getBasis().getColumn(1);
445     Vec3 to = kt.getOrigin() + kt.getBasis().getColumn(1) * -10000.0f;
446     Vec3 hit_point, normal;
447     const Material* material = NULL;
448 
449     // From TerrainInfo::update
450     const TriangleMesh &tm = Track::getCurrentTrack()->getTriangleMesh();
451     bool ret = tm.castRay(from, to, &hit_point, &material, &normal,
452         /*interpolate*/false);
453     if (!ret || material == NULL)
454         return false;
455 
456     Track::getCurrentTrack()->getTrackObjectManager()->castRay
457         (from, to, &hit_point, &material, &normal, /*interpolate*/false);
458     *out = btTransform(shortestArcQuat(Vec3(0, 1, 0), normal),
459         hit_point);
460     return true;
461 }   // getDroppedFlagTrans
462 
463 // ----------------------------------------------------------------------------
getColor(unsigned int kart_id) const464 video::SColor CaptureTheFlag::getColor(unsigned int kart_id) const
465 {
466     return getKartTeam(kart_id) == KART_TEAM_RED ?
467         video::SColor(255, 255, 0, 0) : video::SColor(255, 0, 0, 255);
468 }   // getColor
469 
470 // ----------------------------------------------------------------------------
isRaceOver()471 bool CaptureTheFlag::isRaceOver()
472 {
473     if (m_unfair_team)
474         return true;
475 
476     if (NetworkConfig::get()->isNetworking() &&
477         NetworkConfig::get()->isClient())
478         return false;
479 
480     if ((m_count_down_reached_zero && RaceManager::get()->hasTimeTarget()) ||
481         (m_red_scores >= RaceManager::get()->getHitCaptureLimit() ||
482         m_blue_scores >= RaceManager::get()->getHitCaptureLimit()))
483         return true;
484 
485     return false;
486 }   // isRaceOver
487 
488 // ----------------------------------------------------------------------------
loseFlagForKart(int kart_id)489 void CaptureTheFlag::loseFlagForKart(int kart_id)
490 {
491     if (!(m_red_flag->getHolder() == kart_id ||
492         m_blue_flag->getHolder() == kart_id))
493         return;
494 
495     bool drop_red_flag = m_red_flag->getHolder() == kart_id;
496     btTransform dropped_trans = drop_red_flag ?
497         m_red_flag->getBaseTrans() :
498         m_blue_flag->getBaseTrans();
499     bool succeed = getDroppedFlagTrans(getKart(kart_id)->getTrans(),
500         &dropped_trans);
501     if (drop_red_flag)
502     {
503         if (succeed)
504             m_red_flag->dropFlagAt(dropped_trans);
505         else
506         {
507             m_red_flag->resetToBase(
508                 RaceManager::get()->getFlagDeactivatedTicks());
509         }
510     }
511     else
512     {
513         if (succeed)
514             m_blue_flag->dropFlagAt(dropped_trans);
515         else
516         {
517             m_blue_flag->resetToBase(
518                 RaceManager::get()->getFlagDeactivatedTicks());
519         }
520     }
521 }   // loseFlagForKart
522 
523 // ----------------------------------------------------------------------------
kartHit(int kart_id,int hitter)524 bool CaptureTheFlag::kartHit(int kart_id, int hitter)
525 {
526     if (isRaceOver())
527         return false;
528 
529     if (!NetworkConfig::get()->isNetworking() ||
530         NetworkConfig::get()->isServer())
531         handleScoreInServer(kart_id, hitter);
532 
533     loseFlagForKart(kart_id);
534     return true;
535 }   // kartHit
536 
537 //-----------------------------------------------------------------------------
getRescuePositionIndex(AbstractKart * kart)538 unsigned int CaptureTheFlag::getRescuePositionIndex(AbstractKart *kart)
539 {
540     return m_kart_position_map.at(kart->getWorldKartId());
541 }   // getRescuePositionIndex
542 
543 // ----------------------------------------------------------------------------
544 /** Returns the internal identifier for this race.
545  */
getIdent() const546 const std::string& CaptureTheFlag::getIdent() const
547 {
548     return IDENT_CTF;
549 }   // getIdent
550 
551 // ----------------------------------------------------------------------------
saveCompleteState(BareNetworkString * bns,STKPeer * peer)552 void CaptureTheFlag::saveCompleteState(BareNetworkString* bns, STKPeer* peer)
553 {
554     FreeForAll::saveCompleteState(bns, peer);
555     bns->addUInt32(m_red_scores).addUInt32(m_blue_scores);
556 }   // saveCompleteState
557 
558 // ----------------------------------------------------------------------------
restoreCompleteState(const BareNetworkString & b)559 void CaptureTheFlag::restoreCompleteState(const BareNetworkString& b)
560 {
561     FreeForAll::restoreCompleteState(b);
562     m_red_scores = b.getUInt32();
563     m_blue_scores = b.getUInt32();
564 }   // restoreCompleteState
565