diff --git a/data/gui/screens/track_info.stkgui b/data/gui/screens/track_info.stkgui
index 49093e9328b..f5290c22c2e 100644
--- a/data/gui/screens/track_info.stkgui
+++ b/data/gui/screens/track_info.stkgui
@@ -73,6 +73,16 @@
+
+
diff --git a/src/config/user_config.hpp b/src/config/user_config.hpp
index e61441e8207..f2c0f874354 100644
--- a/src/config/user_config.hpp
+++ b/src/config/user_config.hpp
@@ -457,6 +457,9 @@ namespace UserConfigParams
PARAM_PREFIX BoolUserConfigParam m_use_ffa_mode
PARAM_DEFAULT(BoolUserConfigParam(false, "use-ffa-mode",
&m_race_setup_group, "Use ffa mode instead of 3 strikes battle."));
+ PARAM_PREFIX BoolUserConfigParam m_tire_steal
+ PARAM_DEFAULT(BoolUserConfigParam(false, "tire-steal",
+ &m_race_setup_group, "Steal the tire when you hit a kart."));
PARAM_PREFIX IntUserConfigParam m_lap_trial_time_limit
PARAM_DEFAULT(IntUserConfigParam(3, "lap-trial-time-limit",
&m_race_setup_group, "Time limit in lap trial mode."));
diff --git a/src/karts/controller/spare_tire_ai.cpp b/src/karts/controller/spare_tire_ai.cpp
index 09210fabc24..21c0381525e 100644
--- a/src/karts/controller/spare_tire_ai.cpp
+++ b/src/karts/controller/spare_tire_ai.cpp
@@ -18,6 +18,7 @@
#include "karts/controller/spare_tire_ai.hpp"
+#include "config/user_config.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/kart_gfx.hpp"
#include "karts/max_speed.hpp"
@@ -142,12 +143,12 @@ void SpareTireAI::crashed(const AbstractKart *k)
// Nothing happen when two spare tire karts crash each other
if (dynamic_cast(k->getController()) != NULL) return;
- // Tell players that they can have at most 3 lives
+ // Add a life
RaceGUIBase* r = World::getWorld()->getRaceGUI();
- if (m_tsb_world->getKartLife(k->getWorldKartId()) == 3)
+ if (m_tsb_world->getKartLife(k->getWorldKartId()) == UserConfigParams::m_ffa_time_limit && !UserConfigParams::m_tire_steal)
{
if (r)
- r->addMessage(_("You can have at most 3 lives!"), k, 2.0f);
+ r->addMessage(_("You can have at most %d lives!"), k, 2.0f);
}
// Otherwise add one life for that kart
else
diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp
index c4f4c7982f9..86412044d7f 100644
--- a/src/modes/three_strikes_battle.cpp
+++ b/src/modes/three_strikes_battle.cpp
@@ -31,6 +31,7 @@
#include "karts/kart_properties_manager.hpp"
#include "physics/physics.hpp"
#include "states_screens/race_gui_base.hpp"
+#include "states_screens/track_info_screen.hpp"
#include "tracks/arena_graph.hpp"
#include "tracks/arena_node.hpp"
#include "tracks/terrain_info.hpp"
@@ -55,7 +56,7 @@ ThreeStrikesBattle::ThreeStrikesBattle() : WorldWithRank()
m_insert_tire = 0;
m_tire = irr_driver->getMesh(file_manager->getAsset(FileManager::MODEL,
- "tire.spm") );
+ "tire.spm") );
irr_driver->grabAllTextures(m_tire);
m_total_rescue = 0;
@@ -63,8 +64,36 @@ ThreeStrikesBattle::ThreeStrikesBattle() : WorldWithRank()
m_start_time = irr_driver->getRealTime();
m_total_hit = 0;
+
} // ThreeStrikesBattle
+namespace
+{
+ const int gradientLength = 9;
+
+ const int redGradient[gradientLength + 3] = {200, 255, 255, 255, 128, 0, 0, 0, 0, 0, 192, 200};
+ // ------------------------------------------------------------------------
+ const int greenGradient[gradientLength + 3] = {0, 100, 200, 255, 255, 255, 128, 255, 128, 0, 0, 0};
+ // ------------------------------------------------------------------------
+ const int blueGradient[gradientLength + 3] = {0, 0, 0, 0, 0, 0, 0, 255, 255, 192, 192, 0};
+ // ------------------------------------------------------------------------
+
+ video::SColor calculateColor(int current_lives, int starting_lives)
+ {
+ float lerp_time;
+ lerp_time = float(current_lives - 1)/float(starting_lives);
+ int lerp_index = floor(lerp_time*gradientLength);
+ lerp_time = lerp_time * gradientLength - lerp_index;
+ lerp_index = lerp_index % (gradientLength + 2);
+
+ auto channelBrightness = [lerp_time, lerp_index](const int* gradient)
+ {
+ return lerp(gradient[lerp_index], gradient[lerp_index + 1], lerp_time);
+ };
+
+ return video::SColor(255, channelBrightness(redGradient), channelBrightness(greenGradient), channelBrightness(blueGradient));
+ }
+}
//-----------------------------------------------------------------------------
/** Initialises the three strikes battle. It sets up the data structure
* to keep track of points etc. for each kart.
@@ -98,11 +127,11 @@ ThreeStrikesBattle::~ThreeStrikesBattle()
void ThreeStrikesBattle::reset(bool restart)
{
WorldWithRank::reset(restart);
-
+
float next_spawn_time =
- RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_BEST ? 40.0f :
- RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_HARD ? 30.0f :
- RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_MEDIUM ?
+ RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_BEST ? 40.0f :
+ RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_HARD ? 30.0f :
+ RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_MEDIUM ?
25.0f : 20.0f;
m_next_sta_spawn_ticks = stk_config->time2Ticks(next_spawn_time);
@@ -116,7 +145,8 @@ void ThreeStrikesBattle::reset(bool restart)
}
else
{
- m_kart_info[n].m_lives = 3;
+ // Gets starting amount of lives straight from the config (please tell me if this is the best method)
+ m_kart_info[n].m_lives = UserConfigParams::m_ffa_time_limit;
}
// no positions in this mode
@@ -130,11 +160,11 @@ void ThreeStrikesBattle::reset(bool restart)
if (core::stringc(curr->getName()) == "tire1")
{
- curr->setVisible(true);
+ curr->setVisible(UserConfigParams::m_ffa_time_limit >= 3);
}
else if (core::stringc(curr->getName()) == "tire2")
{
- curr->setVisible(true);
+ curr->setVisible(UserConfigParams::m_ffa_time_limit >= 2);
}
}
@@ -229,10 +259,17 @@ bool ThreeStrikesBattle::kartHit(int kart_id, int hitter)
assert(kart_id < (int)m_karts.size());
// make kart lose a life, ignore if in profiling mode
if (!UserConfigParams::m_arena_ai_stats)
+ {
m_kart_info[kart_id].m_lives--;
+ // Don't return the tire if 1. the checkbox says so or if 2. the kart hit itself or 3. if there is no hitter. That causes a crash otherwise.
+ if (hitter != -1 && hitter != kart_id && UserConfigParams::m_tire_steal)
+ addKartLife(hitter);
+
+ }
else
+ {
m_total_hit++;
-
+ }
// record event
BattleEvent evt;
evt.m_time = getTime();
@@ -316,6 +353,11 @@ bool ThreeStrikesBattle::kartHit(int kart_id, int hitter)
// schedule a tire to be thrown away (but can't do it in this callback
// because the caller is currently iterating the list of track objects)
+ // don't do this if the tire is getting stolen
+
+ if (hitter != -1 && hitter != kart_id && UserConfigParams::m_tire_steal)
+ return true;
+
m_insert_tire++;
core::vector3df wheel_pos(m_karts[kart_id]->getKartWidth()*0.5f,
0.0f, 0.0f);
@@ -512,25 +554,18 @@ void ThreeStrikesBattle::getKartsDisplayInfo(
{
RaceGUIBase::KartIconDisplayInfo& rank_info = (*info)[i];
- // reset color
+ // Samples colours from a gradient at a regular interval. The colours get funky if it goes out of bound, i dont know why.
+
rank_info.lap = -1;
-
- switch(m_kart_info[i].m_lives)
+
+ if (m_kart_info[i].m_lives == 0)
{
- case 3:
- rank_info.m_color = video::SColor(255, 0, 255, 0);
- break;
- case 2:
- rank_info.m_color = video::SColor(255, 255, 229, 0);
- break;
- case 1:
- rank_info.m_color = video::SColor(255, 255, 0, 0);
- break;
- case 0:
- rank_info.m_color = video::SColor(128, 128, 128, 0);
- break;
+ rank_info.m_color = video::SColor(255,128,128,128);
+ }
+ else
+ {
+ rank_info.m_color = calculateColor(m_kart_info[i].m_lives, UserConfigParams::m_ffa_time_limit);
}
-
std::ostringstream oss;
oss << m_kart_info[i].m_lives;
@@ -542,22 +577,17 @@ void ThreeStrikesBattle::getKartsDisplayInfo(
std::pair ThreeStrikesBattle::getSpeedometerDigit(
const AbstractKart *kart) const
{
+ // Samples colours from a gradient at a regular interval. The colours get funky if it goes out of bound, i dont know why.
+
video::SColor color = video::SColor(255, 255, 255, 255);
int id = kart->getWorldKartId();
- switch(m_kart_info[id].m_lives)
+ if (m_kart_info[id].m_lives == 0)
{
- case 3:
- color = video::SColor(255, 0, 255, 0);
- break;
- case 2:
- color = video::SColor(255, 255, 229, 0);
- break;
- case 1:
- color = video::SColor(255, 255, 0, 0);
- break;
- case 0:
- color = video::SColor(255, 128, 128, 128);
- break;
+ color = video::SColor(255,128,128,128);
+ }
+ else
+ {
+ color = calculateColor(m_kart_info[id].m_lives, UserConfigParams::m_ffa_time_limit);
}
return std::make_pair(m_kart_info[id].m_lives, color);
} // getSpeedometerDigit
diff --git a/src/modes/three_strikes_battle.hpp b/src/modes/three_strikes_battle.hpp
index d9886760f51..3a3dc7d0b0c 100644
--- a/src/modes/three_strikes_battle.hpp
+++ b/src/modes/three_strikes_battle.hpp
@@ -150,6 +150,7 @@ class ThreeStrikesBattle : public WorldWithRank
bool spareTireKartsSpawned() const;
// ------------------------------------------------------------------------
void spawnSpareTireKarts();
+ // ------------------------------------------------------------------------
}; // ThreeStrikesBattles
diff --git a/src/states_screens/track_info_screen.cpp b/src/states_screens/track_info_screen.cpp
index aca3e167c44..fb3b12b779b 100644
--- a/src/states_screens/track_info_screen.cpp
+++ b/src/states_screens/track_info_screen.cpp
@@ -77,7 +77,9 @@ void TrackInfoScreen::loadedFromFile()
m_ai_kart_spinner = getWidget("ai-spinner");
m_ai_kart_label = getWidget("ai-text");
m_option = getWidget("option");
+ m_tire_stealing = getWidget("tire-stealing");
m_record_race = getWidget("record");
+ m_tire_stealing->setState(false);
m_option->setState(false);
m_record_race->setState(false);
@@ -251,14 +253,23 @@ void TrackInfoScreen::init()
m_target_type_label->setText(_("Game mode"), false);
m_target_type_spinner->clearLabels();
m_target_type_spinner->addLabel(_("3 Strikes Battle"));
+
m_target_type_spinner->addLabel(_("Free-For-All"));
m_target_type_spinner->setValue(UserConfigParams::m_use_ffa_mode ? 1 : 0);
-
- m_target_value_label->setText(_("Maximum time (min.)"), false);
m_target_value_spinner->setValue(UserConfigParams::m_ffa_time_limit);
+ if (UserConfigParams::m_use_ffa_mode)
+ m_target_value_label->setText(_("Maximum time (min.)"), false);
+
+ else
+ m_target_value_label->setText(_("Starting tires"), false);
+
+ m_tire_stealing->setVisible(!UserConfigParams::m_use_ffa_mode);
+ getWidget("tire-stealing-text")->setVisible(!UserConfigParams::m_use_ffa_mode);
+ getWidget("tire-stealing-text")->setText(_("Steal tires"), false);
+ m_tire_stealing->setState(UserConfigParams::m_tire_steal);
- m_target_value_label->setVisible(UserConfigParams::m_use_ffa_mode);
- m_target_value_spinner->setVisible(UserConfigParams::m_use_ffa_mode);
+ m_target_value_label->setVisible(true);
+ m_target_value_spinner->setVisible(true);
}
// Lap count m_lap_spinner
@@ -286,12 +297,13 @@ void TrackInfoScreen::init()
m_target_value_label->setText(_("Maximum time (min.)"), false);
m_target_value_spinner->setValue(UserConfigParams::m_lap_trial_time_limit);
}
- // Reverse track or random item in arena
+ // Reverse track and random item in arena
// -------------
const bool reverse_available = m_track->reverseAvailable()
&& !(RaceManager::get()->isEggHuntMode());
const bool random_item = m_track->hasNavMesh();
+
m_option->setVisible(reverse_available || random_item);
getWidget("option-text")->setVisible(reverse_available || random_item);
if (reverse_available)
@@ -663,9 +675,14 @@ void TrackInfoScreen::eventCallback(Widget* widget, const std::string& name,
{
const bool enable_ffa = target_value != 0;
UserConfigParams::m_use_ffa_mode = enable_ffa;
+
+ m_tire_stealing->setVisible(!enable_ffa);
+ getWidget("tire-stealing-text")->setVisible(!enable_ffa);
- m_target_value_label->setVisible(enable_ffa);
- m_target_value_spinner->setVisible(enable_ffa);
+ if (enable_ffa)
+ m_target_value_label->setText(_("Maximum time (min.)"), false);
+ else
+ m_target_value_label->setText(_("Starting tires"), false);
}
}
else if (name == "target-value-spinner")
@@ -682,8 +699,7 @@ void TrackInfoScreen::eventCallback(Widget* widget, const std::string& name,
{
const bool enable_ffa = m_target_type_spinner->getValue() != 0;
- if (enable_ffa)
- UserConfigParams::m_ffa_time_limit = m_target_value_spinner->getValue();
+ UserConfigParams::m_ffa_time_limit = m_target_value_spinner->getValue();
}
else if (m_is_lap_trial)
{
@@ -712,6 +728,10 @@ void TrackInfoScreen::eventCallback(Widget* widget, const std::string& name,
// checkbox.
updateHighScores();
}
+ }
+ else if (name == "tire-stealing")
+ {
+ UserConfigParams::m_tire_steal = m_tire_stealing->getState();
}
else if (name == "record")
{
diff --git a/src/states_screens/track_info_screen.hpp b/src/states_screens/track_info_screen.hpp
index 6ccc66e62bd..13e67e72012 100644
--- a/src/states_screens/track_info_screen.hpp
+++ b/src/states_screens/track_info_screen.hpp
@@ -90,6 +90,9 @@ class TrackInfoScreen : public GUIEngine::Screen,
/** Check box for reverse mode or random item in arena. */
GUIEngine::CheckBoxWidget* m_option;
+ /** Check box for tire stealing. */
+ GUIEngine::CheckBoxWidget* m_tire_stealing;
+
/** Check box for record race. */
GUIEngine::CheckBoxWidget* m_record_race;