Make flag a rewinder

It remove the capturing delay and allow ctf in local splitscreen
This commit is contained in:
Benau 2018-12-24 16:01:21 +08:00
parent 14389925fe
commit d6946198c5
15 changed files with 565 additions and 350 deletions

View File

@ -23,9 +23,12 @@
#include "karts/abstract_kart.hpp" #include "karts/abstract_kart.hpp"
#include "karts/controller/controller.hpp" #include "karts/controller/controller.hpp"
#include "karts/kart_model.hpp" #include "karts/kart_model.hpp"
#include "modes/ctf_flag.hpp"
#include "network/network_config.hpp" #include "network/network_config.hpp"
#include "network/network_string.hpp" #include "network/network_string.hpp"
#include "network/protocols/game_events_protocol.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/server_config.hpp"
#include "network/stk_host.hpp" #include "network/stk_host.hpp"
#include "physics/triangle_mesh.hpp" #include "physics/triangle_mesh.hpp"
@ -35,8 +38,7 @@
#include <algorithm> #include <algorithm>
const Vec3 g_kart_flag_offset(0.0, 0.2f, -0.5f); const float g_capture_length = 2.0f;
const float g_capture_length = 3.0f;
const int g_captured_score = 10; const int g_captured_score = 10;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -80,8 +82,15 @@ CaptureTheFlag::~CaptureTheFlag()
void CaptureTheFlag::init() void CaptureTheFlag::init()
{ {
FreeForAll::init(); FreeForAll::init();
m_orig_red_trans = Track::getCurrentTrack()->getRedFlag(); const btTransform& orig_red = Track::getCurrentTrack()->getRedFlag();
m_orig_blue_trans = Track::getCurrentTrack()->getBlueFlag(); const btTransform& orig_blue = Track::getCurrentTrack()->getBlueFlag();
m_red_flag = std::make_shared<CTFFlag>(FC_RED, orig_red);
m_blue_flag = std::make_shared<CTFFlag>(FC_BLUE, orig_blue);
if (NetworkConfig::get()->isNetworking())
{
m_red_flag->rewinderAdd();
m_blue_flag->rewinderAdd();
}
#ifndef SERVER_ONLY #ifndef SERVER_ONLY
m_red_flag_node = irr_driver->addAnimatedMesh(m_red_flag_mesh, "red_flag"); 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"); file_manager->getAsset(FileManager::GUI_ICON, "blue_arrow.png");
m_red_flag_indicator = irr_driver->addBillboard( 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_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( 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_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 #endif
} // init } // init
@ -110,53 +119,85 @@ void CaptureTheFlag::init()
void CaptureTheFlag::reset(bool restart) void CaptureTheFlag::reset(bool restart)
{ {
FreeForAll::reset(restart); FreeForAll::reset(restart);
m_red_trans = m_orig_red_trans; m_red_scores = m_blue_scores = 0;
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_swatter_reset_kart_ticks.clear(); 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 } // reset
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void CaptureTheFlag::updateGraphics(float dt) void CaptureTheFlag::updateGraphics(float dt)
{ {
FreeForAll::updateGraphics(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(); AbstractKart* kart = getKart(m_red_flag->getHolder());
m_blue_trans.setOrigin(m_blue_trans(g_kart_flag_offset)); const core::stringw& name = kart->getController()->getName();
m_blue_flag_node->setAnimationSpeed(fabsf(getKart(m_blue_holder) // I18N: Show when a player gets the red flag in CTF
->getSpeed()) * 3.0f + 25.0f); msg = _("%s has the red flag!", name);
if (kart->getController()->isLocalPlayerController())
SFXManager::get()->quickSound("wee");
} }
else else if (m_red_flag->isInBase() && !scored_recently)
m_blue_flag_node->setAnimationSpeed(25.0f); {
m_red_flag_indicator->setVisible(!isRedFlagInBase()); // I18N: Show when the red flag is returned to its base in CTF
m_blue_flag_indicator->setVisible(!isBlueFlagInBase()); 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 } // updateGraphics
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void CaptureTheFlag::update(int ticks) 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); FreeForAll::update(ticks);
for (auto it = m_swatter_reset_kart_ticks.begin(); 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 // Update new flags position
if (m_red_holder != -1) m_red_flag->update(ticks);
{ m_blue_flag->update(ticks);
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));
}
const bool red_flag_in_base = if (m_red_flag->getHolder() != -1 && m_blue_flag->isInBase() &&
m_red_trans.getOrigin() == m_orig_red_trans.getOrigin(); (m_blue_flag->getBaseOrigin() - m_red_flag->getOrigin()).length() <
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() <
g_capture_length) g_capture_length)
{ {
// Blue team scored // Blue team scored
NetworkString p(PROTOCOL_GAME_EVENTS); if (!NetworkConfig::get()->isNetworking() ||
p.setSynchronous(true); NetworkConfig::get()->isServer())
p.addUInt8(GameEventsProtocol::GE_CTF_RESET) {
.addUInt8(1 << 1 | 0) // Reset red flag int red_holder = m_red_flag->getHolder();
.addUInt8((int8_t)m_red_holder); int new_score = m_scores.at(red_holder) + g_captured_score;
STKHost::get()->sendPacketToAllPeers(&p, true); m_scores.at(red_holder) = new_score;
m_scores.at(m_red_holder) += g_captured_score; if (NetworkConfig::get()->isServer())
m_red_holder = -1; {
m_red_trans = m_orig_red_trans; NetworkString p(PROTOCOL_GAME_EVENTS);
m_blue_scores++; p.setSynchronous(true);
return; 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 && else if (m_blue_flag->getHolder() != -1 && m_red_flag->isInBase() &&
red_flag_in_base && (m_red_flag->getBaseOrigin() - m_blue_flag->getOrigin()).length() <
(m_orig_red_trans.getOrigin() - m_blue_trans.getOrigin()).length() <
g_capture_length) g_capture_length)
{ {
// Red team scored // Red team scored
NetworkString p(PROTOCOL_GAME_EVENTS); if (!NetworkConfig::get()->isNetworking() ||
p.setSynchronous(true); NetworkConfig::get()->isServer())
p.addUInt8(GameEventsProtocol::GE_CTF_RESET) {
.addUInt8(0 << 1 | 0) // Reset blue flag int blue_holder = m_blue_flag->getHolder();
.addUInt8((int8_t)m_blue_holder); int new_score = m_scores.at(blue_holder) + g_captured_score;
STKHost::get()->sendPacketToAllPeers(&p, true); m_scores.at(blue_holder) = new_score;
m_scores.at(m_blue_holder) += g_captured_score; if (NetworkConfig::get()->isServer())
m_blue_holder = -1; {
m_blue_trans = m_orig_blue_trans; NetworkString p(PROTOCOL_GAME_EVENTS);
m_red_scores++; p.setSynchronous(true);
return; 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 // Test if red or blue flag is touched
@ -279,220 +293,108 @@ void CaptureTheFlag::update(int ticks)
if (k->isEliminated() || k->getKartAnimation() || k->isSquashed()) if (k->isEliminated() || k->getKartAnimation() || k->isSquashed())
continue; continue;
if (m_red_holder == -1 && if (m_red_flag->canBeCaptured() &&
(k->getXYZ() - m_red_trans.getOrigin()).length() < (k->getXYZ() - m_red_flag->getOrigin()).length() <
g_capture_length) g_capture_length)
{ {
uint8_t kart_id = (uint8_t)k->getWorldKartId(); uint8_t kart_id = (uint8_t)k->getWorldKartId();
if (getKartTeam(kart_id) == KART_TEAM_RED) if (getKartTeam(kart_id) == KART_TEAM_RED)
{ {
if (!red_flag_in_base) if (!m_red_flag->isInBase())
{ {
// Return the flag // Return the flag
resetRedFlagToOrigin(); m_red_flag->resetToBase();
} }
} }
else else
{ {
// Get the flag // Get the flag
NetworkString p(PROTOCOL_GAME_EVENTS); m_red_flag->setCapturedByKart(kart_id);
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;
} }
} }
if (m_blue_holder == -1 && if (m_blue_flag->canBeCaptured() &&
(k->getXYZ() - m_blue_trans.getOrigin()).length() < (k->getXYZ() - m_blue_flag->getOrigin()).length() <
g_capture_length) g_capture_length)
{ {
uint8_t kart_id = (uint8_t)k->getWorldKartId(); uint8_t kart_id = (uint8_t)k->getWorldKartId();
if (getKartTeam(kart_id) == KART_TEAM_BLUE) if (getKartTeam(kart_id) == KART_TEAM_BLUE)
{ {
if (!blue_flag_in_base) if (!m_blue_flag->isInBase())
{ {
// Return the flag // Return the flag
resetBlueFlagToOrigin(); m_blue_flag->resetToBase();
} }
} }
else else
{ {
// Get the flag // Get the flag
NetworkString p(PROTOCOL_GAME_EVENTS); m_blue_flag->setCapturedByKart(kart_id);
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;
} }
} }
} }
} // update } // update
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void CaptureTheFlag::resetRedFlagToOrigin() int CaptureTheFlag::getRedHolder() const
{ {
NetworkString p(PROTOCOL_GAME_EVENTS); return m_red_flag->getHolder();
p.setSynchronous(true); } // getRedHolder
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
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void CaptureTheFlag::resetBlueFlagToOrigin() int CaptureTheFlag::getBlueHolder() const
{ {
NetworkString p(PROTOCOL_GAME_EVENTS); return m_blue_flag->getHolder();
p.setSynchronous(true); } // getBlueHolder
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
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void CaptureTheFlag::updateFlagNodes() bool CaptureTheFlag::isRedFlagInBase() const
{ {
#ifndef SERVER_ONLY return m_red_flag->isInBase();
Vec3 hpr; } // isRedFlagInBase
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
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void CaptureTheFlag::attachFlag(NetworkString& ns) bool CaptureTheFlag::isBlueFlagInBase() const
{ {
#ifndef SERVER_ONLY return m_blue_flag->isInBase();
bool attach_red_flag = ns.getUInt8() == 1; } // isBlueFlagInBase
unsigned kart_id = ns.getUInt8();
core::stringw get_msg; // ----------------------------------------------------------------------------
const core::stringw& name = getKart(kart_id)->getController() const Vec3& CaptureTheFlag::getRedFlag() const
->getName(); {
if (attach_red_flag) 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; scored_msg = _("%s captured the blue flag!", name);
m_red_flag_node->setParent(getKart(kart_id)->getNode()); m_red_scores++;
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);
} }
else else
{ {
m_blue_holder = kart_id; scored_msg = _("%s captured the red flag!", name);
m_blue_flag_node->setParent(getKart(kart_id)->getNode()); m_blue_scores++;
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);
} }
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 #ifndef SERVER_ONLY
uint8_t reset_info = ns.getUInt8(); m_race_gui->addMessage(scored_msg, NULL, 3.0f);
bool reset_red_flag = (reset_info >> 1 & 1) == 1; kart->getKartModel()
bool with_custom_transform = (reset_info & 1) == 1; ->setAnimation(KartModel::AF_WIN_START, true/*play_non_loop*/);
int8_t kart_id = ns.getUInt8(); m_scored_sound->play();
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();
#endif #endif
} // resetFlag } // ctfScored
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
bool CaptureTheFlag::getDroppedFlagTrans(const btTransform& kt, bool CaptureTheFlag::getDroppedFlagTrans(const btTransform& kt,
@ -545,48 +447,66 @@ bool CaptureTheFlag::isRaceOver()
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void CaptureTheFlag::loseFlagForKart(int kart_id) 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; return;
bool reset_red_flag = m_red_holder == kart_id; bool drop_red_flag = m_red_flag->getHolder() == kart_id;
btTransform dropped_trans = reset_red_flag ? btTransform dropped_trans = drop_red_flag ?
m_orig_red_trans : m_orig_blue_trans; m_red_flag->getBaseTrans() :
m_blue_flag->getBaseTrans();
bool succeed = getDroppedFlagTrans(getKart(kart_id)->getTrans(), bool succeed = getDroppedFlagTrans(getKart(kart_id)->getTrans(),
&dropped_trans); &dropped_trans);
NetworkString p(PROTOCOL_GAME_EVENTS); if (drop_red_flag)
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)
{ {
p.add(Vec3(dropped_trans.getOrigin())) if (succeed)
.add(dropped_trans.getRotation()); m_red_flag->dropFlagAt(dropped_trans);
} else
STKHost::get()->sendPacketToAllPeers(&p, true); m_red_flag->resetToBase();
if (reset_red_flag)
{
m_red_holder = -1;
m_red_trans = dropped_trans;
} }
else else
{ {
m_blue_holder = -1; if (succeed)
m_blue_trans = dropped_trans; 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 } // loseFlagForKart
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
bool CaptureTheFlag::kartHit(int kart_id, int hitter) bool CaptureTheFlag::kartHit(int kart_id, int hitter)
{ {
if (!FreeForAll::kartHit(kart_id, hitter)) if (isRaceOver())
return false; return false;
if (!NetworkConfig::get()->isNetworking() ||
NetworkConfig::get()->isServer())
handleScoreInServer(kart_id, hitter);
loseFlagForKart(kart_id); loseFlagForKart(kart_id);
return true; return true;
} // kartHit } // kartHit

View File

@ -31,6 +31,8 @@ namespace irr
} }
} }
class CTFFlag;
class CaptureTheFlag : public FreeForAll class CaptureTheFlag : public FreeForAll
{ {
private: private:
@ -48,16 +50,18 @@ private:
SFXBase* m_scored_sound; 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<int, int> m_swatter_reset_kart_ticks; std::map<int, int> m_swatter_reset_kart_ticks;
// ------------------------------------------------------------------------ std::shared_ptr<CTFFlag> m_red_flag, m_blue_flag;
void updateFlagNodes();
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
bool getDroppedFlagTrans(const btTransform& kt, btTransform* out) const; bool getDroppedFlagTrans(const btTransform& kt, btTransform* out) const;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -91,10 +95,6 @@ public:
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
virtual const std::string& getIdent() const OVERRIDE; virtual const std::string& getIdent() const OVERRIDE;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void attachFlag(NetworkString& ns);
// ------------------------------------------------------------------------
void resetFlag(NetworkString& ns);
// ------------------------------------------------------------------------
bool getKartCTFResult(unsigned int kart_id) const bool getKartCTFResult(unsigned int kart_id) const
{ {
if (m_red_scores == m_blue_scores) if (m_red_scores == m_blue_scores)
@ -114,25 +114,19 @@ public:
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
int getBlueScore() const { return m_blue_scores; } 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 bool isRedFlagInBase() const;
{
return m_red_holder == -1 &&
m_red_trans.getOrigin() == m_orig_red_trans.getOrigin();
}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
bool isBlueFlagInBase() const bool isBlueFlagInBase() const;
{
return m_blue_holder == -1 &&
m_blue_trans.getOrigin() == m_orig_blue_trans.getOrigin();
}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
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); void loseFlagForKart(int kart_id);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------

122
src/modes/ctf_flag.cpp Normal file
View File

@ -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<std::string>* 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

137
src/modes/ctf_flag.hpp Normal file
View File

@ -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<std::string>* 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

View File

@ -99,17 +99,37 @@ bool FreeForAll::kartHit(int kart_id, int hitter)
if (isRaceOver()) if (isRaceOver())
return false; return false;
NetworkString p(PROTOCOL_GAME_EVENTS); handleScoreInServer(kart_id, hitter);
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);
return true; return true;
} // kartHit } // 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) void FreeForAll::setKartScoreFromServer(NetworkString& ns)
{ {

View File

@ -31,6 +31,8 @@ protected:
bool m_count_down_reached_zero; bool m_count_down_reached_zero;
std::vector<int> m_scores; std::vector<int> m_scores;
// ------------------------------------------------------------------------
void handleScoreInServer(int kart_id, int hitter);
private: private:
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------

View File

@ -267,6 +267,8 @@ void ClientLobby::addAllPlayers(Event* event)
int hit_capture_limit = data.getUInt32(); int hit_capture_limit = data.getUInt32();
float time_limit = data.getFloat(); float time_limit = data.getFloat();
m_game_setup->setHitCaptureTime(hit_capture_limit, time_limit); m_game_setup->setHitCaptureTime(hit_capture_limit, time_limit);
uint16_t flag_return_timeout = data.getUInt16();
race_manager->setFlagReturnTicks(flag_return_timeout);
} }
configRemoteKart(players); configRemoteKart(players);
loadWorld(); loadWorld();

View File

@ -79,18 +79,14 @@ bool GameEventsProtocol::notifyEvent(Event* event)
ffa->setKartScoreFromServer(data); ffa->setKartScoreFromServer(data);
break; break;
} }
case GE_CTF_ATTACH: case GE_CTF_SCORED:
{ {
if (!ctf) if (!ctf)
throw std::invalid_argument("No CTF world"); throw std::invalid_argument("No CTF world");
ctf->attachFlag(data); uint8_t kart_id = data.getUInt8();
break; bool red_team_scored = data.getUInt8() == 1;
} int new_score = data.getUInt32();
case GE_CTF_RESET: ctf->ctfScored(kart_id, red_team_scored, new_score);
{
if (!ctf)
throw std::invalid_argument("No CTF world");
ctf->resetFlag(data);
break; break;
} }
case GE_STARTUP_BOOST: case GE_STARTUP_BOOST:

View File

@ -16,9 +16,8 @@ public:
GE_RESET_BALL = 3, GE_RESET_BALL = 3,
GE_PLAYER_GOAL = 4, GE_PLAYER_GOAL = 4,
GE_BATTLE_KART_SCORE = 5, GE_BATTLE_KART_SCORE = 5,
GE_CTF_ATTACH = 6, GE_CTF_SCORED = 6,
GE_CTF_RESET = 7, GE_STARTUP_BOOST = 7,
GE_STARTUP_BOOST = 8,
}; // GameEventType }; // GameEventType
private: private:
int m_last_finished_position; int m_last_finished_position;

View File

@ -595,6 +595,10 @@ void ServerLobby::asynchronousUpdate()
auto hcl = getHitCaptureLimit((float)players.size()); auto hcl = getHitCaptureLimit((float)players.size());
load_world_message->addUInt32(hcl.first).addFloat(hcl.second); load_world_message->addUInt32(hcl.first).addFloat(hcl.second);
m_game_setup->setHitCaptureTime(hcl.first, 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); configRemoteKart(players);

View File

@ -261,6 +261,15 @@ void loadServerLobbyFromConfig()
if (m_voting_timeout == 20.0f) if (m_voting_timeout == 20.0f)
m_voting_timeout = 30.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; int frequency_in_config = m_state_frequency;
if (frequency_in_config <= 0 || if (frequency_in_config <= 0 ||
frequency_in_config > stk_config->getPhysicsFPS()) frequency_in_config > stk_config->getPhysicsFPS())

View File

@ -81,6 +81,7 @@ RaceManager::RaceManager()
m_have_kart_last_position_on_overworld = false; m_have_kart_last_position_on_overworld = false;
m_num_local_players = 0; m_num_local_players = 0;
m_hit_capture_limit = 0; m_hit_capture_limit = 0;
m_flag_return_ticks = stk_config->time2Ticks(20.0f);
setMaxGoal(0); setMaxGoal(0);
setTimeTarget(0.0f); setTimeTarget(0.0f);
setReverseTrack(false); setReverseTrack(false);

View File

@ -356,6 +356,7 @@ private:
unsigned int m_num_spare_tire_karts; unsigned int m_num_spare_tire_karts;
unsigned int m_num_finished_karts; unsigned int m_num_finished_karts;
unsigned int m_num_finished_players; unsigned int m_num_finished_players;
unsigned m_flag_return_ticks;
int m_coin_target; int m_coin_target;
float m_time_target; float m_time_target;
int m_goal_target; int m_goal_target;
@ -896,7 +897,10 @@ public:
return m_minor_mode == MINOR_MODE_SOCCER || return m_minor_mode == MINOR_MODE_SOCCER ||
m_minor_mode == MINOR_MODE_CAPTURE_THE_FLAG; 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 }; // RaceManager
extern RaceManager *race_manager; extern RaceManager *race_manager;

View File

@ -532,8 +532,10 @@ void RaceGUI::drawGlobalMiniMap()
lower_y -(int)(draw_at.getY()-(m_minimap_player_size/2.2f))); lower_y -(int)(draw_at.getY()-(m_minimap_player_size/2.2f)));
draw2DImage(m_red_flag, rp, rs, NULL, NULL, true, true); 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<s32> rs(core::position2di(0, 0), m_red_flag->getSize()); core::rect<s32> rs(core::position2di(0, 0), m_red_flag->getSize());
core::rect<s32> rp(m_map_left+(int)(draw_at.getX()-(m_minimap_player_size/1.4f)), core::rect<s32> 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)), 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); 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<s32> bs(core::position2di(0, 0), m_blue_flag->getSize()); core::rect<s32> bs(core::position2di(0, 0), m_blue_flag->getSize());
core::rect<s32> bp(m_map_left+(int)(draw_at.getX()-(m_minimap_player_size/1.4f)), core::rect<s32> 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)), lower_y -(int)(draw_at.getY()+(m_minimap_player_size/2.2f)),