diff --git a/sources.cmake b/sources.cmake index ba4868d71..d4f28ae4d 100644 --- a/sources.cmake +++ b/sources.cmake @@ -1,5 +1,5 @@ # Modify this file to change the last-modified date when you add/remove a file. -# This will then trigger a new cmake run automatically. +# This will then trigger a new cmake run automatically. file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp") file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp") file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*") diff --git a/src/modes/capture_the_flag.cpp b/src/modes/capture_the_flag.cpp index d54138208..c7529a6fc 100644 --- a/src/modes/capture_the_flag.cpp +++ b/src/modes/capture_the_flag.cpp @@ -23,9 +23,12 @@ #include "karts/abstract_kart.hpp" #include "karts/controller/controller.hpp" #include "karts/kart_model.hpp" +#include "modes/ctf_flag.hpp" #include "network/network_config.hpp" #include "network/network_string.hpp" #include "network/protocols/game_events_protocol.hpp" +#include "network/rewind_info.hpp" +#include "network/rewind_manager.hpp" #include "network/server_config.hpp" #include "network/stk_host.hpp" #include "physics/triangle_mesh.hpp" @@ -35,8 +38,7 @@ #include -const Vec3 g_kart_flag_offset(0.0, 0.2f, -0.5f); -const float g_capture_length = 3.0f; +const float g_capture_length = 2.0f; const int g_captured_score = 10; // ---------------------------------------------------------------------------- @@ -80,8 +82,15 @@ CaptureTheFlag::~CaptureTheFlag() void CaptureTheFlag::init() { FreeForAll::init(); - m_orig_red_trans = Track::getCurrentTrack()->getRedFlag(); - m_orig_blue_trans = Track::getCurrentTrack()->getBlueFlag(); + const btTransform& orig_red = Track::getCurrentTrack()->getRedFlag(); + const btTransform& orig_blue = Track::getCurrentTrack()->getBlueFlag(); + m_red_flag = std::make_shared(FC_RED, orig_red); + m_blue_flag = std::make_shared(FC_BLUE, orig_blue); + if (NetworkConfig::get()->isNetworking()) + { + m_red_flag->rewinderAdd(); + m_blue_flag->rewinderAdd(); + } #ifndef SERVER_ONLY m_red_flag_node = irr_driver->addAnimatedMesh(m_red_flag_mesh, "red_flag"); @@ -96,13 +105,13 @@ void CaptureTheFlag::init() file_manager->getAsset(FileManager::GUI_ICON, "blue_arrow.png"); m_red_flag_indicator = irr_driver->addBillboard( - core::dimension2df(1.5f, 1.5f), red_path, NULL); + core::dimension2df(1.5f, 1.5f), red_path, NULL); m_red_flag_indicator->setPosition(Vec3( - m_orig_red_trans(Vec3(0.0f, 2.5f, 0.0f))).toIrrVector()); + orig_red(Vec3(0.0f, 2.5f, 0.0f))).toIrrVector()); m_blue_flag_indicator = irr_driver->addBillboard( - core::dimension2df(1.5f, 1.5f), blue_path, NULL); + core::dimension2df(1.5f, 1.5f), blue_path, NULL); m_blue_flag_indicator->setPosition(Vec3( - m_orig_blue_trans(Vec3(0.0f, 2.5f, 0.0f))).toIrrVector()); + orig_blue(Vec3(0.0f, 2.5f, 0.0f))).toIrrVector()); #endif } // init @@ -110,53 +119,85 @@ void CaptureTheFlag::init() void CaptureTheFlag::reset(bool restart) { FreeForAll::reset(restart); - m_red_trans = m_orig_red_trans; - m_blue_trans = m_orig_blue_trans; - m_red_return_ticks = m_blue_return_ticks = m_red_scores = - m_blue_scores = 0; - m_red_holder = m_blue_holder = -1; - updateFlagNodes(); + m_red_scores = m_blue_scores = 0; m_swatter_reset_kart_ticks.clear(); + m_last_captured_flag_ticks = 0; + m_red_flag_status = m_blue_flag_status = CTFFlag::IN_BASE; + m_red_flag->resetToBase(); + m_blue_flag->resetToBase(); +#ifndef SERVER_ONLY + if (m_red_flag_node) + m_red_flag->updateFlagGraphics(m_red_flag_node); + if (m_blue_flag_node) + m_blue_flag->updateFlagGraphics(m_blue_flag_node); +#endif } // reset // ---------------------------------------------------------------------------- void CaptureTheFlag::updateGraphics(float dt) { FreeForAll::updateGraphics(dt); - if (!NetworkConfig::get()->isNetworking() || - NetworkConfig::get()->isClient()) - { - if (m_red_holder != -1) - { - m_red_trans = getKart(m_red_holder)->getSmoothedTrans(); - m_red_trans.setOrigin(m_red_trans(g_kart_flag_offset)); - m_red_flag_node->setAnimationSpeed(fabsf(getKart(m_red_holder) - ->getSpeed()) * 3.0f + 25.0f); - } - else - m_red_flag_node->setAnimationSpeed(25.0f); - if (m_blue_holder != -1) +#ifndef SERVER_ONLY + if (m_red_flag_node) + m_red_flag->updateFlagGraphics(m_red_flag_node); + if (m_blue_flag_node) + m_blue_flag->updateFlagGraphics(m_blue_flag_node); + if (m_red_flag_indicator) + m_red_flag_indicator->setVisible(!m_red_flag->isInBase()); + if (m_blue_flag_indicator) + m_blue_flag_indicator->setVisible(!m_blue_flag->isInBase()); + + core::stringw msg; + // Don't show flag has been returned message if there was scored event + // happening recently + const bool scored_recently = + getTicksSinceStart() > m_last_captured_flag_ticks && + getTicksSinceStart() - m_last_captured_flag_ticks < 200; + if (m_red_flag_status != m_red_flag->getStatus()) + { + if (m_red_flag->getHolder() != -1) { - m_blue_trans = getKart(m_blue_holder)->getSmoothedTrans(); - m_blue_trans.setOrigin(m_blue_trans(g_kart_flag_offset)); - m_blue_flag_node->setAnimationSpeed(fabsf(getKart(m_blue_holder) - ->getSpeed()) * 3.0f + 25.0f); + AbstractKart* kart = getKart(m_red_flag->getHolder()); + const core::stringw& name = kart->getController()->getName(); + // I18N: Show when a player gets the red flag in CTF + msg = _("%s has the red flag!", name); + if (kart->getController()->isLocalPlayerController()) + SFXManager::get()->quickSound("wee"); } - else - m_blue_flag_node->setAnimationSpeed(25.0f); - m_red_flag_indicator->setVisible(!isRedFlagInBase()); - m_blue_flag_indicator->setVisible(!isBlueFlagInBase()); + else if (m_red_flag->isInBase() && !scored_recently) + { + // I18N: Show when the red flag is returned to its base in CTF + msg = _("The red flag has returned!"); + } + m_red_flag_status = m_red_flag->getStatus(); } + else if (m_blue_flag_status != m_blue_flag->getStatus()) + { + if (m_blue_flag->getHolder() != -1) + { + AbstractKart* kart = getKart(m_blue_flag->getHolder()); + const core::stringw& name = kart->getController()->getName(); + // I18N: Show when a player gets the blue flag in CTF + msg = _("%s has the blue flag!", name); + if (kart->getController()->isLocalPlayerController()) + SFXManager::get()->quickSound("wee"); + } + else if (m_blue_flag->isInBase() && !scored_recently) + { + // I18N: Show when the blue flag is returned to its base in CTF + msg = _("The blue flag has returned!"); + } + m_blue_flag_status = m_blue_flag->getStatus(); + } + if (!msg.empty()) + m_race_gui->addMessage(msg, NULL, 1.5f); +#endif } // updateGraphics // ---------------------------------------------------------------------------- void CaptureTheFlag::update(int ticks) { - if (m_red_holder != -1 && m_blue_holder != -1 && - m_red_holder == m_blue_holder) - Log::fatal("CaptureTheFlag", "Flag management messed up, abort."); - FreeForAll::update(ticks); for (auto it = m_swatter_reset_kart_ticks.begin(); @@ -189,88 +230,61 @@ void CaptureTheFlag::update(int ticks) } } - if (!NetworkConfig::get()->isNetworking() || - NetworkConfig::get()->isClient()) - return; - // Update new flags position - if (m_red_holder != -1) - { - m_red_trans = getKart(m_red_holder)->getTrans(); - m_red_trans.setOrigin(m_red_trans(g_kart_flag_offset)); - } - if (m_blue_holder != -1) - { - m_blue_trans = getKart(m_blue_holder)->getTrans(); - m_blue_trans.setOrigin(m_blue_trans(g_kart_flag_offset)); - } + m_red_flag->update(ticks); + m_blue_flag->update(ticks); - const bool red_flag_in_base = - m_red_trans.getOrigin() == m_orig_red_trans.getOrigin(); - const bool blue_flag_in_base = - m_blue_trans.getOrigin() == m_orig_blue_trans.getOrigin(); - - // Check if not returning for too long - if (m_red_holder != -1 || red_flag_in_base) - m_red_return_ticks = 0; - else - m_red_return_ticks++; - - if (m_blue_holder != -1 || blue_flag_in_base) - m_blue_return_ticks = 0; - else - m_blue_return_ticks++; - - const int max_flag_return_timeout = stk_config->time2Ticks( - ServerConfig::m_flag_return_timemout); - if (m_red_holder == -1 && m_red_return_ticks > max_flag_return_timeout) - { - resetRedFlagToOrigin(); - m_red_return_ticks = 0; - return; - } - if (m_blue_holder == -1 && m_blue_return_ticks > max_flag_return_timeout) - { - resetBlueFlagToOrigin(); - m_blue_return_ticks = 0; - return; - } - - if (m_red_holder != -1 && m_blue_holder == -1 && - blue_flag_in_base && - (m_orig_blue_trans.getOrigin() - m_red_trans.getOrigin()).length() < + if (m_red_flag->getHolder() != -1 && m_blue_flag->isInBase() && + (m_blue_flag->getBaseOrigin() - m_red_flag->getOrigin()).length() < g_capture_length) { // Blue team scored - NetworkString p(PROTOCOL_GAME_EVENTS); - p.setSynchronous(true); - p.addUInt8(GameEventsProtocol::GE_CTF_RESET) - .addUInt8(1 << 1 | 0) // Reset red flag - .addUInt8((int8_t)m_red_holder); - STKHost::get()->sendPacketToAllPeers(&p, true); - m_scores.at(m_red_holder) += g_captured_score; - m_red_holder = -1; - m_red_trans = m_orig_red_trans; - m_blue_scores++; - return; + if (!NetworkConfig::get()->isNetworking() || + NetworkConfig::get()->isServer()) + { + int red_holder = m_red_flag->getHolder(); + int new_score = m_scores.at(red_holder) + g_captured_score; + m_scores.at(red_holder) = new_score; + if (NetworkConfig::get()->isServer()) + { + NetworkString p(PROTOCOL_GAME_EVENTS); + p.setSynchronous(true); + p.addUInt8(GameEventsProtocol::GE_CTF_SCORED) + .addUInt8((int8_t)red_holder) + .addUInt8(0/*red_team_scored*/) + .addUInt32(new_score); + STKHost::get()->sendPacketToAllPeers(&p, true); + } + ctfScored(red_holder, false/*red_team_scored*/, new_score); + } + m_last_captured_flag_ticks = World::getWorld()->getTicksSinceStart(); + m_red_flag->resetToBase(); } - else if (m_blue_holder != -1 && m_red_holder == -1 && - red_flag_in_base && - (m_orig_red_trans.getOrigin() - m_blue_trans.getOrigin()).length() < + else if (m_blue_flag->getHolder() != -1 && m_red_flag->isInBase() && + (m_red_flag->getBaseOrigin() - m_blue_flag->getOrigin()).length() < g_capture_length) { // Red team scored - NetworkString p(PROTOCOL_GAME_EVENTS); - p.setSynchronous(true); - p.addUInt8(GameEventsProtocol::GE_CTF_RESET) - .addUInt8(0 << 1 | 0) // Reset blue flag - .addUInt8((int8_t)m_blue_holder); - STKHost::get()->sendPacketToAllPeers(&p, true); - m_scores.at(m_blue_holder) += g_captured_score; - m_blue_holder = -1; - m_blue_trans = m_orig_blue_trans; - m_red_scores++; - return; + if (!NetworkConfig::get()->isNetworking() || + NetworkConfig::get()->isServer()) + { + int blue_holder = m_blue_flag->getHolder(); + int new_score = m_scores.at(blue_holder) + g_captured_score; + m_scores.at(blue_holder) = new_score; + if (NetworkConfig::get()->isServer()) + { + NetworkString p(PROTOCOL_GAME_EVENTS); + p.setSynchronous(true); + p.addUInt8(GameEventsProtocol::GE_CTF_SCORED) + .addUInt8((int8_t)blue_holder) + .addUInt8(1/*red_team_scored*/) + .addUInt32(new_score); + STKHost::get()->sendPacketToAllPeers(&p, true); + } + ctfScored(blue_holder, true/*red_team_scored*/, new_score); + } + m_last_captured_flag_ticks = World::getWorld()->getTicksSinceStart(); + m_blue_flag->resetToBase(); } // Test if red or blue flag is touched @@ -279,220 +293,108 @@ void CaptureTheFlag::update(int ticks) if (k->isEliminated() || k->getKartAnimation() || k->isSquashed()) continue; - if (m_red_holder == -1 && - (k->getXYZ() - m_red_trans.getOrigin()).length() < + if (m_red_flag->canBeCaptured() && + (k->getXYZ() - m_red_flag->getOrigin()).length() < g_capture_length) { uint8_t kart_id = (uint8_t)k->getWorldKartId(); if (getKartTeam(kart_id) == KART_TEAM_RED) { - if (!red_flag_in_base) + if (!m_red_flag->isInBase()) { // Return the flag - resetRedFlagToOrigin(); + m_red_flag->resetToBase(); } } else { // Get the flag - NetworkString p(PROTOCOL_GAME_EVENTS); - p.setSynchronous(true); - p.addUInt8(GameEventsProtocol::GE_CTF_ATTACH) - .addUInt8(1) // Attach red flag - .addUInt8(kart_id); - STKHost::get()->sendPacketToAllPeers(&p, true); - m_red_holder = kart_id; + m_red_flag->setCapturedByKart(kart_id); } } - if (m_blue_holder == -1 && - (k->getXYZ() - m_blue_trans.getOrigin()).length() < + if (m_blue_flag->canBeCaptured() && + (k->getXYZ() - m_blue_flag->getOrigin()).length() < g_capture_length) { uint8_t kart_id = (uint8_t)k->getWorldKartId(); if (getKartTeam(kart_id) == KART_TEAM_BLUE) { - if (!blue_flag_in_base) + if (!m_blue_flag->isInBase()) { // Return the flag - resetBlueFlagToOrigin(); + m_blue_flag->resetToBase(); } } else { // Get the flag - NetworkString p(PROTOCOL_GAME_EVENTS); - p.setSynchronous(true); - p.addUInt8(GameEventsProtocol::GE_CTF_ATTACH) - .addUInt8(0) // Attach blue flag - .addUInt8(kart_id); - STKHost::get()->sendPacketToAllPeers(&p, true); - m_blue_holder = kart_id; + m_blue_flag->setCapturedByKart(kart_id); } } } } // update // ---------------------------------------------------------------------------- -void CaptureTheFlag::resetRedFlagToOrigin() +int CaptureTheFlag::getRedHolder() const { - NetworkString p(PROTOCOL_GAME_EVENTS); - p.setSynchronous(true); - p.addUInt8(GameEventsProtocol::GE_CTF_RESET) - .addUInt8(1 << 1 | 0) // Reset red flag to original - .addUInt8(((int8_t)-1)); - STKHost::get()->sendPacketToAllPeers(&p, true); - m_red_trans = m_orig_red_trans; -} // resetRedFlagToOrigin + return m_red_flag->getHolder(); +} // getRedHolder // ---------------------------------------------------------------------------- -void CaptureTheFlag::resetBlueFlagToOrigin() +int CaptureTheFlag::getBlueHolder() const { - NetworkString p(PROTOCOL_GAME_EVENTS); - p.setSynchronous(true); - p.addUInt8(GameEventsProtocol::GE_CTF_RESET) - .addUInt8(0 << 1 | 0) // Reset blue flag to original - .addUInt8(((int8_t)-1)); - STKHost::get()->sendPacketToAllPeers(&p, true); - m_blue_trans = m_orig_blue_trans; -} // resetBlueFlagToOrigin + return m_blue_flag->getHolder(); +} // getBlueHolder // ---------------------------------------------------------------------------- -void CaptureTheFlag::updateFlagNodes() +bool CaptureTheFlag::isRedFlagInBase() const { -#ifndef SERVER_ONLY - Vec3 hpr; - if (m_red_holder == -1) - { - m_red_flag_node->setPosition( - Vec3(m_red_trans.getOrigin()).toIrrVector()); - hpr.setHPR(m_red_trans.getRotation()); - m_red_flag_node->setRotation(hpr.toIrrHPR()); - } - - if (m_blue_holder == -1) - { - m_blue_flag_node->setPosition( - Vec3(m_blue_trans.getOrigin()).toIrrVector()); - hpr.setHPR(m_blue_trans.getRotation()); - m_blue_flag_node->setRotation(hpr.toIrrHPR()); - } -#endif -} // updateFlagNodes + return m_red_flag->isInBase(); +} // isRedFlagInBase // ---------------------------------------------------------------------------- -void CaptureTheFlag::attachFlag(NetworkString& ns) +bool CaptureTheFlag::isBlueFlagInBase() const { -#ifndef SERVER_ONLY - bool attach_red_flag = ns.getUInt8() == 1; - unsigned kart_id = ns.getUInt8(); - core::stringw get_msg; - const core::stringw& name = getKart(kart_id)->getController() - ->getName(); - if (attach_red_flag) + return m_blue_flag->isInBase(); +} // isBlueFlagInBase + +// ---------------------------------------------------------------------------- +const Vec3& CaptureTheFlag::getRedFlag() const +{ + return m_red_flag->getOrigin(); +} // getRedFlag + +// ---------------------------------------------------------------------------- +const Vec3& CaptureTheFlag::getBlueFlag() const +{ + return m_blue_flag->getOrigin(); +} // getBlueFlag + +// ---------------------------------------------------------------------------- +void CaptureTheFlag::ctfScored(int kart_id, bool red_team_scored, + int new_score) +{ + m_scores.at(kart_id) = new_score; + AbstractKart* kart = getKart(kart_id); + core::stringw scored_msg; + const core::stringw& name = kart->getController()->getName(); + if (red_team_scored) { - m_red_holder = kart_id; - m_red_flag_node->setParent(getKart(kart_id)->getNode()); - m_red_flag_node->setPosition(g_kart_flag_offset.toIrrVector()); - m_red_flag_node->setRotation(core::vector3df(0.0f, 180.0f, 0.0f)); - // I18N: Show when a player gets the flag in CTF - get_msg = _("%s has the red flag!", name); + scored_msg = _("%s captured the blue flag!", name); + m_red_scores++; } else { - m_blue_holder = kart_id; - m_blue_flag_node->setParent(getKart(kart_id)->getNode()); - m_blue_flag_node->setPosition(g_kart_flag_offset.toIrrVector()); - m_blue_flag_node->setRotation(core::vector3df(0.0f, 180.0f, 0.0f)); - // I18N: Show when a player gets the flag in CTF - get_msg = _("%s has the blue flag!", name); + scored_msg = _("%s captured the red flag!", name); + m_blue_scores++; } - if (getKart(kart_id)->getController()->isLocalPlayerController()) - SFXManager::get()->quickSound("wee"); - m_race_gui->addMessage(get_msg, NULL, 1.5f); -#endif -} // attachFlag - -// ---------------------------------------------------------------------------- -void CaptureTheFlag::resetFlag(NetworkString& ns) -{ #ifndef SERVER_ONLY - uint8_t reset_info = ns.getUInt8(); - bool reset_red_flag = (reset_info >> 1 & 1) == 1; - bool with_custom_transform = (reset_info & 1) == 1; - int8_t kart_id = ns.getUInt8(); - if (kart_id != -1) - { - core::stringw scored_msg; - AbstractKart* kart = getKart(kart_id); - const core::stringw& name = kart->getController()->getName(); - if (reset_red_flag) - { - m_scores.at(kart_id) += g_captured_score; - m_red_holder = -1; - m_red_trans = m_orig_red_trans; - // I18N: Show when a player captured the flag in CTF - scored_msg = _("%s captured the red flag!", name); - m_red_flag_node->setParent( - irr_driver->getSceneManager()->getRootSceneNode()); - m_blue_scores++; - } - else - { - m_scores.at(kart_id) += g_captured_score; - m_blue_holder = -1; - m_blue_trans = m_orig_blue_trans; - // I18N: Show when a player captured the flag in CTF - scored_msg = _("%s captured the blue flag!", name); - m_blue_flag_node->setParent( - irr_driver->getSceneManager()->getRootSceneNode()); - m_red_scores++; - } - m_race_gui->addMessage(scored_msg, NULL, 3.0f); - kart->getKartModel() - ->setAnimation(KartModel::AF_WIN_START, true/* play_non_loop*/); - m_scored_sound->play(); - } - else - { - core::stringw returned_msg; - if (reset_red_flag) - { - btTransform t = m_orig_red_trans; - // I18N: Show when the red flag is returned to its base in CTF - if (!with_custom_transform) - returned_msg = _("The red flag has returned!"); - else - { - t.setOrigin(ns.getVec3()); - t.setRotation(ns.getQuat()); - } - m_red_holder = -1; - m_red_trans = t; - m_red_flag_node->setParent( - irr_driver->getSceneManager()->getRootSceneNode()); - } - else - { - btTransform t = m_orig_blue_trans; - // I18N: Show when the blue flag is returned to its base in CTF - if (!with_custom_transform) - returned_msg = _("The blue flag has returned!"); - else - { - t.setOrigin(ns.getVec3()); - t.setRotation(ns.getQuat()); - } - m_blue_holder = -1; - m_blue_trans = t; - m_blue_flag_node->setParent( - irr_driver->getSceneManager()->getRootSceneNode()); - } - if (!returned_msg.empty()) - m_race_gui->addMessage(returned_msg, NULL, 1.5f); - } - updateFlagNodes(); + m_race_gui->addMessage(scored_msg, NULL, 3.0f); + kart->getKartModel() + ->setAnimation(KartModel::AF_WIN_START, true/*play_non_loop*/); + m_scored_sound->play(); #endif -} // resetFlag +} // ctfScored // ---------------------------------------------------------------------------- bool CaptureTheFlag::getDroppedFlagTrans(const btTransform& kt, @@ -545,48 +447,66 @@ bool CaptureTheFlag::isRaceOver() // ---------------------------------------------------------------------------- void CaptureTheFlag::loseFlagForKart(int kart_id) { - if (!(m_red_holder == kart_id || m_blue_holder == kart_id)) + if (!(m_red_flag->getHolder() == kart_id || + m_blue_flag->getHolder() == kart_id)) return; - bool reset_red_flag = m_red_holder == kart_id; - btTransform dropped_trans = reset_red_flag ? - m_orig_red_trans : m_orig_blue_trans; + bool drop_red_flag = m_red_flag->getHolder() == kart_id; + btTransform dropped_trans = drop_red_flag ? + m_red_flag->getBaseTrans() : + m_blue_flag->getBaseTrans(); bool succeed = getDroppedFlagTrans(getKart(kart_id)->getTrans(), &dropped_trans); - NetworkString p(PROTOCOL_GAME_EVENTS); - p.setSynchronous(true); - // If reset red flag - uint8_t reset_info = reset_red_flag ? 1 : 0; - reset_info <<= 1; - // With custom transform - if (succeed) - reset_info |= 1; - p.addUInt8(GameEventsProtocol::GE_CTF_RESET).addUInt8(reset_info) - .addUInt8(((int8_t)-1)); - if (succeed) + if (drop_red_flag) { - p.add(Vec3(dropped_trans.getOrigin())) - .add(dropped_trans.getRotation()); - } - STKHost::get()->sendPacketToAllPeers(&p, true); - if (reset_red_flag) - { - m_red_holder = -1; - m_red_trans = dropped_trans; + if (succeed) + m_red_flag->dropFlagAt(dropped_trans); + else + m_red_flag->resetToBase(); } else { - m_blue_holder = -1; - m_blue_trans = dropped_trans; + if (succeed) + m_blue_flag->dropFlagAt(dropped_trans); + else + m_blue_flag->resetToBase(); + } + if (NetworkConfig::get()->isNetworking() && + NetworkConfig::get()->isClient()) + { + RewindManager::get()->addRewindInfoEventFunction(new + RewindInfoEventFunction(World::getWorld()->getTicksSinceStart(), + [](){}, + /*replay_function*/[dropped_trans, drop_red_flag, succeed, this]() + { + if (drop_red_flag) + { + if (succeed) + m_red_flag->dropFlagAt(dropped_trans); + else + m_red_flag->resetToBase(); + } + else + { + if (succeed) + m_blue_flag->dropFlagAt(dropped_trans); + else + m_blue_flag->resetToBase(); + } + })); } } // loseFlagForKart // ---------------------------------------------------------------------------- bool CaptureTheFlag::kartHit(int kart_id, int hitter) { - if (!FreeForAll::kartHit(kart_id, hitter)) + if (isRaceOver()) return false; + if (!NetworkConfig::get()->isNetworking() || + NetworkConfig::get()->isServer()) + handleScoreInServer(kart_id, hitter); + loseFlagForKart(kart_id); return true; } // kartHit diff --git a/src/modes/capture_the_flag.hpp b/src/modes/capture_the_flag.hpp index 60fe50a4c..2dfcb7946 100644 --- a/src/modes/capture_the_flag.hpp +++ b/src/modes/capture_the_flag.hpp @@ -31,6 +31,8 @@ namespace irr } } +class CTFFlag; + class CaptureTheFlag : public FreeForAll { private: @@ -48,16 +50,18 @@ private: SFXBase* m_scored_sound; - int m_red_scores, m_blue_scores, m_red_holder, m_blue_holder; + int m_red_scores, m_blue_scores; - btTransform m_red_trans, m_blue_trans, m_orig_red_trans, m_orig_blue_trans; + /* Save the last captured flag ticks */ + int m_last_captured_flag_ticks; - int m_red_return_ticks, m_blue_return_ticks; + /* Used in updateGraphics to add race gui message for flag */ + int m_red_flag_status, m_blue_flag_status; std::map m_swatter_reset_kart_ticks; - // ------------------------------------------------------------------------ - void updateFlagNodes(); + std::shared_ptr m_red_flag, m_blue_flag; + // ------------------------------------------------------------------------ bool getDroppedFlagTrans(const btTransform& kt, btTransform* out) const; // ------------------------------------------------------------------------ @@ -91,10 +95,6 @@ public: // ------------------------------------------------------------------------ virtual const std::string& getIdent() const OVERRIDE; // ------------------------------------------------------------------------ - void attachFlag(NetworkString& ns); - // ------------------------------------------------------------------------ - void resetFlag(NetworkString& ns); - // ------------------------------------------------------------------------ bool getKartCTFResult(unsigned int kart_id) const { if (m_red_scores == m_blue_scores) @@ -114,25 +114,19 @@ public: // ------------------------------------------------------------------------ int getBlueScore() const { return m_blue_scores; } // ------------------------------------------------------------------------ - int getRedHolder() const { return m_red_holder; } + int getRedHolder() const; // ------------------------------------------------------------------------ - int getBlueHolder() const { return m_blue_holder; } + int getBlueHolder() const; // ------------------------------------------------------------------------ - bool isRedFlagInBase() const - { - return m_red_holder == -1 && - m_red_trans.getOrigin() == m_orig_red_trans.getOrigin(); - } + bool isRedFlagInBase() const; // ------------------------------------------------------------------------ - bool isBlueFlagInBase() const - { - return m_blue_holder == -1 && - m_blue_trans.getOrigin() == m_orig_blue_trans.getOrigin(); - } + bool isBlueFlagInBase() const; // ------------------------------------------------------------------------ - const Vec3& getRedFlag() const { return (Vec3&)m_red_trans.getOrigin(); } + const Vec3& getRedFlag() const; // ------------------------------------------------------------------------ - const Vec3& getBlueFlag() const { return (Vec3&)m_blue_trans.getOrigin(); } + const Vec3& getBlueFlag() const; + // ------------------------------------------------------------------------ + void ctfScored(int kart_id, bool red_team_scored, int new_score); // ------------------------------------------------------------------------ void loseFlagForKart(int kart_id); // ------------------------------------------------------------------------ diff --git a/src/modes/ctf_flag.cpp b/src/modes/ctf_flag.cpp new file mode 100644 index 000000000..ee5ffa6f8 --- /dev/null +++ b/src/modes/ctf_flag.cpp @@ -0,0 +1,122 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2018 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "modes/ctf_flag.hpp" +#include "graphics/irr_driver.hpp" +#include "karts/abstract_kart.hpp" +#include "modes/world.hpp" +#include "network/network_string.hpp" +#include "utils/mini_glm.hpp" + +#include "LinearMath/btQuaternion.h" + +// ============================================================================ +// Position offset to attach in kart model +const Vec3 g_kart_flag_offset(0.0, 0.2f, -0.5f); +// ============================================================================ +BareNetworkString* CTFFlag::saveState(std::vector* ru) +{ + using namespace MiniGLM; + ru->push_back(getUniqueIdentity()); + BareNetworkString* buffer = new BareNetworkString(); + buffer->addUInt8(m_flag_status); + if (m_flag_status == OFF_BASE) + { + Vec3 normal = quatRotate(m_flag_trans.getRotation(), + Vec3(0.0f, 1.0f, 0.0f)); + buffer->add(m_flag_trans.getOrigin()); + buffer->addUInt32( + compressVector3(Vec3(normal.normalize()).toIrrVector())); + buffer->addUInt16(m_ticks_since_off_base); + } + return buffer; +} // saveState + +// ---------------------------------------------------------------------------- +void CTFFlag::restoreState(BareNetworkString* buffer, int count) +{ + using namespace MiniGLM; + m_flag_status = buffer->getUInt8(); + if (m_flag_status == OFF_BASE) + { + Vec3 origin = buffer->getVec3(); + uint32_t normal_packed = buffer->getUInt32(); + Vec3 normal = decompressVector3(normal_packed); + m_flag_trans.setOrigin(origin); + m_flag_trans.setRotation( + shortestArcQuat(Vec3(0.0f, 1.0f, 0.0f), normal)); + m_ticks_since_off_base = buffer->getUInt16(); + } + updateFlagTrans(m_flag_trans); +} // restoreState + +// ---------------------------------------------------------------------------- +void CTFFlag::updateFlagTrans(const btTransform& off_base_trans) +{ + if (getHolder() != -1) + { + AbstractKart* k = World::getWorld()->getKart(getHolder()); + m_flag_trans = k->getTrans(); + m_flag_trans.setOrigin(m_flag_trans(g_kart_flag_offset)); + } + else if (m_flag_status == OFF_BASE) + { + m_flag_trans = off_base_trans; + } + else + m_flag_trans = m_flag_base_trans; +} + +// ---------------------------------------------------------------------------- +void CTFFlag::update(int ticks) +{ + updateFlagTrans(m_flag_trans); + + // Check if not returning for too long + if (m_flag_status != OFF_BASE) + return; + + m_ticks_since_off_base += ticks; + if (m_ticks_since_off_base > race_manager->getFlagReturnTicks()) + { + resetToBase(); + } +} // update + +// ---------------------------------------------------------------------------- +void CTFFlag::updateFlagGraphics(irr::scene::IAnimatedMeshSceneNode* flag_node) +{ + if (getHolder() != -1) + { + AbstractKart* k = World::getWorld()->getKart(getHolder()); + flag_node->setParent(k->getNode()); + flag_node->setPosition(g_kart_flag_offset.toIrrVector()); + flag_node->setRotation(core::vector3df(0.0f, 180.0f, 0.0f)); + flag_node->setAnimationSpeed(fabsf(k->getSpeed()) * 3.0f + 25.0f); + } + else + { + flag_node->setParent( + irr_driver->getSceneManager()->getRootSceneNode()); + Vec3 hpr; + flag_node->setPosition( + Vec3(m_flag_trans.getOrigin()).toIrrVector()); + hpr.setHPR(m_flag_trans.getRotation()); + flag_node->setRotation(hpr.toIrrHPR()); + flag_node->setAnimationSpeed(25.0f); + } +} // updateFlagPosition diff --git a/src/modes/ctf_flag.hpp b/src/modes/ctf_flag.hpp new file mode 100644 index 000000000..e9c500b7c --- /dev/null +++ b/src/modes/ctf_flag.hpp @@ -0,0 +1,137 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2018 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_CTF_FLAG_HPP +#define HEADER_CTF_FLAG_HPP + +#include "network/rewinder.hpp" +#include "utils/types.hpp" +#include "utils/vec3.hpp" + +#include "LinearMath/btTransform.h" + +enum FlagColor : unsigned int +{ + FC_RED = 0, + FC_BLUE = 1 +}; + +namespace irr +{ + namespace scene + { + class IAnimatedMeshSceneNode; + } +} +class CTFFlag : public Rewinder +{ +public: + static const int IN_BASE = -1; + static const int OFF_BASE = -2; +private: + /* Either save the above 2 status, or kart id holder this flag. */ + int8_t m_flag_status; + + /* Currnet flag transformation. */ + btTransform m_flag_trans; + + /* Transformation of IN_BASE. */ + const btTransform m_flag_base_trans; + + /* If OFF_BASE of m_flag_status, save the ticks since off base, use for + * auto return base for a flag (see ServerConfig). */ + uint16_t m_ticks_since_off_base; + + FlagColor m_flag_color; + +public: + // ------------------------------------------------------------------------ + CTFFlag(FlagColor fc, const btTransform& base_trans) + : Rewinder(fc == FC_RED ? "TR" : "TB"), m_flag_base_trans(base_trans) + { + // UID rewinder with "T" which is after "Kx" for kart so + // updateFlagTrans is called after kart is rewound + m_flag_status = IN_BASE; + m_flag_trans.setOrigin(Vec3(0.0f)); + m_flag_trans.setRotation(btQuaternion(0.0f, 0.0f, 0.0f, 1.0f)); + m_flag_color = fc; + m_ticks_since_off_base = 0; + } + // ------------------------------------------------------------------------ + virtual void saveTransform() {} + // ------------------------------------------------------------------------ + virtual void computeError() {} + // ------------------------------------------------------------------------ + virtual BareNetworkString* saveState(std::vector* ru); + // ------------------------------------------------------------------------ + virtual void undoEvent(BareNetworkString* buffer) {} + // ------------------------------------------------------------------------ + virtual void rewindToEvent(BareNetworkString* buffer) {} + // ------------------------------------------------------------------------ + virtual void restoreState(BareNetworkString* buffer, int count); + // ------------------------------------------------------------------------ + virtual void undoState(BareNetworkString* buffer) {} + // ------------------------------------------------------------------------ + int getHolder() const + { + if (m_flag_status >= 0) + return m_flag_status; + return -1; + } + // ------------------------------------------------------------------------ + int getStatus() const { return m_flag_status; } + // ------------------------------------------------------------------------ + const Vec3& getOrigin() const { return (Vec3&)m_flag_trans.getOrigin(); } + // ------------------------------------------------------------------------ + const Vec3& getBaseOrigin() const + { return (Vec3&)m_flag_base_trans.getOrigin(); } + // ------------------------------------------------------------------------ + const btTransform& getBaseTrans() const { return m_flag_base_trans; } + // ------------------------------------------------------------------------ + void resetToBase() + { + m_flag_status = IN_BASE; + m_ticks_since_off_base = 0; + updateFlagTrans(); + } + // ------------------------------------------------------------------------ + void setCapturedByKart(int kart_id) + { + m_flag_status = (int8_t)kart_id; + m_ticks_since_off_base = 0; + updateFlagTrans(); + } + // ------------------------------------------------------------------------ + void dropFlagAt(const btTransform& t) + { + m_flag_status = OFF_BASE; + m_ticks_since_off_base = 0; + m_flag_trans = t; + } + // ------------------------------------------------------------------------ + bool isInBase() const { return m_flag_status == IN_BASE; } + // ------------------------------------------------------------------------ + bool canBeCaptured() const { return !(m_flag_status >= 0); } + // ------------------------------------------------------------------------ + void update(int ticks); + // ------------------------------------------------------------------------ + void updateFlagTrans(const btTransform& off_base_trans = btTransform()); + // ------------------------------------------------------------------------ + void updateFlagGraphics(irr::scene::IAnimatedMeshSceneNode* flag_node); +}; // CTFFlag +#endif + diff --git a/src/modes/free_for_all.cpp b/src/modes/free_for_all.cpp index c38b28374..0d1a3b9d4 100644 --- a/src/modes/free_for_all.cpp +++ b/src/modes/free_for_all.cpp @@ -99,17 +99,37 @@ bool FreeForAll::kartHit(int kart_id, int hitter) if (isRaceOver()) return false; - NetworkString p(PROTOCOL_GAME_EVENTS); - p.setSynchronous(true); - p.addUInt8(GameEventsProtocol::GE_BATTLE_KART_SCORE); - if (kart_id == hitter || hitter == -1) - p.addUInt8((uint8_t)kart_id).addUInt32(--m_scores[kart_id]); - else - p.addUInt8((uint8_t)hitter).addUInt32(++m_scores[hitter]); - STKHost::get()->sendPacketToAllPeers(&p, true); + handleScoreInServer(kart_id, hitter); return true; } // kartHit +// ---------------------------------------------------------------------------- +/** Called when the score of kart needs updated. + * \param kart_id The world kart id of the kart that was hit. + * \param hitter The world kart id of the kart who hit(-1 if none). + */ +void FreeForAll::handleScoreInServer(int kart_id, int hitter) +{ + int new_score = 0; + if (kart_id == hitter || hitter == -1) + new_score = --m_scores[kart_id]; + else + new_score = ++m_scores[hitter]; + + if (NetworkConfig::get()->isNetworking() && + NetworkConfig::get()->isServer()) + { + NetworkString p(PROTOCOL_GAME_EVENTS); + p.setSynchronous(true); + p.addUInt8(GameEventsProtocol::GE_BATTLE_KART_SCORE); + if (kart_id == hitter || hitter == -1) + p.addUInt8((uint8_t)kart_id).addUInt32(new_score); + else + p.addUInt8((uint8_t)hitter).addUInt32(new_score); + STKHost::get()->sendPacketToAllPeers(&p, true); + } +} // handleScoreInServer + // ---------------------------------------------------------------------------- void FreeForAll::setKartScoreFromServer(NetworkString& ns) { diff --git a/src/modes/free_for_all.hpp b/src/modes/free_for_all.hpp index 4d2c7fe6a..7ff5ecba0 100644 --- a/src/modes/free_for_all.hpp +++ b/src/modes/free_for_all.hpp @@ -31,6 +31,8 @@ protected: bool m_count_down_reached_zero; std::vector m_scores; + // ------------------------------------------------------------------------ + void handleScoreInServer(int kart_id, int hitter); private: // ------------------------------------------------------------------------ diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index 21f9bfc4a..80a536988 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -267,6 +267,8 @@ void ClientLobby::addAllPlayers(Event* event) int hit_capture_limit = data.getUInt32(); float time_limit = data.getFloat(); m_game_setup->setHitCaptureTime(hit_capture_limit, time_limit); + uint16_t flag_return_timeout = data.getUInt16(); + race_manager->setFlagReturnTicks(flag_return_timeout); } configRemoteKart(players); loadWorld(); diff --git a/src/network/protocols/game_events_protocol.cpp b/src/network/protocols/game_events_protocol.cpp index 8362c182f..b204884de 100644 --- a/src/network/protocols/game_events_protocol.cpp +++ b/src/network/protocols/game_events_protocol.cpp @@ -79,18 +79,14 @@ bool GameEventsProtocol::notifyEvent(Event* event) ffa->setKartScoreFromServer(data); break; } - case GE_CTF_ATTACH: + case GE_CTF_SCORED: { if (!ctf) throw std::invalid_argument("No CTF world"); - ctf->attachFlag(data); - break; - } - case GE_CTF_RESET: - { - if (!ctf) - throw std::invalid_argument("No CTF world"); - ctf->resetFlag(data); + uint8_t kart_id = data.getUInt8(); + bool red_team_scored = data.getUInt8() == 1; + int new_score = data.getUInt32(); + ctf->ctfScored(kart_id, red_team_scored, new_score); break; } case GE_STARTUP_BOOST: diff --git a/src/network/protocols/game_events_protocol.hpp b/src/network/protocols/game_events_protocol.hpp index 47ccaf737..522d0d691 100644 --- a/src/network/protocols/game_events_protocol.hpp +++ b/src/network/protocols/game_events_protocol.hpp @@ -16,9 +16,8 @@ public: GE_RESET_BALL = 3, GE_PLAYER_GOAL = 4, GE_BATTLE_KART_SCORE = 5, - GE_CTF_ATTACH = 6, - GE_CTF_RESET = 7, - GE_STARTUP_BOOST = 8, + GE_CTF_SCORED = 6, + GE_STARTUP_BOOST = 7, }; // GameEventType private: int m_last_finished_position; diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index a4db28f8f..1aa53e111 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -595,6 +595,10 @@ void ServerLobby::asynchronousUpdate() auto hcl = getHitCaptureLimit((float)players.size()); load_world_message->addUInt32(hcl.first).addFloat(hcl.second); m_game_setup->setHitCaptureTime(hcl.first, hcl.second); + uint16_t flag_return_time = (uint16_t)stk_config->time2Ticks( + ServerConfig::m_flag_return_timemout); + load_world_message->addUInt16(flag_return_time); + race_manager->setFlagReturnTicks(flag_return_time); } configRemoteKart(players); diff --git a/src/network/server_config.cpp b/src/network/server_config.cpp index 003720b59..f2431fdfa 100644 --- a/src/network/server_config.cpp +++ b/src/network/server_config.cpp @@ -261,6 +261,15 @@ void loadServerLobbyFromConfig() if (m_voting_timeout == 20.0f) m_voting_timeout = 30.0f; + if (stk_config->time2Ticks(m_flag_return_timemout) > 65535) + { + float timeout = m_flag_return_timemout; + // in CTFFlag it uses 16bit unsigned integer for timeout + Log::warn("ServerConfig", "Invalid %f m_flag_return_timemout which " + "is too large, use default value.", timeout); + m_flag_return_timemout.revertToDefaults(); + } + int frequency_in_config = m_state_frequency; if (frequency_in_config <= 0 || frequency_in_config > stk_config->getPhysicsFPS()) diff --git a/src/race/race_manager.cpp b/src/race/race_manager.cpp index 5ede04370..2bc36058d 100644 --- a/src/race/race_manager.cpp +++ b/src/race/race_manager.cpp @@ -81,6 +81,7 @@ RaceManager::RaceManager() m_have_kart_last_position_on_overworld = false; m_num_local_players = 0; m_hit_capture_limit = 0; + m_flag_return_ticks = stk_config->time2Ticks(20.0f); setMaxGoal(0); setTimeTarget(0.0f); setReverseTrack(false); diff --git a/src/race/race_manager.hpp b/src/race/race_manager.hpp index b24871060..b149d491a 100644 --- a/src/race/race_manager.hpp +++ b/src/race/race_manager.hpp @@ -356,6 +356,7 @@ private: unsigned int m_num_spare_tire_karts; unsigned int m_num_finished_karts; unsigned int m_num_finished_players; + unsigned m_flag_return_ticks; int m_coin_target; float m_time_target; int m_goal_target; @@ -896,7 +897,10 @@ public: return m_minor_mode == MINOR_MODE_SOCCER || m_minor_mode == MINOR_MODE_CAPTURE_THE_FLAG; } - + // ------------------------------------------------------------------------ + void setFlagReturnTicks(unsigned ticks) { m_flag_return_ticks = ticks; } + // ------------------------------------------------------------------------ + unsigned getFlagReturnTicks() const { return m_flag_return_ticks; } }; // RaceManager extern RaceManager *race_manager; diff --git a/src/states_screens/race_gui.cpp b/src/states_screens/race_gui.cpp index b1a871c77..f992b62ce 100644 --- a/src/states_screens/race_gui.cpp +++ b/src/states_screens/race_gui.cpp @@ -532,8 +532,10 @@ void RaceGUI::drawGlobalMiniMap() lower_y -(int)(draw_at.getY()-(m_minimap_player_size/2.2f))); draw2DImage(m_red_flag, rp, rs, NULL, NULL, true, true); } + Vec3 pos = ctf_world->getRedHolder() == -1 ? ctf_world->getRedFlag() : + ctf_world->getKart(ctf_world->getRedHolder())->getSmoothedTrans().getOrigin(); - track->mapPoint2MiniMap(ctf_world->getRedFlag(), &draw_at); + track->mapPoint2MiniMap(pos, &draw_at); core::rect rs(core::position2di(0, 0), m_red_flag->getSize()); core::rect rp(m_map_left+(int)(draw_at.getX()-(m_minimap_player_size/1.4f)), lower_y -(int)(draw_at.getY()+(m_minimap_player_size/2.2f)), @@ -553,7 +555,10 @@ void RaceGUI::drawGlobalMiniMap() draw2DImage(m_blue_flag, rp, rs, NULL, NULL, true, true); } - track->mapPoint2MiniMap(ctf_world->getBlueFlag(), &draw_at); + pos = ctf_world->getBlueHolder() == -1 ? ctf_world->getBlueFlag() : + ctf_world->getKart(ctf_world->getBlueHolder())->getSmoothedTrans().getOrigin(); + + track->mapPoint2MiniMap(pos, &draw_at); core::rect bs(core::position2di(0, 0), m_blue_flag->getSize()); core::rect bp(m_map_left+(int)(draw_at.getX()-(m_minimap_player_size/1.4f)), lower_y -(int)(draw_at.getY()+(m_minimap_player_size/2.2f)),