Skip to content

Make three strike battle any other strike battle and add an option to steal tires #5307

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions data/gui/screens/track_info.stkgui
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@
<label id="option-text" proportion="3" I18N="In the track info screen" text_align="left" align="center"/>
</div>

<div width="100%" height="fit" layout="horizontal-row" >
<div proportion="1" height="fit" layout="horizontal-row">
<div width="100%" height="fit" text-align="center" layout="vertical-row" >
<checkbox id="tire-stealing" align="center"/>
</div>
</div>
<spacer width="3%"/>
<label id="tire-stealing-text" proportion="3" I18N="In the track info screen" text_align="left" align="center"/>
</div>

<spacer width="1" height="1%"/>

<div width="100%" height="fit" layout="horizontal-row" >
Expand Down
3 changes: 3 additions & 0 deletions src/config/user_config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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."));
Expand Down
17 changes: 4 additions & 13 deletions src/karts/controller/spare_tire_ai.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,20 +142,11 @@ void SpareTireAI::crashed(const AbstractKart *k)
// Nothing happen when two spare tire karts crash each other
if (dynamic_cast<const SpareTireAI*>(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 (r)
r->addMessage(_("You can have at most 3 lives!"), k, 2.0f);
}
// Otherwise add one life for that kart
else
{
m_tsb_world->addKartLife(k->getWorldKartId());
if (r)
r->addMessage(_("+1 life."), k, 2.0f);
}
m_tsb_world->addKartLife(k->getWorldKartId());
if (r)
r->addMessage(_("+1 life."), k, 2.0f);
unspawn();

} // crashed
103 changes: 67 additions & 36 deletions src/modes/three_strikes_battle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -55,16 +56,38 @@ 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;
m_frame_count = 0;
m_start_time = irr_driver->getRealTime();
m_total_hit = 0;


} // ThreeStrikesBattle

namespace
{
const int redGradient[12] = {30, 64, 255, 255, 255, 128, 0, 0, 0, 0, 0, 0};
// ------------------------------------------------------------------------
const int greenGradient[12] = {0, 0, 0, 128, 255, 255, 255, 128, 255, 128, 0, 0};
// ------------------------------------------------------------------------
const int blueGradient[12] = {0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 192, 64};
// ------------------------------------------------------------------------
/* Must be two lower than the actual length, the first and last items in the
array are just buffers to prevent floating-point disasters */
const int gradientLength = 10;

video::SColor calculateColor(int lerp_index, float lerp_time)
{
int redVal = lerp(redGradient[lerp_index + 1], redGradient[lerp_index + 2], lerp_time);
int greenVal = lerp(greenGradient[lerp_index + 1], greenGradient[lerp_index + 2], lerp_time);
int blueVal = lerp(blueGradient[lerp_index + 1], blueGradient[lerp_index + 2], lerp_time);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be a separate function on an array/vector to invoke three times, but I suppose at this point it's kinda ok already.

return video::SColor(255, redVal, greenVal, blueVal);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is called twice in two different ifs, those ifs seem identical and related only to the color logic, and not to the game mode itself. Pass the current and the maximum number of lives to this function, and let it do all the transformations with lerp_*** inside. Don't forget to put spaces around operators -*/.

lerp is still repeated three times. Please move it into a separate function or lambda, e.g. return video::SColor(255, f(redGradient), f(greenGradient), f(blueGradient)) with

auto f = [=](const int* a) -> int {
    return lerp(a[lerp_index], a[lerp_index + 1], new_lerp_time);
};

}

//-----------------------------------------------------------------------------
/** Initialises the three strikes battle. It sets up the data structure
* to keep track of points etc. for each kart.
Expand Down Expand Up @@ -98,11 +121,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);

Expand All @@ -116,7 +139,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
Expand Down Expand Up @@ -229,10 +253,16 @@ 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)
m_kart_info[hitter].m_lives++;
}
else
{
m_total_hit++;

}
// record event
BattleEvent evt;
evt.m_time = getTime();
Expand Down Expand Up @@ -316,6 +346,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);
Expand Down Expand Up @@ -512,25 +547,22 @@ 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(128,128,128,0);
}
else
{
float lerp_time;
lerp_time = float(m_kart_info[i].m_lives-1)/float(UserConfigParams::m_ffa_time_limit);
int lerp_lower = floor(lerp_time*gradientLength);

rank_info.m_color = calculateColor(lerp_lower, lerp_time * gradientLength - lerp_lower);
}

std::ostringstream oss;
oss << m_kart_info[i].m_lives;

Expand All @@ -542,22 +574,21 @@ void ThreeStrikesBattle::getKartsDisplayInfo(
std::pair<int, video::SColor> 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)
{
color = video::SColor(128,128,128,0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this an intentionally semi-transparent yellow, or it should have been video::SColor(255, 128, 128, 128); ?

Copy link
Author

@zikzaksuper zikzaksuper Feb 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think i wrote that piece of code before noticing the alpha is the first parameter

(although that doesnt explain the 0)

}
else
{
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;
float lerp_time;
lerp_time = float(m_kart_info[id].m_lives-1)/float(UserConfigParams::m_ffa_time_limit);
int lerp_lower = floor(lerp_time*gradientLength);

color = color = calculateColor(lerp_lower, lerp_time * gradientLength - lerp_lower);
}
return std::make_pair(m_kart_info[id].m_lives, color);
} // getSpeedometerDigit
Expand Down
1 change: 1 addition & 0 deletions src/modes/three_strikes_battle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ class ThreeStrikesBattle : public WorldWithRank
bool spareTireKartsSpawned() const;
// ------------------------------------------------------------------------
void spawnSpareTireKarts();
// ------------------------------------------------------------------------

}; // ThreeStrikesBattles

Expand Down
38 changes: 29 additions & 9 deletions src/states_screens/track_info_screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ void TrackInfoScreen::loadedFromFile()
m_ai_kart_spinner = getWidget<SpinnerWidget>("ai-spinner");
m_ai_kart_label = getWidget<LabelWidget>("ai-text");
m_option = getWidget<CheckBoxWidget>("option");
m_tire_stealing = getWidget<CheckBoxWidget>("tire-stealing");
m_record_race = getWidget<CheckBoxWidget>("record");
m_tire_stealing->setState(false);
m_option->setState(false);
m_record_race->setState(false);

Expand Down Expand Up @@ -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<LabelWidget>("tire-stealing-text")->setVisible(!UserConfigParams::m_use_ffa_mode);
getWidget<LabelWidget>("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
Expand Down Expand Up @@ -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<LabelWidget>("option-text")->setVisible(reverse_available || random_item);
if (reverse_available)
Expand Down Expand Up @@ -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<LabelWidget>("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")
Expand All @@ -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)
{
Expand Down Expand Up @@ -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")
{
Expand Down
3 changes: 3 additions & 0 deletions src/states_screens/track_info_screen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Loading