From 8992a820ad22f4a3c1193416afa80f46d320d62d Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Fri, 10 Nov 2023 16:31:34 +0100 Subject: [PATCH 001/188] Double physics FPS from 120 to 240 This comes with a performance cost that will require optimization, but is long overdue to unlock higher FPS and improve high-speed collisions --- data/stk_config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/stk_config.xml b/data/stk_config.xml index 278f95d377f..ef4b23e64cd 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -144,7 +144,7 @@ --> Date: Fri, 10 Nov 2023 16:32:15 +0100 Subject: [PATCH 002/188] Drastically reduce nitro respawn time Nitro respawn time : 2.0 -> 1.0 Gift box respawn time : 2.0 -> 1.5 The aim is to significantly reduce the impact of nitro denial in online races. It has been tested to still make a 'stop and collect nitro' tactic ineffective even with a big nitro. --- data/stk_config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/stk_config.xml b/data/stk_config.xml index ef4b23e64cd..243ff5c769b 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -222,7 +222,7 @@ - + From 38836e5018488d7d5c237ec07c4f4ac22b91638d Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Fri, 10 Nov 2023 19:24:19 +0100 Subject: [PATCH 004/188] Nerf the swatter Duration once held : 10.0 -> 7.0 | Effective swatting distance : 3.0 -> 2.5 Duration squashed : 5.0 -> 4.0 | Squashed speed fraction : 0.5 -> 0.6 --- data/kart_characteristics.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 4f37ba4a3b6..7187d6f2e7a 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -257,8 +257,8 @@ squash-duration: How long a kart will remain squashed. squash-slowdown: Percentage of max speed that a kart is restricted to. --> - + - + + @@ -377,24 +378,26 @@ duration-factor="*1" fade-out-time="*1" /> - + + - + + From ea59d4450244326320bde3a578b3629b685e16e1 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sat, 11 Nov 2023 13:32:18 +0100 Subject: [PATCH 006/188] Increase the skidding reduce-turn-max parameter --- data/kart_characteristics.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index d76c2cff96e..223fbebcb4d 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -370,7 +370,7 @@ - + @@ -384,7 +384,7 @@ - + @@ -397,7 +397,7 @@ - + From d4f2b1b6aec12de5ca982f1ffbdb6881c4d3cc0e Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sat, 11 Nov 2023 15:35:22 +0100 Subject: [PATCH 007/188] Rebalance nitro usage and kart classes - The bonus nitro duration (after consumption is stopped) is reduced from 1.0 to 0.8 - Big nitro cans give 2.5 instead of 3.0 - Light karts get a max speed boost of 6 instead of 4.5 - Medium karts get a max speed boost of 5.5 instead of 5 - Light and medium karts nitro consumption are slightly increased to balance the speed bonus - The minimum nitro burst is changed from 8/15th of a second to 0.5s It was too common for light and medium karts to collect nitro faster than they could use it, this issue should be alleviated. Furthermore, switch the min_nitro_consumption to an int16 to allow values greater than 64 (required with the physics FPS change). --- data/kart_characteristics.xml | 10 +++++----- src/karts/kart.hpp | 2 +- src/karts/kart_properties.cpp | 2 +- src/karts/kart_properties.hpp | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 223fbebcb4d..d877f9689e7 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -316,8 +316,8 @@ speed due to nitro fades out. max: How much nitro a kart can store. --> + small-container="1" big-container="2.5" + max-speed-increase="5" duration="0.8" fade-out-time="2.0" max="20" /> - - @@ -379,7 +379,7 @@ - @@ -392,7 +392,7 @@ - From 1f8297e2e95cf5f1abb1e74683228a2153be104d Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 12 Nov 2023 01:50:20 +0100 Subject: [PATCH 010/188] Implement a third-level purple skid, - Reduce red skid boost from 6.5 to 6.0 - Introduce a new skid boost of 7.5 lasting 5s after 4.2s of accumulation - Update the code to handle graphics for purple skid - Update replay recording and reading to handle purple skid --- data/gfx/skid3.xml | 29 ++++++++++++++++++ data/kart_characteristics.xml | 6 ++-- src/karts/ghost_kart.cpp | 3 +- src/karts/kart_gfx.cpp | 56 ++++++++++++++++++++++++++-------- src/karts/kart_gfx.hpp | 19 +++++++++--- src/karts/max_speed.hpp | 1 + src/karts/skidding.cpp | 28 +++++++++-------- src/replay/replay_base.hpp | 4 ++- src/replay/replay_play.cpp | 56 ++++++++++++++++++++++++++++++++-- src/replay/replay_recorder.cpp | 5 +-- 10 files changed, 166 insertions(+), 41 deletions(-) create mode 100644 data/gfx/skid3.xml diff --git a/data/gfx/skid3.xml b/data/gfx/skid3.xml new file mode 100644 index 00000000000..dcd4009ad47 --- /dev/null +++ b/data/gfx/skid3.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 2f186456a1a..05f2d0bd86d 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -168,9 +168,9 @@ but you will always keep on doing a left turn, just more or less. --> diff --git a/src/karts/ghost_kart.cpp b/src/karts/ghost_kart.cpp index 99f92a55ef7..14fc3ac4ce7 100644 --- a/src/karts/ghost_kart.cpp +++ b/src/karts/ghost_kart.cpp @@ -215,7 +215,8 @@ void GhostKart::update(int ticks) getKartGFX()->setGFXFromReplay(m_all_replay_events[idx].m_nitro_usage, m_all_replay_events[idx].m_zipper_usage, m_all_replay_events[idx].m_skidding_effect, - m_all_replay_events[idx].m_red_skidding); + m_all_replay_events[idx].m_red_skidding, + m_all_replay_events[idx].m_purple_skidding); getKartGFX()->update(dt); Vec3 front(0, 0, getKartLength()*0.5f); diff --git a/src/karts/kart_gfx.cpp b/src/karts/kart_gfx.cpp index b727879767c..5633359f22f 100644 --- a/src/karts/kart_gfx.cpp +++ b/src/karts/kart_gfx.cpp @@ -43,6 +43,7 @@ KartGFX::KartGFX(const AbstractKart *kart, bool is_day) m_nitro_light = NULL; m_skidding_light_1 = NULL; m_skidding_light_2 = NULL; + m_skidding_light_3 = NULL; m_kart = kart; m_wheel_toggle = 0; m_skid_level = 0; @@ -83,9 +84,20 @@ KartGFX::KartGFX(const AbstractKart *kart, bool is_day) m_skidding_light_2->setVisible(false); m_skidding_light_2->setName(("skidding emitter 2 (" + m_kart->getIdent() + ")").c_str() ); + + // For the second skidding level + m_skidding_light_3 = + irr_driver->addLight(core::vector3df(0.0f, 0.1f, -0.5f * length - + 0.05f), /* force */0.4f, /*radius*/5.0f, + 0.7f, 0.0f, 0.8f, false, node); + m_skidding_light_3->setVisible(false); + m_skidding_light_3->setName(("skidding emitter 3 (" + m_kart->getIdent() + + ")").c_str() ); + m_nitro_light->grab(); m_skidding_light_1->grab(); m_skidding_light_2->grab(); + m_skidding_light_3->grab(); } #endif @@ -118,6 +130,8 @@ KartGFX::KartGFX(const AbstractKart *kart, bool is_day) addEffect(KGFX_SKID1R, "skid1.xml", rear_right, true ); addEffect(KGFX_SKID2L, "skid2.xml", rear_left, true ); addEffect(KGFX_SKID2R, "skid2.xml", rear_right, true ); + addEffect(KGFX_SKID3L, "skid3.xml", rear_left, true ); + addEffect(KGFX_SKID3R, "skid3.xml", rear_right, true ); addEffect(KGFX_SKID0L, "skid0.xml", rear_left, true ); addEffect(KGFX_SKID0R, "skid0.xml", rear_right, true ); if (!kart->getKartModel()->getExhaustXML().empty()) @@ -151,6 +165,7 @@ KartGFX::~KartGFX() m_nitro_light->drop(); m_skidding_light_1->drop(); m_skidding_light_2->drop(); + m_skidding_light_3->drop(); } #endif @@ -189,9 +204,10 @@ void KartGFX::addEffect(KartGFXType type, const std::string &file_name, throw std::runtime_error(err); } //kind = new ParticleKind(file_manager->getGfxFile(file_name)); - // Skid0 and Skid2 are only used to store the emitter type, and a - // wheeless kart has no terrain effects. + // Skid0, Skid2 and Skid3 are only used to store the emitter type, + // and a wheeless kart has no terrain effects. if(type==KGFX_SKID0L || type==KGFX_SKID0R || type==KGFX_SKID2L || type==KGFX_SKID2R || + type==KGFX_SKID3L || type==KGFX_SKID3R || (type==KGFX_TERRAIN && m_kart->isWheeless()) ) emitter = NULL; else if(type==KGFX_TERRAIN) @@ -221,6 +237,8 @@ void KartGFX::addEffect(KartGFXType type, const std::string &file_name, m_skid_kind1 = kind; else if (type==KGFX_SKID2L || type==KGFX_SKID2R) m_skid_kind2 = kind; + else if (type==KGFX_SKID3L || type==KGFX_SKID3R) + m_skid_kind3 = kind; #else m_all_emitters.push_back(NULL); #endif @@ -249,12 +267,12 @@ void KartGFX::reset() // ---------------------------------------------------------------------------- /** Selects the correct skidding particle type depending on skid bonus level. * \param level Must be 0 (no bonus, showing tiny sparks), 1 (accumulated enough - * for level 1 bonus), or 2 (accumulated enough for level 2 bonus). + * for level 1 bonus), 2 (accumulated enough for level 2 bonus) or 3. */ void KartGFX::setSkidLevel(const unsigned int level) { assert(level >= 0); - assert(level <= 2); + assert(level <= 3); m_skid_level = level; const ParticleKind *pk; if (level == 0) @@ -265,10 +283,14 @@ void KartGFX::setSkidLevel(const unsigned int level) { pk = m_skid_kind1; } - else + else if (level == 2) { pk = m_skid_kind2; } + else + { + pk = m_skid_kind3; + } // const ParticleKind *pk = level==1 ? m_skid_kind1 : m_skid_kind2; #ifndef SERVER_ONLY if (GUIEngine::isNoGraphics()) @@ -493,7 +515,7 @@ void KartGFX::updateNitroGraphics(float nitro_frac) // ---------------------------------------------------------------------------- /** Updates the skiddng light (including disabling it). * \param level Which level of light to display: 0 no light at all, - * 1: level 1, 2 level 2. + * 1: level 1, 2 level 2, 3 level 3. */ void KartGFX::updateSkidLight(unsigned int level) { @@ -501,14 +523,15 @@ void KartGFX::updateSkidLight(unsigned int level) if (!GUIEngine::isNoGraphics() && CVS->isGLSL()) { m_skidding_light_1->setVisible(level == 1); - m_skidding_light_2->setVisible(level > 1); + m_skidding_light_2->setVisible(level == 2); + m_skidding_light_3->setVisible(level > 2); } #endif } // updateSkidLight // ---------------------------------------------------------------------------- void KartGFX::getGFXStatus(int* nitro, bool* zipper, - int* skidding, bool* red_skidding) const + int* skidding, bool* red_skidding, bool* purple_skidding) const { #ifndef SERVER_ONLY if (GUIEngine::isNoGraphics()) @@ -518,6 +541,7 @@ void KartGFX::getGFXStatus(int* nitro, bool* zipper, bool z = false; int s = 0; bool r = false; + bool p = false; if (m_all_emitters[KGFX_NITRO1]) { @@ -533,18 +557,20 @@ void KartGFX::getGFXStatus(int* nitro, bool* zipper, { s = m_all_emitters[KGFX_SKIDL]->getCreationRate(); r = m_skid_level == 2; + p = m_skid_level == 3; } *nitro = n; *zipper = z; *skidding = s; *red_skidding = r; + *purple_skidding = p; #endif } // getGFXStatus // ---------------------------------------------------------------------------- void KartGFX::setGFXFromReplay(int nitro, bool zipper, - int skidding, bool red_skidding) + int skidding, bool red_skidding, bool purple_skidding) { #ifndef SERVER_ONLY if (GUIEngine::isNoGraphics()) @@ -576,8 +602,9 @@ void KartGFX::setGFXFromReplay(int nitro, bool zipper, if (skidding > 0) { - const ParticleKind* skid_kind = red_skidding ? m_skid_kind2 - : m_skid_kind1; + const ParticleKind* skid_kind = purple_skidding ? m_skid_kind3 : + red_skidding ? m_skid_kind2 : + m_skid_kind1; if (m_all_emitters[KGFX_SKID1L]) m_all_emitters[KGFX_SKID1L]->setParticleType(skid_kind); @@ -586,8 +613,9 @@ void KartGFX::setGFXFromReplay(int nitro, bool zipper, if (CVS->isGLSL()) { - m_skidding_light_1->setVisible(!red_skidding); - m_skidding_light_2->setVisible(red_skidding); + m_skidding_light_1->setVisible(!red_skidding && !purple_skidding); + m_skidding_light_2->setVisible(red_skidding && !purple_skidding); + m_skidding_light_3->setVisible(purple_skidding); } setCreationRateAbsolute(KartGFX::KGFX_SKIDL, (float)skidding); @@ -602,6 +630,7 @@ void KartGFX::setGFXFromReplay(int nitro, bool zipper, { m_skidding_light_1->setVisible(false); m_skidding_light_2->setVisible(false); + m_skidding_light_3->setVisible(false); } } #endif @@ -616,6 +645,7 @@ void KartGFX::setGFXInvisible() m_nitro_light->setVisible(false); m_skidding_light_1->setVisible(false); m_skidding_light_2->setVisible(false); + m_skidding_light_3->setVisible(false); m_kart->getKartModel()->toggleHeadlights(false); } #endif diff --git a/src/karts/kart_gfx.hpp b/src/karts/kart_gfx.hpp index 6515d07a586..8ca5b41d122 100644 --- a/src/karts/kart_gfx.hpp +++ b/src/karts/kart_gfx.hpp @@ -57,6 +57,8 @@ class KartGFX KGFX_SKID1R = KGFX_SKIDR, KGFX_SKID2L, KGFX_SKID2R, + KGFX_SKID3L, + KGFX_SKID3R, KGFX_SKID0L, KGFX_SKID0R, KGFX_EXHAUST1, @@ -64,15 +66,19 @@ class KartGFX KGFX_COUNT}; private: - /** The particle kind for skidding bonus level 0. */ + /** The particle kind for skidding bonus level 0 (no boost yet). */ const ParticleKind *m_skid_kind0; - /** The particle kind for skidding bonus level 1. */ + /** The particle kind for skidding bonus level 1 (yellow). */ const ParticleKind *m_skid_kind1; - /** The particle kind for skidding bonus level 2. */ + /** The particle kind for skidding bonus level 2 (red). */ const ParticleKind *m_skid_kind2; + /** The particle kind for skidding bonus level 3 (purple). */ + const ParticleKind *m_skid_kind3; + + /** Vector of all particle emitters. */ std::vector m_all_emitters; @@ -94,6 +100,9 @@ class KartGFX /** A light that's shown on the second skid-level with another color. */ irr::scene::ISceneNode* m_skidding_light_2; + /** A light that's shown on the third skid-level with another color. */ + irr::scene::ISceneNode* m_skidding_light_3; + void addEffect(KartGFXType type, const std::string &file_name, const Vec3 &position, bool important); void resizeBox(const KartGFXType type, float new_size); @@ -112,9 +121,9 @@ class KartGFX void updateNitroGraphics(float f); void updateSkidLight(unsigned int level); void getGFXStatus(int* nitro, bool* zipper, - int* skidding, bool* red_skidding) const; + int* skidding, bool* red_skidding, bool* purple_skidding) const; void setGFXFromReplay(int nitro, bool zipper, - int skidding, bool red_skidding); + int skidding, bool red_skidding, bool purple_skidding); void setGFXInvisible(); }; // KartWGFX diff --git a/src/karts/max_speed.hpp b/src/karts/max_speed.hpp index ac7fa46eb6c..1f0422d8504 100644 --- a/src/karts/max_speed.hpp +++ b/src/karts/max_speed.hpp @@ -41,6 +41,7 @@ friend class KartRewinder; MS_INCREASE_RUBBER, MS_INCREASE_SKIDDING, MS_INCREASE_RED_SKIDDING, + MS_INCREASE_PURPLE_SKIDDING, MS_INCREASE_MAX}; /** The categories to use for decreasing the speed of a kart: diff --git a/src/karts/skidding.cpp b/src/karts/skidding.cpp index d022bf5e47f..e792627ce0b 100644 --- a/src/karts/skidding.cpp +++ b/src/karts/skidding.cpp @@ -269,17 +269,18 @@ float Skidding::updateGraphics(float dt) float bonus_time, bonus_speed, bonus_force; unsigned int level = getSkidBonus(&bonus_time, &bonus_speed, &bonus_force); - if (m_kart->m_max_speed - ->isSpeedIncreaseActive(MaxSpeed::MS_INCREASE_SKIDDING) && - m_skid_bonus_end_ticks > World::getWorld()->getTicksSinceStart()) - { - level = 1; - } - else if (m_kart->m_max_speed - ->isSpeedIncreaseActive(MaxSpeed::MS_INCREASE_RED_SKIDDING) && - m_skid_bonus_end_ticks > World::getWorld()->getTicksSinceStart()) + + if (m_skid_bonus_end_ticks > World::getWorld()->getTicksSinceStart()) { - level = 2; + if (m_kart->m_max_speed + ->isSpeedIncreaseActive(MaxSpeed::MS_INCREASE_SKIDDING)) + level = 1; + else if (m_kart->m_max_speed + ->isSpeedIncreaseActive(MaxSpeed::MS_INCREASE_RED_SKIDDING)) + level = 2; + else if (m_kart->m_max_speed + ->isSpeedIncreaseActive(MaxSpeed::MS_INCREASE_PURPLE_SKIDDING)) + level = 3; } if (level == 0 && m_graphical_remaining_jump_time <= 0.0f && @@ -295,7 +296,7 @@ float Skidding::updateGraphics(float dt) m_kart->getKartGFX()->updateSkidLight(level); } - if (bonus_time > 0 || level == 1 || level == 2) + if (bonus_time > 0 || level == 1 || level == 2 || level == 3) { m_kart->getKartGFX()->setCreationRateRelative(KartGFX::KGFX_SKIDL, 1.0f); @@ -549,8 +550,9 @@ void Skidding::update(int ticks, bool is_on_ground, m_skid_time = stk_config->time2Ticks(t); if(bonus_time>0) { - unsigned int bonus_cat = (level == 1) ? MaxSpeed::MS_INCREASE_SKIDDING : - MaxSpeed::MS_INCREASE_RED_SKIDDING; + unsigned int bonus_cat = (level == 1) ? MaxSpeed::MS_INCREASE_SKIDDING : + (level == 2) ? MaxSpeed::MS_INCREASE_RED_SKIDDING : + MaxSpeed::MS_INCREASE_PURPLE_SKIDDING; m_kart->m_max_speed-> instantSpeedIncrease(bonus_cat, bonus_speed, bonus_speed/2, diff --git a/src/replay/replay_base.hpp b/src/replay/replay_base.hpp index 0a996e12882..92ac30cfd2e 100644 --- a/src/replay/replay_base.hpp +++ b/src/replay/replay_base.hpp @@ -93,6 +93,8 @@ class ReplayBase : public NoCopy int m_skidding_effect; /** Kart skidding showing red flame or not. */ bool m_red_skidding; + /** Kart skidding showing purple flame or not. */ + bool m_purple_skidding; /** True if the kart recorded is jumping. */ bool m_jumping; }; // KartReplayEvent @@ -105,7 +107,7 @@ class ReplayBase : public NoCopy // ------------------------------------------------------------------------ /** Returns the version number of the replay file recorderd by this executable. * This is also used as a maximum supported version by this exexcutable. */ - unsigned int getCurrentReplayVersion() const { return 4; } + unsigned int getCurrentReplayVersion() const { return 5; } // ------------------------------------------------------------------------ /** This is used to check that a loaded replay file can still diff --git a/src/replay/replay_play.cpp b/src/replay/replay_play.cpp index 1891f089d5e..35ba1dccc21 100644 --- a/src/replay/replay_play.cpp +++ b/src/replay/replay_play.cpp @@ -387,7 +387,7 @@ void ReplayPlay::readKartData(FILE *fd, char *next_line, bool second_replay) fgets(s, 1023, fd); float x, y, z, rx, ry, rz, rw, time, speed, steer, w1, w2, w3, w4, nitro_amount, distance; int skidding_state, attachment, item_amount, item_type, special_value, - nitro, zipper, skidding, red_skidding, jumping; + nitro, zipper, skidding, red_skidding, purple_skidding, jumping; // Check for EV_TRANSFORM event: // ----------------------------- @@ -440,8 +440,8 @@ void ReplayPlay::readKartData(FILE *fd, char *next_line, bool second_replay) } } - //version 4 replays (STK 0.9.4 and higher) - else + //version 4 replays (STK 1.0 to 1.4 and higher) + else if (rd.m_replay_version == 4) { if(sscanf(s, "%f %f %f %f %f %f %f %f %f %f %f %f %f %f %d %d %f %d %d %d %f %d %d %d %d %d\n", &time, @@ -488,6 +488,56 @@ void ReplayPlay::readKartData(FILE *fd, char *next_line, bool second_replay) Log::warn("Replay", "Ignored."); } } + + //version 5 replays (Development towards STK 2.0, definition may be changed) + else if (rd.m_replay_version == 5) + { + if(sscanf(s, "%f %f %f %f %f %f %f %f %f %f %f %f %f %f %d %d %f %d %d %d %f %d %d %d %d %d %d\n", + &time, + &x, &y, &z, + &rx, &ry, &rz, &rw, + &speed, &steer, &w1, &w2, &w3, &w4, &skidding_state, + &attachment, &nitro_amount, &item_amount, &item_type, &special_value, + &distance, &nitro, &zipper, &skidding, &red_skidding, &purple_skidding, &jumping + )==27) + { + btQuaternion q(rx, ry, rz, rw); + btVector3 xyz(x, y, z); + PhysicInfo pi = {0}; + BonusInfo bi = {0}; + KartReplayEvent kre = {0}; + + pi.m_speed = speed; + pi.m_steer = steer; + pi.m_suspension_length[0] = w1; + pi.m_suspension_length[1] = w2; + pi.m_suspension_length[2] = w3; + pi.m_suspension_length[3] = w4; + pi.m_skidding_state = skidding_state; + bi.m_attachment = attachment; + bi.m_nitro_amount = nitro_amount; + bi.m_item_amount = item_amount; + bi.m_item_type = item_type; + bi.m_special_value = special_value; + kre.m_distance = distance; + kre.m_nitro_usage = nitro; + kre.m_zipper_usage = zipper!=0; + kre.m_skidding_effect = skidding; + kre.m_red_skidding = red_skidding!=0; + kre.m_purple_skidding = purple_skidding!=0; + kre.m_jumping = jumping != 0; + m_ghost_karts[kart_num]->addReplayEvent(time, + btTransform(q, xyz), pi, bi, kre); + } + else + { + // Invalid record found + // --------------------- + Log::warn("Replay", "Can't read replay data line %d:", i); + Log::warn("Replay", "%s", s); + Log::warn("Replay", "Ignored."); + } + } } // for i } // readKartData diff --git a/src/replay/replay_recorder.cpp b/src/replay/replay_recorder.cpp index 59156c08ea5..35a70626de4 100644 --- a/src/replay/replay_recorder.cpp +++ b/src/replay/replay_recorder.cpp @@ -300,7 +300,7 @@ void ReplayRecorder::update(int ticks) r->m_distance = 0.0f; kart->getKartGFX()->getGFXStatus(&(r->m_nitro_usage), - &(r->m_zipper_usage), &(r->m_skidding_effect), &(r->m_red_skidding)); + &(r->m_zipper_usage), &(r->m_skidding_effect), &(r->m_red_skidding), &(r->m_purple_skidding)); r->m_jumping = kart->isJumping(); } // for i @@ -446,7 +446,7 @@ void ReplayRecorder::save() const PhysicInfo *q = &(m_physic_info[k][i]); const BonusInfo *b = &(m_bonus_info[k][i]); const KartReplayEvent *r = &(m_kart_replay_event[k][i]); - fprintf(fd, "%f %f %f %f %f %f %f %f %f %f %f %f %f %f %d %d %f %d %d %d %f %d %d %d %d %d\n", + fprintf(fd, "%f %f %f %f %f %f %f %f %f %f %f %f %f %f %d %d %f %d %d %d %f %d %d %d %d %d %d\n", p->m_time, p->m_transform.getOrigin().getX(), p->m_transform.getOrigin().getY(), @@ -472,6 +472,7 @@ void ReplayRecorder::save() (int)r->m_zipper_usage, r->m_skidding_effect, (int)r->m_red_skidding, + (int)r->m_purple_skidding, (int)r->m_jumping ); } // for i From 51ceb4eeabef0b85824d7ab111b4cad2a40e7883 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 12 Nov 2023 01:59:22 +0100 Subject: [PATCH 011/188] Adjust the engine power boost from skidding per kart-class --- data/kart_characteristics.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 05f2d0bd86d..04716aced73 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -170,7 +170,7 @@ visual="1.25" visual-time="0.7" revert-visual-time="0.7" min-speed="10" time-till-bonus="1.0 2.7 4.2" bonus-speed="4.0 6.0 7.5" bonus-time="3.0 4.0 5.0" - bonus-force="250 350 500" + bonus-force="300 400 500" physical-jump-time="0" graphical-jump-time="0.4" post-skid-rotate-factor="1" reduce-turn-min="0.2" reduce-turn-max="0.8" enabled="true" /> @@ -370,7 +370,7 @@ - + @@ -384,7 +384,7 @@ - + @@ -397,7 +397,7 @@ - + From 7f45111d1ea71c347d38c05ed0f1874c43fed65e Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 12 Nov 2023 02:15:38 +0100 Subject: [PATCH 012/188] Introduce the medium-h kart class It shares stats with medium, except in three crucial points: - It has a lower max-speed - It has a better turn radius, almost as good as light karts - It can trigger skid bonuses 10% earlier than other kart classes More testing and balancing will be required. --- data/kart_characteristics.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 04716aced73..8778f48d0e4 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -377,6 +377,20 @@ + + + + + + + + + + + Date: Sun, 12 Nov 2023 03:04:51 +0100 Subject: [PATCH 013/188] Fix per-class explosion duration Also change the criteria for double rotation from direct hit to an explosion duration >= 1.15f ; as the double rotation looks bad with the short explosion duration of heavies. --- data/kart_characteristics.xml | 10 +++++----- src/karts/explosion_animation.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 8778f48d0e4..99dacc9d38e 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -296,7 +296,7 @@ determines how height the kart is being thrown). invulnerability-time: how long a kart will be invulnerable after being hit by an explosion. - radius: Kart closer to this value will be affected by + radius: Karts closer to this value will be affected by an explosion as well. --> @@ -372,7 +372,7 @@ - + @@ -387,7 +387,7 @@ - + @@ -400,7 +400,7 @@ - + @@ -413,7 +413,7 @@ - + diff --git a/src/karts/explosion_animation.cpp b/src/karts/explosion_animation.cpp index fc7f2c4a323..7bff68d351c 100644 --- a/src/karts/explosion_animation.cpp +++ b/src/karts/explosion_animation.cpp @@ -226,7 +226,7 @@ void ExplosionAnimation::init(bool direct_hit, const Vec3& normal, m_curr_rotation.setPitch(pitch); m_curr_rotation.setRoll(roll); - const int max_rotation = direct_hit ? 2 : 1; + const int max_rotation = (timer > 1.15f ) ? 2 : 1; // To get rotations in both directions for each axis we determine a // number calculated by world created ticks between // -(max_rotation-1) and +(max_rotation-1) From 965414cb8e91a4401fd031341c19bf9ce7f5e0dc Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 12 Nov 2023 03:12:58 +0100 Subject: [PATCH 014/188] Adjust the speed display in kart selection --- src/guiengine/widgets/kart_stats_widget.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/guiengine/widgets/kart_stats_widget.cpp b/src/guiengine/widgets/kart_stats_widget.cpp index 6e1b0d6e7e5..e4625182278 100644 --- a/src/guiengine/widgets/kart_stats_widget.cpp +++ b/src/guiengine/widgets/kart_stats_widget.cpp @@ -103,10 +103,10 @@ void KartStatsWidget::setValues(const KartProperties* props, HandicapLevel h) kp_computed.getCombinedCharacteristic()->getMass()/3.89f, "mass.png", "mass", _("Mass")); - // The base speed is of 25 + // The base speed is of 27.5 // Here we are not fully proportional, because small differences matter more setSkillValues(SKILL_SPEED, - (kp_computed.getCombinedCharacteristic()->getEngineMaxSpeed() - 20.0f) * 15.0f, + (kp_computed.getCombinedCharacteristic()->getEngineMaxSpeed() - 22.25f) * 15.0f, "speed.png", "speed", _("Maximum speed")); // The acceleration depend on power and mass, and it changes depending on speed From cddf482bb4cb647f901648b741adde868176c26d Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 12 Nov 2023 14:11:15 +0100 Subject: [PATCH 015/188] Tweak kart classes turn radiuses The change from 1/0.8 to 1/0.9 of the regular turn radius with skidding was quite significant. - Increase again the base turn radiuses so that the turn radiuses while skidding remain worse than in 1.X. - Reduce the heavy penalty compared to light from 40% to 36%, as heavies already felt on the edge. - Make medium-h slightly worse relatively. - Make turning at low speeds (< 10) easier --- data/kart_characteristics.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 99dacc9d38e..c9ef67e70bf 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -364,7 +364,7 @@ - + - + - + - + Date: Sun, 12 Nov 2023 14:36:34 +0100 Subject: [PATCH 016/188] Fix reverse gear ignoring turn radiuses by speed - Also lower the max-speed cap for reverse gear - Improve turn radiuses at low speeds again (mostly with a speed between 5 and 15) --- data/kart_characteristics.xml | 31 +++++++++++++++---------------- src/karts/kart.cpp | 4 ++++ 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index c9ef67e70bf..4dbbe514fb8 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -98,7 +98,7 @@ gear. --> + brake-time-increase="6" max-speed-reverse-ratio="0.6" /> - + - + - + @@ -364,9 +367,8 @@ - - + + @@ -378,9 +380,8 @@ duration-factor="*1" fade-out-time="*1" /> - - + + @@ -392,9 +393,8 @@ - - + + @@ -405,9 +405,8 @@ - - + + diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index aef467d75fd..995e34aff70 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -913,6 +913,10 @@ float Kart::getMaxSteerAngle(float speed) const turn_angle_at_speed.setY(i, sinf( 1.0f / turn_angle_at_speed.getY(i)) * m_kart_properties->getWheelBase()); + // Make reverse mode turn the same way as forward + if (speed < 0.0f) + speed = -speed; + return turn_angle_at_speed.get(speed); } // getMaxSteerAngle From d190f2ec7d8f01cd2413bc0a09c378f84c3f7b9e Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 12 Nov 2023 17:41:03 +0100 Subject: [PATCH 017/188] Fix skidding bonuses cumulating at fadeout - Skidding bonuses no longer cumulate with others during fadeout - The bonus speed and engine force is always the maximum of the active and fadeout bonuses - Purple skidding bonus no longer accumulate with other skid bonuses --- src/karts/max_speed.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/karts/max_speed.cpp b/src/karts/max_speed.cpp index f586a84cb38..aee04b37b42 100644 --- a/src/karts/max_speed.cpp +++ b/src/karts/max_speed.cpp @@ -405,6 +405,9 @@ void MaxSpeed::update(int ticks) // determines the overall decrease of maximum speed. // --------------------------------------------------- float slowdown_factor = 1.0f; + float max_skid_speed = 0.0f; + float max_skid_engine_force = 0.0f; + for(unsigned int i=MS_DECREASE_MIN; i 0 && - getSpeedIncreaseTicksLeft(MS_INCREASE_RED_SKIDDING) > 0) + + // Prevent the different kinds of skidding speed increases from cumulating + // We select the highest active max-speed bonus and engine-force bonus, + // which may come from different skid levels, including fade-out bonuses, + // and remove the others. + + for(unsigned int i=MS_INCREASE_SKIDDING; i<=MS_INCREASE_PURPLE_SKIDDING; i++) { - SpeedIncrease &speedup = m_speed_increase[MS_INCREASE_SKIDDING]; + SpeedIncrease &speedup = m_speed_increase[i]; + if (speedup.getSpeedIncrease() > max_skid_speed) + max_skid_speed = speedup.getSpeedIncrease(); + if (speedup.getEngineForce() > max_skid_engine_force) + max_skid_engine_force = speedup.getEngineForce(); + m_current_max_speed -= speedup.getSpeedIncrease(); m_add_engine_force -= speedup.getEngineForce(); } + m_current_max_speed += max_skid_speed; + m_add_engine_force += max_skid_engine_force; + m_current_max_speed *= slowdown_factor; // Then cap the current speed of the kart From b72853bd8fe00d72f2990be4690a00ed66188699 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 12 Nov 2023 19:02:23 +0100 Subject: [PATCH 018/188] Fix karts chassis colliding into the ground This happened at either very high kart speeds or at high physics FPS --- src/physics/btKart.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp index 657e99686f4..6ea4db9b961 100644 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -493,9 +493,17 @@ void btKart::updateVehicle( btScalar step ) float dif = m_kart->getKartProperties()->getStabilityDownwardImpulseFactor(); if(dif!=0 && m_num_wheels_on_ground==4) { - float f = -fabsf(m_kart->getSpeed()) * dif; + float f1 = fabsf(m_kart->getSpeed()); + // Excessive downward impulse at high speeds is harmful + if (f1 > 35.0f) + f1 = 35.0f; + // Adjust the impulse force for the time step, + // to prevent changes to physics FPS from breaking things. + // An increase in impulse frequency is almost equivalent to + // an increase in impulse strength of the same factor. + float f2 = -f1 * dif * step * 120.0f; btVector3 downwards_impulse = m_chassisBody->getWorldTransform().getBasis() - * btVector3(0, f, 0); + * btVector3(0, f2, 0); m_chassisBody->applyCentralImpulse(downwards_impulse); } From 21bc047f9e1b176bdd153e86d969a7fecb58c220 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Mon, 13 Nov 2023 00:58:43 +0100 Subject: [PATCH 019/188] Use a different config folder in anticipation of breaking profile changes --- src/io/file_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/file_manager.cpp b/src/io/file_manager.cpp index 2d3dce66f47..ea40c80c206 100644 --- a/src/io/file_manager.cpp +++ b/src/io/file_manager.cpp @@ -1066,7 +1066,7 @@ void FileManager::checkAndCreateConfigDir() if(m_user_config_dir.size()>0 && *m_user_config_dir.rbegin()!='/') m_user_config_dir += "/"; - m_user_config_dir += "config-0.10/"; + m_user_config_dir += "config-2.X-dev/"; if(!checkAndCreateDirectoryP(m_user_config_dir)) { Log::warn("FileManager", "Can not create config dir '%s', " From bb83ba3dc0858d10012cc6325fdccf4162f30710 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Mon, 13 Nov 2023 02:11:55 +0100 Subject: [PATCH 020/188] Tweak difficulties, use more relative values for classes --- data/kart_characteristics.xml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 4dbbe514fb8..a3761caf326 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -348,16 +348,22 @@ + + - + + - + + @@ -372,7 +378,7 @@ - + @@ -385,8 +391,8 @@ - + @@ -398,7 +404,7 @@ - + @@ -410,7 +416,7 @@ - + From 5d7bc057577d7fdaf06b5c5f961e9e1e2fb5139e Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Mon, 13 Nov 2023 14:07:32 +0100 Subject: [PATCH 021/188] Add a fifth difficulty level between novice and intermediate Relative speeds of difficulties: Old (no skid, no nitro) : ----_----_----_----_----_----_----_----_----_----_----_----X----_----_----_----X----_----_-X--_----X Old ( + skid, + nitro) : ----_----_----_----_----_----_----_----_----_----_----_----_----_----X----_----_----X----_---X_----X New : ----_----_----_----_----_----_----_----_----_----_----_----X----_----_----X----_----X----_----X----X - Add support for a fifth difficulty in the race selection screen - Add support for a fifth difficulty level in story mode - Ensure old replays are read with the correct difficutly setting AI has received no tweak specific for this difficulty level, as AI rewrites are intended. Likewise the story mode challenges are just placeholders. --- data/achievements.xml | 2 + data/challenges/abyss.challenge | 4 + data/challenges/candela_city.challenge | 4 + data/challenges/cocoa_temple.challenge | 6 ++ data/challenges/cornfield_crossing.challenge | 4 + data/challenges/fortmagma.challenge | 4 + data/challenges/gp1.challenge | 4 + data/challenges/gp2.challenge | 4 + data/challenges/gp3.challenge | 4 + data/challenges/gp4.challenge | 4 + data/challenges/granparadiso_island.challenge | 4 + data/challenges/green_valley.challenge | 4 + data/challenges/hacienda.challenge | 4 + data/challenges/lighthouse.challenge | 4 + data/challenges/mansion.challenge | 4 + data/challenges/mines.challenge | 4 + data/challenges/minigolf.challenge | 4 + data/challenges/olivermath.challenge | 4 + data/challenges/sandtrack.challenge | 4 + data/challenges/scotland.challenge | 4 + data/challenges/snowmountain.challenge | 4 + data/challenges/snowpeak.challenge | 4 + data/challenges/stk_enterprise.challenge | 4 + data/challenges/volcano_island.challenge | 4 + data/challenges/xr591.challenge | 4 + data/challenges/zengarden.challenge | 4 + data/gui/dialogs/select_challenge.stkgui | 2 + data/gui/icons/cup_chocolate.png | Bin 0 -> 12506 bytes data/gui/icons/difficulty_casual.png | Bin 0 -> 5846 bytes data/gui/screens/race_setup.stkgui | 2 + data/kart_characteristics.xml | 9 +- data/stk_config.xml | 21 ++++ src/achievements/achievements_status.cpp | 4 +- src/achievements/achievements_status.hpp | 92 +++++++++--------- src/challenges/challenge_data.cpp | 20 ++-- src/challenges/challenge_data.hpp | 1 + src/challenges/challenge_status.cpp | 14 ++- src/challenges/challenge_status.hpp | 7 +- src/challenges/story_mode_status.cpp | 12 +++ src/challenges/story_mode_status.hpp | 10 +- src/config/player_profile.hpp | 7 +- src/karts/controller/arena_ai.cpp | 3 +- src/karts/controller/battle_ai.cpp | 1 + src/karts/kart_properties.cpp | 2 + src/modes/demo_world.cpp | 7 +- src/modes/world.cpp | 14 +-- src/race/race_manager.cpp | 3 + src/race/race_manager.hpp | 2 + src/replay/replay_play.cpp | 7 ++ .../dialogs/achievement_progress_dialog.cpp | 4 + .../dialogs/select_challenge.cpp | 28 ++++-- src/states_screens/feature_unlocked.cpp | 9 ++ src/states_screens/race_gui_overworld.cpp | 29 +++--- src/states_screens/race_gui_overworld.hpp | 4 +- 54 files changed, 319 insertions(+), 99 deletions(-) create mode 100644 data/gui/icons/cup_chocolate.png create mode 100644 data/gui/icons/difficulty_casual.png diff --git a/data/achievements.xml b/data/achievements.xml index c611e56f538..a6871e3366a 100644 --- a/data/achievements.xml +++ b/data/achievements.xml @@ -57,6 +57,8 @@ III - Race started and finished counters, by difficulty (linear races only) easy-started easy-finished + casual-started + casual-finished medium-started medium-finished hard-started diff --git a/data/challenges/abyss.challenge b/data/challenges/abyss.challenge index f88e3e71fea..0f91f783770 100644 --- a/data/challenges/abyss.challenge +++ b/data/challenges/abyss.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/candela_city.challenge b/data/challenges/candela_city.challenge index 10082f3aefd..9fcebae5045 100644 --- a/data/challenges/candela_city.challenge +++ b/data/challenges/candela_city.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/cocoa_temple.challenge b/data/challenges/cocoa_temple.challenge index 3f058b99baa..59fab638059 100644 --- a/data/challenges/cocoa_temple.challenge +++ b/data/challenges/cocoa_temple.challenge @@ -20,6 +20,12 @@ replay_file="challenge_cocoa_temple_intermediate.replay"/> + + + + + diff --git a/data/challenges/cornfield_crossing.challenge b/data/challenges/cornfield_crossing.challenge index bcaf00ab1f6..f6cc68cebea 100644 --- a/data/challenges/cornfield_crossing.challenge +++ b/data/challenges/cornfield_crossing.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/fortmagma.challenge b/data/challenges/fortmagma.challenge index d2993a51c55..d9d491476f3 100644 --- a/data/challenges/fortmagma.challenge +++ b/data/challenges/fortmagma.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/gp1.challenge b/data/challenges/gp1.challenge index b54da93f616..227ac3c1210 100644 --- a/data/challenges/gp1.challenge +++ b/data/challenges/gp1.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/gp2.challenge b/data/challenges/gp2.challenge index 2a1043a9cb7..05e1d1dd1d5 100644 --- a/data/challenges/gp2.challenge +++ b/data/challenges/gp2.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/gp3.challenge b/data/challenges/gp3.challenge index 58241edba02..ca5df6dec27 100644 --- a/data/challenges/gp3.challenge +++ b/data/challenges/gp3.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/gp4.challenge b/data/challenges/gp4.challenge index 4ed7eeb9079..1acd2b15fc4 100644 --- a/data/challenges/gp4.challenge +++ b/data/challenges/gp4.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/granparadiso_island.challenge b/data/challenges/granparadiso_island.challenge index 8eea0148bb8..c1bd855ca90 100644 --- a/data/challenges/granparadiso_island.challenge +++ b/data/challenges/granparadiso_island.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/green_valley.challenge b/data/challenges/green_valley.challenge index 4b3f5a83899..a8385af10d4 100644 --- a/data/challenges/green_valley.challenge +++ b/data/challenges/green_valley.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/hacienda.challenge b/data/challenges/hacienda.challenge index c3211c96793..ebbaa7ccf07 100644 --- a/data/challenges/hacienda.challenge +++ b/data/challenges/hacienda.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/lighthouse.challenge b/data/challenges/lighthouse.challenge index f20b4f065af..a3504c59e6a 100644 --- a/data/challenges/lighthouse.challenge +++ b/data/challenges/lighthouse.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/mansion.challenge b/data/challenges/mansion.challenge index d45eaa261aa..2185dc266f5 100644 --- a/data/challenges/mansion.challenge +++ b/data/challenges/mansion.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/mines.challenge b/data/challenges/mines.challenge index bbbcd23cd95..931f15cc2ed 100644 --- a/data/challenges/mines.challenge +++ b/data/challenges/mines.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/minigolf.challenge b/data/challenges/minigolf.challenge index e42dedd4240..f8504ec6309 100644 --- a/data/challenges/minigolf.challenge +++ b/data/challenges/minigolf.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/olivermath.challenge b/data/challenges/olivermath.challenge index 5955ac90f18..52d186635e0 100644 --- a/data/challenges/olivermath.challenge +++ b/data/challenges/olivermath.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/sandtrack.challenge b/data/challenges/sandtrack.challenge index 1cb76437c0c..fad25f1b2df 100644 --- a/data/challenges/sandtrack.challenge +++ b/data/challenges/sandtrack.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/scotland.challenge b/data/challenges/scotland.challenge index 2d2cb676d9e..3424b285631 100644 --- a/data/challenges/scotland.challenge +++ b/data/challenges/scotland.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/snowmountain.challenge b/data/challenges/snowmountain.challenge index 28bf6975e43..32f471ade51 100644 --- a/data/challenges/snowmountain.challenge +++ b/data/challenges/snowmountain.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/snowpeak.challenge b/data/challenges/snowpeak.challenge index c72874e86a9..4ca3f6c4e26 100644 --- a/data/challenges/snowpeak.challenge +++ b/data/challenges/snowpeak.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/stk_enterprise.challenge b/data/challenges/stk_enterprise.challenge index 7a9803cbc26..e6298ca50c1 100644 --- a/data/challenges/stk_enterprise.challenge +++ b/data/challenges/stk_enterprise.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/volcano_island.challenge b/data/challenges/volcano_island.challenge index 064d851e48e..738e3f709ed 100644 --- a/data/challenges/volcano_island.challenge +++ b/data/challenges/volcano_island.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/xr591.challenge b/data/challenges/xr591.challenge index 92a90747f12..86c5d73a667 100644 --- a/data/challenges/xr591.challenge +++ b/data/challenges/xr591.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/challenges/zengarden.challenge b/data/challenges/zengarden.challenge index b00c129f483..74b82a065d7 100644 --- a/data/challenges/zengarden.challenge +++ b/data/challenges/zengarden.challenge @@ -17,6 +17,10 @@ + + + + diff --git a/data/gui/dialogs/select_challenge.stkgui b/data/gui/dialogs/select_challenge.stkgui index 2db2228d30e..ade0e497d56 100644 --- a/data/gui/dialogs/select_challenge.stkgui +++ b/data/gui/dialogs/select_challenge.stkgui @@ -9,6 +9,8 @@ + EX>4Tx04R}tkv&MmKpe$iQ>CIU4ptCx$WWc^q9Tr3g(6f4wL+^7CYOFelZGV4 z#ZhoAIQX$xb#QUk)xlK|1V2DrU7QqMq{ROvg%&X$9QWhhy~o`jaO`Vc`7g9ypYGUg;H1>f;?j{slq;yla$+@B+&<}C&UMB-Uym^SeS@${x` zaNZ}5vXZP4pA(OpbV1@rt}7nDaW1+n@XV;0NzW5UiN#_ED;>;ArbawP98)!&@`bF& zD(5ZETBXKX_v9}O=k=9kuG1Vr5{p=Z1Q7~qD5C-!aawg!ETm~a;o~26{Svtpa+Sfz zv4AQx$gUs!4}N!R6(%RVq;LZ0esP?SQ6RJnH0zG@ee5{R6Cn5uT`3I^o1%`{U_t^oo0pGnACrMmmzb6f>*Tnx1 z+Sv0)C-CbJ_0!b04AIaEsaoXpC~jsFnVmvrCV6te;)g@A_udW60;hnNfmie~bu~{| z;wmQeHhpZ=*8FB*d9@3AfREhMjFANCl)%n|V}WCXYlC!=QnIs4aCS&?s`3`MSGn~d za0qw_IH-@kfO8!HSKgBUIp7yHF;a`U_gBcrb8o7WgLcHQ;+o84ODu09$}ZfM1A;^TiIw6}W{WhL}~vx0J${j*Tqkk@+kI zS8`$;27m@2d+A@-1iW7#I)%e0vv;l6_1b;WM`ciMw3G{SBdsCW{RU(zmv8_kY zloTXQ1CdO4$iTHMvW{e;0FNDkBJif=ux0x?X7U!9yhSFPXDnOf{6vm(GndTLr-6R} z{!ZHx*8vc5+5RvhN*7G%)$Ofp?`2mxk7{X${z*N?tJPcz)s2Q>*u=wB`+ z6dgD*4*LdSL|c*X)W)Keu9D!cRFcDWK{`m+0oUdDaF%`NM;N-Ou3g|L0{A_0rMT4~`4cRVZ;C1qN*};>cHv^j_7-?}U5ch!kF!%@Vw;KbYaaj!E%rqj0EVd!(*Th`2m=h0QX$Xu z^fb9biJq1pwdzHaJdM3b?kC5?TuQ0MHUcx-Nx6k#8L>vcI}m z_yO?V*IFgK9suY-W!~Cj4S^1Ls9$ldFbqvNL+N!x{VjtasX%}NiTaFI3*Ku@@Cp!s zItS{Wpa=<7xgRN!($eQ7I5yHFzP1g@QbAwix=88xs-s{@zPhK#K>XbN7`XRZ2f$;* zWZHHugZug*fxr|X453B3;$~k82nmKJybzTj5TZg7fT@TcQ3vY13V%^shi;jxJ;J-P zivE}J#FtuPgzN_O-jPYk|2`QzKU;M$X~_b>i^4Sp&RN=^IWmtPyA{|IJ^7){kgn16 z5_5ncC<%n-Y9Ye*KmZ5`Ghis9KO^u`AigAs4BzsH5YfG8VFlqOO6>cRz^&l?An~OH z8^KW4Ny1*U#tpAEJAF*7kH<3*Q!O~W_8T`M_Qac8v#T| z^nwrG(n)=36dOoLsXi3>Z7~GjrM;wG zdj6ZL%)ivZrvC$c9T>e_0IUH%4ZLfC(xOTG@ec;voG$=htoAS)dfP#^GCMU%Av1;H zl$3fEE{*^bgM{O#ZudxUNzS!bA$EECE1Ekg^1jLBS`g{3MU?9z>+8 zlfwam`-fW=^`0FOLwJwt9b6m$4ALe3?UqggvYl5FbFA? zogm5t4gta=y!R_bLf<1&0wH9C@FIYLfcBEMF9pKJq9(X+ELIX-O32uPpAJY)&cN7f zZ+J`Kx3n~#R|R|q_*iT}rUTw{$0iy~Q4s~p7cHJSI>@2(iwcQ`Y2k6DVbavOoTi>$ ztYV46#0aIC3G93Z(~y|B%Aoh@=lF&rfUrFd7iC~zsq9~cNoB-)0Tt>1O=xWc1c)t# zz>%&`aPP9I5{zfz{3MLdNb=fRzSPkJtnGoj*S6A-G|G}q2f^~TwfIXW84=>G%g z*5UT565xZtm!ns2?oRP5_iT=jfJFl!+8~mC}gIPmZc4R>8lG+hQ9a1Of3b3CZxGYndW!|)kF@mRFK#XAQ3o@#Fdb@VQ2=X zaxk5RoTX~ha*H(6dw^fMxrVmdfEOgpfTt6Tf|fm@ue@+34hZOo zcCBIC%GR_i&p@^bNhq=iJEZG#?a9}&sv%urI?#V z7V^ma4266aX_a{_2#&C%S)kkuhzrLjyf!W)vP2@ZXDG3xpkzzNaxjrqAjsvFbvFZK z;&p#$v;e=nRnXIrsC0vaHcmBHSP1rA$n)q+7o)cx2iAEw461;S#uC4;t->H0m@05= zaGK^CLAowUZ%cx@govHFwz>o#yVKz8cp=W`UD)u59}(L%ZC%u~boee@i)$kti$Z1w zH$RIk=5TC1)sBT_ssA0jSoN!tRm`4RjiQwFs)qzN0$aLT&r3@BS`&$3C~0GOLCQt= z-5=NhYnlYY5J=DS@x2in3FI7;@!0}{S%*xaz{cJ#`qDM(GuqDC*;@~EM?ui8Y5j*m z3D5zY3<6+tcZ&BvaHAgr9T}hEYfqeHNY4H6?^svM9V;8E2135H#}0)-7bF3K+^>l* zv{5I$aBVbx@1J+VnY=kv7}7;Lwx9i5NHE8d%B}NXYZc%i7@n0(P2oUT(A(KPT;kiO=TDFS!ksPG!j>ZYDgxl99?5tXzXJEoH*}VRIKnI&Hrf^t z9vFOsNBG3oj(5Z-_o}%A=RB%mV$72qLPFZh}0b^0eEbV$d05}`h#RYJ*uTnP#` zSS2O)Ev-(hnB>>HwaH$>WQPE{a*;kDi*;u9F*bnE)PHD|o2q^({gO+W{i} z4IwZ4d%V2VTt4^xJ@D}AfKe&J>hu%4t%cMUgvK*xa{TLoi~h84?yd+a{02~O0QW_8 z|08#9Q2~BeRwI5<2P}?iaZ+hL17CPLSK*F&40LOc`r#GUmULe|N*m$|+)f1WF*iVh zUuRTtyKZWPo4TssMOb?hX`PIIhzBOEmE* zA}&V42(P{|0)}80aL*dydludVdv?Wl&q!iwNd|SOH;Jlc*`#UF4g5(teyzqo?^706 z(hzYou(JXH8v^N?6K5;9{~u6wY`GYQ|BlXOha|#JhT+^ysgl$6m>#~UjV>98z0V_h zH3)nIaKXW_OOzb*V8`QhG0%5IZ*y0F4Cp=P$x5EB3>l8=WYE(wS=T9T+5bZbErVrd z>2Hn)z}DcIRoy)yQ!z50pJ&<*>KJxteDP2vQN34=Q>2iciHX6xVd92LuXM!Dd*5{z z=FGpwBNteu22Q5u7oe|jlI>M0*^1?D^qcdCM9fG-cn%)7*R$ank-BARh1&+js^bikD}@V z{4s#iX_c@R)YYbZG)Osz8t|n+#0Chwt|;ZIa<6#>!>c4YbcTYND(^pWnmE1x1MR9W zWQ0_DUqJF?@)engStA;n9-qzo6SuLy!dJVGg{>%dfcI#F#UIa(!TvEBV#ZWs8j-Bk zj&;yC52%5l6!5s*145vVFP@NYM7mr7`{1(OPL&b-;n9HWhv47{yjbaUod({=!YYPM z;7!33>sF`z`5c^#nr=)PQOkXFCht$&9UHo%7qmxX26K$_sAXLt_vD)c@Qs6TU>Kgb z0F7-Ofr1MeRs$Xo;y@5RqTD|h7fW|>T>dKI=?CICi1J zeLi8J>i7IH6M2RY-&+wtSPuN>Mf}FEY5#N%bt@yB=7ep52nd2iFB8`ee8vr5=Pm6D zgrt4o%?S!o{aF`jTV4XL{y-bl3mqRPQfmLfTRKBu9OKpf$UrS$4&m>WGi8a>*U?O+ zEy(Y)xF$+}2>fPTv?`?U{Zz69>~MTP3WDh2xDmt84Rb%iFoUe38Jx?K0x~9Ga0!mb zEGlRJ$wgfA5Nd^$KRSW+opt{2*>^4*7tQ^ip@TZ#4!-kG&z=hD;}1W)F3yP>W z28 zgAwWq5V&)vDq_a?0*U|r=o|m#uGY|R7@D>D{wcdE_eTNXXiGBmlmFWfPKG9^Au0Hc zJG!cY;LG!Xpior`_{6e#e9tj5*<2U|P80}M#1*vldlBM?zQd_JFQZbSivIWYyNopf zoC?t|eIwA->sgcf>+H)@0bhtC{)cuns8Vt7Ssk0d`pHn7xc%+m<3`gIj{~2JUiH_H zg;P^~tu_4ejj^k{-%rfqgLyT8S8@d$znG!q!k$BC%PVbVARgcW!PQmCc=-W+F4WZ{ z(9%W58G3>BeGv%|(*KL}@f}?uT(BThRF9DAOpsjIO3b->Q~fXCL(!8T*iuVhYcljO zU;NQ<-1VO$l2D4{)+cq$- zT0qCGuo+yT>H*!5_bRZY;P|NI%p?>f)HOmwdm00<9E(CxQMii5Q3U>|3h_UWTCo)x z@7}1axLYLPfxISyaKpC)(jegYobab=a*594}CU&^7s@tu4tsazKofzJ@xz& z@U?@palUMc%4N>QxZsN_{Wr*$O4v3mTh>lXOAE=H;q2M-oWC&4OUH*<-QC3Y^}V{t zu8MMYgZ{p(Bz_$C2VFwxP_+VNF82>X&X&A92p!96xqI6RMrZOAN@{IHKAS_-LF4qC zeqLq{;{Q(cUoNO}a2E}ab~ z?J0*tV-@$_rSJd4z_}YxjULMqU`>OXJ|Q@GZk)EZH2Fe~3E!MuVjum3}`cSLez46?QYS6$Mx z?gd9jVCBko*7kLgE!vc97u%6cPmW=`P&85)8He%e-oUUXc5r+K)%Lc6_&d650}Evh zvbN;Y&rQcE*+(=(R}5ngee*b~2q857^oc?8mJ`yOJ9-*<&)TZ-?2lFZ0w+L3={n?# zMVeX~S>D~r=-6aA8i;DM7kFoc}c@hmx?7DqBH{G~_tTChnRtOoe6)rfyn zcS0{nRC7`&I(&9_tn)B$A<29IaDbmDQr!dasi#KB+F=6TzM`IoW5WdeE$~b8#Q};X zsBdflVB4mCp4z>yoa_N+r^d--VDHf()^FOt!w=q1V_lMjX)-!KLt}kYe1woHxq#3! zIA!j4!`8nH#0&FQ_ozuyow`epJI|e>`(G%k90j z)EH$qt*_Z6VcVR_R3wjnRwF#rb^C$U_5Mdrk7j94H`35hN4lk%;o&h(o;^pVWMWF2 zC!T(VwzdR6bKmW>q|*$XALNxor&zy!Ewwc!Cr+JbO;0*L&?6&JKN(pGM6hlk-ujmk z4vj%~cPHt_T6#O0*mG!zWz8vS>uOlh-Nu>o6AWIQW^7`Xndt%zZS`8ZIf%SVp$HSz z=lXfng5U~Q{Gl$`wLhZNl|v2`kMp^ z*NuW8K}^f_2UewgJF~6cF>dp`nhx72T9_MGhW3&%lKdOcNfw`)15!g3Qb` zKiPALx7@RnR9zkQ_4OP&a+dz?Cch1Ac}bW0KaqbMy#kMwNes z*!=DexND`M`XT5JDOtOEQp~ z`=$@{*J+zrP+OCrt=8tHQ)9ex>;fmwUSMc6%hr{(WC}Kpge~h=f?=@lz)5<#+E~84 zQ(y05<#P;=Ptu*P55)xXmcxrD2HAVEKr-Rb-k1vY_wn1kNSkA$7RiQM4h$4&u61;D zOYW^K<6|TBF3@ zwG8g*Lp7B`E$)~ox_s{C*z5P}{J?_O@-4Pq{1j^O$5TYww}1MlMLx1s(9>8>Lq6Ci z$Xhb5(6M~Of=#+Mp;eVhdrLEKZV@0|vaU%(LUG#iWiF5J8{zDQi}d#{XLM|mo3^eC zW$8MXr8#_jJP_V>IeT%6m(OQex3V3~EUAPUSp+J4!>>?Oe|K$P%h<#;&mTBXPlL^l zj=A&xt&K1_HOo}q!j;g|ta<`~+KiwP_=%$f+1som&c(k;nH1S4v=i>kenSNc7^N8Mw{MdQyDfG3IIX` zB7J4<>u#=PJfG!-mj)3M`g+@_NhbU$-goFIo%J=eHKsUzVUmFBtbRwGQ<)iIC_&F_L4(hTFHTA!!&qf9L}JJ*})>(av5i);sH9Bm)B@6QpXA z3{U1bc;W(+GmtC5=5E!#AX9|A1;rw|FQ5vG7FbwqcOUR`TSTbU&5?0<^i1r>e+>K# zbJ~_y9sr(A@PAPY5!MIaI6SS&gjO^OTm&wXt=(|yVr8teBL(Z)lD-{))^+}CDMKLW zYiponXBnG?lc$F{e&zy0W5XOie2!GYWoLgo1EU#^4CQ&~o}DC2gWWHkWc${&)Fe!n zwKed}p;3C<>qrVk{E3{+{)-L|+_pjG8>A$kpJj5UKu>!UE0#6#!qI6~q}6h+*&-Z0 zmt`uG2NP;i%5!W?f#s^60C@}Sf+DFI7Io3%+K;I6eDASDMbWV!!Mo2X5ijEqfEpMu6z5-BC=<|c04)WeG>C;irM zr$xLE4w=wo>23es!dVf03}-qr=c0xxTc+zE4x{>ayh$ptmBrQ{hXZ+ zi+>xxRmFl;z&p2!&|;B&!>VjNW_(r8eeQAyc)0-ZfTw$y-yBeWb1-4@q0N!( zP=P=S*K4e7BfajMAs|Rz9++k4)_%6ES5KW7I1uc7WC5U#`*@3lF6xA zmZh5l!5oL>D>|6Tm&3iIGqAj)B~-alt5R;c-UJp5n}zixjf(@|glp9Uh~; z2F{#4A1TnZ%f%wDq`nR&^0p>6OwZ;50Uyasu7qhQLcDn79F7ZzhGDu0Th_O;b#)hY z3CXkjPIBnPc_qGky2%uk8gT4#D;PV5Qc11J#^A_Fg*?9j$X{RTsJR}=*9M0?@Kpi8|*nf%ZAlmq+6PJcJCoZ zM#fpUuAe)0tmTGvJxpgNF>TdF>%{pi1u1E&feRxOtnTgf?Mkay`fV|;Hnp!e6eDFDxLh$``ul_RX~D+yOvax{+f8V?402 znRKee!GR1DvstER;KKL_2?PTdMtR}z5Zk&SU8?|)t~F^%x$HYR!`@@#95^{iz5uzL zOOpwAtx{>P1X&Tk+Iu&lk6!SP94%ffPO@);=UHUFLgXhpq;*tfWvX z0d+~imkw9e`5(E`#JbY^26pH;_AkUI)XEh!maD|xi&I|6*1)&-g-|NNG|E1^?P4YX z+o3U)Wcv-P$}5QUXh6z|nt)qW7$Jt3}XaviG-LDK|2-wsG{plJ%&p%zVIW!D2 z1?cUmW&7$bQne|Hg(A-#ILnC%SlbHqHBc&KD7vi`@rT~!%PEvxl7)+YHreC;VvPRx zmf(3SYf83SNMcuCO61Mye`Mi{MPE|@YyrN-9IeeQDKNDhwj8y47gHX?D^V{o%1)nW z7nlYknG($nkhL6cSQFo3Y;b6d<~rp~oSVooQG$0paD&=JO4|cZ?mo!baX3F!;?|XL zW>WR?t*e2A2{;92awR%yQ`Dqtc+=K?o_OIbYg?hc0bU*~(w3g3C9PK7PEXBpdSsmG zX&b8uqONQiJ2BRObi!*jGk~fzE>?FHQmB%yyL4jh;8FrW8u&hQ?A`lFA0#)G3i|*6 z4&O;cK~zOq0=QD4z7IG$CE1u3y2p;{!if+eAdKf>avFZ{^bxxG)79`ny#5n3S9y9OIP}s!*~7^f$ueN1>}3mNkM%!QD5kV`O5MmyZq6 z(lW`;4ZYN)RN+P5hWdnHQ>Wy>kzs6xb?_QeX4$^9w09W{{#2XvWnLCTDiG*Kro4T5 zRRa*sVBgmaH?>r3fX|}}Nh=~+@7)9s_CiZtxQXIS0kS0}?ME}Pp4mt!U-NV-qZERQm?zN^tM3>oE<3riNNpuV`anXoT5JmiBZrGnonUMQBbzjj3Y& z-OYk@tuowfM}fImR2*E?#J35T_$IXYPiQZoQ`-Y(0+NQZ1Cpk?_Z^+Et_j8_V+#eF zfE6rm_3*1E0Zdf$B7d|1zj;$xtVtPO9AUdEw(j+w9?g5*xXX>5g;$^{1X2oGY7(@x zhYL_vHrKE+v~*eZZ!cLkG66}`WZRHhIbxDK9W@y%XNS7$Lck^7wTeqy^ z`@2uj)t;rLrJaf4@j&21HG?kNDke~{)b69URW|&bIk-RQ1-Lr?Whcv(?Y==(55Krw zJ=d4_%V;S5{lJHpLLg`pkw#M=xkpnlg)AB+&FLAu`W>bp;EgwH3+nFLA< zj7*a>4Vvp~n8}qmGdM&c3-ui^nS<5KV0vPf@1L8+tWm`D*)s_pt+f~;Ec$aLrSJ1L z*!gm_&ua)B=lVq_dqjHyK||1EkebM6&&Q{GE?h40u2lcjBSe-dzq=pSwZ*qlpU6oD zr{TxrVY}hOy1`kw^CmtJVBx!dWF5&52HCaG7Nof(AlU`z0-MRz~)Pk%_|9DX^ytEh%d$%e>K{di&AX^P+JF0DM;5t zOC6-^l*??0s$t=yTKz5wH}IpV9YOu^jkCa6)ew4Tpnz;DuZR=PaZCr)g(m?$AOu(8 zn!>4StJn957F^@|Sm<}GFT z1(v18>i(ed7pHTNp<=~18qgdt1Ya74r$^_UbC!w(0M#<){g-othk9UpS6SQ#_W-K) zux6FrKb@HJZy7M=ys`TntZ#?ry70Cy<+c67*ZY~GiWcUIN>UUuWp}Tr9uWSlx5-m*qbDh$ zp~j0f%}@NG-Y*r)obSc@<8*(N`>o5L=P&BHUUJDGiE2D_RA&Phz2RgL*pgPQ&86Pr z&8YH4!6U9$lPfDf7u^I(0__cgq$zxBdoJQ2q%1^6~Dp2Pf|;;!Aqat zLDbUlKj#XzuDp^Gz#CuFK|pV}$~gP=v`%f_6Mg^dM>H3@iYsj`5u#EdxYFB|l}EMD zIyxIE5h1RhkN9~@1$|0d?*~P{7xJ~MSpFXOS24K$QqM))Za}ko4&SBc^lMj|Sg+Og z@o7|@K#MklgE8!n6Iq1%?tbWP_Ur)R2SdUx;KG|0N8M3>1$h<1+pZ$K=kDb#W%1`N z?ze!8mBOqmkljmb-+meu0iJrb3G-S3V6IV*+EX}`Nc!!qVFNG- zh9Dq_i4R2M4iy`NM+hGze#w?(^hQrbONsrWRZ09{o69`PN* zAjrmu{>A{|P5qa6cK#EptlNLB#!`>~9uN;kgGp~+4*hNA%@`{Tm<2w)8`Hxln3yWs zO5A%~U$S+A$Av;Z?CTc;;=d4M^~Z_+D%?M-WBIw)bK;F90ssg2chUKLY!D`L(oc>A zk7~wWHG?pEt?f$R$jcNJ*Jt&Dj*RE$2Z(P6h+kR&@q>n670drGUT@+r6&2u(0X?s8 z37$MK3G15zdr1I>P-1&AheveVQJkJDDK5_zRZ^#DYeP?Gcn4g=Z@^Q=^^N9R7;!~aLmn7$3EdT(u*yLNp^cMmZAa9&2 z1`zW%*t8)^czYhMk0buYxu5^Xz+0Ch@t2w#SZ(A`UC8TEd*X#aJB+VAQSW4W08l8 zYUy5oolLKdm+%#Bt1XyFuL9Afeul;FqIP{;K~LfHmkNZ$W;)G>9fPEO(t0Dz8)6Ak4Yfm2sr zm)|l~(af^`S|YZ(A@ zDqKmgnfpcJJHTC)LtFy@zDeup`%z_MvHRLT+z)s5c-yUp*JH?-9mW6fg{pgdN#GS! zUf?-Y$=9jt0C??2FKVZZcLH~7Uwz*3CwJDyzu{CY9R}e0r>wd9^F+rW@C2%J=l58m z^?z400D4hd&%Q_d<%@dm5AA4RWsBB!-mc(5ArQBntmE?a!zK1lFZx+$QN4Ws@oE6W zQU|~m;M1t8+C@EUUjiQ9(m-EpGC*h%VDw0IcMf1Yd8)wPD!ZO701SVDYU|=$2f*US zJAuDvfxU+M60m($EqC-ag_{P2_7IM&#q>Zh7ce5=n8@4gIbY(`aAm8zImQow_p$^t zi((0L{ws4mUyp!$H#f0uWjegmx9h~}^y>f1>cChCI9!{jPUm>x!n_jV8(M{32fzZx zW7?0e;PMA<>|}jsJrygfgL+)X>-G>hDu{k%!10r_Gdy=T77P-VmpFJG0CSIu3Z9J} z@T+&MT_DjSiaZV&k@K&H)EJm5@x_-bRta9Q29H;2EX8_&ifH@?c5TGl!MpmTR|7+^ z%T(}uc|Rgy7*{}Q7$I=45=aF0L&j0`b07*qoM6N<$f}Bb7dH?_b literal 0 HcmV?d00001 diff --git a/data/gui/icons/difficulty_casual.png b/data/gui/icons/difficulty_casual.png new file mode 100644 index 0000000000000000000000000000000000000000..6b499fdb07a89b652d11baac3b652988262bacb7 GIT binary patch literal 5846 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 literal 0 HcmV?d00001 diff --git a/data/gui/screens/race_setup.stkgui b/data/gui/screens/race_setup.stkgui index b5aebdfe257..e729f28ca1e 100644 --- a/data/gui/screens/race_setup.stkgui +++ b/data/gui/screens/race_setup.stkgui @@ -11,6 +11,8 @@ + - + + + + + + diff --git a/data/stk_config.xml b/data/stk_config.xml index b51d6e56277..29ae57c0aa7 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -365,6 +365,27 @@ rb-skid-probability="0:0.0" skidding-threshold="4.0" /> + + getNode("easy"); - difficulties[1] = root->getNode("medium"); - difficulties[2] = root->getNode("hard"); - difficulties[3] = root->getNode("best"); + difficulties[1] = root->getNode("casual"); + difficulties[2] = root->getNode("medium"); + difficulties[3] = root->getNode("hard"); + difficulties[4] = root->getNode("best"); if (difficulties[0] == NULL || difficulties[1] == NULL || - difficulties[2] == NULL || difficulties[3] == NULL) + difficulties[2] == NULL || difficulties[3] == NULL || + difficulties[4] == NULL) { - error(" or or or "); + error(" or or or or "); } for (int d=0; d<=RaceManager::DIFFICULTY_BEST; d++) @@ -574,12 +576,14 @@ ChallengeData::GPLevel ChallengeData::isGPFulfilled() const // getting a cup of the inferior level rather than // nothing at all int unlock_level = d - rank; - if (unlock_level == 3) + if (unlock_level == 4) return GP_BEST; - if (unlock_level == 2) + if (unlock_level == 3) return GP_HARD; - if (unlock_level == 1) + if (unlock_level == 2) return GP_MEDIUM; + if (unlock_level == 1) + return GP_CASUAL; if (unlock_level == 0) return GP_EASY; return GP_NONE; diff --git a/src/challenges/challenge_data.hpp b/src/challenges/challenge_data.hpp index 88afc80cd88..68ac9c3d64c 100644 --- a/src/challenges/challenge_data.hpp +++ b/src/challenges/challenge_data.hpp @@ -51,6 +51,7 @@ class ChallengeData { GP_NONE, GP_EASY, + GP_CASUAL, GP_MEDIUM, GP_HARD, GP_BEST diff --git a/src/challenges/challenge_status.cpp b/src/challenges/challenge_status.cpp index 0824a1ddbfc..19d8ef0a03c 100644 --- a/src/challenges/challenge_status.cpp +++ b/src/challenges/challenge_status.cpp @@ -53,12 +53,14 @@ void ChallengeStatus::load(const XMLNode* challenges_node) // Solving at a difficulty also marks lower difficulties as solved if (solved == "easy") m_solved = 0x01; - else if (solved == "medium") + else if (solved == "casual") m_solved = 0x03; - else if (solved == "hard") + else if (solved == "medium") m_solved = 0x07; - else if (solved == "best") + else if (solved == "hard") m_solved = 0x0F; + else if (solved == "best") + m_solved = 0x1F; } // if has 'solved' attribute if (!node->get("best_while_slower", &m_max_req_in_lower_diff)) m_max_req_in_lower_diff = false; @@ -70,9 +72,9 @@ void ChallengeStatus::load(const XMLNode* challenges_node) // Also resets active flag. void ChallengeStatus::setSolved(RaceManager::Difficulty d) { - if ((int) d <= 3) + if ((int) d <= 4) { - m_solved |= (0x0F >> (3 - (int) d)); // Sets the last d+1 bits to 1 + m_solved |= (0x1F >> (4 - (int) d)); // Sets the last d+1 bits to 1 m_active &= ~m_solved; // Sets to 0 all bits which are at 1 in m_solved } } // setSolved @@ -100,6 +102,8 @@ void ChallengeStatus::save(UTFWriter& writer) writer << " solved=\"hard\""; else if (isSolved(RaceManager::DIFFICULTY_MEDIUM)) writer << " solved=\"medium\""; + else if (isSolved(RaceManager::DIFFICULTY_CASUAL)) + writer << " solved=\"casual\""; else if (isSolved(RaceManager::DIFFICULTY_EASY)) writer << " solved=\"easy\""; else diff --git a/src/challenges/challenge_status.hpp b/src/challenges/challenge_status.hpp index 2b64561bdbf..e030d2aca7f 100644 --- a/src/challenges/challenge_status.hpp +++ b/src/challenges/challenge_status.hpp @@ -91,9 +91,10 @@ class ChallengeStatus : public NoCopy */ RaceManager::Difficulty highestSolved() const { - return (m_solved & 0x08) ? RaceManager::DIFFICULTY_BEST : - (m_solved & 0x04) ? RaceManager::DIFFICULTY_HARD : - (m_solved & 0x02) ? RaceManager::DIFFICULTY_MEDIUM : + return (m_solved & 0x10) ? RaceManager::DIFFICULTY_BEST : + (m_solved & 0x08) ? RaceManager::DIFFICULTY_HARD : + (m_solved & 0x04) ? RaceManager::DIFFICULTY_MEDIUM : + (m_solved & 0x02) ? RaceManager::DIFFICULTY_CASUAL : (m_solved & 0x01) ? RaceManager::DIFFICULTY_EASY : RaceManager::DIFFICULTY_NONE; } // highestSolved diff --git a/src/challenges/story_mode_status.cpp b/src/challenges/story_mode_status.cpp index 11ecac8c773..19bda1885ab 100644 --- a/src/challenges/story_mode_status.cpp +++ b/src/challenges/story_mode_status.cpp @@ -36,6 +36,7 @@ StoryModeStatus::StoryModeStatus(const XMLNode *node) m_next_unlock_points = 0; m_first_time = true; m_easy_challenges = 0; + m_casual_challenges = 0; m_medium_challenges = 0; m_hard_challenges = 0; m_best_challenges = 0; @@ -107,6 +108,7 @@ void StoryModeStatus::computeActive(bool first_call) m_points = 0; m_next_unlock_points = 0; m_easy_challenges = 0; + m_casual_challenges = 0; m_medium_challenges = 0; m_hard_challenges = 0; m_best_challenges = 0; @@ -142,6 +144,11 @@ void StoryModeStatus::computeActive(bool first_call) m_points += CHALLENGE_POINTS[RaceManager::DIFFICULTY_MEDIUM]*gp_factor; m_medium_challenges++; } + else if (i->second->isSolved(RaceManager::DIFFICULTY_CASUAL)) + { + m_points += CHALLENGE_POINTS[RaceManager::DIFFICULTY_CASUAL]*gp_factor; + m_casual_challenges++; + } else if (i->second->isSolved(RaceManager::DIFFICULTY_EASY)) { m_points += CHALLENGE_POINTS[RaceManager::DIFFICULTY_EASY]*gp_factor; @@ -155,6 +162,8 @@ void StoryModeStatus::computeActive(bool first_call) case RaceManager::DIFFICULTY_NONE: i->second->setActive(RaceManager::DIFFICULTY_EASY); case RaceManager::DIFFICULTY_EASY: + i->second->setActive(RaceManager::DIFFICULTY_CASUAL); + case RaceManager::DIFFICULTY_CASUAL: i->second->setActive(RaceManager::DIFFICULTY_MEDIUM); case RaceManager::DIFFICULTY_MEDIUM: i->second->setActive(RaceManager::DIFFICULTY_HARD); @@ -319,6 +328,9 @@ void StoryModeStatus::grandPrixFinished() case ChallengeData::GP_EASY: difficulty = RaceManager::DIFFICULTY_EASY; break; + case ChallengeData::GP_CASUAL: + difficulty = RaceManager::DIFFICULTY_CASUAL; + break; case ChallengeData::GP_MEDIUM: difficulty = RaceManager::DIFFICULTY_MEDIUM; break; diff --git a/src/challenges/story_mode_status.hpp b/src/challenges/story_mode_status.hpp index 74a2a7075f0..fde528351f2 100644 --- a/src/challenges/story_mode_status.hpp +++ b/src/challenges/story_mode_status.hpp @@ -34,7 +34,7 @@ class ChallengeStatus; class UTFWriter; class XMLNode; -const int CHALLENGE_POINTS[] = { 6, 7, 8, 10 }; +const int CHALLENGE_POINTS[] = { 5, 6, 7, 8, 10 }; const int GP_FACTOR = 3; /** This class contains the progression through challenges for the story mode. @@ -69,6 +69,7 @@ class StoryModeStatus bool m_first_time; int m_easy_challenges; + int m_casual_challenges; int m_medium_challenges; int m_hard_challenges; int m_best_challenges; @@ -111,8 +112,8 @@ class StoryModeStatus void clearUnlocked () {m_unlocked_features.clear(); } // ---------------------------------------------------------------------------------------- /** Returns the number of completed challenges. */ - int getNumCompletedChallenges () const { return (m_easy_challenges + m_medium_challenges + - m_hard_challenges + m_best_challenges); } + int getNumCompletedChallenges () const { return (m_easy_challenges + m_casual_challenges + + m_medium_challenges + m_hard_challenges + m_best_challenges); } // ---------------------------------------------------------------------------------------- /** Returns the number of challenges with the superTux time beaten in a lower difficulty. */ int getNumReqMetInLowerDiff () const; @@ -129,6 +130,9 @@ class StoryModeStatus /** Returns the number of fulfilled challenges at easy level. */ int getNumEasyTrophies () const { return m_easy_challenges; } // ---------------------------------------------------------------------------------------- + /** Returns the number of fulfilled challenges at casual level. */ + int getNumCasualTrophies() const { return m_casual_challenges; } + // ---------------------------------------------------------------------------------------- /* Returns the number of fulfilled challenges at medium level. */ int getNumMediumTrophies() const { return m_medium_challenges; } // ---------------------------------------------------------------------------------------- diff --git a/src/config/player_profile.hpp b/src/config/player_profile.hpp index ed73334653a..d52fcf50c6e 100644 --- a/src/config/player_profile.hpp +++ b/src/config/player_profile.hpp @@ -263,10 +263,15 @@ class PlayerProfile : public NoCopy return m_story_mode_status->getNumEasyTrophies(); } // getNumEasyTrophies // ---------------------------------------------------------------------------------------- + unsigned int getNumCasualTrophies() const + { + return m_story_mode_status->getNumCasualTrophies(); + } // getNumCasualTrophies + // ---------------------------------------------------------------------------------------- unsigned int getNumMediumTrophies() const { return m_story_mode_status->getNumMediumTrophies(); - } // getNumEasyTrophies + } // getNumMediumTrophies // ----------------------------------------------------------------------- unsigned int getNumHardTrophies() const { diff --git a/src/karts/controller/arena_ai.cpp b/src/karts/controller/arena_ai.cpp index b99c54514d3..cc47a91c870 100644 --- a/src/karts/controller/arena_ai.cpp +++ b/src/karts/controller/arena_ai.cpp @@ -433,7 +433,8 @@ void ArenaAI::useItems(const float dt) m_time_since_last_shot += dt; float min_bubble_time = 2.0f; - const bool difficulty = m_cur_difficulty == RaceManager::DIFFICULTY_EASY || + const bool difficulty = m_cur_difficulty == RaceManager::DIFFICULTY_EASY || + m_cur_difficulty == RaceManager::DIFFICULTY_CASUAL || m_cur_difficulty == RaceManager::DIFFICULTY_MEDIUM; const bool fire_behind = closest_kart_point_lc.z() < 0 && !difficulty; diff --git a/src/karts/controller/battle_ai.cpp b/src/karts/controller/battle_ai.cpp index 384ede92639..9dd37adfc21 100644 --- a/src/karts/controller/battle_ai.cpp +++ b/src/karts/controller/battle_ai.cpp @@ -144,6 +144,7 @@ void BattleAI::findTarget() switch (m_cur_difficulty) { case RaceManager::DIFFICULTY_EASY: + case RaceManager::DIFFICULTY_CASUAL: case RaceManager::DIFFICULTY_MEDIUM: { find_sta = m_tsb_world->getKartLife(m_kart->getWorldKartId()) == 1; diff --git a/src/karts/kart_properties.cpp b/src/karts/kart_properties.cpp index f5273c1ec08..77d51584855 100644 --- a/src/karts/kart_properties.cpp +++ b/src/karts/kart_properties.cpp @@ -475,6 +475,8 @@ void KartProperties::getAllData(const XMLNode * root) { const XMLNode *easy = ai_node->getNode("easy"); m_ai_properties[RaceManager::DIFFICULTY_EASY]->load(easy); + const XMLNode *casual = ai_node->getNode("casual"); + m_ai_properties[RaceManager::DIFFICULTY_CASUAL]->load(casual); const XMLNode *medium = ai_node->getNode("medium"); m_ai_properties[RaceManager::DIFFICULTY_MEDIUM]->load(medium); const XMLNode *hard = ai_node->getNode("hard"); diff --git a/src/modes/demo_world.cpp b/src/modes/demo_world.cpp index d032eb9a205..492ec93c8b2 100644 --- a/src/modes/demo_world.cpp +++ b/src/modes/demo_world.cpp @@ -67,9 +67,10 @@ DemoWorld::DemoWorld() switch (atoi(UserConfigParams::m_difficulty.toString().c_str())) { case 0: RaceManager::get()->setDifficulty(RaceManager::DIFFICULTY_EASY); break; - case 1: RaceManager::get()->setDifficulty(RaceManager::DIFFICULTY_MEDIUM); break; - case 2: RaceManager::get()->setDifficulty(RaceManager::DIFFICULTY_HARD); break; - case 3: RaceManager::get()->setDifficulty(RaceManager::DIFFICULTY_BEST); break; + case 1: RaceManager::get()->setDifficulty(RaceManager::DIFFICULTY_CASUAL); break; + case 2: RaceManager::get()->setDifficulty(RaceManager::DIFFICULTY_MEDIUM); break; + case 3: RaceManager::get()->setDifficulty(RaceManager::DIFFICULTY_HARD); break; + case 4: RaceManager::get()->setDifficulty(RaceManager::DIFFICULTY_BEST); break; default: RaceManager::get()->setDifficulty(RaceManager::DIFFICULTY_HARD); break; } diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 5cb55833ce2..c358eeb28ad 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -382,10 +382,11 @@ void World::reset(bool restart) { PlayerManager::trackEvent(RaceManager::get()->getTrackName(), AchievementsStatus::TR_STARTED); AchievementsStatus::AchievementData diff; - diff = (RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_EASY) ? AchievementsStatus::EASY_STARTED : + diff = (RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_EASY) ? AchievementsStatus::EASY_STARTED : + (RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_CASUAL) ? AchievementsStatus::CASUAL_STARTED : (RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_MEDIUM) ? AchievementsStatus::MEDIUM_STARTED : - (RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_HARD) ? AchievementsStatus::HARD_STARTED : - AchievementsStatus::BEST_STARTED; + (RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_HARD) ? AchievementsStatus::HARD_STARTED : + AchievementsStatus::BEST_STARTED; PlayerManager::increaseAchievement(diff,1); } else if (RaceManager::get()->isEggHuntMode()) @@ -1714,10 +1715,11 @@ void World::updateAchievementDataEndRace() if (RaceManager::get()->isLinearRaceMode()) { ACS::AchievementData diff; - diff = (RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_EASY) ? ACS::EASY_FINISHED : + diff = (RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_EASY) ? ACS::EASY_FINISHED : + (RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_CASUAL) ? ACS::CASUAL_FINISHED : (RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_MEDIUM) ? ACS::MEDIUM_FINISHED : - (RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_HARD) ? ACS::HARD_FINISHED : - ACS::BEST_FINISHED; + (RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_HARD) ? ACS::HARD_FINISHED : + ACS::BEST_FINISHED; PlayerManager::increaseAchievement(diff,1); PlayerManager::trackEvent(RaceManager::get()->getTrackName(), ACS::TR_FINISHED); diff --git a/src/race/race_manager.cpp b/src/race/race_manager.cpp index 25bd7c77d85..6acb933938d 100644 --- a/src/race/race_manager.cpp +++ b/src/race/race_manager.cpp @@ -295,6 +295,8 @@ RaceManager::Difficulty { if (difficulty == "novice") return DIFFICULTY_EASY; + else if (difficulty == "casual") + return DIFFICULTY_CASUAL; else if (difficulty == "intermediate") return DIFFICULTY_MEDIUM; else if (difficulty == "expert") @@ -1307,6 +1309,7 @@ core::stringw RaceManager::getDifficultyName(Difficulty diff) const switch (diff) { case RaceManager::DIFFICULTY_EASY: return _("Novice"); break; + case RaceManager::DIFFICULTY_CASUAL: return _("Casual"); break; case RaceManager::DIFFICULTY_MEDIUM: return _("Intermediate"); break; case RaceManager::DIFFICULTY_HARD: return _("Expert"); break; case RaceManager::DIFFICULTY_BEST: return _("SuperTux"); break; diff --git a/src/race/race_manager.hpp b/src/race/race_manager.hpp index 6f02c873f24..ed994d22d5c 100644 --- a/src/race/race_manager.hpp +++ b/src/race/race_manager.hpp @@ -229,6 +229,7 @@ class RaceManager /** Game difficulty. */ enum Difficulty { DIFFICULTY_EASY = 0, DIFFICULTY_FIRST = DIFFICULTY_EASY, + DIFFICULTY_CASUAL, DIFFICULTY_MEDIUM, DIFFICULTY_HARD, DIFFICULTY_BEST, @@ -602,6 +603,7 @@ class RaceManager switch(diff) { case RaceManager::DIFFICULTY_EASY: return "easy"; break; + case RaceManager::DIFFICULTY_CASUAL: return "casual"; break; case RaceManager::DIFFICULTY_MEDIUM: return "medium"; break; case RaceManager::DIFFICULTY_HARD: return "hard"; break; case RaceManager::DIFFICULTY_BEST: return "best"; break; diff --git a/src/replay/replay_play.cpp b/src/replay/replay_play.cpp index 35ba1dccc21..ce61b9c2a07 100644 --- a/src/replay/replay_play.cpp +++ b/src/replay/replay_play.cpp @@ -217,6 +217,13 @@ bool ReplayPlay::addReplayFile(const std::string& fn, bool custom_replay, int ca return false; } + // Display the correct difficulty for STK 1.X (and older) replays + if (version <= 4) + { + if (rd.m_difficulty >= 1) + rd.m_difficulty++; + } + if (version >= 4) { fgets(s, 1023, fd); diff --git a/src/states_screens/dialogs/achievement_progress_dialog.cpp b/src/states_screens/dialogs/achievement_progress_dialog.cpp index 086c0afd04b..99254f8a2fc 100644 --- a/src/states_screens/dialogs/achievement_progress_dialog.cpp +++ b/src/states_screens/dialogs/achievement_progress_dialog.cpp @@ -183,6 +183,10 @@ core::stringw AchievementProgressDialog::niceGoalName(std::string internal_name) // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. if(internal_name=="easy-finished") nice_name = _("Novice races finished"); // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. + if(internal_name=="casual-started") nice_name = _("Casual races started"); + // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. + if(internal_name=="casual-finished") nice_name = _("Casual races finished"); + // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. if(internal_name=="medium-started") nice_name = _("Intermediate races started"); // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. if(internal_name=="medium-finished") nice_name = _("Intermediate races finished"); diff --git a/src/states_screens/dialogs/select_challenge.cpp b/src/states_screens/dialogs/select_challenge.cpp index 9c342a4e6b5..f46563c05d1 100644 --- a/src/states_screens/dialogs/select_challenge.cpp +++ b/src/states_screens/dialogs/select_challenge.cpp @@ -148,12 +148,15 @@ SelectChallengeDialog::SelectChallengeDialog(const float percentWidth, challenge_info->setText(getLabel(RaceManager::DIFFICULTY_EASY, c->getData()), false ); break; case 1: - challenge_info->setText(getLabel(RaceManager::DIFFICULTY_MEDIUM, c->getData()), false ); + challenge_info->setText(getLabel(RaceManager::DIFFICULTY_CASUAL, c->getData()), false ); break; case 2: - challenge_info->setText(getLabel(RaceManager::DIFFICULTY_HARD, c->getData()), false ); + challenge_info->setText(getLabel(RaceManager::DIFFICULTY_MEDIUM, c->getData()), false ); break; case 3: + challenge_info->setText(getLabel(RaceManager::DIFFICULTY_HARD, c->getData()), false ); + break; + case 4: if (UserConfigParams::m_difficulty == RaceManager::DIFFICULTY_BEST && PlayerManager::getCurrentPlayer()->isLocked("difficulty_best")) { @@ -166,7 +169,8 @@ SelectChallengeDialog::SelectChallengeDialog(const float percentWidth, break; } - updateSolvedIcon(c, RaceManager::DIFFICULTY_EASY, "novice", "cup_bronze.png"); + updateSolvedIcon(c, RaceManager::DIFFICULTY_EASY, "novice", "cup_chocolate.png"); + updateSolvedIcon(c, RaceManager::DIFFICULTY_CASUAL, "casual", "cup_bronze.png"); updateSolvedIcon(c, RaceManager::DIFFICULTY_MEDIUM, "intermediate", "cup_silver.png"); updateSolvedIcon(c, RaceManager::DIFFICULTY_HARD, "expert", "cup_gold.png"); updateSolvedIcon(c, RaceManager::DIFFICULTY_BEST, "supertux", "cup_platinum.png"); @@ -308,12 +312,15 @@ GUIEngine::EventPropagation SelectChallengeDialog::processEvent(const std::strin c_data->setRace(RaceManager::DIFFICULTY_EASY); break; case 1: - c_data->setRace(RaceManager::DIFFICULTY_MEDIUM); + c_data->setRace(RaceManager::DIFFICULTY_CASUAL); break; case 2: - c_data->setRace(RaceManager::DIFFICULTY_HARD); + c_data->setRace(RaceManager::DIFFICULTY_MEDIUM); break; case 3: + c_data->setRace(RaceManager::DIFFICULTY_HARD); + break; + case 4: if (UserConfigParams::m_difficulty == RaceManager::DIFFICULTY_BEST && PlayerManager::getCurrentPlayer()->isLocked("difficulty_best")) { @@ -354,20 +361,25 @@ GUIEngine::EventPropagation SelectChallengeDialog::processEvent(const std::strin description = getLabel(RaceManager::DIFFICULTY_EASY, c_stat->getData()); UserConfigParams::m_difficulty = 0; } + else if (selected == "casual") + { + description = getLabel(RaceManager::DIFFICULTY_CASUAL, c_stat->getData()); + UserConfigParams::m_difficulty = 1; + } else if (selected == "intermediate") { description = getLabel(RaceManager::DIFFICULTY_MEDIUM, c_stat->getData()); - UserConfigParams::m_difficulty = 1; + UserConfigParams::m_difficulty = 2; } else if (selected == "expert") { description = getLabel(RaceManager::DIFFICULTY_HARD, c_stat->getData()); - UserConfigParams::m_difficulty = 2; + UserConfigParams::m_difficulty = 3; } else if (selected == "supertux") { description = getLabel(RaceManager::DIFFICULTY_BEST, c_stat->getData()); - UserConfigParams::m_difficulty = 3; + UserConfigParams::m_difficulty = 4; } else { diff --git a/src/states_screens/feature_unlocked.cpp b/src/states_screens/feature_unlocked.cpp index fafa38ab765..124886d246e 100644 --- a/src/states_screens/feature_unlocked.cpp +++ b/src/states_screens/feature_unlocked.cpp @@ -256,6 +256,12 @@ void FeatureUnlockedCutScene::addTrophy(RaceManager::Difficulty difficulty, CHALLENGE_POINTS[RaceManager::DIFFICULTY_EASY]*gp_factor, CHALLENGE_POINTS[max_unlocked_difficulty]*gp_factor); break; + case RaceManager::DIFFICULTY_CASUAL: + msg = _("You completed the casual challenge! " + "Points earned on this level: %i/%i", + CHALLENGE_POINTS[RaceManager::DIFFICULTY_CASUAL]*gp_factor, + CHALLENGE_POINTS[max_unlocked_difficulty]*gp_factor); + break; case RaceManager::DIFFICULTY_MEDIUM: msg = _("You completed the intermediate challenge! " "Points earned on this level: %i/%i", @@ -284,6 +290,9 @@ void FeatureUnlockedCutScene::addTrophy(RaceManager::Difficulty difficulty, case RaceManager::DIFFICULTY_EASY: model = file_manager->getAsset(FileManager::MODEL,"trophy_bronze.spm"); break; + case RaceManager::DIFFICULTY_CASUAL: + model = file_manager->getAsset(FileManager::MODEL,"trophy_bronze.spm"); + break; case RaceManager::DIFFICULTY_MEDIUM: model = file_manager->getAsset(FileManager::MODEL,"trophy_silver.spm"); break; diff --git a/src/states_screens/race_gui_overworld.cpp b/src/states_screens/race_gui_overworld.cpp index b57932b2131..0d4825a682b 100644 --- a/src/states_screens/race_gui_overworld.cpp +++ b/src/states_screens/race_gui_overworld.cpp @@ -65,9 +65,10 @@ using namespace irr; const int LOCKED = 0; const int OPEN = 1; const int COMPLETED_EASY = 2; -const int COMPLETED_MEDIUM = 3; -const int COMPLETED_HARD = 4; -const int COMPLETED_BEST = 5; +const int COMPLETED_CASUAL = 3; +const int COMPLETED_MEDIUM = 4; +const int COMPLETED_HARD = 5; +const int COMPLETED_BEST = 6; /** The constructor is called before anything is attached to the scene node. * So rendering to a texture can be done here. But world is not yet fully @@ -83,10 +84,11 @@ RaceGUIOverworld::RaceGUIOverworld() m_is_minimap_initialized = false; m_close_to_a_challenge = false; m_current_challenge = NULL; - m_trophy[0] = irr_driver->getTexture(FileManager::GUI_ICON, "cup_bronze.png"); - m_trophy[1] = irr_driver->getTexture(FileManager::GUI_ICON, "cup_silver.png"); - m_trophy[2] = irr_driver->getTexture(FileManager::GUI_ICON, "cup_gold.png" ); - m_trophy[3] = irr_driver->getTexture(FileManager::GUI_ICON, "cup_platinum.png" ); + m_trophy[0] = irr_driver->getTexture(FileManager::GUI_ICON, "cup_chocolate.png"); + m_trophy[1] = irr_driver->getTexture(FileManager::GUI_ICON, "cup_bronze.png"); + m_trophy[2] = irr_driver->getTexture(FileManager::GUI_ICON, "cup_silver.png"); + m_trophy[3] = irr_driver->getTexture(FileManager::GUI_ICON, "cup_gold.png" ); + m_trophy[4] = irr_driver->getTexture(FileManager::GUI_ICON, "cup_platinum.png" ); bool multitouch_enabled = (UserConfigParams::m_multitouch_active == 1 && irr_driver->getDevice()->supportsTouchDevice()) || @@ -118,7 +120,8 @@ RaceGUIOverworld::RaceGUIOverworld() m_icons[3] = m_trophy[1]; m_icons[4] = m_trophy[2]; m_icons[5] = m_trophy[3]; - m_icons[6] = m_locked_bonus; + m_icons[6] = m_trophy[4]; + m_icons[7] = m_locked_bonus; } // RaceGUIOverworld // ---------------------------------------------------------------------------- @@ -281,20 +284,21 @@ void RaceGUIOverworld::drawTrophyPoints() PlayerManager::getCurrentPlayer()->isLocked("difficulty_best") ? size*2.0f : size*1.0f; // Draw trophies icon and the number of trophy obtained by type - for (unsigned int i=0;i<4;i++) + for (unsigned int i=0;i<5;i++) { if (m_close_to_a_challenge) break; - if (i==3 && PlayerManager::getCurrentPlayer()->isLocked("difficulty_best")) + if (i==4 && PlayerManager::getCurrentPlayer()->isLocked("difficulty_best")) break; draw2DImage(m_trophy[i], dest, source, NULL, NULL, true /* alpha */); dest += core::position2di((int)(size*1.5f), 0); std::string trophies = (i==0) ? StringUtils::toString(player->getNumEasyTrophies()) : - (i==1) ? StringUtils::toString(player->getNumMediumTrophies()) : - (i==2) ? StringUtils::toString(player->getNumHardTrophies()) : + (i==1) ? StringUtils::toString(player->getNumCasualTrophies()) : + (i==2) ? StringUtils::toString(player->getNumMediumTrophies()) : + (i==3) ? StringUtils::toString(player->getNumHardTrophies()) : StringUtils::toString(player->getNumBestTrophies()); core::stringw trophiesW(trophies.c_str()); font->setBlackBorder(true); @@ -472,6 +476,7 @@ void RaceGUIOverworld::drawGlobalMiniMap() if (c->isSolved(RaceManager::DIFFICULTY_BEST)) state = COMPLETED_BEST; else if (c->isSolved(RaceManager::DIFFICULTY_HARD)) state = COMPLETED_HARD; else if (c->isSolved(RaceManager::DIFFICULTY_MEDIUM)) state = COMPLETED_MEDIUM; + else if (c->isSolved(RaceManager::DIFFICULTY_CASUAL)) state = COMPLETED_CASUAL; else if (c->isSolved(RaceManager::DIFFICULTY_EASY)) state = COMPLETED_EASY; const core::rect source(core::position2d(0,0), diff --git a/src/states_screens/race_gui_overworld.hpp b/src/states_screens/race_gui_overworld.hpp index 0f65960cfbf..62d569ccc6b 100644 --- a/src/states_screens/race_gui_overworld.hpp +++ b/src/states_screens/race_gui_overworld.hpp @@ -53,13 +53,13 @@ class RaceGUIOverworld : public RaceGUIBase // Minimap related variables // ------------------------- - video::ITexture* m_trophy[4]; + video::ITexture* m_trophy[5]; video::ITexture *m_lock; video::ITexture *m_open_challenge; video::ITexture *m_locked_bonus; - video::ITexture* m_icons[7]; + video::ITexture* m_icons[8]; /** Texture for speedometer. */ irr::video::ITexture *m_speed_meter_icon; From 8a758cb72c25d035b83da14671b887693aee31f9 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Mon, 13 Nov 2023 14:44:23 +0100 Subject: [PATCH 022/188] Improve low speed maneuvrability Also: - Limit more reverse speed to make it easier to turn at max reverse speed - Give light karts a slightly worse skidding to turn-radius ratio --- data/kart_characteristics.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 548c64ed438..efe42e03680 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -79,7 +79,7 @@ settings the expected time-to-full-steer is: 0.5 * 0.25 + 0.5 * 0.15 = 0.2 ... which is overall the same time we had previously. --> - @@ -98,7 +98,7 @@ gear. --> + brake-time-increase="6" max-speed-reverse-ratio="0.55" /> - + @@ -393,7 +393,7 @@ duration-factor="*1" fade-out-time="*1" /> - + @@ -406,7 +406,7 @@ - + @@ -418,7 +418,7 @@ - + From 882e9c012aacbc4bf0649fd5b9c83d67b59fc85c Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Mon, 13 Nov 2023 15:03:52 +0100 Subject: [PATCH 023/188] Increase time to full steer at low difficulties Newbies are notoriously prone to oversteering, and low speeds have small turn radiuses. Higher time to full steer should make it easier for newbies. --- data/kart_characteristics.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index efe42e03680..67fc0e1168b 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -70,15 +70,13 @@ higher than 25 can only be reached with powerups). time-full-steer: This is the amount of change in steering depending on current steering. So if the steering is between 0 and 0.5, - the time-for-steering-change is 0.15. If the current steering is - between 0.5 and 1.0, the time-for-steering-change is 0.25. + the time-for-steering-change is 0.17. If the current steering is + between 0.5 and 1.0, the time-for-steering-change is 0.28. The speed is used as dt/time-for-steering-change. In short: steering at less than halfway is somewhat faster, - which should avoid oversteering (by pressing the key for too long), - but slower when you want to steer more. Overwall with the current - settings the expected time-to-full-steer is: - 0.5 * 0.25 + 0.5 * 0.15 = 0.2 ... which is overall the same - time we had previously. --> + but slower when you want to steer more, which should avoid + oversteering (by pressing the key for too long), + --> @@ -350,6 +348,7 @@ max-speed-reverse-ratio="0.75"/> + @@ -357,7 +356,8 @@ max-speed-reverse-ratio="0.675"/> - + + Date: Mon, 13 Nov 2023 20:13:31 +0100 Subject: [PATCH 024/188] Buff purple skidding --- data/kart_characteristics.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 67fc0e1168b..5222e8cb1f8 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -166,9 +166,9 @@ but you will always keep on doing a left turn, just more or less. --> From 913dec01a812385b42580bd5e3761618b379faf4 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Mon, 13 Nov 2023 21:05:24 +0100 Subject: [PATCH 025/188] Set version to 2.0-git for more clarity --- CMakeLists.txt | 4 ++-- android/make.sh | 2 +- data/{supertuxkart.git => supertuxkart.2.0-git} | 0 src/utils/string_utils.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename data/{supertuxkart.git => supertuxkart.2.0-git} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 25f70747117..1f950d00fec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8.4) # root CMakeLists for the SuperTuxKart project project(SuperTuxKart) -set(PROJECT_VERSION "git") +set(PROJECT_VERSION "2.0-git") add_definitions( -DSUPERTUXKART_VERSION="${PROJECT_VERSION}" ) @@ -293,7 +293,7 @@ if (BUILD_RECORDER) find_library(OPENGLRECORDER_LIBRARY NAMES openglrecorder libopenglrecorder PATHS "${PROJECT_SOURCE_DIR}/${DEPENDENCIES}/lib") find_path(OPENGLRECORDER_INCLUDEDIR NAMES openglrecorder.h PATHS "${PROJECT_SOURCE_DIR}/${DEPENDENCIES}/include") if (NOT OPENGLRECORDER_LIBRARY OR NOT OPENGLRECORDER_INCLUDEDIR) - if(PROJECT_VERSION STREQUAL "git") + if(PROJECT_VERSION STREQUAL "2.0-git") message(WARNING "libopenglrecorder not found, disabling in-game recorder. " "To use recorder, install libopenglrecorder.") set(OPENGLRECORDER_LIBRARY CACHE INTERNAL "") diff --git a/android/make.sh b/android/make.sh index 3d5b6150cc3..2d41db7d039 100755 --- a/android/make.sh +++ b/android/make.sh @@ -217,7 +217,7 @@ fi if [ -z "$PROJECT_VERSION" ]; then if [ $IS_DEBUG_BUILD -ne 0 ]; then - export PROJECT_VERSION="git" + export PROJECT_VERSION="2.0-git" else echo "Error: Variable PROJECT_VERSION is not set. It must have unique" \ "value for release build." diff --git a/data/supertuxkart.git b/data/supertuxkart.2.0-git similarity index 100% rename from data/supertuxkart.git rename to data/supertuxkart.2.0-git diff --git a/src/utils/string_utils.cpp b/src/utils/string_utils.cpp index e963827006d..6207fd8335c 100644 --- a/src/utils/string_utils.cpp +++ b/src/utils/string_utils.cpp @@ -954,7 +954,7 @@ namespace StringUtils int versionToInt(const std::string &version_string) { // Special case: GIT - if(version_string=="GIT" || version_string=="git") + if(version_string=="GIT" || version_string=="git" || version_string=="2.0-git") { // GIT version will be version 99.99.99i-rcJ return 10000000*99 From 804d0acbe2e111adf4f474b6e420116315f9820e Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Tue, 14 Nov 2023 08:14:17 +0100 Subject: [PATCH 026/188] Make Cake speed relative to the user's kart speed - Remove the old formula based on multiplying by a kart speed factor and adding a base speed on top - Instead add an offset based on the user's kart speed ; this makes backward cakes less dangerous - Allow negative cake speeds (although this doesn't matter in most situations) --- data/powerup.xml | 7 +++++-- src/items/cake.cpp | 21 ++++++++------------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/data/powerup.xml b/data/powerup.xml index 36e978d294e..d78ae338fa0 100644 --- a/data/powerup.xml +++ b/data/powerup.xml @@ -7,10 +7,13 @@ force-updown="1" force-to-target="40" max-distance="25" /> + + force-updown="25" max-distance="50" /> diff --git a/src/items/cake.cpp b/src/items/cake.cpp index 8e09cff63fc..28a633e9df0 100644 --- a/src/items/cake.cpp +++ b/src/items/cake.cpp @@ -85,6 +85,8 @@ void Cake::onFireFlyable() btQuaternion q = m_owner->getTrans().getRotation(); gravity_vector = Vec3(0, -1, 0).rotate(q.getAxis(), q.getAngle()); gravity_vector = gravity_vector.normalize() * m_gravity; + const bool backwards = m_owner->getControls().getLookBack(); + // A bit of a hack: the mass of this kinematic object is still 1.0 // (see flyable), which enables collisions. I tried setting // collisionFilterGroup/mask, but still couldn't get this object to @@ -93,17 +95,11 @@ void Cake::onFireFlyable() // time a homing-track collision happens). float forward_offset=m_owner->getKartLength()/2.0f + m_extend.getZ()/2.0f; - float up_velocity = m_speed/7.0f; - - // give a speed proportional to kart speed. m_speed is defined in flyable - m_speed *= m_owner->getSpeed() / 23.0f; - - //when going backwards, decrease speed of cake by less - if (m_owner->getSpeed() < 0) m_speed /= 3.6f; + float up_velocity = m_speed/6.3f; - m_speed += 16.0f; - - if (m_speed < 1.0f) m_speed = 1.0f; + // Adds the speed of the kart owning the cake. m_speed is defined in flyable + m_speed += (backwards ? -m_owner->getSpeed() + : m_owner->getSpeed()); btTransform trans = m_owner->getTrans(); @@ -111,7 +107,6 @@ void Cake::onFireFlyable() float pitch = m_owner->getTerrainPitch(heading); // Find closest kart in front of the current one - const bool backwards = m_owner->getControls().getLookBack(); const AbstractKart *closest_kart=NULL; Vec3 direction; float kart_dist_squared; @@ -124,8 +119,8 @@ void Cake::onFireFlyable() // this code finds the correct angle and upwards velocity to hit an opponents' // vehicle if they were to continue travelling in the same direction and same speed // (barring any obstacles in the way of course) - if(closest_kart != NULL && kart_dist_squared < m_st_max_distance_squared && - m_speed>closest_kart->getSpeed()) + + if(closest_kart != NULL && kart_dist_squared < m_st_max_distance_squared) { m_target = (AbstractKart*)closest_kart; From 88725ae2cb77853b708117f679da6befdefc7e36 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Tue, 14 Nov 2023 08:14:38 +0100 Subject: [PATCH 027/188] Reduce the bubble gum shield duration from 10s to 8s --- data/kart_characteristics.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 5222e8cb1f8..842678f7b29 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -234,7 +234,7 @@ fade-in-time: How quick the slowdown takes effect. shield-duration: How long the bubblegum shield lasts --> + shield-duration="8.0" /> - + + + + + + + + + + + diff --git a/data/gui/icons/gauge_full_bright.png b/data/gui/icons/gauge_full_bright.png index aa1ba9385a2d916eac87a89c4b5e72e3d34d5d4a..bf8a736a882ad8f3c65514589c2ebf1282234846 100644 GIT binary patch literal 20150 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 literal 10743 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

vXg-b{*OolJK(+3%ohF?U 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 literal 0 HcmV?d00001 diff --git a/data/gui/icons/gauge_full_hack_bright.png b/data/gui/icons/gauge_full_hack_bright.png new file mode 100644 index 0000000000000000000000000000000000000000..4967cd354175518c27a691cd444aa252c1367171 GIT binary patch literal 21023 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 literal 0 HcmV?d00001 diff --git a/data/gui/icons/gauge_negative.png b/data/gui/icons/gauge_negative.png new file mode 100644 index 0000000000000000000000000000000000000000..5c7bffbf11902692db6dfb11e8f969c70c9972ec GIT binary patch literal 127873 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 z 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σ{zV3Rx{YFdyKE@e$v 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 M*3{RiRday-7svN$Q2+n{ literal 0 HcmV?d00001 diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 34f27372a35..29d801211f0 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -317,6 +317,11 @@ small-container="1" big-container="2.5" max-speed-increase="5" duration="0.8" fade-out-time="2.0" max="20" /> + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - + - + - - - + + + - + - - + + - - + + - - + + diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index 65c234f3986..d66298479cf 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -168,6 +168,15 @@ void Powerup::set(PowerupManager::PowerupType type, int n) case PowerupManager::POWERUP_ZIPPER: break ; + // TODO : add sound effects + case PowerupManager::POWERUP_SUDO: + m_sound_use = SFXManager::get()->createSoundSource("sudo_bad"); + break ; + + // TODO : add sound effects + case PowerupManager::POWERUP_ELECTRO: + break ; + case PowerupManager::POWERUP_BOWLING: m_sound_use = SFXManager::get()->createSoundSource("bowling_shoot"); break ; @@ -293,10 +302,7 @@ void Powerup::use() { im->switchItems(); if (!has_played_sound) - { - m_sound_use->setPosition(m_kart->getXYZ()); m_sound_use->play(); - } break; } case PowerupManager::POWERUP_CAKE: @@ -311,7 +317,7 @@ void Powerup::use() m_sound_use->play(); } ProjectileManager::get()->newProjectile(m_kart, m_type); - break ; + break; case PowerupManager::POWERUP_SWATTER: m_kart->getAttachment() @@ -319,6 +325,62 @@ void Powerup::use() stk_config->time2Ticks(kp->getSwatterDuration())); break; + case PowerupManager::POWERUP_SUDO: + { + AbstractKart* player_kart = NULL; + + float stolen_energy = 0.0f; + + // Steal some nitro from the five karts ahead. + // This can set their nitro count to a negative number + for(unsigned int i = 0 ; i < world->getNumKarts(); ++i) + { + AbstractKart *kart=world->getKart(i); + // Standard invulnerability (the "stars") is not useful here + if( kart->isEliminated() || kart== m_kart || kart->hasFinishedRace()) + continue; + if(m_kart->getPosition() > kart->getPosition() && + m_kart->getPosition() <= kart->getPosition() + 5) + { + float amount_to_steal = 1.25f; + // Remove nitro from a target kart and add to the recipient + kart->addEnergy(-amount_to_steal, /* allow negatives */ true); + stolen_energy += amount_to_steal; + // This is used for display in the race GUI + kart->setStolenNitro(amount_to_steal, /* duration */ 1.75f); + + // Remember if the target kart is player controlled + // for a negative sound effects (TODO) + if(kart->getController()->isLocalPlayerController()) + player_kart = kart; + } + } + + // Gift one additional small nitro on top of the stolen energy + // and activate the "nitro boost" mode + // TODO make the gift of nitro depend on kart class/ nitro efficiency ?? + m_kart->addEnergy(stolen_energy + 1.5f, /* allow negatives */ false); + m_kart->activateNitroHack(); + + // Play a good sound for the kart that benefits from the "nitro-hack", + // if it's a local player + if (!has_played_sound && m_kart->getController()->isLocalPlayerController()) + { + //Extraordinary. Usually sounds are set in Powerup::set() + m_sound_use = SFXManager::get()->createSoundSource("sudo_good"); + //In this case this is a workaround, since the sudo item has two different sounds + + m_sound_use->play(); + } + // Play a bad sound if the affected kart (but not the user) is a local player + else if (!has_played_sound && player_kart != NULL) + { + m_sound_use->play(); + } + + break; + } // end of PowerupManager::POWERUP_SUDO + case PowerupManager::POWERUP_BUBBLEGUM: // use the bubble gum the traditional way, if the kart is looking back if (m_kart->getControls().getLookBack()) @@ -500,7 +562,7 @@ void Powerup::use() } // use //----------------------------------------------------------------------------- -/** This function is called when a bnous box is it. This function can be +/** This function is called when a bonus box is it. This function can be * called on a server (in which case item and add_info are not used), * or on a client, in which case the item and additional info is used * to make sure server and clients are synched correctly. @@ -555,11 +617,11 @@ void Powerup::hitBonusBox(const ItemState &item_state) // is divided by 10, meaning even if there is one frame difference, // the client will still have a 90% chance to correctly predict the // item. We multiply the item with a 'large' (more or less random) - // number to spread the random values across the (typically 200) + // number to spread the random values across the (typically 1000) // weights used in the PowerupManager - same for the position. uint64_t random_number = item_state.getItemId() * 31 + - world->getTicksSinceStart() / 10 + position * 23 + - powerup_manager->getRandomSeed(); + world->getTicksSinceStart() / stk_config->time2Ticks(0.083334f) + + position * 23 + powerup_manager->getRandomSeed(); // Use this random number as a seed of a PRNG (based on the one in // bullet's btSequentialImpulseConstraintSolver) to avoid getting diff --git a/src/items/powerup_manager.cpp b/src/items/powerup_manager.cpp index e818e7d3793..a448e58b9be 100644 --- a/src/items/powerup_manager.cpp +++ b/src/items/powerup_manager.cpp @@ -110,7 +110,8 @@ PowerupManager::PowerupType static std::string powerup_names[] = { "", /* Nothing */ "bubblegum", "cake", "bowling", "zipper", "plunger", "switch", - "swatter", "rubber-ball", "parachute", "anchor" + "swatter", "rubber-ball", "parachute", "nitro-hack", "electro-shield", + "anchor" }; for(unsigned int i=POWERUP_FIRST; i<=POWERUP_LAST; i++) diff --git a/src/items/powerup_manager.hpp b/src/items/powerup_manager.hpp index b7f61d8a251..fff4f0f5a2d 100644 --- a/src/items/powerup_manager.hpp +++ b/src/items/powerup_manager.hpp @@ -126,16 +126,15 @@ class PowerupManager : public NoCopy std::map > m_all_weights; public: - // The anvil and parachute must be at the end of the enum, and the - // zipper just before them (see Powerup::hitBonusBox). enum PowerupType {POWERUP_NOTHING, POWERUP_FIRST, POWERUP_BUBBLEGUM = POWERUP_FIRST, POWERUP_CAKE, POWERUP_BOWLING, POWERUP_ZIPPER, POWERUP_PLUNGER, - POWERUP_SWITCH, POWERUP_SWATTER, POWERUP_RUBBERBALL, - POWERUP_PARACHUTE, - POWERUP_ANVIL, //powerup.cpp assumes these two come last + POWERUP_SWITCH, POWERUP_SWATTER, + POWERUP_RUBBERBALL, POWERUP_PARACHUTE, + POWERUP_SUDO, POWERUP_ELECTRO, + POWERUP_ANVIL, POWERUP_LAST=POWERUP_ANVIL, POWERUP_MAX }; diff --git a/src/karts/abstract_characteristic.cpp b/src/karts/abstract_characteristic.cpp index eb492a43635..1ec7d987393 100644 --- a/src/karts/abstract_characteristic.cpp +++ b/src/karts/abstract_characteristic.cpp @@ -211,6 +211,10 @@ AbstractCharacteristic::ValueType AbstractCharacteristic::getType( return TYPE_FLOAT; case NITRO_MAX: return TYPE_FLOAT; + case NITROHACK_DURATION: + return TYPE_FLOAT; + case NITROHACK_FACTOR: + return TYPE_FLOAT; case SLIPSTREAM_DURATION_FACTOR: return TYPE_FLOAT; case SLIPSTREAM_BASE_SPEED: @@ -445,6 +449,10 @@ std::string AbstractCharacteristic::getName(CharacteristicType type) return "NITRO_FADE_OUT_TIME"; case NITRO_MAX: return "NITRO_MAX"; + case NITROHACK_DURATION: + return "NITROHACK_DURATION"; + case NITROHACK_FACTOR: + return "NITROHACK_FACTOR"; case SLIPSTREAM_DURATION_FACTOR: return "SLIPSTREAM_DURATION_FACTOR"; case SLIPSTREAM_BASE_SPEED: @@ -1451,6 +1459,30 @@ float AbstractCharacteristic::getNitroMax() const return result; } // getNitroMax +// ---------------------------------------------------------------------------- +float AbstractCharacteristic::getNitrohackDuration() const +{ + float result; + bool is_set = false; + process(NITROHACK_DURATION, &result, &is_set); + if (!is_set) + Log::fatal("AbstractCharacteristic", "Can't get characteristic %s", + getName(NITROHACK_DURATION).c_str()); + return result; +} // getNitrohackDuration + +// ---------------------------------------------------------------------------- +float AbstractCharacteristic::getNitrohackFactor() const +{ + float result; + bool is_set = false; + process(NITROHACK_FACTOR, &result, &is_set); + if (!is_set) + Log::fatal("AbstractCharacteristic", "Can't get characteristic %s", + getName(NITROHACK_FACTOR).c_str()); + return result; +} // getNitrohackFactor + // ---------------------------------------------------------------------------- float AbstractCharacteristic::getSlipstreamDurationFactor() const { diff --git a/src/karts/abstract_characteristic.hpp b/src/karts/abstract_characteristic.hpp index 008c60796a4..14450f157e6 100644 --- a/src/karts/abstract_characteristic.hpp +++ b/src/karts/abstract_characteristic.hpp @@ -189,6 +189,10 @@ class AbstractCharacteristic NITRO_FADE_OUT_TIME, NITRO_MAX, + // Nitrohack + NITROHACK_DURATION, + NITROHACK_FACTOR, + // Slipstream SLIPSTREAM_DURATION_FACTOR, SLIPSTREAM_BASE_SPEED, @@ -356,6 +360,9 @@ class AbstractCharacteristic float getNitroFadeOutTime() const; float getNitroMax() const; + float getNitrohackDuration() const; + float getNitrohackFactor() const; + float getSlipstreamDurationFactor() const; float getSlipstreamBaseSpeed() const; float getSlipstreamLength() const; diff --git a/src/karts/abstract_kart.hpp b/src/karts/abstract_kart.hpp index 28e86a66910..b07a6e2edee 100644 --- a/src/karts/abstract_kart.hpp +++ b/src/karts/abstract_kart.hpp @@ -367,10 +367,23 @@ class AbstractKart : public Moveable * \param item The item that was hit. */ virtual void collectedItem(ItemState *item_state) = 0; // ------------------------------------------------------------------------ + /** Called when the NitroHack powerup is used. **/ + virtual void activateNitroHack() = 0; + // ------------------------------------------------------------------------ + /** Returns the nitro hack status of this kart. */ + virtual bool isNitroHackActive() const = 0; + // ------------------------------------------------------------------------ + /** Sets the stolen nitro info of this kart. */ + virtual void setStolenNitro(float amount, float duration) = 0; + // ------------------------------------------------------------------------ + virtual bool hasStolenNitro() const = 0; + // ------------------------------------------------------------------------ + virtual float getStolenNitro() const = 0; + // ------------------------------------------------------------------------ /** Returns the current position of this kart in the race. */ virtual int getPosition() const = 0; // ------------------------------------------------------------------------ - /** Returns the current position of this kart in the race. */ + /** Sets the position of this kart in the race. */ virtual void setPosition(int p) = 0; // ------------------------------------------------------------------------ /** Returns the initial position of this kart. */ @@ -410,6 +423,8 @@ class AbstractKart : public Moveable /** Returns true if this kart has finished the race. */ virtual bool hasFinishedRace() const = 0; // ------------------------------------------------------------------------ + virtual void addEnergy(float val, bool allow_negative = false) { }; + // ------------------------------------------------------------------------ virtual void setEnergy(float val) = 0; // ------------------------------------------------------------------------ /** Return whether nitro is being used despite the nitro button not being diff --git a/src/karts/controller/ai_base_controller.hpp b/src/karts/controller/ai_base_controller.hpp index fd6900cb9f9..6213386b076 100644 --- a/src/karts/controller/ai_base_controller.hpp +++ b/src/karts/controller/ai_base_controller.hpp @@ -93,8 +93,6 @@ class AIBaseController : public Controller virtual void crashed(const AbstractKart *k) OVERRIDE {}; virtual void handleZipper(bool play_sound) OVERRIDE {}; virtual void finishedRace(float time) OVERRIDE {}; - virtual void collectedItem(const ItemState &item, - float previous_energy=0) OVERRIDE {}; virtual void setPosition(int p) OVERRIDE {}; virtual bool isPlayerController() const OVERRIDE { return false; } virtual bool isLocalPlayerController() const OVERRIDE { return false; } diff --git a/src/karts/controller/controller.hpp b/src/karts/controller/controller.hpp index 787b6a73375..7400b70fc0d 100644 --- a/src/karts/controller/controller.hpp +++ b/src/karts/controller/controller.hpp @@ -63,8 +63,9 @@ class Controller virtual void reset () = 0; virtual void update (int ticks) = 0; virtual void handleZipper (bool play_sound) = 0; + // This function is only used by the local controller to play sounds virtual void collectedItem (const ItemState &item, - float previous_energy=0) = 0; + float previous_energy=0) { }; virtual void crashed (const AbstractKart *k) = 0; virtual void crashed (const Material *m) = 0; virtual void setPosition (int p) = 0; diff --git a/src/karts/controller/ghost_controller.hpp b/src/karts/controller/ghost_controller.hpp index 07c9768b650..d008d39fa12 100644 --- a/src/karts/controller/ghost_controller.hpp +++ b/src/karts/controller/ghost_controller.hpp @@ -53,8 +53,6 @@ class GhostController : public Controller virtual void crashed(const AbstractKart *k) OVERRIDE {} virtual void handleZipper(bool play_sound) OVERRIDE {} virtual void finishedRace(float time) OVERRIDE {} - virtual void collectedItem(const ItemState &item, - float previous_energy=0) OVERRIDE {} virtual void setPosition(int p) OVERRIDE {} virtual bool isPlayerController() const OVERRIDE { return false; } virtual bool isLocalPlayerController() const OVERRIDE { return false; } diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index 53b2d664e1d..553623ad59e 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -1256,6 +1256,15 @@ void SkiddingAI::handleItems(const float dt, const Vec3 *aim_point, int last_nod case PowerupManager::POWERUP_ANVIL: break; // POWERUP_ANVIL + case PowerupManager::POWERUP_SUDO: + if (m_time_since_last_shot > 3.0f) + m_controls->setFire(true); + break; // POWERUP_SUDO + + case PowerupManager::POWERUP_ELECTRO: + m_controls->setFire(true); + break; // POWERUP_ELECTRO + case PowerupManager::POWERUP_SWATTER: { // if the kart has a shield, do not break it by using a swatter. diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index b9920c762e5..e4dad793bca 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -132,6 +132,10 @@ Kart::Kart (const std::string& ident, unsigned int world_kart_id, m_saved_controller = NULL; m_consumption_per_tick = stk_config->ticks2Time(1) * m_kart_properties->getNitroConsumption(); + m_nitro_hack_ticks = 0; + m_nitro_hack_factor = 1.0f; + m_stolen_nitro_ticks = 0; + m_stolen_nitro_amount = 0.0f; m_fire_clicked = 0; m_default_suspension_force = 0.0f; m_boosted_ai = false; @@ -330,6 +334,10 @@ void Kart::reset() m_energy_to_min_ratio = 0; m_consumption_per_tick = stk_config->ticks2Time(1) * m_kart_properties->getNitroConsumption(); + m_nitro_hack_ticks = 0; + m_nitro_hack_factor = 1.0f; + m_stolen_nitro_ticks = 0; + m_stolen_nitro_amount = 0.0f; // Reset star effect in case that it is currently being shown. if (m_stars_effect) @@ -1180,6 +1188,8 @@ void Kart::collectedItem(ItemState *item_state) if ( m_collected_energy > m_kart_properties->getNitroMax()) m_collected_energy = m_kart_properties->getNitroMax(); + + // Play sound effects if the kart is controlled by a local player m_controller->collectedItem(*item_state, old_energy); } // collectedItem @@ -2238,13 +2248,73 @@ void Kart::handleZipper(const Material *material, bool play_sound) } // handleZipper +/** Allows to add nitro while enforcing max nitro storage. */ +void Kart::addEnergy(float val, bool allow_negative = false) +{ + // Negative nitro is allowed in general, but this specific + // change to the nitro level is not allowed to make it negative + // or more negative + if (!allow_negative && val < 0 && m_collected_energy > 0) + { + if (-val > m_collected_energy) + m_collected_energy = 0; + else + m_collected_energy += val; + } + else + { + m_collected_energy += val; + } + + // Max nitro is always enforced no matter what + if (m_collected_energy > m_kart_properties->getNitroMax()) + m_collected_energy = m_kart_properties->getNitroMax(); +} + +//----------------------------------------------------------------------------- +/** Called when the NitroHack powerup is used by the kart. **/ +void Kart::activateNitroHack() +{ + m_nitro_hack_ticks = stk_config->time2Ticks(m_kart_properties->getNitrohackDuration()); + m_nitro_hack_factor = m_kart_properties->getNitrohackFactor(); +} // activateNitroHack + +//----------------------------------------------------------------------------- +/** Called when the NitroHack powerup targets this kart. **/ +void Kart::setStolenNitro(float amount, float duration) +{ + // If a second steal happens while the first one is displayed, + // display both until the expiration of the second one. + m_stolen_nitro_amount += amount; + m_stolen_nitro_ticks = stk_config->time2Ticks(duration); +} // setStolenNitro + // ----------------------------------------------------------------------------- /** Updates the current nitro status. * \param ticks Number of physics time steps - should be 1. */ void Kart::updateNitro(int ticks) { - if (m_collected_energy == 0) + if (m_stolen_nitro_ticks > 0) + m_stolen_nitro_ticks -= ticks; + if (m_nitro_hack_ticks > 0) + m_nitro_hack_ticks -= ticks; + + // Reset the stolen nitro amount when the display duration expires + if (m_stolen_nitro_ticks <= 0) + { + m_stolen_nitro_ticks = 0; + m_stolen_nitro_amount = 0.0f; + } + + // Reset the nitro multiplier when the nitro-hack expires + if (m_nitro_hack_ticks <= 0) + { + m_nitro_hack_ticks = 0; + m_nitro_hack_factor = 1.0f; + } + + if (m_collected_energy <= 0) m_min_nitro_ticks = 0; if (m_controls.getNitro() && m_min_nitro_ticks <= 0 && m_collected_energy > 0) @@ -2274,13 +2344,17 @@ void Kart::updateNitro(int ticks) return; } - + float previous_energy = m_collected_energy; m_collected_energy -= m_consumption_per_tick*ticks; - if (m_collected_energy < 0) + + // We don't allow nitro to go into the negative + // as a result of normal nitro usage. + // It can happen as a result of the Nitro-Hack powerup, however. + if (previous_energy >= 0 && m_collected_energy < 0) { + m_collected_energy = 0; if(m_nitro_sound->getStatus() == SFXBase::SFX_PLAYING && !rewinding) m_nitro_sound->stop(); - m_collected_energy = 0; return; } @@ -2290,8 +2364,8 @@ void Kart::updateNitro(int ticks) m_nitro_sound->play(); m_max_speed->increaseMaxSpeed(MaxSpeed::MS_INCREASE_NITRO, - m_kart_properties->getNitroMaxSpeedIncrease(), - m_kart_properties->getNitroEngineForce(), + m_kart_properties->getNitroMaxSpeedIncrease()*m_nitro_hack_factor, + m_kart_properties->getNitroEngineForce()*m_nitro_hack_factor, stk_config->time2Ticks(m_kart_properties->getNitroDuration()*m_energy_to_min_ratio), stk_config->time2Ticks(m_kart_properties->getNitroFadeOutTime())); } @@ -3306,7 +3380,7 @@ void Kart::updateGraphics(float dt) // the normal maximum speed of the kart. if(nitro_frac>1.0f) nitro_frac = 1.0f; } - m_kart_gfx->updateNitroGraphics(nitro_frac); + m_kart_gfx->updateNitroGraphics(nitro_frac, m_nitro_hack_ticks > 0); // Handle leaning of karts // ----------------------- diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index d75cefadbcd..b229d6083da 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -230,6 +230,17 @@ class Kart : public AbstractKart float m_energy_to_min_ratio; + /** If > 0 then nitro-hack effect is on. */ + int16_t m_nitro_hack_ticks; + + /** The bonus factor for nitro use **/ + float m_nitro_hack_factor; + + /** Used to display stolen nitro in the UI **/ + int16_t m_stolen_nitro_ticks; + + float m_stolen_nitro_amount; + float m_startup_boost; float m_falling_time; @@ -308,6 +319,11 @@ class Kart : public AbstractKart virtual void startEngineSFX () OVERRIDE; virtual void collectedItem(ItemState *item) OVERRIDE; + virtual void activateNitroHack() OVERRIDE; + virtual bool isNitroHackActive() const OVERRIDE { return m_nitro_hack_ticks > 0; } + virtual void setStolenNitro(float amount, float duration) OVERRIDE; + virtual bool hasStolenNitro() const OVERRIDE { return m_stolen_nitro_ticks > 0; } + virtual float getStolenNitro() const OVERRIDE { return m_stolen_nitro_amount; } virtual float getStartupBoostFromStartTicks(int ticks) const OVERRIDE; virtual float getStartupBoost() const OVERRIDE { return m_startup_boost; } virtual void setStartupBoost(float val) OVERRIDE { m_startup_boost = val; } @@ -398,6 +414,9 @@ class Kart : public AbstractKart /** Returns the remaining collected energy. */ virtual float getEnergy() const OVERRIDE { return m_collected_energy; } // ---------------------------------------------------------------------------------------- + /** Allows to add nitro while enforcing max nitro storage. */ + virtual void addEnergy(float val, bool allow_negative); + // ---------------------------------------------------------------------------------------- /** Sets the energy the kart has collected. */ virtual void setEnergy(float val) OVERRIDE { m_collected_energy = val; } // ---------------------------------------------------------------------------------------- diff --git a/src/karts/kart_gfx.cpp b/src/karts/kart_gfx.cpp index 5633359f22f..3e48e0e2279 100644 --- a/src/karts/kart_gfx.cpp +++ b/src/karts/kart_gfx.cpp @@ -41,6 +41,7 @@ KartGFX::KartGFX(const AbstractKart *kart, bool is_day) { m_nitro_light = NULL; + m_nitro_hack_light = NULL; m_skidding_light_1 = NULL; m_skidding_light_2 = NULL; m_skidding_light_3 = NULL; @@ -61,9 +62,16 @@ KartGFX::KartGFX(const AbstractKart *kart, bool is_day) /*radius*/ 5.0f, 0.0f, 0.4f, 1.0f, false, node); m_nitro_light->setVisible(false); + + m_nitro_hack_light = irr_driver->addLight(location, /*force*/ 0.4f, + /*radius*/ 5.0f, 0.1f, 1.0f, 0.1f, + false, node); + m_nitro_hack_light->setVisible(false); #ifdef DEBUG m_nitro_light->setName( ("nitro emitter (" + m_kart->getIdent() + ")").c_str() ); + m_nitro_hack_light->setName( ("nitro-hack emitter (" + m_kart->getIdent() + + ")").c_str() ); #endif // Create skidding lights @@ -95,6 +103,7 @@ KartGFX::KartGFX(const AbstractKart *kart, bool is_day) + ")").c_str() ); m_nitro_light->grab(); + m_nitro_hack_light->grab(); m_skidding_light_1->grab(); m_skidding_light_2->grab(); m_skidding_light_3->grab(); @@ -122,6 +131,8 @@ KartGFX::KartGFX(const AbstractKart *kart, bool is_day) // in the order of KartGFXType. addEffect(KGFX_NITRO1, "nitro.xml", rear_nitro_right, true ); addEffect(KGFX_NITRO2, "nitro.xml", rear_nitro_left, true ); + addEffect(KGFX_NITROHACK1, "nitro-hack.xml", rear_nitro_right, true ); + addEffect(KGFX_NITROHACK2, "nitro-hack.xml", rear_nitro_left, true ); addEffect(KGFX_NITROSMOKE1, "nitro-smoke.xml", rear_nitro_left, false); addEffect(KGFX_NITROSMOKE2, "nitro-smoke.xml", rear_nitro_right, false); addEffect(KGFX_ZIPPER, "zipper_fire.xml", rear_center, true ); @@ -163,6 +174,7 @@ KartGFX::~KartGFX() if (!GUIEngine::isNoGraphics() && CVS->isGLSL()) { m_nitro_light->drop(); + m_nitro_hack_light->drop(); m_skidding_light_1->drop(); m_skidding_light_2->drop(); m_skidding_light_3->drop(); @@ -476,7 +488,7 @@ void KartGFX::update(float dt) /** Updates nitro dependent particle effects. * \param nitro_frac Nitro fraction/ */ -void KartGFX::updateNitroGraphics(float nitro_frac) +void KartGFX::updateNitroGraphics(float nitro_frac, bool isNitroHackOn) { #ifndef SERVER_ONLY if (GUIEngine::isNoGraphics()) @@ -487,23 +499,47 @@ void KartGFX::updateNitroGraphics(float nitro_frac) // -------------------------------------------------------- if (nitro_frac > 0) { - setCreationRateRelative(KartGFX::KGFX_NITRO1, nitro_frac); - setCreationRateRelative(KartGFX::KGFX_NITRO2, nitro_frac); + if(isNitroHackOn) + { + setCreationRateAbsolute(KartGFX::KGFX_NITRO1, 0); + setCreationRateAbsolute(KartGFX::KGFX_NITRO2, 0); + setCreationRateRelative(KartGFX::KGFX_NITROHACK1, nitro_frac); + setCreationRateRelative(KartGFX::KGFX_NITROHACK2, nitro_frac); + if (CVS->isGLSL()) + { + m_nitro_light->setVisible(false); + m_nitro_hack_light->setVisible(true); + } + } + else + { + setCreationRateRelative(KartGFX::KGFX_NITRO1, nitro_frac); + setCreationRateRelative(KartGFX::KGFX_NITRO2, nitro_frac); + setCreationRateAbsolute(KartGFX::KGFX_NITROHACK1, 0); + setCreationRateAbsolute(KartGFX::KGFX_NITROHACK2, 0); + if (CVS->isGLSL()) + { + m_nitro_light->setVisible(true); + m_nitro_hack_light->setVisible(false); + } + } setCreationRateRelative(KartGFX::KGFX_NITROSMOKE1, nitro_frac); setCreationRateRelative(KartGFX::KGFX_NITROSMOKE2, nitro_frac); - - if (CVS->isGLSL()) - m_nitro_light->setVisible(true); } else { setCreationRateAbsolute(KartGFX::KGFX_NITRO1, 0); setCreationRateAbsolute(KartGFX::KGFX_NITRO2, 0); + setCreationRateAbsolute(KartGFX::KGFX_NITROHACK1, 0); + setCreationRateAbsolute(KartGFX::KGFX_NITROHACK2, 0); setCreationRateAbsolute(KartGFX::KGFX_NITROSMOKE1, 0); setCreationRateAbsolute(KartGFX::KGFX_NITROSMOKE2, 0); if (CVS->isGLSL()) + { m_nitro_light->setVisible(false); + m_nitro_hack_light->setVisible(false); + } } // Exhaust is always emitting @@ -643,6 +679,7 @@ void KartGFX::setGFXInvisible() if (!GUIEngine::isNoGraphics() && CVS->isGLSL()) { m_nitro_light->setVisible(false); + m_nitro_hack_light->setVisible(false); m_skidding_light_1->setVisible(false); m_skidding_light_2->setVisible(false); m_skidding_light_3->setVisible(false); diff --git a/src/karts/kart_gfx.hpp b/src/karts/kart_gfx.hpp index 8ca5b41d122..7b4e2b7ce49 100644 --- a/src/karts/kart_gfx.hpp +++ b/src/karts/kart_gfx.hpp @@ -47,6 +47,8 @@ class KartGFX * is the number of entries and must therefore be last. */ enum KartGFXType { KGFX_NITRO1=0, KGFX_NITRO2, + KGFX_NITROHACK1, + KGFX_NITROHACK2, KGFX_NITROSMOKE1, KGFX_NITROSMOKE2, KGFX_ZIPPER, @@ -94,6 +96,9 @@ class KartGFX /** A light that's shown when the kart uses nitro. */ irr::scene::ISceneNode* m_nitro_light; + /** A light that's shown when the kart uses nitro with "nitro-hack" on. */ + irr::scene::ISceneNode* m_nitro_hack_light; + /** Light that is shown when the kart is skidding. */ irr::scene::ISceneNode* m_skidding_light_1; @@ -118,7 +123,7 @@ class KartGFX void setCreationRateRelative(const KartGFXType type, float f); void updateTerrain(const ParticleKind *pk); void update(float dt); - void updateNitroGraphics(float f); + void updateNitroGraphics(float f, bool isNitroHackOn); void updateSkidLight(unsigned int level); void getGFXStatus(int* nitro, bool* zipper, int* skidding, bool* red_skidding, bool* purple_skidding) const; diff --git a/src/karts/kart_properties.cpp b/src/karts/kart_properties.cpp index 77d51584855..f29bb1ebdc2 100644 --- a/src/karts/kart_properties.cpp +++ b/src/karts/kart_properties.cpp @@ -1197,6 +1197,19 @@ float KartProperties::getNitroMax() const return m_cached_characteristic->getNitroMax(); } // getNitroMax +// ---------------------------------------------------------------------------- +float KartProperties::getNitrohackDuration() const +{ + return m_cached_characteristic->getNitrohackDuration(); +} // getNitrohackDuration + + +// ---------------------------------------------------------------------------- +float KartProperties::getNitrohackFactor() const +{ + return m_cached_characteristic->getNitrohackFactor(); +} // getNitrohackFactor + // ---------------------------------------------------------------------------- float KartProperties::getSlipstreamDurationFactor() const { diff --git a/src/karts/kart_properties.hpp b/src/karts/kart_properties.hpp index 97c02af5263..e03c07df87c 100644 --- a/src/karts/kart_properties.hpp +++ b/src/karts/kart_properties.hpp @@ -494,6 +494,9 @@ class KartProperties float getNitroFadeOutTime() const; float getNitroMax() const; + float getNitrohackDuration() const; + float getNitrohackFactor() const; + float getSlipstreamDurationFactor() const; float getSlipstreamBaseSpeed() const; float getSlipstreamLength() const; diff --git a/src/karts/xml_characteristic.cpp b/src/karts/xml_characteristic.cpp index 8fa8cbf033b..f2f6744e836 100644 --- a/src/karts/xml_characteristic.cpp +++ b/src/karts/xml_characteristic.cpp @@ -553,6 +553,14 @@ void XmlCharacteristic::load(const XMLNode *node) &m_values[NITRO_MAX]); } + if (const XMLNode *sub_node = node->getNode("nitro-hack")) + { + sub_node->get("duration", + &m_values[NITROHACK_DURATION]); + sub_node->get("factor", + &m_values[NITROHACK_FACTOR]); + } + if (const XMLNode *sub_node = node->getNode("slipstream")) { sub_node->get("duration-factor", diff --git a/src/replay/replay_play.cpp b/src/replay/replay_play.cpp index ce61b9c2a07..11a722c4f2a 100644 --- a/src/replay/replay_play.cpp +++ b/src/replay/replay_play.cpp @@ -433,6 +433,7 @@ void ReplayPlay::readKartData(FILE *fd, char *next_line, bool second_replay) kre.m_zipper_usage = zipper!=0; kre.m_skidding_effect = skidding; kre.m_red_skidding = red_skidding!=0; + kre.m_purple_skidding = 0; //not saved in version 3 replays kre.m_jumping = jumping != 0; m_ghost_karts[kart_num]->addReplayEvent(time, btTransform(q, xyz), pi, bi, kre); @@ -482,6 +483,7 @@ void ReplayPlay::readKartData(FILE *fd, char *next_line, bool second_replay) kre.m_zipper_usage = zipper!=0; kre.m_skidding_effect = skidding; kre.m_red_skidding = red_skidding!=0; + kre.m_purple_skidding = 0; //not saved in version 4 replays kre.m_jumping = jumping != 0; m_ghost_karts[kart_num]->addReplayEvent(time, btTransform(q, xyz), pi, bi, kre); diff --git a/src/states_screens/race_gui.cpp b/src/states_screens/race_gui.cpp index 18c2574e10b..a8d137ae5a8 100644 --- a/src/states_screens/race_gui.cpp +++ b/src/states_screens/race_gui.cpp @@ -787,8 +787,18 @@ void RaceGUI::drawEnergyMeter(int x, int y, const AbstractKart *kart, float state = (float)(kart->getEnergy()) / kart->getKartProperties()->getNitroMax(); - if (state < 0.0f) state = 0.0f; - else if (state > 1.0f) state = 1.0f; + bool negative_nitro = false; + float stolen_nitro = kart->getStolenNitro() / kart->getKartProperties()->getNitroMax(); + assert (stolen_nitro >= 0.0f); + float full_state = state + stolen_nitro; + + if (state < 0.0f) + { + state = -state; + negative_nitro = true; + } + if (state > 1.0f) + state = 1.0f; core::vector2df offset; offset.X = (float)(x-gauge_width) - 9.5f*scaling.X; @@ -832,8 +842,15 @@ void RaceGUI::drawEnergyMeter(int x, int y, const AbstractKart *kart, position[8].X = 0.94f;//G2 (margin for gauge goal) position[8].Y = 0.17f;//G2 - // The states at which different polygons must be used. + core::vector2df negative_position[vertices_count]; + negative_position[0] = position[0]; + for (int i=1; i 0.0f) + if (state > 0.0f || kart->hasStolenNitro()) { video::S3DVertex vertices[vertices_count]; + unsigned int count; + //3D effect : wait for the full border to appear before drawing for (int i=0;i<5;i++) { + if ((full_state-0.2f*i < 0.006f && full_state-0.2f*i >= 0.0f) || (0.2f*i-full_state < 0.003f && 0.2f*i-full_state >= 0.0f) ) + { + full_state = 0.2f*i-0.003f; + } if ((state-0.2f*i < 0.006f && state-0.2f*i >= 0.0f) || (0.2f*i-state < 0.003f && 0.2f*i-state >= 0.0f) ) { state = 0.2f*i-0.003f; - break; } } - unsigned int count = computeVerticesForMeter(position, threshold, vertices, vertices_count, + if (negative_nitro) + { + count = computeVerticesForMeter(negative_position, threshold, vertices, vertices_count, state, gauge_width, gauge_height, offset); - - if(kart->getControls().getNitro() || kart->isOnMinNitroTime()) - drawMeterTexture(m_gauge_full_bright, vertices, count, true); + drawMeterTexture(m_gauge_negative, vertices, count, true); + } else - drawMeterTexture(m_gauge_full, vertices, count, true); + { + count = computeVerticesForMeter(position, threshold, vertices, vertices_count, + state, gauge_width, gauge_height, offset); + + if(kart->isNitroHackActive()) + { + if(kart->getControls().getNitro() || kart->isOnMinNitroTime()) + drawMeterTexture(m_gauge_full_hack_bright, vertices, count, true); + else + drawMeterTexture(m_gauge_full_hack, vertices, count, true); + } + else + { + if(kart->getControls().getNitro() || kart->isOnMinNitroTime()) + drawMeterTexture(m_gauge_full_bright, vertices, count, true); + else + drawMeterTexture(m_gauge_full, vertices, count, true); + } + } + + // If some nitro was stolen from the kart, display the stolen amount + // If the amount of nitro we had before the steal was already negative, + // (case full_state <= 0.0f), there is nothing to do + if (kart->hasStolenNitro() && full_state > 0.0f) + { + unsigned int count_temp, count_final; + // We still have some nitro left + if (!negative_nitro && count > 0) + { + // The variable vertice is the one stored at index [1] + // TODO : Clean up documentation, the explanations of computeVerticesForMeter + // give the wrong impression that the variable vertice is stored last + video::S3DVertex variable_vertice = vertices[1]; + + count_temp = computeVerticesForMeter(position, threshold, vertices, vertices_count, + full_state, gauge_width, gauge_height, offset); + + // Consider a case where the count is 4 with vertices A, B, C, D, + // and count_temp is 5 with vertices A, B, C, D', E + // We want to trace the gauge using vertices A, D, D', E + // The count of required vertices will hence follow the formula below + // In theory count_final is an unnecessary variable but it's easier to reason with + assert(count <= count_temp); + count_final = count_temp - count + 3; + + // The first vertice is always the same, the second use the saved vertice + vertices[1] = variable_vertice; + + // Loop over the new vertices + for (unsigned int i=2;igetTexture(FileManager::GUI_ICON, "gauge_full.png"); - m_gauge_full_bright = irr_driver->getTexture(FileManager::GUI_ICON, "gauge_full_bright.png"); - m_gauge_empty = irr_driver->getTexture(FileManager::GUI_ICON, "gauge_empty.png"); - m_gauge_goal = irr_driver->getTexture(FileManager::GUI_ICON, "gauge_goal.png"); - m_lap_flag = irr_driver->getTexture(FileManager::GUI_ICON, "lap_flag.png"); - m_dist_show_overlap = 2; - m_icons_inertia = 2; + m_gauge_full = irr_driver->getTexture(FileManager::GUI_ICON, "gauge_full.png"); + m_gauge_full_bright = irr_driver->getTexture(FileManager::GUI_ICON, "gauge_full_bright.png"); + m_gauge_full_hack = irr_driver->getTexture(FileManager::GUI_ICON, "gauge_full_hack.png"); + m_gauge_full_hack_bright = irr_driver->getTexture(FileManager::GUI_ICON, "gauge_full_hack_bright.png"); + m_gauge_negative = irr_driver->getTexture(FileManager::GUI_ICON, "gauge_negative.png"); + m_gauge_empty = irr_driver->getTexture(FileManager::GUI_ICON, "gauge_empty.png"); + m_gauge_goal = irr_driver->getTexture(FileManager::GUI_ICON, "gauge_goal.png"); + m_lap_flag = irr_driver->getTexture(FileManager::GUI_ICON, "lap_flag.png"); + m_dist_show_overlap = 2; + m_icons_inertia = 2; m_referee = NULL; m_multitouch_gui = NULL; diff --git a/src/states_screens/race_gui_base.hpp b/src/states_screens/race_gui_base.hpp index 353e94d3d3e..d7219027c15 100644 --- a/src/states_screens/race_gui_base.hpp +++ b/src/states_screens/race_gui_base.hpp @@ -188,6 +188,14 @@ class RaceGUIBase video::ITexture *m_gauge_full; /** Highlight gauge, used when a kart uses nitro. */ video::ITexture *m_gauge_full_bright; + /** Texture for nitro gauge with a nitro-hack activated. */ + video::ITexture *m_gauge_full_hack; + /** Highlight gauge, used when a kart uses nitro + * while the nitro-hack is activated. */ + video::ITexture *m_gauge_full_hack_bright; + /** Used when the nitro amount is negative, + * or to highlight nitro that just got stolen. */ + video::ITexture *m_gauge_negative; video::ITexture *m_gauge_goal; From cd71af8546d96a9fd27e32aefc5db9eacadd9581 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Thu, 16 Nov 2023 20:44:41 +0100 Subject: [PATCH 030/188] Improvements for the Nitro-Hack powerup - Make the number of target karts, the stolen amount and the bonus amount if no steal configurable in .xml - Make the stolen amount different depending on rank difference, so that the steal doesn't become too small in races with very few karts - Do not stack stolen nitro with free nitro - Tune down the strength of the powerup. The amount of nitro that can be obtained doesn't exceed 4.1 now (up to 7.25 previously) and the duration is down to 7.0 from 8.0. It's likely still too strong. - Increase the number of karts targeted for steal from 5 to 6 - Add a debug entry for Nitro-Hack in the artist debug mode menu (no hotkey) - Clean some comments --- data/kart_characteristics.xml | 2 +- data/powerup.xml | 36 ++++++++++++++++------ src/items/powerup.cpp | 46 +++++++++++++++++----------- src/items/powerup_manager.cpp | 57 ++++++++++++++++++++++++++++++++++- src/items/powerup_manager.hpp | 11 +++++++ src/items/rubber_ball.cpp | 2 +- src/utils/debug.cpp | 5 +++ 7 files changed, 130 insertions(+), 29 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 29d801211f0..c867d4155e5 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -320,7 +320,7 @@ - + + + max-speed-increase="5" min-burst="0.5" duration="0.8" + fade-out-time="1.6" max="20" /> + max-targets="6" bonus-no-kart="0.125" + stolen-max="1.25" stolen-add="0.275" stolen-multiply="0.34"/> - + diff --git a/src/karts/abstract_characteristic.cpp b/src/karts/abstract_characteristic.cpp index 1ec7d987393..6ad04f6a73b 100644 --- a/src/karts/abstract_characteristic.cpp +++ b/src/karts/abstract_characteristic.cpp @@ -207,6 +207,8 @@ AbstractCharacteristic::ValueType AbstractCharacteristic::getType( return TYPE_FLOAT; case NITRO_MAX_SPEED_INCREASE: return TYPE_FLOAT; + case NITRO_MIN_BURST: + return TYPE_FLOAT; case NITRO_FADE_OUT_TIME: return TYPE_FLOAT; case NITRO_MAX: @@ -445,6 +447,8 @@ std::string AbstractCharacteristic::getName(CharacteristicType type) return "NITRO_BIG_CONTAINER"; case NITRO_MAX_SPEED_INCREASE: return "NITRO_MAX_SPEED_INCREASE"; + case NITRO_MIN_BURST: + return "NITRO_MIN_BURST"; case NITRO_FADE_OUT_TIME: return "NITRO_FADE_OUT_TIME"; case NITRO_MAX: @@ -1435,6 +1439,18 @@ float AbstractCharacteristic::getNitroMaxSpeedIncrease() const return result; } // getNitroMaxSpeedIncrease +// ---------------------------------------------------------------------------- +float AbstractCharacteristic::getNitroMinBurst() const +{ + float result; + bool is_set = false; + process(NITRO_MIN_BURST, &result, &is_set); + if (!is_set) + Log::fatal("AbstractCharacteristic", "Can't get characteristic %s", + getName(NITRO_MIN_BURST).c_str()); + return result; +} // getNitroMinBurst + // ---------------------------------------------------------------------------- float AbstractCharacteristic::getNitroFadeOutTime() const { diff --git a/src/karts/abstract_characteristic.hpp b/src/karts/abstract_characteristic.hpp index 14450f157e6..2b611ec5155 100644 --- a/src/karts/abstract_characteristic.hpp +++ b/src/karts/abstract_characteristic.hpp @@ -186,6 +186,7 @@ class AbstractCharacteristic NITRO_SMALL_CONTAINER, NITRO_BIG_CONTAINER, NITRO_MAX_SPEED_INCREASE, + NITRO_MIN_BURST, NITRO_FADE_OUT_TIME, NITRO_MAX, @@ -357,6 +358,7 @@ class AbstractCharacteristic float getNitroSmallContainer() const; float getNitroBigContainer() const; float getNitroMaxSpeedIncrease() const; + float getNitroMinBurst() const; float getNitroFadeOutTime() const; float getNitroMax() const; diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index e4dad793bca..a2e6224ff4b 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -2319,7 +2319,7 @@ void Kart::updateNitro(int ticks) if (m_controls.getNitro() && m_min_nitro_ticks <= 0 && m_collected_energy > 0) { - m_min_nitro_ticks = m_kart_properties->getNitroMinConsumptionTicks(); + m_min_nitro_ticks = stk_config->time2Ticks(m_kart_properties->getNitroMinBurst()); float min_consumption = m_min_nitro_ticks * m_consumption_per_tick; m_energy_to_min_ratio = std::min(1, m_collected_energy/min_consumption); } diff --git a/src/karts/kart_properties.cpp b/src/karts/kart_properties.cpp index f29bb1ebdc2..91bc690bb1a 100644 --- a/src/karts/kart_properties.cpp +++ b/src/karts/kart_properties.cpp @@ -99,7 +99,7 @@ KartProperties::KartProperties(const std::string &filename) m_color = video::SColor(255, 0, 0, 0); m_shape = 32; // close enough to a circle. m_engine_sfx_type = "engine_small"; - m_nitro_min_consumption = 120; //FIXME make this a config parameter independent of physics FPS + // The default constructor for stk_config uses filename="" if (filename != "") { @@ -1185,6 +1185,12 @@ float KartProperties::getNitroMaxSpeedIncrease() const return m_cached_characteristic->getNitroMaxSpeedIncrease(); } // getNitroMaxSpeedIncrease +// ---------------------------------------------------------------------------- +float KartProperties::getNitroMinBurst() const +{ + return m_cached_characteristic->getNitroMinBurst(); +} // getNitroMinBurst + // ---------------------------------------------------------------------------- float KartProperties::getNitroFadeOutTime() const { diff --git a/src/karts/kart_properties.hpp b/src/karts/kart_properties.hpp index e03c07df87c..e2ec18a1a37 100644 --- a/src/karts/kart_properties.hpp +++ b/src/karts/kart_properties.hpp @@ -143,11 +143,6 @@ class KartProperties */ float m_physical_wheel_position; - /** Minimum time during which nitro is consumed when pressing - * the nitro key (to prevent using in very small bursts) - */ - int16_t m_nitro_min_consumption; - bool m_is_addon; /** Type of the kart (for the properties) */ @@ -382,12 +377,6 @@ class KartProperties // ------------------------------------------------------------------------ float getAccelerationEfficiency() const; // ------------------------------------------------------------------------ - /** Returns minimum time during which nitro is consumed when pressing nitro - * key, to prevent using nitro in very short bursts - */ - int16_t getNitroMinConsumptionTicks() const - { return m_nitro_min_consumption; } - // ------------------------------------------------------------------------ bool isAddon() const { return m_is_addon; } // Script-generated content generated by tools/create_kart_properties.py defs @@ -491,6 +480,7 @@ class KartProperties float getNitroSmallContainer() const; float getNitroBigContainer() const; float getNitroMaxSpeedIncrease() const; + float getNitroMinBurst() const; float getNitroFadeOutTime() const; float getNitroMax() const; diff --git a/src/karts/xml_characteristic.cpp b/src/karts/xml_characteristic.cpp index f2f6744e836..966892311f2 100644 --- a/src/karts/xml_characteristic.cpp +++ b/src/karts/xml_characteristic.cpp @@ -547,6 +547,8 @@ void XmlCharacteristic::load(const XMLNode *node) &m_values[NITRO_BIG_CONTAINER]); sub_node->get("max-speed-increase", &m_values[NITRO_MAX_SPEED_INCREASE]); + sub_node->get("min-burst", + &m_values[NITRO_MIN_BURST]); sub_node->get("fade-out-time", &m_values[NITRO_FADE_OUT_TIME]); sub_node->get("max", From daa82ed49dbea8ea1cbc94be7c8c791c1db1b768 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Fri, 17 Nov 2023 23:59:14 +0100 Subject: [PATCH 032/188] Tweak the basket-ball - Make the transition between normal mode and fast-ping mode seamless - Reduce the standard bounce interval, to make squashing more of a threat - Extend the fast-ping distance, but don't shorten the bounce interval as aggressively - Slightly increase the speeds of the basket-ball when far away from the leading kart. This complements the change of max-speed being indexed on heavies to make basket-balls more effective in high-level races. In 1.X the min-offset was set at 9.0 in config but the code enforced a min value of 10. - Align the squash penalty with the revised swatter - Add a debug timer that measure how long it takes a basket ball to hit its target, from first to last bounce --- data/powerup.xml | 18 +++++++++--------- src/items/rubber_ball.cpp | 29 ++++++++++++++++++++++++----- src/items/rubber_ball.hpp | 3 +++ 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/data/powerup.xml b/data/powerup.xml index baaa3e6a26c..68f72c78674 100644 --- a/data/powerup.xml +++ b/data/powerup.xml @@ -76,18 +76,18 @@ --> diff --git a/src/items/rubber_ball.cpp b/src/items/rubber_ball.cpp index f71f88d9c52..78df64f916a 100644 --- a/src/items/rubber_ball.cpp +++ b/src/items/rubber_ball.cpp @@ -60,6 +60,8 @@ float RubberBall::m_st_max_offset_distance; // Debug only, so that we can get a feel on how well balls are aiming etc. #undef PRINT_BALL_REMOVE_INFO +// Debug only +#undef PRINT_BALL_TIME_TO_TARGET RubberBall::RubberBall(AbstractKart *kart) : Flyable(kart, PowerupManager::POWERUP_RUBBERBALL, 0.0f /* mass */), @@ -123,6 +125,10 @@ void RubberBall::onFireFlyable() m_restoring_state = true; computeTarget(); +#ifdef PRINT_BALL_TIME_TO_TARGET + m_hit_timer = 0.0f; +#endif + // initialises the current graph node TrackSector::update(getXYZ()); const Vec3& normal = @@ -352,8 +358,8 @@ void RubberBall::init(const XMLNode &node, scene::IMesh *rubberball) //sanity checks - if (m_st_min_speed_offset < 10.0f) - m_st_min_speed_offset = 10.0f; + if (m_st_min_speed_offset < 1.0f) + m_st_min_speed_offset = 1.0f; if (m_st_min_speed_offset > m_st_max_speed_offset) { m_st_max_speed_offset = m_st_min_speed_offset; @@ -446,6 +452,9 @@ bool RubberBall::updateAndDelete(int ticks) TerrainInfo::update(next_xyz + getNormal()*vertical_offset, -getNormal()); m_height_timer += stk_config->ticks2Time(ticks); +#ifdef PRINT_BALL_TIME_TO_TARGET + m_hit_timer += stk_config->ticks2Time(ticks); +#endif float height = updateHeight()+m_extend.getY()*0.5f; if(UserConfigParams::logFlyable()) @@ -710,16 +719,22 @@ float RubberBall::updateHeight() if(m_fast_ping) { // Some experimental formulas - m_current_max_height = 0.5f*sqrt(m_distance_to_target); + m_current_max_height = 0.4f*sqrt(m_distance_to_target+11)-0.005f*m_distance_to_target; // If the ball just missed the target, m_distance_to_target // can be huge (close to track length) due to the order in // which a lost target is detected. Avoid this by clamping // m_current_max_height. if(m_current_max_height>m_max_height) m_current_max_height = m_max_height; - m_interval = m_current_max_height / 10.0f; + + float fast_ping_ratio = 1.0f - (m_distance_to_target / m_st_fast_ping_distance); + // In case the ball just missed the target + if (fast_ping_ratio < 0.0f) + fast_ping_ratio = 0.0f; + + m_interval = m_current_max_height / (5.0f + 2.5f * fast_ping_ratio); // Avoid too small hops and esp. a division by zero - if(m_interval<0.01f) + if(m_interval<0.1f) m_interval = 0.1f; } else @@ -857,6 +872,10 @@ bool RubberBall::hit(AbstractKart* kart, PhysicalObject* object) #ifdef PRINT_BALL_REMOVE_INFO if(kart) Log::debug("[RuberBall]", "ball %d hit kart.", m_id); +#endif +#ifdef PRINT_BALL_TIME_TO_TARGET + if(kart && kart == m_target) + Log::info("[RuberBall]", "ball %d hit kart after time %f.", m_id, m_hit_timer); #endif if(kart && kart!=m_target) { diff --git a/src/items/rubber_ball.hpp b/src/items/rubber_ball.hpp index 034c9229d74..2c0d1471bdd 100644 --- a/src/items/rubber_ball.hpp +++ b/src/items/rubber_ball.hpp @@ -38,6 +38,9 @@ class RubberBall: public Flyable, public TrackSector * a unique number for each ball. */ int m_id; + /** Only used if a debug #define is enabled at compile-time*/ + float m_hit_timer; + /** A class variable to store the default interval size. */ static float m_st_interval; From 6224d35fff261f0d4bcfa3a7f020f965abde1d4e Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sat, 18 Nov 2023 23:31:10 +0100 Subject: [PATCH 033/188] Introduce the Electro-Shield powerup - When active, gives a constant small top speed boost and a massive acceleration boost - Also acts as a shield against : bowling ball, swatter, cake, basket ball, bananas - The shield is removed when it protects against something and the speed boost is then lost. - Add functions to allow to end a max-speed increase suddenly without removing the fade-out time - Unlike the bubblegum shield, does not protect when driving on ground gums - Fix Nolok's gum shield behaving differently than other gum shields - Simplify the code setting the bubblegum shield attachment on a kart - Also move the location of some code related to NitroHack for consistency Still to be done: - Graphics (currently uses gum shield graphics as a placeholder) both for when it is used and for when it goes off - Icon (the current icon is a placeholder) - Sound effects (setting in, being active, ending) - Balancing the strength, and the apparition rates --- data/gui/icons/electro-shield-icon.png | Bin 0 -> 14421 bytes data/kart_characteristics.xml | 19 +++- data/powerup.xml | 52 +++++----- src/items/attachment.cpp | 25 ++++- src/items/attachment.hpp | 1 + src/items/attachment_manager.cpp | 3 +- src/items/item_manager.cpp | 4 +- src/items/powerup.cpp | 60 ++++++------ src/karts/abstract_characteristic.cpp | 128 ++++++++++++++++++------- src/karts/abstract_characteristic.hpp | 25 +++-- src/karts/abstract_kart.hpp | 9 ++ src/karts/kart.cpp | 67 +++++++++++-- src/karts/kart.hpp | 6 ++ src/karts/kart_properties.cpp | 49 +++++++--- src/karts/kart_properties.hpp | 11 ++- src/karts/max_speed.cpp | 7 ++ src/karts/max_speed.hpp | 8 +- src/karts/xml_characteristic.cpp | 28 ++++-- src/utils/debug.cpp | 5 + 19 files changed, 368 insertions(+), 139 deletions(-) create mode 100644 data/gui/icons/electro-shield-icon.png diff --git a/data/gui/icons/electro-shield-icon.png b/data/gui/icons/electro-shield-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..080c5c4fbc4476b78640a235a92f51ecbfd37eaa GIT binary patch literal 14421 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 literal 0 HcmV?d00001 diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index cd3a87f7569..0209ac2552a 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -268,6 +268,20 @@ band-speed-increase="7" band-fade-out-time="3" in-face-time="4.5" /> + + + + + @@ -320,11 +334,6 @@ max-speed-increase="5" min-burst="0.5" duration="0.8" fade-out-time="1.6" max="20" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/items/attachment.cpp b/src/items/attachment.cpp index 739432556bf..2c95e361561 100644 --- a/src/items/attachment.cpp +++ b/src/items/attachment.cpp @@ -275,11 +275,10 @@ void Attachment::hitBanana(ItemState *item_state) if (RaceManager::get()->isLinearRaceMode()) PlayerManager::increaseAchievement(AchievementsStatus::BANANA_1RACE, 1); } - //Bubble gum shield effect: - if(m_type == ATTACH_BUBBLEGUM_SHIELD || - m_type == ATTACH_NOLOK_BUBBLEGUM_SHIELD) + //Shield effect: protect but is used + if(m_kart->isShielded()) { - m_ticks_left = 0; + m_kart->decreaseShieldTime(); return; } @@ -559,6 +558,24 @@ void Attachment::update(int ticks) Track::getCurrentTrack()->getItemManager()->dropNewItem(Item::ITEM_BUBBLEGUM, m_kart); } break; + + case ATTACH_ELECTRO_SHIELD: + m_initial_speed = 0; + if (m_ticks_left <= 0) + { + //TODO Add a sound effect for when the electro shield stops working + /* + if (!RewindManager::get()->isRewinding()) + { + if (m_electro_malfunction_sound) m_electro_malfunction_sound->deleteSFX(); + m_electro_malfunction_sound = + SFXManager::get()->createSoundSource("electro_malfunction"); + m_electro_malfunction_sound->setPosition(m_kart->getXYZ()); + m_electro_malfunction_sound->play(); + } + */ + } + break; } // switch // Detach attachment if its time is up. diff --git a/src/items/attachment.hpp b/src/items/attachment.hpp index 376902fac3b..bbd164f41fd 100644 --- a/src/items/attachment.hpp +++ b/src/items/attachment.hpp @@ -71,6 +71,7 @@ class Attachment: public NoCopy ATTACH_SWATTER_ANIM, ATTACH_BUBBLEGUM_SHIELD, ATTACH_NOLOK_BUBBLEGUM_SHIELD, + ATTACH_ELECTRO_SHIELD, ATTACH_MAX, ATTACH_NOTHING }; diff --git a/src/items/attachment_manager.cpp b/src/items/attachment_manager.cpp index 6fc17f6a9f7..6149d152e6e 100644 --- a/src/items/attachment_manager.cpp +++ b/src/items/attachment_manager.cpp @@ -60,7 +60,8 @@ static const initAttachmentType iat[]= {Attachment::ATTACH_NOLOKS_SWATTER, "swatter_nolok.spm", "swatter-icon.png" }, {Attachment::ATTACH_SWATTER_ANIM, "swatter_anim.spm", "swatter-icon.png" }, {Attachment::ATTACH_BUBBLEGUM_SHIELD, "bubblegum_shield.spm", "shield-icon.png" }, - {Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD, "bubblegum_shield_nolok.spm", "shield-icon.png" }, + {Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD, "bubblegum_shield_nolok.spm", "shield-icon.png" }, + {Attachment::ATTACH_ELECTRO_SHIELD, "electro_shield.spm", "electro-shield-icon.png" }, {Attachment::ATTACH_MAX, "", "" }, }; diff --git a/src/items/item_manager.cpp b/src/items/item_manager.cpp index f192e33cec0..54e9816fedf 100644 --- a/src/items/item_manager.cpp +++ b/src/items/item_manager.cpp @@ -409,8 +409,8 @@ void ItemManager::checkItemHit(AbstractKart* kart) // Ignore items that have been collected or are not available atm if ((!*i) || !(*i)->isAvailable() || (*i)->isUsedUp()) continue; - // Shielded karts can simply drive over bubble gums without any effect - if ( kart->isShielded() && + // Gum-Shielded karts can simply drive over bubble gums without any effect + if ( kart->isGumShielded() && ( (*i)->getType() == ItemState::ITEM_BUBBLEGUM || (*i)->getType() == ItemState::ITEM_BUBBLEGUM_NOLOK ) ) { diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index 80e53b52c39..033c63af1ba 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -168,7 +168,6 @@ void Powerup::set(PowerupManager::PowerupType type, int n) case PowerupManager::POWERUP_ZIPPER: break ; - // TODO : add sound effects case PowerupManager::POWERUP_SUDO: m_sound_use = SFXManager::get()->createSoundSource("sudo_bad"); break ; @@ -393,6 +392,18 @@ void Powerup::use() break; } // end of PowerupManager::POWERUP_SUDO + case PowerupManager::POWERUP_ELECTRO: + { + // This takes care of the speed boost + m_kart->setElectroShield(); + + // We set the attachment + m_kart->getAttachment()->set(Attachment::ATTACH_ELECTRO_SHIELD, + stk_config->time2Ticks(kp->getElectroDuration())); + + break; + } // end of PowerupManager::POWERUP_ELECTRO + case PowerupManager::POWERUP_BUBBLEGUM: // use the bubble gum the traditional way, if the kart is looking back if (m_kart->getControls().getLookBack()) @@ -409,41 +420,30 @@ void Powerup::use() } else // if the kart is looking forward, use the bubblegum as a shield { + Attachment::AttachmentType type; + + if (m_kart->getIdent() == "nolok") + type = Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD; + else + type = Attachment::ATTACH_BUBBLEGUM_SHIELD; - if(!m_kart->isShielded()) //if the previous shield had been used up. + printf("The gum shield status is %i\n", m_kart->isGumShielded()); + if(!m_kart->isGumShielded()) //if the previous shield had been used up. { - if (m_kart->getIdent() == "nolok") - { - m_kart->getAttachment() - ->set(Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD, - stk_config-> - time2Ticks(kp->getBubblegumShieldDuration())); - } - else - { - m_kart->getAttachment() - ->set(Attachment::ATTACH_BUBBLEGUM_SHIELD, - stk_config-> - time2Ticks(kp->getBubblegumShieldDuration())); - } + m_kart->getAttachment()->set(type, + stk_config-> + time2Ticks(kp->getBubblegumShieldDuration())); } - else // using a bubble gum while still having a shield + // using a bubble gum while still having a gum shield + // In this case, half of the remaining time of the active + // shield is added. The maximum duration of a shield is + // never above twice the standard duration. + else { - if (m_kart->getIdent() == "nolok") - { - m_kart->getAttachment() - ->set(Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD, - stk_config-> - time2Ticks(kp->getBubblegumShieldDuration())); - } - else - { - m_kart->getAttachment() - ->set(Attachment::ATTACH_BUBBLEGUM_SHIELD, + m_kart->getAttachment()->set(type, stk_config-> time2Ticks(kp->getBubblegumShieldDuration() - + m_kart->getShieldTime() ) ); - } + + (m_kart->getShieldTime() / 2.0f)) ); } if (!has_played_sound) diff --git a/src/karts/abstract_characteristic.cpp b/src/karts/abstract_characteristic.cpp index 6ad04f6a73b..f076efd903c 100644 --- a/src/karts/abstract_characteristic.cpp +++ b/src/karts/abstract_characteristic.cpp @@ -177,6 +177,18 @@ AbstractCharacteristic::ValueType AbstractCharacteristic::getType( return TYPE_FLOAT; case PLUNGER_IN_FACE_TIME: return TYPE_FLOAT; + case NITROHACK_DURATION: + return TYPE_FLOAT; + case NITROHACK_FACTOR: + return TYPE_FLOAT; + case ELECTRO_DURATION: + return TYPE_FLOAT; + case ELECTRO_ENGINE_MULT: + return TYPE_FLOAT; + case ELECTRO_MAX_SPEED_INCREASE: + return TYPE_FLOAT; + case ELECTRO_FADE_OUT_TIME: + return TYPE_FLOAT; case STARTUP_TIME: return TYPE_FLOAT_VECTOR; case STARTUP_BOOST: @@ -213,10 +225,6 @@ AbstractCharacteristic::ValueType AbstractCharacteristic::getType( return TYPE_FLOAT; case NITRO_MAX: return TYPE_FLOAT; - case NITROHACK_DURATION: - return TYPE_FLOAT; - case NITROHACK_FACTOR: - return TYPE_FLOAT; case SLIPSTREAM_DURATION_FACTOR: return TYPE_FLOAT; case SLIPSTREAM_BASE_SPEED: @@ -417,6 +425,18 @@ std::string AbstractCharacteristic::getName(CharacteristicType type) return "PLUNGER_BAND_FADE_OUT_TIME"; case PLUNGER_IN_FACE_TIME: return "PLUNGER_IN_FACE_TIME"; + case NITROHACK_DURATION: + return "NITROHACK_DURATION"; + case NITROHACK_FACTOR: + return "NITROHACK_FACTOR"; + case ELECTRO_DURATION: + return "ELECTRO_DURATION"; + case ELECTRO_ENGINE_MULT: + return "ELECTRO_ENGINE_MULT"; + case ELECTRO_MAX_SPEED_INCREASE: + return "ELECTRO_MAX_SPEED_INCREASE"; + case ELECTRO_FADE_OUT_TIME: + return "ELECTRO_FADE_OUT_TIME"; case STARTUP_TIME: return "STARTUP_TIME"; case STARTUP_BOOST: @@ -453,10 +473,6 @@ std::string AbstractCharacteristic::getName(CharacteristicType type) return "NITRO_FADE_OUT_TIME"; case NITRO_MAX: return "NITRO_MAX"; - case NITROHACK_DURATION: - return "NITROHACK_DURATION"; - case NITROHACK_FACTOR: - return "NITROHACK_FACTOR"; case SLIPSTREAM_DURATION_FACTOR: return "SLIPSTREAM_DURATION_FACTOR"; case SLIPSTREAM_BASE_SPEED: @@ -1259,6 +1275,78 @@ float AbstractCharacteristic::getPlungerInFaceTime() const return result; } // getPlungerInFaceTime +// ---------------------------------------------------------------------------- +float AbstractCharacteristic::getNitrohackDuration() const +{ + float result; + bool is_set = false; + process(NITROHACK_DURATION, &result, &is_set); + if (!is_set) + Log::fatal("AbstractCharacteristic", "Can't get characteristic %s", + getName(NITROHACK_DURATION).c_str()); + return result; +} // getNitrohackDuration + +// ---------------------------------------------------------------------------- +float AbstractCharacteristic::getNitrohackFactor() const +{ + float result; + bool is_set = false; + process(NITROHACK_FACTOR, &result, &is_set); + if (!is_set) + Log::fatal("AbstractCharacteristic", "Can't get characteristic %s", + getName(NITROHACK_FACTOR).c_str()); + return result; +} // getNitrohackFactor + +// ---------------------------------------------------------------------------- +float AbstractCharacteristic::getElectroDuration() const +{ + float result; + bool is_set = false; + process(ELECTRO_DURATION, &result, &is_set); + if (!is_set) + Log::fatal("AbstractCharacteristic", "Can't get characteristic %s", + getName(ELECTRO_DURATION).c_str()); + return result; +} // getElectroDuration + +// ---------------------------------------------------------------------------- +float AbstractCharacteristic::getElectroEngineMult() const +{ + float result; + bool is_set = false; + process(ELECTRO_ENGINE_MULT, &result, &is_set); + if (!is_set) + Log::fatal("AbstractCharacteristic", "Can't get characteristic %s", + getName(ELECTRO_ENGINE_MULT).c_str()); + return result; +} // getElectroEngineMult + +// ---------------------------------------------------------------------------- +float AbstractCharacteristic::getElectroMaxSpeedIncrease() const +{ + float result; + bool is_set = false; + process(ELECTRO_MAX_SPEED_INCREASE, &result, &is_set); + if (!is_set) + Log::fatal("AbstractCharacteristic", "Can't get characteristic %s", + getName(ELECTRO_MAX_SPEED_INCREASE).c_str()); + return result; +} // getElectroMaxSpeedIncrease + +// ---------------------------------------------------------------------------- +float AbstractCharacteristic::getElectroFadeOutTime() const +{ + float result; + bool is_set = false; + process(ELECTRO_FADE_OUT_TIME, &result, &is_set); + if (!is_set) + Log::fatal("AbstractCharacteristic", "Can't get characteristic %s", + getName(ELECTRO_FADE_OUT_TIME).c_str()); + return result; +} // getElectroFadeOutTime + // ---------------------------------------------------------------------------- std::vector AbstractCharacteristic::getStartupTime() const { @@ -1475,30 +1563,6 @@ float AbstractCharacteristic::getNitroMax() const return result; } // getNitroMax -// ---------------------------------------------------------------------------- -float AbstractCharacteristic::getNitrohackDuration() const -{ - float result; - bool is_set = false; - process(NITROHACK_DURATION, &result, &is_set); - if (!is_set) - Log::fatal("AbstractCharacteristic", "Can't get characteristic %s", - getName(NITROHACK_DURATION).c_str()); - return result; -} // getNitrohackDuration - -// ---------------------------------------------------------------------------- -float AbstractCharacteristic::getNitrohackFactor() const -{ - float result; - bool is_set = false; - process(NITROHACK_FACTOR, &result, &is_set); - if (!is_set) - Log::fatal("AbstractCharacteristic", "Can't get characteristic %s", - getName(NITROHACK_FACTOR).c_str()); - return result; -} // getNitrohackFactor - // ---------------------------------------------------------------------------- float AbstractCharacteristic::getSlipstreamDurationFactor() const { diff --git a/src/karts/abstract_characteristic.hpp b/src/karts/abstract_characteristic.hpp index 2b611ec5155..d1092d52e45 100644 --- a/src/karts/abstract_characteristic.hpp +++ b/src/karts/abstract_characteristic.hpp @@ -164,6 +164,16 @@ class AbstractCharacteristic PLUNGER_BAND_FADE_OUT_TIME, PLUNGER_IN_FACE_TIME, + // Nitrohack + NITROHACK_DURATION, + NITROHACK_FACTOR, + + // Electro-shield + ELECTRO_DURATION, + ELECTRO_ENGINE_MULT, + ELECTRO_MAX_SPEED_INCREASE, + ELECTRO_FADE_OUT_TIME, + // Startup STARTUP_TIME, STARTUP_BOOST, @@ -190,10 +200,6 @@ class AbstractCharacteristic NITRO_FADE_OUT_TIME, NITRO_MAX, - // Nitrohack - NITROHACK_DURATION, - NITROHACK_FACTOR, - // Slipstream SLIPSTREAM_DURATION_FACTOR, SLIPSTREAM_BASE_SPEED, @@ -340,6 +346,14 @@ class AbstractCharacteristic float getPlungerBandFadeOutTime() const; float getPlungerInFaceTime() const; + float getNitrohackDuration() const; + float getNitrohackFactor() const; + + float getElectroDuration() const; + float getElectroEngineMult() const; + float getElectroMaxSpeedIncrease() const; + float getElectroFadeOutTime() const; + std::vector getStartupTime() const; std::vector getStartupBoost() const; @@ -362,9 +376,6 @@ class AbstractCharacteristic float getNitroFadeOutTime() const; float getNitroMax() const; - float getNitrohackDuration() const; - float getNitrohackFactor() const; - float getSlipstreamDurationFactor() const; float getSlipstreamBaseSpeed() const; float getSlipstreamLength() const; diff --git a/src/karts/abstract_kart.hpp b/src/karts/abstract_kart.hpp index b07a6e2edee..f53a9f5c057 100644 --- a/src/karts/abstract_kart.hpp +++ b/src/karts/abstract_kart.hpp @@ -297,6 +297,12 @@ class AbstractKart : public Moveable /** Makes the kart unsquashed again. */ virtual void unsetSquash() = 0; // ------------------------------------------------------------------------ + /** This activates the kart's electro-shield. */ + virtual void setElectroShield() = 0; + // ------------------------------------------------------------------------ + /** This disables the kart's electro-shield */ + virtual void unsetElectroShield() = 0; + // ------------------------------------------------------------------------ /** Returns the speed of the kart in meters/second. This is not declared * pure abstract, since this function is not needed for certain classes, * like Ghost. */ @@ -485,6 +491,9 @@ class AbstractKart : public Moveable /** Returns if the kart is protected by a shield. */ virtual bool isShielded() const = 0; // ------------------------------------------------------------------------ + /** Returns if the kart is protected by a gum shield. */ + virtual bool isGumShielded() const = 0; + // ------------------------------------------------------------------------ virtual void setShieldTime(float t) = 0; // ------------------------------------------------------------------------ virtual float getShieldTime() const = 0; diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index a2e6224ff4b..27a99c26e93 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1265,7 +1265,8 @@ bool Kart::isNearGround() const } // isNearGround // ------------------------------------------------------------------------ -/** Enables a kart shield protection for a certain amount of time. +/** Sets the duration of an active shield protection. + * This does not increase the boost time for the electro-shield. */ void Kart::setShieldTime(float t) { @@ -1285,7 +1286,8 @@ bool Kart::isShielded() const { Attachment::AttachmentType type = getAttachment()->getType(); return type == Attachment::ATTACH_BUBBLEGUM_SHIELD || - type == Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD; + type == Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD || + type == Attachment::ATTACH_ELECTRO_SHIELD; } else { @@ -1293,6 +1295,24 @@ bool Kart::isShielded() const } } // isShielded +// ------------------------------------------------------------------------ +/** + * Returns true if the kart is protected by a gum shield. + */ +bool Kart::isGumShielded() const +{ + if(getAttachment() != NULL) + { + Attachment::AttachmentType type = getAttachment()->getType(); + return type == Attachment::ATTACH_BUBBLEGUM_SHIELD || + type == Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD; + } + else + { + return false; + } +} // isGumShielded + // ------------------------------------------------------------------------ /** *Returns the remaining time the kart is protected by a shield. @@ -1307,14 +1327,15 @@ float Kart::getShieldTime() const // ------------------------------------------------------------------------ /** - * Decreases the kart's shield time. - * \param t The time subtracted from the shield timer. If t == 0.0f, the - default amout of time is subtracted. - */ + * Decreases the kart's shield time to zero. */ void Kart::decreaseShieldTime() { if (isShielded()) { + // If the shield is an electro-shield, also disable the speed boost + if(getAttachment()->getType() == Attachment::ATTACH_ELECTRO_SHIELD) + unsetElectroShield(); + getAttachment()->setTicksLeft(0); } } // decreaseShieldTime @@ -1863,6 +1884,32 @@ void Kart::showZipperFire() m_kart_gfx->setCreationRateAbsolute(KartGFX::KGFX_ZIPPER, 800.0f); } +//----------------------------------------------------------------------------- +/** This activates the kart's electro-shield. */ +void Kart::setElectroShield() +{ + float max_speed_increase = m_kart_properties->getElectroMaxSpeedIncrease(); + float duration = m_kart_properties->getElectroDuration(); + float fade_out_time = m_kart_properties->getElectroFadeOutTime(); + + // The engine multiplier is automatically enabled when + // a speed increase from MS_INCREASE_ELECTRO is detected. + + m_max_speed->increaseMaxSpeed(MaxSpeed::MS_INCREASE_ELECTRO, + max_speed_increase, + /* engine force */ 0, + stk_config->time2Ticks(duration), + stk_config->time2Ticks(fade_out_time)); +} // setElectroShield + +//----------------------------------------------------------------------------- +/** This disables the kart's electro-shield. */ +void Kart::unsetElectroShield() +{ + // Ends the speed increase without disabling fade out time + m_max_speed->endSpeedIncrease(MaxSpeed::MS_INCREASE_ELECTRO); +} // unsetElectroShield + //----------------------------------------------------------------------------- /** Squashes this kart: it will scale the kart in up direction, and causes * a slowdown while this kart is squashed. @@ -2584,7 +2631,7 @@ void Kart::playCrashSFX(const Material* m, AbstractKart *k) // In case that the sfx is longer than 0.5 seconds, only play it if // it's not already playing. - if (isShielded() || (k != NULL && k->isShielded())) + if (isGumShielded() || (k != NULL && k->isGumShielded())) { crash_sound_emitter->play(getSmoothedXYZ(), m_boing_sound); } @@ -2855,6 +2902,12 @@ void Kart::updateEnginePowerAndBrakes(int ticks) engine_power*= m_kart_properties->getNitroEngineMult(); } + // apply electro-shield engine boost if relevant + if(getSpeedIncreaseTicksLeft(MaxSpeed::MS_INCREASE_ELECTRO) > 0) + { + engine_power*= m_kart_properties->getElectroEngineMult(); + } + // apply bubblegum physics if relevant if (m_bubblegum_ticks > 0) { diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index b229d6083da..2ab92b13a77 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -339,6 +339,9 @@ class Kart : public AbstractKart void setSquashGraphics(); virtual void unsetSquash () OVERRIDE; + virtual void setElectroShield() OVERRIDE; + virtual void unsetElectroShield() OVERRIDE; + virtual void crashed (AbstractKart *k, bool update_attachments) OVERRIDE; virtual void crashed (const Material *m, const Vec3 &normal) OVERRIDE; virtual float getHoT () const OVERRIDE; @@ -478,6 +481,9 @@ class Kart : public AbstractKart /** Returns if the kart is protected by a shield. */ virtual bool isShielded() const OVERRIDE; // ---------------------------------------------------------------------------------------- + /** Returns if the kart is protected by a gum shield. */ + virtual bool isGumShielded() const OVERRIDE; + // ---------------------------------------------------------------------------------------- /** Returns the remaining time the kart is protected by a shield. */ virtual float getShieldTime() const OVERRIDE; // ---------------------------------------------------------------------------------------- diff --git a/src/karts/kart_properties.cpp b/src/karts/kart_properties.cpp index 91bc690bb1a..a626d05ab4f 100644 --- a/src/karts/kart_properties.cpp +++ b/src/karts/kart_properties.cpp @@ -1095,6 +1095,42 @@ float KartProperties::getPlungerInFaceTime() const return m_cached_characteristic->getPlungerInFaceTime(); } // getPlungerInFaceTime +// ---------------------------------------------------------------------------- +float KartProperties::getNitrohackDuration() const +{ + return m_cached_characteristic->getNitrohackDuration(); +} // getNitrohackDuration + +// ---------------------------------------------------------------------------- +float KartProperties::getNitrohackFactor() const +{ + return m_cached_characteristic->getNitrohackFactor(); +} // getNitrohackFactor + +// ---------------------------------------------------------------------------- +float KartProperties::getElectroDuration() const +{ + return m_cached_characteristic->getElectroDuration(); +} // getElectroDuration + +// ---------------------------------------------------------------------------- +float KartProperties::getElectroEngineMult() const +{ + return m_cached_characteristic->getElectroEngineMult(); +} // getElectroEngineMult + +// ---------------------------------------------------------------------------- +float KartProperties::getElectroMaxSpeedIncrease() const +{ + return m_cached_characteristic->getElectroMaxSpeedIncrease(); +} // getElectroMaxSpeedIncrease + +// ---------------------------------------------------------------------------- +float KartProperties::getElectroFadeOutTime() const +{ + return m_cached_characteristic->getElectroFadeOutTime(); +} // getElectroFadeOutTime + // ---------------------------------------------------------------------------- std::vector KartProperties::getStartupTime() const { @@ -1203,19 +1239,6 @@ float KartProperties::getNitroMax() const return m_cached_characteristic->getNitroMax(); } // getNitroMax -// ---------------------------------------------------------------------------- -float KartProperties::getNitrohackDuration() const -{ - return m_cached_characteristic->getNitrohackDuration(); -} // getNitrohackDuration - - -// ---------------------------------------------------------------------------- -float KartProperties::getNitrohackFactor() const -{ - return m_cached_characteristic->getNitrohackFactor(); -} // getNitrohackFactor - // ---------------------------------------------------------------------------- float KartProperties::getSlipstreamDurationFactor() const { diff --git a/src/karts/kart_properties.hpp b/src/karts/kart_properties.hpp index e2ec18a1a37..51a26bab7a0 100644 --- a/src/karts/kart_properties.hpp +++ b/src/karts/kart_properties.hpp @@ -462,6 +462,14 @@ class KartProperties float getPlungerBandFadeOutTime() const; float getPlungerInFaceTime() const; + float getNitrohackDuration() const; + float getNitrohackFactor() const; + + float getElectroDuration() const; + float getElectroEngineMult() const; + float getElectroMaxSpeedIncrease() const; + float getElectroFadeOutTime() const; + std::vector getStartupTime() const; std::vector getStartupBoost() const; @@ -484,9 +492,6 @@ class KartProperties float getNitroFadeOutTime() const; float getNitroMax() const; - float getNitrohackDuration() const; - float getNitrohackFactor() const; - float getSlipstreamDurationFactor() const; float getSlipstreamBaseSpeed() const; float getSlipstreamLength() const; diff --git a/src/karts/max_speed.cpp b/src/karts/max_speed.cpp index aee04b37b42..35f19e6b97b 100644 --- a/src/karts/max_speed.cpp +++ b/src/karts/max_speed.cpp @@ -391,6 +391,13 @@ int MaxSpeed::isSpeedDecreaseActive(unsigned int category) return m_speed_decrease[category].isActive(); } // isSpeedDecreaseActive +// ---------------------------------------------------------------------------- +/** Gracefully ends a max-speed increase, keeping the fade-out time active */ +void MaxSpeed::endSpeedIncrease(unsigned int category) +{ + m_speed_increase[category].endSpeedIncrease(); +} // endSpeedIncrease + // ---------------------------------------------------------------------------- /** Updates all speed increase and decrease objects, and determines the * current maximum speed. Note that the function can be called with diff --git a/src/karts/max_speed.hpp b/src/karts/max_speed.hpp index 1f0422d8504..29d47e58994 100644 --- a/src/karts/max_speed.hpp +++ b/src/karts/max_speed.hpp @@ -33,7 +33,7 @@ friend class KartRewinder; public: /** The categories to use for increasing the speed of a kart: * Increase due to zipper, slipstream, nitro, rubber band, - * skidding usage. */ + * skidding usage, or electro-shield. */ enum {MS_INCREASE_MIN, MS_INCREASE_ZIPPER = MS_INCREASE_MIN, MS_INCREASE_SLIPSTREAM, @@ -42,6 +42,7 @@ friend class KartRewinder; MS_INCREASE_SKIDDING, MS_INCREASE_RED_SKIDDING, MS_INCREASE_PURPLE_SKIDDING, + MS_INCREASE_ELECTRO, MS_INCREASE_MAX}; /** The categories to use for decreasing the speed of a kart: @@ -120,6 +121,10 @@ friend class KartRewinder; return m_duration > 0 ? (float)m_engine_force / 10.0f : 0; } // getEngineForce // -------------------------------------------------------------------- + /** This allows to gracefully end a speed increase, keeping + * the fade-out time active */ + void endSpeedIncrease() { m_duration = 0; } + // -------------------------------------------------------------------- /** Returns if this speed increase is active atm. */ bool isActive() const { return m_duration > -m_fade_out_time; } }; // SpeedIncrease @@ -198,6 +203,7 @@ friend class KartRewinder; int getSpeedIncreaseTicksLeft(unsigned int category); int isSpeedIncreaseActive(unsigned int category); int isSpeedDecreaseActive(unsigned int category); + void endSpeedIncrease(unsigned int category); void update(int ticks); void reset(); void saveState(BareNetworkString *buffer) const; diff --git a/src/karts/xml_characteristic.cpp b/src/karts/xml_characteristic.cpp index 966892311f2..93c8ecb4bf5 100644 --- a/src/karts/xml_characteristic.cpp +++ b/src/karts/xml_characteristic.cpp @@ -503,6 +503,26 @@ void XmlCharacteristic::load(const XMLNode *node) &m_values[PLUNGER_IN_FACE_TIME]); } + if (const XMLNode *sub_node = node->getNode("nitro-hack")) + { + sub_node->get("duration", + &m_values[NITROHACK_DURATION]); + sub_node->get("factor", + &m_values[NITROHACK_FACTOR]); + } + + if (const XMLNode *sub_node = node->getNode("electro")) + { + sub_node->get("duration", + &m_values[ELECTRO_DURATION]); + sub_node->get("engine-mult", + &m_values[ELECTRO_ENGINE_MULT]); + sub_node->get("max-speed-increase", + &m_values[ELECTRO_MAX_SPEED_INCREASE]); + sub_node->get("fade-out-time", + &m_values[ELECTRO_FADE_OUT_TIME]); + } + if (const XMLNode *sub_node = node->getNode("startup")) { sub_node->get("time", @@ -555,14 +575,6 @@ void XmlCharacteristic::load(const XMLNode *node) &m_values[NITRO_MAX]); } - if (const XMLNode *sub_node = node->getNode("nitro-hack")) - { - sub_node->get("duration", - &m_values[NITROHACK_DURATION]); - sub_node->get("factor", - &m_values[NITROHACK_FACTOR]); - } - if (const XMLNode *sub_node = node->getNode("slipstream")) { sub_node->get("duration-factor", diff --git a/src/utils/debug.cpp b/src/utils/debug.cpp index 0ce56de0036..f94e34d1cf3 100644 --- a/src/utils/debug.cpp +++ b/src/utils/debug.cpp @@ -124,6 +124,7 @@ enum DebugMenuCommand DEBUG_POWERUP_SWITCH, DEBUG_POWERUP_ZIPPER, DEBUG_POWERUP_SUDO, + DEBUG_POWERUP_ELECTRO, DEBUG_POWERUP_NITRO, DEBUG_POWERUP_NOTHING, DEBUG_NITRO_CLEAR, @@ -575,6 +576,9 @@ bool handleContextMenuAction(s32 cmd_id) case DEBUG_POWERUP_SUDO: addPowerup(PowerupManager::POWERUP_SUDO, 255); break; + case DEBUG_POWERUP_ELECTRO: + addPowerup(PowerupManager::POWERUP_ELECTRO, 255); + break; case DEBUG_POWERUP_NITRO: { setNitro(100.0f); @@ -1260,6 +1264,7 @@ bool onEvent(const SEvent &event) sub->addItem(L"Switch (F9)", DEBUG_POWERUP_SWITCH ); sub->addItem(L"Zipper (F10)", DEBUG_POWERUP_ZIPPER ); sub->addItem(L"Nitro-hack", DEBUG_POWERUP_SUDO ); + sub->addItem(L"Electro-Shield", DEBUG_POWERUP_ELECTRO ); sub->addItem(L"Nitro (Insert)", DEBUG_POWERUP_NITRO ); mnu->addItem(L"Attachments >",-1,true, true); From 84c6bdf2a479716cdad837e79822377e91ad93af Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 19 Nov 2023 12:01:03 +0100 Subject: [PATCH 034/188] Fix the electro-shield popping when using a weapon The restrict-shield setting meant for bubblegums was applied for it. The AI remains clueless. --- data/stk_config.xml | 2 +- src/items/powerup.cpp | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/data/stk_config.xml b/data/stk_config.xml index 3a284fb9ee8..12766e2eaa8 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -242,7 +242,7 @@ + restrict-weapons: If true, using weapons will destroy the user's gum shield --> - - - + - - - + - - + - - - + - - + + - - - - - + + + + - - - - - + + + + - - - - - + + + + From 953a35139c804f63f643c99251cd61e307eb3a8c Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 19 Nov 2023 21:49:12 +0100 Subject: [PATCH 039/188] Revert "Bump the server version to avoid clients connecting to an incompatible server" This reverts commit 3552f13ec5fd20907d4de89d740e44265baf2872. --- data/stk_config.xml | 6 +++--- src/network/server_config.hpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/data/stk_config.xml b/data/stk_config.xml index d0fe5067915..12766e2eaa8 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -5,13 +5,13 @@ Older version will be ignored. --> - - - + - + limited-items-timeout : if world time is less than this value, then cakes, + basketballs, parachutes and nitro-hacks are not given. + --> + - + + diff --git a/data/stk_config.xml b/data/stk_config.xml index adcbd831c77..77f9237ded8 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -233,8 +233,11 @@ is different, add more if it is the same. limited-items-timeout : if world time is less than this value, then cakes, basketballs, parachutes and nitro-hacks are not given. + full-random : if true, powerups are (for all practical purposes) fully random, + if false, variance is reduced while the rate at which any given powerup + is collected stays the same. --> - + - - + + increasing times. + The startup boost uses the same boost category as zippers. --> @@ -363,7 +365,7 @@ - + @@ -372,7 +374,7 @@ - + From e78f76abb1cfab29b28b990b7620804f0cc8890c Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Wed, 22 Nov 2023 19:33:56 +0100 Subject: [PATCH 046/188] Changes to braking - Allow braking while accelerating - Braking no longer relies on applying negative engine force - Display negative speed on the speedometer - Various tweaks and code restructuration --- data/kart_characteristics.xml | 19 ++- src/karts/abstract_characteristic.cpp | 14 +- src/karts/abstract_characteristic.hpp | 4 +- src/karts/controller/player_controller.cpp | 6 +- src/karts/kart.cpp | 168 +++++++++++---------- src/karts/kart.hpp | 1 + src/karts/kart_properties.cpp | 6 +- src/karts/kart_properties.hpp | 2 +- src/karts/xml_characteristic.cpp | 4 +- src/physics/btKart.cpp | 33 ++-- src/states_screens/race_gui.cpp | 4 +- 11 files changed, 131 insertions(+), 130 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index fe83798bc4c..b1f49f44bc6 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -89,14 +89,13 @@ is the max speed independently of kart type and of handicap, which is used by basket balls. brake-factor: Value used when braking. - brake-time-increase: The brake force is multiplied by - (1 + brake_time) * brake_time_increase - i.e. the longer the - brake was pressed, the harder the kart will brake. + time-full-brake: The brake force will be applied progressively until + time-full-brake. max-speed-reverse-ratio is the percentage of max speed for reverse gear. --> - + g=`X{upSS|+`B zjJuCD1FaK>NTDt94$4h-xrLrzVvMg9X|U9iu`kUJa%YoG`{3YTf-L=)l3&cWD;w}C z_U+!^a^j|tH1jZp0COpr^^|oJ##jjbS(N|WBWJam@nGO1 z?758nh5i5L@LWoy6zKdNcY=xLAY7nL#t#=T+ThHq(DNf|kred^_p9rrxEtexJQ5RL zm4z#t> zIm+UQ|G)|5JVZamB1tOO?kk~ zXx7(+9i4c^7^Z_MNnZ{bq5@Sb(lNGzl@3lt7`yYf;?C&3q%2OT^ zZI3441;&IE{>>t@RcuXNHB=T?r)n$8Ikd^nJ1%WYABpJJxEAOtZUSD%WVZ0r`A`OU z&+L(|GNV%i)j}V+wx@ZsH7=RNc%B}vPgo0Ud7LC_z{1;cykq<|8G~ud{nZeBvsoB~6C_YE`Qp_wGg24g z{SGIU`eA$BV&wX*#9-`;K&nE)9z+qK_FPLBdxUC@>4`Qi}d8toTt9l}@WTS165j|3EE-*qFih zM%D!a7(kV2oin))^On9P7AjT}i&YfcotUtkjDol!-s9I{7PX@6e1mG%I>m!!r$!6! z%$k{^0uB|W22zI3nOF7R=!_lUo2OG#IJr+WyjOoxo%Bqbr!8;i9y*9cv)2D@T`5I0Rjg@^OY&2^B1fB_A{3nhdz>qN(Lb=v7GbK=bBIN}Jb zTeUor@_eTg&9j~JL?Su}Ms`0Jlh7E78kO;WXcB)5A+TNtn5 z-CxsxeF&00KoIs(JYu-MP8hm1RDAy}57PrdL(d73dj#BNJ<-446QS|~4M-4^0MRmR zO4M(k{NRr*dNb>z)nixG5B^MXxJccnxTkAUB;_GdZCYyX99mIqqB;k$2_1D<(?I7; zR`Z@71r~~OUXb&b+ZvIDEd_VNNk#unSB@?oMMyu5<;=juhg{BaMc}LF4&IK7`Am$V zm|$bxMg!lyFiPU=LN8ehE!xog4O}-T5PE8Q=N$PL!k{D*uvNSvlRabL2f_ua+B%q- z5_~^`XpFybIl!imKY80N3&l};lqm?#5*OKN*G&e-W^1>Rw9_070z?`R8fk>IeMy3@Q zUygnd4oURSw0mTd&gL_j7k^1YuGWgf_m!@81OJS?uKzv!yNjbaepGsKWidabq3hJT z)7$UrmZ>Lv3SEX1%7Th8D!vLi*x&fKP6V}qiy}o`Ty!RJBXv(zAEm=Vy8R_6)i|); z{%xEkc>l-tM?HxNhe3pGba_ocIvjzg^1CNRiKZzCWx(iT`QeUknUl1~)Lu2e?8MTb z2rvpLaZBj-5ju@}L?=jmzVm#wWD??gZyrNdv5OIn#&99ySH;4zbSMA|4uT0TS(sys z*nqnm$I#+i?}YiY3}#>-E1F;i&NBL@rR+g0OyCe^u6`Dl(YSzsKgm(M8Te! zBWoQXOxU(J!0+Z0V`o)p1&QFQR{J=o25?8jd4yc955|RdIa9%JC&U}>F5Z*NN_$qq z5uA_LvY>E=PCDBAwPdh46u7kF;@>UmrnNFJM) zNE_Vh4Qd{T<^Ccnoc#p5cz9#tF?jDo#7YG$dVWl@Rq1%+$>d9JGkd%?tFHUzENu^1 zcp_@g7RGD$Ap&5%NG9h`%(>Ozvm}z@QbbOPn5*}YWwjpqGhqms>$$S4z%>F;QqN=> zV|QFC*15O-pKaMVQ9rR4>BYR)!gc5)zb_6v6bGs&k!wFnEVmMSAnAlJ0b(In7ThHp zp!-Xgtj<70CuwSvY3neI@8W_tx{pB{oBpX(=yL}H-tlL$cR7Dw{N-vRkcM~^R2tXH z>LJ+kKVN`JBh~~1(LN46KU&jHHPcRQr>C?W*TA+M=H1+d8*rZ1-n(T7f_=omeY2Zl zSkEvD(bpIrm;!T~^guy(5c$imb6;7Ehd%VQ;VdrVoVGSa{$t?H9L#N=Q2XYQ;GQSL3Uk!+joJOqF}f(|qL9s04>$E! zyfAcYg2Rl*@0QKDR{laO9uy0T0F{AsrO#y*fNiSF^x>1aM=!KQg}6uWPtq-?#Uof? zE6n!@&bLH*?%3TfQUd;`JucmC6PlPCz3WQ}q^|m0r|9SPDX0Et%yJ&bHhLKQ(;m{dea*wKY&Zi>6eQ z!*a9HVJTK?kBvL3?wvRj5+(g^O6s{FKCwTF1$;aBbfXX*hDA{aNc=0h6`bejan0U6 z%*TdZmdQQkZap=?`T+5V0I7P`Q*wimg;1kN0C`3y@6HyyAH~Q-szh?l2yt9tdq63e zKJ-M#2*UI=CY(Fa2&zyVw~oBwaA+f5W-$%jt$DU*rmEtFe8d3EzJhCszqONJt)LsM zR;H4{TmCEQo@NtHchAFFduKjVi$l}v4oV2dS8o@SXJg}Q;)k%O?CpDgWf-WaUBW1f zYovoS5ldjEgv`FU{xMGd#c^{=z$I>)ghwp7ocu#^h)yodsFZMK4xe_EXQTU_T$QLE zhooiv3o6#D4$)yHQtrdgE~Ob%UtSC{;15qL-!7O$a&qA03OE?Cu~C?6;Er2#1d8@! zZn4qQ?sUpvKeFDF<=+20_kl);U@D)tyuE*X)Y15{`sJozJq|IWV;rr>CTWx>RjDX5 zFyWGEk4Pt2C(@b5K1sKs2AyX|X97}uJMDj`ZyN1tsvwP2?cAFtgYM~~F|}AJ*3WHa zW?&9)MGe@8tq3u!k?0+8rycgsT_}G?W2Bc1q|Y=C0QavLDVXccTO6!=k3ix80^n9h zIa6gwSEo!^PktltbE56|Kv{7Z83bI<`=C!_4mxpHmfKkd@OyVqZf|1!qaQ1<-r;Al z47%U*q?t}mQQdxM_8rq9pHk^YQQ%mN2WF8hWmR&VVoF8Q!Ha)72Sc zb#Bvlpj&@$M;9}R7jRQ@jc7%SSOWMiy|)&Vjs{2iMcPXwqu4l3c{3zOk&Sek6I)z& zJ57FS#lH%fynbgZ` zmYR0c1>ZDP3xpS;%>q>EP7Y2%lqS#0D1iFHeh+^MO6taD;Oj(~s`(;C;k9g#ThkNX z@xS6T-_ji5PmM;(VGVYxa>4gvPXbk&WrKA#HqEgbU;aXW-}|iQTI}ys8B`=id#4&7 z4Cb3Qeythp)(!jU2*KC6bDE=8kwz+(DdiB9pbdn(u64Xy!EwA0L><5~k2Z!6my*A} z`%cj~Af7tZ?R=_Czf&&A-o&|<4KKpw4s0>uHCCgGq^&O{Q!6#Ju`MMCHB@?2qu^CN z>-$iU9?|G6x}ZEDO=fs-B`|PCaBsaV1k=^|Z=&$EY z4?4#kuETYm5qMX0J=NZf#8O*3Z}7p_hTycznfVN!`K?{M`HpI+wW9JlZ_w3)36{ZI z|5%5yPBOF_c!>>-2Q0CovDp&f6`#}tHvPs7G3H6$N}11glHJ$oT>@LSphojEgdEs* z8LB_vReui$NUzXDZ9?XL630iiKMX*HMGiw*s5`t)nZaxh+Jwn#;Eo0H%M z%U-b3>cOk{&Ln!p^?BF{86*csm>IDh!gKCEZmFnfO})#RR;y|iZI>2SRfyh^%~g*YOE*_!w*ybY6kcI zNzrEOf3q08xi1^Vy23+3W~jShdnQiMi?hrKzVP0KnWf_;IZZ8{Ndhz~Q*83LLI=Jd zZ!*}=l_7rl(H7sX4tLZbYn$QNpK`(-U`nOFYyJr>f~o0#uw@Q{eZ|cDzO^rKcPdKzcd7sG(43(zp`p#XAcQ`XMd>Hk zv#>wrl_jljX%6!4uKnoz+FC`TomS=TBx7N4lz? zUckH)B5KY!VejUU*-Wl|VCLm*kX`fBVoS*&ux1K2Nlu4m_ieDEL*5YJvKul3Yg4cjG7>Rcc zbby{4d$JmRv{(g)HvaL@i3G*!#G;=&JWYhjd_4w~=|N49<#04f5y7ApOnV&!pM8VS!PK5)vkrA=aS5O8N9;Hs+}z6|cgRg-qsR z=hHI&B{GHy!ZOgaVMt6%<}BN_T3>p&ji=U7 zeIvkTL2?+yr}xep&)_w-TK12NeQ7ryGDwiPUfq3qbpSy$KI}}1{DUAUyZY}&$ccOg zo-uv+bcS3aL&mV63_jhzww-7xgfsidSd(j_4v9nwmNMlt@e0%sc0t z`+na2Vb9n3?Y-Ap*Y&R}00>sT_mQoW%m?MsbnZb2sUlSUD6zXw+44--v77;?>fSwP zBRq%{1Yffi?;o@E_}w5Bn(mx?WP+OI**hP$ChVk&DZdQh2R z9?D)O$E5eg;98}KBWtT<+HBMw-NARuDA+lg^&n;erSaTe!mfoVl1*5}9d$*3s>ng+ z2t#IqVqV!fQIbk{m%>E8X?I&g;ZD}(>P+ov783(G#27-Sr-Y2|c)NpaC~FMptBJI! zrziP`qIq!1JgWFB4x`8|wOfNye7OB5?@|e3o5)GlDp;MK=N9i;P?H5i(?Al{*;~7sT%q9l%W!Tz#a%wh9Fre;Epow=`j! zj?k>3i*o;}GCxZW>yX}N3TptY;n4NBOEZc2+dTas%tn-KgVe@W6vK>KIwvm4!9eYBpOiqqu`xm*Y*!dnAGxS@$jWg`E}NPb&BVvnG-qwrwgK1*OyVnGS&sCs57} z&5JAbi|R*xafyrS&sm9W0xx8Y>k>+Zf{vOkdY&tsxKAmK7P#CcozoEzwVqPzLxQ;6 z92FnM<^oUFj4qt;3;n2-6O%w%yy1gqBeIx{-*M7(CKEa7j}Wd}R|6?gTk7~EFP1Ro z#x6;k+IndhhVi>^P4rMN@Pm^3JFBnu(JMp$xwgZmu$d`f?>16*FT(dQM?mhk9;na! zJB6V-B*i-fUAg~l7k*b@c`rw;r!6B07WLHcerpGNHLr`IEd{Wqk+N{vm%$CLKmOHuF;%m z-fD4$pj*L2o_gKE%BieyxLv9LS-tjye(cK|(C)O?E|}#lv*B3w3!Qt%+~T_rc-h*I z`4}i`3+8?VzC4vlKyjAkN~$W9w5}8c*cZDMxk~ZA_riB88bN>B4m}J@AL)KSS^ih6 zUtJ(+o6}Nz5i%v&J(}@brm<^Q6t#+DXB%L zPsX!$=&}G$+K=B#FNN$d4>tas=rSB;vAPhI;HCjR^SZZ%O;wVHia@>`|MLp&PDj#b z0eWpSGN4s<*k@ozY}LpFv;)ja>q#IuC2pnmqV#bK0la#LA%efVhO2p_J5=+V*xM%* z){qkh9=@g3Y?B$5ZHGPz#`_V1u|CPuKd{o#xWvIy%;Ub!>?^k3zJi_{EB4(}ah#j? zdv*TojbbDewT*qW=)JdwT|ihRKv6^E4OqGEi%7m42cUcBn&eJYmois%yyRcSw;ns_Qz{hZLoQu+k8LEGilP`0kVyG|*f!^n@~r z`;io#??sAY`_%_Y6>s|41tK)YZw9u{A`%fM%moEd4uowdln1A;?WoSb3xf*3(9wWI zlqAe{#kFpQbCQX?t5rVrBz07|O)^&dz&s0CQ$CQeP-*zed9VJ1SdJoGXjM)AMUCGN z{?r2<8?(EH9YK>_GbppdoV6Iyl}o75pI}+;7axc`@=Ey-C(ouz3{}WPPCD?X9~Em- zeJodk@*93yDRRmST+pyv-$czNVa7v#q&9?!1{Bz8NH{^w$=fF6o31Uo`f17KS500j z*YYrBO%>+e$?L^!Kxe6kCv#MhBxuR*g1NRw87%+QuCK=~_jAzL<`W1Ec?gRQ7g<(i zBE`kD=1w5*f$a#Hlaa6A=a3gcWv0&NjBKlYyk7M%2^xry{$?uWKnon0VH_Iy0^=Am zuO6Npq0xPJ&?}>7Y--3zrnlRXa?u#ebQ0+#Xcx&8(^dNKQ%EZL^{s=*Y8bNms&Ev$ z&)$q(0amGyw*`54Qvt-dLxxmaRa%dqbM;N~lNxp}}Ilej; zxgj&Ty|-~LDoF4;RqdTPH?#}mcDd<7G;h=8g}R*Vw$aRObNNk!r@Yz3N1GHotd_{< z<8n=zzG1@tmQ~f#ZjH#jqHf>%&y9-1*`=!d6pcnuF{)_eIHgD{^MGc*qi@F$<7C$| zCy;p$$VetEa<94hx^Ygc`ST$8MXi1=*L2D5r}b52E*q| zdL-hZMk5*OL~hZz7zi?&IKAZp^F3|!A8~s}@KX7mWT}%~SMDy`>cW}9!E4jpD@C=m z%qsL`A9JcbC`;n5+3uwH4n9KHWgYF5on=mZ(stW}7>5Jpn27Fos zdDC{CU>WE46dfg4G%m2N$)-|SfQVD|hqV!B4+~Ms)%&?;o%Pftkd;n?>kAefwowF| zEdb}-hOR4}M%3otJb>cmpuc2OzwgQqQ6@ef5bXB9GMR9`h_jeg|9WqVgJ_-{>G@y< z!Mx_j?fQtcc384=#H^k}N?R{1+-5EQ-lW@0Sdkblj8Y^4BggRmgHYc(_bSHT1_cwM1k+)&_#)ihu8z=GDX}ktZ(+P%o1-$0 ztliWQ%5}8A0Mwpy+EwJK`vLy>k=)yDY#bA+*F>J4-+abzw{62k?Qy73eUyFv$2`gy z$Qq$flEha=U@SNvswQ6V`f1Pnx4F~{HA4iEXBmDGx8B3(C&N@e`Zl@F(TvT+4xIKz zKh6RA;fo9`@qt%;?k^X@0mB12O$|JO!lfdcl+oULDo%(~lzq!-*5^3rP06T5;35&1 zweP$fxAJvqaXi-raTL7v7k%dH*wa_4Ljv24QAGQiFJ)qQ1Y(I)G;T|hrr-&R$&5hu8E{A?c3B@LyojvnDZFqVq3Ko1Z#ix8?u zZ+NzMwa(e;ddis*N@e*A=T)>l3X@9P&FP3E)g7s*t-Tnq>cF%lN;`S@D~+v^tL^#x z0B2LLgJvaPdPE_%%Yurj-`vu1+_TA``~Zf8PD@dXg{QyDKI54uroQQSCR;nhNw~zUtSO1+?kHDSt%I? z$*?v866-;8CDv$|yo=1A{q+FnWX0?91Y(AhtUcVp{{&=~yk@JU) zr%{W2Qldb{sU{chvvkwnZqXYfeabO5;tqr?p#t+h4YGZNT%g`25{j+y77YH zqBQRA{dXd^KS#SrY!z*Oo%E}-ef{zeOfT$zvy=t>T?}IQ0wY2W-Li3Tq$%?ELy&@a ze}wL5f%#q?Cy*69Q7=_0eBlIvrXaqq{*h?62#;*3jlw$0T^8@3OGQ`M0q64#5KMB% zNdyjvpNXtGdi2``OE|-E9*M_sNe)!O{p)wU-t$tk3vP5%eef6MCQcGet!mS!&B{sh zRU?;RRGOk290a0%$u-w&`6o`>vA?YS{T3O;yC z(-wzSiwlLc%Mp&$P+ZdR4reC*H`yf)7UFStw#oTmQQ;aw{_vD49|9H)7;CQ3Wb8{( zh2k$*ndkjHPwlM5hiIJ%`p&0+5_U9bf5DPr{s$*2F#M-$F9uC$KyVYgsKlEnslRhn zzhi$I;Q?!IAkv%mwzYiO_ovRXiIFWDTn4%kU`~|E&Prlln{&Af;IjEKAA*i^j3Tq$ zF^#<5$BKI4TREr+0>l@Hyf6{4bsb5VgbU*%>L!ilLXMpKxig}tzj_39OExKRW&#}$%G{3umHAq(Y8{X&Do+3qvfWZH+B?=4@nz0a0R z+PCYOviOn)q#CIq%JW!=MJyLS>Rl!6W~Lh4_WoJvMfA)h_RTjT?P%m|O10yyFB%aM zVj@+Ni&WvYHXK=Ff5rJBcORr`xKF!|Xm5p&AMJ!Qg3dltKJJ)F*X7qTTy4;Gl$-n$ zd(P@hSy~FrPks`!<0*C7m)h^b&dFV#5rKI^oR4lk@PI~R zz)G*My9hN*d#oL}nYb69&%Tyw+N}djr+5Cv7|&C~6gBJJ!3T1N$Jh44b9thAYX@ax z`0-SbI;xTn^>y(c)=-#g6iuyX4mu$_I4p|v=Wckujx4vT%+0&BQ0DE(mk`xXc4wXn z0f|P}p8mF9_P*-F(~cJA#Ek0w9VC;<=YXsm9^;i0x|*RswY7)JRu2as&?|Cgz@0?PE^|rZ_Kgo}Sg!{Rp0q~q);x+xSEq*3 z!8MiDYe_rIdYezb<6}i+A8~&(jJ0KpwyKDCOEbSN7?wpNZ9g zki7u)-8^=?1Y3uD?|!d#4<69ZH(J-iksmz;vmsS#OvUL;9dz<<99Ol;M>aI zf9p-&Qp_^d_2;Dffb%uno(3%j3$?RCD=?-d$?fgDe0mR+sPodfQHYO3`{;Rux`)!+ zHB8}S^h$IDm@bSi1v4gkAk1tNvW$yax!wokG!Hz;3Ya@G`N_oeuj{FiqcA+72iml? z)ENDEpq?)1Qo#!9RmKQ=gHWa6B%r_jxZV0^l<4z)us4@eLOi}*KtWtSpaSz3a zE441%kjs1`mNICX917C5&cpDgyC6W$#d;#d>Mu>2SIek0+;)Hz~UJwzNSXzM^R z%^b6Xght}buW~uU2YSn+Xjknzx|u5Q_lt8d!XN_7|3nFe$G-v>Juk*n`?+e%(+oYQG|EIF2OLY3Eg+B-|Hh&qt%tu5 z>MG?xMs1|y)+IIuDgJ(&lyE?rP8aHM`_OfcyKAoFt@KeKXF{uN1Y~1J$n_V`M z(6N)m@2x;F&a#9HP=V*af0R72VwbFs97|`ox&sr#zAIKNoSx{!yV>?!^RpT|f+-R2 z^|@*Qa5;qMTeH`nHb_PK0a;Q)L;^C9@)0MPdllvMq9tW;i$;(UvhW5krI`7{d)sC2 z=Yk(NetQ~>Cd205$V*W=*nd*t#yoni7lVD1YWO~mC~1J6n>%ObxhRvFYc@SzWE?aJ z-13%rN4kSbY0qWh>&}-qZoekW`HTK?J$);(m;U&RNE8?Q$_EhHkl1|Y)we9HEMGvE zmY|tk4T}-WQ^GxqZ?fKQ6Wm9a1<4YI>(q%v0yJWOy))PQU(tN5%iCBV6zs*cbukU$ zvlRv0?3cRz@9VO$sfus5wD~wMY`W&h#Ky=y@2zSUYvNEAd6FkM`);{W z6PK6bx(c;iMb(LsRYo5X@ipNmR!rr*jhha)^}v`9l@V*EoOm|Xg}>Le1PRJB2`gJa z{w#YWxvJVVk5M41DYTZ#B=z@=hF-s`dMuXvOiRh@dN$}q)%68?TICGU&}{qPpL%1E zs?J3r@4V*ipfy;9C`57q+j7l zIwxC)`kZ2!RU{8Oyy?nJh5RK`q*d2}4c;}f7I;Cshm)C|ta95B`~>V@R^2>`6OQ=y zZwFF%d`X~lP@}&vWhbuY%>q|O~SjH@RTC&RnV_97ubC|ddh1+l9N(IS+P%ykqbO6 zLazG@x^HFzx~nNORgB*feSXYsQZOotY{w72iz6xTcsy>q-6hgqOvCIpJ~U5rYfhgM ztL`S1YXNG%9b=QbV6n6xEov7sD+8)e*m8&x+FD##a8-1qTatZbvHWER?x)aK0^%1M zkfYTFlTyW+eqP8p%2Yo64Oh`>cYa!-Z8mnO{Ql=_pOG|-Vpb>e-tBaGzrr%!PxQ#2 zqUo2s$M54p5c+Qvomb)CR;al~JtkC&OVq~;*`x6+edBNB3RdNOPO+fNJw>^Hc-z13 z+cP+ih@LTsRqUrEGN_mx{fK8Lx-Mav!rMeuf&|CTaVdGgq@kBOpZh)0)u$2Vlw%{v z$ex77i<_UZL@5}WD8)|m6ilkA?Pcla6a+W=l2-Z>sI{wj!r5=^4D(G+1=>h@Nhxka zX78odTE(Xlt0~39m>=L3M8kk2>8{_DA`FC^hg2e)Ue;I;p^<_2jA91fAp={XjmXDz|4(g5SH- zVFzD7>7gmziRlev%kK}HNo5*NrhR4pP`V$GaO91Qxbh&E$<59BTh`e{2Bj} z{rO;;OFMTu^{XI!l0PYP2L+7e8r>zm3mu9C^XdIJp)XN|_+p9e3v z4rMQcT{GfL7Fmees7$Bv*!4tV#S@F?^-GERTiW|Mx`}?j(IT^)9}X@W~Fd`=nBJ>G|2muEQ?Tj{m95_Blir#e@Gj z8-^+jv0jkt()%gQaCnpmUM*=inFbZ0E`TJIVMO+!Vh5U&KkPKUNn*0AM%O$AuwgXs z$zH)aHx8FSyxzt6SHIwb_s}>wh8}nsd>|Q3gubova8fit1uQ|+p#%*hMJry$Rz5#M zgT`^WJUuIC6f_`3eD7Nk)&vJ)$Q)oXUiU!_k)0>9^$_{}Dc+Q13TX{8;V%(fOUWeu zulNMpjNWqPCT;8I?KEf$^uwclB34Mx*RLCRAN&YCeTTwsu1mZ;HjqZQIBM0Jyjk;d zWaj7t^->L7D}cGD`=={#5`wzV7?RxM&Qhtm_kx@Fc zrdARoms@ktTc3j|gmr)aFOh@0Rj!yvW80C}4-mt7hZM1vb~{ntc?@QNmD+(>zPb58 zD}l9KRhkscZq=fh4~FH&7Dp1NfB`p+NJ@i}g8f;D zw@d~ge2)W-ui}JmJa4cTxs8;qLDnH4zl8>n_aMVLgMtXwtrU_nThjHgzmpu+IS4+h zZz0pxwxkcNo+tF&XGaKdMX6VAvHyCPDNj#keMnI&syh1hlypuFzf@C3i=GdTc&K`k zB|Rm6zPze5h~=gb2rkH0`Rg9z7w1xkiz4VGlMzOyTDLHaV)3AiXTrCCI zdYR|w8`fOwUn2TiEIw;{N#ga+R{oCsomuB+<#ZEJk_1u)szUQ}lIiv086DhUhHG{f zHef-kz@(Y=(;!r=p5j?h+(u6K_FfCb+E(ItsEtbKKWODD%9l9MBg*S zvk$P|_Or|L0+{-;aTO+&PufzaV?nWrwB|w02vLLM;Kghdbid~!#DQ|m&3bPVkpXvw z_nkSP_a0Jt6%}6XJ7O{ESlNG$gNF@#4g$Regydb#G1C&M-qcC*K>lJy=@(1o?d5P< zQ(!fF*Q2@uBtkCM+cQhvJyA%Ug26wKV!0WJ!d~zI$mZj})4#Oq)QEA9Xj%|00H0B6 zp14sAvrdV{1*P-YC2(=GJ?9{0v{#1pX_6gT%ctfv{e^JJw9yZYSw*}JXcQsM_qxzc*<_e=|dQckr9{u z=k=dU^dn$nKkw=(RlSZN^(OC$3sDXA8C=zurJ`#2sMIHSdTrL}H~mO}RC+b=uY25$ z^c7b5IxJuHRYAyqMSM@6R^7L?L1H%~j_JqqD?gV5IQ6L*f#=jmKpi%LWME7`bFCt> z;}FZKygI_mxWLez<5(fe#g%(q6N<~^v@AC)=QHPpn{77Og*^JOKPlPOsMAeOXs>%T z;$%ey$2Q}6Q^pFy1dy#hc1*mQc|M5OKSVvl!HFM{lNIEo?Pj{`iCIdw@*xnzVu>m7 zT5IPWCPA?n;)O-|YJ$NH(WVW|#M$!q>Ra3_VNAEaW@Yp42WyBUB-AKuw zy*)}ygtBfihq*DNNg7lZJdqbmdo9)YHbhbZbX97ulJ%OhVY6aSu347@cqRtqOl#)eNcsS998?ZyD)0pRC}82rg|0 z_l}9J(gWATcGpfl+H#(`;D=IdnUu9l$1EGCZ{EK}qs>5=-7u^&^!&4o|BaW%c%rS4h7`NONl$vCWdh5bTk`THTW?6^a`f2~&LP1=xq@Yby>-ltU zijjBq50X5#C>ueOV75WF8dH{U61p-eU66uIIx00nu%EKK^ROM)WN4LnS_JXS^umXo z%u;QVoi**OB|9sv&eXI7Af4VFG<-z?SLjwOUvy?S%lupChGe*ZpLPcJeBhvFqF!2x z+hV)|$am~^q2l$P$W_tW_CMbi{J6w1uZtB)92Fx-G_diI(DD5s_01e09&HxqfUPAR6K!po(Iu(zUKQ0*Bc;0$Q z=kOgBp4@IQF{kNkJn-;G#UH4-xU&b#q-F_-()M9Wx`93 zAx3@4-}U-AcA7<~90a6jwj5e6=?!j`itn>fG`+ht*|5hsAw#HXlHjFIoyqx?W*#9el>i*cdRn- zbS1L=pXDbXc=HwS+)Eb(=(&#mN0URjCv#go!yLum4M*7tq1{PXu{5>Kq_5bJsrgeA zoxpWP3I-;Qd0&j3vD%3I!-z`;QM{M7YPcYqIDnf)BNa?FCul1 zI>?6W7MT$&rb^8cL_cn2i#&yO`F!DJGJ5y+LFyfj+Fu8a<{%9}oQ=pcshF4p!YQCO z^utPuYmomw-L~hgDlNX>IXv}*1b$3qkb={zbV9p^eSZ2IvIENX)DKZhMn@~9UtJjw z%4K{$-b3RuUbCnv=R%2MWW|=ha|F+?&d0d?iF$km7j#bbYv}IjREn|qb4JTIHmXTc zGse%5o>>3=jWrmYv|XmUbqhh*uw(SD+ei08P<+IQO5G{g-$~9U{=U^6X@%UM+o^Qt zG0zTf!_Jg)qiVtbcTR2({Gsn#x`lX=-$?nK@{hHlYpm}IQ1uNg2626WE|x=QI!fs1 zD@8J`cBGE~j3hu=0?YBXUZ7wmWjt@GUm{yOD>0Ga4*UbfGvjjy3IKfl*&53dJ9=Fq zmw*0k`^&kd+H8Kx;ovlla1CP;Q?GnKBO=()B6E_nTAl?6@D+YJ;YOqH}Wvn?-J+i6eQI_xRpTzLp3)iw@qxD6TdK-M;GSwB~xAFGwQIZ|ZpZ=TJ z*6%z!fL~5z<-z_uO!(uWu8FxyuWm$O#J3Ou+@D7o@PjpzYbbn;xaZU!(P)|+bWUl5 zw?!+PmV(3TZRJIKhVVb{7ovl3gacvp!>#+*i}t|zN#sT+>c3k1wl>yIj0NQSmvlSm z#b<45iY6Jpx0c1nghwaln5Ou z6^)(g<-A^uBfV~1(z`y{<;VOT<-ug*e(&*k>i0F*$#jjFLL?T`fmb9-aU^`@V6wNS zpjZMQ`zCxEF)uNu@@n+(iS}Jz?g8WjS`k7-%TK?Ny^z<3*k!qJp zP@A>{%?4I4O{&DOD1^Qf?@4Wb!(67eH7?$u4mD)>E%d(n0RtD*qGn=XzFf05O_?#x z$8$hhqp@0QxbVcCZ2M$3q!9E7dOvSx0w?*EVDG0O9HjoMN@*T~y>-$(F4-W9AA((% z?#97w-bpozBum({UoQ6*udw;cH~$j=|CME;LeqmC{$x*~Vq+#R9dNQbL^9q~#2D52 z&=agYl(Ul@XnO&it|Zw$=)*RDSxHFjn=ZmVt+^k5tDqtjmL_}?D-Lwj%5>Sb)+a{> z-0l8WosPS0%biOxm`jrGEy?xn59gdOPcVDkVp>!dUQtw6l$*T7JARR^$5F(}3&+?9 z>mRtm32x)eo7Xnq1eKi8QICx~1@zuZOw!fd2hOeLb=V1tMjI((qTX_eZ^v0a+z>7M zg&F?Ib@FDo#Umo5ECkX;a-5MEQNeK7ShF-ZFNmL_nsO~mHlFA&aSLu9Wn-`1r~Nep ze(tj;XCXE+w31_t7zyOE54x?{vt5-(G%l~6XX9>3Y~+zO+X1r}h8zAQJta~6`QIS5aw^YQEa@wa)zS-2c`i{L52uUim`%%IPt`Y!}{}T0| zHAKH7+P5Cm)v@_fbfj+>V*i<|hy%jx^Hfpy^XWs6R-XH2ikqM=2t%xP0HzpMxG^CO zUnXV##1^e@{9P%zU#8Nxu_HG(q?8)K#p3U{VnM<6@Nj%&i1;rj)1C1!x^?Tw?+I)V zn(??hz$V*^4efVJ;5?$>lI|9>J#13Fd#RguTVztCP%HL~KkR)afcn3l$;Bd)PaV`8 zWXuGab+9ui1Ktx|e>Qh}wmc=Ac>y_BK(cX^0SYv;Te(wViV07!Y!~HE1y1S&0g{8x zWVGxBKVSIX!I`zIB4^C{00~a<;$pN{L)qxHkz`xs0K{ zrYPoPHJ@mD3X(hT?{U1tzaU_~$&~I21vTsPqE41km`G8tF%etCB4uvKl_f0EpXW8%z%z3OjF8+n8f^ZV5xFJ;)Uql40L%XmNWIq8+SG8dMj}7{3_-+P&6dw4L|}ewGaJ zdDa`{K|>G}wY(b{ zcv8m?B$X*rUqj9%C~v3s!N2o=SdS3^sszxs8ishxtjs@|Uf}OX`)shIxT?RC=gH@!Ptq#;^xnkf0%&gNBh*QAq!~pRIFOPCIw@?&)iLOV8sCW^c710C zP)vc29>r6&bv}7X26HcJ*gZ$>}*l2w)m;1P~zIGVF%Cg8H3E$J2EZu zahWAnKN;V>H!Ca#Kfe?ycfL6?puy+k1F0<{DmDjZ)mhy528bVUX%6T_r3r9UGGLwL zyYzqCZGA;dGmAGXFW=A)a{X8Lfb$uAupVZ=45I^Ku7KN7wR(W)DkYqgA_Z8)TZP0v z!KQLDcoV!8y`(^CaS?;xGY06H;v;Kr-(WUY%Tn)wD7qrAFuohuM%ZQKZOjb=$KvcO zJ1;Nd%tO~D8^7uDmmiVs#KzaxdpwxIQ+$jVEOP)BdY^W@mr&oX9PDdVt6zWy;hKnu zYnyzIacw)s!+=1aX!oH<(Wo0oN?uqgkvXqy?Ulk@>;4^^m`b*O&QXsi1oW+^<4Ogd zXMrgAfu!!{vlPl10|@WOjDoR=L}1YL*VbG9WN{d!PKq7z1JbOTz{g4O) zLQIwN5or~;rmi6%0ZCWsYKm$vBbrTi2mbs3IfqJmEc!t$>MDep_gX$NFw{SC^j^eA z{SdE4hMpNMNzEQpOSlXYFZr5hgl@aIbI!_A&QmL20kt;~bV!#UbYt)KryLV@SIr>o z&))y51^DH2c{{ASBTY!zH)S1*7d)Cpm9^?5Sf}(}DEC9$Bu&(gh#MA9Ur|ujE1}!R zVYU|}%c9)r<3Q!g&)Xg9+tA&9LF84<7^a3&#ovJLWBzB(dn*6sa**(RWp0Zaq|{8% z%_l%bUf0d~(%e2y^@%rqQjBZH8@bC=lmuRCAaaWvJrnu+OrlT|tfYmNqF6f+hMeCG znZtU(5|J!mNuJg5;JaTOl=1DXSC!=SMS?C2k|Bp`y-^F&71GMZO!+;8tclWHOA!(X zQL=91-q?d*n--~lQ`{2v0GTLbhDWOFP zVg3jvhN8t`a=fi@uuWqE$6y30R|-L9S0DU-n6-q%B3x2;xBtNVt?oj`Xqan)g5ZoC z9rYScey8R&H)iShag^$xae!Z%4j%IG6{CCwA@RhdCY!+ zc(*tw-bk&}qe?W9%WFP$t_P>}T~(Trw;vuDU_uA~hih*$Oe?mMV}febsF1ZqN?;*W za6Dx%JgTKD8m99kN>M|IrWJEu#Q=XEA*6T+MgGAUP{=OMP^@7KK?mBu+|PFe*hL9xsBH8hS44^kaQctHC1*yQ7et2Y^d$x2QWRim+ItY#lXlJb{ycRWC^0WHH% zdESq_#|3W1JtsTaKYh@C-%kc6;PPvQP?EHO#fG-f!4=*kSXthvvj1n9u`~m+1Z8*4 zR0sqWz~ZXk`mNxCC^o!bZspdwp4@uY$f7t65${!Vm&ixKWN9bgkX<9;vnXTq;$x`_ znXrpj!+C?nAJm&eWO%KgYG)IfaKubSlym1+?n~|o7vw}#tSP8a63c2Ke>j4&DU>dn z`oDj6_xAUC=EW?0K=*JHs|oxc$F#lraixtIlg(6gyPn)tZK$sO7U}(nAx%8^Ngvku zIDKy^=i+1qA19bBfSz+v0;@2aGmidt%BWp^0t{jC*16VNjfeAkRo&jGv_8eJmFak; z3Zip9*1`|eH~{u%Pq`_`kdHs`i};Vbv?XV8daK~`(TC^t9+eK5W6u`J9LM(Er7>cr z_rDg4HmAyOoOBm9WF3Sv-&K%BBo@v=hh;}awspVi%iI0Xa+$p!%OJwLP$|3`xu(#2 z^NYxA>M6@OV=;H{X9<3_T1~>WL4u8`Bjle#nwJhS0t$F6qDUt$q*+qv5p74Or+0q0 zi;~=fwWa?V8iZ?7bD+&Ed-o#N-}~_DqRi+|LbDYw3YEd<-{}>ShZKX!3A{5`=?ejh zzmYQ7NYVcU^rS7tuc7!}P4Hh}-PSN+>Zw|Fu_ky{E8jZ5-Nd8*+xGD%Ll!20aw790 zGrUFB8lv+$=?AymCNWq6TBA=h}4To%#=w(?- z?zyJRd}YwAJ<=Sl{l`uPx+a`4JlgQq2FdB)=cE<9;8V-BAmoLU0`6FvFNTD z4^0MB$)?k zOnnDB6J$Oi~w zS5i=oyBM$HeH((8h)gVf%sOi(SMWO)hUD**QAZ5CW!@95&nHdrbRD9VaCePo&*ld6 z#5d;elrOWcP#NoBFC?dmXYN;qjWGy zRQ&_BG=Q7`!_M4X6LHvaK)3R;6&>Lsk-6fnWt;TE&Q zy}zL;K^(<^sK4LLzPgz~S&(%sE(?+I!-Jv6O4n6;HNi{~K)=!Uq@HohzQ`1q+ak;t zRsvS?pF1&4W4JU$bx2aLO;S$8By(QuSp`QQ>#< zrIiQiARg~zNPjuydDk8e`I6Y0~AK#m*BpN*QmWQk582YWE4thQ)MVZC1 zH!$Oi^@C|pb$HXI+Z_td1#jI6dqXee{m8DXR{Kjh0pe+*EbTuH77<@P$3hB9S6tSV z=Km&?fafOr1dJ^9*p~&lA4s!H19IwyjAB#^3klId$T!mFptB9Zwic%_*q>o6qcrE< z|Foe29bJk=ve?Hg3O#=s^Whqvt0(WM00&Cf3v2*%;4-fHR`DrjIS{+)@+mKjuEyab z_`T!xlVhTDjPVO-MePVGjTr|gyXGg&7KDB4wuy=DC})$}7PkUbv#LMm&$kx74_jvzltc zUYx@(tn$);Y}WCP(fY3c0`w1mg~A%JT4&f7wt@c`5r?*|Vj=@_mR9Bpg~k`#q7SPe zydVWasFEpWjO0}fa)i+J2XL1$5eQ@a_F<8mHKeB@o;s^H%7|9PlNp|Z#iNi!d6a?m zmBE^6^9%&6?xujq+vt*|sXEIKiGFDbiBqqK5ILXRfox3`NSllJS{B5qow&3mGF(^4 z0GjbWtbg@)Q9OVeeL2K+4L0Y!`;2iyQ|bfuy=2#Ss_P_*DA3F7pqQ{ofl|>jN8`kV zsaO~T%BJGpaZ?wf;#gCSEi`vzP6#({!A7zf4^4T}@PFIQChfJQHFgJ={0};B5OxIH zt*ax25g>2I3#=!o^$Hg3PxWL`*b5@Zgz^U`@rAwaP{m5K+yoRF`_wnso>Z~Z(|{NN@5WKUrm=I4+Jh~Pf0cO5T?2Y<_Pq)umi1|+4b{{&%(sVb76B{^W%=U-u>d`p`i#S z<##CHFwum)zUwnBDIc50>e+5f)>cGJn7w1`)~`hwfuS_rcxHKb67iLIiPz|d?ZIGd{mbovGLC;dL;Q#%*tSttj> z7%%Csg{S2!Yq+c#a-raxq~4(b`(A%k@KzFU@6$=EQb@o=Bp7=m^l$JON1M-9<~8EWs~^%frl1m<5fe@0(7@1HmO zM1Sp#I-c!~+U_IVU_3BpnS2QY1Z#lTh$57!BI{VoLYucO!H#;+1ViWeMm8bfDL)mz zDoChW`ScuG@KIXkf!(VX3LSG;t&9B7lW#xk9vf+T+~f7d_iqqXeN6pmkQlFs*g58J zuthnE6?@^aG#-Cyyl!~c$%&=-^PLn&e7uUFPwk+vy%0DkSOmgI=C^vK@zcYRq%8>W)Q`#!Ri^dcQbFK1 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

#7-(_x*FJ-Goi;5J4e@4oOaCAQT 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 meter_pos((int)offset.X, (int)(offset.Y-meter_height), @@ -1104,14 +1104,14 @@ void RaceGUI::drawSpeedEnergyRank(const AbstractKart* kart, // Draw the actual speed bar (if the speed is >0) // ---------------------------------------------- - float speed_ratio = speed/40.0f; //max displayed speed of 40 + float speed_ratio = speed/55.0f; //max displayed speed of 55 if(speed_ratio<0) speed_ratio = -speed_ratio; // display negative speeds too if(speed_ratio>1) speed_ratio = 1; // see computeVerticesForMeter for the detail of the drawing // If increasing this, update drawMeterTexture - const int vertices_count = 12; + const int vertices_count = 13; video::S3DVertex vertices[vertices_count]; @@ -1119,64 +1119,57 @@ void RaceGUI::drawSpeedEnergyRank(const AbstractKart* kart, // They are calculated from speedometer.png // A is the center of the speedometer's circle - // B2, C, D, E, F, G, H, I and J1 are points on the line - // from A to their respective 1/8th threshold division - // B2 is 36,9° clockwise from the vertical (on bottom-left) - // J1 s 70,7° clockwise from the vertical (on upper-right) - // B1 and J2 are used for correct display of the 3D effect - // They are 1,13* further than the speedometer farther position because - // the lines between them would otherwise cut through the outside circle. + // Points B to M are points on the line + // from A to their respective 1/11th threshold division + // (There is 12 points because it goes from 0/11 to 11/11) + // They are further away (at a constand distance from A) + // than the speedometer farther position because the lines + // between them would otherwise cut through the outside circle. core::vector2df position[vertices_count]; - position[0].X = 0.546f;//A - position[0].Y = 0.566f;//A - position[1].X = 0.216f;//B1 - position[1].Y = 1.036f;//B1 - position[2].X = 0.201f;//B2 - position[2].Y = 1.023f;//B2 - position[3].X = 0.036f;//C - position[3].Y = 0.831f;//C - position[4].X = -0.029f;//D - position[4].Y = 0.589f;//D - position[5].X = 0.018f;//E - position[5].Y = 0.337f;//E - position[6].X = 0.169f;//F - position[6].Y = 0.134f;//F - position[7].X = 0.391f;//G - position[7].Y = 0.014f;//G - position[8].X = 0.642f;//H - position[8].Y = 0.0f;//H - position[9].X = 0.878f;//I - position[9].Y = 0.098f;//I - position[10].X = 1.046f;//J1 - position[10].Y = 0.285f;//J1 - position[11].X = 1.052f;//J2 - position[11].Y = 0.297f;//J2 + position[0].X = 0.5332f;//A + position[0].Y = 0.5469f;//A + position[1].X = 0.2051f;//B + position[1].Y = 1.0225f;//B + position[2].X = 0.0674f;//C + position[2].Y = 0.8887f;//C + position[3].X = -0.0195f;//D + position[3].Y = 0.7168f;//D + position[4].X = -0.0449f;//E + position[4].Y = 0.5264f;//E + position[5].X = -0.0059f;//F + position[5].Y = 0.3379f;//F + position[6].X = 0.0928f;//G + position[6].Y = 0.1729f;//G + position[7].X = 0.2402f;//H + position[7].Y = 0.0488f;//H + position[8].X = 0.4199f;//I + position[8].Y = -0.0195f;//I + position[9].X = 0.6113f;//J + position[9].Y = -0.0254f;//J + position[10].X = 0.7949f;//K + position[10].Y = 0.0322f;//K + position[11].X = 0.9492f;//L + position[11].Y = 0.1465f;//L + position[12].X = 1.0576f;//M + position[12].Y = 0.3047f;//M + // The speed ratios at which different triangles must be used. float threshold[vertices_count-2]; - threshold[0] = 0.00001f;//for the 3D margin - threshold[1] = 0.125f; - threshold[2] = 0.25f; - threshold[3] = 0.375f; - threshold[4] = 0.50f; - threshold[5] = 0.625f; - threshold[6] = 0.750f; - threshold[7] = 0.875f; - threshold[8] = 0.99999f;//for the 3D margin - threshold[9] = 1.0f; - - //3D effect : wait for the full border to appear before drawing - for (int i=0;i<8;i++) - { - if ((speed_ratio-0.125f*i < 0.00625f && speed_ratio-0.125f*i >= 0.0f) || (0.125f*i-speed_ratio < 0.0045f && 0.125f*i-speed_ratio >= 0.0f) ) - { - speed_ratio = 0.125f*i-0.0045f; - break; - } - } + threshold[0] = 0.0909f; + threshold[1] = 0.1818f; + threshold[2] = 0.2727f; + threshold[3] = 0.3636f; + threshold[4] = 0.4545f; + threshold[5] = 0.5454f; + threshold[6] = 0.6363f; + threshold[7] = 0.7272f; + threshold[8] = 0.8181f; + threshold[9] = 0.9090f; + threshold[10] = 1.0f; unsigned int count = computeVerticesForMeter(position, threshold, vertices, vertices_count, speed_ratio, meter_width, meter_height, offset); @@ -1192,7 +1185,7 @@ void RaceGUI::drawMeterTexture(video::ITexture *meter_texture, video::S3DVertex //Should be greater or equal than the greatest vertices_count used by the meter functions if (count < 2) return; - short int index[12]; + short int index[15]; for(unsigned int i=0; i Date: Thu, 23 Nov 2023 17:40:56 +0100 Subject: [PATCH 048/188] Fix interpolated weights unscrambling --- src/items/powerup_manager.cpp | 7 +++++-- src/items/powerup_manager.hpp | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/items/powerup_manager.cpp b/src/items/powerup_manager.cpp index 8a32dc361c8..835364d55d7 100644 --- a/src/items/powerup_manager.cpp +++ b/src/items/powerup_manager.cpp @@ -387,6 +387,9 @@ void PowerupManager::WeightsData::interpolate(WeightsData *prev, l.push_back(int(interpolated_weight + 0.5f)); } } // for l < prev->m_weights_for_section.size() + + // Copy over one m_powerup_order list + m_powerup_order = prev->m_powerup_order; } // WeightsData::interpolate // ---------------------------------------------------------------------------- @@ -552,9 +555,9 @@ int PowerupManager::WeightsData::getRandomItem(int rank, int random_number) // We align with the beginning of the enum and return // We don't do more, because it would need to be decoded from enum later #ifdef ITEM_DISTRIBUTION_DEBUG - Log::verbose("Powerup", "World %d rank %d random %d %" PRIu64 " item %d", + Log::verbose("Powerup", "World %d rank %d random %d %" PRIu64 " item %d powerup order size %lu", World::getWorld()->getTicksSinceStart(), rank, random_number, - original_random_number, powerup); + original_random_number, powerup, m_powerup_order.size()); #endif // The value stored in powerup is still scrambled because of the weights reordering diff --git a/src/items/powerup_manager.hpp b/src/items/powerup_manager.hpp index 92869158eb1..d74d9b69e4b 100644 --- a/src/items/powerup_manager.hpp +++ b/src/items/powerup_manager.hpp @@ -212,4 +212,4 @@ class PowerupManager : public NoCopy extern PowerupManager* powerup_manager; -#endif +#endif \ No newline at end of file From 4ecdaf787b2b1c2743407b025deece87029ccf72 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Fri, 24 Nov 2023 23:29:43 +0100 Subject: [PATCH 049/188] Improve purple skid appearance --- data/gfx/skid3.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/gfx/skid3.xml b/data/gfx/skid3.xml index dcd4009ad47..324c2acb2e2 100644 --- a/data/gfx/skid3.xml +++ b/data/gfx/skid3.xml @@ -23,7 +23,7 @@ - + From 606430274f00b58b9a87747f44b822e63e5cd8b6 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sat, 25 Nov 2023 15:39:24 +0100 Subject: [PATCH 050/188] Greatly improve the kart-on-kart collision code - Greatly expand the caps on mass and speed for the collision impulse factor - this makes the difference between kart classes, and between fast and slow moving karts, meaningful. - Significantly increase the strength of kart-on-kart collision impulses to make them meaningful. - Increase the duration of the impulse, but make it decay in strength. Instead of the kart lateral movement stopping abruptly, it is progressive, which makes for much better gameplay and appearance. - Throw out the old code for ramming, that was completely useless, and replace it by an effective solution to assess if (1) one kart is ramming the other (2) how significant the ramming is - Remove the squaring of the factors that was done before to (poorly) increase impulse strength All these changes make it easier to push another kart (especially with a heavier kart against a lighter kart), but they also eliminate the dreaded collision deadlock (where one kart would push another into a rotation, and the other kart trying to not rotate away from the track direction would push back against the first kart and lock both karts until they crashed off-track). The values of the impulse strength and of how important the ramming-factor can be are provisional, pending multiplayer playtesting, as the AI is not good to test this with. A kart can only be subject to one impulse at a time, but for now this limitation is minor. A more significant issue is that the impulse direction is relative to the kart. This means that if the kart is subject to a rotation while the impulse is active, the impulse will push the kart in an incorrect direction. This is very noticeable in races with many AI karts or if a kart is in an already incorrect position when pushed. --- data/stk_config.xml | 8 +-- src/karts/kart.cpp | 2 +- src/physics/btKart.cpp | 5 +- src/physics/btKart.hpp | 6 ++ src/physics/physics.cpp | 128 +++++++++++++++++++++++++--------------- 5 files changed, 97 insertions(+), 52 deletions(-) diff --git a/data/stk_config.xml b/data/stk_config.xml index 77f9237ded8..8c9e4901830 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -472,9 +472,9 @@ effect. impulse: an additional impulse to be applied in a non-frontal collision to push two karts away from each other. - impulse-time: The impulse will be applied over a certain time - period, which results in less abrupt changes. If set to 0, - the impulse is only applied once. + impulse-time: The kart-on-kart collision impulse will be applied + over a certain time period, which results in less abrupt changes. + If set to 0, the impulse is only applied once. resitution: restitution value to be used for the kart rigid bodies. The restitution used depends on the speed to avoid physics issues (a collision with high speed and high restitution will push the @@ -499,7 +499,7 @@ outside of the chassis and results in more stable physical behaviour of the karts. --> diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index c60b1e4dbfc..7f68eab6c37 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -611,7 +611,7 @@ void Kart::setPosition(int p) // ----------------------------------------------------------------------------- /** Sets that the view is blocked by a plunger. The duration depends on - * the difficulty, see KartPorperties getPlungerInFaceTime. + * the difficulty, see KartProperties getPlungerInFaceTime. */ void Kart::blockViewWithPlunger() { diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp index 3f54630e5bd..cc4221156b5 100644 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -115,6 +115,7 @@ void btKart::reset() m_num_wheels_on_ground = 0; m_additional_impulse = btVector3(0,0,0); m_ticks_additional_impulse = 0; + m_ticks_total_impulse = 0; m_additional_rotation = 0; m_ticks_additional_rotation = 0; m_max_speed = -1.0f; @@ -513,7 +514,9 @@ void btKart::updateVehicle( btScalar step ) { // We have fixed timestep float dt = stk_config->ticks2Time(1); - m_chassisBody->applyCentralImpulse(m_additional_impulse*dt); + float remaining_impulse_strength = (float)m_ticks_additional_impulse / + (float) m_ticks_total_impulse; + m_chassisBody->applyCentralImpulse(m_additional_impulse*dt*remaining_impulse_strength); m_ticks_additional_impulse--; } diff --git a/src/physics/btKart.hpp b/src/physics/btKart.hpp index 65ecdd28286..816ce7e626f 100644 --- a/src/physics/btKart.hpp +++ b/src/physics/btKart.hpp @@ -83,6 +83,11 @@ class btKart : public btActionInterface /** The time the additional impulse should be applied. */ uint16_t m_ticks_additional_impulse; + /** The total time the additional impulse should be applied. + * This is used to weaken the impulse progressively, as an + * impulse suddenly stopping is very noticeable with strong impulses. */ + uint16_t m_ticks_total_impulse; + /** Additional rotation in y-axis that is applied over a certain amount of time. */ float m_additional_rotation; @@ -231,6 +236,7 @@ class btKart : public btActionInterface // Only add impulse if no other impulse is active. if (m_ticks_additional_impulse > 0 && !rewind) return; m_additional_impulse = imp; + m_ticks_total_impulse = t; m_ticks_additional_impulse = t; } // setTimedImpulse // ------------------------------------------------------------------------ diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index ded8a5346be..a308f35fda5 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -446,86 +446,122 @@ void Physics::KartKartCollision(AbstractKart *kart_a, kart_b->crashed(kart_a, /*handle_attachments*/false); AbstractKart *left_kart, *right_kart; + float left_kart_contact_Z, right_kart_contact_Z; // Determine which kart is pushed to the left, and which one to the - // right. Ideally the sign of the X coordinate of the local conact point + // right. Ideally the sign of the X coordinate of the local contact point // could decide the direction (negative X --> was hit on left side, gets // push to right), but that can lead to both karts being pushed in the // same direction (front left of kart hits rear left). // So we just use a simple test (which does the right thing in ideal // crashes, but avoids pushing both karts in corner cases // - pun intended ;) ). + if(contact_point_a.getX() < contact_point_b.getX()) { left_kart = kart_b; right_kart = kart_a; + left_kart_contact_Z = (kart_b->getKartLength()/2 + contact_point_b.getZ()) / kart_b->getKartLength(); + right_kart_contact_Z = (kart_a->getKartLength()/2 + contact_point_a.getZ()) / kart_a->getKartLength(); } else { left_kart = kart_a; right_kart = kart_b; + left_kart_contact_Z = (kart_a->getKartLength()/2 + contact_point_a.getZ()) / kart_a->getKartLength(); + right_kart_contact_Z = (kart_b->getKartLength()/2 + contact_point_b.getZ()) / kart_b->getKartLength(); } + // If both karts have an active impulse, we can return immediately + if(right_kart->getVehicle()->getCentralImpulseTicks() > 0 && + left_kart->getVehicle()->getCentralImpulseTicks() > 0) + return; + // Add a scaling factor depending on the mass (avoid div by zero). // The value of f_right is applied to the right kart, and f_left // to the left kart. f_left = 1 / f_right + // The max value based on mass with standard settings is 350/210 = 1.6667 float f_right = right_kart->getKartProperties()->getMass() > 0 ? left_kart->getKartProperties()->getMass() / right_kart->getKartProperties()->getMass() - : 1.5f; + : 2.0f; + + if (f_right > 2.0f) + f_right = 2.0f; + if (f_right < 0.5f) + f_right = 0.5f; + // Add a scaling factor depending on speed (avoid div by 0) - f_right *= right_kart->getSpeed() > 0 - ? left_kart->getSpeed() - / right_kart->getSpeed() - : 1.5f; - // Cap f_right to [0.8,1.25], which results in f_left being - // capped in the same interval - if(f_right > 1.25f) - f_right = 1.25f; - else if(f_right< 0.8f) - f_right = 0.8f; + // The impulse is weaker for the kart going faster, + // stronger for the kart going slower. + float speed_impulse_factor = right_kart->getSpeed() > 0 ? + left_kart->getSpeed() / right_kart->getSpeed() : 1.6f; + if (speed_impulse_factor > 1.6f) + speed_impulse_factor = 1.6f; + if (speed_impulse_factor < 0.625f) + speed_impulse_factor = 0.625f; + + f_right *= speed_impulse_factor; + + // Because of the caps on the mass and speed factors, + // f_right is effectively capped to the interval [0.3125, 3.2] + // f_left is capped in the same interval as 3.2*0.3125 = 1 + float f_left = 1/f_right; // Check if a kart is more 'actively' trying to push another kart - // by checking its local sidewards velocity - float vel_left = left_kart->getVelocityLC().getX(); - float vel_right = right_kart->getVelocityLC().getX(); - - // Use the difference in speed to determine which kart gets a - // ramming bonus. Normally vel_right and vel_left will have - // a different sign: right kart will be driving to the left, - // and left kart to the right (both pushing at each other). - // By using the sum we get the intended effect: if both karts - // are pushing with the same speed, vel_diff is 0, if the right - // kart is driving faster vel_diff will be < 0. If both velocities - // have the same sign, one kart is trying to steer away from the - // other, in which case it gets an even bigger push. - float vel_diff = vel_right + vel_left; - - // More driving towards left --> left kart gets bigger impulse - if(vel_diff<0) + // To do this we check two things: + // - The position of the contact point on the kart body. + // The active kart is touching with the front of the kart + // - The angle between the two karts. A higher angle indicates + // ramming. + + // The "kart_contact_Z" are between 0.0f (kart touching at the very back) + // and 1.0f (kart touching at the very front) + // Note that the wheels are usually not at the very front + bool left_kart_ramming = false, right_kart_ramming = false; + if (left_kart_contact_Z > 0.7f && + left_kart_contact_Z > right_kart_contact_Z + 0.15f) + left_kart_ramming = true; + else if (right_kart_contact_Z > 0.7f && + right_kart_contact_Z > left_kart_contact_Z + 0.15f) + right_kart_ramming = true; + + // Kart heading is in the range [-pi, pi]. It depends on the direction + // the kart is pointing at. + // We can assume that both karts are more or less in the same plane + // when they collide, in which case the heading difference indicates + // the angle at which they collided in this plane + // We use this difference to compute a "ramming factor" + float heading_difference = left_kart->getHeading() - right_kart->getHeading(); + + // The difference is in the range [-2pi, 2pi] + // First, we must normalize it + // The small inaccuracies in approximating pi are irrelevant here + // Ramming is maximized if one kart has a perpendicular direction to the other + if (heading_difference < 0.0f) + heading_difference += 6.2831854f; + if (heading_difference > 3.1415927f) + heading_difference -= 3.1415927f; + if (heading_difference > 1.57079635f) + heading_difference = 3.1415927f - heading_difference; + + // Now heading difference is in the range [0, pi/2] (approximately) + // With 0 indicating parallel karts and pi/2 perpendicular karts + // We set ramming_factor in the [1, 1+pi/4] range + float ramming_factor = 1.0f + (heading_difference * 0.5f); + + if (left_kart_ramming) { - // Avoid too large impulse for karts that are driving - // slow (and division by zero) - if(fabsf(vel_left)>2.0f) - f_left *= 1.0f - vel_diff/fabsf(vel_left); - if(f_left > 2.0f) - f_left = 2.0f; + f_left = f_left / ramming_factor; + f_right = f_right * ramming_factor; } - else + else if (right_kart_ramming) { - // Avoid too large impulse for karts that are driving - // slow (and division by zero) - if(fabsf(vel_right)>2.0f) - f_right *= 1.0f + vel_diff/fabsf(vel_right); - if(f_right > 2.0f) - f_right = 2.0f; + f_right = f_right / ramming_factor; + f_left = f_left * ramming_factor; } - // Increase the effect somewhat by squaring the factors - f_left = f_left * f_left; - f_right = f_right * f_right; - // First push one kart to the left (if there is not already // an impulse happening - one collision might cause more // than one impulse otherwise) From 39644a356ce244bee7334f130d1b94e81bd31cd6 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sat, 25 Nov 2023 18:29:34 +0100 Subject: [PATCH 051/188] Improve kart-on-kart collisions again - Prevent karts from being rotated as the impulse is applied, this makes it almost impossible for the impact to make the kart lose its direction. It doesn't prevent turning while the effect is on. - Add a very short fade-in period for the bounce, to make it look better. - Tune down the typical bounce strength. - Increase the maximum ramming factor. - Reduce the volume of kart-on-kart collisions. A better sound is still needed. --- data/stk_config.xml | 2 +- src/karts/kart.cpp | 3 +++ src/physics/btKart.cpp | 19 ++++++++++++++++--- src/physics/physics.cpp | 19 +++++++++++++++---- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/data/stk_config.xml b/data/stk_config.xml index 8c9e4901830..70e95f87212 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -499,7 +499,7 @@ outside of the chassis and results in more stable physical behaviour of the karts. --> diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 7f68eab6c37..2317e95d669 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -2646,6 +2646,9 @@ void Kart::playCrashSFX(const Material* m, AbstractKart *k) const float LengthOfDifference = VelocityDifference.length(); volume = sqrt( abs(LengthOfDifference / speed_for_max_volume)); + // temporary fix for kart collision sounds being annoying + // TODO : find a new sound for kart-on-kart collisions + volume *= 0.5f; } if (volume > max_volume) { volume = max_volume; } diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp index cc4221156b5..00cdf7444f0 100644 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -514,9 +514,22 @@ void btKart::updateVehicle( btScalar step ) { // We have fixed timestep float dt = stk_config->ticks2Time(1); - float remaining_impulse_strength = (float)m_ticks_additional_impulse / - (float) m_ticks_total_impulse; - m_chassisBody->applyCentralImpulse(m_additional_impulse*dt*remaining_impulse_strength); + float impulse_strength; + + float remaining_impulse_time_fraction = (float)m_ticks_additional_impulse / + (float)m_ticks_total_impulse; + + // The impulse strength will increase rapidly at the beginning + // up to maximum strength (as (1.0f - 0.8f)*5.0f = 1.0f) + // Then fade away slower. + if (remaining_impulse_time_fraction < 0.8f) + impulse_strength = remaining_impulse_time_fraction*1.25f; + else + impulse_strength = (1.0f - remaining_impulse_time_fraction)*5.0f; + + m_chassisBody->applyCentralImpulse(m_additional_impulse*dt*impulse_strength); + // Prevent the kart from getting rotated + m_kart->getBody()->setAngularVelocity(btVector3(0,0,0)); m_ticks_additional_impulse--; } diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index a308f35fda5..fe4e8a63333 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -509,6 +509,17 @@ void Physics::KartKartCollision(AbstractKart *kart_a, float f_left = 1/f_right; + // Reduce the bouncing for low-speed impacts + float max_speed = std::max(right_kart->getSpeed(), left_kart->getSpeed()); + if (max_speed < 20.0f) + { + if (max_speed < 0.0f) + max_speed = 0.0f; + f_right *= max_speed / 15.0f; + f_left *= max_speed / 15.0f; + } + + // Check if a kart is more 'actively' trying to push another kart // To do this we check two things: // - The position of the contact point on the kart body. @@ -548,8 +559,8 @@ void Physics::KartKartCollision(AbstractKart *kart_a, // Now heading difference is in the range [0, pi/2] (approximately) // With 0 indicating parallel karts and pi/2 perpendicular karts - // We set ramming_factor in the [1, 1+pi/4] range - float ramming_factor = 1.0f + (heading_difference * 0.5f); + // We set ramming_factor in the [1, 1+3*pi/8] range + float ramming_factor = 1.0f + (heading_difference * 0.75f); if (left_kart_ramming) { @@ -574,7 +585,7 @@ void Physics::KartKartCollision(AbstractKart *kart_a, ->setTimedCentralImpulse( (uint16_t)stk_config->time2Ticks(kp->getCollisionImpulseTime()), impulse); - right_kart ->getBody()->setAngularVelocity(btVector3(0,0,0)); + // The kart rotation will be prevented as the impulse is applied } // Then push the other kart to the right (if there is no @@ -588,7 +599,7 @@ void Physics::KartKartCollision(AbstractKart *kart_a, ->setTimedCentralImpulse( (uint16_t)stk_config->time2Ticks(kp->getCollisionImpulseTime()), impulse); - left_kart->getBody()->setAngularVelocity(btVector3(0,0,0)); + // The kart rotation will be prevented as the impulse is applied } } // KartKartCollision From 4856b3c87933d780f0d4d26b7f05343e7181aea2 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sat, 25 Nov 2023 22:06:27 +0100 Subject: [PATCH 052/188] More kart-on-kart collision tweaks - Set the karts to lean away from each other after a collision, improving visuals - Remove the speed-based impulse reduction and replace it by an impulse reduction based on relative velocity - Tweak again the collision impulse strength and duration --- data/stk_config.xml | 2 +- src/karts/kart.cpp | 9 +++++++++ src/physics/btKart.cpp | 15 +++++++++++++++ src/physics/btKart.hpp | 8 ++++++++ src/physics/physics.cpp | 36 +++++++++++++++++++++--------------- 5 files changed, 54 insertions(+), 16 deletions(-) diff --git a/data/stk_config.xml b/data/stk_config.xml index 70e95f87212..c9b96b18cb6 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -499,7 +499,7 @@ outside of the chassis and results in more stable physical behaviour of the karts. --> diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 2317e95d669..3ae79c4999a 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -3509,6 +3509,15 @@ void Kart::updateGraphics(float dt) m_current_lean = 0.0f; } + float temp_lean = 0.0f; + if (getVehicle()->getCentralImpulseTicks() > 0) + temp_lean = getVehicle()->getCollisionLean(); + + if (temp_lean > 0.0f) + m_current_lean = std::max(temp_lean, m_current_lean); + else + m_current_lean = std::min(temp_lean, m_current_lean); + // If the kart is leaning, part of the kart might end up 'in' the track. // To avoid this, raise the kart enough to offset the leaning. float lean_height = tanf(m_current_lean) * getKartWidth()*0.5f; diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp index 00cdf7444f0..91e23f8c461 100644 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -120,6 +120,7 @@ void btKart::reset() m_ticks_additional_rotation = 0; m_max_speed = -1.0f; m_min_speed = 0.0f; + m_leaning_right = true; // Set the brakes so that karts don't slide downhill setAllBrakes(20.0f); @@ -558,6 +559,20 @@ void btKart::updateVehicle( btScalar step ) adjustSpeed(m_min_speed, m_max_speed); } // updateVehicle +// ---------------------------------------------------------------------------- +float btKart::getCollisionLean() const +{ + float lean_factor = (float)m_ticks_additional_impulse / + (float)m_ticks_total_impulse; + if (lean_factor > 0.5f) + lean_factor = 1.0f - lean_factor; + + if (m_leaning_right == false) + lean_factor = -lean_factor; + + return 0.35f*lean_factor; +} // getCollisionLean + // ---------------------------------------------------------------------------- void btKart::setSteeringValue(btScalar steering, int wheel) { diff --git a/src/physics/btKart.hpp b/src/physics/btKart.hpp index 816ce7e626f..9de4dce82a1 100644 --- a/src/physics/btKart.hpp +++ b/src/physics/btKart.hpp @@ -77,6 +77,9 @@ class btKart : public btActionInterface */ bool m_allow_sliding; + /** Used to know the direction of the visual kart-on-kart collision lean */ + bool m_leaning_right; + /** An additional impulse that is applied for a certain amount of time. */ btVector3 m_additional_impulse; @@ -240,10 +243,15 @@ class btKart : public btActionInterface m_ticks_additional_impulse = t; } // setTimedImpulse // ------------------------------------------------------------------------ + void setCollisionLean(bool lean_right) { m_leaning_right = lean_right; } + // ------------------------------------------------------------------------ /** Returns the time an additional impulse is activated. */ uint16_t getCentralImpulseTicks() const { return m_ticks_additional_impulse; } // ------------------------------------------------------------------------ + /** Returns the collision (visual) lean. */ + float getCollisionLean() const; + // ------------------------------------------------------------------------ const btVector3& getAdditionalImpulse() const { return m_additional_impulse; } // ------------------------------------------------------------------------ diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index fe4e8a63333..b2715d6cf51 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -509,17 +509,6 @@ void Physics::KartKartCollision(AbstractKart *kart_a, float f_left = 1/f_right; - // Reduce the bouncing for low-speed impacts - float max_speed = std::max(right_kart->getSpeed(), left_kart->getSpeed()); - if (max_speed < 20.0f) - { - if (max_speed < 0.0f) - max_speed = 0.0f; - f_right *= max_speed / 15.0f; - f_left *= max_speed / 15.0f; - } - - // Check if a kart is more 'actively' trying to push another kart // To do this we check two things: // - The position of the contact point on the kart body. @@ -559,8 +548,8 @@ void Physics::KartKartCollision(AbstractKart *kart_a, // Now heading difference is in the range [0, pi/2] (approximately) // With 0 indicating parallel karts and pi/2 perpendicular karts - // We set ramming_factor in the [1, 1+3*pi/8] range - float ramming_factor = 1.0f + (heading_difference * 0.75f); + // We set ramming_factor in the [1, 1+pi/2] range + float ramming_factor = 1.0f + (heading_difference * 1.0f); if (left_kart_ramming) { @@ -573,6 +562,21 @@ void Physics::KartKartCollision(AbstractKart *kart_a, f_left = f_left * ramming_factor; } + // Reduce the bouncing if relative velocity is low + const Vec3 right_velocity = right_kart->getVelocity(); + const Vec3 left_velocity = left_kart->getVelocity(); + const Vec3 velocity_difference = right_velocity - left_velocity; + const float velocity_value = velocity_difference.length(); + + float impulse_time_factor = 1.0f; + + if (velocity_value < 8.0f) // velocity_value is never negative + { + f_right *= 0.375f + velocity_value / 12.8f; + f_left *= 0.375f + velocity_value / 12.8f; + impulse_time_factor = 0.6875f + velocity_value / 25.6f; + } + // First push one kart to the left (if there is not already // an impulse happening - one collision might cause more // than one impulse otherwise) @@ -583,8 +587,9 @@ void Physics::KartKartCollision(AbstractKart *kart_a, impulse = right_kart->getTrans().getBasis() * impulse; right_kart->getVehicle() ->setTimedCentralImpulse( - (uint16_t)stk_config->time2Ticks(kp->getCollisionImpulseTime()), + (uint16_t)stk_config->time2Ticks(kp->getCollisionImpulseTime() * impulse_time_factor), impulse); + right_kart->getVehicle()->setCollisionLean(/* towards the right*/ true); // The kart rotation will be prevented as the impulse is applied } @@ -597,8 +602,9 @@ void Physics::KartKartCollision(AbstractKart *kart_a, impulse = left_kart->getTrans().getBasis() * impulse; left_kart->getVehicle() ->setTimedCentralImpulse( - (uint16_t)stk_config->time2Ticks(kp->getCollisionImpulseTime()), + (uint16_t)stk_config->time2Ticks(kp->getCollisionImpulseTime() * impulse_time_factor), impulse); + left_kart->getVehicle()->setCollisionLean(/* towards the right*/ false); // The kart rotation will be prevented as the impulse is applied } From 6e9b7751f11b0cce6b693f74d6166722598cc05f Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 26 Nov 2023 17:06:51 +0100 Subject: [PATCH 053/188] Different minor tweaks to balance - The basketball minimum speed is very slightly reduced by 0.25, giving a slightly bigger opportunity to grab an additional item box or of firing an accurate backward item. This represents about 10% of the relative speedup basketballs got over karts in 2.X balance in the final stretch. - Cut the rescue duration time from 1.2s to 1.0s, for Expert and SuperTux difficulties only. - Increase engine power slightly, as the increase of max-speed was not associated with one. This makes it slightly easier and faster to reach max-speed. - Slightly increase the ratio of lower gears, to make acceleration a bit faster. Although the changes to acceleration are small, they allow to gain 0.1-0.2s over the previous values when accelerating from a standstill, which reduces the damage caused by offensive weapons. --- data/kart_characteristics.xml | 15 +++++++++------ data/powerup.xml | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index b1f49f44bc6..80cadee48d9 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -94,7 +94,7 @@ max-speed-reverse-ratio is the percentage of max speed for reverse gear. --> - - + + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple =" 0 0 0 0 0 0 0 0 0 0 0 0" /> + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple =" 0 0 10 0 0 0 0 0 0 0 0 0" /> + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple =" 0 0 25 0 0 0 0 0 0 0 0 0" /> + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple =" 0 0 30 0 0 0 0 0 0 0 0 0" /> + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple =" 0 0 60 0 0 0 0 0 0 0 0 0" /> + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple =" 0 0 0 0 0 0 0 0 0 0 0 0" /> + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple =" 0 0 10 0 0 0 0 0 0 0 0 0" /> + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple =" 0 0 25 0 0 0 0 0 0 0 0 0" /> + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple =" 0 0 35 0 15 0 0 0 0 0 0 0" /> + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple =" 60 0 60 100 26 0 0 0 0 0 0 0" /> + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple =" 0 0 0 0 0 0 0 0 0 0 0 0" /> + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple =" 0 0 15 0 0 0 0 0 0 0 0 0" /> + double =" 0 0 0 0 10 0 0 0 0 0 0 0" + triple =" 0 0 35 0 0 0 0 0 0 0 0 0" /> + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple =" 20 0 55 0 33 0 0 0 0 0 0 0" /> + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple =" 95 0 60 140 30 0 0 0 0 0 0 0" /> + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple =" 0 0 0 0 0 0 0 0 0 0 0 0" /> + double =" 0 0 20 0 0 0 0 0 0 0 0 0" + triple =" 0 0 0 0 0 0 0 0 0 0 0 0" /> + double =" 0 0 40 0 20 0 0 0 0 0 0 0" + triple =" 0 0 0 0 0 0 0 0 0 0 0 0" /> + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple =" 35 0 70 20 50 0 0 0 0 0 0 0" /> + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple ="115 0 60 180 30 0 0 0 0 0 15 0" /> + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple =" 0 0 0 0 0 0 0 0 0 0 0 0" /> + double =" 0 0 25 0 0 0 0 0 0 0 0 0" + triple =" 0 0 0 0 0 0 0 0 0 0 0 0" /> + double =" 0 0 45 0 30 0 0 0 0 0 0 0" + triple =" 0 0 0 0 0 0 0 0 0 0 0 0" /> + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple =" 45 0 80 40 65 0 0 0 0 0 0 0" /> + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple ="125 0 60 240 0 0 0 0 0 0 60 0" /> @@ -248,18 +273,23 @@ - + - - - + + + - + @@ -267,7 +297,8 @@ + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple =" 0 0 5 0 0 0 0 0 0 0 0 0" /> @@ -275,7 +306,8 @@ + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple =" 0 0 5 0 0 0 0 0 0 0 0 0" /> @@ -283,7 +315,8 @@ + double =" 0 0 0 0 0 0 0 0 0 0 0 0" + triple =" 0 0 100 0 0 0 0 0 0 0 0 0" /> @@ -294,8 +327,10 @@ touching multiple karts are at the top even if other powerups can situationally be more useful. Likewise, it doesn't matter much if e.g. a switch is really better or worse than a swatter. + Currently this is only used for races. Other modes would likely need their own lists. bubble cake bowl zipper plunger switch swattr rubber para sudo electro anvil --> - + diff --git a/src/items/powerup_manager.cpp b/src/items/powerup_manager.cpp index 835364d55d7..13ea64b7262 100644 --- a/src/items/powerup_manager.cpp +++ b/src/items/powerup_manager.cpp @@ -194,10 +194,12 @@ void PowerupManager::sortRaceWeights(const XMLNode *powerup_node, // (1) - Extract the goodness data from the XML node std::string single_string; node->get("single", &single_string); - std::string multi_string; - node->get("multi", &multi_string); + std::string double_string; + node->get("double", &double_string); + std::string triple_string; + node->get("triple", &triple_string); std::vector l_string = - StringUtils::split(single_string+" "+multi_string, ' '); + StringUtils::split(single_string+" "+double_string+" "+triple_string, ' '); std::vector values; for(unsigned int i=0; igetName().c_str()); } - if(values.size()>2*(int)POWERUP_LAST) + if(values.size() > 3* (int)POWERUP_LAST) { Log::fatal("PowerupManager", "Too many entries for '%s' in powerup.xml.", @@ -223,9 +225,9 @@ void PowerupManager::sortRaceWeights(const XMLNode *powerup_node, } // Make sure the entries are in the right range - for (unsigned int i=0;i< 2* (int)POWERUP_LAST; i++) + for (unsigned int i=0;i< 3 * (int)POWERUP_LAST; i++) { - if (values[i] < 0 || values[i] > 2* (int)POWERUP_LAST) + if (values[i] < 0 || values[i] > 3 * (int)POWERUP_LAST) Log::fatal("PowerupManager", "Incorrect entries for '%s' in powerup.xml.", node->getName().c_str()); @@ -302,10 +304,12 @@ void PowerupManager::WeightsData::readData(int num_karts, const XMLNode *node) const XMLNode *w = node->getNode(i); std::string single_item; w->get("single", &single_item); - std::string multi_item; - w->get("multi", &multi_item); + std::string double_item; + w->get("double", &double_item); + std::string triple_item; + w->get("triple", &triple_item); std::vector l_string = - StringUtils::split(single_item+" "+multi_item,' '); + StringUtils::split(single_item+" "+double_item+" "+triple_item,' '); // Keep a reference for shorter access to the list std::vector &l = m_weights_for_section.back(); @@ -317,14 +321,14 @@ void PowerupManager::WeightsData::readData(int num_karts, const XMLNode *node) l.push_back(n); } // Make sure we have the right number of entries - if (l.size() < 2 * (int)POWERUP_LAST) + if (l.size() < 3 * (int)POWERUP_LAST) { Log::fatal("PowerupManager", "Not enough entries for '%s' in powerup.xml", node->getName().c_str()); - while (l.size() < 2 * (int)POWERUP_LAST) l.push_back(0); + while (l.size() < 3 * (int)POWERUP_LAST) l.push_back(0); } - if(l.size()>2*(int)POWERUP_LAST) + if(l.size() > 3 * (int)POWERUP_LAST) { Log::fatal("PowerupManager", "Too many entries for '%s' in powerup.xml.", @@ -338,7 +342,7 @@ void PowerupManager::WeightsData::readData(int num_karts, const XMLNode *node) */ void PowerupManager::WeightsData::sortWeights() { - if (m_powerup_order.size() != 2*(int)POWERUP_LAST) + if (m_powerup_order.size() != 3*(int)POWERUP_LAST) Log::fatal("PowerupManager", "SortWeights called without m_powerup_order being set."); @@ -346,7 +350,7 @@ void PowerupManager::WeightsData::sortWeights() { std::vector temp_weights_copy = m_weights_for_section[i]; - for (unsigned int j=0; j< 2 * (int)POWERUP_LAST; j++) + for (unsigned int j=0; j< 3 * (int)POWERUP_LAST; j++) { // m_powerup_order[j] contains the new index for the j-th powerup m_weights_for_section[i][m_powerup_order[j]] = temp_weights_copy[j]; @@ -492,7 +496,7 @@ void PowerupManager::WeightsData::precomputeWeights() std::vector summed_weights; for (unsigned int j = 0; - j <= 2 * POWERUP_LAST - POWERUP_FIRST; j++) + j <= 3 * POWERUP_LAST - POWERUP_FIRST; j++) { float av = (1.0f - weight) * m_weights_for_section[prev][j] + weight * m_weights_for_section[next][j]; @@ -507,14 +511,14 @@ void PowerupManager::WeightsData::precomputeWeights() int target_sum = 32768 / REPEAT_WEIGHTS; for (unsigned int j = 0; - j <= 2 * POWERUP_LAST - POWERUP_FIRST; j++) + j <= 3 * POWERUP_LAST - POWERUP_FIRST; j++) { int normalized_sum = int( (summed_weights[j]/sum_av*target_sum) + 0.5f); m_summed_weights_for_rank[i].push_back(normalized_sum); } #ifdef ITEM_DISTRIBUTION_DEBUG Log::verbose("PowerupManager", "Final summed weight for rank %d is %d", - i, m_summed_weights_for_rank[i][2*POWERUP_LAST - POWERUP_FIRST]); + i, m_summed_weights_for_rank[i][3*POWERUP_LAST - POWERUP_FIRST]); #endif } } // WeightsData::precomputeWeights @@ -792,10 +796,20 @@ PowerupManager::PowerupType PowerupManager::getRandomPowerup(unsigned int pos, if(powerup > POWERUP_LAST) { powerup -= (POWERUP_LAST-POWERUP_FIRST+1); - *n = 3; + if(powerup > POWERUP_LAST) + { + powerup -= (POWERUP_LAST-POWERUP_FIRST+1); + *n = 3; + } + else + { + *n = 2; + } } else + { *n=1; + } // Prevents some items early on: // - Cakes right after the start destroy too much and too easily @@ -851,15 +865,17 @@ void PowerupManager::unitTesting() // Get the sum of all weights, which determine the number // of different random numbers we need to test. num_weights = wd.m_summed_weights_for_rank[section].back(); - std::vector count(2*POWERUP_LAST); + std::vector count(3*POWERUP_LAST); for (int i = 0; igetRandomPowerup(position, &n, i); if(n==1) count[powerup-1]++; - else + else if (n==2) count[powerup+POWERUP_LAST-POWERUP_FIRST]++; + else + count[powerup+2*POWERUP_LAST-2*POWERUP_FIRST]++; } // Now make sure we reproduce the original weight distribution. From 82fb4fda272a68ca27f7f23df668b49731180292 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 26 Nov 2023 22:36:27 +0100 Subject: [PATCH 055/188] Remove unused/useless bowling ball code --- src/items/bowling.cpp | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/src/items/bowling.cpp b/src/items/bowling.cpp index ca4372ec277..d5914878b46 100644 --- a/src/items/bowling.cpp +++ b/src/items/bowling.cpp @@ -85,22 +85,6 @@ bool Bowling::updateAndDelete(int ticks) return true; } - const AbstractKart *kart=0; - Vec3 direction; - float minDistance; - getClosestKart(&kart, &minDistance, &direction); - if(kart && minDistancegetLinearVelocity().angle(direction)) < 1.3) - { - direction*=1/direction.length()*m_st_force_to_target; - m_body->applyCentralForce(direction); - } - } - - // Bowling balls lose energy (e.g. when hitting the track), so increase // the speed if the ball is too slow, but only if it's not too high (if // the ball is too high, it is 'pushed down', which can reduce the @@ -119,19 +103,8 @@ bool Bowling::updateAndDelete(int ticks) } } btVector3 v = m_body->getLinearVelocity(); - float vlen = v.length2(); - if (hat<= m_max_height) - { - if(vlen<0.8*m_speed*m_speed) - { // bowling lost energy (less than 80%), i.e. it's too slow - speed it up: - if(vlen==0.0f) { - v = btVector3(.5f, .0, 0.5f); // avoid 0 div. - } - // m_body->setLinearVelocity(v*(m_speed/sqrt(vlen))); - } // vlen < 0.8*m_speed*m_speed - } // hat< m_max_height - - if(vlen<0.1) + + if(v.length2() < 0.1) { hit(NULL); removeRollSfx(); From be76b664acd82513f863e5e71df2e22c11c2d57a Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 26 Nov 2023 23:19:28 +0100 Subject: [PATCH 056/188] Fix handicap skid multipliers for purple skidding --- data/kart_characteristics.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 80cadee48d9..7de9bc5b48e 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -464,7 +464,7 @@ - + From 2644c5af31b47f8840f7b3a630aa93dea353476b Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 26 Nov 2023 23:43:57 +0100 Subject: [PATCH 057/188] Add a drifting skill in the kart selection screen Also tweaks the display of mass to exaggerate somewhat the difference, and correct the acceleration display scaling after the recent tweak to engine power. --- src/guiengine/widgets/kart_stats_widget.cpp | 23 +++++++++++++++++---- src/guiengine/widgets/kart_stats_widget.hpp | 1 + 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/guiengine/widgets/kart_stats_widget.cpp b/src/guiengine/widgets/kart_stats_widget.cpp index e4625182278..1c1bb8658d6 100644 --- a/src/guiengine/widgets/kart_stats_widget.cpp +++ b/src/guiengine/widgets/kart_stats_widget.cpp @@ -98,9 +98,9 @@ void KartStatsWidget::setValues(const KartProperties* props, HandicapLevel h) // A value of 100 takes the whole bar width, including borders. // So values should be in the 0-99 range - // The base mass is of 350 ; 350/3.89 ~= 90 + // The base mass is of 350 ; 250/2.78 ~= 90 setSkillValues(SKILL_MASS, - kp_computed.getCombinedCharacteristic()->getMass()/3.89f, + (kp_computed.getCombinedCharacteristic()->getMass() - 100.0f)/2.78f, "mass.png", "mass", _("Mass")); // The base speed is of 27.5 @@ -111,9 +111,9 @@ void KartStatsWidget::setValues(const KartProperties* props, HandicapLevel h) // The acceleration depend on power and mass, and it changes depending on speed // We call a function which gives us a single number to represent it - // power/mass gives numbers in the 1-10 range, so we multiply it by 10. + // power/mass gives numbers in the 1-11 range, so we multiply it by 9. setSkillValues(SKILL_ACCELERATION, - kp_computed.getAccelerationEfficiency()*10.0f, + kp_computed.getAccelerationEfficiency()*9.0f, "power.png", "acceleration", _("Acceleration")); // The base nitro consumption is 1, higher for heavier karts. @@ -123,6 +123,21 @@ void KartStatsWidget::setValues(const KartProperties* props, HandicapLevel h) 18.0f * kp_computed.getCombinedCharacteristic()->getNitroMaxSpeedIncrease() / kp_computed.getCombinedCharacteristic()->getNitroConsumption(), "nitro.png", "nitro", _("Nitro efficiency")); + + // The base time for the skidding bonuses is 1, 2.7 and 4 + // The lower, the better. We add 4 times level 1, 2 times level 2 and 1 time level 3 + // to obtain an aggregate value. + // We proceed similarly for the skid bonuses (4.0, 6.0, 8.0 by default) + float aggregate_skid_time = 4 * kp_computed.getCombinedCharacteristic()->getSkidTimeTillBonus()[0] + + 2 * kp_computed.getCombinedCharacteristic()->getSkidTimeTillBonus()[1] + + kp_computed.getCombinedCharacteristic()->getSkidTimeTillBonus()[2]; + + float aggregate_bonus_speed = 4 * kp_computed.getCombinedCharacteristic()->getSkidBonusSpeed()[0] + + 2 * kp_computed.getCombinedCharacteristic()->getSkidBonusSpeed()[1] + + kp_computed.getCombinedCharacteristic()->getSkidBonusSpeed()[2]; + setSkillValues(SKILL_SKIDDING, + (56.0f * aggregate_bonus_speed / aggregate_skid_time) - 90.0f, + "android/drift.png", "skidding", _("Drifting bonuses")); RaceManager::get()->setDifficulty(previous_difficulty); } // setValues diff --git a/src/guiengine/widgets/kart_stats_widget.hpp b/src/guiengine/widgets/kart_stats_widget.hpp index 1fe13c4747a..fc3adbc5437 100644 --- a/src/guiengine/widgets/kart_stats_widget.hpp +++ b/src/guiengine/widgets/kart_stats_widget.hpp @@ -49,6 +49,7 @@ namespace GUIEngine SKILL_SPEED, SKILL_ACCELERATION, SKILL_NITRO_EFFICIENCY, + SKILL_SKIDDING, SKILL_COUNT }; From f5975e293e3424ce90d91549454cc89286dfb5ce Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Wed, 29 Nov 2023 23:40:37 +0100 Subject: [PATCH 058/188] Reduce visual lean for small collisions --- src/physics/btKart.cpp | 2 +- src/physics/btKart.hpp | 5 +++++ src/physics/physics.cpp | 4 ++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp index 91e23f8c461..ae22b535445 100644 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -570,7 +570,7 @@ float btKart::getCollisionLean() const if (m_leaning_right == false) lean_factor = -lean_factor; - return 0.35f*lean_factor; + return 0.35f*m_leaning_factor*lean_factor; } // getCollisionLean // ---------------------------------------------------------------------------- diff --git a/src/physics/btKart.hpp b/src/physics/btKart.hpp index 9de4dce82a1..d894fc17d36 100644 --- a/src/physics/btKart.hpp +++ b/src/physics/btKart.hpp @@ -80,6 +80,9 @@ class btKart : public btActionInterface /** Used to know the direction of the visual kart-on-kart collision lean */ bool m_leaning_right; + /** Used to know the strength of the visual kart-on-kart collision lean */ + float m_leaning_factor; + /** An additional impulse that is applied for a certain amount of time. */ btVector3 m_additional_impulse; @@ -245,6 +248,8 @@ class btKart : public btActionInterface // ------------------------------------------------------------------------ void setCollisionLean(bool lean_right) { m_leaning_right = lean_right; } // ------------------------------------------------------------------------ + void setCollisionLeanFactor(float lean_factor) { m_leaning_factor = lean_factor; } + // ------------------------------------------------------------------------ /** Returns the time an additional impulse is activated. */ uint16_t getCentralImpulseTicks() const { return m_ticks_additional_impulse; } diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index b2715d6cf51..b93dd177009 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -577,6 +577,8 @@ void Physics::KartKartCollision(AbstractKart *kart_a, impulse_time_factor = 0.6875f + velocity_value / 25.6f; } + float lean_factor = std::min(1.25f, std::max(f_right, f_left)) * 0.8f; + // First push one kart to the left (if there is not already // an impulse happening - one collision might cause more // than one impulse otherwise) @@ -590,6 +592,7 @@ void Physics::KartKartCollision(AbstractKart *kart_a, (uint16_t)stk_config->time2Ticks(kp->getCollisionImpulseTime() * impulse_time_factor), impulse); right_kart->getVehicle()->setCollisionLean(/* towards the right*/ true); + right_kart->getVehicle()->setCollisionLeanFactor(lean_factor); // The kart rotation will be prevented as the impulse is applied } @@ -605,6 +608,7 @@ void Physics::KartKartCollision(AbstractKart *kart_a, (uint16_t)stk_config->time2Ticks(kp->getCollisionImpulseTime() * impulse_time_factor), impulse); left_kart->getVehicle()->setCollisionLean(/* towards the right*/ false); + left_kart->getVehicle()->setCollisionLeanFactor(lean_factor); // The kart rotation will be prevented as the impulse is applied } From 0adb73da375a838f1e2806cb689e1cc8084293e7 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Thu, 30 Nov 2023 21:53:16 +0100 Subject: [PATCH 059/188] Introduce the 'mini-wish' powerup - Add new logic to have different states for the held powerup and to display those states to the player. The first press of fire locks the mini-item choice, the second press fires the selected mini-item. - Add new icons for the powerup and to show these states - Add a new mini-zipper effect. The boost is also of zipper type. - Temporarily use a short-duration gum shield and a normal cake for the mini-gum and mini-cake effects. - Clarify the error message when the game fails to find a powerup icon - Decrease the bubble gum flashing time from 3.0s to 2.0s The powerup weights are temporary. --- data/gui/icons/mini_all-on.png | Bin 0 -> 47398 bytes data/gui/icons/mini_cake-on.png | Bin 0 -> 47827 bytes data/gui/icons/mini_cake-selected.png | Bin 0 -> 44328 bytes data/gui/icons/mini_gum-on.png | Bin 0 -> 47635 bytes data/gui/icons/mini_gum-selected.png | Bin 0 -> 42867 bytes data/gui/icons/mini_zipper-on.png | Bin 0 -> 47880 bytes data/gui/icons/mini_zipper-selected.png | Bin 0 -> 40755 bytes data/powerup.xml | 228 ++++++++++++------------ src/items/attachment.cpp | 2 +- src/items/powerup.cpp | 202 +++++++++++++++------ src/items/powerup.hpp | 4 + src/items/powerup_manager.cpp | 126 ++++++++++++- src/items/powerup_manager.hpp | 15 ++ src/karts/abstract_kart.hpp | 3 +- src/karts/controller/skidding_ai.cpp | 8 +- src/karts/kart.cpp | 27 ++- src/karts/kart.hpp | 3 +- 17 files changed, 442 insertions(+), 176 deletions(-) create mode 100644 data/gui/icons/mini_all-on.png create mode 100644 data/gui/icons/mini_cake-on.png create mode 100644 data/gui/icons/mini_cake-selected.png create mode 100644 data/gui/icons/mini_gum-on.png create mode 100644 data/gui/icons/mini_gum-selected.png create mode 100644 data/gui/icons/mini_zipper-on.png create mode 100644 data/gui/icons/mini_zipper-selected.png diff --git a/data/gui/icons/mini_all-on.png b/data/gui/icons/mini_all-on.png new file mode 100644 index 0000000000000000000000000000000000000000..ea6bfbedf1a6edd0283bce57af86140e2ea487c8 GIT binary patch literal 47398 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®gkKg{ec6!iwMk^*e z%uc9Lugo}VW)J%$a)zM{V?u72!qU(i(!`{9*}|+DpU}`L1vVH7MM3C|MEoA;s(Mm$ zYT3L=6>+Lf?^TGwtH14<5bbXk+$I%Y1h@ZY4SbhTbNXrTTBY&yc)mnp<&;*+XV2te zUaT9>Ci-%a%Bu5cY$Ub7{+mNjSCCV&I)2J@7e$8 ztGb`Z&F2Q(`Ac2zU@6wE5bLo+QHXTlUBIkr0e|y#_YOI`)o3`m6O(J(@Y0{vNKoS9 zkYiD$#L^?r{I@K|xu2Ck(Hn+}eBGb{Gf}<-NseAe=@Y zA!+5&6e`QfrS%?IxNk7)e*nC+Uux+VX!mK>F+D^o}FLz$H83$ zKFWUPQEi6`YZ9w#pz~x3sT0a;LwL36+r@Pl?Fk3=5Jn^{;h~0z(-wIwwvdq+>SGO}7Wts6Cx(UTkYkDK<>nPfYDm2I z<^Xj>P3oJ0pKT}1+aav!a3)4!@`x-lg>`0t@v5Ulib|a zh?sdF!AnDBn+RRpG0FbCn6E3UVU$fyvG={}`RP6f7JN}_N)h7e_4Fuah`{!gBnb7T zL3IPOwt1e%h31Dm3Qcf$E?+CSfwqH6Y~&ZzLXKWWP|)J>urjOmHIlb@P<|IMLl zwbk<)5>F^=#$BQGeHW1$&?qZV0_kOm+SRw#hb@J`4R(f@4T&={NI-f_)FoT#m90UW zQVPXoSp$C8<=tVGzj@?-^Xb3IuX>^nAnH}cp3z`5rE-?&5ttOvdM79=nc%zrz_jyx zOLNKoMZo`#OX}=~i#GU<<-NDfM~)Q6WRf=)HnT%4VjrDHY^)eyxGn!01ZS}r>-P!Ls~61+W9``a>kXbQ^+ zw4G=$?(rw$KhM=Z0EoB4F8&^fja)U&&z z=!WsD%K%yO0hw`Sk@pM!6uHLk-T^^1hDRN^J80NkiuH!*Zt?{N=}=(z3U!fKIBe}a zUpU9dog>7bun#8P(006o$})N|dvMYoF$<6L8A(a}*dvZ! zy)GM1GLQzbiT3zDf{S|7(=@G61;q6dW6Vp<`NHr`K*G_NDG!tuc8!wfLHU^~n}UML zzgCu?Io$Ft2{uRryCvTHZlRQ2IpV$PKXgox?NG-^ho1&wtbmMqP0)fNw{2}@-YSD| zfH7NcGN}Sb@7NgkT?Es|yv|v}?Y%MYjdSmg!JeHRqeGGxwRz>@g2G8vHuCOGa+h-;Zm~ zg8mroXVYZmhifjD?I&8NMIBv5J*Pit2gq?47B2WZyK&!@+^$G>k#yhPPKSn=0vbgf*LhMl3qZ3VAd~b$)yCSpN4rjLE39Q6}?j{E= zDn!kMsaiJ^vPi_uX!|pjaEAGEqc|j{PKK_D9dmOBGH$HZycNU`nsxq7ZEE7Yx0iPiofB0(?Y8s-; zTcbaTiRzeObWxzA^k|BTAPI$O_Kd+|U3IOIOgUc^3cbQHDyoILY>U%d`?qp8BI>TM zF!u-iwjabVMajXg7k8q4({*meT`Uix(Om^QkA3X>H7Jb#ns?uO?j6&;2|;Y{41afV zZMWEHV9~GZZ^LGY=1`IN^)7-eiNb!o3;Iz9@%XsC^hEvPr_Y<)!Pi050QjG!E*zp3 zcxlQrvt4zR7wnmxuj2}>qP*-=*&Fmh)MKU1rH~7RIDf8*&b{oP=MO?3)=Oe| z>R(DV#f>LvHpSSJM3pwo6- z5A6MLmMXbl-3M$`NwU&RqxxYW#H+z-Z+TP=k(_S!{u-~eK5@tTod&ymzYA%J4C`!= z7oY&YWcL~Jb5Jk+dXp|pUUr0a$>bL)r=WAzZ8Jk?5LyBcFB1J^TuMDnxSv4e)PU3L z-xK30O@U!N%$g7?83}M8r%3@~?4v6fhn72P9G~pjd3!=~PVU#xCv$tARHMlCp9DmUIlZlp^uCs6n=l zv$a#XmOp=gysf_lje9MnB}G$y=e73subdbXNUw-uRj==Onb7-B2qMzz|0wm{{AU;l zm7OEtW@0~trhk_E+?t`VnP{v^(L@3@4mCB+j#l=H_54X_Lwe4-p>XXstYW_Rzh!5D zu&SVJAl=e;v%aHS29~dsMLxK~B+^P6HZR9gQ*b!GOU|N^xbObMPrR$Hm)Y@1fA~sv z#^x~B`uy|DWM_!=(^fGGoPJ^Bh0*>Ui9SX0B<#UNmRel|d|*~gW-!*s^|Mgzi05yT zVm+Xe#67uF)PmguU!J{}44nv-h=?0#$jHh}EK~1m=!~O)f+HtqP|q2zCih5Lh|xj8 z6p8Arhq)=ekTUqfW37cg;IO)Vd)*AbtFgC@+SQ%r0nmUX&=r%4>G6dys1j8hGP@N8 zZDsAcp6(Hx4ETQQbE&>-pLB#58%9Qf^;{WJC+?2%u{JQQtQSQ`CkX&c99EFU~z_-wJ#Ovw zg1_vr9Idy`&zSzHS+Exdp~G{AE81G2+k6XcfyulY1~2nv&u*4F7yGX79eJMLb6)u9 zjoux$ef&KNK1j-1H?GPh9Rr8=N;)v*j$@~;*N_1!GLM=HH)?20bd5=+dw4~=1;yV< zH?caXGo$zNVlU499LDjXspB5NFCOY&^KR=(val5SZC|rcIkmlp9kQ*Q@?z)l+l<)Z z<&>pN$a$^wRR2-MO~bw@lukK)ep{Ht?bYko-I@x2(=49bzt{G2&d)5?Ib~<>vi|s% z!L-9=CK?$(-WPsR@BfxU^;^gK0%Cmt`R09cWmw#O`kY*%G*{3U1q}&vt^rH%Bc_zp zacg~Oh~n;0!+13Xy&n8LPQ#rc&Vnq ziFb-0N%!fu7Ju2D;;Y*YEN5Qbf0b@(b3v)svg-~=>6nnlGP02H_%~TIFIh4@Nk*?$ zDCfr!<=fa%TkW{<0izQKQa`waUTZM()&RuI#9=^8l4woY-`l=5mv5rZ1UN{tcjMLM zGSL{d4>=MA^--Dh&ro5%V{Kw}OQZ}nz3HK0i0j#|r-^|T^>7S*Z{NB9k$#cUvJYiC zg~|-@b#&X6QD8Am+zP)AwP!dNSm6tCHpKglTd)!!ANjG4>;-+%N!QB3^><&}x$RF) zqKpiIu<@^$g<{}BUvGOTbu6ex-qj3Wb)OCy4aDP9uX3DPF`VSMRjS3p#nb)%u`u+L zOn1MbkmbHf!hHNmsrTXhrmC-(GTF+khyP*B&Bn7V#s?Z>Siz{?v-iO~Py44dD;s6^ z^Kobo;q#``U^6wd_~8tLeQMu-M!cN(1$~=lw%IR8^iV7c&Dt8)mhO>mXDN}rxHN)* zRHoPB&5M)BFwOAGCK*Sx8X;Cx>@ZCEH@3-RveVxvE2x_taJuf0bhlYX#t}0nqYCv+ zD&kH9;-oyS25xRX82kM$OeZQmYo$=P@+C2gBhaGC3Vxv2ivZ`px9!(E^Pw5v-hY%m z9f%plwR4zPFRLs^x|x>uiY9BMsK0LO*Uw4_FWhn6V-phw0a`K`CJBqH@1lR`XL@Q- ze65>pzfX$HmAn?cEJjhK-(Go1-vz_7|0O$z`^i0QpzSo&i|#>4pSD2%>~7Ptc>=Gal0WCQy67)3qgJDY<##tg(JWEZ;~eLznt5(`?LF|EcwxI<;Jl zXOMIcrtLWkL$(X3s$eKpbW08 z=M8GOd1;pLC7=eaGKxh z>>Mwf=ohY1(tLeO52~sIpp!!sMO-}RJ)vw&RDErKoT@hswTe3?tE(?UY|-?@#t|-I zyL)h+Vp5k#n1g@!*ZJ&f)#PrUgPTz?1Pfu){3`5m0%Nh8lGThAj9Zh90RkUNOzq4W zV{IoC?Wohr*biB<7 zv7CRljr|9dh$leP=`-m;AMKX73F6WC9l%gW0}=Gvq`yHQ5qfA|XCaJy;*9t~Z^CHYdvlNi(HILbK1TkdFM2JT1g?Br2kr$@HDHa zDRmFi-xWSO{Uou)*@t}ABj=}sfF{{}tzmgAsse&wa|G?@wq7NU-q~{1w5uG!fBcI3 z5=)ExM#$RvOAPBNlwTBDPXDd*92LdN+UDQ+ulE8hSdS!DTwHpF0@y^Ty%H$g7D4h# z76m$mXzZ_9W7Twky(xi^srSIG`YJKbND2iVAg~3&ru22lASp1qK3k0&XE3oOKzU!Eh_wExauwaC#aN5N_|$55oRJN+`sk;948c)l5Pjo54Ilm*jbgr3j$s zjtB8z*Lm#>^?X1p`7)A(qz`^SNPO%=13_=><(|t@Ud;_=Xj=N-Cz%j8N*}xU{C3YI z`N=BM)Ce~GuW$0tef3)#&sst6Bf&(&9D#Xhh|$x(?mn!P+iD8;1CG<4R=^M2F0ET_M0t=>9rGeY?tEBai)ZjfLSKzrNnB z>rX$fCG5lWN9X3|8HpRSYL2hmEigZC{VlY+UIbg$;_RYcZSdk|6D+E`C1%t{qSQ}% zsV-db(0nf9`Xm>IPS&pWuw2tEx5J%+RI#ISi6TskwWXNoG%aDp?g5J*LNa+b(b{c7 zK$7Ms8u$hLf^VV7LGtFw^8NO!Dc>kqZhD*r?VFcUh+kfzUAQ;F|H3-ErJ-%doQ969 zad?1cmLYfX#S7`-sauU^qwt;XuKLj?Monhmfw*4W7j1vNJ63AGS~aD_kHb(r5xzoE zuyVHMiA%QmB8JTZ_+0*_(g~vRU*r%~*!n?f)%Kq+i_qP50mY%HlLT*jp)XNIe5wu=#%qC~;CUW2TrATfN zC!Oqj7vg{WEMuqH1E1M~LZ$R($LZt@(Hh~yKQ?b_1cJ|BBJGC>2s&@-PW~tkX(6|K zD2J)OG0ZN)g)c6;NT|-Uv>W zK|VpFW1})Xl=>*!h$3aiF;E1XvuECtua||xi-U`sfEI=+%zklYb#=5NyOm=3N^m`) zs(&tSFB>vR&ugNKm)<%@uABE7X?u3n4%#br)j4{0nJyP-UE1U+y}US~U zhjVk8?viy_@ye=?7euM=`t9tNon)8yznfpa;18W8)Fmi#19H4dq{%L#yeL&yS!`t_5^y?S@#&6hQQrAN7jf* zSHr6WNeHN$D70F~8n>;dX_sVK&vi(sU^lPajiIRIUX3z|*b!Rf%+~wi6@(MyF^l^p zlW^!4nW+ssIV9KsOi&yN($Yhj!X}cF2QqX}K;oaR=#&I;Q`CS5Nd%-E%;FTWCUIK> z$lrzHqI~(>fkME|>W(5j8Sde;CHiV&1+XouKYs)Q#FQG(j58JYtxU2QW}35I-DBhy zvr@d|%W+fX69d+3y+fK5EE&Ah$A@>Ok7y-1^*?dLYU^?Ny(-E?85_x?otSEn_I51s zIF*s6&`q5^T{qI0c4^{Yupvlh`82cMCAQnS9^0w<2yI@M}Z=<;qx@MjYq&_18T9yvO0*Ni~Q zf;AWP!Uor z9bQ1b)d0c7=ohC!AiZdi6n2?a3qkK&Vg+>c*T61JYtD`zEr!_}*rjmHZ?^9Z(#lb- zfx8WoW!?fx-cr+&RE30ZLQc_jyivlI-l{o#r>|XUn+fn!919%o46qz ztqFC1+zM>0L*R;29%XY%@UbT?yqlCklB*;T9jg{s=8{+);L+JW6F53ST3Fo+oy9fVzshhzxEJr%G8u3EI?d$nA8w>O*$|Tf$I~vm za6E9s8i9XexntOKrPbMU97t;jy^a6TVx0m`(K~qZQ^!@^=ten-Eu8qC2+n(#TRO~s zzcKGN=cC6S0<1@%a4B%+f71lwIGy5k3D%v8!f%rl^~Y1Gl{36n55J{08%|3*%9MYu zCv7%@zG$?{Cb^^qAlxiUiHDO?yYxX(MbOaAqIQ$Qm%X8}1C>3zVy$;z!lRyGhC)N5S2s z@B5z3q&*)*>RRBBA#eP|Sk<|7{Uv*`2<*KLxz)FoBdLWJg@n1{%W;s5M=W_JW8yaG z(@qH^z1v8cI|t(4_crXkT96K-g%yLAwi{}FF}n<}^f*=$gLk$ing%?T)AVX0`h% zMThra{MqF}llP!egWNku%nBPd_J^Ix)9^T=mUj0k3jE*Yzi?t_@t4Y#?dr3w?p;4XNJS7PI0DB^#IOk2Cq3v<4K)@evKm%M+{- zgr(Ql-+l*Q!6WMrfcGu5oB2ydyZ3gq^Zzaz7&;K)`&~P}OT*rO6=40;I7{p3*k4%p z8@6;=!TP#VJllR#;uIMmFsfbths{n3K0in3TaeO~?oX*D0TaPJjh6P<%t*4x4XHaG zo{^TJg1XLlaXm(E@;rX?Zb;TbNJA8#>vliG{Y!^Bi@vn&l+E&zCkk=I35`1>)gBz( z|5A*P_rKzGU84-lW{)gkeHlwni_zS>nZ%KU)`-zOhF8}{Q?b0qz2N9f*CTebot+9C zzUfg@O<1e7szVyg+RlC(RrlZIQoolj1c=}liD87uw7si7aiu9NC~1>Er8d^w^#QQaI~@)m}dqZ*@OM zTrjb!YaO#<3d@@&WbN9EGxT_%rZ@6cTpHa|?vf8lG)uUk14@zB`a4aY@0z{Nt|;!J)B4#Xt+Mlz2sW2)_{s>DQmyLwWy9fJe$>@3hir@LP^1(bI{pM$U1`Q93bd_;p&4vHJ2=*}V z>;&FdT(4Cr@l-}?qgYWfuxzmPPyU+IC2w@ZutM~39$)q!)`e6q-ycE}GCk?H$T@0# z-?0atdK0%ighh7-8eT4Qwxzs2GFZJ`b*Q8(ftexa3gnhId9uNtuGcKXZf1`nvb5as z%-EH@Ra*jB6d5H%L!#Yr=bYyCQ&CbJdo5~u0}asfmw+(NZWY;7+BX1^Zr z2y67@`JD$HKyiaHl4g3>zm`ZNMR0jbDV{MLCum|~oibGqJ`{#tQI{>0wfSAchAUSv zztI1_6FDVZIGxO!tfk(17G1xTH2=Qf=A_`JoJ*6Y$W41+JC&Sa>|iiu`qdXrwT>Jp@dt=(D!OUa(_MQ zCS9n+6s#%W@73aUKe|&z>X_jS&r=PGGLA(7bx_N&4kRTvL7&rhL)hbRFRE$!;h2x7 zLsDcwk>t+xGwgnWf?#0pnjpccZB=W`nw5e6%J_xEP=^q5ql-BZD#H8j8EX+^Irs}xF(a<^G?uUUzPaVwv1RC| ztE7*RZj`%^D6A`@b)%td$Sc%C`joP@i`)Dkci-Ee)d1y@9RX66YsWQ0o60f!$G+$V zs+a;iS=OKk*Fk;CB$R){1uFL<1+hxXtsdX>B)(P#>ly^T?IRc?d z+R~}wXw%fEABjf(vMoh2TjFfH1=hL+2CIj43Kn#oY|r2-P_F&6X#aS3^t{%u8ntKA%fAb~bsSi5ZWHa*9AlgDBY1f*drksHP9d0goE)C9>gA&=+$!w2xoT)Nr_ zYm5QG(;D&3(l_v-Sb#MKq+{(uIJw*gJ!gI>4R8-orCD?euvNP9HRwSL!wCRbmHUM} z4OE48fWcn9N7LlrYuCdW-rpPpAcT=(H(`z|qP`%wq&r~!HPFzJYOR>+^0hYKWOpjw z9dbejCy@L28Tigb{o8ui3nf9{yhz;Z1VP3If%7p)LiI z?{C(O0Lk+3U6(E$#MRWQ(9dA#^uC_`Q?Cho({KKVYC_}!SPmZE*cP*T7)aK>d~ha* z2Col{K+Mr!Z;4n~ZsUtuz=CH=Z>(KC!W1f-j0?`L=g+}2k2hVuU=RE5L;>Xn*(={4 z@V7GQaI_y%a+@>1SD7$i$<8Ms{iibkcPx-|q9;P-W~9$82ITDQO6YPTfHYQQPeEiq zzgvG+NM?WLaWox`WwjB5!Ho+sftaYToT#tyhA$^dD@tT&{UpV$uHaY^^(M>dl?zH_ z(&TZ*Bpo}PE&y;cM-yb6#}^evg!u&%;sFqpk$!UcO+s=nHv%{0ZbnvC ztubI|7(LRcn-O-o?IJyxK{05NDK(RHK5*$&z^WpMUZsB(7tOiH*H8XNZFw~UwF`?n zED*0RDcWCW2<#fP+`m5)b_$Pxmo~Lw;Rg|701zmVzCzkZGdg0_wAV8?)NpB9#-L9w zC-N9DMU0+Ta;-VCN5_=^La0{Px?=wC|1u|~|84|wqWCdb`K3C3TIzv!{aNit?Nmh# zBgDp|`70ve{Amf`4zu!GPV}<0+f!LO4nbM%mYqm(YJzI z+ElUq@Bs*dWJv?GHWkUJ?PQk|-DtCNl_Y8r$6sG{{~tfhb{#&Wk)+i~s-t literal 0 HcmV?d00001 diff --git a/data/gui/icons/mini_cake-selected.png b/data/gui/icons/mini_cake-selected.png new file mode 100644 index 0000000000000000000000000000000000000000..77523e16869e2b677192043e08ded41660948532 GIT binary patch literal 44328 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 literal 0 HcmV?d00001 diff --git a/data/gui/icons/mini_gum-on.png b/data/gui/icons/mini_gum-on.png new file mode 100644 index 0000000000000000000000000000000000000000..86377b3e4b96fd6fcdceefaf1ec2da37482caf84 GIT binary patch literal 47635 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 Tju5`T0Dy|3=9^l1%kcjLT^Dhs literal 0 HcmV?d00001 diff --git a/data/gui/icons/mini_gum-selected.png b/data/gui/icons/mini_gum-selected.png new file mode 100644 index 0000000000000000000000000000000000000000..e69739a1cac08d44365638f4ff648413d1eaf71e GIT binary patch literal 42867 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 PfFCV&J+%)i){*}MZCGp{ literal 0 HcmV?d00001 diff --git a/data/gui/icons/mini_zipper-on.png b/data/gui/icons/mini_zipper-on.png new file mode 100644 index 0000000000000000000000000000000000000000..db8bb2f2d35637f2ba69486c3d237c3021b90e7b GIT binary patch literal 47880 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

R^q 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 + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - + - + - - - + + + - + - - + + - - + + - - + + @@ -328,9 +332,9 @@ be more useful. Likewise, it doesn't matter much if e.g. a switch is really better or worse than a swatter. Currently this is only used for races. Other modes would likely need their own lists. - bubble cake bowl zipper plunger switch swattr rubber para sudo electro anvil --> - + bubble cake bowl zipper plunger switch swattr rubber para sudo electro mini anvil --> + diff --git a/src/items/attachment.cpp b/src/items/attachment.cpp index 2c95e361561..e10fd89cc90 100644 --- a/src/items/attachment.cpp +++ b/src/items/attachment.cpp @@ -643,7 +643,7 @@ void Attachment::updateGraphics(float dt) m_node->setScale(core::vector3df( wanted_node_scale, wanted_node_scale, wanted_node_scale)); } - int slow_flashes = stk_config->time2Ticks(3.0f); + int slow_flashes = stk_config->time2Ticks(2.0f); if (is_shield && m_ticks_left < slow_flashes) { // Bubble gum flashing when close to dropping diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index 3bd70392908..809db329231 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -63,6 +63,7 @@ Powerup::~Powerup() void Powerup::reset() { m_type = PowerupManager::POWERUP_NOTHING; + m_mini_state = PowerupManager::NOT_MINI; m_number = 0; // Ghost kart will update powerup every frame @@ -176,6 +177,10 @@ void Powerup::set(PowerupManager::PowerupType type, int n) case PowerupManager::POWERUP_ELECTRO: break ; + case PowerupManager::POWERUP_MINI: + m_mini_state = PowerupManager::MINI_SELECT; + break ; + case PowerupManager::POWERUP_BOWLING: m_sound_use = SFXManager::get()->createSoundSource("bowling_shoot"); break ; @@ -224,6 +229,32 @@ void Powerup::setNum(int n) */ Material *Powerup::getIcon() const { + // The mini powerup has multiple states, + // and each state has its own icon. + if (m_type == PowerupManager::POWERUP_MINI) + { + if (m_mini_state == PowerupManager::MINI_ZIPPER) + return powerup_manager->getMiniIcon(3); + else if (m_mini_state == PowerupManager::MINI_CAKE) + return powerup_manager->getMiniIcon(4); + else if (m_mini_state == PowerupManager::MINI_GUM) + return powerup_manager->getMiniIcon(5); + + // FIXME + // This duplicates the logic to determine which powerup would be + // selected by the current cycle + if (m_mini_state == PowerupManager::MINI_SELECT) + { + int cycle_ticks = stk_config->time2Ticks(0.5f); + int cycle_value = World::getWorld()->getTicksSinceStart() % (3 * cycle_ticks); + if (cycle_value < cycle_ticks) + return powerup_manager->getMiniIcon(0); + else if (cycle_value < 2*cycle_ticks) + return powerup_manager->getMiniIcon(1); + else + return powerup_manager->getMiniIcon(2); + } + } // Check if it's one of the types which have a separate // data file which includes the icon: return powerup_manager->getIcon(m_type); @@ -404,65 +435,69 @@ void Powerup::use() stk_config->time2Ticks(kp->getElectroDuration())); break; - } // end of PowerupManager::POWERUP_ELECTRO + } // end of PowerupManager::POWERUP_ELECTRO* - case PowerupManager::POWERUP_BUBBLEGUM: - // use the bubble gum the traditional way, if the kart is looking back - if (m_kart->getControls().getLookBack()) - { - Item *new_item = im->dropNewItem(Item::ITEM_BUBBLEGUM, m_kart); - // E.g. ground not found in raycast. - if(!new_item) return; - if (!has_played_sound) - { - Powerup::adjustSound(); - m_sound_use->play(); - } - } - else // if the kart is looking forward, use the bubblegum as a shield + case PowerupManager::POWERUP_MINI: { - Attachment::AttachmentType type; - - if (m_kart->getIdent() == "nolok") - type = Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD; - else - type = Attachment::ATTACH_BUBBLEGUM_SHIELD; - - if(!m_kart->isGumShielded()) //if the previous shield had been used up. - { - m_kart->getAttachment()->set(type, - stk_config-> - time2Ticks(kp->getBubblegumShieldDuration())); - } - // using a bubble gum while still having a gum shield - // In this case, half of the remaining time of the active - // shield is added. The maximum duration of a shield is - // never above twice the standard duration. - else + switch (m_mini_state) { - m_kart->getAttachment()->set(type, - stk_config-> - time2Ticks(kp->getBubblegumShieldDuration() - + (m_kart->getShieldTime() / 2.0f)) ); - } - - if (!has_played_sound) - { - if (m_sound_use != NULL) + case PowerupManager::NOT_MINI: // Keeps the compiler happy + // Lock the selected mini-powerup + case PowerupManager::MINI_SELECT: { - m_sound_use->deleteSFX(); - m_sound_use = NULL; + m_number++; // Avoid the powerup being removed when validating the mini-choice + + int cycle_ticks = stk_config->time2Ticks(0.5f); + int cycle_value = World::getWorld()->getTicksSinceStart() % (3 * cycle_ticks); + if (cycle_value < cycle_ticks) + m_mini_state = PowerupManager::MINI_ZIPPER; + else if (cycle_value < 2*cycle_ticks) + m_mini_state = PowerupManager::MINI_CAKE; + else + m_mini_state = PowerupManager::MINI_GUM; + + break; } - //Extraordinary. Usually sounds are set in Powerup::set() - m_sound_use = SFXManager::get()->createSoundSource("inflate"); - //In this case this is a workaround, since the bubblegum item has two different sounds. + // Mini-cake case + case PowerupManager::MINI_CAKE: + { + // make weapon usage destroy gum shields + if(stk_config->m_shield_restrict_weapons && + m_kart->isGumShielded()) + m_kart->decreaseShieldTime(); + if (!has_played_sound) + { + Powerup::adjustSound(); + m_sound_use = SFXManager::get()->createSoundSource("shoot"); + m_sound_use->play(); + } + ProjectileManager::get()->newProjectile(m_kart, PowerupManager::POWERUP_CAKE); + break; + } // mini-cake case - Powerup::adjustSound(); - m_sound_use->play(); - } + // Mini-zipper case + case PowerupManager::MINI_ZIPPER: + { + m_kart->handleZipper(NULL, /* play sound*/ true, /* mini zipper */ true); + break; + } // mini-zipper case - } // end of PowerupManager::POWERUP_BUBBLEGUM + + // Mini-gum case + case PowerupManager::MINI_GUM: + { + useBubblegum(has_played_sound, /* mini */ true); + break; + } // mini-gum case + } // Switch mini-state + } // end of PowerupManager::POWERUP_MINI + break; + + case PowerupManager::POWERUP_BUBBLEGUM: + { + useBubblegum(has_played_sound); + } break; case PowerupManager::POWERUP_ANVIL: @@ -574,6 +609,71 @@ void Powerup::use() } } // use +void Powerup::useBubblegum(bool has_played_sound, bool mini) +{ + ItemManager* im = Track::getCurrentTrack()->getItemManager(); + const KartProperties *kp = m_kart->getKartProperties(); + + // use the bubble gum the traditional way, if the kart is looking back + if (m_kart->getControls().getLookBack()) + { + Item *new_item = im->dropNewItem(Item::ITEM_BUBBLEGUM, m_kart); + + // E.g. ground not found in raycast. + if(!new_item) return; + if (!has_played_sound) + { + Powerup::adjustSound(); + m_sound_use->play(); + } + } + else // if the kart is looking forward, use the bubblegum as a shield + { + Attachment::AttachmentType type; + + // TODO : make the mini-gum functinally different + float mini_factor = (mini) ? 0.4f : 1.0f; + + if (m_kart->getIdent() == "nolok") + type = Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD; + else + type = Attachment::ATTACH_BUBBLEGUM_SHIELD; + + if(!m_kart->isGumShielded()) //if the previous shield had been used up. + { + m_kart->getAttachment()->set(type, + stk_config-> + time2Ticks(kp->getBubblegumShieldDuration() * mini_factor)); + } + // using a bubble gum while still having a gum shield + // In this case, half of the remaining time of the active + // shield is added. The maximum duration of a shield is + // never above twice the standard duration. + else + { + m_kart->getAttachment()->set(type, + stk_config-> + time2Ticks(kp->getBubblegumShieldDuration() * mini_factor + + (m_kart->getShieldTime() / 2.0f)) ); + } + + if (!has_played_sound) + { + if (m_sound_use != NULL) + { + m_sound_use->deleteSFX(); + m_sound_use = NULL; + } + //Extraordinary. Usually sounds are set in Powerup::set() + m_sound_use = SFXManager::get()->createSoundSource("inflate"); + //In this case this is a workaround, since the bubblegum item has two different sounds. + + Powerup::adjustSound(); + m_sound_use->play(); + } + } +} // useBubblegum + //----------------------------------------------------------------------------- /** This function is called when a bonus box is it. This function can be * called on a server (in which case item and add_info are not used), diff --git a/src/items/powerup.hpp b/src/items/powerup.hpp index bca8063d0da..2799fde58fe 100644 --- a/src/items/powerup.hpp +++ b/src/items/powerup.hpp @@ -43,6 +43,9 @@ class Powerup : public NoCopy /** The powerup type. */ PowerupManager::PowerupType m_type; + /** The mini-state (used only by the mini-wish powerup) */ + PowerupManager::MiniState m_mini_state; + /** Number of collected powerups. */ int m_number; @@ -53,6 +56,7 @@ class Powerup : public NoCopy /** Returns an integer in the 0-32767 range.*/ int simplePRNG(const int seed, const int time, const int item_id, const int position); + void useBubblegum(bool has_played_sound, bool mini = false); public: Powerup (AbstractKart* kart_); diff --git a/src/items/powerup_manager.cpp b/src/items/powerup_manager.cpp index 13ea64b7262..67a592ad0c1 100644 --- a/src/items/powerup_manager.cpp +++ b/src/items/powerup_manager.cpp @@ -111,7 +111,7 @@ PowerupManager::PowerupType "", /* Nothing */ "bubblegum", "cake", "bowling", "zipper", "plunger", "switch", "swatter", "rubber-ball", "parachute", "nitro-hack", "electro-shield", - "anchor" + "mini-wish", "anchor" }; for(unsigned int i=POWERUP_FIRST; i<=POWERUP_LAST; i++) @@ -245,6 +245,16 @@ void PowerupManager::sortRaceWeights(const XMLNode *powerup_node, // The WeightsData can now sort itself wd[i]->sortWeights(); } + +//#ifdef ITEM_DISTRIBUTION_DEBUG + std::sort(values.begin(), values.end()); + for (unsigned int i = 0; i < 3 * (int)POWERUP_LAST - 1; i++) + { + if (values[i] == values[i+1]) + Log::error("PowerupManager", "Two powerups have an identical goodness value of %d", + values[i]); + } +//#endif } // sortRaceWeights @@ -595,7 +605,8 @@ void PowerupManager::loadPowerup(PowerupType type, const XMLNode &node) if (icon_file.size() == 0) { Log::fatal("PowerupManager", - "Cannot load powerup %i, no 'icon' attribute under XML node", + "Cannot load powerup %i, no 'icon' attribute under XML node" + " or incorrect icon path", type); } #endif @@ -644,6 +655,8 @@ void PowerupManager::loadPowerup(PowerupType type, const XMLNode &node) RubberBall::init(node, m_all_meshes[type]); break; case POWERUP_SUDO: loadNitroHack(node); break; + case POWERUP_MINI: + loadMiniIcons(node); break; default: break; } // switch } // loadPowerup @@ -701,6 +714,115 @@ float PowerupManager::getNitroHackStolenDiff(unsigned int diff) const return m_nh_stolen_amount[diff-1]; } //getNitroHackStolenDiff +/** Loads the special icons of the Mini-Wish powerup + */ +void PowerupManager::loadMiniIcons(const XMLNode &node) +{ + std::string icon_file(""); + + // Load the first special icon + node.get("icon_cycle1", &icon_file); + icon_file = GUIEngine::getSkin()->getThemedIcon("gui/icons/" + icon_file); + +#ifdef DEBUG + long unsigned int min_size = icon_file.size(); +#endif + + m_mini_icons[0] = material_manager->getMaterial(icon_file, + /* full_path */ true, + /*make_permanent */ true, + /*complain_if_not_found*/ true, + /*strip_path*/ false); + + // Load the second special icon + node.get("icon_cycle2", &icon_file); + icon_file = GUIEngine::getSkin()->getThemedIcon("gui/icons/" + icon_file); + +#ifdef DEBUG + if (icon_file.size() < min_size) + min_size = icon_file.size(); +#endif + + m_mini_icons[1] = material_manager->getMaterial(icon_file, + /* full_path */ true, + /*make_permanent */ true, + /*complain_if_not_found*/ true, + /*strip_path*/ false); + + // Load the third special icon + node.get("icon_cycle3", &icon_file); + icon_file = GUIEngine::getSkin()->getThemedIcon("gui/icons/" + icon_file); + +#ifdef DEBUG + if (icon_file.size() < min_size) + min_size = icon_file.size(); +#endif + + m_mini_icons[2] = material_manager->getMaterial(icon_file, + /* full_path */ true, + /*make_permanent */ true, + /*complain_if_not_found*/ true, + /*strip_path*/ false); + + // Load the fourth special icon + node.get("icon_selected1", &icon_file); + icon_file = GUIEngine::getSkin()->getThemedIcon("gui/icons/" + icon_file); + +#ifdef DEBUG + if (icon_file.size() < min_size) + min_size = icon_file.size(); +#endif + + m_mini_icons[3] = material_manager->getMaterial(icon_file, + /* full_path */ true, + /*make_permanent */ true, + /*complain_if_not_found*/ true, + /*strip_path*/ false); + + // Load the fifth special icon + node.get("icon_selected2", &icon_file); + icon_file = GUIEngine::getSkin()->getThemedIcon("gui/icons/" + icon_file); + +#ifdef DEBUG + if (icon_file.size() < min_size) + min_size = icon_file.size(); +#endif + + m_mini_icons[4] = material_manager->getMaterial(icon_file, + /* full_path */ true, + /*make_permanent */ true, + /*complain_if_not_found*/ true, + /*strip_path*/ false); + + // Load the sixth special icon + node.get("icon_selected3", &icon_file); + icon_file = GUIEngine::getSkin()->getThemedIcon("gui/icons/" + icon_file); + +#ifdef DEBUG + if (icon_file.size() < min_size) + min_size = icon_file.size(); + + if (min_size == 0) + { + Log::fatal("PowerupManager", + "Cannot load at least one of the special mini-icons, missing attribute under XML node" + " or incorrect icon path"); + } +#endif + + m_mini_icons[5] = material_manager->getMaterial(icon_file, + /* full_path */ true, + /*make_permanent */ true, + /*complain_if_not_found*/ true, + /*strip_path*/ false); + + for (unsigned int i=0; i<6; i++) + { + assert(m_mini_icons[i] != NULL); + assert(m_mini_icons[i]->getTexture() != NULL); + } +} // loadMiniIcons + // ---------------------------------------------------------------------------- /** Create a (potentially interpolated) WeightsData objects for the current * race based on the number of karts. diff --git a/src/items/powerup_manager.hpp b/src/items/powerup_manager.hpp index d74d9b69e4b..6dd53b3ca89 100644 --- a/src/items/powerup_manager.hpp +++ b/src/items/powerup_manager.hpp @@ -148,16 +148,27 @@ class PowerupManager : public NoCopy POWERUP_SWITCH, POWERUP_SWATTER, POWERUP_RUBBERBALL, POWERUP_PARACHUTE, POWERUP_SUDO, POWERUP_ELECTRO, + POWERUP_MINI, POWERUP_ANVIL, POWERUP_LAST=POWERUP_ANVIL, POWERUP_MAX }; + enum MiniState {NOT_MINI, + MINI_SELECT, + MINI_ZIPPER, + MINI_CAKE, + MINI_GUM + }; + private: /** The icon for each powerup. */ Material* m_all_icons [POWERUP_MAX]; + /** The special icons for the mini-wish powerup. */ + Material* m_mini_icons [6]; + /** The mesh for each model (if the powerup has a model), e.g. a switch has none. */ irr::scene::IMesh *m_all_meshes[POWERUP_MAX]; @@ -188,6 +199,7 @@ class PowerupManager : public NoCopy void computeWeightsForRace(int num_karts); void loadPowerup (PowerupType type, const XMLNode &node); void loadNitroHack (const XMLNode &node); + void loadMiniIcons (const XMLNode &node); PowerupManager::PowerupType getRandomPowerup(unsigned int pos, unsigned int *n, uint64_t random_number); @@ -195,6 +207,9 @@ class PowerupManager : public NoCopy /** Returns the icon(material) for a powerup. */ Material* getIcon(int type) const {return m_all_icons [type];} // ------------------------------------------------------------------------ + /** Returns the icon(material) for a mini status. */ + Material* getMiniIcon(int index) const {return m_mini_icons [index];} + // ------------------------------------------------------------------------ /** Returns the mesh for a certain powerup. * \param type Mesh type for which the model is returned. */ irr::scene::IMesh *getMesh(int type) const {return m_all_meshes[type];} diff --git a/src/karts/abstract_kart.hpp b/src/karts/abstract_kart.hpp index 17511b4707d..f285eb19b73 100644 --- a/src/karts/abstract_kart.hpp +++ b/src/karts/abstract_kart.hpp @@ -430,7 +430,8 @@ class AbstractKart : public Moveable * taken from this material (parameters that are <0 will be using the * kart-specific values from kart-properties. */ virtual void handleZipper(const Material *m=NULL, - bool play_sound=false) = 0; + bool play_sound=false, + bool mini_zipper=false) = 0; // ------------------------------------------------------------------------ /** Returns true if this kart has finished the race. */ virtual bool hasFinishedRace() const = 0; diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index 553623ad59e..b3833aad123 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -1262,9 +1262,15 @@ void SkiddingAI::handleItems(const float dt, const Vec3 *aim_point, int last_nod break; // POWERUP_SUDO case PowerupManager::POWERUP_ELECTRO: - m_controls->setFire(true); + if (m_time_since_last_shot > 1.0f) + m_controls->setFire(true); break; // POWERUP_ELECTRO + case PowerupManager::POWERUP_MINI: + if (m_time_since_last_shot > 1.0f) + m_controls->setFire(true); + break; // POWERUP_MINI + case PowerupManager::POWERUP_SWATTER: { // if the kart has a shield, do not break it by using a swatter. diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 3ae79c4999a..35b42af530d 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -2253,7 +2253,7 @@ void Kart::handleMaterialGFX(float dt) //----------------------------------------------------------------------------- /** Sets zipper time, and apply one time additional speed boost. It can be - * used with a specific material, in which case the zipper parmaters are + * used with a specific material, in which case the zipper parameters are * taken from this material (parameters that are <0 will be using the * kart-specific values from kart-properties. * \param material If not NULL, will be used to determine the zipper @@ -2261,8 +2261,9 @@ void Kart::handleMaterialGFX(float dt) * will be used. * \param play_sound If true this will cause a sfx to be played even if the * terrain hasn't changed. It is used by the zipper powerup. + * \param mini If true, the duration and other bonuses will be lower. */ -void Kart::handleZipper(const Material *material, bool play_sound) +void Kart::handleZipper(const Material *material, bool play_sound, bool mini_zipper) { /** The additional speed allowed on top of the kart-specific maximum kart * speed. */ @@ -2295,11 +2296,23 @@ void Kart::handleZipper(const Material *material, bool play_sound) } else { - max_speed_increase = m_kart_properties->getZipperMaxSpeedIncrease(); - duration = m_kart_properties->getZipperDuration(); - speed_gain = m_kart_properties->getZipperSpeedGain(); - fade_out_time = m_kart_properties->getZipperFadeOutTime(); - engine_force = m_kart_properties->getZipperForce(); + if (mini_zipper) + { + max_speed_increase = m_kart_properties->getZipperMaxSpeedIncrease()*0.6; + duration = m_kart_properties->getZipperDuration()*0.5; + speed_gain = m_kart_properties->getZipperSpeedGain()*0.75; + fade_out_time = m_kart_properties->getZipperFadeOutTime()*0.5; + engine_force = m_kart_properties->getZipperForce()*0.5; + printf("mini Zipper used!\n"); + } + else + { + max_speed_increase = m_kart_properties->getZipperMaxSpeedIncrease(); + duration = m_kart_properties->getZipperDuration(); + speed_gain = m_kart_properties->getZipperSpeedGain(); + fade_out_time = m_kart_properties->getZipperFadeOutTime(); + engine_force = m_kart_properties->getZipperForce(); + } } // Ignore a zipper that's activated while braking if(m_controls.getBrake() || m_speed<0) return; diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 6d8434c26c0..c050595ba87 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -344,7 +344,8 @@ class Kart : public AbstractKart virtual void reset () OVERRIDE; virtual void handleZipper (const Material *m=NULL, - bool play_sound=false) OVERRIDE; + bool play_sound=false, + bool mini_zipper=false) OVERRIDE; virtual bool setSquash (float time, float slowdown) OVERRIDE; void setSquashGraphics(); virtual void unsetSquash () OVERRIDE; From 3abe0e0bbaccb956efa6afbb9cbbba3eed00d840 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Fri, 1 Dec 2023 20:45:01 +0100 Subject: [PATCH 060/188] Introduce small ground bubblegums - Reuses mechanics of the standard gum : produces the same effect when driven on, gum shield protects against them, etc. - Uses the same model as the standard gum, but scaled down - Is dropped when using a mini-gum while looking back - Is switched into a small nitro As all track items are currently triggered at the same distance from the kart, the difference (outside of swapping) is only visual - for now. --- data/items.xml | 2 ++ data/stk_config.xml | 4 ++-- src/items/item.cpp | 14 ++++++++++---- src/items/item.hpp | 14 +++++++++++--- src/items/item_manager.cpp | 23 +++++++++++------------ src/items/powerup.cpp | 6 ++++-- src/karts/kart.cpp | 2 +- 7 files changed, 41 insertions(+), 24 deletions(-) diff --git a/data/items.xml b/data/items.xml index cdad77a912f..d856074d132 100644 --- a/data/items.xml +++ b/data/items.xml @@ -9,6 +9,8 @@ handled as one, so list it here --> + + diff --git a/data/stk_config.xml b/data/stk_config.xml index c9b96b18cb6..646663352eb 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -241,8 +241,8 @@ - + small-bubble-gum, small-nolok-bubble-gum, easter egg--> + + + + - - - - - - - + + + + + + \ No newline at end of file diff --git a/data/stk_config.xml b/data/stk_config.xml index 646663352eb..8eef24f4af6 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -242,7 +242,7 @@ items for each item list the index of the item it is switched with. Order: giftbox, banana, big-nitro, small-nitro, bubble-gum, nolok-bubble-gum, small-bubble-gum, small-nolok-bubble-gum, easter egg--> - + - + - - + + - - - - - - - - + + + + - - - - - + + + + - - - - - + + + - - - - - + + + From 24e37862a00f13a0a06660eb423f202a225a3ebf Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sat, 2 Dec 2023 15:33:45 +0100 Subject: [PATCH 067/188] Fix small gums being displayed as nolok small gums --- src/items/item.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/items/item.cpp b/src/items/item.cpp index ed02ef0b078..a6435685478 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -191,10 +191,10 @@ void ItemState::collected(const AbstractKart *kart) * account). */ Item::ItemType ItemState::getGraphicalType() const { - return m_previous_owner && m_previous_owner->getIdent() == "nolok" && - getType() == ITEM_BUBBLEGUM ? ITEM_BUBBLEGUM_NOLOK : - getType() == ITEM_BUBBLEGUM_SMALL ? ITEM_BUBBLEGUM_SMALL_NOLOK : - getType(); + bool nolok_owned = (m_previous_owner && m_previous_owner->getIdent() == "nolok"); + return nolok_owned && getType() == ITEM_BUBBLEGUM ? ITEM_BUBBLEGUM_NOLOK : + nolok_owned && getType() == ITEM_BUBBLEGUM_SMALL ? ITEM_BUBBLEGUM_SMALL_NOLOK : + getType(); } // getGraphicalType //----------------------------------------------------------------------------- From d57eaa11bcc64a601748c9a13c275d0731825f9f Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sat, 2 Dec 2023 19:14:31 +0100 Subject: [PATCH 068/188] Implement the mini-cake - Same graphics as the standard cake, but downscaled to 0.6x standard size. Because the standard size is somewhat small, it has been increase to 1.2x. - The mini-cake only has 90% of the speed of the standard cake. This makes it slightly worse at hitting its targets. - The mini-cake does no damage to karts near the main explosion. - The mini-cake only does indirect hit damage to a kart that's hit directly. This means that the affected kart loses less time in explosion animation. --- src/items/cake.cpp | 21 ++++++++++++++++++--- src/items/cake.hpp | 5 ++++- src/items/flyable.cpp | 4 ++-- src/items/flyable.hpp | 5 ++++- src/items/powerup.cpp | 2 +- src/items/projectile_manager.cpp | 21 +++++++++++++++++---- src/network/rewinder.hpp | 3 ++- 7 files changed, 48 insertions(+), 13 deletions(-) diff --git a/src/items/cake.cpp b/src/items/cake.cpp index 28a633e9df0..215c899440c 100644 --- a/src/items/cake.cpp +++ b/src/items/cake.cpp @@ -26,7 +26,9 @@ #include "utils/constants.hpp" #include "utils/random_generator.hpp" -#include "utils/log.hpp" //TODO: remove after debugging is done +#include "utils/log.hpp" +#include + float Cake::m_st_max_distance_squared; float Cake::m_gravity; @@ -69,14 +71,17 @@ bool Cake::hit(AbstractKart* kart, PhysicalObject* obj) kart->decreaseShieldTime(); return false; //Not sure if a shield hit is a real hit. } - explode(kart, obj); + if (!m_mini) + explode(kart, obj); + else + explode(kart, obj, /* secondary hits */ false, /* indirect damage */ true); } return was_real_hit; } // hit // ---------------------------------------------------------------------------- -void Cake::onFireFlyable() +void Cake::onFireFlyable(bool mini) { Flyable::onFireFlyable(); setDoTerrainInfo(false); @@ -86,6 +91,12 @@ void Cake::onFireFlyable() gravity_vector = Vec3(0, -1, 0).rotate(q.getAxis(), q.getAngle()); gravity_vector = gravity_vector.normalize() * m_gravity; const bool backwards = m_owner->getControls().getLookBack(); + m_mini = mini; + + if (m_mini) + m_node->setScale(core::vector3df(0.6f,0.6f,0.6f)); + else + m_node->setScale(core::vector3df(1.2f,1.2f,1.2f)); // A bit of a hack: the mass of this kinematic object is still 1.0 // (see flyable), which enables collisions. I tried setting @@ -95,6 +106,10 @@ void Cake::onFireFlyable() // time a homing-track collision happens). float forward_offset=m_owner->getKartLength()/2.0f + m_extend.getZ()/2.0f; + // Mini cakes are slightly slower than normal cakes + if(m_mini) + m_speed = 0.9f * m_speed; + float up_velocity = m_speed/6.3f; // Adds the speed of the kart owning the cake. m_speed is defined in flyable diff --git a/src/items/cake.hpp b/src/items/cake.hpp index 40b1fb124b3..af1d4800dde 100644 --- a/src/items/cake.hpp +++ b/src/items/cake.hpp @@ -47,6 +47,9 @@ class Cake : public Flyable /** Which kart is targeted by this projectile (NULL if none). */ Moveable* m_target; + /** If it is a mini-cake */ + bool m_mini; + public: Cake (AbstractKart *kart); static void init (const XMLNode &node, scene::IMesh *cake_model); @@ -73,7 +76,7 @@ class Cake : public Flyable virtual void setVelocity(const btVector3& v) OVERRIDE { m_initial_velocity = v; } // ------------------------------------------------------------------------ - virtual void onFireFlyable() OVERRIDE; + virtual void onFireFlyable(bool mini=false) OVERRIDE; }; // Cake #endif diff --git a/src/items/flyable.cpp b/src/items/flyable.cpp index d22b6e64e7d..5a60e64f93c 100644 --- a/src/items/flyable.cpp +++ b/src/items/flyable.cpp @@ -569,7 +569,7 @@ bool Flyable::hit(AbstractKart *kart_hit, PhysicalObject* object) * also be affected. */ void Flyable::explode(AbstractKart *kart_hit, PhysicalObject *object, - bool secondary_hits) + bool secondary_hits, bool indirect_damage) { // Apply explosion effect // ---------------------- @@ -597,7 +597,7 @@ void Flyable::explode(AbstractKart *kart_hit, PhysicalObject *object, { // The explosion animation will register itself with the kart // and will free it later. - ExplosionAnimation::create(kart, getXYZ(), kart==kart_hit); + ExplosionAnimation::create(kart, getXYZ(), indirect_damage ? false : kart==kart_hit); if (kart == kart_hit) { world->kartHit(kart->getWorldKartId(), diff --git a/src/items/flyable.hpp b/src/items/flyable.hpp index e2734f60bbf..24377371d59 100644 --- a/src/items/flyable.hpp +++ b/src/items/flyable.hpp @@ -201,7 +201,7 @@ class Flyable : public Moveable, public TerrainInfo, bool isOwnerImmunity(const AbstractKart *kart_hit) const; virtual bool hit(AbstractKart* kart, PhysicalObject* obj=NULL); void explode(AbstractKart* kart, PhysicalObject* obj=NULL, - bool secondary_hits=true); + bool secondary_hits=true, bool indirect_damage=false); unsigned int getOwnerId(); // ------------------------------------------------------------------------ /** Returns if this flyable has an animation playing (e.g. cannon). */ @@ -271,6 +271,9 @@ class Flyable : public Moveable, public TerrainInfo, * projectile_manager. */ virtual void onFireFlyable(); // ------------------------------------------------------------------------ + /** This is overriden by cakes to allow mini-cakes */ + virtual void onFireFlyable(bool mini) { onFireFlyable(); } + // ------------------------------------------------------------------------ virtual void onDeleteFlyable(); // ------------------------------------------------------------------------ void setCreatedTicks(int ticks) { m_created_ticks = ticks; } diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index f837b00320c..070de98d386 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -472,7 +472,7 @@ void Powerup::use() m_sound_use = SFXManager::get()->createSoundSource("shoot"); m_sound_use->play(); } - ProjectileManager::get()->newProjectile(m_kart, PowerupManager::POWERUP_CAKE); + ProjectileManager::get()->newProjectile(m_kart, PowerupManager::POWERUP_MINI); break; } // mini-cake case diff --git a/src/items/projectile_manager.cpp b/src/items/projectile_manager.cpp index f44e220b1e3..ad029270355 100644 --- a/src/items/projectile_manager.cpp +++ b/src/items/projectile_manager.cpp @@ -178,7 +178,7 @@ std::shared_ptr // Flyable has already created before and now rewinding, re-fire it if (it != m_active_projectiles.end()) { - it->second->onFireFlyable(); + it->second->onFireFlyable(type == PowerupManager::POWERUP_MINI ? true : false); return it->second; } @@ -194,6 +194,9 @@ std::shared_ptr case PowerupManager::POWERUP_CAKE: f = std::make_shared(kart); break; + case PowerupManager::POWERUP_MINI: + f = std::make_shared(kart); + break; case PowerupManager::POWERUP_RUBBERBALL: f = std::make_shared(kart); break; @@ -201,7 +204,7 @@ std::shared_ptr return nullptr; } // This cannot be done in constructor because of virtual function - f->onFireFlyable(); + f->onFireFlyable(type == PowerupManager::POWERUP_MINI ? true : false); m_active_projectiles[uid] = f; if (RewindManager::get()->isEnabled()) f->addForRewind(uid); @@ -298,6 +301,11 @@ std::string ProjectileManager::getUniqueIdentity(AbstractKart* kart, uid.addUInt8(RN_CAKE); break; } + case PowerupManager::POWERUP_MINI: + { + uid.addUInt8(RN_CAKE_MINI); + break; + } case PowerupManager::POWERUP_RUBBERBALL: { uid.addUInt8(RN_RUBBERBALL); @@ -324,7 +332,7 @@ std::shared_ptr RewinderName rn = (RewinderName)data.getUInt8(); if (!(rn == RN_BOWLING || rn == RN_PLUNGER || - rn == RN_CAKE || rn == RN_RUBBERBALL)) + rn == RN_CAKE || rn == RN_CAKE_MINI || rn == RN_RUBBERBALL)) return nullptr; AbstractKart* kart = World::getWorld()->getKart(data.getUInt8()); @@ -347,6 +355,11 @@ std::shared_ptr f = std::make_shared(kart); break; } + case RN_CAKE_MINI: + { + f = std::make_shared(kart); + break; + } case RN_RUBBERBALL: { f = std::make_shared(kart); @@ -359,7 +372,7 @@ std::shared_ptr } assert(f); f->setCreatedTicks(created_ticks); - f->onFireFlyable(); + f->onFireFlyable(/* mini */ rn == RN_CAKE_MINI ? true : false); f->addForRewind(uid); Flyable* flyable = f.get(); Log::debug("ProjectileManager", "Missed a firing event, " diff --git a/src/network/rewinder.hpp b/src/network/rewinder.hpp index c47a960b78b..02513ea5d81 100644 --- a/src/network/rewinder.hpp +++ b/src/network/rewinder.hpp @@ -37,7 +37,8 @@ enum RewinderName : char RN_BOWLING = 0x06, RN_PLUNGER = 0x07, RN_RUBBERBALL = 0x08, - RN_PHYSICAL_OBJ = 0x09 + RN_PHYSICAL_OBJ = 0x09, + RN_CAKE_MINI = 0x0A, }; class Rewinder : public std::enable_shared_from_this From a3a6493cca98ddf4741c7b4e2c902863ec8ea677 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sat, 2 Dec 2023 20:32:32 +0100 Subject: [PATCH 069/188] Make the mini-wish work correctly in networked mode - Transmit the MiniState, for correct display (clients would falsely activate the item otherwise) - Fix a crash that happened when rewinding, with the node for cakes being potentially undefined --- src/items/cake.cpp | 11 +++++++---- src/items/powerup.hpp | 4 ++++ src/karts/abstract_kart.hpp | 2 ++ src/karts/kart.cpp | 5 +++++ src/karts/kart.hpp | 1 + src/karts/kart_rewinder.cpp | 10 ++++++++++ 6 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/items/cake.cpp b/src/items/cake.cpp index 215c899440c..149704677e3 100644 --- a/src/items/cake.cpp +++ b/src/items/cake.cpp @@ -93,10 +93,13 @@ void Cake::onFireFlyable(bool mini) const bool backwards = m_owner->getControls().getLookBack(); m_mini = mini; - if (m_mini) - m_node->setScale(core::vector3df(0.6f,0.6f,0.6f)); - else - m_node->setScale(core::vector3df(1.2f,1.2f,1.2f)); + if (m_node != NULL) // It can be null when rewinding + { + if (m_mini) + m_node->setScale(core::vector3df(0.6f,0.6f,0.6f)); + else + m_node->setScale(core::vector3df(1.2f,1.2f,1.2f)); + } // A bit of a hack: the mass of this kinematic object is still 1.0 // (see flyable), which enables collisions. I tried setting diff --git a/src/items/powerup.hpp b/src/items/powerup.hpp index 2799fde58fe..a371f593c78 100644 --- a/src/items/powerup.hpp +++ b/src/items/powerup.hpp @@ -79,6 +79,10 @@ class Powerup : public NoCopy PowerupManager::PowerupType getType () const {return m_type; } // ------------------------------------------------------------------------ + PowerupManager::MiniState getMiniState () const { return m_mini_state; } + // ------------------------------------------------------------------------ + void setMiniState (PowerupManager::MiniState new_mini_state) + { m_mini_state = new_mini_state; } }; #endif diff --git a/src/karts/abstract_kart.hpp b/src/karts/abstract_kart.hpp index 16a403e2854..3783765892d 100644 --- a/src/karts/abstract_kart.hpp +++ b/src/karts/abstract_kart.hpp @@ -386,6 +386,8 @@ class AbstractKart : public Moveable // ------------------------------------------------------------------------ virtual float getStolenNitro() const = 0; // ------------------------------------------------------------------------ + virtual bool hasHeldMini() const = 0; + // ------------------------------------------------------------------------ /** Returns the current position of this kart in the race. */ virtual int getPosition() const = 0; // ------------------------------------------------------------------------ diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 3b1be5f9a84..83cad7e2c08 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1201,6 +1201,11 @@ void Kart::collectedItem(ItemState *item_state) } // collectedItem +bool Kart::hasHeldMini() const +{ + return (m_powerup->getType() == PowerupManager::POWERUP_MINI); +} // hasHeldMini + //----------------------------------------------------------------------------- /** Called the first time a kart accelerates after 'ready'. It searches * through the startup times to find the appropriate slot, and returns the diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 832a3ca3852..2ac73a85d1d 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -337,6 +337,7 @@ class Kart : public AbstractKart virtual void setStolenNitro(float amount, float duration) OVERRIDE; virtual bool hasStolenNitro() const OVERRIDE { return m_stolen_nitro_ticks > 0; } virtual float getStolenNitro() const OVERRIDE { return m_stolen_nitro_amount; } + virtual bool hasHeldMini() const OVERRIDE; virtual float getStartupBoostFromStartTicks(int ticks) const OVERRIDE; virtual float getStartupBoost() const OVERRIDE { return m_startup_boost; } virtual void setStartupBoost(float val) OVERRIDE { m_startup_boost = val; } diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index 04ef0c9ac8f..56ac7ac31c5 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -213,6 +213,8 @@ BareNetworkString* KartRewinder::saveState(std::vector* ru) bool_for_each_data_2 |= (1 << 5); if (getPowerupMask()) bool_for_each_data_2 |= (1 << 6); + if (hasHeldMini()) + bool_for_each_data_2 |= (1 << 7); buffer->addUInt8(bool_for_each_data_2); if (m_bubblegum_ticks > 0) @@ -227,6 +229,8 @@ BareNetworkString* KartRewinder::saveState(std::vector* ru) buffer->addFloat(getEnergy()); if (getPowerupMask()) buffer->addUInt32(m_powerup_mask); + if (hasHeldMini()) + buffer->addUInt8(m_powerup->getMiniState()); // 3) Kart animation status or physics values (transform and velocities) // ------------------------------------------- @@ -315,6 +319,7 @@ void KartRewinder::restoreState(BareNetworkString *buffer, int count) m_bubblegum_torque_sign = ((bool_for_each_data_2 >> 4) & 1) == 1; bool read_nitro_hack = ((bool_for_each_data_2 >> 5) & 1) == 1; bool read_powerup_mask = ((bool_for_each_data_2 >> 6) & 1) == 1; + bool has_held_mini = ((bool_for_each_data_2 >> 7) & 1) == 1; if (read_bubblegum) m_bubblegum_ticks = buffer->getUInt16(); @@ -349,6 +354,11 @@ void KartRewinder::restoreState(BareNetworkString *buffer, int count) else m_powerup_mask = 0; + if (has_held_mini) + m_powerup->setMiniState((PowerupManager::MiniState) buffer->getUInt8()); + else + m_powerup->setMiniState(PowerupManager::NOT_MINI); + // 3) Kart animation status or transform and velocities // ----------- if (has_animation_in_state) From eb7b42c1d4899cc92cd76e2199f22561953a61e1 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Wed, 6 Dec 2023 05:28:16 +0100 Subject: [PATCH 070/188] Start preparing achievements for 2.0 - Disable achievement syncing for now through a #define - Fix duplicate and incorrect strings for egg hunt counters - Correctly fetch the number of standard tracks for all-track achievements (see #4957) - For egg-hunt all-track achievements, only count tracks with egg-hunts available (see #4957) - Add achievement counters for hard egg-hunts (as the egg layout depends on difficulty ; Expert and SuperTux share the hardest layout) - Change achievement IDs to start with 100. This requires more work, including server-side, but the goal is to allow STK servers to store both 1.0 and 2.0 achievements, but only sync the version-appropriate achievements. - Remove the space from the translatable string for conditions, as translators have a bad tendency to remove it when translating. - Add two new achievements, Egg-Seeker for completing 3 egg hunts and Grand Explorer, for completing all standard egg hunts. - Use a new function to clean up repetitive code when checking for all-track status of an achievement goal. --- data/achievements.xml | 42 ++++++---- src/achievements/achievement.cpp | 51 ++++-------- src/achievements/achievement_info.cpp | 16 +--- src/achievements/achievement_info.hpp | 1 + src/achievements/achievements_status.cpp | 67 ++++++++++----- src/achievements/achievements_status.hpp | 69 ++++++++------- src/modes/world.cpp | 8 ++ src/online/online_player_profile.cpp | 3 +- .../dialogs/achievement_progress_dialog.cpp | 83 ++++++++++--------- .../online/online_profile_achievements.cpp | 2 +- src/states_screens/tracks_and_gp_screen.cpp | 2 +- 11 files changed, 186 insertions(+), 158 deletions(-) diff --git a/data/achievements.xml b/data/achievements.xml index a6871e3366a..4425e3bfeea 100644 --- a/data/achievements.xml +++ b/data/achievements.xml @@ -140,41 +140,47 @@ egg-hunt-finished-all --> - - + + - + - + + + + - + - - + + - + - - + + + + - + - - + + - + + - - + + - - + + diff --git a/src/achievements/achievement.cpp b/src/achievements/achievement.cpp index 411d953d28c..ee17741cb01 100644 --- a/src/achievements/achievement.cpp +++ b/src/achievements/achievement.cpp @@ -161,42 +161,34 @@ int Achievement::getProgress() // ---------------------------------------------------------------------------- -/** Should ONLY be called if the achievement has one goal (a sum counts as one goal). - * Returning an error code with a number is not full-proof because a sum goal can - * legitimately be negative (a counter can be chosen to count against the - * achievement's fullfilment). */ +/** Should ONLY be called if the achievement has one goal (a sum counts as one goal). */ int Achievement::computeGoalProgress(AchievementInfo::goalTree &progress, AchievementInfo::goalTree &reference, bool same_tree) { + // This should NOT happen if (progress.children.size() >= 2) - { - // This should NOT happen + { + // Returning an error code with a number is not sufficient because a sum goal can + // legitimately be negative (a counter can be chosen to count against the + // achievement's fullfilment). assert(false); return 0; } + // Can happen when showing the progress status of all parts of the goal tree - else if (progress.children.size() == 0) + if (progress.children.size() == 0) { - //TODO : find a more automatic way ; clean up repetition - if (progress.type == "race-started-all" || - progress.type == "race-finished-all" || - progress.type == "race-won-all" || - progress.type == "race-finished-reverse-all" || - progress.type == "race-finished-alone-all" || - progress.type == "less-laps-all" || - progress.type == "more-laps-all" || - progress.type == "twice-laps-all" || - progress.type == "egg-hunt-started-all" || - progress.type == "egg-hunt-finished-all") + int all_track_status = PlayerManager::getCurrentAchievementsStatus()->getAllTrackStatus(progress.type); + if (all_track_status >= 1) { if (same_tree) { return PlayerManager::getCurrentAchievementsStatus() - ->getNumTracksAboveValue(0, reference.type); + ->getNumTracksAboveValue(0, reference.type, (all_track_status == 2)); } // Compare against the target value (in the reference tree) ! // Progress is only shown for the current local accuont, so we can use the current achievements status return PlayerManager::getCurrentAchievementsStatus() - ->getNumTracksAboveValue(reference.value, reference.type); + ->getNumTracksAboveValue(reference.value, reference.type, (all_track_status == 2)); } return progress.value; } @@ -209,22 +201,13 @@ int Achievement::computeGoalProgress(AchievementInfo::goalTree &progress, Achiev } else { - //TODO : find a more automatic way - if (progress.children[0].type == "race-started-all" || - progress.children[0].type == "race-finished-all" || - progress.children[0].type == "race-won-all" || - progress.children[0].type == "race-finished-reverse-all" || - progress.children[0].type == "race-finished-alone-all" || - progress.children[0].type == "less-laps-all" || - progress.children[0].type == "more-laps-all" || - progress.children[0].type == "twice-laps-all" || - progress.children[0].type == "egg-hunt-started-all" || - progress.children[0].type == "egg-hunt-finished-all") + int all_track_status = PlayerManager::getCurrentAchievementsStatus()->getAllTrackStatus(progress.children[0].type); + if (all_track_status >= 1) { // Compare against the target value (in the reference tree) ! - // Progress is only shown for the current local accuont, so we can use the current achievements status + // Progress is only shown for the current local account, so we can use the current achievements status return PlayerManager::getCurrentAchievementsStatus() - ->getNumTracksAboveValue(reference.children[0].value, reference.children[0].type); + ->getNumTracksAboveValue(reference.children[0].value, reference.children[0].type, (all_track_status == 2)); } else { @@ -241,7 +224,7 @@ int Achievement::computeGoalProgress(AchievementInfo::goalTree &progress, Achiev * resetable counter. It is applied if the parent node is of type * SUM or AND-AT-ONCE, ignored otherwise. * If it is LOGM- ; the update is for the highest achieved value of a - * resetable counter. It is appliedif the parent node is of type + * resetable counter. It is applied if the parent node is of type * AND or OR, ignored otherwise. * If there is no logical prefix, the new value is set in all cases. * diff --git a/src/achievements/achievement_info.cpp b/src/achievements/achievement_info.cpp index ab2811b11f7..6ffc0d62518 100644 --- a/src/achievements/achievement_info.cpp +++ b/src/achievements/achievement_info.cpp @@ -233,19 +233,11 @@ int AchievementInfo::recursiveProgressCount(goalTree &parent) } else { - //TODO : find a more automatic way - if (parent.children[0].type == "race-started-all" || - parent.children[0].type == "race-finished-all" || - parent.children[0].type == "race-won-all" || - parent.children[0].type == "race-finished-reverse-all" || - parent.children[0].type == "race-finished-alone-all" || - parent.children[0].type == "less-laps-all" || - parent.children[0].type == "more-laps-all" || - parent.children[0].type == "twice-laps-all" || - parent.children[0].type == "egg-hunt-started-all" || - parent.children[0].type == "egg-hunt-finished-all") + int all_track_status = PlayerManager::getCurrentAchievementsStatus()->getAllTrackStatus(parent.children[0].type); + + if (all_track_status >= 1) { - return PlayerManager::getCurrentAchievementsStatus()->getNumAchieveTracks(); + return PlayerManager::getCurrentAchievementsStatus()->getNumAchieveTracks(all_track_status == 2); } else { diff --git a/src/achievements/achievement_info.hpp b/src/achievements/achievement_info.hpp index 5cb8929e4d5..a8356a91b22 100644 --- a/src/achievements/achievement_info.hpp +++ b/src/achievements/achievement_info.hpp @@ -96,6 +96,7 @@ class AchievementInfo std::string getRawName() const { return m_name; } std::string getRawDescription() const { return m_description; } bool isSecret() const { return m_is_secret; } + int allTrackStatus(std::string type); // This function should not be called if copy already has children void copyGoalTree(goalTree ©, goalTree &model, bool set_values_to_zero); diff --git a/src/achievements/achievements_status.cpp b/src/achievements/achievements_status.cpp index 190e2d8a046..b8ace6fe78f 100644 --- a/src/achievements/achievements_status.cpp +++ b/src/achievements/achievements_status.cpp @@ -120,6 +120,8 @@ void AchievementsStatus::setEnumToString() m_ach_enum_to_xml[(int)SOCCER_FINISHED] = "soccer-finished"; m_ach_enum_to_xml[(int)EGG_HUNT_STARTED] = "egg-hunt-started"; m_ach_enum_to_xml[(int)EGG_HUNT_FINISHED] = "egg-hunt-finished"; + m_ach_enum_to_xml[(int)EGG_HUNT_STARTED_HARD] = "egg-hunt-started-hard"; + m_ach_enum_to_xml[(int)EGG_HUNT_FINISHED_HARD] = "egg-hunt-finished-hard"; m_ach_enum_to_xml[(int)WITH_GHOST_STARTED] = "with-ghost-started"; m_ach_enum_to_xml[(int)WITH_GHOST_FINISHED] = "with-ghost-finished"; m_ach_enum_to_xml[(int)CTF_STARTED] = "ctf-started"; @@ -155,8 +157,11 @@ void AchievementsStatus::setEnumToString() m_tr_enum_to_xml[(int)TR_MORE_LAPS] = "more-laps"; m_tr_enum_to_xml[(int)TR_MIN_TWICE_LAPS] = "twice-laps"; m_tr_enum_to_xml[(int)TR_FINISHED_ALONE] = "race-finished-alone"; - m_tr_enum_to_xml[(int)TR_EGG_HUNT_STARTED] = "egg-hunt-started"; - m_tr_enum_to_xml[(int)TR_EGG_HUNT_FINISHED] = "egg-hunt-started"; + m_tr_enum_to_xml[(int)TR_EGG_HUNT_STARTED] = "egg-hunt-started-tr"; + m_tr_enum_to_xml[(int)TR_EGG_HUNT_FINISHED] = "egg-hunt-finished-tr"; + m_tr_enum_to_xml[(int)TR_EGG_HUNT_STARTED_HARD] = "egg-hunt-started-hard-tr"; + m_tr_enum_to_xml[(int)TR_EGG_HUNT_FINISHED_HARD] = "egg-hunt-finished-hard-tr"; + m_tr_enum_to_xml[(int)TR_STARTED + (int)TR_DATA_NUM] = "race-started-all"; m_tr_enum_to_xml[(int)TR_FINISHED + (int)TR_DATA_NUM] = "race-finished-all"; @@ -167,7 +172,9 @@ void AchievementsStatus::setEnumToString() m_tr_enum_to_xml[(int)TR_MIN_TWICE_LAPS + (int)TR_DATA_NUM] = "twice-laps-all"; m_tr_enum_to_xml[(int)TR_FINISHED_ALONE + (int)TR_DATA_NUM] = "race-finished-alone-all"; m_tr_enum_to_xml[(int)TR_EGG_HUNT_STARTED + (int)TR_DATA_NUM] = "egg-hunt-started-all"; - m_tr_enum_to_xml[(int)TR_EGG_HUNT_FINISHED + (int)TR_DATA_NUM] = "egg-hunt-started-all"; + m_tr_enum_to_xml[(int)TR_EGG_HUNT_FINISHED + (int)TR_DATA_NUM] = "egg-hunt-finished-all"; + m_tr_enum_to_xml[(int)TR_EGG_HUNT_STARTED_HARD + (int)TR_DATA_NUM] = "egg-hunt-started-hard-all"; + m_tr_enum_to_xml[(int)TR_EGG_HUNT_FINISHED_HARD + (int)TR_DATA_NUM] = "egg-hunt-finished-hard-all"; } // setEnumToString // ---------------------------------------------------------------------------- @@ -238,6 +245,8 @@ void AchievementsStatus::load(const XMLNode * input) xml_achievement_tracks[i]->get("t_laps",&m_track_stats[j].track_data[(int)TR_MIN_TWICE_LAPS]); xml_achievement_tracks[i]->get("eh_sta",&m_track_stats[j].track_data[(int)TR_EGG_HUNT_STARTED]); xml_achievement_tracks[i]->get("eh_fin",&m_track_stats[j].track_data[(int)TR_EGG_HUNT_FINISHED]); + xml_achievement_tracks[i]->get("eh_stah",&m_track_stats[j].track_data[(int)TR_EGG_HUNT_STARTED_HARD]); + xml_achievement_tracks[i]->get("eh_finh",&m_track_stats[j].track_data[(int)TR_EGG_HUNT_FINISHED_HARD]); track_found = true; break; } @@ -257,6 +266,8 @@ void AchievementsStatus::load(const XMLNode * input) xml_achievement_tracks[i]->get("t_laps",&new_track.track_data[(int)TR_MIN_TWICE_LAPS]); xml_achievement_tracks[i]->get("eh_sta",&new_track.track_data[(int)TR_EGG_HUNT_STARTED]); xml_achievement_tracks[i]->get("eh_fin",&new_track.track_data[(int)TR_EGG_HUNT_FINISHED]); + xml_achievement_tracks[i]->get("eh_stah",&new_track.track_data[(int)TR_EGG_HUNT_STARTED_HARD]); + xml_achievement_tracks[i]->get("eh_finh",&new_track.track_data[(int)TR_EGG_HUNT_FINISHED_HARD]); m_track_stats.push_back(new_track); } @@ -321,6 +332,8 @@ void AchievementsStatus::save(UTFWriter &out) out << " t_laps=\"" << m_track_stats[n].track_data[(int)TR_MIN_TWICE_LAPS] << "\""; out << " eh_sta=\"" << m_track_stats[n].track_data[(int)TR_EGG_HUNT_STARTED] << "\""; out << " eh_fin=\"" << m_track_stats[n].track_data[(int)TR_EGG_HUNT_FINISHED] << "\""; + out << " eh_stah=\"" << m_track_stats[n].track_data[(int)TR_EGG_HUNT_STARTED_HARD] << "\""; + out << " eh_finh=\"" << m_track_stats[n].track_data[(int)TR_EGG_HUNT_FINISHED_HARD] << "\""; out << "/>\n"; } // for n\n"; @@ -342,6 +355,8 @@ Achievement * AchievementsStatus::getAchievement(uint32_t id) */ void AchievementsStatus::sync(const std::vector & achieved_ids) { +#ifdef SYNC_ACHIEVEMENTS + std::vector done; for(unsigned int i =0; i < achieved_ids.size(); ++i) { @@ -377,6 +392,7 @@ void AchievementsStatus::sync(const std::vector & achieved_ids) request->addParameter("achievementid", ids); request->queue(); } +#endif } // sync // ---------------------------------------------------------------------------- @@ -385,7 +401,7 @@ void AchievementsStatus::sync(const std::vector & achieved_ids) * It returns -1 if the goal type is invalid. * \param value - the value to match or exceed * \param goal_string - the identifier of the value to check. */ -int AchievementsStatus::getNumTracksAboveValue(int value, std::string goal_string) +int AchievementsStatus::getNumTracksAboveValue(int value, std::string goal_string, bool is_egg_hunt) { int counter = 0; int enum_id = -1; @@ -406,13 +422,10 @@ int AchievementsStatus::getNumTracksAboveValue(int value, std::string goal_strin for (unsigned int i=0;i= value) + // Increment on standard tracks, ignore all other kind of tracks (addons, WIP, etc.) + if ( m_track_stats[i].track_data[enum_id] >= value + && track_manager->getTrack(m_track_stats[i].ident)->isInGroup("standard") + && (track_manager->getTrack(m_track_stats[i].ident)->hasEasterEggs() || !is_egg_hunt)) counter++; } return counter; @@ -420,17 +433,15 @@ int AchievementsStatus::getNumTracksAboveValue(int value, std::string goal_strin // ---------------------------------------------------------------------------- /* This function returns the number of tracks valid for by-track achievements. */ -int AchievementsStatus::getNumAchieveTracks() +int AchievementsStatus::getNumAchieveTracks(bool is_egg_hunt) { int num_tracks = 0; for (unsigned int i=0;igetTrack(m_track_stats[i].ident)->isInGroup("standard") + && (!is_egg_hunt || track_manager->getTrack(m_track_stats[i].ident)->hasEasterEggs() || !is_egg_hunt)) + num_tracks++; } return num_tracks; } //getNumAchieveTracks @@ -504,7 +515,25 @@ void AchievementsStatus::updateAchievementsProgress(UpdateType type, unsigned in i->second->setGoalValue(goal_string[0],max_kart_hits); } } -} +} // updateAchievementsProgress + +// ---------------------------------------------------------------------------- +/** Returns 0 if it is not an all-track goal, 1 if it is an all-track goal + * (non egg hunt) and 2 if it is a egg hunt all track goal */ +int AchievementsStatus::getAllTrackStatus(std::string type) +{ + if (type == "race-started-all" || type == "race-finished-all" || + type == "race-won-all" || type == "race-finished-reverse-all" || + type == "race-finished-alone-all" || type == "less-laps-all" || + type == "more-laps-all" || type == "twice-laps-all") + return 1; + + else if (type == "egg-hunt-started-all" || type == "egg-hunt-finished-all" || + type == "egg-hunt-started-hard-all" || type == "egg-hunt-finished-hard-all") + return 2; + + return 0; +} // getAllTrackStatus // ---------------------------------------------------------------------------- void AchievementsStatus::increaseDataVar(unsigned int achieve_data_id, int increase) diff --git a/src/achievements/achievements_status.hpp b/src/achievements/achievements_status.hpp index 3b329c6167d..4b549c4db89 100644 --- a/src/achievements/achievements_status.hpp +++ b/src/achievements/achievements_status.hpp @@ -20,6 +20,8 @@ #ifndef HEADER_ACHIEVEMENTS_SLOT_HPP #define HEADER_ACHIEVEMENTS_SLOT_HPP +#undef SYNC_ACHIEVEMENTS + #include "utils/types.hpp" #include @@ -85,43 +87,45 @@ public : SOCCER_FINISHED = 27, EGG_HUNT_STARTED = 28, EGG_HUNT_FINISHED = 29, - WITH_GHOST_STARTED = 30, - WITH_GHOST_FINISHED = 31, - CTF_STARTED = 32, - CTF_FINISHED = 33, - FFA_STARTED = 34, - FFA_FINISHED = 35, + EGG_HUNT_STARTED_HARD = 30, + EGG_HUNT_FINISHED_HARD = 31, + WITH_GHOST_STARTED = 32, + WITH_GHOST_FINISHED = 33, + CTF_STARTED = 34, + CTF_FINISHED = 35, + FFA_STARTED = 36, + FFA_FINISHED = 37, // Count the number of powerups used by the player. - POWERUP_USED = 36, - POWERUP_USED_1RACE = 37, - POWERUP_USED_1RACE_MAX = 38, + POWERUP_USED = 38, + POWERUP_USED_1RACE = 39, + POWERUP_USED_1RACE_MAX = 40, // Count how many times a bowling ball from the player hit a kart - BOWLING_HIT = 39, - BOWLING_HIT_1RACE = 40, - BOWLING_HIT_1RACE_MAX = 41, + BOWLING_HIT = 41, + BOWLING_HIT_1RACE = 42, + BOWLING_HIT_1RACE_MAX = 43, // Count how many times a swatter from the player hit a kart - SWATTER_HIT = 42, - SWATTER_HIT_1RACE = 43, - SWATTER_HIT_1RACE_MAX = 44, + SWATTER_HIT = 44, + SWATTER_HIT_1RACE = 45, + SWATTER_HIT_1RACE_MAX = 46, // Count how many times a swatter, bowling ball or cake from // the player hit a kart (excluding the player's own kart) - ALL_HITS = 45, - ALL_HITS_1RACE = 46, - ALL_HITS_1RACE_MAX = 47, + ALL_HITS = 47, + ALL_HITS_1RACE = 48, + ALL_HITS_1RACE_MAX = 49, // Count the number of bananas hit - BANANA = 48, - BANANA_1RACE = 49, - BANANA_1RACE_MAX = 50, + BANANA = 50, + BANANA_1RACE = 51, + BANANA_1RACE_MAX = 52, // Count how many times the player skidded - SKIDDING = 51, - SKIDDING_1RACE = 52, - SKIDDING_1RACE_MAX = 53, - SKIDDING_1LAP = 54, - SKIDDING_1LAP_MAX = 55, + SKIDDING = 53, + SKIDDING_1RACE = 54, + SKIDDING_1RACE_MAX = 55, + SKIDDING_1LAP = 56, + SKIDDING_1LAP_MAX = 57, - ACHIEVE_DATA_NUM = 56 + ACHIEVE_DATA_NUM = 58 }; private: @@ -131,7 +135,7 @@ public : // one variable may be used by several achievements. // TODO // Currently this only uses an int counter. - // Evaluate if additional data keeping (max achived ?) can be useful, + // Evaluate if additional data keeping (max achieved ?) can be useful, // and either expand the struct or remove it. struct AchievementVariable { @@ -167,8 +171,10 @@ public : // counters for egg hunts TR_EGG_HUNT_STARTED = 8, TR_EGG_HUNT_FINISHED = 9, + TR_EGG_HUNT_STARTED_HARD = 10, + TR_EGG_HUNT_FINISHED_HARD = 11, - TR_DATA_NUM = 10 + TR_DATA_NUM = 12 }; private: @@ -226,8 +232,9 @@ public : void resetKartHits(int num_karts); void addKartHit(int kart_id); void updateAllAchievementsProgress(); - int getNumTracksAboveValue(int value, std::string goal_string); - int getNumAchieveTracks(); + int getNumTracksAboveValue(int value, std::string goal_string, bool is_egg_hunt); + int getNumAchieveTracks(bool is_egg_hunt); + int getAllTrackStatus(std::string type); // ------------------------------------------------------------------------ std::map& getAllAchievements() { diff --git a/src/modes/world.cpp b/src/modes/world.cpp index c358eeb28ad..935baa15c39 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -392,6 +392,8 @@ void World::reset(bool restart) else if (RaceManager::get()->isEggHuntMode()) { PlayerManager::trackEvent(RaceManager::get()->getTrackName(), AchievementsStatus::TR_EGG_HUNT_STARTED); + if (RaceManager::get()->getDifficulty() >= RaceManager::DIFFICULTY_HARD) + PlayerManager::trackEvent(RaceManager::get()->getTrackName(), AchievementsStatus::TR_EGG_HUNT_STARTED_HARD); } if (reset_streak) PlayerManager::onRaceEnd(true /* previous race aborted */); @@ -1781,6 +1783,8 @@ void World::updateAchievementDataEndRace() else if (RaceManager::get()->isEggHuntMode()) { PlayerManager::trackEvent(RaceManager::get()->getTrackName(), ACS::TR_EGG_HUNT_FINISHED); + if (RaceManager::get()->getDifficulty() >= RaceManager::DIFFICULTY_HARD) + PlayerManager::trackEvent(RaceManager::get()->getTrackName(), AchievementsStatus::TR_EGG_HUNT_FINISHED_HARD); } updateAchievementModeCounters(false /*start*/); @@ -1798,7 +1802,11 @@ void World::updateAchievementModeCounters(bool start) else if (RaceManager::get()->isFollowMode()) PlayerManager::increaseAchievement(start ? ACS::FTL_STARTED : ACS::FTL_FINISHED,1); else if (RaceManager::get()->isEggHuntMode()) + { PlayerManager::increaseAchievement(start ? ACS::EGG_HUNT_STARTED : ACS::EGG_HUNT_FINISHED,1); + if (RaceManager::get()->getDifficulty() >= RaceManager::DIFFICULTY_HARD) + PlayerManager::increaseAchievement(start ? ACS::EGG_HUNT_STARTED_HARD : ACS::EGG_HUNT_FINISHED_HARD,1); + } else if (RaceManager::get()->isSoccerMode()) PlayerManager::increaseAchievement(start ? ACS::SOCCER_STARTED : ACS::SOCCER_FINISHED,1); else if (RaceManager::get()->isBattleMode()) diff --git a/src/online/online_player_profile.cpp b/src/online/online_player_profile.cpp index 30850244ea4..2825a0051b4 100644 --- a/src/online/online_player_profile.cpp +++ b/src/online/online_player_profile.cpp @@ -256,10 +256,9 @@ namespace Online saveSession(getOnlineId(), getToken()); } - std::string achieved_string(""); - // Even if no achievements were sent, we have to call sync // in order to upload local achievements to the server + std::string achieved_string(""); input->get("achieved", &achieved_string); std::vector achieved_ids = StringUtils::splitToUInt(achieved_string, ' '); diff --git a/src/states_screens/dialogs/achievement_progress_dialog.cpp b/src/states_screens/dialogs/achievement_progress_dialog.cpp index 99254f8a2fc..1a7c248d19b 100644 --- a/src/states_screens/dialogs/achievement_progress_dialog.cpp +++ b/src/states_screens/dialogs/achievement_progress_dialog.cpp @@ -158,6 +158,7 @@ void AchievementProgressDialog::recursiveFillTable(AchievementInfo::goalTree &pr core::stringw AchievementProgressDialog::niceGoalName(std::string internal_name) { core::stringw nice_name; + core::stringw space_string = irr::core::stringw(" "); // I18N: For achievements, a parent goal linking logically several subgoals if(internal_name=="AND") nice_name = _("Fulfill all the subgoals"); // I18N: For achievements, a parent goal linking logically several subgoals @@ -223,6 +224,10 @@ core::stringw AchievementProgressDialog::niceGoalName(std::string internal_name) // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. if(internal_name=="egg-hunt-finished") nice_name = _("Egg Hunts finished"); // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. + if(internal_name=="egg-hunt-started-hard") nice_name = _("Egg Hunts started in Expert or SuperTux"); + // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. + if(internal_name=="egg-hunt-finished-hard") nice_name = _("Egg Hunts finished in Expert or SuperTux"); + // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. if(internal_name=="with-ghost-started") nice_name = _("Races started with a ghost replay"); // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. if(internal_name=="with-ghost-finished") nice_name = _("Races finished with a ghost replay"); @@ -237,25 +242,25 @@ core::stringw AchievementProgressDialog::niceGoalName(std::string internal_name) // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. if(internal_name=="powerup-used") nice_name = _("Powerups used"); // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="powerup-used-1race") { nice_name = _("Powerups used"); nice_name += _(" (1 race)"); } + if(internal_name=="powerup-used-1race") { nice_name = _("Powerups used"); nice_name += space_string + _("(1 race)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. if(internal_name=="bowling-hit") nice_name = _("Bowling ball hits"); // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="bowling-hit-1race") { nice_name = _("Bowling ball hits"); nice_name += _(" (1 race)"); } + if(internal_name=="bowling-hit-1race") { nice_name = _("Bowling ball hits"); nice_name += space_string + _("(1 race)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. if(internal_name=="swatter-hit") nice_name = _("Swatter hits"); // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="swatter-hit-1race") { nice_name = _("Swatter hits"); nice_name += _(" (1 race)"); } + if(internal_name=="swatter-hit-1race") { nice_name = _("Swatter hits"); nice_name += space_string + _("(1 race)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. if(internal_name=="all-hits") nice_name = _("All hits"); // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="all-hits-1race") { nice_name = _("All hits"); nice_name += _(" (1 race)"); } + if(internal_name=="all-hits-1race") { nice_name = _("All hits"); nice_name += space_string + _("(1 race)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="hit-same-kart-1race") { nice_name = _("Hits against the same kart"); nice_name += _(" (1 race)"); } + if(internal_name=="hit-same-kart-1race") { nice_name = _("Hits against the same kart"); nice_name += space_string + _("(1 race)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. if(internal_name=="banana") nice_name = _("Bananas collected"); // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="banana-1race") { nice_name = _("Bananas collected"); nice_name += _(" (1 race)"); } + if(internal_name=="banana-1race") { nice_name = _("Bananas collected"); nice_name += space_string + _("(1 race)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. if(internal_name=="skidding") nice_name = _("Skidding"); if (internal_name=="skidding-1race") @@ -263,65 +268,63 @@ core::stringw AchievementProgressDialog::niceGoalName(std::string internal_name) // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. nice_name = _("Skidding"); // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - nice_name += _(" (1 race)"); + nice_name += space_string + _("(1 race)"); } if (internal_name=="skidding-1lap") { // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. nice_name = _("Skidding"); // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - nice_name += _(" (1 lap)"); + nice_name += space_string + _("(1 lap)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="race-started") { nice_name =_("Races started"); nice_name += _(" (maximum on one official track)"); } + if(internal_name=="race-started") { nice_name =_("Races started"); nice_name += space_string + _("(maximum on one official track)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="race-finished") { nice_name =_("Races finished"); nice_name += _(" (maximum on one official track)"); } + if(internal_name=="race-finished") { nice_name =_("Races finished"); nice_name += space_string + _("(maximum on one official track)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="race-won") { nice_name =_("Races won"); nice_name += _(" (maximum on one official track)"); } + if(internal_name=="race-won") { nice_name =_("Races won"); nice_name += space_string + _("(maximum on one official track)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="race-finished-reverse") { nice_name =_("Reverse direction races finished"); nice_name += _(" (maximum on one official track)"); } + if(internal_name=="race-finished-reverse") { nice_name =_("Reverse direction races finished"); nice_name += space_string + _("(maximum on one official track)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="race-finished-alone") { nice_name =_("Races finished alone"); nice_name += _(" (maximum on one official track)"); } + if(internal_name=="race-finished-alone") { nice_name =_("Races finished alone"); nice_name += space_string + _("(maximum on one official track)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="less-laps") { nice_name =_("Races with less than the default lap number"); nice_name += _(" (maximum on one official track)"); } + if(internal_name=="less-laps") { nice_name =_("Races with less than the default lap number"); nice_name += space_string + _("(maximum on one official track)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="more-laps") { nice_name =_("Races with more than the default lap number"); nice_name += _(" (maximum on one official track)"); } + if(internal_name=="more-laps") { nice_name =_("Races with more than the default lap number"); nice_name += space_string + _("(maximum on one official track)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="twice-laps") { nice_name =_("Races with at least twice as much as the default lap number"); nice_name += _(" (maximum on one official track)"); } + if(internal_name=="twice-laps") { nice_name =_("Races with at least twice as much as the default lap number"); nice_name += space_string + _("(maximum on one official track)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="egg-hunt-started") { nice_name =_("Egg hunts started"); nice_name += _(" (maximum on one official track)"); } - if (internal_name=="egg-hunt-finished") - { - // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - nice_name =_("Egg hunts finished"); - // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - nice_name += _(" (maximum on one official track)"); - } + if(internal_name=="egg-hunt-started-tr") { nice_name =_("Egg hunts started"); nice_name += space_string + _("(maximum on one official track)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="race-started-all") { nice_name =_("Races started"); nice_name += _(" (official tracks matching the goal)"); } + if(internal_name=="egg-hunt-finished-tr") { nice_name =_("Egg hunts finished"); nice_name += space_string +_("(maximum on one official track)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="race-finished-all") { nice_name =_("Races finished"); nice_name += _(" (official tracks matching the goal)"); } + if(internal_name=="egg-hunt-started-hard-tr") { nice_name = _("Egg Hunts started in Expert or SuperTux"); nice_name += space_string +_("(maximum on one official track)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="race-won-all") { nice_name =_("Races won"); nice_name += _(" (official tracks matching the goal)"); } + if(internal_name=="egg-hunt-finished-hard-tr") { nice_name = _("Egg Hunts finished in Expert or SuperTux"); nice_name += space_string +_("(maximum on one official track)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="race-finished-reverse-all") { nice_name =_("Reverse direction races finished"); nice_name += _(" (official tracks matching the goal)"); } + if(internal_name=="race-started-all") { nice_name =_("Races started"); nice_name += space_string + _("(official tracks matching the goal)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="race-finished-alone-all") { nice_name =_("Races finished alone"); nice_name += _(" (official tracks matching the goal)"); } + if(internal_name=="race-finished-all") { nice_name =_("Races finished"); nice_name += space_string + _("(official tracks matching the goal)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="less-laps-all") { nice_name =_("Races with less than the default lap number"); nice_name += _(" (official tracks matching the goal)"); } + if(internal_name=="race-won-all") { nice_name =_("Races won"); nice_name += space_string + _("(official tracks matching the goal)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="more-laps-all") { nice_name =_("Races with more than the default lap number"); nice_name += _(" (official tracks matching the goal)"); } + if(internal_name=="race-finished-reverse-all") { nice_name =_("Reverse direction races finished"); nice_name += space_string + _("(official tracks matching the goal)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="twice-laps-all") { nice_name =_("Races with at least twice as much as the default lap number"); nice_name += _(" (official tracks matching the goal)"); } + if(internal_name=="race-finished-alone-all") { nice_name =_("Races finished alone"); nice_name += space_string + _("(official tracks matching the goal)"); } // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - if(internal_name=="egg-hunt-started-all") { nice_name =_("Egg hunts started"); nice_name += _(" (official tracks matching the goal)"); } - if(internal_name=="egg-hunt-finished-all") - { - // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - nice_name =_("Egg hunts finished"); - // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. - nice_name += _(" (official tracks matching the goal)"); - } + if(internal_name=="less-laps-all") { nice_name =_("Races with less than the default lap number"); nice_name += space_string + _("(official tracks matching the goal)"); } + // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. + if(internal_name=="more-laps-all") { nice_name =_("Races with more than the default lap number"); nice_name += space_string + _("(official tracks matching the goal)"); } + // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. + if(internal_name=="twice-laps-all") { nice_name =_("Races with at least twice as much as the default lap number"); nice_name += space_string + _("(official tracks matching the goal)"); } + // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. + if(internal_name=="egg-hunt-started-all") { nice_name =_("Egg hunts started"); nice_name += space_string + _("(official tracks matching the goal)"); } + // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. + if(internal_name=="egg-hunt-finished-all") { nice_name =_("Egg hunts finished"); nice_name += space_string + _("(official tracks matching the goal)"); } + // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. + if(internal_name=="egg-hunt-started-hard-all") { nice_name = _("Egg Hunts started in Expert or SuperTux"); nice_name += space_string +_("(official tracks matching the goal)"); } + // I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals. + if(internal_name=="egg-hunt-finished-hard-all") { nice_name = _("Egg Hunts finished in Expert or SuperTux"); nice_name += space_string +_("(official tracks matching the goal)"); } return nice_name; } // niceGoalName diff --git a/src/states_screens/online/online_profile_achievements.cpp b/src/states_screens/online/online_profile_achievements.cpp index efee9bcd0f7..bb9bd0644a7 100644 --- a/src/states_screens/online/online_profile_achievements.cpp +++ b/src/states_screens/online/online_profile_achievements.cpp @@ -270,7 +270,7 @@ void BaseOnlineProfileAchievements::displayResults() // ---------------------------------------------------------------------------- /** True if a's goal progression is <= to b's. * If they are equal, goalSort(a,b) != goalSort(b,a), - ù as the bool can't handle the 3 values required to avoid this + * as the bool can't handle the 3 values required to avoid this */ bool BaseOnlineProfileAchievements::goalSort(Achievement *a, Achievement *b) { diff --git a/src/states_screens/tracks_and_gp_screen.cpp b/src/states_screens/tracks_and_gp_screen.cpp index e272e924a8e..2f48ec4d778 100644 --- a/src/states_screens/tracks_and_gp_screen.cpp +++ b/src/states_screens/tracks_and_gp_screen.cpp @@ -199,7 +199,7 @@ void TracksAndGPScreen::init() const GrandPrixData* gp = grand_prix_manager->getGrandPrix(n); const std::vector tracks = gp->getTrackNames(true); - //Skip epmpty GPs + //Skip empty GPs if (gp->getNumberOfTracks()==0) continue; From 0bcc80f762769b41947c1d369910e36a5812c3a8 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Wed, 6 Dec 2023 06:11:42 +0100 Subject: [PATCH 071/188] More achievement fixes - Fix a potential crash introduced in the previous commit, if a track exists in achievements counter but isn't active (for example a deleted addon) - Ensure that the min and max values across tracks for egg hunt per-track counters are only computed across tracks with egg hunts --- src/achievements/achievements_status.cpp | 17 +++++++++++------ src/achievements/achievements_status.hpp | 6 ++++-- src/modes/world.cpp | 2 +- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/achievements/achievements_status.cpp b/src/achievements/achievements_status.cpp index b8ace6fe78f..071f46e7c94 100644 --- a/src/achievements/achievements_status.cpp +++ b/src/achievements/achievements_status.cpp @@ -424,6 +424,7 @@ int AchievementsStatus::getNumTracksAboveValue(int value, std::string goal_strin { // Increment on standard tracks, ignore all other kind of tracks (addons, WIP, etc.) if ( m_track_stats[i].track_data[enum_id] >= value + && track_manager->getTrack(m_track_stats[i].ident) != NULL && track_manager->getTrack(m_track_stats[i].ident)->isInGroup("standard") && (track_manager->getTrack(m_track_stats[i].ident)->hasEasterEggs() || !is_egg_hunt)) counter++; @@ -439,7 +440,8 @@ int AchievementsStatus::getNumAchieveTracks(bool is_egg_hunt) for (unsigned int i=0;igetTrack(m_track_stats[i].ident)->isInGroup("standard") + if ( track_manager->getTrack(m_track_stats[i].ident) != NULL + && track_manager->getTrack(m_track_stats[i].ident)->isInGroup("standard") && (!is_egg_hunt || track_manager->getTrack(m_track_stats[i].ident)->hasEasterEggs() || !is_egg_hunt)) num_tracks++; } @@ -467,12 +469,15 @@ void AchievementsStatus::updateAchievementsProgress(UpdateType type, unsigned in goal_string[0] = m_tr_enum_to_xml[(int)enum_id]; // The "one-track at least" goal goal_string[1] = m_tr_enum_to_xml[(int)enum_id+(int)TR_DATA_NUM]; // The "all tracks" goal + bool is_egg_hunt_update = (enum_id >= TR_EGG_HUNT_FIRST && enum_id <= TR_EGG_HUNT_LAST); + for (unsigned int i=0;igetTrack(m_track_stats[i].ident) == NULL + || !track_manager->getTrack(m_track_stats[i].ident)->isInGroup("standard") + || (is_egg_hunt_update && !track_manager->getTrack(m_track_stats[i].ident)->hasEasterEggs())) continue; if (m_track_stats[i].track_data[enum_id] > max_across_tracks) @@ -547,7 +552,7 @@ void AchievementsStatus::increaseDataVar(unsigned int achieve_data_id, int incre #ifdef DEBUG else { - Log::error("Achievements", "Achievement data id %i don't match any variable.", + Log::error("Achievements", "Achievement data id %i doesn't match any variable.", achieve_data_id); } #endif diff --git a/src/achievements/achievements_status.hpp b/src/achievements/achievements_status.hpp index 4b549c4db89..1c68a2f9020 100644 --- a/src/achievements/achievements_status.hpp +++ b/src/achievements/achievements_status.hpp @@ -169,12 +169,14 @@ public : TR_MORE_LAPS = 6, TR_MIN_TWICE_LAPS = 7, // at least twice the track's default lap count // counters for egg hunts - TR_EGG_HUNT_STARTED = 8, + TR_EGG_HUNT_FIRST = 8, + TR_EGG_HUNT_STARTED = TR_EGG_HUNT_FIRST, TR_EGG_HUNT_FINISHED = 9, TR_EGG_HUNT_STARTED_HARD = 10, TR_EGG_HUNT_FINISHED_HARD = 11, + TR_EGG_HUNT_LAST = TR_EGG_HUNT_FINISHED_HARD, - TR_DATA_NUM = 12 + TR_DATA_NUM = 12, }; private: diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 935baa15c39..84ea7a1cc7f 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -1784,7 +1784,7 @@ void World::updateAchievementDataEndRace() { PlayerManager::trackEvent(RaceManager::get()->getTrackName(), ACS::TR_EGG_HUNT_FINISHED); if (RaceManager::get()->getDifficulty() >= RaceManager::DIFFICULTY_HARD) - PlayerManager::trackEvent(RaceManager::get()->getTrackName(), AchievementsStatus::TR_EGG_HUNT_FINISHED_HARD); + PlayerManager::trackEvent(RaceManager::get()->getTrackName(), ACS::TR_EGG_HUNT_FINISHED_HARD); } updateAchievementModeCounters(false /*start*/); From 1f596e29bd0cc837926439f2171f4d6cfd1db82b Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Wed, 6 Dec 2023 06:24:59 +0100 Subject: [PATCH 072/188] Update documentation in achievements.xml --- data/achievements.xml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/data/achievements.xml b/data/achievements.xml index 4425e3bfeea..cb0de934c93 100644 --- a/data/achievements.xml +++ b/data/achievements.xml @@ -82,6 +82,8 @@ soccer-finished egg-hunt-started egg-hunt-finished + egg-hunt-started-hard + egg-hunt-finished-hard with-ghost-started with-ghost-finished ctf-started @@ -120,8 +122,10 @@ less-laps more-laps twice-laps - egg-hunt-started - egg-hunt-finished + egg-hunt-started-tr + egg-hunt-finished-tr + egg-hunt-started-hard-tr + egg-hunt-finished-hard-tr VII - Per-track counters (all non-addon tracks reach the value) @@ -137,7 +141,9 @@ // For egg hunts, all non-addon tracks with egg hunt support // must reach the value egg-hunt-started-all - egg-hunt-finished-all + egg-hunt-finished-all + egg-hunt-started-hard-all + egg-hunt-finished-hard-all --> From dbad55d50abfec1f49316ce22991d92f379a6249 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Thu, 7 Dec 2023 00:08:07 +0100 Subject: [PATCH 073/188] Update the restitution array - Make the restitution curves smoother, so that increases in kart speed don't lead to decrease in the rebound force experienced by the kart - Decrease restitution at low speeds, increase it slightly at moderate speeds, reduce it by up to 25% at high speeds. This means kart won't fly off as much after high-speed collisions. --- data/stk_config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/stk_config.xml b/data/stk_config.xml index 7b78fbe3e7c..08c959fd75b 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -500,7 +500,7 @@ behaviour of the karts. --> - - - - - - - + boost="6.5 3.5" + engine-force="160.0 120.0" + duration="4.0" fade-out-time ="2.0" /> - @@ -411,7 +413,7 @@ - + @@ -424,9 +426,9 @@ - + - + @@ -437,9 +439,9 @@ power-increase="2.5 2.08 1.76 1.44 1.22 1" /> - + - + @@ -449,8 +451,8 @@ - - + + AbstractCharacteristic::getStartupBoost() const return result; } // getStartupBoost +// ---------------------------------------------------------------------------- +std::vector AbstractCharacteristic::getStartupEngineForce() const +{ + std::vector result; + bool is_set = false; + process(STARTUP_ENGINE_FORCE, &result, &is_set); + if (!is_set) + Log::fatal("AbstractCharacteristic", "Can't get characteristic %s", + getName(STARTUP_ENGINE_FORCE).c_str()); + return result; +} // getStartupEngineForce + +// ---------------------------------------------------------------------------- +float AbstractCharacteristic::getStartupDuration() const +{ + float result; + bool is_set = false; + process(STARTUP_DURATION, &result, &is_set); + if (!is_set) + Log::fatal("AbstractCharacteristic", "Can't get characteristic %s", + getName(STARTUP_DURATION).c_str()); + return result; +} // getStartupDuration + +// ---------------------------------------------------------------------------- +float AbstractCharacteristic::getStartupFadeOutTime() const +{ + float result; + bool is_set = false; + process(STARTUP_FADE_OUT_TIME, &result, &is_set); + if (!is_set) + Log::fatal("AbstractCharacteristic", "Can't get characteristic %s", + getName(STARTUP_FADE_OUT_TIME).c_str()); + return result; +} // getStartupFadeOutTime + // ---------------------------------------------------------------------------- float AbstractCharacteristic::getRescueDuration() const { diff --git a/src/karts/abstract_characteristic.hpp b/src/karts/abstract_characteristic.hpp index cb4d1d82a01..30c781ca75f 100644 --- a/src/karts/abstract_characteristic.hpp +++ b/src/karts/abstract_characteristic.hpp @@ -177,6 +177,9 @@ class AbstractCharacteristic // Startup STARTUP_TIME, STARTUP_BOOST, + STARTUP_ENGINE_FORCE, + STARTUP_DURATION, + STARTUP_FADE_OUT_TIME, // Rescue RESCUE_DURATION, @@ -356,6 +359,9 @@ class AbstractCharacteristic std::vector getStartupTime() const; std::vector getStartupBoost() const; + std::vector getStartupEngineForce() const; + float getStartupDuration() const; + float getStartupFadeOutTime() const; float getRescueDuration() const; float getRescueVertOffset() const; diff --git a/src/karts/abstract_kart.hpp b/src/karts/abstract_kart.hpp index 3783765892d..7fc54619f66 100644 --- a/src/karts/abstract_kart.hpp +++ b/src/karts/abstract_kart.hpp @@ -576,11 +576,13 @@ class AbstractKart : public Moveable // ------------------------------------------------------------------------ virtual void makeKartRest(); // ------------------------------------------------------------------------ - virtual void setStartupBoost(float val) = 0; + virtual void setStartupBoost(uint8_t boost_level) = 0; // ------------------------------------------------------------------------ virtual float getStartupBoost() const = 0; // ------------------------------------------------------------------------ - virtual float getStartupBoostFromStartTicks(int ticks) const = 0; + virtual uint8_t getStartupBoostLevel() const = 0; + // ------------------------------------------------------------------------ + virtual void setStartupBoostFromStartTicks(int ticks) = 0; // ------------------------------------------------------------------------ virtual Stars* getStarsEffect() const = 0; // ------------------------------------------------------------------------ diff --git a/src/karts/controller/local_player_controller.cpp b/src/karts/controller/local_player_controller.cpp index 24657892861..69dcc69a934 100644 --- a/src/karts/controller/local_player_controller.cpp +++ b/src/karts/controller/local_player_controller.cpp @@ -183,9 +183,8 @@ bool LocalPlayerController::action(PlayerAction action, int value, m_has_started = true; if (!NetworkConfig::get()->isNetworking()) { - float f = m_kart->getStartupBoostFromStartTicks( + m_kart->setStartupBoostFromStartTicks( World::getWorld()->getAuxiliaryTicks()); - m_kart->setStartupBoost(f); } else if (NetworkConfig::get()->isClient()) { diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index b3833aad123..352c9343eff 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -2169,8 +2169,7 @@ void SkiddingAI::handleRaceStart() m_start_delay+=stk_config->m_penalty_ticks; return; } - m_kart->setStartupBoost(m_kart->getStartupBoostFromStartTicks( - m_start_delay + stk_config->time2Ticks(1.0f))); + m_kart->setStartupBoostFromStartTicks(m_start_delay + stk_config->time2Ticks(1.0f)); m_start_delay = 0; } } // handleRaceStart diff --git a/src/karts/controller/test_ai.cpp b/src/karts/controller/test_ai.cpp index be53f0e6e85..9a3e5bab059 100644 --- a/src/karts/controller/test_ai.cpp +++ b/src/karts/controller/test_ai.cpp @@ -1566,8 +1566,7 @@ void SkiddingAI::handleRaceStart() m_start_delay+=stk_config->m_penalty_ticks; return; } - m_kart->setStartupBoost(m_kart->getStartupBoostFromStartTicks( - m_start_delay + stk_config->time2Ticks(1.0f))); + m_kart->setStartupBoostFromStartTicks(m_start_delay + stk_config->time2Ticks(1.0f)); m_start_delay = 0; } } // handleRaceStart diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 83cad7e2c08..c6b28a7c832 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -401,6 +401,8 @@ void Kart::reset() m_is_jumping = false; m_flying = false; m_startup_boost = 0.0f; + m_startup_engine_force = 0.0f; + m_startup_boost_level = 1; // 0 penalty, 1 nothing, 2+ boost if (m_node) m_node->setScale(core::vector3df(1.0f, 1.0f, 1.0f)); @@ -1208,25 +1210,47 @@ bool Kart::hasHeldMini() const //----------------------------------------------------------------------------- /** Called the first time a kart accelerates after 'ready'. It searches - * through the startup times to find the appropriate slot, and returns the - * speed-boost from the corresponding entry. + * through the startup times to find the appropriate slot, and sets up + * m_startup_boost and m_startup_engine_force to the appropriate values * If the kart started too slow (i.e. slower than the longest time in the * startup times list), it returns 0. */ -float Kart::getStartupBoostFromStartTicks(int ticks) const +void Kart::setStartupBoostFromStartTicks(int ticks) { int ticks_since_ready = ticks - stk_config->time2Ticks(1.0f); if (ticks_since_ready < 0) - return 0.0f; + return; float t = stk_config->ticks2Time(ticks_since_ready); std::vector startup_times = m_kart_properties->getStartupTime(); for (unsigned int i = 0; i < startup_times.size(); i++) { if (t <= startup_times[i]) - return m_kart_properties->getStartupBoost()[i]; + { + m_startup_boost = m_kart_properties->getStartupBoost()[i]; + m_startup_engine_force = m_kart_properties->getStartupEngineForce()[i]; + m_startup_boost_level = 1 + startup_times.size() - i; + printf("m_startup_boost_level is %u", (uint)m_startup_boost_level); + return; + } } - return 0.0f; -} // getStartupBoostFromStartTicks + m_startup_boost = 0.0f; + m_startup_engine_force = 0.0f; + m_startup_boost_level = 1; + +} // setStartupBoostFromStartTicks + +//----------------------------------------------------------------------------- +/** Called directly in networking mode */ +void Kart::setStartupBoost(uint8_t boost_level) +{ + std::vector startup_times = m_kart_properties->getStartupTime(); + int index = m_kart_properties->getStartupTime().size() - boost_level + 1; + assert(index >= 0 && index <= (int)m_kart_properties->getStartupTime().size()); + + m_startup_boost = m_kart_properties->getStartupBoost()[index]; + m_startup_engine_force = m_kart_properties->getStartupEngineForce()[index]; + m_startup_boost_level = boost_level; +} // setStartupBoost //----------------------------------------------------------------------------- /** Simulates gears by adjusting the force of the engine. It also takes the @@ -2809,10 +2833,10 @@ void Kart::updatePhysics(int ticks) m_kart_gfx->setCreationRateAbsolute(KartGFX::KGFX_ZIPPER, 100.0f * m_startup_boost); m_max_speed->instantSpeedIncrease(MaxSpeed::MS_INCREASE_ZIPPER, - 0.9f * m_startup_boost, m_startup_boost, - /*engine_force*/200.0f, - /*duration*/stk_config->time2Ticks(4.0f), - /*fade_out_time*/stk_config->time2Ticks(2.0f)); + /*instant speed*/ m_startup_boost, /*max speed*/ m_startup_boost, + /*engine_force*/ m_startup_engine_force, + /*duration*/ stk_config->time2Ticks(m_kart_properties->getStartupDuration()), + /*fade_out_time*/ stk_config->time2Ticks(m_kart_properties->getStartupFadeOutTime())); } } if (m_bounce_back_ticks > 0) diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 2ac73a85d1d..adb6b56fdbd 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -246,6 +246,14 @@ class Kart : public AbstractKart float m_startup_boost; + float m_startup_engine_force; + + // boost-level 0 corresponds to a start penalty + // boost-level 1 corresponds to a start without boost or penalty + // boost-level 2 or more corresponds to a start with boost + // This is only used for networking + uint8_t m_startup_boost_level; + float m_falling_time; float m_weight; @@ -338,9 +346,10 @@ class Kart : public AbstractKart virtual bool hasStolenNitro() const OVERRIDE { return m_stolen_nitro_ticks > 0; } virtual float getStolenNitro() const OVERRIDE { return m_stolen_nitro_amount; } virtual bool hasHeldMini() const OVERRIDE; - virtual float getStartupBoostFromStartTicks(int ticks) const OVERRIDE; + virtual void setStartupBoostFromStartTicks(int ticks) OVERRIDE; virtual float getStartupBoost() const OVERRIDE { return m_startup_boost; } - virtual void setStartupBoost(float val) OVERRIDE { m_startup_boost = val; } + virtual uint8_t getStartupBoostLevel() const OVERRIDE { return m_startup_boost_level; } + virtual void setStartupBoost(uint8_t boost_level) OVERRIDE; virtual const Material *getMaterial() const OVERRIDE; virtual const Material *getLastMaterial() const OVERRIDE; /** Returns the pitch of the terrain depending on the heading. */ diff --git a/src/karts/kart_properties.cpp b/src/karts/kart_properties.cpp index d911667ca72..37c713fa86b 100644 --- a/src/karts/kart_properties.cpp +++ b/src/karts/kart_properties.cpp @@ -1143,6 +1143,24 @@ std::vector KartProperties::getStartupBoost() const return m_cached_characteristic->getStartupBoost(); } // getStartupBoost +// ---------------------------------------------------------------------------- +std::vector KartProperties::getStartupEngineForce() const +{ + return m_cached_characteristic->getStartupEngineForce(); +} // getStartupEngineForce + +// ---------------------------------------------------------------------------- +float KartProperties::getStartupDuration() const +{ + return m_cached_characteristic->getStartupDuration(); +} // getStartupDuration + +// ---------------------------------------------------------------------------- +float KartProperties::getStartupFadeOutTime() const +{ + return m_cached_characteristic->getStartupFadeOutTime(); +} // getStartupFadeOutTime + // ---------------------------------------------------------------------------- float KartProperties::getRescueDuration() const { diff --git a/src/karts/kart_properties.hpp b/src/karts/kart_properties.hpp index 035df96f720..b4708c5468d 100644 --- a/src/karts/kart_properties.hpp +++ b/src/karts/kart_properties.hpp @@ -472,6 +472,9 @@ class KartProperties std::vector getStartupTime() const; std::vector getStartupBoost() const; + std::vector getStartupEngineForce() const; + float getStartupDuration() const; + float getStartupFadeOutTime() const; float getRescueDuration() const; float getRescueVertOffset() const; diff --git a/src/karts/xml_characteristic.cpp b/src/karts/xml_characteristic.cpp index c4c6203d222..5feffca7f74 100644 --- a/src/karts/xml_characteristic.cpp +++ b/src/karts/xml_characteristic.cpp @@ -529,6 +529,12 @@ void XmlCharacteristic::load(const XMLNode *node) &m_values[STARTUP_TIME]); sub_node->get("boost", &m_values[STARTUP_BOOST]); + sub_node->get("engine-force", + &m_values[STARTUP_ENGINE_FORCE]); + sub_node->get("duration", + &m_values[STARTUP_DURATION]); + sub_node->get("fade-out-time", + &m_values[STARTUP_FADE_OUT_TIME]); } if (const XMLNode *sub_node = node->getNode("rescue")) diff --git a/src/network/protocols/game_events_protocol.cpp b/src/network/protocols/game_events_protocol.cpp index 87279d66565..961ef0d24a4 100644 --- a/src/network/protocols/game_events_protocol.cpp +++ b/src/network/protocols/game_events_protocol.cpp @@ -112,28 +112,28 @@ bool GameEventsProtocol::notifyEvent(Event* event) kart_id, event->getPeer()->getAddress().toString().c_str()); return true; } - float f = LobbyProtocol::get() + uint8_t boost_level = LobbyProtocol::get() ->getStartupBoostOrPenaltyForKart( event->getPeer()->getAveragePing(), kart_id); NetworkString *ns = getNetworkString(); ns->setSynchronous(true); - ns->addUInt8(GE_STARTUP_BOOST).addUInt8(kart_id).addFloat(f); + ns->addUInt8(GE_STARTUP_BOOST).addUInt8(kart_id).addUInt8(boost_level); sendMessageToPeers(ns, true); delete ns; } else { uint8_t kart_id = data.getUInt8(); - float boost = data.getFloat(); + uint8_t boost_level = data.getUInt8(); AbstractKart* k = World::getWorld()->getKart(kart_id); - if (boost < 0.0f) + if (boost_level == 0) { PlayerController* pc = dynamic_cast(k->getController()); pc->displayPenaltyWarning(); } else - k->setStartupBoost(boost); + k->setStartupBoost(boost_level); } break; } diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 3516ffe0367..9e66f0d4356 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -5043,12 +5043,17 @@ void ServerLobby::listBanTable() } // listBanTable //----------------------------------------------------------------------------- -float ServerLobby::getStartupBoostOrPenaltyForKart(uint32_t ping, +uint8_t ServerLobby::getStartupBoostOrPenaltyForKart(uint32_t ping, unsigned kart_id) { + // boost-level 0 corresponds to a start penalty + // boost-level 1 corresponds to a start without boost or penalty + // boost-level 2 or more corresponds to a start with boost + AbstractKart* k = World::getWorld()->getKart(kart_id); - if (k->getStartupBoost() != 0.0f) - return k->getStartupBoost(); + // If a boost already exists, return it + if (k->getStartupBoostLevel() >= 2) + return k->getStartupBoostLevel(); uint64_t now = STKHost::get()->getNetworkTimer(); uint64_t client_time = now - ping / 2; uint64_t server_time = client_time + m_server_delay; @@ -5059,11 +5064,10 @@ float ServerLobby::getStartupBoostOrPenaltyForKart(uint32_t ping, PlayerController* pc = dynamic_cast(k->getController()); pc->displayPenaltyWarning(); - return -1.0f; + return 0; } - float f = k->getStartupBoostFromStartTicks(ticks); - k->setStartupBoost(f); - return f; + k->setStartupBoostFromStartTicks(ticks); + return k->getStartupBoostLevel(); } // getStartupBoostOrPenaltyForKart //----------------------------------------------------------------------------- diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index 53d3acedaeb..54aebc927aa 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -400,7 +400,7 @@ class ServerLobby : public LobbyProtocol virtual bool isRacing() const OVERRIDE { return m_state.load() == RACING; } bool allowJoinedPlayersWaiting() const; void setSaveServerConfig(bool val) { m_save_server_config = val; } - float getStartupBoostOrPenaltyForKart(uint32_t ping, unsigned kart_id); + uint8_t getStartupBoostOrPenaltyForKart(uint32_t ping, unsigned kart_id); int getDifficulty() const { return m_difficulty.load(); } int getGameMode() const { return m_game_mode.load(); } int getLobbyPlayers() const { return m_lobby_players.load(); } From 5cb63fbf31bff1c63f9c9e9e8aae756878a35f1c Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Fri, 8 Dec 2023 00:52:08 +0100 Subject: [PATCH 079/188] Fix mini-display in networking --- src/items/powerup.cpp | 24 +++++++++++++----------- src/items/powerup_manager.hpp | 2 +- src/karts/kart.cpp | 1 - src/states_screens/race_gui_base.cpp | 7 ++++++- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index be848f64cf7..852972819de 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -245,18 +245,20 @@ Material *Powerup::getIcon(bool wide) const // FIXME // This duplicates the logic to determine which powerup would be // selected by the current cycle - if (m_mini_state == PowerupManager::MINI_SELECT) - { - int cycle_ticks = stk_config->time2Ticks(0.65f); - int cycle_value = World::getWorld()->getTicksSinceStart() % (3 * cycle_ticks); - if (cycle_value < cycle_ticks) - return powerup_manager->getMiniIcon(0+wide_offset); - else if (cycle_value < 2*cycle_ticks) - return powerup_manager->getMiniIcon(1+wide_offset); - else - return powerup_manager->getMiniIcon(2+wide_offset); - } + + // If not in one of the mini-powerup-specific states, + // We assume that the mini_state is MINI_SELECT for correct display + // This may not always be true in networking mode right after item collection. + int cycle_ticks = stk_config->time2Ticks(0.65f); + int cycle_value = World::getWorld()->getTicksSinceStart() % (3 * cycle_ticks); + if (cycle_value < cycle_ticks) + return powerup_manager->getMiniIcon(0+wide_offset); + else if (cycle_value < 2*cycle_ticks) + return powerup_manager->getMiniIcon(1+wide_offset); + else + return powerup_manager->getMiniIcon(2+wide_offset); } + // Check if it's one of the types which have a separate // data file which includes the icon: return powerup_manager->getIcon(m_type); diff --git a/src/items/powerup_manager.hpp b/src/items/powerup_manager.hpp index 9079db1c049..5e42730698b 100644 --- a/src/items/powerup_manager.hpp +++ b/src/items/powerup_manager.hpp @@ -207,7 +207,7 @@ class PowerupManager : public NoCopy uint64_t random_number); // ------------------------------------------------------------------------ /** Returns the icon(material) for a powerup. */ - Material* getIcon(int type) const {return m_all_icons [type];} + Material* getIcon(int type) const { return m_all_icons [type];} // ------------------------------------------------------------------------ /** Returns the icon(material) for a mini status. */ Material* getMiniIcon(int index) const {return m_mini_icons [index];} diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index c6b28a7c832..6e6e34efa8c 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1229,7 +1229,6 @@ void Kart::setStartupBoostFromStartTicks(int ticks) m_startup_boost = m_kart_properties->getStartupBoost()[i]; m_startup_engine_force = m_kart_properties->getStartupEngineForce()[i]; m_startup_boost_level = 1 + startup_times.size() - i; - printf("m_startup_boost_level is %u", (uint)m_startup_boost_level); return; } } diff --git a/src/states_screens/race_gui_base.cpp b/src/states_screens/race_gui_base.cpp index 6c82d3869dd..71b6d383d95 100644 --- a/src/states_screens/race_gui_base.cpp +++ b/src/states_screens/race_gui_base.cpp @@ -433,12 +433,17 @@ void RaceGUIBase::drawPowerupIcons(const AbstractKart* kart, int x2 = 0; assert(powerup != NULL); - assert(powerup->getIcon() != NULL); video::ITexture *t; if (powerup->hasWideIcon()) + { + assert(powerup->getIcon(true) != NULL); t=powerup->getIcon(/* wide */ true)->getTexture(); + } else + { + assert(powerup->getIcon() != NULL); t=powerup->getIcon()->getTexture(); + } assert(t != NULL); core::rect rect(core::position2di(0, 0), t->getSize()); From 7ca39d73074859864dba96a941507fc76260ef0b Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Fri, 8 Dec 2023 02:38:08 +0100 Subject: [PATCH 080/188] Fix #3653 Force the AI to follow the same time-full-steer as players, as defined in kart_characteristics.xml --- data/stk_config.xml | 15 +++++---------- src/karts/controller/ai_base_controller.cpp | 5 +++-- src/karts/controller/ai_properties.cpp | 3 --- src/karts/controller/ai_properties.hpp | 4 ---- src/karts/controller/skidding_ai.cpp | 2 +- src/karts/controller/test_ai.cpp | 2 +- 6 files changed, 10 insertions(+), 21 deletions(-) diff --git a/data/stk_config.xml b/data/stk_config.xml index 3f9d764a47d..15bc006e7a2 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -349,8 +349,7 @@ collection in the first place). --> - - - - - 1.0f) steer_fraction = 1.0f; else if(steer_fraction < -1.0f) steer_fraction = -1.0f; + // FIXME this should be handled in AI code, NOT here if(m_kart->getBlockedByPlungerTicks()>0) { if (steer_fraction > 0.5f) steer_fraction = 0.5f; else if(steer_fraction < -0.5f) steer_fraction = -0.5f; } - // The AI has its own 'time full steer' value (which is the time - float max_steer_change = dt/m_ai_properties->m_time_full_steer; + // We use the common time-full-steer from kart_characteristics + float max_steer_change = dt/m_kart->getTimeFullSteer(fabsf(old_steer)); if(old_steer < steer_fraction) { m_controls->setSteer(( old_steer+max_steer_change > steer_fraction) diff --git a/src/karts/controller/ai_properties.cpp b/src/karts/controller/ai_properties.cpp index 99666c10970..423bd3b0986 100644 --- a/src/karts/controller/ai_properties.cpp +++ b/src/karts/controller/ai_properties.cpp @@ -32,7 +32,6 @@ AIProperties::AIProperties(RaceManager::Difficulty difficulty) m_max_item_angle = UNDEFINED; m_max_item_angle_high_speed = UNDEFINED; - m_time_full_steer = UNDEFINED; m_bad_item_closeness_2 = UNDEFINED; m_straight_length_for_zipper = UNDEFINED; m_skidding_threshold = UNDEFINED; @@ -59,7 +58,6 @@ void AIProperties::load(const XMLNode *ai_node) ai_node->get("disable-slipstream-usage", &m_disable_slipstream_usage ); ai_node->get("max-item-angle", &m_max_item_angle ); ai_node->get("max-item-angle-high-speed", &m_max_item_angle_high_speed ); - ai_node->get("time-full-steer", &m_time_full_steer ); ai_node->get("bad-item-closeness", &m_bad_item_closeness_2 ); ai_node->get("collect-item-probability", &m_collect_item_probability ); ai_node->get("straight-length-for-zipper",&m_straight_length_for_zipper); @@ -95,7 +93,6 @@ void AIProperties::checkAllSet(const std::string &filename) const } CHECK_NEG(m_max_item_angle, "max-item-angle" ); CHECK_NEG(m_max_item_angle_high_speed, "max-item-angle-high-speed" ); - CHECK_NEG(m_time_full_steer, "time-full-steer" ); CHECK_NEG(m_bad_item_closeness_2, "bad-item-closeness" ); CHECK_NEG(m_straight_length_for_zipper,"straight-length-for-zipper"); CHECK_NEG(m_skidding_threshold, "skidding-threshold" ); diff --git a/src/karts/controller/ai_properties.hpp b/src/karts/controller/ai_properties.hpp index b3bdcabe88d..ce6db1c867d 100644 --- a/src/karts/controller/ai_properties.hpp +++ b/src/karts/controller/ai_properties.hpp @@ -67,10 +67,6 @@ class AIProperties */ float m_bad_item_closeness_2; - /** Time for AI karts to reach full steer angle (used to reduce shaking - * of karts). */ - float m_time_full_steer; - /** Minimum length of a straight in order to activate a zipper. */ float m_straight_length_for_zipper; diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index 352c9343eff..d8e608d363f 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -3150,7 +3150,7 @@ void SkiddingAI::setSteering(float angle, float dt) float old_steer = m_controls->getSteer(); // The AI has its own 'time full steer' value (which is the time - float max_steer_change = dt/m_ai_properties->m_time_full_steer; + float max_steer_change = dt/m_kart->getTimeFullSteer(fabsf(old_steer)); if(old_steer < steer_fraction) { m_controls->setSteer( (old_steer+max_steer_change > steer_fraction) diff --git a/src/karts/controller/test_ai.cpp b/src/karts/controller/test_ai.cpp index 9a3e5bab059..cf0a78532e2 100644 --- a/src/karts/controller/test_ai.cpp +++ b/src/karts/controller/test_ai.cpp @@ -2492,7 +2492,7 @@ void SkiddingAI::setSteering(float angle, float dt) float old_steer = m_controls->getSteer(); // The AI has its own 'time full steer' value (which is the time - float max_steer_change = dt/m_ai_properties->m_time_full_steer; + float max_steer_change = dt/m_kart->getTimeFullSteer(fabsf(old_steer)); if(old_steer < steer_fraction) { m_controls->setSteer( (old_steer+max_steer_change > steer_fraction) From b8dec8aca9f0e3066fbc27600681b0ffece6335a Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Fri, 8 Dec 2023 02:51:05 +0100 Subject: [PATCH 081/188] Reduce time-full-steer for medium to high difficulties --- data/kart_characteristics.xml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 1ce2178249c..1fa94ecb031 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -76,9 +76,10 @@ In short: steering at less than halfway is somewhat faster, but slower when you want to steer more, which should avoid oversteering (by pressing the key for too long), + time-reset-steer --> + time-full-steer="0:0.16 0.5:0.16 0.5:0.24 1.0:0.24" /> - + diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index f702ad6ceb2..1c55d755920 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -3796,11 +3796,10 @@ void Kart::updateGraphics(float dt) { // Use steering and speed ^ 2, // which means less effect at lower steering and speed. - speed_frac = std::min(speed_frac - 0.6f, 1.0f); + speed_frac = std::min(speed_frac - 0.55f, 1.0f); steer_frac = (steer_frac+0.25f)*0.8f; - const float f = m_skidding->getSteeringFraction(); const float max_lean = -m_kart_properties->getLeanMax() * DEGREE_TO_RAD - * f * speed_frac * speed_frac; + * steer_frac * speed_frac * speed_frac; int max_lean_sign = extract_sign(max_lean); m_current_lean += max_lean_sign * dt* roll_speed; @@ -3823,7 +3822,7 @@ void Kart::updateGraphics(float dt) if (temp_lean > 0.0f) m_current_lean = std::max(temp_lean, m_current_lean); - else + else if (temp_lean < 0.0f) m_current_lean = std::min(temp_lean, m_current_lean); // If the kart is leaning, part of the kart might end up 'in' the track. From c9c21ae6774b5dbbba9bac45668326d1ee84f8fb Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sat, 9 Dec 2023 16:23:32 +0100 Subject: [PATCH 086/188] Clean up some unused function declarations and parameters --- src/achievements/achievement_info.hpp | 1 - src/achievements/achievements_manager.hpp | 1 - src/config/stk_config.hpp | 2 -- src/graphics/irr_driver.hpp | 6 ------ src/guiengine/skin.hpp | 4 ---- src/io/file_manager.cpp | 1 - src/karts/skidding.cpp | 3 +-- src/modes/easter_egg_hunt.hpp | 1 - src/modes/world.hpp | 2 -- src/modes/world_status.hpp | 1 - src/modes/world_with_rank.hpp | 2 -- src/network/protocols/game_protocol.hpp | 1 - src/network/protocols/server_lobby.hpp | 1 - src/physics/btKart.hpp | 1 - src/states_screens/dialogs/achievement_progress_dialog.cpp | 3 +-- src/states_screens/dialogs/select_challenge.cpp | 5 ----- src/states_screens/online/tracks_screen.cpp | 3 ++- src/states_screens/race_setup_screen.hpp | 2 -- src/tracks/track.hpp | 3 --- 19 files changed, 4 insertions(+), 39 deletions(-) diff --git a/src/achievements/achievement_info.hpp b/src/achievements/achievement_info.hpp index a8356a91b22..5cb8929e4d5 100644 --- a/src/achievements/achievement_info.hpp +++ b/src/achievements/achievement_info.hpp @@ -96,7 +96,6 @@ class AchievementInfo std::string getRawName() const { return m_name; } std::string getRawDescription() const { return m_description; } bool isSecret() const { return m_is_secret; } - int allTrackStatus(std::string type); // This function should not be called if copy already has children void copyGoalTree(goalTree ©, goalTree &model, bool set_values_to_zero); diff --git a/src/achievements/achievements_manager.hpp b/src/achievements/achievements_manager.hpp index baa96e1fafe..9d46f574ced 100644 --- a/src/achievements/achievements_manager.hpp +++ b/src/achievements/achievements_manager.hpp @@ -48,7 +48,6 @@ class AchievementsManager AchievementsManager (); ~AchievementsManager (); - AchievementsStatus * createNewSlot(unsigned int id, bool online); public: /** Static function to create the instance of the achievement manager. */ diff --git a/src/config/stk_config.hpp b/src/config/stk_config.hpp index 542c4e10589..31ac310777a 100644 --- a/src/config/stk_config.hpp +++ b/src/config/stk_config.hpp @@ -294,8 +294,6 @@ class STKConfig : public NoCopy void init_defaults(); void getAllData(const XMLNode * root); void load(const std::string &filename); - const std::string &getMainMenuPicture(int n); - const std::string &getBackgroundPicture(int n); void initMusicFiles(); void getAllScores(std::vector *all_scores, int num_karts); // ------------------------------------------------------------------------ diff --git a/src/graphics/irr_driver.hpp b/src/graphics/irr_driver.hpp index 5dcba21c5b9..8f747117dc4 100644 --- a/src/graphics/irr_driver.hpp +++ b/src/graphics/irr_driver.hpp @@ -148,8 +148,6 @@ class IrrDriver : public IEventReceiver, public NoCopy int m_screen_orientation; std::vector m_modes; - void setupViewports(); - /** Whether the mouse cursor is currently shown */ bool m_pointer_shown; @@ -209,7 +207,6 @@ class IrrDriver : public IEventReceiver, public NoCopy void getOpenGLData(std::string *vendor, std::string *renderer, std::string *version); - void increaseObjectCount(); core::array &getMainSetup(); void updateConfigIfRelevant(); core::recti getSplitscreenWindow(int WindowNum); @@ -518,9 +515,6 @@ class IrrDriver : public IEventReceiver, public NoCopy void onLoadWorld(); void onUnloadWorld(); - void updateSplitAndLightcoordRangeFromComputeShaders(size_t width, - size_t height); - void uploadLightingData(); void sameRestart() { m_resolution_changing = RES_CHANGE_SAME; } // ------------------------------------------------------------------------ diff --git a/src/guiengine/skin.hpp b/src/guiengine/skin.hpp index b7c7f0db741..8d8ab3df3be 100644 --- a/src/guiengine/skin.hpp +++ b/src/guiengine/skin.hpp @@ -311,10 +311,6 @@ namespace GUIEngine const bool pressed, bool focused); void drawSpinnerBody(const core::rect< s32 > &rect, Widget* widget, const bool pressed, bool focused); - void drawGauge(const core::rect< s32 > &rect, Widget* widget, - bool focused); - void drawGaugeFill(const core::rect< s32 > &rect, Widget* widget, - bool focused); void drawCheckBox(const core::rect< s32 > &rect, Widget* widget, bool focused); void drawList(const core::rect< s32 > &rect, Widget* widget, diff --git a/src/io/file_manager.cpp b/src/io/file_manager.cpp index ea40c80c206..0b7b94ff471 100644 --- a/src/io/file_manager.cpp +++ b/src/io/file_manager.cpp @@ -883,7 +883,6 @@ bool FileManager::searchTextureContainerId(std::string& container_id, return true; } } - full_path = ""; return false; } // findFile diff --git a/src/karts/skidding.cpp b/src/karts/skidding.cpp index e792627ce0b..69901f7f901 100644 --- a/src/karts/skidding.cpp +++ b/src/karts/skidding.cpp @@ -84,7 +84,6 @@ void Skidding::reset() m_smoothing_dt = -1.0f; m_skid_bonus_end_ticks = -1; - btVector3 rot(0, 0, 0); // Only access the vehicle if the kart is not a ghost if (!m_kart->isGhostKart()) m_kart->getVehicle()->setTimedRotation(0, 0); @@ -528,7 +527,7 @@ void Skidding::update(int ticks, bool is_on_ground, unsigned int level = getSkidBonus(&bonus_time, &bonus_speed, &bonus_force); - if (level >= 1 && !(level == 0 && m_remaining_jump_time <= 0.0f)) + if (level >= 1) { m_skid_bonus_ready = true; } diff --git a/src/modes/easter_egg_hunt.hpp b/src/modes/easter_egg_hunt.hpp index 7ae432c1827..f0d7a32768e 100644 --- a/src/modes/easter_egg_hunt.hpp +++ b/src/modes/easter_egg_hunt.hpp @@ -72,7 +72,6 @@ class EasterEggHunt: public LinearWorld const int numberOfEggsFound() { return m_eggs_found; } const int numberOfEggsToFind() { return m_number_of_eggs; } - void updateKartRanks(); void readData(const std::string &filename); virtual void checkForWrongDirection(unsigned int i, float dt) OVERRIDE; diff --git a/src/modes/world.hpp b/src/modes/world.hpp index 339fc3923c5..797880bfa64 100644 --- a/src/modes/world.hpp +++ b/src/modes/world.hpp @@ -324,8 +324,6 @@ class World : public WorldStatus void scheduleExitRace() { m_schedule_exit_race = true; } void scheduleTutorial(); void updateWorld(int ticks); - void handleExplosion(const Vec3 &xyz, Kart *kart_hit, - PhysicalObject *object); Kart* getPlayerKart(unsigned int player) const; Kart* getLocalPlayerKart(unsigned int n) const; virtual const btTransform &getStartTransform(int index); diff --git a/src/modes/world_status.hpp b/src/modes/world_status.hpp index 88535e64dff..06417a34bac 100644 --- a/src/modes/world_status.hpp +++ b/src/modes/world_status.hpp @@ -157,7 +157,6 @@ class WorldStatus virtual void reset(bool restart); virtual void updateTime(int ticks); virtual void update(int ticks); - void startReadySetGo(); virtual void pause(Phase phase); virtual void unpause(); virtual void enterRaceOverState(); diff --git a/src/modes/world_with_rank.hpp b/src/modes/world_with_rank.hpp index ca13a4e4c23..9c2c8a37595 100644 --- a/src/modes/world_with_rank.hpp +++ b/src/modes/world_with_rank.hpp @@ -59,8 +59,6 @@ class WorldWithRank : public World bool m_position_setting_initialised; #endif - unsigned int getClosestStartPoint(Kart *kart); - /** Stores the current graph node and track coordinates for each kart. */ std::vector m_kart_track_sector; diff --git a/src/network/protocols/game_protocol.hpp b/src/network/protocols/game_protocol.hpp index fa4c28d9f3e..70969e42a5e 100644 --- a/src/network/protocols/game_protocol.hpp +++ b/src/network/protocols/game_protocol.hpp @@ -76,7 +76,6 @@ class GameProtocol : public Protocol void handleControllerAction(Event *event); void handleState(Event *event); - void handleAdjustTime(Event *event); void handleItemEventConfirmation(Event *event); static std::weak_ptr m_game_protocol[PT_COUNT]; NetworkItemManager* m_network_item_manager; diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index 54aebc927aa..3d393379716 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -337,7 +337,6 @@ class ServerLobby : public LobbyProtocol double getModeFactor(); double getModeSpread(); double getTimeSpread(double time); - double getUncertaintySpread(uint32_t online_id); double scalingValueForTime(double time); double computeH2HResult(double player1_time, double player2_time); double computeDataAccuracy(double player1_rd, double player2_rd, diff --git a/src/physics/btKart.hpp b/src/physics/btKart.hpp index d894fc17d36..c07b9041755 100644 --- a/src/physics/btKart.hpp +++ b/src/physics/btKart.hpp @@ -180,7 +180,6 @@ class btKart : public btActionInterface void updateSuspension(btScalar deltaTime); virtual void updateFriction(btScalar timeStep); void setSliding(bool active); - void instantSpeedIncreaseTo(btScalar speed); void adjustSpeed(btScalar min_speed, btScalar max_speed); void updateAllWheelPositions(); void getVisualContactPoint(const btTransform& chassis_trans, diff --git a/src/states_screens/dialogs/achievement_progress_dialog.cpp b/src/states_screens/dialogs/achievement_progress_dialog.cpp index 1a7c248d19b..8a3d2247fe0 100644 --- a/src/states_screens/dialogs/achievement_progress_dialog.cpp +++ b/src/states_screens/dialogs/achievement_progress_dialog.cpp @@ -82,8 +82,7 @@ void AchievementProgressDialog::recursiveFillTable(AchievementInfo::goalTree &pr { if (progress.children.size() != 1) { - int goal = -1; // Will be filled with goals or progress - int target = -1; + int goal, target; // Will be filled with goals or progress if (progress.type == "AND" || progress.type == "AND-AT-ONCE" || diff --git a/src/states_screens/dialogs/select_challenge.cpp b/src/states_screens/dialogs/select_challenge.cpp index f46563c05d1..e23f5ccdbbe 100644 --- a/src/states_screens/dialogs/select_challenge.cpp +++ b/src/states_screens/dialogs/select_challenge.cpp @@ -276,11 +276,6 @@ GUIEngine::EventPropagation SelectChallengeDialog::processEvent(const std::strin ModalDialog::dismiss(); - core::rect pos(15, - 10, - 15 + UserConfigParams::m_width/2, - 10 + GUIEngine::getTitleFontHeight()); - RaceManager::get()->exitRace(); //StateManager::get()->resetActivePlayers(); diff --git a/src/states_screens/online/tracks_screen.cpp b/src/states_screens/online/tracks_screen.cpp index a47d72f7ba9..25a86d1b67b 100644 --- a/src/states_screens/online/tracks_screen.cpp +++ b/src/states_screens/online/tracks_screen.cpp @@ -785,7 +785,8 @@ void TracksScreen::setResult(uint32_t winner_host, if (!m_timer || winner_host == std::numeric_limits::max() || !m_vote_list) return; - if (m_timer) m_timer->setVisible(false); + + m_timer->setVisible(false); m_winning_index = winner_host; if (auto lp = LobbyProtocol::get()) diff --git a/src/states_screens/race_setup_screen.hpp b/src/states_screens/race_setup_screen.hpp index 60ca8bfe2d0..cbccbe02889 100644 --- a/src/states_screens/race_setup_screen.hpp +++ b/src/states_screens/race_setup_screen.hpp @@ -32,8 +32,6 @@ class RaceSetupScreen : public GUIEngine::Screen, public GUIEngine::ScreenSingle RaceSetupScreen(); - void onGameModeChanged(); - void assignDifficulty(); public: diff --git a/src/tracks/track.hpp b/src/tracks/track.hpp index 47922b1156d..7dda86e9d81 100644 --- a/src/tracks/track.hpp +++ b/src/tracks/track.hpp @@ -476,9 +476,6 @@ class Track core::stringw getSortName() const; bool isInGroup(const std::string &group_name); const core::vector3df& getSunRotation(); - /** Sets the current ambient color for a kart with index k. */ - void setAmbientColor(const video::SColor &color, - unsigned int k); void handleExplosion(const Vec3 &pos, const PhysicalObject *mp, bool secondary_hits=true) const; From aceca1b8503aa3a5515e438b0171813ae14e948d Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sat, 9 Dec 2023 17:27:11 +0100 Subject: [PATCH 087/188] More clean up --- src/karts/cannon_animation.cpp | 1 - src/karts/skidding.cpp | 10 +++------- src/physics/btKart.cpp | 8 +------- src/physics/physics.cpp | 4 ++-- src/race/race_manager.cpp | 4 ++-- 5 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/karts/cannon_animation.cpp b/src/karts/cannon_animation.cpp index 7e653ff1bfc..6632bf9cc98 100644 --- a/src/karts/cannon_animation.cpp +++ b/src/karts/cannon_animation.cpp @@ -25,7 +25,6 @@ #include "karts/kart.hpp" #include "karts/kart_model.hpp" #include "karts/kart_properties.hpp" -#include "modes/world.hpp" #include "network/network_string.hpp" #include "tracks/check_cannon.hpp" #include "tracks/check_manager.hpp" diff --git a/src/karts/skidding.cpp b/src/karts/skidding.cpp index 69901f7f901..2cd03f6df47 100644 --- a/src/karts/skidding.cpp +++ b/src/karts/skidding.cpp @@ -455,14 +455,10 @@ void Skidding::update(int ticks, bool is_on_ground, // Some karts might use a graphical-only jump. Set it up: m_remaining_jump_time = kp->getSkidGraphicalJumpTime(); // Don't re-update for local player controller when rewinding - if (m_graphical_remaining_jump_time == 0.0f) - { - if (RewindManager::get()->isRewinding() && - !m_kart->getController()->isLocalPlayerController()) - m_graphical_remaining_jump_time = m_remaining_jump_time; - else if (!RewindManager::get()->isRewinding()) + if (m_graphical_remaining_jump_time == 0.0f && + (!RewindManager::get()->isRewinding() || + !m_kart->getController()->isLocalPlayerController())) m_graphical_remaining_jump_time = m_remaining_jump_time; - } #ifdef SKID_DEBUG #define SPEED 20.0f diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp index ae22b535445..7ad0de5bf08 100644 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -215,12 +215,6 @@ void btKart::updateAllWheelTransformsWS() { updateAllWheelPositions(); - const btTransform& chassisTrans = getChassisWorldTransform(); - - btVector3 forwardW(chassisTrans.getBasis()[0][m_indexForwardAxis], - chassisTrans.getBasis()[1][m_indexForwardAxis], - chassisTrans.getBasis()[2][m_indexForwardAxis]); - // Simulate suspension // ------------------- @@ -567,7 +561,7 @@ float btKart::getCollisionLean() const if (lean_factor > 0.5f) lean_factor = 1.0f - lean_factor; - if (m_leaning_right == false) + if (!m_leaning_right) lean_factor = -lean_factor; return 0.35f*m_leaning_factor*lean_factor; diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index 85d2e3b5ce9..94d2d28cc0f 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -254,7 +254,7 @@ void Physics::update(int ticks) lib_id = library->getID(); lib_id_ptr = &lib_id; - if (!is_child && scripting_function.size() > 0) + if (!is_child && !scripting_function.empty()) { Scripting::ScriptEngine* script_engine = Scripting::ScriptEngine::getInstance(); script_engine->runFunction(true, "void " + scripting_function + "(int, const string, const string)", @@ -347,7 +347,7 @@ void Physics::update(int ticks) PhysicalObject* obj = p->getUserPointer(1)->getPointerPhysicalObject(); std::string obj_id = obj->getID(); std::string scripting_function = obj->getOnItemCollisionFunction(); - if (!is_child && scripting_function.size() > 0) + if (!is_child && !scripting_function.empty()) { Scripting::ScriptEngine* script_engine = Scripting::ScriptEngine::getInstance(); script_engine->runFunction(true, "void " + scripting_function + "(int, int, const string)", diff --git a/src/race/race_manager.cpp b/src/race/race_manager.cpp index 8a639bbb563..1e17c7653a2 100644 --- a/src/race/race_manager.cpp +++ b/src/race/race_manager.cpp @@ -364,9 +364,9 @@ void RaceManager::computeRandomKartList() if (m_ai_kart_override != "") { - for (unsigned int n = 0; n < m_ai_kart_list.size(); n++) + for (unsigned int i = 0; i < m_ai_kart_list.size(); i++) { - m_ai_kart_list[n] = m_ai_kart_override; + m_ai_kart_list[i] = m_ai_kart_override; } } From 6eca82123fa9119539ae81d01d820897dd11b151 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 10 Dec 2023 19:02:07 +0100 Subject: [PATCH 088/188] More minor clean up --- src/karts/ghost_kart.cpp | 1 - src/karts/kart.cpp | 49 ++++++++++++++++-------------------- src/modes/soccer_world.cpp | 14 +++-------- src/race/grand_prix_data.cpp | 1 - src/race/highscores.cpp | 7 +++--- src/race/highscores.hpp | 3 +-- 6 files changed, 30 insertions(+), 45 deletions(-) diff --git a/src/karts/ghost_kart.cpp b/src/karts/ghost_kart.cpp index 5c8e242324c..35a753cf7f5 100644 --- a/src/karts/ghost_kart.cpp +++ b/src/karts/ghost_kart.cpp @@ -234,7 +234,6 @@ void GhostKart::update(int ticks) m_is_jumping = false; getKartModel()->setAnimation(KartModel::AF_DEFAULT); } - } // update // ---------------------------------------------------------------------------- diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 1c55d755920..1ecdd8e3234 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1084,10 +1084,8 @@ void Kart::finishedRace(float time, bool from_server) // it would trigger a race end again. if (m_finished_race) return; - const bool is_linear_race = - RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_NORMAL_RACE || - RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL || - RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER; + const bool is_linear_race = RaceManager::get()->isLinearRaceMode(); + if (NetworkConfig::get()->isNetworking() && !from_server) { if (NetworkConfig::get()->isServer()) @@ -1210,27 +1208,23 @@ void Kart::setRaceResult() RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL || RaceManager::get()->isLapTrialMode()) { + bool standard_result_animation = true; if (m_controller->isLocalPlayerController()) // if player is on this computer { - PlayerProfile *player = PlayerManager::getCurrentPlayer(); - const ChallengeStatus *challenge = player->getCurrentChallengeStatus(); + const ChallengeStatus *challenge = PlayerManager::getCurrentPlayer()->getCurrentChallengeStatus(); // In case of a GP challenge don't make the end animation depend // on if the challenge is fulfilled if (challenge && !challenge->getData()->isGrandPrix()) { + standard_result_animation = false; if (challenge->getData()->isChallengeFulfilled()) m_race_result = true; else m_race_result = false; } - else if (this->getPosition() <= 0.5f * - World::getWorld()->getCurrentNumKarts() || - this->getPosition() == 1) - m_race_result = true; - else - m_race_result = false; } - else + // Display a happy animation if in the first half, sad otherwise + if (standard_result_animation) { if (this->getPosition() <= 0.5f * World::getWorld()->getCurrentNumKarts() || @@ -1240,7 +1234,7 @@ void Kart::setRaceResult() m_race_result = false; } } - else if (RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER || + else if (RaceManager::get()->isFollowMode() || RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_3_STRIKES) { // the kart wins if it isn't eliminated @@ -1261,7 +1255,7 @@ void Kart::setRaceResult() SoccerWorld* sw = dynamic_cast(World::getWorld()); m_race_result = sw->getKartSoccerResult(this->getWorldKartId()); } - else if (RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_EASTER_EGG) + else if (RaceManager::get()->isEggHuntMode()) { // Easter egg mode only has one player, so always win m_race_result = true; @@ -2609,22 +2603,23 @@ void Kart::setStolenNitro(float amount, float duration) void Kart::updateNitro(int ticks) { if (m_stolen_nitro_ticks > 0) - m_stolen_nitro_ticks -= ticks; - if (m_nitro_hack_ticks > 0) - m_nitro_hack_ticks -= ticks; - - // Reset the stolen nitro amount when the display duration expires - if (m_stolen_nitro_ticks <= 0) { - m_stolen_nitro_ticks = 0; - m_stolen_nitro_amount = 0.0f; + // Decrement ticks and, if now negative, reset relevant variables + if ((m_stolen_nitro_ticks -= ticks) <= 0) + { + m_stolen_nitro_ticks = 0; + m_stolen_nitro_amount = 0.0f; + } } - // Reset the nitro multiplier when the nitro-hack expires - if (m_nitro_hack_ticks <= 0) + if (m_nitro_hack_ticks > 0) { - m_nitro_hack_ticks = 0; - m_nitro_hack_factor = 1.0f; + // Decrement ticks and, if now negative, reset relevant variables + if ((m_nitro_hack_ticks -= ticks) <= 0) + { + m_nitro_hack_ticks = 0; + m_nitro_hack_factor = 1.0f; + } } if (m_collected_energy <= 0) diff --git a/src/modes/soccer_world.cpp b/src/modes/soccer_world.cpp index 50b7ca4a61b..d29d6abde11 100644 --- a/src/modes/soccer_world.cpp +++ b/src/modes/soccer_world.cpp @@ -860,16 +860,10 @@ int SoccerWorld::getBallNode() const bool SoccerWorld::isCorrectGoal(unsigned int kart_id, bool first_goal) const { KartTeam team = getKartTeam(kart_id); - if (first_goal) - { - if (team == KART_TEAM_RED) - return true; - } - else if (!first_goal) - { - if (team == KART_TEAM_BLUE) - return true; - } + if (( first_goal && team == KART_TEAM_RED ) || + (!first_goal && team == KART_TEAM_BLUE)) + return true; + return false; } // isCorrectGoal diff --git a/src/race/grand_prix_data.cpp b/src/race/grand_prix_data.cpp index d4354634d25..aa0d7bffda0 100644 --- a/src/race/grand_prix_data.cpp +++ b/src/race/grand_prix_data.cpp @@ -646,7 +646,6 @@ irr::core::stringw GrandPrixData::reverseTypeToString(GPReverseType reverse_type default: return "N/A"; } - return "N/A"; } /* EOF */ diff --git a/src/race/highscores.cpp b/src/race/highscores.cpp index ae1605bc0a3..d9cd07a3e33 100644 --- a/src/race/highscores.cpp +++ b/src/race/highscores.cpp @@ -211,8 +211,7 @@ int Highscores::matches(int num_karts, m_gp_minor_mode == minor_mode ); } -int Highscores::findHighscorePosition(const std::string& kart_name, - const core::stringw& name, const float time) +int Highscores::findHighscorePosition(const float time) { int position=-1; for(int i=0; i=0) { m_track = RaceManager::get()->getTrackName(); @@ -271,7 +270,7 @@ int Highscores::addData(const std::string& kart_name, int Highscores::addGPData(const std::string& kart_name, const core::stringw& name, std::string track_name, const float time) { - int position = findHighscorePosition(kart_name, name, time); + int position = findHighscorePosition(time); if(position>=0) { m_track = track_name; diff --git a/src/race/highscores.hpp b/src/race/highscores.hpp index 17f1c56e4dd..7fa65331803 100644 --- a/src/race/highscores.hpp +++ b/src/race/highscores.hpp @@ -70,8 +70,7 @@ class Highscores static SortOrder m_sort_order; - int findHighscorePosition(const std::string& kart_name, - const core::stringw& name, const float time); + int findHighscorePosition(const float time); public: bool operator < (const Highscores& hi) const; From 6c7660a5cbdc4b62417b1b7ed971d6e8250ffcd7 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 10 Dec 2023 20:21:23 +0100 Subject: [PATCH 089/188] Support new attachment and powerups in replays --- src/replay/replay_recorder.cpp | 50 ++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/src/replay/replay_recorder.cpp b/src/replay/replay_recorder.cpp index b53a74bad32..b52d92317d3 100644 --- a/src/replay/replay_recorder.cpp +++ b/src/replay/replay_recorder.cpp @@ -496,6 +496,7 @@ int ReplayRecorder::enumToCode (Attachment::AttachmentType type) (type == Attachment::ATTACH_BOMB) ? 3 : (type == Attachment::ATTACH_SWATTER) ? 4 : (type == Attachment::ATTACH_BUBBLEGUM_SHIELD) ? 5 : + (type == Attachment::ATTACH_ELECTRO_SHIELD) ? 6 : -1 ; return code; @@ -507,16 +508,20 @@ int ReplayRecorder::enumToCode (Attachment::AttachmentType type) int ReplayRecorder::enumToCode (PowerupManager::PowerupType type) { int code = - (type == PowerupManager::POWERUP_NOTHING) ? 0 : - (type == PowerupManager::POWERUP_BUBBLEGUM) ? 1 : - (type == PowerupManager::POWERUP_CAKE) ? 2 : - (type == PowerupManager::POWERUP_BOWLING) ? 3 : - (type == PowerupManager::POWERUP_ZIPPER) ? 4 : - (type == PowerupManager::POWERUP_PLUNGER) ? 5 : - (type == PowerupManager::POWERUP_SWITCH) ? 6 : - (type == PowerupManager::POWERUP_SWATTER) ? 7 : - (type == PowerupManager::POWERUP_RUBBERBALL) ? 8 : - (type == PowerupManager::POWERUP_PARACHUTE) ? 9 : + (type == PowerupManager::POWERUP_NOTHING) ? 0 : + (type == PowerupManager::POWERUP_BUBBLEGUM) ? 1 : + (type == PowerupManager::POWERUP_CAKE) ? 2 : + (type == PowerupManager::POWERUP_BOWLING) ? 3 : + (type == PowerupManager::POWERUP_ZIPPER) ? 4 : + (type == PowerupManager::POWERUP_PLUNGER) ? 5 : + (type == PowerupManager::POWERUP_SWITCH) ? 6 : + (type == PowerupManager::POWERUP_SWATTER) ? 7 : + (type == PowerupManager::POWERUP_RUBBERBALL) ? 8 : + (type == PowerupManager::POWERUP_PARACHUTE) ? 9 : + (type == PowerupManager::POWERUP_SUDO) ? 10 : + (type == PowerupManager::POWERUP_ELECTRO) ? 11 : + (type == PowerupManager::POWERUP_MINI) ? 12 : + (type == PowerupManager::POWERUP_ANVIL) ? 13 : -1 ; return code; @@ -532,6 +537,7 @@ Attachment::AttachmentType ReplayRecorder::codeToEnumAttach (int code) (code == 3) ? Attachment::ATTACH_BOMB : (code == 4) ? Attachment::ATTACH_SWATTER : (code == 5) ? Attachment::ATTACH_BUBBLEGUM_SHIELD : + (code == 6) ? Attachment::ATTACH_ELECTRO_SHIELD : Attachment::ATTACH_NOTHING ; return type; @@ -541,16 +547,20 @@ Attachment::AttachmentType ReplayRecorder::codeToEnumAttach (int code) PowerupManager::PowerupType ReplayRecorder::codeToEnumItem (int code) { PowerupManager::PowerupType type = - (code == 0) ? PowerupManager::POWERUP_NOTHING : - (code == 1) ? PowerupManager::POWERUP_BUBBLEGUM : - (code == 2) ? PowerupManager::POWERUP_CAKE : - (code == 3) ? PowerupManager::POWERUP_BOWLING : - (code == 4) ? PowerupManager::POWERUP_ZIPPER : - (code == 5) ? PowerupManager::POWERUP_PLUNGER : - (code == 6) ? PowerupManager::POWERUP_SWITCH : - (code == 7) ? PowerupManager::POWERUP_SWATTER : - (code == 8) ? PowerupManager::POWERUP_RUBBERBALL : - (code == 9) ? PowerupManager::POWERUP_PARACHUTE : + (code == 0) ? PowerupManager::POWERUP_NOTHING : + (code == 1) ? PowerupManager::POWERUP_BUBBLEGUM : + (code == 2) ? PowerupManager::POWERUP_CAKE : + (code == 3) ? PowerupManager::POWERUP_BOWLING : + (code == 4) ? PowerupManager::POWERUP_ZIPPER : + (code == 5) ? PowerupManager::POWERUP_PLUNGER : + (code == 6) ? PowerupManager::POWERUP_SWITCH : + (code == 7) ? PowerupManager::POWERUP_SWATTER : + (code == 8) ? PowerupManager::POWERUP_RUBBERBALL : + (code == 9) ? PowerupManager::POWERUP_PARACHUTE : + (code == 10) ? PowerupManager::POWERUP_SUDO : + (code == 11) ? PowerupManager::POWERUP_ELECTRO : + (code == 12) ? PowerupManager::POWERUP_MINI : + (code == 13) ? PowerupManager::POWERUP_ANVIL : PowerupManager::POWERUP_NOTHING ; return type; From 4f008542412a5a9200bbd87de65e855bf19b03bd Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 10 Dec 2023 23:54:33 +0100 Subject: [PATCH 090/188] Make skidding fade-out time configurable --- data/kart_characteristics.xml | 4 +++- src/karts/abstract_characteristic.cpp | 16 ++++++++++++++ src/karts/abstract_characteristic.hpp | 2 ++ src/karts/kart_properties.cpp | 6 ++++++ src/karts/kart_properties.hpp | 1 + src/karts/skidding.cpp | 30 +++++++++++++++------------ src/karts/skidding.hpp | 4 ++-- src/karts/xml_characteristic.cpp | 2 ++ 8 files changed, 49 insertions(+), 16 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 452cc7d69b3..9497bf92cec 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -149,6 +149,8 @@ time-till-bonus: How long a kart needs to skid in order to get a bonus. bonus-speed: A speedup applied to the kart whick skidded for a while. bonus-time: How long the bonus-force is applied. + fade-out-time: How long will the max-speed bonus take to fade out. + The additional engine force is not active during fade-out. bonus-force: Additional engine force (this is used to offset the fact that turning after skidding (e.g. to correct direction) often uses up the skid bonus). @@ -169,7 +171,7 @@ visual="1.25" visual-time="0.7" revert-visual-time="0.7" min-speed="10" time-till-bonus="1.0 2.7 4.0" bonus-speed="4.0 6.0 8.0" bonus-time="3.0 4.3 5.7" - bonus-force="300 400 600" + fade-out-time="1.0 1.0 1.0" bonus-force="300 400 600" physical-jump-time="0" graphical-jump-time="0.4" post-skid-rotate-factor="1" reduce-turn-min="0.2" reduce-turn-max="0.8" enabled="true" /> diff --git a/src/karts/abstract_characteristic.cpp b/src/karts/abstract_characteristic.cpp index 25f2d4a4055..d7fdea7a103 100644 --- a/src/karts/abstract_characteristic.cpp +++ b/src/karts/abstract_characteristic.cpp @@ -275,6 +275,8 @@ AbstractCharacteristic::ValueType AbstractCharacteristic::getType( return TYPE_FLOAT_VECTOR; case SKID_BONUS_TIME: return TYPE_FLOAT_VECTOR; + case SKID_FADE_OUT_TIME: + return TYPE_FLOAT_VECTOR; case SKID_BONUS_FORCE: return TYPE_FLOAT_VECTOR; case SKID_PHYSICAL_JUMP_TIME: @@ -529,6 +531,8 @@ std::string AbstractCharacteristic::getName(CharacteristicType type) return "SKID_BONUS_SPEED"; case SKID_BONUS_TIME: return "SKID_BONUS_TIME"; + case SKID_FADE_OUT_TIME: + return "SKID_FADE_OUT_TIME"; case SKID_BONUS_FORCE: return "SKID_BONUS_FORCE"; case SKID_PHYSICAL_JUMP_TIME: @@ -1875,6 +1879,18 @@ std::vector AbstractCharacteristic::getSkidBonusTime() const return result; } // getSkidBonusTime +// ---------------------------------------------------------------------------- +std::vector AbstractCharacteristic::getSkidFadeOutTime() const +{ + std::vector result; + bool is_set = false; + process(SKID_FADE_OUT_TIME, &result, &is_set); + if (!is_set) + Log::fatal("AbstractCharacteristic", "Can't get characteristic %s", + getName(SKID_FADE_OUT_TIME).c_str()); + return result; +} // getSkidFadeOutTime + // ---------------------------------------------------------------------------- std::vector AbstractCharacteristic::getSkidBonusForce() const { diff --git a/src/karts/abstract_characteristic.hpp b/src/karts/abstract_characteristic.hpp index 30c781ca75f..23ed168e439 100644 --- a/src/karts/abstract_characteristic.hpp +++ b/src/karts/abstract_characteristic.hpp @@ -228,6 +228,7 @@ class AbstractCharacteristic SKID_TIME_TILL_BONUS, SKID_BONUS_SPEED, SKID_BONUS_TIME, + SKID_FADE_OUT_TIME, SKID_BONUS_FORCE, SKID_PHYSICAL_JUMP_TIME, SKID_GRAPHICAL_JUMP_TIME, @@ -405,6 +406,7 @@ class AbstractCharacteristic std::vector getSkidTimeTillBonus() const; std::vector getSkidBonusSpeed() const; std::vector getSkidBonusTime() const; + std::vector getSkidFadeOutTime() const; std::vector getSkidBonusForce() const; float getSkidPhysicalJumpTime() const; float getSkidGraphicalJumpTime() const; diff --git a/src/karts/kart_properties.cpp b/src/karts/kart_properties.cpp index 37c713fa86b..ff1f0083649 100644 --- a/src/karts/kart_properties.cpp +++ b/src/karts/kart_properties.cpp @@ -1389,6 +1389,12 @@ std::vector KartProperties::getSkidBonusTime() const return m_cached_characteristic->getSkidBonusTime(); } // getSkidBonusTime +// ---------------------------------------------------------------------------- +std::vector KartProperties::getSkidFadeOutTime() const +{ + return m_cached_characteristic->getSkidFadeOutTime(); +} // getSkidFadeOutTime + // ---------------------------------------------------------------------------- std::vector KartProperties::getSkidBonusForce() const { diff --git a/src/karts/kart_properties.hpp b/src/karts/kart_properties.hpp index b4708c5468d..ced96e896b4 100644 --- a/src/karts/kart_properties.hpp +++ b/src/karts/kart_properties.hpp @@ -518,6 +518,7 @@ class KartProperties std::vector getSkidTimeTillBonus() const; std::vector getSkidBonusSpeed() const; std::vector getSkidBonusTime() const; + std::vector getSkidFadeOutTime() const; std::vector getSkidBonusForce() const; float getSkidPhysicalJumpTime() const; float getSkidGraphicalJumpTime() const; diff --git a/src/karts/skidding.cpp b/src/karts/skidding.cpp index 2cd03f6df47..c1430cbfc0b 100644 --- a/src/karts/skidding.cpp +++ b/src/karts/skidding.cpp @@ -266,8 +266,8 @@ float Skidding::updateGraphics(float dt) m_kart->getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDR, 0); m_kart->getKartGFX()->updateSkidLight(0); - float bonus_time, bonus_speed, bonus_force; - unsigned int level = getSkidBonus(&bonus_time, &bonus_speed, &bonus_force); + float bonus_time, fade_out_time, bonus_speed, bonus_force; + unsigned int level = getSkidBonus(&bonus_time, &fade_out_time, &bonus_speed, &bonus_force); if (m_skid_bonus_end_ticks > World::getWorld()->getTicksSinceStart()) { @@ -519,9 +519,9 @@ void Skidding::update(int ticks, bool is_on_ground, m_kart->getMaxSteerAngle(m_kart->getSpeed())); #endif m_skid_time += ticks; - float bonus_time, bonus_speed, bonus_force; - unsigned int level = getSkidBonus(&bonus_time, &bonus_speed, - &bonus_force); + float bonus_time, fade_out_time, bonus_speed, bonus_force; + unsigned int level = getSkidBonus(&bonus_time, &fade_out_time, + &bonus_speed, &bonus_force); if (level >= 1) { @@ -553,7 +553,7 @@ void Skidding::update(int ticks, bool is_on_ground, bonus_speed, bonus_speed/2, bonus_force, stk_config->time2Ticks(bonus_time), - /*fade-out-time*/ stk_config->time2Ticks(1.0f)); + stk_config->time2Ticks(fade_out_time)); m_skid_bonus_end_ticks = World::getWorld()->getTicksSinceStart() + stk_config->time2Ticks(1.0f); @@ -595,27 +595,31 @@ void Skidding::update(int ticks, bool is_on_ground, /** Determines the bonus time and speed given the currently accumulated * m_skid_time. * \param bonus_time On return contains how long the bonus should be active. + * \param fade_out_time * \param bonus_speed How much additional speed the kart should get. * \param bonus_force Additional engine force. * \return The bonus level: 0 = no bonus, 1 = first entry in bonus array etc. */ unsigned int Skidding::getSkidBonus(float *bonus_time, + float *fade_out_time, float *bonus_speed, float *bonus_force) const { const KartProperties *kp = m_kart->getKartProperties(); - *bonus_time = 0; - *bonus_speed = 0; - *bonus_force = 0; + *bonus_time = 0; + *fade_out_time = 0; + *bonus_speed = 0; + *bonus_force = 0; for (unsigned int i = 0; i < kp->getSkidBonusSpeed().size(); i++) { if (stk_config->ticks2Time(m_skid_time) <= kp->getSkidTimeTillBonus()[i]) return i; - *bonus_speed = kp->getSkidBonusSpeed()[i]; - *bonus_time = kp->getSkidBonusTime()[i]; - *bonus_force = kp->getSkidBonusForce()[i]; + *bonus_speed = kp->getSkidBonusSpeed()[i]; + *fade_out_time = kp->getSkidFadeOutTime()[i]; + *bonus_time = kp->getSkidBonusTime()[i]; + *bonus_force = kp->getSkidBonusForce()[i]; } return (unsigned int) kp->getSkidBonusSpeed().size(); -} // getSkidBonusForce +} // getSkidBonus diff --git a/src/karts/skidding.hpp b/src/karts/skidding.hpp index c54ca14e674..bcf31aa4ce2 100644 --- a/src/karts/skidding.hpp +++ b/src/karts/skidding.hpp @@ -101,8 +101,8 @@ friend class KartRewinder; ShowCurve *m_actual_curve; #endif - unsigned int getSkidBonus(float *bonus_time, float *bonus_speed, - float *bonus_force) const; + unsigned int getSkidBonus(float *bonus_time, float *fade_out_time, + float *bonus_speed, float *bonus_force) const; float updateSteering(float steer, int ticks); public: Skidding(Kart *kart); diff --git a/src/karts/xml_characteristic.cpp b/src/karts/xml_characteristic.cpp index 2961ec62088..43a69c4b7dd 100644 --- a/src/karts/xml_characteristic.cpp +++ b/src/karts/xml_characteristic.cpp @@ -629,6 +629,8 @@ void XmlCharacteristic::load(const XMLNode *node) &m_values[SKID_BONUS_SPEED]); sub_node->get("bonus-time", &m_values[SKID_BONUS_TIME]); + sub_node->get("fade-out-time", + &m_values[SKID_FADE_OUT_TIME]); sub_node->get("bonus-force", &m_values[SKID_BONUS_FORCE]); sub_node->get("physical-jump-time", From 6cfe43037cd24c3e253931a943438a8c31fe71e5 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Mon, 11 Dec 2023 05:34:43 +0100 Subject: [PATCH 091/188] Skidding code cleanup and bugfixes - Split getSkidBonus into getSkidBonus and getSkidLevel, to avoid passing and assigning floats pointlessly when their values is irrelevant - Rewrite logic to first determine the level, then assign the correct values, instead of assigning repeatedly in the loop - Fix a bug that had the incorrect skidding graphics if a higher-level bonus was triggered with a lower one still active. This bug also existed in 1.X, but the overlap between level 1 and 2 was as good as non-existent with default values, whereas overlap between levels is common now. - Multiple code simplifications --- src/karts/max_speed.cpp | 9 +++ src/karts/max_speed.hpp | 6 ++ src/karts/skidding.cpp | 148 ++++++++++++++++------------------------ src/karts/skidding.hpp | 2 + 4 files changed, 76 insertions(+), 89 deletions(-) diff --git a/src/karts/max_speed.cpp b/src/karts/max_speed.cpp index e18b1d15241..87a896f7d72 100644 --- a/src/karts/max_speed.cpp +++ b/src/karts/max_speed.cpp @@ -56,6 +56,7 @@ MaxSpeed::MaxSpeed(Kart *kart) m_add_engine_force = 0; // This can be used if command line option -N is used m_current_max_speed = 0; + m_last_triggered_skid_level = 0; } // MaxSpeed // ---------------------------------------------------------------------------- @@ -67,6 +68,7 @@ void MaxSpeed::reset() { m_current_max_speed = m_kart->getKartProperties()->getEngineMaxSpeed(); m_min_speed = -1.0f; + m_last_triggered_skid_level = 0; for(unsigned int i=MS_DECREASE_MIN; i 65535) { diff --git a/src/karts/max_speed.hpp b/src/karts/max_speed.hpp index 9635b7b40cb..24a45e44623 100644 --- a/src/karts/max_speed.hpp +++ b/src/karts/max_speed.hpp @@ -67,6 +67,10 @@ friend class KartRewinder; /** If >0 then the minimum speed a kart should have (used for zippers). */ float m_min_speed; + /** Used for display of skid particles after triggering a skid bonus. + * 0 is no bonus, 1 is first-stage bonus, etc. */ + uint8_t m_last_triggered_skid_level; + // ------------------------------------------------------------------------ /** An internal class to store and handle speed increase related data. */ class SpeedIncrease @@ -217,6 +221,8 @@ friend class KartRewinder; // ------------------------------------------------------------------------ /** Returns the current maximum speed for this kart. */ float getCurrentMaxSpeed() const { return m_current_max_speed; } + // -------------------------------------------------------------------- + unsigned int getLatestSkidLevel() const { return m_last_triggered_skid_level; } // ------------------------------------------------------------------------ /** Returns the additional engine force. */ float getCurrentAdditionalEngineForce() const { return m_add_engine_force;} diff --git a/src/karts/skidding.cpp b/src/karts/skidding.cpp index c1430cbfc0b..031cb802d6a 100644 --- a/src/karts/skidding.cpp +++ b/src/karts/skidding.cpp @@ -25,7 +25,6 @@ #include "config/player_manager.hpp" #include "karts/kart.hpp" #include "karts/kart_gfx.hpp" -#include "karts/kart_properties.hpp" #include "karts/max_speed.hpp" #include "karts/controller/controller.hpp" #include "modes/world.hpp" @@ -149,6 +148,7 @@ float Skidding::updateSteering(float steer, int ticks) float dt = stk_config->ticks2Time(ticks); float skid_time_float = stk_config->ticks2Time(m_skid_time); float steer_result = 0.0f; + float skid_v = -1.0f; // skid left value const KartProperties *kp = m_kart->getKartProperties(); @@ -181,40 +181,22 @@ float Skidding::updateSteering(float steer, int ticks) } break; case SKID_ACCUMULATE_RIGHT: - { - float f = (1.0f+steer)*0.5f; // map [-1,1] --> [0, 1] - steer_result = kp->getSkidReduceTurnMin() - + m_skid_reduce_turn_delta * f; - if(skid_time_float < kp->getSkidVisualTime()) - m_visual_rotation = kp->getSkidVisual() - * steer_result * skid_time_float - / kp->getSkidVisualTime(); - else - m_visual_rotation = kp->getSkidVisual() * steer_result; - break; - } // SKID_ACCUMULATE_RIGHT + skid_v = 1.0f; // fallthrough case SKID_ACCUMULATE_LEFT: - { - float f = (-1.0f+steer)*0.5f; // map [-1,1] --> [-1, 0] - steer_result = -kp->getSkidReduceTurnMin() - + m_skid_reduce_turn_delta * f; - if(skid_time_float < kp->getSkidVisualTime()) - m_visual_rotation = kp->getSkidVisual() - * steer_result * skid_time_float - / kp->getSkidVisualTime(); - else - m_visual_rotation = kp->getSkidVisual() * steer_result; - break; - } // case SKID_ACCUMULATE_LEFT - + // map [-1,1] to [0,1] (right) or [-1, 0] (left) + float f = (skid_v + steer) * 0.5f; + // Multiply by 1 (right) or by -1 (left) + steer_result = skid_v * kp->getSkidReduceTurnMin() + + m_skid_reduce_turn_delta * f; + m_visual_rotation = kp->getSkidVisual() * steer_result; + if(skid_time_float < kp->getSkidVisualTime()) + m_visual_rotation *= skid_time_float / kp->getSkidVisualTime(); + break; } // switch m_skid_state // A visual rotation outside of [-1, 1] bounds leads to severe graphical errors // This allows the "reduce-turn-max" parameter for skidding to be set arbitrarily. - if (m_visual_rotation > 1.0f) - m_visual_rotation = 1.0f; - if (m_visual_rotation < -1.0f) - m_visual_rotation = -1.0f; + m_visual_rotation = std::min(1.0f, std::max(-1.0f, m_visual_rotation)); return steer_result; } // updateSteering @@ -266,36 +248,26 @@ float Skidding::updateGraphics(float dt) m_kart->getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDR, 0); m_kart->getKartGFX()->updateSkidLight(0); - float bonus_time, fade_out_time, bonus_speed, bonus_force; - unsigned int level = getSkidBonus(&bonus_time, &fade_out_time, &bonus_speed, &bonus_force); + unsigned int level = getSkidLevel(m_kart->getKartProperties()); + // Get the type of the last triggered skid level, + // if in the period after bonus trigger where particles are still shown if (m_skid_bonus_end_ticks > World::getWorld()->getTicksSinceStart()) - { - if (m_kart->m_max_speed - ->isSpeedIncreaseActive(MaxSpeed::MS_INCREASE_SKIDDING)) - level = 1; - else if (m_kart->m_max_speed - ->isSpeedIncreaseActive(MaxSpeed::MS_INCREASE_RED_SKIDDING)) - level = 2; - else if (m_kart->m_max_speed - ->isSpeedIncreaseActive(MaxSpeed::MS_INCREASE_PURPLE_SKIDDING)) - level = 3; - } + level = m_kart->m_max_speed->getLatestSkidLevel(); - if (level == 0 && m_graphical_remaining_jump_time <= 0.0f && - m_skid_state != SKID_NONE) + // If at least level 1 bonus is reached, show appropriate gfx + if (level >= 1) { - // Show tiny sparks if bonus not yet reached m_kart->getKartGFX()->setSkidLevel(level); + m_kart->getKartGFX()->updateSkidLight(level); } - else if (level >= 1) + // Show tiny sparks if bonus not yet reached + else if ( m_graphical_remaining_jump_time <= 0.0f && m_skid_state != SKID_NONE) { - // If at least level 1 bonus is reached, show appropriate gfx m_kart->getKartGFX()->setSkidLevel(level); - m_kart->getKartGFX()->updateSkidLight(level); } - if (bonus_time > 0 || level == 1 || level == 2 || level == 3) + if (level >= 1) { m_kart->getKartGFX()->setCreationRateRelative(KartGFX::KGFX_SKIDL, 1.0f); @@ -404,10 +376,6 @@ void Skidding::update(int ticks, bool is_on_ground, else if (m_skid_factor < 1.0f) m_skid_factor = 1.0f; - - // This is only reached if the new skidding is enabled - // --------------------------------------------------- - // There are four distinct states related to skidding, controlled // by m_skid_state: // SKID_NONE: no skidding is happening. From here SKID_ACCUMULATE @@ -422,10 +390,9 @@ void Skidding::update(int ticks, bool is_on_ground, // independently handled in the kart physics). // SKID_SHOW_GFX_{LEFT,RIGHT} // Shows the skidding gfx while the bonus is available. - // FIXME: what should we do if skid key is pressed while still in - // SKID_SHOW_GFX??? Adjusting the body rotation is difficult. - // For now skidding will only start again once SKID_SHOW_GFX - // is changed to SKID_NONE. + // If the skid key is pressed again, skidding will only start + // again once SKID_SHOW_GFX is changed to SKID_NONE. + // SKID_BREAK: the skidding was stopped without bonus switch(m_skid_state) { case SKID_NONE: @@ -439,9 +406,8 @@ void Skidding::update(int ticks, bool is_on_ground, m_kart->getSpeed() < kp->getSkidMinSpeed()) break; - m_skid_state = skidding==KartControl::SC_RIGHT - ? SKID_ACCUMULATE_RIGHT - : SKID_ACCUMULATE_LEFT; + m_skid_state = skidding==KartControl::SC_RIGHT ? SKID_ACCUMULATE_RIGHT : + SKID_ACCUMULATE_LEFT; // Add a little jump to the kart. Determine the vertical speed // necessary for the kart to go 0.5*jump_time up (then it needs // the same time to come down again), based on v = gravity * t. @@ -524,16 +490,14 @@ void Skidding::update(int ticks, bool is_on_ground, &bonus_speed, &bonus_force); if (level >= 1) - { m_skid_bonus_ready = true; - } + // If player stops skidding, trigger bonus, and change state to // SKID_SHOW_GFX_* if(skidding == KartControl::SC_NONE) { - m_skid_state = m_skid_state == SKID_ACCUMULATE_LEFT - ? SKID_SHOW_GFX_LEFT - : SKID_SHOW_GFX_RIGHT; + m_skid_state = m_skid_state == SKID_ACCUMULATE_LEFT ? SKID_SHOW_GFX_LEFT : + SKID_SHOW_GFX_RIGHT; float skid_time_float = stk_config->ticks2Time(m_skid_time); float t = std::min(skid_time_float, kp->getSkidVisualTime()); t = std::min(t, kp->getSkidRevertVisualTime()); @@ -548,12 +512,12 @@ void Skidding::update(int ticks, bool is_on_ground, unsigned int bonus_cat = (level == 1) ? MaxSpeed::MS_INCREASE_SKIDDING : (level == 2) ? MaxSpeed::MS_INCREASE_RED_SKIDDING : MaxSpeed::MS_INCREASE_PURPLE_SKIDDING; - m_kart->m_max_speed-> - instantSpeedIncrease(bonus_cat, - bonus_speed, bonus_speed/2, - bonus_force, - stk_config->time2Ticks(bonus_time), - stk_config->time2Ticks(fade_out_time)); + + m_kart->m_max_speed->instantSpeedIncrease(bonus_cat, + bonus_speed, bonus_speed/2, + bonus_force, + stk_config->time2Ticks(bonus_time), + stk_config->time2Ticks(fade_out_time)); m_skid_bonus_end_ticks = World::getWorld()->getTicksSinceStart() + stk_config->time2Ticks(1.0f); @@ -592,34 +556,40 @@ void Skidding::update(int ticks, bool is_on_ground, } // update // ---------------------------------------------------------------------------- -/** Determines the bonus time and speed given the currently accumulated - * m_skid_time. +/** Determines the bonus time and speed given the currently accumulated m_skid_time. * \param bonus_time On return contains how long the bonus should be active. * \param fade_out_time * \param bonus_speed How much additional speed the kart should get. * \param bonus_force Additional engine force. - * \return The bonus level: 0 = no bonus, 1 = first entry in bonus array etc. - */ + * \return The bonus level: 0 = no bonus, 1 = first entry in bonus array etc. */ unsigned int Skidding::getSkidBonus(float *bonus_time, float *fade_out_time, float *bonus_speed, float *bonus_force) const { const KartProperties *kp = m_kart->getKartProperties(); + unsigned int level = getSkidLevel(kp); - *bonus_time = 0; - *fade_out_time = 0; - *bonus_speed = 0; - *bonus_force = 0; - for (unsigned int i = 0; i < kp->getSkidBonusSpeed().size(); i++) + *bonus_time = (level == 0) ? 0 : kp->getSkidBonusTime()[level-1]; + *fade_out_time = (level == 0) ? 0 : kp->getSkidFadeOutTime()[level-1]; + *bonus_speed = (level == 0) ? 0 : kp->getSkidBonusSpeed()[level-1]; + *bonus_force = (level == 0) ? 0 : kp->getSkidBonusForce()[level-1]; + return level; +} // getSkidBonus + +// ---------------------------------------------------------------------------- +/** Determines the level of the currently accumulating skid. + * \return The bonus level: 0 = no bonus, 1 = first entry in bonus array etc. + */ +unsigned int Skidding::getSkidLevel(const KartProperties *kp) const +{ + unsigned int level = 0; + for (level = 0; level < kp->getSkidBonusSpeed().size(); level++) { if (stk_config->ticks2Time(m_skid_time) <= - kp->getSkidTimeTillBonus()[i]) - return i; - *bonus_speed = kp->getSkidBonusSpeed()[i]; - *fade_out_time = kp->getSkidFadeOutTime()[i]; - *bonus_time = kp->getSkidBonusTime()[i]; - *bonus_force = kp->getSkidBonusForce()[i]; + kp->getSkidTimeTillBonus()[level]) + break; } - return (unsigned int) kp->getSkidBonusSpeed().size(); -} // getSkidBonus + + return level; +} // getSkidLevel diff --git a/src/karts/skidding.hpp b/src/karts/skidding.hpp index bcf31aa4ce2..225063f716f 100644 --- a/src/karts/skidding.hpp +++ b/src/karts/skidding.hpp @@ -20,6 +20,7 @@ #define HEADER_SKIDDING_HPP #include "karts/controller/kart_control.hpp" +#include "karts/kart_properties.hpp" #include "utils/leak_check.hpp" #include "utils/no_copy.hpp" #include "utils/types.hpp" @@ -103,6 +104,7 @@ friend class KartRewinder; unsigned int getSkidBonus(float *bonus_time, float *fade_out_time, float *bonus_speed, float *bonus_force) const; + unsigned int getSkidLevel(const KartProperties *kp) const; float updateSteering(float steer, int ticks); public: Skidding(Kart *kart); From 922dff7f4a29bcbb49d8ecbc86031afb8fdf6084 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Mon, 11 Dec 2023 12:01:33 +0100 Subject: [PATCH 092/188] More balance tweaks Notably, increases the standard max-speed by 0.25, tweak slightly base duration and fade-out-time for skid bonuses, very slightly increase base speed gaps between classes, slightly relax steering for heavies, along minor adjustments --- data/kart_characteristics.xml | 38 +++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 9497bf92cec..dd34102bbbc 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -96,7 +96,7 @@ max-speed-reverse-ratio is the percentage of max speed for reverse gear. --> - - @@ -248,8 +248,8 @@ kart-specific maximum kart speed. fade-out-time: determines how long it takes for a zipper to fade out (after 'time'). --> - + + max-speed-increase="5" min-burst="0.5" duration="0.9" + fade-out-time="1.45" max="20" /> getNitroHackMaxTargets(); float base_bonus = powerup_manager->getNitroHackBaseBonus(); + float negative_multiply = powerup_manager->getNitroHackNegativeMultiply(); float stolen_energy = 0.0f; unsigned int steal_counter = 0; @@ -400,6 +401,9 @@ void Powerup::use() } } } + // Multiply current nitro by a given factor if it is currently negative + if(m_kart->getEnergy() < 0) + m_kart->setEnergy(m_kart->getEnergy()*negative_multiply); // Gift some free nitro if there is not enough targets in front if (steal_counter < steal_targets) @@ -873,4 +877,4 @@ int Powerup::simplePRNG(const int seed, const int time, const int item_id, const // Return the final value and drop the lower order bits return (rand/65536); -} // simplePRNG \ No newline at end of file +} // simplePRNG diff --git a/src/items/powerup_manager.cpp b/src/items/powerup_manager.cpp index 96784ddabb3..220dc346851 100644 --- a/src/items/powerup_manager.cpp +++ b/src/items/powerup_manager.cpp @@ -669,6 +669,7 @@ void PowerupManager::loadNitroHack(const XMLNode &node) // the config file m_nh_max_targets = 5; m_nh_base_bonus = 0.2f; + m_nh_negative_multiply = 1.0f; float stolen_max = 1.6f; float stolen_add = 0.4f; @@ -677,6 +678,9 @@ void PowerupManager::loadNitroHack(const XMLNode &node) if(!node.get("max-targets", &m_nh_max_targets)) Log::warn("powerup", "No max-targets specified for nitro-hack."); + if(!node.get("negative-multiply", &m_nh_negative_multiply)) + Log::warn("powerup", + "No negative-multiply specified for nitro-hack."); if(!node.get("bonus-no-kart", &m_nh_base_bonus)) Log::warn("powerup", "No bonus-no-kart specified for nitro-hack."); diff --git a/src/items/powerup_manager.hpp b/src/items/powerup_manager.hpp index 5e42730698b..68f1c99a8bc 100644 --- a/src/items/powerup_manager.hpp +++ b/src/items/powerup_manager.hpp @@ -184,6 +184,7 @@ class PowerupManager : public NoCopy /** Parameters for the nitro-hack powerup */ unsigned int m_nh_max_targets; + float m_nh_negative_multiply; float m_nh_base_bonus; float m_nh_stolen_amount[20]; @@ -222,6 +223,7 @@ class PowerupManager : public NoCopy // ------------------------------------------------------------------------ /** Functions for the NitroHack powerup */ unsigned int getNitroHackMaxTargets() const { return m_nh_max_targets; } + float getNitroHackNegativeMultiply() const { return m_nh_negative_multiply; } float getNitroHackBaseBonus() const { return m_nh_base_bonus; } float getNitroHackStolenDiff(unsigned int diff) const; @@ -229,4 +231,4 @@ class PowerupManager : public NoCopy extern PowerupManager* powerup_manager; -#endif \ No newline at end of file +#endif From d19f4c1eb241aed5a7fc5fe2e1e6c40e8fa7e144 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 7 Jan 2024 00:54:41 +0100 Subject: [PATCH 096/188] Fix network transmission of max_speed boosts The bitflag for the active boost was supporting at most 8 boosts type but with nomagno's split of zipper and ground zipper types, there was 9 types, and so the electro-shield boost was not transmitted anymore. --- src/karts/max_speed.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/karts/max_speed.cpp b/src/karts/max_speed.cpp index 680fb3feb24..97087d458ff 100644 --- a/src/karts/max_speed.cpp +++ b/src/karts/max_speed.cpp @@ -537,13 +537,13 @@ void MaxSpeed::saveState(BareNetworkString *buffer) const // Now save the speedup state // -------------------------- // Get the bit pattern of all active speedups - uint8_t active_speedups = 0; + uint16_t active_speedups = 0; for(unsigned int i=MS_INCREASE_MIN, b=1; iaddUInt8(active_speedups); + buffer->addUInt16(active_speedups); for(unsigned int i=MS_INCREASE_MIN, b=1; igetUInt8(); + uint16_t active_speedups = buffer->getUInt16(); for(unsigned int i=MS_INCREASE_MIN, b=1; i Date: Sat, 3 Feb 2024 00:58:44 +0100 Subject: [PATCH 097/188] Remove useless code and misleading comment Time-full-steer is always applied not matter the input device, so code that was meant to help not apply it is pointless --- src/input/device_config.hpp | 3 +-- src/input/gamepad_device.cpp | 12 ------------ 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/input/device_config.hpp b/src/input/device_config.hpp index d88fb34fce3..fc3c5e6a470 100644 --- a/src/input/device_config.hpp +++ b/src/input/device_config.hpp @@ -103,8 +103,7 @@ class DeviceConfig : public NoCopy virtual bool load(const XMLNode *config); // ------------------------------------------------------------------------ - /** Returns true if this device has analog axis, so that steering values - * will not be affected by time-full-steer delays. */ + /** Returns true if this device has an analog axis. */ virtual bool isAnalog(Input::InputType type, int id) const { return false;} // ------------------------------------------------------------------------ /** Returns true if this device should desensitize its input at values diff --git a/src/input/gamepad_device.cpp b/src/input/gamepad_device.cpp index 630be6c076a..ac9d7b78942 100644 --- a/src/input/gamepad_device.cpp +++ b/src/input/gamepad_device.cpp @@ -146,18 +146,6 @@ bool GamePadDevice::processAndMapInput(Input::InputType type, const int id, { if (!m_configuration->isEnabled()) return false; - // A digital input value is 32767 or -32768 (which then triggers - // time-full-steer to be used to adjust actual steering values. - // To prevent this delay for analog gamesticks, make sure that - // 32767/-32768 are never used. - if(m_configuration->isAnalog(type, id)) - { - if(*value==32767) - *value = 32766; - else if(*value==-32768) - *value = -32767; - } - // Desensitizing means to map an input in the range x in [0,1] to // x^2. This results in changes close to 0 to have less impact // (less sensitive). From e88f4fd856500b9be91f6c5fd69c95c0ae694f93 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sat, 3 Feb 2024 01:46:05 +0100 Subject: [PATCH 098/188] Tweak time-full-steer Although discontinuous changes are maintained, there are two points of discontinuity instead of one. This allows to get an effect similar to the old time-reset-steer variable, where turning back towards a neutral straight direction is swifter. The main goal of this patch is to improve the handling feeling of heavies (and to a lesser degree, mediums), making them more fun and slightly easier to play, without changing much their peak potential. --- data/kart_characteristics.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index dd34102bbbc..54ae8aa66b2 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -81,7 +81,7 @@ at 0 is used. --> + time-full-steer="0:0.1 0.3:0.1 0.3:0.14 0.6:0.14 0.6:0.21 1.0:0.21" /> + time-full-brake="0.35" max-speed-reverse-ratio="0.48" /> - - + + @@ -423,8 +424,9 @@ - - + + @@ -437,8 +439,9 @@ - - + + @@ -450,8 +453,9 @@ - - + + From 4266022bb1cf4e86ce164ab39be25435f492a2fc Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sat, 13 Apr 2024 02:44:13 +0200 Subject: [PATCH 100/188] Tweak the balanced randomness values Use 2 * 12 = 24 buckets instead of 3 * 9 = 27 buckets to reduce freak streaks and to make the system kick in faster ; as an average length race will usually have around twelve to fifteen collected item boxes. The max number of buckets whose repetition is avoided is reduced from 15 (55.6% of 27 buckets) to 13 (54.2% of 24 buckets). --- src/items/powerup_manager.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/items/powerup_manager.hpp b/src/items/powerup_manager.hpp index 68f1c99a8bc..2ffcd09915e 100644 --- a/src/items/powerup_manager.hpp +++ b/src/items/powerup_manager.hpp @@ -20,13 +20,13 @@ #define HEADER_POWERUPMANAGER_HPP #undef ITEM_DISTRIBUTION_DEBUG -#define REPEAT_WEIGHTS 3 -#define BUCKETS_PER_WEIGHT_LIST 9 +#define REPEAT_WEIGHTS 2 +#define BUCKETS_PER_WEIGHT_LIST 12 /** The maximum supported number of buckets is 32, * additional buckets will not be set in the bitmask */ #define BUCKET_COUNT (REPEAT_WEIGHTS * BUCKETS_PER_WEIGHT_LIST) /** A kart will remember at most the last MAX_BUCKETS */ -#define MAX_BUCKETS 15 +#define MAX_BUCKETS 13 #include "utils/leak_check.hpp" #include "utils/no_copy.hpp" From eca86162e88b5dd7c7d7e50b5329265639866c6d Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sat, 13 Apr 2024 03:02:31 +0200 Subject: [PATCH 101/188] Reduce nitro consumption in lower difficulties Lower difficulties collect the same number of nitro bottles in a lap as higher difficulties, but the lap lasts longer, so the proportion of time with active nitro is significantly reduced there. This change slightly speeds up lower difficulties and increase fun from nitro. As the speed boost from nitro is smaller compared to higher difficulties (unlike in 1.X), this is not too strong. --- data/kart_characteristics.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 2025fa703a1..911ee332f3f 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -372,7 +372,7 @@ max-speed-reverse-ratio="0.65"/> - + @@ -382,7 +382,7 @@ max-speed-reverse-ratio="0.6"/> - + @@ -391,14 +391,14 @@ - + - + + + + + + + - - - + From 0cc40279b6abdd56757ad68d91aece1ce591a5c6 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sat, 13 Apr 2024 13:31:52 +0200 Subject: [PATCH 103/188] Fix #5051 (Normalize downward impulse for weight) The speed at which different karts jump from an obstacle is now identical. The choice of 300 as the base mass decreases the propensity to leave the ground of heavy karts (350), and increases that of medium (262,5) and light karts (210). --- src/physics/btKart.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp index 7ad0de5bf08..22d7b7b16fd 100644 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -493,6 +493,9 @@ void btKart::updateVehicle( btScalar step ) // Excessive downward impulse at high speeds is harmful if (f1 > 35.0f) f1 = 35.0f; + // Normalize for mass, to avoid heavier karts jumping easier + // than lighter karts (#5051) + f1 = f1 * m_kart->getKartProperties()->getMass() / 300; // Adjust the impulse force for the time step, // to prevent changes to physics FPS from breaking things. // An increase in impulse frequency is almost equivalent to From ea648598141d8d04bef9e38d313fb5ab08cd7015 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sat, 13 Apr 2024 14:01:57 +0200 Subject: [PATCH 104/188] Make suspension stiffness dependent on kart class With heavier karts receiving a stronger downward impulse to normalize jumping characteristics, heavier karts also became more prone to have the kart body move too much down on the suspensions and to have excessive up and down movements on uneven road surfaces. This commit fixes this issue. --- data/kart_characteristics.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 911ee332f3f..be8d861ef6a 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -34,7 +34,7 @@ http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?f=9&t=7369\&p=25236&hilit=vehicle#p25236 for details. max-force: Maximum suspension force --> - + @@ -424,6 +425,7 @@ + @@ -439,6 +441,7 @@ + @@ -453,6 +456,7 @@ + From e1094d5b40de4299c806ce5d54cb0b6de551c929 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Mon, 15 Apr 2024 17:00:38 +0200 Subject: [PATCH 105/188] Slightly reduce how easily kart jumps --- src/physics/btKart.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp index 22d7b7b16fd..2eac114f673 100644 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -495,7 +495,7 @@ void btKart::updateVehicle( btScalar step ) f1 = 35.0f; // Normalize for mass, to avoid heavier karts jumping easier // than lighter karts (#5051) - f1 = f1 * m_kart->getKartProperties()->getMass() / 300; + f1 = f1 * m_kart->getKartProperties()->getMass() / 280; // Adjust the impulse force for the time step, // to prevent changes to physics FPS from breaking things. // An increase in impulse frequency is almost equivalent to From 0e551cdb4ef26611059238e5286ad5ac168c4aff Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Tue, 16 Apr 2024 18:26:48 +0200 Subject: [PATCH 106/188] Introduce multi-level handicap, single-player handicap - Scrap the existing handicap characteristics and define six new handicap levels only affecting speed and engine power - Make handicap selection in local multiplayer use a dedicated spinner - Enable handicap selection in single player - Increase the vertical height of the kart stats bar, both in single player kart selection and in local multiplayer kart selection - Add a shift y up property for labels, allowing to shift the labels in local multiplayer kart selection up, to not overlap with the kart stats bar - Allow any widget to be set with a player ID reserving its selection with a keyboard or gamepad to the player owning it. - Clean up redundant player ID code for spinner widgets and player spinner widgets This new multi-level handicap is not yet supported for networking. --- data/kart_characteristics.xml | 59 ++++-- src/guiengine/event_handler.cpp | 3 +- src/guiengine/skin.cpp | 4 +- src/guiengine/widget.cpp | 4 + src/guiengine/widget.hpp | 20 +- src/guiengine/widgets/model_view_widget.cpp | 2 + src/guiengine/widgets/player_kart_widget.cpp | 176 ++++++++++++------ src/guiengine/widgets/player_kart_widget.hpp | 4 +- src/guiengine/widgets/player_name_spinner.cpp | 11 +- src/guiengine/widgets/player_name_spinner.hpp | 9 +- src/guiengine/widgets/spinner_widget.cpp | 1 - src/guiengine/widgets/spinner_widget.hpp | 3 - src/karts/controller/player_controller.cpp | 2 +- src/karts/kart_properties.cpp | 10 +- src/network/remote_kart_info.hpp | 7 +- .../dialogs/network_player_dialog.cpp | 2 +- .../dialogs/splitscreen_player_dialog.cpp | 2 +- src/states_screens/kart_selection.cpp | 2 +- .../online/network_kart_selection.cpp | 2 +- src/states_screens/race_result_gui.cpp | 4 +- 20 files changed, 223 insertions(+), 104 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index be8d861ef6a..69708e8da7e 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -476,18 +476,53 @@ - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/guiengine/event_handler.cpp b/src/guiengine/event_handler.cpp index f45c76bafab..7d8c760a04f 100644 --- a/src/guiengine/event_handler.cpp +++ b/src/guiengine/event_handler.cpp @@ -543,7 +543,8 @@ int EventHandler::findIDClosestWidget(const NavigationDirection nav, const int p if (w_test == NULL || !Widget::isFocusableId(i) || w == w_test || (!w_test->isVisible() && ignore_disabled) || (!w_test->isActivated() && ignore_disabled) || - (playerID != PLAYER_ID_GAME_MASTER && !w_test->m_supports_multiplayer)) + (playerID != PLAYER_ID_GAME_MASTER && !w_test->m_supports_multiplayer) || + (w_test->hasPlayerID() && playerID != w_test->getPlayerID())) continue; // Ignore empty ribbon widgets and lists diff --git a/src/guiengine/skin.cpp b/src/guiengine/skin.cpp index 101a3cbf5aa..8c5832a32b2 100644 --- a/src/guiengine/skin.cpp +++ b/src/guiengine/skin.cpp @@ -1641,7 +1641,7 @@ void Skin::drawSpinnerBody(const core::recti &rect, Widget* widget, SColorf color_rgb = { 1,1,1,1 }; if(q->getUseBackgroundColor()) { - int player_id=q->getSpinnerWidgetPlayerID(); + int player_id=q->getPlayerID(); std::string spinner = "spinner::deactivated"; @@ -2441,7 +2441,7 @@ void Skin::process3DPane(IGUIElement *element, const core::recti &rect, if (widget->m_badges != 0) { - drawBadgeOn(widget, rect+position2d(widget->m_badge_x_shift, 0)); + drawBadgeOn(widget, rect+position2d(widget->m_badge_x_shift, -widget->m_badge_y_up_shift)); } } // process3DPane diff --git a/src/guiengine/widget.cpp b/src/guiengine/widget.cpp index 19fcf642f5f..b50951778c5 100644 --- a/src/guiengine/widget.cpp +++ b/src/guiengine/widget.cpp @@ -66,6 +66,7 @@ Widget::Widget(WidgetType type, bool reserve_id) m_h = -1; m_id = -1; m_badge_x_shift = 0; + m_badge_y_up_shift = 0; m_element = NULL; m_title_font = false; m_type = type; @@ -100,6 +101,9 @@ Widget::Widget(WidgetType type, bool reserve_id) m_deactivated = false; m_is_visible = true; m_badges = 0; + m_player_id = 0; + m_has_player_id = false; + // set a default value, derivates can override this as they wish m_check_inside_me = (m_type == WTYPE_DIV); diff --git a/src/guiengine/widget.hpp b/src/guiengine/widget.hpp index 2170a4590aa..efcbf1477f4 100644 --- a/src/guiengine/widget.hpp +++ b/src/guiengine/widget.hpp @@ -282,6 +282,9 @@ namespace GUIEngine /** If a badge wouldn't look too pretty on the very side of the widget */ int m_badge_x_shift; + /** In cases where a badge shouldn't be at the bottom */ + int m_badge_y_up_shift; + bool m_has_tooltip; irr::core::stringw m_tooltip_text; @@ -321,6 +324,10 @@ namespace GUIEngine once the widget is added, will be e.g. 80.) */ int m_x, m_y, m_w, m_h; + /** If this widget belongs to a specific player, and to which one*/ + int m_player_id; + bool m_has_player_id; + /** Whether to show a bounding box around this widget (used for sections) */ bool m_show_bounding_box; @@ -689,10 +696,15 @@ namespace GUIEngine * \brief Get which badges are currently on this widget * \return a bitmask of BadgeType values */ - int getBadges() const - { - return m_badges; - } + int getBadges() const { return m_badges; } + + int hasPlayerID() const { return m_has_player_id; } + + int getPlayerID() const { return m_player_id; } + + void setPlayerID(int playerID) { m_player_id = playerID; } + + void setPlayerIDSupport(bool support) { m_has_player_id = support; } /** * \} diff --git a/src/guiengine/widgets/model_view_widget.cpp b/src/guiengine/widgets/model_view_widget.cpp index 2dc2d0de3cf..5dbb4f65a8b 100644 --- a/src/guiengine/widgets/model_view_widget.cpp +++ b/src/guiengine/widgets/model_view_widget.cpp @@ -126,6 +126,8 @@ void ModelViewWidget::addModel(irr::scene::IMesh* mesh, // ----------------------------------------------------------------------------- void ModelViewWidget::update(float delta) { + m_badge_y_up_shift = m_h/10; + if (m_rtt_unsupported) return; if (m_rotation_mode == ROTATE_CONTINUOUSLY) diff --git a/src/guiengine/widgets/player_kart_widget.cpp b/src/guiengine/widgets/player_kart_widget.cpp index 1f693da5767..25cd28441f0 100644 --- a/src/guiengine/widgets/player_kart_widget.cpp +++ b/src/guiengine/widgets/player_kart_widget.cpp @@ -73,11 +73,41 @@ PlayerKartWidget::PlayerKartWidget(KartSelectionScreen* parent, m_player_ident_spinner = NULL; m_player_ident_spinner = new PlayerNameSpinner(parent, m_player_id); - m_player_ident_spinner->m_x = player_name_x; - m_player_ident_spinner->m_y = player_name_y; + // The positions are defined later, only the dimensions matter here m_player_ident_spinner->m_w = player_name_w; m_player_ident_spinner->m_h = player_name_h; + // ---- Handicap spinner + if (true) //UserConfigParams::m_per_player_difficulty) + { + m_handicap_spinner = NULL; + + m_handicap_spinner = new SpinnerWidget(/* gauge */ false); + m_handicap_spinner->m_w = player_name_w; + m_handicap_spinner->m_h = player_name_h; + + m_handicap_spinner->setPlayerID(m_player_id); + m_handicap_spinner->setPlayerIDSupport(true); + + core::stringw label = _("No handicap"); + m_handicap_spinner->addLabel(label); + m_handicap_spinner->setValue(label); + // I18N: 'Handicap' indicates that per-player handicaps are + // activated for this kart (i.e. it will drive slower) + label = _("Handicap (4%)"); + m_handicap_spinner->addLabel(label); + label = _("Handicap (8%)"); + m_handicap_spinner->addLabel(label); + label = _("Handicap (12%)"); + m_handicap_spinner->addLabel(label); + label = _("Handicap (16%)"); + m_handicap_spinner->addLabel(label); + label = _("Handicap (20%)"); + m_handicap_spinner->addLabel(label); + label = _("Handicap (24%)"); + m_handicap_spinner->addLabel(label); + } + // ---- KartStatsWidget m_kart_stats = NULL; @@ -120,9 +150,9 @@ PlayerKartWidget::PlayerKartWidget(KartSelectionScreen* parent, m_player_ident_spinner->m_tab_down_root = g_root_id; } - spinnerID = StringUtils::insertValues("@p%i_spinner", m_player_id); + playerSpinnerID = StringUtils::insertValues("@p%i_spinner", m_player_id); - m_player_ident_spinner->m_properties[PROP_ID] = spinnerID; + m_player_ident_spinner->m_properties[PROP_ID] = playerSpinnerID; if (parent->m_multiplayer) { const int player_amount = PlayerManager::get()->getNumPlayers(); @@ -139,6 +169,7 @@ PlayerKartWidget::PlayerKartWidget(KartSelectionScreen* parent, //m_player_ident_spinner->m_event_handler = this; m_children.push_back(m_player_ident_spinner); + m_children.push_back(m_handicap_spinner); // ----- Kart model view m_model_view = new ModelViewWidget(); @@ -263,9 +294,15 @@ PlayerKartWidget::~PlayerKartWidget() m_player_ident_spinner->setListener(NULL); if (m_player_ident_spinner->getIrrlichtElement() != NULL) - { m_player_ident_spinner->getIrrlichtElement()->remove(); - } + } + + if (m_handicap_spinner != NULL) + { + m_handicap_spinner->setListener(NULL); + + if (m_handicap_spinner->getIrrlichtElement() != NULL) + m_handicap_spinner->getIrrlichtElement()->remove(); } if (m_model_view->getIrrlichtElement() != NULL) @@ -306,14 +343,14 @@ void PlayerKartWidget::setPlayerID(const int newPlayerID) // Change the player ID m_player_id = newPlayerID; if (!m_ready) - m_player_ident_spinner->setID(m_player_id); + m_player_ident_spinner->setPlayerID(m_player_id); m_kart_stats->setDisplayIcons(m_player_id == 0); // restore previous focus, but with new player ID if (focus != NULL) focus->setFocusForPlayer(m_player_id); if (m_player_ident_spinner != NULL) { - m_player_ident_spinner->setID(m_player_id); + m_player_ident_spinner->setPlayerID(m_player_id); } } // setPlayerID @@ -359,6 +396,9 @@ void PlayerKartWidget::add() m_player_ident_spinner->add(); m_player_ident_spinner->getIrrlichtElement()->setTabStop(false); m_player_ident_spinner->setListener(this); + m_handicap_spinner->add(); + m_handicap_spinner->getIrrlichtElement()->setTabStop(false); + m_handicap_spinner->setListener(this); m_kart_stats->add(); m_model_view->add(); m_kart_name->add(); @@ -380,13 +420,6 @@ void PlayerKartWidget::add() core::stringw name = PlayerManager::get()->getPlayer(n)->getName(); core::stringw label = name; m_player_ident_spinner->addLabel(label); - if (UserConfigParams::m_per_player_difficulty) - { - // I18N: 'handicapped' indicates that per-player handicaps are - // activated for this kart (i.e. it will drive slower) - label = _("%s (handicapped)", name); - m_player_ident_spinner->addLabel(label); - } } // select the right player profile in the spinner @@ -398,6 +431,9 @@ void PlayerKartWidget::add() m_player_ident_spinner->setVisible(false); } + if (!UserConfigParams::m_per_player_difficulty) + m_handicap_spinner->setVisible(false); + assert(m_player_ident_spinner->getStringValue() == label); } // add @@ -452,6 +488,8 @@ void PlayerKartWidget::markAsReady() delete m_player_ident_spinner; m_player_ident_spinner = NULL; + m_handicap_spinner->setActive(false); + SFXManager::get()->quickSound( "wee" ); m_model_view->setRotateTo(30.0f, 1.0f); @@ -551,6 +589,13 @@ void PlayerKartWidget::onUpdate(float delta) player_name_w, player_name_h ); } + if (m_handicap_spinner != NULL) + { + m_handicap_spinner->move(player_name_x, + player_name_y + player_name_h + 5, + player_name_w, + player_name_h ); + } if (m_ready_text != NULL) { m_ready_text->setRelativePosition( @@ -564,13 +609,16 @@ void PlayerKartWidget::onUpdate(float delta) m_kart_stats_w, m_kart_stats_h); } - else + else // multiplayer kart selection { - m_kart_stats->move(m_x, m_y + m_h/2, - m_w, m_h/2); + // The 4/10 and 7/10 values were derived from testing how + // to best use the space to avoid too small stat bars + // The kart models don't use the bottom of the space assigned to them. + // The badges on kart models are shifted upwards to accomodate this. + m_kart_stats->move(m_x, m_y + (4 * m_h / 10), + m_w, (7 * m_h / 10 )); } - m_model_view->move(model_x, model_y, model_w, @@ -600,16 +648,10 @@ GUIEngine::EventPropagation PlayerKartWidget::transmitEvent(Widget* w, // if it's declared ready, there is really nothing to process if (m_ready) return EVENT_LET; - //std::cout << "= kart selection :: transmitEvent " - // << originator << " =\n"; - std::string name = w->m_properties[PROP_ID]; - //std::cout << " (checking if that's me: I am " - // << spinnerID << ")\n"; - // update player profile when spinner changed - if (originator == spinnerID) + if (originator == playerSpinnerID) { if(UserConfigParams::logGUI()) { @@ -623,29 +665,59 @@ GUIEngine::EventPropagation PlayerKartWidget::transmitEvent(Widget* w, if (m_parent_screen->m_multiplayer) { int spinner_value = m_player_ident_spinner->getValue(); - PlayerProfile* profile = PlayerManager::get()->getPlayer( - UserConfigParams::m_per_player_difficulty ? spinner_value / 2 : spinner_value); + PlayerProfile* profile = PlayerManager::get()->getPlayer(spinner_value); m_associated_player->setPlayerProfile(profile); - if(UserConfigParams::m_per_player_difficulty && spinner_value % 2 != 0) + m_model_view->getModelViewRenderInfo()->setHue( + m_associated_player->getConstProfile()->getDefaultKartColor()); + } + } // playerSpinnerID + + if (UserConfigParams::m_per_player_difficulty && + originator == handicapSpinnerID) + { + int spinner_value = m_handicap_spinner->getValue(); + if (spinner_value == 0) + { + m_handicap = HANDICAP_NONE; + m_model_view->unsetBadge(ANCHOR_BADGE); + m_kart_stats->setValues(kart_properties_manager->getKart(m_kart_internal_name), HANDICAP_NONE); + } + else + { + if (spinner_value == 1) { - m_handicap = HANDICAP_MEDIUM; + m_handicap = HANDICAP_4; m_model_view->setBadge(ANCHOR_BADGE); - m_kart_stats->setValues( - kart_properties_manager->getKart(m_kart_internal_name), - HANDICAP_MEDIUM); } - else + else if (spinner_value == 2) { - m_handicap = HANDICAP_NONE; - m_model_view->unsetBadge(ANCHOR_BADGE); - m_kart_stats->setValues( - kart_properties_manager->getKart(m_kart_internal_name), - HANDICAP_NONE); + m_handicap = HANDICAP_8; + m_model_view->setBadge(ANCHOR_BADGE); } - m_model_view->getModelViewRenderInfo()->setHue( - m_associated_player->getConstProfile()->getDefaultKartColor()); + else if (spinner_value == 3) + { + m_handicap = HANDICAP_12; + m_model_view->setBadge(ANCHOR_BADGE); + } + else if (spinner_value == 4) + { + m_handicap = HANDICAP_16; + m_model_view->setBadge(ANCHOR_BADGE); + } + else if (spinner_value == 5) + { + m_handicap = HANDICAP_20; + m_model_view->setBadge(ANCHOR_BADGE); + } + else // spinner_value == 6 + { + m_handicap = HANDICAP_24; + m_model_view->setBadge(ANCHOR_BADGE); + } + + m_kart_stats->setValues(kart_properties_manager->getKart(m_kart_internal_name), m_handicap); } - } + } // handicapSpinnerID return EVENT_LET; // continue propagating the event } // transmitEvent @@ -663,11 +735,7 @@ void PlayerKartWidget::setSize(const int x, const int y, const int w, const int // -- sizes player_name_h = GUIEngine::getFontHeight(); - // Set it a bit higher so there's space for "(handicapped)" - if(UserConfigParams::m_per_player_difficulty) - player_name_w = std::min(GUIEngine::getFontHeight() * 12, w); - else - player_name_w = std::min(GUIEngine::getFontHeight() * 10, w); + player_name_w = std::min(GUIEngine::getFontHeight() * 12, w); kart_name_w = w; kart_name_h = GUIEngine::getFontHeight(); @@ -690,14 +758,11 @@ void PlayerKartWidget::setSize(const int x, const int y, const int w, const int const int modelMaxWidth = w; const int bestSize = std::min(modelMaxWidth, modelMaxHeight); model_x = x + w/2 - (int)(bestSize/2); - model_y = y + player_name_h; + model_y = y + 2 * player_name_h + 5; model_w = bestSize; model_h = bestSize; - m_kart_stats_w = model_w; - m_kart_stats_h = model_h; - m_kart_stats_x = x + w/2 - (int)(bestSize/2); - m_kart_stats_y = model_y + model_h; + // The kart stats module is positioned later on } else { @@ -711,7 +776,8 @@ void PlayerKartWidget::setSize(const int x, const int y, const int w, const int model_h = bestSize; m_kart_stats_w = w/2; - m_kart_stats_h = h; + // There is room for it no matter the resolution and it improves the display. + m_kart_stats_h = h*1.2; m_kart_stats_x = x + w/2; m_kart_stats_y = y; } @@ -750,11 +816,11 @@ EventPropagation PlayerKartWidget::onSpinnerConfirmed() // ------------------------------------------------------------------------- void PlayerKartWidget::enableHandicapForNetwork() { - m_handicap = HANDICAP_MEDIUM; + m_handicap = HANDICAP_4; m_model_view->setBadge(ANCHOR_BADGE); m_kart_stats->setValues( kart_properties_manager->getKart(m_kart_internal_name), - HANDICAP_MEDIUM); + HANDICAP_4); core::stringw label = _("%s (handicapped)", m_player_ident_spinner->getCustomText()); m_player_ident_spinner->setCustomText(label); diff --git a/src/guiengine/widgets/player_kart_widget.hpp b/src/guiengine/widgets/player_kart_widget.hpp index 7692563909a..92ad2881366 100644 --- a/src/guiengine/widgets/player_kart_widget.hpp +++ b/src/guiengine/widgets/player_kart_widget.hpp @@ -66,7 +66,8 @@ namespace GUIEngine /** Internal name of the spinner; useful to interpret spinner events, * which contain the name of the activated object */ - std::string spinnerID; + std::string playerSpinnerID; + std::string handicapSpinnerID; #ifdef DEBUG long m_magic_number; @@ -78,6 +79,7 @@ namespace GUIEngine /** Sub-widgets created by this widget */ PlayerNameSpinner* m_player_ident_spinner; + SpinnerWidget* m_handicap_spinner; KartStatsWidget* m_kart_stats; ModelViewWidget* m_model_view; LabelWidget* m_kart_name; diff --git a/src/guiengine/widgets/player_name_spinner.cpp b/src/guiengine/widgets/player_name_spinner.cpp index 178ca0562de..942c484c608 100644 --- a/src/guiengine/widgets/player_name_spinner.cpp +++ b/src/guiengine/widgets/player_name_spinner.cpp @@ -27,19 +27,14 @@ using namespace GUIEngine; PlayerNameSpinner::PlayerNameSpinner(KartSelectionScreen* parent, const int player_id) { - m_player_id = player_id; + m_player_id = player_id; // m_player_id comes from the inheritance of Widget + m_has_player_id = true; m_incorrect = false; m_red_mark_widget = NULL; m_parent = parent; setUseBackgroundColor();//except for multiplayer kart selection, this is false - setSpinnerWidgetPlayerID(m_player_id); } // PlayerNameSpinner -// ------------------------------------------------------------------------ -void PlayerNameSpinner::setID(const int m_player_id) -{ - PlayerNameSpinner::m_player_id = m_player_id; - setSpinnerWidgetPlayerID(m_player_id); -} // setID + // ------------------------------------------------------------------------ /** Add a red mark on the spinner to mean "invalid choice" */ void PlayerNameSpinner::markAsIncorrect() diff --git a/src/guiengine/widgets/player_name_spinner.hpp b/src/guiengine/widgets/player_name_spinner.hpp index 9b32c2e0186..0c563368782 100644 --- a/src/guiengine/widgets/player_name_spinner.hpp +++ b/src/guiengine/widgets/player_name_spinner.hpp @@ -26,21 +26,16 @@ class KartSelectionScreen; namespace GUIEngine { - /** A small extension to the spinner widget to add features like player ID - * management or badging */ + /** A small extension to the spinner widget to add features like badging + * and to guarantee that the widget is tied to a Player ID. */ class PlayerNameSpinner : public GUIEngine::SpinnerWidget { - int m_player_id; bool m_incorrect; irr::gui::IGUIImage* m_red_mark_widget; KartSelectionScreen* m_parent; - //virtual EventPropagation focused(const int m_playerID) ; public: PlayerNameSpinner(KartSelectionScreen* parent, const int playerID); - // ------------------------------------------------------------------------ - void setID(const int m_playerID); - // ------------------------------------------------------------------------ /** Add a red mark on the spinner to mean "invalid choice" */ void markAsIncorrect(); diff --git a/src/guiengine/widgets/spinner_widget.cpp b/src/guiengine/widgets/spinner_widget.cpp index 9f7ee549365..a635efdd923 100644 --- a/src/guiengine/widgets/spinner_widget.cpp +++ b/src/guiengine/widgets/spinner_widget.cpp @@ -97,7 +97,6 @@ SpinnerWidget::SpinnerWidget(const bool gauge) : Widget(WTYPE_SPINNER) m_supports_multiplayer = true; m_value = -1; m_use_background_color=false; - m_spinner_widget_player_id=-1; m_min = 0; m_max = 999; m_step = 1.0; diff --git a/src/guiengine/widgets/spinner_widget.hpp b/src/guiengine/widgets/spinner_widget.hpp index 840849352fa..c42d20cf4bd 100644 --- a/src/guiengine/widgets/spinner_widget.hpp +++ b/src/guiengine/widgets/spinner_widget.hpp @@ -59,7 +59,6 @@ namespace GUIEngine int m_value, m_min, m_max; float m_step; - int m_spinner_widget_player_id; bool m_use_background_color; /** If each value the spinner can take has an associated text, this vector will be non-empty */ @@ -128,8 +127,6 @@ namespace GUIEngine // next four functions are for background colour behind playername in multikart screen selection void setUseBackgroundColor() {m_use_background_color=true; } bool getUseBackgroundColor() {return m_use_background_color; } - void setSpinnerWidgetPlayerID(int playerID) {m_spinner_widget_player_id=playerID;} - int getSpinnerWidgetPlayerID() {return m_spinner_widget_player_id; } void unsetUseBackgroundColor() {m_use_background_color=false; } void activateSelectedButton(); diff --git a/src/karts/controller/player_controller.cpp b/src/karts/controller/player_controller.cpp index 55d0b191842..d0c0569716d 100644 --- a/src/karts/controller/player_controller.cpp +++ b/src/karts/controller/player_controller.cpp @@ -368,7 +368,7 @@ core::stringw PlayerController::getName(bool include_handicap_string) const const RemoteKartInfo& rki = RaceManager::get()->getKartInfo( m_kart->getWorldKartId()); name = rki.getPlayerName(); - if (include_handicap_string && rki.getHandicap() == HANDICAP_MEDIUM) + if (include_handicap_string && rki.getHandicap() != HANDICAP_NONE) { #ifdef SERVER_ONLY name += L" (handicapped)"; diff --git a/src/karts/kart_properties.cpp b/src/karts/kart_properties.cpp index ff1f0083649..2bbfd35fbda 100644 --- a/src/karts/kart_properties.cpp +++ b/src/karts/kart_properties.cpp @@ -58,8 +58,14 @@ std::string KartProperties::getHandicapAsString(HandicapLevel h) { switch(h) { - case HANDICAP_NONE: return "normal"; break; - case HANDICAP_MEDIUM: return "handicap"; break; + case HANDICAP_NONE: return "normal"; break; + case HANDICAP_4: return "handicap-4"; break; + case HANDICAP_8: return "handicap-8"; break; + case HANDICAP_12: return "handicap-12"; break; + case HANDICAP_16: return "handicap-16"; break; + case HANDICAP_20: return "handicap-20"; break; + case HANDICAP_24: return "handicap-24"; break; + default: assert(false); } return ""; diff --git a/src/network/remote_kart_info.hpp b/src/network/remote_kart_info.hpp index 80a6339187c..a5bfecd20e0 100644 --- a/src/network/remote_kart_info.hpp +++ b/src/network/remote_kart_info.hpp @@ -41,7 +41,12 @@ enum KartTeam : int8_t enum HandicapLevel : uint8_t { HANDICAP_NONE = 0, - HANDICAP_MEDIUM, + HANDICAP_4, + HANDICAP_8, + HANDICAP_12, + HANDICAP_16, + HANDICAP_20, + HANDICAP_24, HANDICAP_COUNT }; diff --git a/src/states_screens/dialogs/network_player_dialog.cpp b/src/states_screens/dialogs/network_player_dialog.cpp index a488d456204..d620572cb5e 100644 --- a/src/states_screens/dialogs/network_player_dialog.cpp +++ b/src/states_screens/dialogs/network_player_dialog.cpp @@ -268,7 +268,7 @@ GUIEngine::EventPropagation HandicapLevel new_handicap = HANDICAP_NONE; if (m_handicap == HANDICAP_NONE) { - new_handicap = HANDICAP_MEDIUM; + new_handicap = HANDICAP_4; } NetworkString change_handicap(PROTOCOL_LOBBY_ROOM); change_handicap.addUInt8(LobbyProtocol::LE_CHANGE_HANDICAP) diff --git a/src/states_screens/dialogs/splitscreen_player_dialog.cpp b/src/states_screens/dialogs/splitscreen_player_dialog.cpp index 98b37950d6e..5a735b805f0 100644 --- a/src/states_screens/dialogs/splitscreen_player_dialog.cpp +++ b/src/states_screens/dialogs/splitscreen_player_dialog.cpp @@ -113,7 +113,7 @@ GUIEngine::EventPropagation assert(pid < PlayerManager::get()->getNumPlayers()); PlayerProfile* p = m_available_players[pid]; const HandicapLevel h = m_handicap->getState() ? - HANDICAP_MEDIUM : HANDICAP_NONE; + HANDICAP_4 : HANDICAP_NONE; if (NetworkConfig::get()->addNetworkPlayer(m_device, p, h)) { core::stringw name = p->getName(); diff --git a/src/states_screens/kart_selection.cpp b/src/states_screens/kart_selection.cpp index ca9dd0e4082..2ba312a8bd6 100644 --- a/src/states_screens/kart_selection.cpp +++ b/src/states_screens/kart_selection.cpp @@ -1330,7 +1330,7 @@ void KartSelectionScreen::allPlayersDone() RaceManager::get()->setPlayerKart(n, selected_kart); // Set handicap if needed - if (m_multiplayer && UserConfigParams::m_per_player_difficulty) + if (UserConfigParams::m_per_player_difficulty) RaceManager::get()->setPlayerHandicap(n, m_kart_widgets[n].getHandicap()); } diff --git a/src/states_screens/online/network_kart_selection.cpp b/src/states_screens/online/network_kart_selection.cpp index 8539caa567d..8e2539a419e 100644 --- a/src/states_screens/online/network_kart_selection.cpp +++ b/src/states_screens/online/network_kart_selection.cpp @@ -64,7 +64,7 @@ void NetworkKartSelectionScreen::init() for (auto& p : NetworkConfig::get()->getNetworkPlayers()) { joinPlayer(std::get<0>(p), std::get<1>(p)); - if (std::get<2>(p) == HANDICAP_MEDIUM) + if (std::get<2>(p) == HANDICAP_4) { m_kart_widgets.get(m_kart_widgets.size() -1) ->enableHandicapForNetwork(); diff --git a/src/states_screens/race_result_gui.cpp b/src/states_screens/race_result_gui.cpp index a0852aad469..a974694899b 100644 --- a/src/states_screens/race_result_gui.cpp +++ b/src/states_screens/race_result_gui.cpp @@ -1598,7 +1598,7 @@ void RaceResultGUI::unload() const bool own_goal = !(scorers.at(i).m_correct_goal); result_text = scorers.at(i).m_player; - if (scorers.at(i).m_handicap_level == HANDICAP_MEDIUM) + if (scorers.at(i).m_handicap_level != HANDICAP_NONE) result_text = _("%s (handicapped)", result_text); if (own_goal) @@ -1660,7 +1660,7 @@ void RaceResultGUI::unload() const bool own_goal = !(scorers.at(i).m_correct_goal); result_text = scorers.at(i).m_player; - if (scorers.at(i).m_handicap_level == HANDICAP_MEDIUM) + if (scorers.at(i).m_handicap_level != HANDICAP_NONE) result_text = _("%s (handicapped)", result_text); if (own_goal) From 9b70b92a12c745aee9bae39e4eae68d99dc842e2 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Tue, 16 Apr 2024 21:37:25 +0200 Subject: [PATCH 107/188] Improve the speed stats bar - Ensure that negative values are not displayed for high handicap values - Use non-linear scaling to keep the speed differences between kart classes visually important --- src/guiengine/widgets/kart_stats_widget.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/guiengine/widgets/kart_stats_widget.cpp b/src/guiengine/widgets/kart_stats_widget.cpp index 1c1bb8658d6..87595e6c1a6 100644 --- a/src/guiengine/widgets/kart_stats_widget.cpp +++ b/src/guiengine/widgets/kart_stats_widget.cpp @@ -103,10 +103,17 @@ void KartStatsWidget::setValues(const KartProperties* props, HandicapLevel h) (kp_computed.getCombinedCharacteristic()->getMass() - 100.0f)/2.78f, "mass.png", "mass", _("Mass")); - // The base speed is of 27.5 - // Here we are not fully proportional, because small differences matter more + // The base speed is of 27.75 + // Speed is the characteristics most affected by handicap, but it is also + // important to display the base differences between classes as significant, + // as small differences matter a lot. + // Therefore, a non-linear formula is used. + float speed_power_four = kp_computed.getCombinedCharacteristic()->getEngineMaxSpeed(); + speed_power_four *= speed_power_four; // squaring + speed_power_four *= speed_power_four; // squaring again + speed_power_four *= 0.001f; // divide by 1000 to improve readbility of the formula setSkillValues(SKILL_SPEED, - (kp_computed.getCombinedCharacteristic()->getEngineMaxSpeed() - 22.25f) * 15.0f, + (speed_power_four - 100.0f) * 0.1924f, "speed.png", "speed", _("Maximum speed")); // The acceleration depend on power and mass, and it changes depending on speed From f281f771946f98efdc1c1b6dfe80f501429b967e Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Fri, 10 May 2024 01:48:35 +0200 Subject: [PATCH 108/188] Fix a bug with the electro-shield speed boost When a new attachment would be activated without shields being explicitely removed, such as when activating a swatter or a gum shield, the speed boost was not properly removed. --- src/items/attachment.cpp | 11 +++++++++-- src/items/attachment.hpp | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/items/attachment.cpp b/src/items/attachment.cpp index cb06fdbb5c4..0c16c7564fe 100644 --- a/src/items/attachment.cpp +++ b/src/items/attachment.cpp @@ -119,7 +119,7 @@ void Attachment::set(AttachmentType type, int ticks, { bool was_bomb = m_type == ATTACH_BOMB; int16_t prev_ticks = m_ticks_left; - clear(); + clear(type); // If necessary create the appropriate plugin which encapsulates // the associated behavior @@ -176,8 +176,15 @@ void Attachment::set(AttachmentType type, int ticks, * takes care of resetting the owner kart's physics structures to account for * the updated mass. */ -void Attachment::clear() +void Attachment::clear(AttachmentType type) { + // Ensure that the boost from an electro-shield is always removed when the + // electro-shield attachment gets cleared, except when a new electro-shield + // is about to be set, as this would incorrectly remove the boost. + if(m_ticks_left > 0 && type != ATTACH_ELECTRO_SHIELD && + m_type == ATTACH_ELECTRO_SHIELD) + m_kart->unsetElectroShield(); + if (m_plugin) { delete m_plugin; diff --git a/src/items/attachment.hpp b/src/items/attachment.hpp index d77f1d1952f..1e245e9a6c1 100644 --- a/src/items/attachment.hpp +++ b/src/items/attachment.hpp @@ -118,7 +118,7 @@ class Attachment: public NoCopy public: Attachment(Kart* kart); ~Attachment(); - void clear(); + void clear(AttachmentType type = ATTACH_NOTHING); void hitBanana(ItemState *item); void updateGraphics(float dt); From 82a732acc172245fdf816aaab0fe3b3b64d8947d Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Fri, 10 May 2024 01:49:27 +0200 Subject: [PATCH 109/188] Fix #5060 --- src/items/attachment.cpp | 43 ++++++++++++++++++++++++---------------- src/items/attachment.hpp | 3 +++ src/items/powerup.cpp | 16 ++++++++++----- 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/src/items/attachment.cpp b/src/items/attachment.cpp index 0c16c7564fe..48010dd5bcd 100644 --- a/src/items/attachment.cpp +++ b/src/items/attachment.cpp @@ -554,23 +554,7 @@ void Attachment::update(int ticks) case ATTACH_NOLOK_BUBBLEGUM_SHIELD_SMALL: m_initial_speed = 0; if (m_ticks_left <= 0) - { - if (!RewindManager::get()->isRewinding()) - { - if (m_bubble_explode_sound) m_bubble_explode_sound->deleteSFX(); - m_bubble_explode_sound = - SFXManager::get()->createSoundSource("bubblegum_explode"); - m_bubble_explode_sound->setPosition(m_kart->getXYZ()); - m_bubble_explode_sound->play(); - } - if (!m_kart->isGhostKart()) - { - if (m_type == ATTACH_BUBBLEGUM_SHIELD || m_type == ATTACH_NOLOK_BUBBLEGUM_SHIELD) - Track::getCurrentTrack()->getItemManager()->dropNewItem(Item::ITEM_BUBBLEGUM, m_kart); - else - Track::getCurrentTrack()->getItemManager()->dropNewItem(Item::ITEM_BUBBLEGUM_SMALL, m_kart); - } - } + popGumShield(); break; case ATTACH_ELECTRO_SHIELD: @@ -724,3 +708,28 @@ float Attachment::weightAdjust() const ? m_kart->getKartProperties()->getAnvilWeight() : 0.0f; } // weightAdjust + +// ---------------------------------------------------------------------------- +/** Performs all actions necessary for a gum shield to leave a ground gum behind. + * The expiration of the gum shield is not managed here. + */ +void Attachment::popGumShield() +{ + // Neither sound nor actual ground gum should be left behind by a ghost kart + // TODO: when replay of normal races is supported, drop a "ghost ground gum"? + if (m_kart->isGhostKart()) + return; + + if (!RewindManager::get()->isRewinding()) + { + if (m_bubble_explode_sound) m_bubble_explode_sound->deleteSFX(); + m_bubble_explode_sound = + SFXManager::get()->createSoundSource("bubblegum_explode"); + m_bubble_explode_sound->setPosition(m_kart->getXYZ()); + m_bubble_explode_sound->play(); + } + if (m_type == ATTACH_BUBBLEGUM_SHIELD || m_type == ATTACH_NOLOK_BUBBLEGUM_SHIELD) + Track::getCurrentTrack()->getItemManager()->dropNewItem(Item::ITEM_BUBBLEGUM, m_kart); + else + Track::getCurrentTrack()->getItemManager()->dropNewItem(Item::ITEM_BUBBLEGUM_SMALL, m_kart); +} // popGumShield \ No newline at end of file diff --git a/src/items/attachment.hpp b/src/items/attachment.hpp index 1e245e9a6c1..8fb97bd8fcb 100644 --- a/src/items/attachment.hpp +++ b/src/items/attachment.hpp @@ -151,6 +151,9 @@ class Attachment: public NoCopy /** Returns additional weight for the kart. */ float weightAdjust() const; // ------------------------------------------------------------------------ + /** Performs all actions necessary for a gum shield to leave a ground gum behind. */ + void popGumShield(); + // ------------------------------------------------------------------------ /** Return the currently associated scene node (used by e.g the swatter) */ scene::IAnimatedMeshSceneNode* getNode() {return m_node;} // ------------------------------------------------------------------------ diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index 5fa6cffb8c1..8f09cab9f1d 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -356,11 +356,17 @@ void Powerup::use() break; case PowerupManager::POWERUP_SWATTER: - m_kart->getAttachment() - ->set(Attachment::ATTACH_SWATTER, - stk_config->time2Ticks(kp->getSwatterDuration())); - break; - + { + // Instead of having the swatter attachment just replace the gum shield, + // ensure the gum is popped on the ground. + // DecreaseShieldTime would not work here because the new attachment + // would be set before the next attachment update. + if(m_kart->isGumShielded()) + m_kart->getAttachment()->popGumShield(); + m_kart->getAttachment()->set(Attachment::ATTACH_SWATTER, + stk_config->time2Ticks(kp->getSwatterDuration())); + break; + } case PowerupManager::POWERUP_SUDO: { Kart* player_kart = NULL; From dde62963904af33fa5dff15ea678726af9b34f73 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Tue, 21 May 2024 13:00:41 +0200 Subject: [PATCH 110/188] Remove an outdated operation that caused an assert fail --- src/states_screens/kart_selection.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/states_screens/kart_selection.cpp b/src/states_screens/kart_selection.cpp index efc38d23e8f..ea12cd12d47 100644 --- a/src/states_screens/kart_selection.cpp +++ b/src/states_screens/kart_selection.cpp @@ -1377,8 +1377,6 @@ bool KartSelectionScreen::validateIdentChoices() if (m_multiplayer) { int spinner_value = m_kart_widgets[n].m_player_ident_spinner->getValue(); - if (UserConfigParams::m_per_player_difficulty) - spinner_value /= 2; assert(m_kart_widgets[n].getAssociatedPlayer()->getProfile() == PlayerManager::get()->getPlayer(spinner_value)); } From 181bac7c75487733ad66f24dfbf81cfbb560e492 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Fri, 24 May 2024 20:35:40 +0200 Subject: [PATCH 111/188] Fix nitro-hack sound management - Use a new PowerupAudio class to avoid managing creation and deletion in powerup.cpp: the SFX source for the nitro-hack is created on first use and reused as needed. It is a non-positional sound played only for local player karts, therefore it doesn't need multiple instance. It is acceptable to have the sound start over in splitscreen multiplayer if two players happen to use it almost at the same time, or to be on the receiving end of two distincts nitro-hacks almost at the same time. - This fixes the nitro-hack sounds getting interrupted early - This fixes the nitro-hack sound sources leaking, leading to OpenAL running out of audio sources Logging shows there are potentially other leaks, although the leaks from the nitro-hack were the most severe. --- src/items/powerup.cpp | 21 ++++++---------- src/items/powerup_audio.cpp | 48 +++++++++++++++++++++++++++++++++++++ src/items/powerup_audio.hpp | 44 ++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 14 deletions(-) create mode 100644 src/items/powerup_audio.cpp create mode 100644 src/items/powerup_audio.hpp diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index 8f09cab9f1d..9406425c51b 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -24,6 +24,7 @@ #include "config/stk_config.hpp" #include "items/attachment.hpp" #include "items/item_manager.hpp" +#include "items/powerup_audio.hpp" #include "items/projectile_manager.hpp" #include "items/rubber_ball.hpp" #include "karts/kart.hpp" @@ -169,8 +170,8 @@ void Powerup::set(PowerupManager::PowerupType type, int n) case PowerupManager::POWERUP_ZIPPER: break ; + // Special sound effect management case PowerupManager::POWERUP_SUDO: - m_sound_use = SFXManager::get()->createSoundSource("sudo_bad"); break ; // TODO : add sound effects @@ -422,19 +423,11 @@ void Powerup::use() // Play a good sound for the kart that benefits from the "nitro-hack", // if it's a local player - if (!has_played_sound && m_kart->getController()->isLocalPlayerController()) - { - //Extraordinary. Usually sounds are set in Powerup::set() - m_sound_use = SFXManager::get()->createSoundSource("sudo_good"); - //In this case this is a workaround, since the sudo item has two different sounds - - m_sound_use->play(); - } - // Play a bad sound if the affected kart (but not the user) is a local player - else if (!has_played_sound && player_kart != NULL) - { - m_sound_use->play(); - } + if (m_kart->getController()->isLocalPlayerController()) + PowerupAudio::getInstance()->playSudoGoodSFX(); + // Play a bad sound if there is an affected local player + if (player_kart != NULL) + PowerupAudio::getInstance()->playSudoBadSFX(); break; } // end of PowerupManager::POWERUP_SUDO diff --git a/src/items/powerup_audio.cpp b/src/items/powerup_audio.cpp new file mode 100644 index 00000000000..45ca924551c --- /dev/null +++ b/src/items/powerup_audio.cpp @@ -0,0 +1,48 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2024 Alayan +// +// 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 "items/powerup_audio.hpp" + +#include "audio/sfx_base.hpp" +#include "audio/sfx_manager.hpp" + +//----------------------------------------------------------------------------- +PowerupAudio* PowerupAudio::getInstance() +{ + static PowerupAudio instance; + return &instance; +} + +//----------------------------------------------------------------------------- +/** Constructor */ +PowerupAudio::PowerupAudio() +{ + printf("Constructor called\n"); + m_sudo_good = SFXManager::get()->createSoundSource("sudo_good"); + m_sudo_bad = SFXManager::get()->createSoundSource("sudo_bad"); +} + +//----------------------------------------------------------------------------- +/** Frees the memory for the sound effects. + */ +PowerupAudio::~PowerupAudio() +{ + printf("Destructor called\n"); + m_sudo_good->deleteSFX(); + m_sudo_bad->deleteSFX(); +} // ~Powerup diff --git a/src/items/powerup_audio.hpp b/src/items/powerup_audio.hpp new file mode 100644 index 00000000000..43e21becbd2 --- /dev/null +++ b/src/items/powerup_audio.hpp @@ -0,0 +1,44 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2024 Alayan +// +// 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_POWERUPAUDIO_HPP +#define HEADER_POWERUPAUDIO_HPP + +#include "audio/sfx_base.hpp" + +class PowerupAudio : public NoCopy +{ +private: + /** Store the nitro hack sounds separately: + * - This avoids them being interrupted too often. + * - This limits the number of sound sources in use + * - This simplifies creation and deletion management */ + SFXBase *m_sudo_good; + SFXBase *m_sudo_bad; + + PowerupAudio(); +public: + static PowerupAudio* getInstance(); + + ~PowerupAudio(); + + void playSudoGoodSFX() { m_sudo_good->play(); } + void playSudoBadSFX() { m_sudo_bad->play(); } +}; + +#endif \ No newline at end of file From 80cb12287fac90ed30e77cee311a4b93b01176e8 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Fri, 24 May 2024 21:49:02 +0200 Subject: [PATCH 112/188] Fix further audio leaks --- src/items/powerup.cpp | 40 ++++++++++++++++++++-------------------- src/items/powerup.hpp | 1 + 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index 9406425c51b..fc81780c429 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -155,11 +155,7 @@ void Powerup::set(PowerupManager::PowerupType type, int n) if (RewindManager::get()->isRewinding()) return; - if (m_sound_use != NULL) - { - m_sound_use->deleteSFX(); - m_sound_use = NULL; - } + resetSoundSource(); switch (m_type) { @@ -270,6 +266,9 @@ Material *Powerup::getIcon(bool wide) const */ void Powerup::adjustSound() { + if (m_sound_use == NULL) + return; + m_sound_use->setPosition(m_kart->getXYZ()); // in multiplayer mode, sounds are NOT positional (because we have multiple listeners) // so the sounds of all AIs are constantly heard. So reduce volume of sounds. @@ -319,12 +318,6 @@ void Powerup::use() m_type != PowerupManager::POWERUP_ZIPPER) m_kart->playCustomSFX(SFXManager::CUSTOM_SHOOT); - // FIXME - for some collectibles, set() is never called - if (!has_played_sound && m_sound_use == NULL) - { - m_sound_use = SFXManager::get()->createSoundSource("shoot"); - } - m_number--; World *world = World::getWorld(); ItemManager* im = Track::getCurrentTrack()->getItemManager(); @@ -478,8 +471,9 @@ void Powerup::use() m_kart->decreaseShieldTime(); if (!has_played_sound) { - Powerup::adjustSound(); + resetSoundSource(); m_sound_use = SFXManager::get()->createSoundSource("shoot"); + Powerup::adjustSound(); m_sound_use->play(); } ProjectileManager::get()->newProjectile(m_kart, PowerupManager::POWERUP_MINI); @@ -625,7 +619,7 @@ void Powerup::use() void Powerup::useBubblegum(bool has_played_sound, bool mini) { - m_sound_use = SFXManager::get()->createSoundSource("goo"); + resetSoundSource(); ItemManager* im = Track::getCurrentTrack()->getItemManager(); const KartProperties *kp = m_kart->getKartProperties(); @@ -639,6 +633,7 @@ void Powerup::useBubblegum(bool has_played_sound, bool mini) if(!new_item) return; if (!has_played_sound) { + m_sound_use = SFXManager::get()->createSoundSource("goo"); Powerup::adjustSound(); m_sound_use->play(); } @@ -690,21 +685,26 @@ void Powerup::useBubblegum(bool has_played_sound, bool mini) if (!has_played_sound) { - if (m_sound_use != NULL) - { - m_sound_use->deleteSFX(); - m_sound_use = NULL; - } //Extraordinary. Usually sounds are set in Powerup::set() - m_sound_use = SFXManager::get()->createSoundSource("inflate"); //In this case this is a workaround, since the bubblegum item has two different sounds. - + m_sound_use = SFXManager::get()->createSoundSource("inflate"); Powerup::adjustSound(); m_sound_use->play(); } } } // useBubblegum +//----------------------------------------------------------------------------- +/** This function ensure we don't leak sound sources */ +void Powerup::resetSoundSource() +{ + if (m_sound_use != NULL) + { + m_sound_use->deleteSFX(); + m_sound_use = NULL; + } +} // resetSoundSource + //----------------------------------------------------------------------------- /** This function is called when a bonus box is it. This function can be * called on a server (in which case item and add_info are not used), diff --git a/src/items/powerup.hpp b/src/items/powerup.hpp index f92f1e4cda1..322d70f79a8 100644 --- a/src/items/powerup.hpp +++ b/src/items/powerup.hpp @@ -57,6 +57,7 @@ class Powerup : public NoCopy /** Returns an integer in the 0-32767 range.*/ int simplePRNG(const int seed, const int time, const int item_id, const int position); void useBubblegum(bool has_played_sound, bool mini = false); + void resetSoundSource(); public: Powerup (Kart* kart_); From 2fcdbb432890a562e967874fb71e56e3df667702 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Tue, 28 May 2024 23:50:37 +0200 Subject: [PATCH 113/188] Add a SFX debug logging utility --- src/audio/sfx_manager.cpp | 19 +++++++++++++++++++ src/audio/sfx_manager.hpp | 7 +++++++ 2 files changed, 26 insertions(+) diff --git a/src/audio/sfx_manager.cpp b/src/audio/sfx_manager.cpp index 66aeb38b69f..4ac78c407b3 100644 --- a/src/audio/sfx_manager.cpp +++ b/src/audio/sfx_manager.cpp @@ -77,6 +77,9 @@ void SFXManager::destroy() */ SFXManager::SFXManager() { +#ifdef DEBUG_SFX + m_created_count = m_deleted_count = 0; +#endif // The sound manager initialises OpenAL m_initialized = music_manager->initialized(); @@ -692,6 +695,14 @@ SFXBase* SFXManager::createSoundSource(SFXBuffer* buffer, const bool add_to_SFX_list, const bool owns_buffer) { +#ifdef DEBUG_SFX + m_created_count++; + Log::info("SFXManager", "The number of created sound sources is %u," + "the number of deleted sound sources is %u," + "the number of active sources is %u", + m_created_count, m_deleted_count, m_all_sfx.getData().size()); +#endif + bool positional = false; if (RaceManager::get()->getNumLocalPlayers() < 2) @@ -868,6 +879,14 @@ void SFXManager::reallyUpdateNow(SFXCommand *current) */ void SFXManager::deleteSFX(SFXBase *sfx) { +#ifdef DEBUG_SFX + m_deleted_count++; + Log::info("SFXManager", "The number of created sound sources is %u," + "the number of deleted sound sources is %u," + "the number of active sources is %u", + m_created_count, m_deleted_count, m_all_sfx.getData().size()); +#endif + if(sfx) sfx->reallyStopNow(); std::vector::iterator i; diff --git a/src/audio/sfx_manager.hpp b/src/audio/sfx_manager.hpp index fe2d44ff099..d4e22bebb27 100644 --- a/src/audio/sfx_manager.hpp +++ b/src/audio/sfx_manager.hpp @@ -39,6 +39,8 @@ typedef unsigned int ALuint; #endif +#undef DEBUG_SFX + class MusicInformation; class SFXBase; class SFXBuffer; @@ -225,6 +227,11 @@ class SFXManager : public NoCopy, public CanBeDeleted /** A conditional variable to wake up the main loop. */ std::condition_variable m_condition_variable; +#ifdef DEBUG_SFX + unsigned int m_created_count; + unsigned int m_deleted_count; +#endif + void loadSfx(); SFXManager(); virtual ~SFXManager(); From c3a534904f955054ff6786d56221e8e59a10d4d9 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Wed, 29 May 2024 01:13:02 +0200 Subject: [PATCH 114/188] Initial work on managing powerup audio separately There are multiple regressions to be fixed: - Doesn't correctly handle rewind - Doesn't correctly handle multi-kart sound effects - Different karts rely on the same SFXBase But all the direct sound management has been removed from powerup.cpp --- src/items/powerup.cpp | 305 ++++++++---------------------------- src/items/powerup.hpp | 19 +-- src/items/powerup_audio.cpp | 246 ++++++++++++++++++++++++++++- src/items/powerup_audio.hpp | 16 ++ 4 files changed, 327 insertions(+), 259 deletions(-) diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index fc81780c429..e53b39ca73a 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -46,18 +46,9 @@ Powerup::Powerup(Kart* kart) { m_kart = kart; - m_sound_use = NULL; reset(); } // Powerup -//----------------------------------------------------------------------------- -/** Frees the memory for the sound effects. - */ -Powerup::~Powerup() -{ - if(m_sound_use) m_sound_use->deleteSFX(); -} // ~Powerup - //----------------------------------------------------------------------------- /** Resets the powerup, called at begin of a race. */ @@ -114,24 +105,13 @@ void Powerup::rewindTo(BareNetworkString *buffer) //----------------------------------------------------------------------------- void Powerup::update(int ticks) { - // Remove any sound ticks that should have played - const int remove_ticks = World::getWorld()->getTicksSinceStart() - 1000; - for (auto it = m_played_sound_ticks.begin(); - it != m_played_sound_ticks.end();) - { - if (*it < remove_ticks) - { - it = m_played_sound_ticks.erase(it); - continue; - } - break; - } + PowerupAudio::getInstance()->update(m_kart, ticks); } // update //----------------------------------------------------------------------------- /** Sets the collected items. The number of items is increased if the same - * item is currently collected, otherwise replaces the existing item. It also - * sets item specific sounds. + * item is currently collected, otherwise replaces the existing item. + * Also set the item-specific sound. * \param type Thew new type. * \param n Number of items of the given type. */ @@ -140,72 +120,22 @@ void Powerup::set(PowerupManager::PowerupType type, int n) if (m_type==type) { m_number+=n; - // Limit to 255 (save space in network state saving) - if(m_number>255) m_number = 255; - return; } - m_type=type; - - // Limit to 255 (save space in network state saving) - if(n>255) n = 255; - - m_number=n; - - // Don't re-create sound sound during rewinding - if (RewindManager::get()->isRewinding()) - return; - - resetSoundSource(); - - switch (m_type) + else { - // No sound effect when arming the glove - case PowerupManager::POWERUP_SWATTER: - break; - - case PowerupManager::POWERUP_ZIPPER: - break ; + m_type=type; + m_number=n; - // Special sound effect management - case PowerupManager::POWERUP_SUDO: - break ; + // Don't re-create sound during rewinding + if (!RewindManager::get()->isRewinding()) + PowerupAudio::getInstance()->setPowerupSound(m_kart, m_type); - // TODO : add sound effects - case PowerupManager::POWERUP_ELECTRO: - break ; - - case PowerupManager::POWERUP_MINI: + if (m_type == PowerupManager::POWERUP_MINI) m_mini_state = PowerupManager::MINI_SELECT; - break ; - - case PowerupManager::POWERUP_BOWLING: - m_sound_use = SFXManager::get()->createSoundSource("bowling_shoot"); - break ; - - case PowerupManager::POWERUP_ANVIL: - m_sound_use = SFXManager::get()->createSoundSource("anvil"); - break; - - case PowerupManager::POWERUP_PARACHUTE: - m_sound_use = SFXManager::get()->createSoundSource("parachute"); - break; - - case PowerupManager::POWERUP_BUBBLEGUM: - // handled in the useBubblegum function - break ; - - case PowerupManager::POWERUP_SWITCH: - m_sound_use = SFXManager::get()->createSoundSource("swap"); - break; - - case PowerupManager::POWERUP_NOTHING: - case PowerupManager::POWERUP_CAKE: - case PowerupManager::POWERUP_PLUNGER: - default : - m_sound_use = SFXManager::get()->createSoundSource("shoot"); - break ; } + // Limit to 255 (save space in network state saving) + if(n>255) n = 255; } // set //----------------------------------------------------------------------------- @@ -261,46 +191,11 @@ Material *Powerup::getIcon(bool wide) const return powerup_manager->getIcon(m_type); } // getIcon -//----------------------------------------------------------------------------- -/** Does the sound configuration. - */ -void Powerup::adjustSound() -{ - if (m_sound_use == NULL) - return; - - m_sound_use->setPosition(m_kart->getXYZ()); - // in multiplayer mode, sounds are NOT positional (because we have multiple listeners) - // so the sounds of all AIs are constantly heard. So reduce volume of sounds. - if (RaceManager::get()->getNumLocalPlayers() > 1) - { - // player karts played at full volume; AI karts much dimmer - - if (m_kart->getController()->isLocalPlayerController()) - { - m_sound_use->setVolume( 1.0f ); - } - else - { - m_sound_use->setVolume( - std::min(0.5f, 1.0f / RaceManager::get()->getNumberOfKarts()) ); - } - } -} // adjustSound - //----------------------------------------------------------------------------- /** Use (fire) this powerup. */ void Powerup::use() { - const int ticks = World::getWorld()->getTicksSinceStart(); - bool has_played_sound = false; - auto it = m_played_sound_ticks.find(ticks); - if (it != m_played_sound_ticks.end()) - has_played_sound = true; - else - m_played_sound_ticks.insert(ticks); - const KartProperties *kp = m_kart->getKartProperties(); // The player gets an achievement point for using a powerup @@ -312,43 +207,38 @@ void Powerup::use() PlayerManager::increaseAchievement(AchievementsStatus::POWERUP_USED_1RACE, 1); } - // Play custom kart sound when collectible is used //TODO: what about the bubble gum? - if (m_type != PowerupManager::POWERUP_NOTHING && - m_type != PowerupManager::POWERUP_SWATTER && - m_type != PowerupManager::POWERUP_ZIPPER) - m_kart->playCustomSFX(SFXManager::CUSTOM_SHOOT); - m_number--; World *world = World::getWorld(); ItemManager* im = Track::getCurrentTrack()->getItemManager(); + + // Some powerups can play different sounds depending on the situation + int sound_type = 1; + PowerupManager::MiniState audio_mini_state = m_mini_state; + switch (m_type) { case PowerupManager::POWERUP_ZIPPER: - m_kart->handleZipper(NULL, true); - break ; + { + m_kart->handleZipper(NULL, true); + break ; + } case PowerupManager::POWERUP_SWITCH: { im->switchItems(); - if (!has_played_sound) - m_sound_use->play(); break; } case PowerupManager::POWERUP_CAKE: case PowerupManager::POWERUP_RUBBERBALL: case PowerupManager::POWERUP_BOWLING: case PowerupManager::POWERUP_PLUNGER: - // make weapon usage destroy gum shields - if(stk_config->m_shield_restrict_weapons && - m_kart->isGumShielded()) - m_kart->decreaseShieldTime(); - if (!has_played_sound) { - Powerup::adjustSound(); - m_sound_use->play(); + // make weapon usage destroy gum shields + if(stk_config->m_shield_restrict_weapons && + m_kart->isGumShielded()) + m_kart->decreaseShieldTime(); + ProjectileManager::get()->newProjectile(m_kart, m_type); + break; } - ProjectileManager::get()->newProjectile(m_kart, m_type); - break; - case PowerupManager::POWERUP_SWATTER: { // Instead of having the swatter attachment just replace the gum shield, @@ -358,7 +248,7 @@ void Powerup::use() if(m_kart->isGumShielded()) m_kart->getAttachment()->popGumShield(); m_kart->getAttachment()->set(Attachment::ATTACH_SWATTER, - stk_config->time2Ticks(kp->getSwatterDuration())); + stk_config->time2Ticks(kp->getSwatterDuration())); break; } case PowerupManager::POWERUP_SUDO: @@ -401,6 +291,7 @@ void Powerup::use() } } } + // Multiply current nitro by a given factor if it is currently negative if(m_kart->getEnergy() < 0) m_kart->setEnergy(m_kart->getEnergy()*negative_multiply); @@ -414,16 +305,8 @@ void Powerup::use() m_kart->addEnergy(stolen_energy, /* allow negatives */ false); m_kart->activateNitroHack(); - // Play a good sound for the kart that benefits from the "nitro-hack", - // if it's a local player - if (m_kart->getController()->isLocalPlayerController()) - PowerupAudio::getInstance()->playSudoGoodSFX(); - // Play a bad sound if there is an affected local player - if (player_kart != NULL) - PowerupAudio::getInstance()->playSudoBadSFX(); - break; - } // end of PowerupManager::POWERUP_SUDO + } // end of PowerupManager::POWERUP_SUDO case PowerupManager::POWERUP_ELECTRO: { @@ -435,8 +318,7 @@ void Powerup::use() stk_config->time2Ticks(kp->getElectroDuration())); break; - } // end of PowerupManager::POWERUP_ELECTRO* - + } // end of PowerupManager::POWERUP_ELECTRO case PowerupManager::POWERUP_MINI: { @@ -469,13 +351,6 @@ void Powerup::use() if(stk_config->m_shield_restrict_weapons && m_kart->isGumShielded()) m_kart->decreaseShieldTime(); - if (!has_played_sound) - { - resetSoundSource(); - m_sound_use = SFXManager::get()->createSoundSource("shoot"); - Powerup::adjustSound(); - m_sound_use->play(); - } ProjectileManager::get()->newProjectile(m_kart, PowerupManager::POWERUP_MINI); break; } // mini-cake case @@ -495,53 +370,40 @@ void Powerup::use() { // This allows to use multiple mini-wishes in different ways m_mini_state = PowerupManager::MINI_SELECT; - useBubblegum(has_played_sound, /* mini */ true); + sound_type = useBubblegum(/* mini */ true); break; } // mini-gum case } // Switch mini-state - } // end of PowerupManager::POWERUP_MINI - break; + break; + } // end of PowerupManager::POWERUP_MINI case PowerupManager::POWERUP_BUBBLEGUM: { - useBubblegum(has_played_sound); + sound_type = useBubblegum(); + break; } - break; case PowerupManager::POWERUP_ANVIL: - //Attach an anvil(twice as good as the one given - //by the bananas) to the kart in the 1st position. - for(unsigned int i = 0 ; i < world->getNumKarts(); ++i) { - Kart *kart=world->getKart(i); - if(kart->isEliminated() || kart->isInvulnerable()) continue; - if(kart == m_kart) continue; - if(kart->getPosition() == 1) + //Attach an anvil(twice as good as the one given + //by the bananas) to the kart in the 1st position. + for(unsigned int i = 0 ; i < world->getNumKarts(); ++i) { - kart->getAttachment()->set(Attachment::ATTACH_ANVIL, - stk_config-> - time2Ticks(kp->getAnvilDuration()) ); - kart->adjustSpeed(kp->getAnvilSpeedFactor() * 0.5f); - - // should we position the sound at the kart that is hit, - // or the kart "throwing" the anvil? Ideally it should be both. - // Meanwhile, don't play it near AI karts since they obviously - // don't hear anything - if (!has_played_sound) + Kart *kart=world->getKart(i); + if(kart->isEliminated() || kart->isInvulnerable()) continue; + if(kart == m_kart) continue; + if(kart->getPosition() == 1) { - if(kart->getController()->isLocalPlayerController()) - m_sound_use->setPosition(kart->getXYZ()); - else - m_sound_use->setPosition(m_kart->getXYZ()); - - m_sound_use->play(); + kart->getAttachment()->set(Attachment::ATTACH_ANVIL, + stk_config-> + time2Ticks(kp->getAnvilDuration()) ); + kart->adjustSpeed(kp->getAnvilSpeedFactor() * 0.5f); + break; } - break; } + break; } - break; - case PowerupManager::POWERUP_PARACHUTE: { Kart* player_kart = NULL; @@ -569,7 +431,7 @@ void Powerup::use() { float rank_factor; - rank_factor = (float)(kart->getPosition() - 1) + rank_factor = (float)(kart->getPosition() - 1) / (float)(m_kart->getPosition() - 2); position_factor = 1.0f - rank_factor; } @@ -585,31 +447,15 @@ void Powerup::use() player_kart = kart; } } - - // should we position the sound at the kart that is hit, - // or the kart "throwing" the anvil? Ideally it should be both. - // Meanwhile, don't play it near AI karts since they obviously - // don't hear anything - if (!has_played_sound) - { - if(m_kart->getController()->isLocalPlayerController()) - m_sound_use->setPosition(m_kart->getXYZ()); - else if(player_kart) - m_sound_use->setPosition(player_kart->getXYZ()); - m_sound_use->play(); - } + break; } - break; case PowerupManager::POWERUP_NOTHING: - { - if(!m_kart->getKartAnimation()) - m_kart->beep(); - } - break; default : break; } + PowerupAudio::getInstance()->onUseAudio(m_kart, m_type, audio_mini_state, sound_type); + if ( m_number <= 0 ) { m_number = 0; @@ -617,9 +463,14 @@ void Powerup::use() } } // use -void Powerup::useBubblegum(bool has_played_sound, bool mini) +//----------------------------------------------------------------------------- +/** This function handles the bubblegum logic, in backward use (dropping a ground + * gum) and forward use (creating a gum shield). + * Its return value indicates the sound to play: none (0), ground gum (1), shield (2) */ + +int Powerup::useBubblegum(bool mini) { - resetSoundSource(); + int sound_type = 0; ItemManager* im = Track::getCurrentTrack()->getItemManager(); const KartProperties *kp = m_kart->getKartProperties(); @@ -628,15 +479,9 @@ void Powerup::useBubblegum(bool has_played_sound, bool mini) { Item *new_item = im->dropNewItem(mini ? Item::ITEM_BUBBLEGUM_SMALL : Item::ITEM_BUBBLEGUM, m_kart); - - // E.g. ground not found in raycast. - if(!new_item) return; - if (!has_played_sound) - { - m_sound_use = SFXManager::get()->createSoundSource("goo"); - Powerup::adjustSound(); - m_sound_use->play(); - } + // There may not be a new item if e.g. ground is not found in raycast. + if(new_item) + sound_type = 1; } else // if the kart is looking forward, use the bubblegum as a shield { @@ -645,7 +490,7 @@ void Powerup::useBubblegum(bool has_played_sound, bool mini) float mini_factor = (mini) ? 0.5f : 1.0f; // If the kart has a normal gum shield, don't change the shield type - if (mini && !(m_kart->isGumShielded() && !m_kart->isWeakShielded())) + if (mini && (!m_kart->isGumShielded() || m_kart->isWeakShielded() )) { if (m_kart->getIdent() == "nolok") type = Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD_SMALL; @@ -682,29 +527,11 @@ void Powerup::useBubblegum(bool has_played_sound, bool mini) time2Ticks(kp->getBubblegumShieldDuration() * mini_factor + (m_kart->getShieldTime() / 2.0f)) ); } - - if (!has_played_sound) - { - //Extraordinary. Usually sounds are set in Powerup::set() - //In this case this is a workaround, since the bubblegum item has two different sounds. - m_sound_use = SFXManager::get()->createSoundSource("inflate"); - Powerup::adjustSound(); - m_sound_use->play(); - } + sound_type = 2; } + return sound_type; } // useBubblegum -//----------------------------------------------------------------------------- -/** This function ensure we don't leak sound sources */ -void Powerup::resetSoundSource() -{ - if (m_sound_use != NULL) - { - m_sound_use->deleteSFX(); - m_sound_use = NULL; - } -} // resetSoundSource - //----------------------------------------------------------------------------- /** This function is called when a bonus box is it. This function can be * called on a server (in which case item and add_info are not used), diff --git a/src/items/powerup.hpp b/src/items/powerup.hpp index 322d70f79a8..1de96d01601 100644 --- a/src/items/powerup.hpp +++ b/src/items/powerup.hpp @@ -24,8 +24,6 @@ #include "items/powerup_manager.hpp" // needed for powerup_type #include "utils/no_copy.hpp" -#include - class Kart; class BareNetworkString; class ItemState; @@ -37,9 +35,6 @@ class SFXBase; class Powerup : public NoCopy { private: - /** Sound effect that is being played. */ - SFXBase *m_sound_use; - /** The powerup type. */ PowerupManager::PowerupType m_type; @@ -52,21 +47,16 @@ class Powerup : public NoCopy /** The owner (kart) of this powerup. */ Kart* m_kart; - std::set m_played_sound_ticks; - /** Returns an integer in the 0-32767 range.*/ int simplePRNG(const int seed, const int time, const int item_id, const int position); - void useBubblegum(bool has_played_sound, bool mini = false); - void resetSoundSource(); + int useBubblegum(bool mini = false); public: Powerup (Kart* kart_); - ~Powerup (); void set (PowerupManager::PowerupType _type, int n = 1); void setNum (int n = 1); void reset (); Material* getIcon (bool wide=false) const; - void adjustSound (); void use (); void hitBonusBox (const ItemState &item); void saveState(BareNetworkString *buffer) const; @@ -74,18 +64,17 @@ class Powerup : public NoCopy void update(int ticks); /** Returns the number of powerups. */ - int getNum () const {return m_number;} + int getNum () const {return m_number;} // ------------------------------------------------------------------------ /** Returns the type of this powerup. */ - PowerupManager::PowerupType - getType () const {return m_type; } + PowerupManager::PowerupType getType () const {return m_type; } // ------------------------------------------------------------------------ PowerupManager::MiniState getMiniState () const { return m_mini_state; } // ------------------------------------------------------------------------ void setMiniState (PowerupManager::MiniState new_mini_state) { m_mini_state = new_mini_state; } // ------------------------------------------------------------------------ - bool hasWideIcon () const {return m_type == PowerupManager::POWERUP_MINI; } + bool hasWideIcon () const {return m_type == PowerupManager::POWERUP_MINI; } }; #endif diff --git a/src/items/powerup_audio.cpp b/src/items/powerup_audio.cpp index 45ca924551c..d8fd55cea4c 100644 --- a/src/items/powerup_audio.cpp +++ b/src/items/powerup_audio.cpp @@ -20,29 +20,265 @@ #include "audio/sfx_base.hpp" #include "audio/sfx_manager.hpp" +#include "items/powerup_manager.hpp" +#include "karts/controller/controller.hpp" +#include "karts/kart.hpp" +#include "modes/world.hpp" //----------------------------------------------------------------------------- PowerupAudio* PowerupAudio::getInstance() { static PowerupAudio instance; return &instance; -} +} // getInstance //----------------------------------------------------------------------------- /** Constructor */ PowerupAudio::PowerupAudio() { - printf("Constructor called\n"); m_sudo_good = SFXManager::get()->createSoundSource("sudo_good"); m_sudo_bad = SFXManager::get()->createSoundSource("sudo_bad"); -} + + m_powerup_sound = NULL; +} // PowerupAudio //----------------------------------------------------------------------------- /** Frees the memory for the sound effects. */ PowerupAudio::~PowerupAudio() { - printf("Destructor called\n"); m_sudo_good->deleteSFX(); m_sudo_bad->deleteSFX(); -} // ~Powerup + if(m_powerup_sound) m_powerup_sound->deleteSFX(); +} // ~PowerupAudio + +//----------------------------------------------------------------------------- +/** Sets the appropriate sound effect for the current powerup + */ +void PowerupAudio::setPowerupSound(Kart* kart, PowerupManager::PowerupType type) +{ + resetSoundSource(); + + switch (type) + { + // No sound effect when arming the glove + case PowerupManager::POWERUP_SWATTER: + // Special sound management + case PowerupManager::POWERUP_ZIPPER: + // Special sound effect management + case PowerupManager::POWERUP_SUDO: + // TODO : add sound effects + case PowerupManager::POWERUP_ELECTRO: + // Special sound effect management + case PowerupManager::POWERUP_MINI: + break ; + + case PowerupManager::POWERUP_BOWLING: + m_powerup_sound = SFXManager::get()->createSoundSource("bowling_shoot"); + break ; + + case PowerupManager::POWERUP_ANVIL: + m_powerup_sound = SFXManager::get()->createSoundSource("anvil"); + break; + + case PowerupManager::POWERUP_PARACHUTE: + m_powerup_sound = SFXManager::get()->createSoundSource("parachute"); + break; + + case PowerupManager::POWERUP_BUBBLEGUM: + // handled in the useBubblegum function + break ; + + case PowerupManager::POWERUP_SWITCH: + m_powerup_sound = SFXManager::get()->createSoundSource("swap"); + break; + + case PowerupManager::POWERUP_NOTHING: + case PowerupManager::POWERUP_CAKE: + case PowerupManager::POWERUP_PLUNGER: + default : + m_powerup_sound = SFXManager::get()->createSoundSource("shoot"); + break ; + } +} // setPowerupSound + + +//----------------------------------------------------------------------------- +/** Does the sound configuration. + */ +void PowerupAudio::adjustSound(Kart* kart) +{ + if (m_powerup_sound == NULL) + return; + + m_powerup_sound->setPosition(kart->getXYZ()); + // in multiplayer mode, sounds are NOT positional (because we have multiple listeners) + // so the sounds of all AIs are constantly heard. So reduce volume of sounds. + if (RaceManager::get()->getNumLocalPlayers() > 1) + { + // player karts played at full volume; AI karts much dimmer + + if (kart->getController()->isLocalPlayerController()) + { + m_powerup_sound->setVolume( 1.0f ); + } + else + { + m_powerup_sound->setVolume( + std::min(0.5f, 1.0f / RaceManager::get()->getNumberOfKarts()) ); + } + } +} // adjustSound + + +//----------------------------------------------------------------------------- +/** Apply the on-usage sound effects of powerups + */ +void PowerupAudio::onUseAudio(Kart* kart, PowerupManager::PowerupType type, PowerupManager::MiniState mini_state, int sound_type) +{ + const int ticks = World::getWorld()->getTicksSinceStart(); + bool has_played_sound = false; + auto it = m_played_sound_ticks.find(ticks); + if (it != m_played_sound_ticks.end()) + has_played_sound = true; + else + m_played_sound_ticks.insert(ticks); + + if (has_played_sound) + return; + + // Play custom kart sound when collectible is used //TODO: what about the bubble gum? + if (type != PowerupManager::POWERUP_NOTHING && + type != PowerupManager::POWERUP_SWATTER && + type != PowerupManager::POWERUP_ZIPPER) + kart->playCustomSFX(SFXManager::CUSTOM_SHOOT); + + // FIXME - for some collectibles, set() is never called + if (m_powerup_sound == NULL) + m_powerup_sound = SFXManager::get()->createSoundSource("shoot"); + + switch (type) + { + case PowerupManager::POWERUP_ZIPPER: + // TODO + break ; + case PowerupManager::POWERUP_SWITCH: + m_powerup_sound->play(); + break; + case PowerupManager::POWERUP_CAKE: // Fall-through + case PowerupManager::POWERUP_RUBBERBALL: // Fall-through + case PowerupManager::POWERUP_BOWLING: // Fall-through + case PowerupManager::POWERUP_PLUNGER: + adjustSound(kart); + m_powerup_sound->play(); + break; + case PowerupManager::POWERUP_SWATTER: + break; + case PowerupManager::POWERUP_SUDO: + // Play a good sound for the kart that benefits from the "nitro-hack", + // if it's a local player + if (kart->getController()->isLocalPlayerController()) + PowerupAudio::getInstance()->playSudoGoodSFX(); + // Play a bad sound if there is an affected local player + // FIXME if (player_kart != NULL) + // FIXME PowerupAudio::getInstance()->playSudoBadSFX(); + + break; + case PowerupManager::POWERUP_ELECTRO: + break; + case PowerupManager::POWERUP_MINI: + switch (mini_state) + { + case PowerupManager::NOT_MINI: + case PowerupManager::MINI_SELECT: + break; + case PowerupManager::MINI_CAKE: + resetSoundSource(); + m_powerup_sound = SFXManager::get()->createSoundSource("shoot"); + adjustSound(kart); + m_powerup_sound->play(); + break; + // Mini-zipper case + case PowerupManager::MINI_ZIPPER: + // TODO + break; + // Mini-gum case + case PowerupManager::MINI_GUM: + playGumSound(kart, sound_type); + break; + } // Switch mini-state + break; + case PowerupManager::POWERUP_BUBBLEGUM: + playGumSound(kart, sound_type); + break; + + case PowerupManager::POWERUP_ANVIL: + // Not worth the effort + break; + + case PowerupManager::POWERUP_PARACHUTE: + // should we position the sound at the kart that is hit, + // or the kart "throwing" the anvil? Ideally it should be both. + // Meanwhile, don't play it near AI karts since they obviously + // don't hear anything + if(kart->getController()->isLocalPlayerController()) + m_powerup_sound->setPosition(kart->getXYZ()); + // FIXME else if(player_kart) + //FIXME m_powerup_sound->setPosition(player_kart->getXYZ()); + m_powerup_sound->play(); + break; + + case PowerupManager::POWERUP_NOTHING: + if(!kart->getKartAnimation()) + kart->beep(); + break; + default : break; + } +} // onUseAudio + +//----------------------------------------------------------------------------- +void PowerupAudio::playGumSound(Kart* kart, int sound_type) +{ + if (sound_type >= 1) // A sound type of 0 indicates nothing to be played + { + resetSoundSource(); + //Extraordinary. Usually sounds are set in Powerup::set() + //In this case this is a workaround, since the bubblegum item has two different sounds. + if (sound_type == 1) // gum successfully dropped on the ground + m_powerup_sound = SFXManager::get()->createSoundSource("goo"); + else if (sound_type == 2) // gum shield inflating + m_powerup_sound = SFXManager::get()->createSoundSource("inflate"); + adjustSound(kart); + m_powerup_sound->play(); + } +} + +//----------------------------------------------------------------------------- +void PowerupAudio::update(Kart* kart, int ticks) +{ + /* + // Remove any sound ticks that should have played + const int remove_ticks = World::getWorld()->getTicksSinceStart() - 1000; + for (auto it = m_played_sound_ticks.begin(); + it != m_played_sound_ticks.end();) + { + if (*it < remove_ticks) + { + it = m_played_sound_ticks.erase(it); + continue; + } + break; + } + */ +} // update + +//----------------------------------------------------------------------------- +/** This function ensure we don't leak sound sources */ +void PowerupAudio::resetSoundSource() +{ + if (m_powerup_sound != NULL) + { + m_powerup_sound->deleteSFX(); + m_powerup_sound = NULL; + } +} // resetSoundSource \ No newline at end of file diff --git a/src/items/powerup_audio.hpp b/src/items/powerup_audio.hpp index 43e21becbd2..22261cb3b93 100644 --- a/src/items/powerup_audio.hpp +++ b/src/items/powerup_audio.hpp @@ -20,6 +20,11 @@ #define HEADER_POWERUPAUDIO_HPP #include "audio/sfx_base.hpp" +#include "items/powerup_manager.hpp" + +#include + +class Kart; class PowerupAudio : public NoCopy { @@ -31,12 +36,23 @@ class PowerupAudio : public NoCopy SFXBase *m_sudo_good; SFXBase *m_sudo_bad; + SFXBase *m_powerup_sound; + + std::set m_played_sound_ticks; // Must be per kart + PowerupAudio(); + void adjustSound(Kart* kart); + void playGumSound(Kart* kart, int sound_type); + void resetSoundSource(); public: static PowerupAudio* getInstance(); ~PowerupAudio(); + void setPowerupSound(Kart* kart, PowerupManager::PowerupType type); + void onUseAudio(Kart* kart, PowerupManager::PowerupType type, PowerupManager::MiniState mini_state, int sound_type); + void update(Kart* kart, int ticks); + void playSudoGoodSFX() { m_sudo_good->play(); } void playSudoBadSFX() { m_sudo_bad->play(); } }; From 3c38074c9b3fd7a13e1d345fc9f974ef73bec85a Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Wed, 29 May 2024 12:54:33 +0200 Subject: [PATCH 115/188] Only create a sound source when the powerup sound must be played This avoids removing the powerup sound mid-use when collecting a new powerup, fixes #5105 --- src/items/powerup.cpp | 5 --- src/items/powerup_audio.cpp | 68 ++++++++----------------------------- src/items/powerup_audio.hpp | 1 - 3 files changed, 15 insertions(+), 59 deletions(-) diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index e53b39ca73a..dbbfc024478 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -111,7 +111,6 @@ void Powerup::update(int ticks) //----------------------------------------------------------------------------- /** Sets the collected items. The number of items is increased if the same * item is currently collected, otherwise replaces the existing item. - * Also set the item-specific sound. * \param type Thew new type. * \param n Number of items of the given type. */ @@ -126,10 +125,6 @@ void Powerup::set(PowerupManager::PowerupType type, int n) m_type=type; m_number=n; - // Don't re-create sound during rewinding - if (!RewindManager::get()->isRewinding()) - PowerupAudio::getInstance()->setPowerupSound(m_kart, m_type); - if (m_type == PowerupManager::POWERUP_MINI) m_mini_state = PowerupManager::MINI_SELECT; } diff --git a/src/items/powerup_audio.cpp b/src/items/powerup_audio.cpp index d8fd55cea4c..a62871e01d0 100644 --- a/src/items/powerup_audio.cpp +++ b/src/items/powerup_audio.cpp @@ -52,57 +52,6 @@ PowerupAudio::~PowerupAudio() if(m_powerup_sound) m_powerup_sound->deleteSFX(); } // ~PowerupAudio -//----------------------------------------------------------------------------- -/** Sets the appropriate sound effect for the current powerup - */ -void PowerupAudio::setPowerupSound(Kart* kart, PowerupManager::PowerupType type) -{ - resetSoundSource(); - - switch (type) - { - // No sound effect when arming the glove - case PowerupManager::POWERUP_SWATTER: - // Special sound management - case PowerupManager::POWERUP_ZIPPER: - // Special sound effect management - case PowerupManager::POWERUP_SUDO: - // TODO : add sound effects - case PowerupManager::POWERUP_ELECTRO: - // Special sound effect management - case PowerupManager::POWERUP_MINI: - break ; - - case PowerupManager::POWERUP_BOWLING: - m_powerup_sound = SFXManager::get()->createSoundSource("bowling_shoot"); - break ; - - case PowerupManager::POWERUP_ANVIL: - m_powerup_sound = SFXManager::get()->createSoundSource("anvil"); - break; - - case PowerupManager::POWERUP_PARACHUTE: - m_powerup_sound = SFXManager::get()->createSoundSource("parachute"); - break; - - case PowerupManager::POWERUP_BUBBLEGUM: - // handled in the useBubblegum function - break ; - - case PowerupManager::POWERUP_SWITCH: - m_powerup_sound = SFXManager::get()->createSoundSource("swap"); - break; - - case PowerupManager::POWERUP_NOTHING: - case PowerupManager::POWERUP_CAKE: - case PowerupManager::POWERUP_PLUNGER: - default : - m_powerup_sound = SFXManager::get()->createSoundSource("shoot"); - break ; - } -} // setPowerupSound - - //----------------------------------------------------------------------------- /** Does the sound configuration. */ @@ -147,6 +96,8 @@ void PowerupAudio::onUseAudio(Kart* kart, PowerupManager::PowerupType type, Powe if (has_played_sound) return; + resetSoundSource(); + // Play custom kart sound when collectible is used //TODO: what about the bubble gum? if (type != PowerupManager::POWERUP_NOTHING && type != PowerupManager::POWERUP_SWATTER && @@ -161,14 +112,23 @@ void PowerupAudio::onUseAudio(Kart* kart, PowerupManager::PowerupType type, Powe { case PowerupManager::POWERUP_ZIPPER: // TODO - break ; + break; case PowerupManager::POWERUP_SWITCH: + m_powerup_sound = SFXManager::get()->createSoundSource("swap"); m_powerup_sound->play(); break; case PowerupManager::POWERUP_CAKE: // Fall-through + case PowerupManager::POWERUP_PLUNGER: + m_powerup_sound = SFXManager::get()->createSoundSource("shoot"); + adjustSound(kart); + m_powerup_sound->play(); + break; case PowerupManager::POWERUP_RUBBERBALL: // Fall-through + adjustSound(kart); + m_powerup_sound->play(); + break; case PowerupManager::POWERUP_BOWLING: // Fall-through - case PowerupManager::POWERUP_PLUNGER: + m_powerup_sound = SFXManager::get()->createSoundSource("bowling_shoot"); adjustSound(kart); m_powerup_sound->play(); break; @@ -213,10 +173,12 @@ void PowerupAudio::onUseAudio(Kart* kart, PowerupManager::PowerupType type, Powe break; case PowerupManager::POWERUP_ANVIL: + m_powerup_sound = SFXManager::get()->createSoundSource("anvil"); // Not worth the effort break; case PowerupManager::POWERUP_PARACHUTE: + m_powerup_sound = SFXManager::get()->createSoundSource("parachute"); // should we position the sound at the kart that is hit, // or the kart "throwing" the anvil? Ideally it should be both. // Meanwhile, don't play it near AI karts since they obviously diff --git a/src/items/powerup_audio.hpp b/src/items/powerup_audio.hpp index 22261cb3b93..69c335854ec 100644 --- a/src/items/powerup_audio.hpp +++ b/src/items/powerup_audio.hpp @@ -49,7 +49,6 @@ class PowerupAudio : public NoCopy ~PowerupAudio(); - void setPowerupSound(Kart* kart, PowerupManager::PowerupType type); void onUseAudio(Kart* kart, PowerupManager::PowerupType type, PowerupManager::MiniState mini_state, int sound_type); void update(Kart* kart, int ticks); From b1ee2d77b3d587d5db0c4a77c2d51d88913fc091 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Wed, 29 May 2024 14:10:07 +0200 Subject: [PATCH 116/188] Increase the max number of bones to 2048 This will allow karts with more complex animations and more karts to be used before triggering the limit. The Godette kart, with 53 bones, already exceeds the current limit of 51 bones per kart required to not cause issues with 20 karts of equal bone number. Some very old systems may not properly handle more than 1024 bones. --- data/stk_config.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/data/stk_config.xml b/data/stk_config.xml index 15bc006e7a2..aef1f4865b2 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -514,9 +514,10 @@ - + TBO in desktop at least 65536 (max buffer size) / 64, SSBO at least 2^24 / 64. + 1024 is guaranteed to work everywhere, but can be limiting, in kart creation and + in the max number of karts. Most systems should support at least 2048. --> + - + - - + diff --git a/data/gfx/skid2.xml b/data/gfx/skid2.xml index d82d46c4479..d650ac68b84 100644 --- a/data/gfx/skid2.xml +++ b/data/gfx/skid2.xml @@ -12,16 +12,16 @@ - + + max="80" /> - + diff --git a/data/gfx/skid3.xml b/data/gfx/skid3.xml index 324c2acb2e2..dbb3f5419e0 100644 --- a/data/gfx/skid3.xml +++ b/data/gfx/skid3.xml @@ -12,16 +12,16 @@ - + - + - + From 4c3f22c2d08afa6a8214ec3006199bbe80e07734 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Thu, 15 May 2025 15:16:51 +0200 Subject: [PATCH 121/188] Mix particles to warn that the next level skid is almost ready - Add two emitters that show the skid particles of the upcoming skid level (what the level would be if the skid is held for a certain delay) - Use the min/max properties of the skid particles, which were unused, to determine the mixing ratio between the main emitters and the warning emitters This visual informations reduces the need for rote memorization to be able to get tight skid-release timings, especially for red and purple skids. Remaining : support the display of particle mixing for replays --- data/gfx/skid0.xml | 8 +++---- data/gfx/skid1.xml | 8 +++---- data/gfx/skid2.xml | 8 +++---- data/gfx/skid3.xml | 8 +++---- src/karts/kart_gfx.cpp | 52 ++++++++++++++++++++++++++---------------- src/karts/kart_gfx.hpp | 8 ++++--- src/karts/skidding.cpp | 45 +++++++++++++++++++----------------- src/karts/skidding.hpp | 3 ++- 8 files changed, 79 insertions(+), 61 deletions(-) diff --git a/data/gfx/skid0.xml b/data/gfx/skid0.xml index 14d060b50ef..6091b0da64d 100644 --- a/data/gfx/skid0.xml +++ b/data/gfx/skid0.xml @@ -10,10 +10,10 @@ - + is used by the second emitter to warn that the bonus approaches, + the maximum is used by the main emitter. --> + - + is used by the second emitter to warn that the bonus approaches, + the maximum is used by the main emitter. --> + - + is used by the second emitter to warn that the bonus approaches, + the maximum is used by the main emitter. --> + - + is used by the second emitter to warn that the bonus approaches, + the maximum is used by the main emitter. --> + getGfxFile(file_name)); - // Skid0, Skid2 and Skid3 are only used to store the emitter type, + // Skid0 and Skid3 are only used to store the emitter type, // and a wheeless kart has no terrain effects. - if(type==KGFX_SKID0L || type==KGFX_SKID0R || type==KGFX_SKID2L || type==KGFX_SKID2R || + if(type==KGFX_SKID0L || type==KGFX_SKID0R || type==KGFX_SKID3L || type==KGFX_SKID3R || (type==KGFX_TERRAIN && m_kart->isWheeless()) ) emitter = NULL; @@ -281,43 +281,55 @@ void KartGFX::reset() // ---------------------------------------------------------------------------- /** Selects the correct skidding particle type depending on skid bonus level. * \param level Must be 0 (no bonus, showing tiny sparks), 1 (accumulated enough + * for level 1 bonus), 2 (accumulated enough for level 2 bonus) or 3. + * \param upcoming_level Must be 0 (no bonus, showing tiny sparks), 1 (accumulated enough * for level 1 bonus), 2 (accumulated enough for level 2 bonus) or 3. */ -void KartGFX::setSkidLevel(const unsigned int level) +void KartGFX::setSkidLevel(const unsigned int level, const unsigned int upcoming_level) { assert(level >= 0); assert(level <= 3); m_skid_level = level; - const ParticleKind *pk; + const ParticleKind *pk, *pk2; if (level == 0) - { pk = m_skid_kind0; - } else if (level == 1) - { pk = m_skid_kind1; - } else if (level == 2) - { pk = m_skid_kind2; - } else - { pk = m_skid_kind3; - } + + if (upcoming_level == 0) + pk2 = m_skid_kind0; + else if (upcoming_level == 1) + pk2 = m_skid_kind1; + else if (upcoming_level == 2) + pk2 = m_skid_kind2; + else + pk2 = m_skid_kind3; + + // const ParticleKind *pk = level==1 ? m_skid_kind1 : m_skid_kind2; #ifndef SERVER_ONLY if (GUIEngine::isNoGraphics()) return; - if(m_all_emitters[KGFX_SKID1L]) - m_all_emitters[KGFX_SKID1L]->setParticleType(pk); - if(m_all_emitters[KGFX_SKID1R]) - m_all_emitters[KGFX_SKID1R]->setParticleType(pk); - // Relative 0 means it will emitt the minimum rate, i.e. the rate - // set to indicate that the bonus is now available. - setCreationRateRelative(KartGFX::KGFX_SKIDL, 0.0f); - setCreationRateRelative(KartGFX::KGFX_SKIDR, 0.0f); + if(m_all_emitters[KGFX_SKIDL]) + m_all_emitters[KGFX_SKIDL]->setParticleType(pk); + if(m_all_emitters[KGFX_SKIDR]) + m_all_emitters[KGFX_SKIDR]->setParticleType(pk); + if(m_all_emitters[KGFX_SKIDL2]) + m_all_emitters[KGFX_SKIDL2]->setParticleType(pk2); + if(m_all_emitters[KGFX_SKIDR2]) + m_all_emitters[KGFX_SKIDR2]->setParticleType(pk2); + // Relative 1 means it will emit the maximum rate, + // relative 0 means it will emit at the minimum rate. + // This is used to determine the mixing ratios when the levels are different. + setCreationRateRelative(KartGFX::KGFX_SKIDL, 1.0f); + setCreationRateRelative(KartGFX::KGFX_SKIDR, 1.0f); + setCreationRateRelative(KartGFX::KGFX_SKIDL2, 0.0f); + setCreationRateRelative(KartGFX::KGFX_SKIDR2, 0.0f); #endif } // setSkidLevel diff --git a/src/karts/kart_gfx.hpp b/src/karts/kart_gfx.hpp index be5d86bb38c..b18f848a977 100644 --- a/src/karts/kart_gfx.hpp +++ b/src/karts/kart_gfx.hpp @@ -59,10 +59,12 @@ class KartGFX KGFX_TERRAIN, KGFX_SKIDL, KGFX_SKIDR, + KGFX_SKIDL2, + KGFX_SKIDR2, KGFX_SKID1L = KGFX_SKIDL, KGFX_SKID1R = KGFX_SKIDR, - KGFX_SKID2L, - KGFX_SKID2R, + KGFX_SKID2L = KGFX_SKIDL2, + KGFX_SKID2R = KGFX_SKIDR2, KGFX_SKID3L, KGFX_SKID3R, KGFX_SKID0L, @@ -119,7 +121,7 @@ class KartGFX KartGFX(const Kart *kart, bool is_day); ~KartGFX(); void reset(); - void setSkidLevel(const unsigned int level); + void setSkidLevel(const unsigned int level, const unsigned int upcoming_level); void setParticleKind(const KartGFXType type, const ParticleKind *pk); void setXYZ(const KartGFXType type, const Vec3 &xyz); void setCreationRateAbsolute(const KartGFXType type, float f); diff --git a/src/karts/skidding.cpp b/src/karts/skidding.cpp index 031cb802d6a..d92e6f5da5e 100644 --- a/src/karts/skidding.cpp +++ b/src/karts/skidding.cpp @@ -73,8 +73,7 @@ void Skidding::reset() m_visual_rotation = 0.0f; m_skid_bonus_ready = false; m_remaining_jump_time = 0.0f; - m_kart->getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDL, 0); - m_kart->getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDR, 0); + resetParticles(); m_kart->getKartGFX()->updateSkidLight(0); m_kart->getControls().setSkidControl(KartControl::SC_NONE); m_prev_visual_rotation = 0.0f; @@ -88,6 +87,16 @@ void Skidding::reset() m_kart->getVehicle()->setTimedRotation(0, 0); } // reset +// ---------------------------------------------------------------------------- +/** Stops the emission of skidding-particles */ +void Skidding::resetParticles() +{ + m_kart->getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDL, 0); + m_kart->getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDR, 0); + m_kart->getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDL2, 0); + m_kart->getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDR2, 0); +} // resetParticles + // ---------------------------------------------------------------------------- /** Save the skidding state of a kart. It only saves the important physics * values including m_remaining_jump_time (while this is mostly a graphical @@ -244,43 +253,36 @@ float Skidding::getSteeringWhenSkidding(float steering) const */ float Skidding::updateGraphics(float dt) { - m_kart->getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDL, 0); - m_kart->getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDR, 0); + resetParticles(); m_kart->getKartGFX()->updateSkidLight(0); unsigned int level = getSkidLevel(m_kart->getKartProperties()); + unsigned int upcoming_level = getSkidLevel(m_kart->getKartProperties(), 0.18f /* delay */); // Get the type of the last triggered skid level, // if in the period after bonus trigger where particles are still shown if (m_skid_bonus_end_ticks > World::getWorld()->getTicksSinceStart()) + { level = m_kart->m_max_speed->getLatestSkidLevel(); + upcoming_level = level; + } // If at least level 1 bonus is reached, show appropriate gfx if (level >= 1) { - m_kart->getKartGFX()->setSkidLevel(level); + m_kart->getKartGFX()->setSkidLevel(level, upcoming_level); m_kart->getKartGFX()->updateSkidLight(level); } // Show tiny sparks if bonus not yet reached else if ( m_graphical_remaining_jump_time <= 0.0f && m_skid_state != SKID_NONE) { - m_kart->getKartGFX()->setSkidLevel(level); + m_kart->getKartGFX()->setSkidLevel(level, upcoming_level); } - if (level >= 1) - { - m_kart->getKartGFX()->setCreationRateRelative(KartGFX::KGFX_SKIDL, - 1.0f); - m_kart->getKartGFX()->setCreationRateRelative(KartGFX::KGFX_SKIDR, - 1.0f); - } - else if (m_skid_state == SKID_BREAK || m_skid_state == SKID_SHOW_GFX_LEFT - || m_skid_state == SKID_SHOW_GFX_RIGHT) + if (level == 0 && (m_skid_state == SKID_BREAK || m_skid_state == SKID_SHOW_GFX_LEFT + || m_skid_state == SKID_SHOW_GFX_RIGHT)) { - m_kart->getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDL, - 0.0f); - m_kart->getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDR, - 0.0f); + resetParticles(); } if (m_smoothing_dt >= 0.0f) @@ -581,12 +583,13 @@ unsigned int Skidding::getSkidBonus(float *bonus_time, /** Determines the level of the currently accumulating skid. * \return The bonus level: 0 = no bonus, 1 = first entry in bonus array etc. */ -unsigned int Skidding::getSkidLevel(const KartProperties *kp) const +unsigned int Skidding::getSkidLevel(const KartProperties *kp, float ready_delay) const { unsigned int level = 0; for (level = 0; level < kp->getSkidBonusSpeed().size(); level++) { - if (stk_config->ticks2Time(m_skid_time) <= + int test_ticks = m_skid_time + stk_config->time2Ticks(ready_delay); + if (stk_config->ticks2Time(test_ticks) <= kp->getSkidTimeTillBonus()[level]) break; } diff --git a/src/karts/skidding.hpp b/src/karts/skidding.hpp index 225063f716f..fe95c9750d8 100644 --- a/src/karts/skidding.hpp +++ b/src/karts/skidding.hpp @@ -102,9 +102,10 @@ friend class KartRewinder; ShowCurve *m_actual_curve; #endif + void resetParticles(); unsigned int getSkidBonus(float *bonus_time, float *fade_out_time, float *bonus_speed, float *bonus_force) const; - unsigned int getSkidLevel(const KartProperties *kp) const; + unsigned int getSkidLevel(const KartProperties *kp, float ready_delay = 0.0f) const; float updateSteering(float steer, int ticks); public: Skidding(Kart *kart); From 50f88690bb3e75073643de8a939cb408e6b6b0b6 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Thu, 19 Jun 2025 21:14:07 +0200 Subject: [PATCH 122/188] Put karts upright quicker when only some wheels touch the ground (fix #5460) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just applying a naïve corrective torque when some wheels are off the ground would lead to many unwelcome side effects, so a more accurate approach is needed. The code's comments explain the procedure in more details, but in general this commit: - Moves the logic applying the correcting torque to a dedicated function (this allows an early return if conditions are not met, and there is a lot more code there than before) - Adds logic to evaluate if correcting the kart would help or not. To do this, we compare (1) the flying wheels height off the ground, (2) their simulated height off the ground with an upright kart and the wheels touching ground staying in the same position. A correcting torque is only applied if it would bring the flying wheels noticeably closer to the ground. - Creates a new uprightRayCast function that raycasts in the direction gravity pulls the kart towards. It is important to make consistent and comparable raycasts to measure height with. Unlike the others in btKart, this raycast function also avoids changing state. As a result, the duration the kart spends with only two wheels on the ground is reduced by over 70% in the test track Sven provided. There is therefore still a small period with the kart uncontrollable (making the kart rotate too fast would look strange), but the player can quickly recover control and avoid a crash or having to rescue. Because the torque is restricted to the roll axis, this commit only addresses the case of wheels on one side (left or right) being on the ground while wheels on the other sides are not. The detection logic works correctly when the pairings are front/back instead, there is simply no torque on the pitch axis. Because the conditions for this situation are very specific and don't arise in normal gameplay, I have left this as-is. --- src/physics/btKart.cpp | 184 +++++++++++++++++++++++++++++++++++------ src/physics/btKart.hpp | 2 + 2 files changed, 161 insertions(+), 25 deletions(-) diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp index 2eac114f673..f09c689d32b 100644 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -222,7 +222,7 @@ void btKart::updateAllWheelTransformsWS() m_visual_wheels_touch_ground = true; for (int i=0;igetGravity(); - terrain_up = terrain_up.normalize(); - // Length of axis depends on the angle - i.e. the further awat - // the kart is from being upright, the larger the applied impulse - // will be, resulting in fast changes when the kart is on its - // side, but not overcompensating (and therefore shaking) when - // the kart is not much away from being upright. - btVector3 axis = kart_up.cross(terrain_up); - - // To avoid the kart going backwards/forwards (or rolling sideways), - // set the pitch/roll to 0 before applying the 'straightening' impulse. - // TODO: make this works if gravity is changed. - btVector3 av = m_chassisBody->getAngularVelocity(); - av.setX(0); - av.setZ(0); - m_chassisBody->setAngularVelocity(av); - // Give a nicely balanced feeling for rebalancing the kart - float smoothing = m_kart->getKartProperties() - ->getStabilitySmoothFlyingImpulse(); - m_chassisBody->applyTorqueImpulse(axis * smoothing); - } + if(m_num_wheels_on_ground <= 2) + pushVehicleUpright(); // Apply suspension forcen (i.e. upwards force) // -------------------------------------------- @@ -556,6 +535,161 @@ void btKart::updateVehicle( btScalar step ) adjustSpeed(m_min_speed, m_max_speed); } // updateVehicle +// ---------------------------------------------------------------------------- +void btKart::pushVehicleUpright() +{ + btVector3 kart_up = getChassisWorldTransform().getBasis().getColumn(1); + btVector3 terrain_up = -m_chassisBody->getGravity(); + terrain_up = terrain_up.normalize(); + + // If there is at least a wheel on the ground, check if the parallel impulse + // would bring the flying wheels closer to the ground or farther away, + if (m_num_wheels_on_ground >= 1) + { + // To limit raycasts starting below the ground on slopes, + // start raycasts with this much extraHeight. + btVector3 extraHeight = 0.25f * terrain_up; + float current_depth = 0.0f; + float simulated_depth = 0.0f; + const btTransform& chassis_trans = getChassisWorldTransform(); + for(int i=0; i < m_wheelInfo.size(); i++) + { + // If the wheel is flying, we check how far it is from the ground + // (looking down in the direction gravity pulls the kart towards) + // If the wheel is on the ground, we check how far the flying wheels + // would be from the ground if the wheel on the ground's position was + // unchanged and the kart was properly oriented with regards to gravity. + // + // In both cases, we take the minimum depth from two raycasts, + // in order to limit sensitivity to tiny cracks in the terrain + + if (m_wheelInfo[i].m_was_on_ground) + { + float local_sim_depth = 0.0f; + for(int j=0; j < m_wheelInfo.size(); j++) + { + if (!m_wheelInfo[j].m_was_on_ground) + { + // == Simulate new wheel positions == + // 1 - We get the relative position of the target wheel + // compared to the reference wheel. + // Because we do later two slightly different raycasts, + // we also compute two slightly different offsets. + btVector3 wheelOffset = m_wheelInfo[j].m_chassisConnectionPointCS + - m_wheelInfo[i].m_chassisConnectionPointCS; + btVector3 wheelOffset2 = m_wheelInfo[j].m_chassisConnectionPointCS + - (m_wheelInfo[i].m_chassisConnectionPointCS * 0.95f); + + btScalar vector_len = wheelOffset.length(); + btScalar vector_len2 = wheelOffset2.length(); + + // 2 - We apply the kart rotation, to point in the right direction + // from the reference wheel. + wheelOffset = chassis_trans.getBasis() * wheelOffset; + wheelOffset2 = chassis_trans.getBasis() * wheelOffset2; + + // 3 - Now, we project this offset on the plane perpendicular to + // the gravity direction. It gives us the direction towards + // which the wheel would be if the kart was upright. + // For the computations, we make use of terrain_up, which is the + // normalized (i. e. length 1) vector that is normal (i. e. perpendicular) + // to the plane. + wheelOffset = wheelOffset - ((wheelOffset * terrain_up) * terrain_up); + wheelOffset2 = wheelOffset2 - ((wheelOffset2 * terrain_up) * terrain_up); + + // 4 - Finally, to properly simulate the new wheel position, we also + // need to correct back the length of the offsets + // (as projection will have reduced the vector's length) + wheelOffset = wheelOffset * vector_len / wheelOffset.length(); + wheelOffset2 = wheelOffset2 * vector_len2 / wheelOffset2.length(); + + const btVector3& raycastStart = chassis_trans(m_wheelInfo[i].m_chassisConnectionPointCS) + wheelOffset + extraHeight; + //Log::info("BtKart","Raycast started at %f x, %f y, %f z", raycastStart.getX(), raycastStart.getY(), raycastStart.getZ()); + float min_depth = uprightRayCast(raycastStart); + const btVector3& raycastStart2 = chassis_trans(m_wheelInfo[i].m_chassisConnectionPointCS) + wheelOffset2 + extraHeight; + //Log::info("BtKart","Depth of Raycast is %f, depth of Raycast2 is %f", min_depth, uprightRayCast(raycastStart2)); + min_depth = std::min(min_depth, uprightRayCast(raycastStart2)); + local_sim_depth += min_depth; + } + } + simulated_depth = std::max(simulated_depth, local_sim_depth); + } // if (m_wheelInfo[i].m_was_on_ground) + else // wheel flying + { + const btVector3& raycastStart = chassis_trans(m_wheelInfo[i].m_chassisConnectionPointCS * 1.0f) + extraHeight; + const btVector3& raycastStart2 = chassis_trans(m_wheelInfo[i].m_chassisConnectionPointCS * 0.95f) + extraHeight; + min_depth = std::min(uprightRayCast(raycastStart), uprightRayCast(raycastStart2)); + current_depth += min_depth; + } + } + /*Log::info("BtKart","Some wheels are in the air and some are on the ground, " + "simulated flying wheel depth is %f, current depth is %f", + simulated_depth, current_depth);*/ + + // If the torque would bring the flying wheels farther from the ground, + // or if it wouldn't change much, don't do anything. + if (simulated_depth + 0.1f > current_depth) + return; + } + + // The length of axis depends on the angle - i.e. the further away + // the kart is from being upright, the larger the applied impulse + // will be, resulting in fast changes when the kart is on its + // side, but not overcompensating (and therefore shaking) when + // the kart is not much away from being upright. + btVector3 axis = kart_up.cross(terrain_up); + + // To avoid the kart going backwards/forwards (or rolling sideways), + // set the pitch/roll to 0 before applying the 'straightening' impulse. + // TODO: make this works if gravity is changed. + //FIXME : I think the comment above is wrong. From the kart's perspective, + // the torque only applies roll, but no yaw and no pitch. + btVector3 av = m_chassisBody->getAngularVelocity(); + av.setX(0); + av.setZ(0); + m_chassisBody->setAngularVelocity(av); + // Give a nicely balanced feeling for rebalancing the kart + float smoothing = m_kart->getKartProperties()->getStabilitySmoothFlyingImpulse(); + m_chassisBody->applyTorqueImpulse(axis * smoothing); +} // pushVehicleUpright + +// ---------------------------------------------------------------------------- +float btKart::uprightRayCast(const btVector3& raycastStart) +{ + // Work around a bullet problem: when using a convex hull the raycast + // would sometimes hit the chassis (which does not happen when using a + // box shape). Therefore set the collision mask in the chassis body so + // that it is not hit anymore. + short int old_group=0; + if(m_chassisBody->getBroadphaseHandle()) + { + old_group = m_chassisBody->getBroadphaseHandle()->m_collisionFilterGroup; + m_chassisBody->getBroadphaseHandle()->m_collisionFilterGroup = 0; + } + + btVector3 rayDirection = m_chassisBody->getGravity(); + rayDirection = rayDirection.normalize(); + btScalar raylen = 100.0f; + btVector3 rayvector = rayDirection * raylen; + const btVector3& target = raycastStart + rayvector; + btVehicleRaycaster::btVehicleRaycasterResult rayResults; + + btAssert(m_vehicleRaycaster); + + void* object = m_vehicleRaycaster->castRay(raycastStart, target, rayResults); + btScalar depth = raylen * rayResults.m_distFraction; + + if(m_chassisBody->getBroadphaseHandle()) + m_chassisBody->getBroadphaseHandle()->m_collisionFilterGroup = old_group; + + // If the raycast fails to make contact, depth is negative, + // which it shouldn't be: we correct it back to be positve. + if (depth < 0.0f) + depth = -depth; + + return (float)depth; +} // uprightRayCast + // ---------------------------------------------------------------------------- float btKart::getCollisionLean() const { diff --git a/src/physics/btKart.hpp b/src/physics/btKart.hpp index c07b9041755..b7884bfdeb3 100644 --- a/src/physics/btKart.hpp +++ b/src/physics/btKart.hpp @@ -141,6 +141,8 @@ class btKart : public btActionInterface btTransform chassis_trans, bool interpolatedTransform=true, float fraction = 1.0f); + void pushVehicleUpright(); + float uprightRayCast(const btVector3& raycastStart); public: From 6fcd24156f12dcc62870d23a7fc05193548a5b41 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Thu, 19 Jun 2025 22:05:21 +0200 Subject: [PATCH 123/188] Minor cleanup in btKart - Fix a trivial compilation error in the previous commit that came from the incomplete removal of an unnecessary intermediate variable - Remove some code behind define guards that was unused since 0.7.3 - Fix a few warnings --- .../src/BulletDynamics/Vehicle/btWheelInfo.h | 3 ++- src/physics/btKart.cpp | 24 +++---------------- 2 files changed, 5 insertions(+), 22 deletions(-) diff --git a/lib/bullet/src/BulletDynamics/Vehicle/btWheelInfo.h b/lib/bullet/src/BulletDynamics/Vehicle/btWheelInfo.h index b04a0ac972a..5c23bd9b090 100644 --- a/lib/bullet/src/BulletDynamics/Vehicle/btWheelInfo.h +++ b/lib/bullet/src/BulletDynamics/Vehicle/btWheelInfo.h @@ -31,7 +31,7 @@ struct btWheelInfoConstructionInfo btScalar m_frictionSlip; btScalar m_maxSuspensionForce; bool m_bIsFrontWheel; - + btTransform m_worldTransform; }; /// btWheelInfo contains information per wheel about friction and suspension. @@ -102,6 +102,7 @@ struct btWheelInfo m_bIsFrontWheel = ci.m_bIsFrontWheel; m_maxSuspensionForce = ci.m_maxSuspensionForce; m_was_on_ground = true; + m_worldTransform = ci.m_worldTransform; } void updateWheel(const btRigidBody& chassis,RaycastInfo& raycastInfo); diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp index f09c689d32b..9762cbffaca 100644 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -81,6 +81,7 @@ btWheelInfo& btKart::addWheel(const btVector3& connectionPointCS, ci.m_frictionSlip = tuning.m_frictionSlip; ci.m_maxSuspensionTravel = tuning.m_maxSuspensionTravel; ci.m_maxSuspensionForce = tuning.m_maxSuspensionForce; + ci.m_worldTransform = getChassisWorldTransform(); m_wheelInfo.push_back( btWheelInfo(ci)); @@ -618,8 +619,7 @@ void btKart::pushVehicleUpright() { const btVector3& raycastStart = chassis_trans(m_wheelInfo[i].m_chassisConnectionPointCS * 1.0f) + extraHeight; const btVector3& raycastStart2 = chassis_trans(m_wheelInfo[i].m_chassisConnectionPointCS * 0.95f) + extraHeight; - min_depth = std::min(uprightRayCast(raycastStart), uprightRayCast(raycastStart2)); - current_depth += min_depth; + current_depth += std::min(uprightRayCast(raycastStart), uprightRayCast(raycastStart2)); } } /*Log::info("BtKart","Some wheels are in the air and some are on the ground, " @@ -676,7 +676,7 @@ float btKart::uprightRayCast(const btVector3& raycastStart) btAssert(m_vehicleRaycaster); - void* object = m_vehicleRaycaster->castRay(raycastStart, target, rayResults); + m_vehicleRaycaster->castRay(raycastStart, target, rayResults); btScalar depth = raylen * rayResults.m_distFraction; if(m_chassisBody->getBroadphaseHandle()) @@ -1000,13 +1000,8 @@ void btKart::updateFriction(btScalar timeStep) { m_chassisBody->applyImpulse( m_forwardWS[wheel]*(m_forwardImpulse[wheel]), -#define COMPATIBLE_0_7_3 -#ifdef COMPATIBLE_0_7_3 // This was apparently done to help hexley btVector3(0,0,0)); -#else - rel_pos); -#endif } if (m_sideImpulse[wheel] != btScalar(0.)) { @@ -1025,26 +1020,13 @@ void btKart::updateFriction(btScalar timeStep) } btVector3 sideImp = m_axle[wheel] * m_sideImpulse[wheel]; - -#if defined ROLLING_INFLUENCE_FIX && !defined COMPATIBLE_0_7_3 - // fix. It only worked if car's up was along Y - VT. - btVector3 vChassisWorldUp = - getRigidBody()->getCenterOfMassTransform() - .getBasis().getColumn(m_indexUpAxis); - rel_pos -= vChassisWorldUp * (vChassisWorldUp.dot(rel_pos) * - (1.f-wheelInfo.m_rollInfluence) ); -#else rel_pos[m_indexUpAxis] *= wheelInfo.m_rollInfluence; -#endif m_chassisBody->applyImpulse(sideImp,rel_pos); //apply friction impulse on the ground groundObject->applyImpulse(-sideImp,rel_pos2); } // if (m_sideImpulse[wheel] != btScalar(0.)) } // for wheel Date: Fri, 4 Jul 2025 17:08:01 +0200 Subject: [PATCH 124/188] Essentially prevent validating laps driving backwards - Add additional logic to monitor if a kart crosses the previous checkline again, and if so, to undo the triggering of the more advanced checkline. Endlessly driving backwards can never validate a new lap, provided the track has at least two checkline groups. - Take care to properly handle the special behavior of the lapline. As a result, it is technically possible to validate a lap while avoiding to drive between the lapline and the first checkline, or between the first checkline and the second checkline. - Reset the backward driving monitoring status of checkline when a new lap is validated. The lap counter won't be decremented anyways, and so this is important to safeguard againt exploits. Network synchronization should also work, but further validation is needed. --- src/tracks/check_cannon.cpp | 6 +- src/tracks/check_cannon.hpp | 2 +- src/tracks/check_lap.cpp | 13 ++-- src/tracks/check_line.cpp | 7 +- src/tracks/check_line.hpp | 2 +- src/tracks/check_manager.cpp | 6 ++ src/tracks/check_structure.cpp | 126 +++++++++++++++++++++++++-------- src/tracks/check_structure.hpp | 23 ++++-- 8 files changed, 140 insertions(+), 45 deletions(-) diff --git a/src/tracks/check_cannon.cpp b/src/tracks/check_cannon.cpp index 5b967812eca..15b5351a853 100644 --- a/src/tracks/check_cannon.cpp +++ b/src/tracks/check_cannon.cpp @@ -108,13 +108,13 @@ CheckCannon::~CheckCannon() // ---------------------------------------------------------------------------- /** Changes the colour of a check cannon depending on state. */ -void CheckCannon::changeDebugColor(bool is_active) +void CheckCannon::changeDebugColor(bool is_active, bool prevent_backwards) { #if defined(DEBUG) && !defined(SERVER_ONLY) CheckLine::changeDebugColor(is_active); - video::SColor color = is_active ? video::SColor(192, 255, 0, 0) - : video::SColor(192, 128, 128, 128); + video::SColor color = is_active ? video::SColor(192, 255, 0, 0) + : video::SColor(192, 128, 128, 128); for (unsigned int i = 0; i < 4; i++) { m_debug_target_dy_dc->getVerticesVector()[i].m_color = color; diff --git a/src/tracks/check_cannon.hpp b/src/tracks/check_cannon.hpp index ec441067e13..12ef38c38e1 100644 --- a/src/tracks/check_cannon.hpp +++ b/src/tracks/check_cannon.hpp @@ -69,7 +69,7 @@ class CheckCannon : public CheckLine // ------------------------------------------------------------------------ virtual void trigger(unsigned int kart_index) OVERRIDE {} // ------------------------------------------------------------------------ - virtual void changeDebugColor(bool is_active) OVERRIDE; + virtual void changeDebugColor(bool is_active, bool prevent_backwards = false) OVERRIDE; // ------------------------------------------------------------------------ virtual void update(float dt) OVERRIDE; // ------------------------------------------------------------------------ diff --git a/src/tracks/check_lap.cpp b/src/tracks/check_lap.cpp index fb54184026e..c4ac4503b5b 100644 --- a/src/tracks/check_lap.cpp +++ b/src/tracks/check_lap.cpp @@ -51,10 +51,9 @@ void CheckLap::reset(const Track &track) } // reset // ---------------------------------------------------------------------------- -/** True if going from old_pos to new_pos crosses this checkline. This function - * is called from update (of the checkline structure). - * \param old_pos Position in previous frame. - * \param new_pos Position in current frame. +/** This functions ignores the old_pos and new_pos parameters, they are just + * there for function call compatibility in check structures, with other + * structures making use of the parameters. * \param kart_index Index of the kart, can be used to store kart specific * additional data. */ @@ -70,6 +69,12 @@ bool CheckLap::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, if(!lin_world) return false; + // Before physics start to run, some values are not properly set. + // A spurious true here doesn't increment the lap counter, but + // it makes testing and validation more difficult. + if (w->getTimeTicks() == 0) + return false; + // the lapline is considered crossed if the kart is on the road (off-road is not accepted) // near the beginning of the track while the lapline is active (meaning the proper // checklines were activated). "Near the beginning of the track" is arbitrarily diff --git a/src/tracks/check_line.cpp b/src/tracks/check_line.cpp index 6877ec2939d..5230393e433 100644 --- a/src/tracks/check_line.cpp +++ b/src/tracks/check_line.cpp @@ -175,12 +175,13 @@ void CheckLine::resetAfterKartMove(unsigned int kart_index) } // resetAfterKartMove // ---------------------------------------------------------------------------- -void CheckLine::changeDebugColor(bool is_active) +void CheckLine::changeDebugColor(bool is_active, bool prevents_backwards) { if (!m_debug_dy_dc) return; - video::SColor color = is_active ? video::SColor(192, 255, 0, 0) - : video::SColor(192, 128, 128, 128); + video::SColor color = is_active ? video::SColor(192, 255, 0, 0) : + prevents_backwards ? video::SColor(192, 0, 128, 255) + : video::SColor(192, 128, 128, 128); for(unsigned int i = 0; i < 4; i++) { m_debug_dy_dc->getVerticesVector()[i].m_color = color; diff --git a/src/tracks/check_line.hpp b/src/tracks/check_line.hpp index 9857ffb081b..e0871bf634c 100644 --- a/src/tracks/check_line.hpp +++ b/src/tracks/check_line.hpp @@ -78,7 +78,7 @@ class CheckLine : public CheckStructure virtual void resetAfterKartMove(unsigned int kart_index) OVERRIDE; virtual void resetAfterRewind(unsigned int kart_index) OVERRIDE { resetAfterKartMove(kart_index); } - virtual void changeDebugColor(bool is_active) OVERRIDE; + virtual void changeDebugColor(bool is_active, bool prevent_backwards = false) OVERRIDE; virtual bool triggeringCheckline() const OVERRIDE { return true; } // ------------------------------------------------------------------------ /** Sets if this check line should not do a height test for testing diff --git a/src/tracks/check_manager.cpp b/src/tracks/check_manager.cpp index 6bc01248a7a..1d8ece04cea 100644 --- a/src/tracks/check_manager.cpp +++ b/src/tracks/check_manager.cpp @@ -86,9 +86,15 @@ void CheckManager::load(const XMLNode &node) it != check_structures_to_change_state.end(); it++) { if(DriveGraph::get()->isReverse()) + { m_all_checks[*it]->addSuccessor(i); + m_all_checks[i]->addPredecessor(*it); + } else + { m_all_checks[i]->addSuccessor(*it); + m_all_checks[*it]->addPredecessor(i); + } } } diff --git a/src/tracks/check_structure.cpp b/src/tracks/check_structure.cpp index 15643100a13..7e84580a394 100644 --- a/src/tracks/check_structure.cpp +++ b/src/tracks/check_structure.cpp @@ -39,7 +39,8 @@ CheckStructure::CheckStructure(const XMLNode &node, unsigned int index) // This structure is actually filled by the check manager (necessary // in order to support track reversing). - m_check_structures_to_change_state.clear(); + m_next_check_structures.clear(); + m_previous_check_structures.clear(); m_same_group.clear(); std::string kind; @@ -92,6 +93,7 @@ void CheckStructure::reset(const Track &track) { m_previous_position.clear(); m_is_active.clear(); + m_backwards_active.clear(); World *world = World::getWorld(); for(unsigned int i=0; igetNumKarts(); i++) @@ -99,8 +101,9 @@ void CheckStructure::reset(const Track &track) const Vec3 &xyz = world->getKart(i)->getXYZ(); m_previous_position.push_back(xyz); - // Activate all checkline + // Activate all relevant checklines m_is_active.push_back(m_active_at_reset); + m_backwards_active.push_back(false); } // for igetKart(i)->getFrontXYZ(); if(world->getKart(i)->getKartAnimation()) continue; - // Only check active checklines. - if(m_is_active[i] && isTriggered(m_previous_position[i], xyz, i)) + + // Check active checklines that the kart needs to cross to progress, + // and also the previous checkline to prevent lap completion going backwards + if((m_is_active[i] || m_backwards_active[i]) + && isTriggered(m_previous_position[i], xyz, i)) { if(UserConfigParams::m_check_debug) Log::info("CheckStructure", @@ -133,6 +139,10 @@ void CheckStructure::update(float dt) } // update // ---------------------------------------------------------------------------- +// TODO : a lot of this code is specific to checklines, but it's in a class +// which also serves as a parent for many other objects. +// Evaluate the opportunity of getting rid of inheritance here to +// consolidate things. /** Changes the status (active/inactive) of all check structures contained * in the index list indices. * \param indices List of index of check structures in the CheckManager that @@ -141,8 +151,7 @@ void CheckStructure::update(float dt) * \param change_state How to change the state (active, deactivate, toggle). */ void CheckStructure::changeStatus(const std::vector &indices, - int kart_index, - ChangeState change_state) + int kart_index, ChangeState change_state) { int player_kart_index = 0; if (World::getWorld()->getPlayerKart(0)) @@ -150,6 +159,7 @@ void CheckStructure::changeStatus(const std::vector &indices, bool update_debug_colors = UserConfigParams::m_check_debug && RaceManager::get()->getNumPlayers()>0 && kart_index == player_kart_index; + bool activate_exclusive_done = false; CheckManager* cm = Track::getCurrentTrack()->getCheckManager(); for(unsigned int i=0; i &indices, { case CS_DEACTIVATE: cs->m_is_active[kart_index] = false; + // Fall-through + case CS_BACKWARDS_DEACTIVATE: + cs->m_backwards_active[kart_index] = false; if(UserConfigParams::m_check_debug) { Log::info("CheckStructure", "Deactivating %d for %s.", @@ -168,6 +181,18 @@ void CheckStructure::changeStatus(const std::vector &indices, World::getWorld()->getKart(kart_index)->getIdent().c_str()); } break; + case CS_ACTIVATE_EXCLUSIVE: + // Say checkline 2 was triggered then checkline 1 was triggered backwards, + // we want to activate checkline 2 back but also ensure that checkline 3 + // is properly desactivated. + // As all the checklines in a group should share the same successors, + // we only need to do this once + if (!activate_exclusive_done) + { + changeStatus(cs->m_next_check_structures, kart_index, CS_DEACTIVATE); + activate_exclusive_done = true; + } + // Fall-through case CS_ACTIVATE: cs->m_is_active[kart_index] = true; if(UserConfigParams::m_check_debug) @@ -177,6 +202,27 @@ void CheckStructure::changeStatus(const std::vector &indices, World::getWorld()->getKart(kart_index)->getIdent().c_str()); } break; + case CS_BACKWARDS_ACTIVATE: + // We don't want to call CheckLap's isTriggered function to avoid + // incorrect lap validations. Laplines also don't check for being crossed + // physically like other checklines do. + if (cs->m_check_type == CT_NEW_LAP) + break; + + cs->m_backwards_active[kart_index] = true; + if(UserConfigParams::m_check_debug) + { + Log::info("CheckStructure", "Activating backward driving monitoring at %d for %s.", + indices[i], + World::getWorld()->getKart(kart_index)->getIdent().c_str()); + } + // Stop backwards driving monitoring for the checkline group even before the one we just activated + if (!activate_exclusive_done) + { + changeStatus(cs->m_previous_check_structures, kart_index, CS_BACKWARDS_DEACTIVATE); + activate_exclusive_done = true; + } + break; case CS_TOGGLE: if(UserConfigParams::m_check_debug) { @@ -194,22 +240,22 @@ void CheckStructure::changeStatus(const std::vector &indices, } // switch if(update_debug_colors) { - cs->changeDebugColor(cs->m_is_active[kart_index]); + cs->changeDebugColor(cs->m_is_active[kart_index], cs->m_backwards_active[kart_index]); } } // for igetCheckStructureCount(); n++) + if(UserConfigParams::m_check_debug) { - CheckStructure *cs = cm->getCheckStructure(n); - if (dynamic_cast(cs) != NULL) - printf("Checkline %i (LAP) : %i\n", n, (int)cs->m_is_active[kart_index]); - else - printf("Checkline %i : %i\n", n, (int)cs->m_is_active[kart_index]); - + printf("--------\n"); + for (unsigned int n=0; ngetCheckStructureCount(); n++) + { + CheckStructure *cs = cm->getCheckStructure(n); + if (dynamic_cast(cs) != NULL) + printf("Checkline %i (LAP) : %i\n", n, (int)cs->m_is_active[kart_index] + 2*(int)cs->m_backwards_active[kart_index]); + else + printf("Checkline %i : %i\n", n, (int)cs->m_is_active[kart_index] + 2*(int)cs->m_backwards_active[kart_index]); + } } - */ } //changeStatus @@ -229,17 +275,34 @@ void CheckStructure::trigger(unsigned int kart_index) World::getWorld()->getKart(kart_index)->getIdent().c_str(), m_index); } - changeStatus(m_check_structures_to_change_state, - kart_index, CS_ACTIVATE); + changeStatus(m_next_check_structures, kart_index, CS_ACTIVATE); + // Remove any backward-active status remaining + // (the lapline doesn't have a predecessor) + for (unsigned int n=0; n < Track::getCurrentTrack()->getCheckManager()->getCheckStructureCount(); n++) + { + CheckStructure *cs = Track::getCurrentTrack()->getCheckManager()->getCheckStructure(n); + if (cs->m_backwards_active[kart_index]) + { + std::vector indice; + indice.push_back(n); + changeStatus(indice, kart_index, CS_BACKWARDS_DEACTIVATE); + } + } break; + case CT_ACTIVATE: - changeStatus(m_check_structures_to_change_state, - kart_index, CS_ACTIVATE); + if (m_backwards_active[kart_index]) + changeStatus(m_next_check_structures, kart_index, CS_ACTIVATE_EXCLUSIVE); + else + changeStatus(m_next_check_structures, kart_index, CS_ACTIVATE); + + changeStatus(m_previous_check_structures, kart_index, CS_BACKWARDS_ACTIVATE); break; + case CT_TOGGLE: - changeStatus(m_check_structures_to_change_state, - kart_index, CS_TOGGLE); + changeStatus(m_next_check_structures, kart_index, CS_TOGGLE); break; + default: break; } // switch m_check_type @@ -251,7 +314,8 @@ void CheckStructure::saveCompleteState(BareNetworkString* bns) { World* world = World::getWorld(); for (unsigned int i = 0; i < world->getNumKarts(); i++) - bns->add(m_previous_position[i]).addUInt8(m_is_active[i] ? 1 : 0); + bns->add(m_previous_position[i]).addUInt8(m_is_active[i] ? 1 : + m_backwards_active[i] ? 2 : 0); } // saveCompleteState // ---------------------------------------------------------------------------- @@ -259,25 +323,29 @@ void CheckStructure::restoreCompleteState(const BareNetworkString& b) { m_previous_position.clear(); m_is_active.clear(); + m_backwards_active.clear(); World* world = World::getWorld(); for (unsigned int i = 0; i < world->getNumKarts(); i++) { Vec3 xyz = b.getVec3(); - bool is_active = b.getUInt8() == 1; m_previous_position.push_back(xyz); - m_is_active.push_back(is_active); + uint8_t active_value = b.getUInt8(); + m_is_active.push_back(active_value == 1); + m_backwards_active.push_back(active_value == 2); } } // restoreCompleteState // ---------------------------------------------------------------------------- void CheckStructure::saveIsActive(int kart_id, BareNetworkString* bns) { - bns->addUInt8(m_is_active[kart_id] ? 1 : 0); + bns->addUInt8(m_is_active[kart_id] ? 1 : + m_backwards_active[kart_id] ? 2 : 0); } // saveIsActive // ---------------------------------------------------------------------------- void CheckStructure::restoreIsActive(int kart_id, const BareNetworkString& b) { - bool is_active = b.getUInt8() == 1; - m_is_active.at(kart_id) = is_active; + uint8_t active_value = b.getUInt8(); + m_is_active.at(kart_id) = (active_value == 1); + m_backwards_active.at(kart_id) = (active_value == 2); } // restoreIsActive diff --git a/src/tracks/check_structure.hpp b/src/tracks/check_structure.hpp index ceba9cd24a5..537ee33e49e 100644 --- a/src/tracks/check_structure.hpp +++ b/src/tracks/check_structure.hpp @@ -76,6 +76,10 @@ class CheckStructure /** Stores if this check structure is active (for a given kart). */ std::vector m_is_active; + /** Stores if this check structure has to be checked to prevent + * lap completion going backwards (for a given kart). */ + std::vector m_backwards_active; + /** True if this check structure should be activated at a reset. */ bool m_active_at_reset; @@ -91,7 +95,11 @@ class CheckStructure /** Contains the indices of the corresponding check structures that * get their state changed (activated or switched). */ - std::vector m_check_structures_to_change_state; + std::vector m_next_check_structures; + + /** Contains the indices of the previous check structures that + * have to be set to backwards_active. */ + std::vector m_previous_check_structures; /** A list of check lines that should be activated/switched when this * lines is activated/switched. I.e. can be used if more than one lap @@ -100,7 +108,8 @@ class CheckStructure * as huge shortcuts. */ std::vector m_same_group; - enum ChangeState {CS_DEACTIVATE, CS_ACTIVATE, CS_TOGGLE}; + enum ChangeState {CS_DEACTIVATE, CS_ACTIVATE, CS_ACTIVATE_EXCLUSIVE, + CS_TOGGLE, CS_BACKWARDS_DEACTIVATE, CS_BACKWARDS_ACTIVATE}; void changeStatus(const std::vector &indices, int kart_index, ChangeState change_state); @@ -111,7 +120,7 @@ class CheckStructure virtual void update(float dt); virtual void resetAfterKartMove(unsigned int kart_index) {} virtual void resetAfterRewind(unsigned int kart_index) {} - virtual void changeDebugColor(bool is_active) {} + virtual void changeDebugColor(bool is_active, bool prevent_backwards = false) {} /** True if going from old_pos to new_pos crosses this checkline. This function * is called from update (of the checkline structure). * \param old_pos Position in previous frame. @@ -132,9 +141,15 @@ class CheckStructure * by this check structure. */ void addSuccessor(unsigned int i) { - m_check_structures_to_change_state.push_back(i); + m_next_check_structures.push_back(i); } // addSuccessor // ------------------------------------------------------------------------ + /** Adds the index of a predecessor check structure. */ + void addPredecessor(unsigned int i) + { + m_previous_check_structures.push_back(i); + } // addPredecessor + // ------------------------------------------------------------------------ virtual bool triggeringCheckline() const { return false; } // ------------------------------------------------------------------------ virtual void saveCompleteState(BareNetworkString* bns); From dc95287f05c9ed906bc7778a5aa3446c478f36c9 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Mon, 18 Aug 2025 16:21:20 +0200 Subject: [PATCH 125/188] Fix #4625 --- src/items/powerup_manager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/items/powerup_manager.cpp b/src/items/powerup_manager.cpp index 19b30d95efa..e8c99bb0941 100644 --- a/src/items/powerup_manager.cpp +++ b/src/items/powerup_manager.cpp @@ -917,6 +917,7 @@ PowerupManager::PowerupType PowerupManager::getRandomPowerup(unsigned int pos, // basketball, the parachute and the nitro-hack unwelcome // - The swatter and the bowling ball make the start too random if (World::getWorld() && + RaceManager::get()->isLinearRaceMode() && stk_config->ticks2Time(World::getWorld()->getTicksSinceStart()) < stk_config->m_limited_items_timeout) { From 3a6203c57fac0b22c633689a2ccd79eecb5e2a33 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Mon, 18 Aug 2025 16:22:02 +0200 Subject: [PATCH 126/188] Fix #4975 --- src/items/powerup_manager.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/items/powerup_manager.cpp b/src/items/powerup_manager.cpp index e8c99bb0941..0bcb0da64f7 100644 --- a/src/items/powerup_manager.cpp +++ b/src/items/powerup_manager.cpp @@ -933,6 +933,20 @@ PowerupManager::PowerupType PowerupManager::getRandomPowerup(unsigned int pos, *n = 1; } } + + // Prevent basketballs from being received when at least a kart has + // already finished: + // - Avoids the first remaining kart receiving a basket ball + // - Avoids having a very small space between the player getting the + // basket ball and the kart ahead that will be targeted. + if (powerup == POWERUP_RUBBERBALL && + RaceManager::get()->isLinearRaceMode() && + RaceManager::get()->getFinishedKarts() >= 1) + { + powerup = POWERUP_ZIPPER; + *n = 1; + } + return (PowerupType)powerup; } // getRandomPowerup From f493eee9174bf95e0fd1a00820c97bec9d15379a Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sat, 20 Sep 2025 23:12:19 +0200 Subject: [PATCH 127/188] Fix #5021 If the current max speed is lower than the kart's normal max speed (due to terrain slowdown or other factors), dampen the reduction in reverse gear. We use two different formulas that work better with different values and transition smoothly, in order to guarantee: - A reduced max speed cannot make the forward gears slower than the reverse gear, - An increase in terrain slowdown cannot lead to an increase in reverse speed. --- data/kart_characteristics.xml | 4 +++- src/karts/kart.cpp | 27 +++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 69708e8da7e..412ba8fe9e8 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -94,7 +94,9 @@ time-full-brake: The brake force will be applied progressively until time-full-brake. max-speed-reverse-ratio is the percentage of max speed for reverse - gear. + gear. This value is only used exactly in the absence of overall + max speed penalties. It is otherwise adjusted in code to dampen + the effects of e.g. terrain penalties. --> diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 98c83a0eb49..20e618468e0 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -3283,10 +3283,29 @@ void Kart::updateEnginePowerAndBrakes(int ticks) else if (!m_controls.getAccel()) { m_vehicle->setAllBrakes(0); - // going backward, apply reverse gear ratio (unless he goes - // too fast backwards) - if ( -m_speed < m_max_speed->getCurrentMaxSpeed() - *m_kart_properties->getEngineMaxSpeedReverseRatio()) + + // To determine what is the allowable max speed in reverse, + // we do some additional math to avoid ultra-slow-reverse (see #5021). + // This dampens the effect of max-speed penalties. + float max_speed_ratio = m_max_speed->getCurrentMaxSpeed() + / m_kart_properties->getEngineMaxSpeed(); + float reverse_speed_ratio = m_kart_properties->getEngineMaxSpeedReverseRatio(); + if (max_speed_ratio < 1.0f && reverse_speed_ratio < 1.0f) + { + // We want to ensure that reverse speed at a given slowdown level cannot + // exceed the forward max speed (limit A) and that it cannot exceed reverse speed + // at lower slowdown levels (limit B). + // We use two formulas. One guarantees the respect of limit A, and of limit B + // for values >= 0.5f ; the other formula guarantees the respect of limit B, and + // of limit A for values <= 0.5f. Both formulas transition smoothly at 0.5f. + float soft_ratio = reverse_speed_ratio * max_speed_ratio + (1.0f - max_speed_ratio); + float hard_ratio = reverse_speed_ratio * (2.0f - max_speed_ratio); + reverse_speed_ratio = std::min(soft_ratio, hard_ratio); + } + + // When going backward, we apply the reverse gear ratio, unless + // the velocity is too high. + if ( -m_speed < m_max_speed->getCurrentMaxSpeed() * reverse_speed_ratio) engine_power = -2 * base_engine_power; else // -m_speed >= max speed on this terrain engine_power = 0; From 9d6c098322ad0c2c9c1442a2efefac19fcfb9437 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Wed, 24 Sep 2025 18:11:42 +0200 Subject: [PATCH 128/188] Make the reverse gear ratio configurable Editing kart_characteristics.xml is now sufficient. The hardcoded doubling of engine power when reversing has therefore been removed. This is a functional change insofar that extra sources of engine power (such as drifting, nitro, zippers) don't see their power doubled when reversing. Also: - Update the default gears to match the characteristics of heavies - Don't double the extra power boosts in case the kart exceeds the defined gear (although that cannot happen with default kart characteristics, it could when modding) --- data/kart_characteristics.xml | 27 +++++++++------- src/karts/abstract_characteristic.cpp | 16 ++++++++++ src/karts/abstract_characteristic.hpp | 2 ++ src/karts/kart.cpp | 44 +++++++++++++++++---------- src/karts/kart_properties.cpp | 6 ++++ src/karts/kart_properties.hpp | 1 + src/karts/xml_characteristic.cpp | 2 ++ 7 files changed, 71 insertions(+), 27 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 412ba8fe9e8..688ffe173f8 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -102,15 +102,16 @@ time-full-brake="0.35" max-speed-reverse-ratio="0.48" /> - + reverse-power: contains the increase in max power in reverse gear --> + @@ -416,7 +417,8 @@ + power-increase="2.95 2.42 1.99 1.56 1.28 1.0" + reverse-power="5.9"/> @@ -432,7 +434,8 @@ + power-increase="2.5 2.08 1.76 1.44 1.22 1" + reverse-power="5.0"/> @@ -448,7 +451,8 @@ + power-increase="2.5 2.08 1.76 1.44 1.22 1" + reverse-power="5.0"/> @@ -463,7 +467,8 @@ + power-increase="2.05 1.74 1.53 1.32 1.16 1" + reverse-power="4.1"/> diff --git a/src/karts/abstract_characteristic.cpp b/src/karts/abstract_characteristic.cpp index d7fdea7a103..f7787aa82cb 100644 --- a/src/karts/abstract_characteristic.cpp +++ b/src/karts/abstract_characteristic.cpp @@ -101,6 +101,8 @@ AbstractCharacteristic::ValueType AbstractCharacteristic::getType( return TYPE_FLOAT_VECTOR; case GEAR_POWER_INCREASE: return TYPE_FLOAT_VECTOR; + case GEAR_REVERSE_POWER: + return TYPE_FLOAT; case MASS: return TYPE_FLOAT; case WHEELS_DAMPING_RELAXATION: @@ -357,6 +359,8 @@ std::string AbstractCharacteristic::getName(CharacteristicType type) return "GEAR_SWITCH_RATIO"; case GEAR_POWER_INCREASE: return "GEAR_POWER_INCREASE"; + case GEAR_REVERSE_POWER: + return "GEAR_REVERSE_POWER"; case MASS: return "MASS"; case WHEELS_DAMPING_RELAXATION: @@ -835,6 +839,18 @@ std::vector AbstractCharacteristic::getGearPowerIncrease() const return result; } // getGearPowerIncrease +// ---------------------------------------------------------------------------- +float AbstractCharacteristic::getGearReversePower() const +{ + float result; + bool is_set = false; + process(GEAR_REVERSE_POWER, &result, &is_set); + if (!is_set) + Log::fatal("AbstractCharacteristic", "Can't get characteristic %s", + getName(GEAR_REVERSE_POWER).c_str()); + return result; +} // getGearReversePower + // ---------------------------------------------------------------------------- float AbstractCharacteristic::getMass() const { diff --git a/src/karts/abstract_characteristic.hpp b/src/karts/abstract_characteristic.hpp index 23ed168e439..bb3044e5626 100644 --- a/src/karts/abstract_characteristic.hpp +++ b/src/karts/abstract_characteristic.hpp @@ -103,6 +103,7 @@ class AbstractCharacteristic // Gear GEAR_SWITCH_RATIO, GEAR_POWER_INCREASE, + GEAR_REVERSE_POWER, // Mass MASS, @@ -300,6 +301,7 @@ class AbstractCharacteristic std::vector getGearSwitchRatio() const; std::vector getGearPowerIncrease() const; + float getGearReversePower() const; float getMass() const; diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 20e618468e0..436e3aac55e 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1379,27 +1379,36 @@ void Kart::setStartupBoost(uint8_t boost_level) //----------------------------------------------------------------------------- /** Simulates gears by adjusting the force of the engine. It also takes the - * effect of the zipper into account. + * effect of additional engine boost sources into account. */ float Kart::getActualWheelForce() { - float add_force = m_max_speed->getCurrentAdditionalEngineForce(); - assert(!std::isnan(add_force)); - const std::vector& gear_ratio=m_kart_properties->getGearSwitchRatio(); - for(unsigned int i=0; igetCurrentAdditionalEngineForce(); + assert(!std::isnan(extra_force)); + const std::vector& gear_ratio = m_kart_properties->getGearSwitchRatio(); + + float gear_factor = 1.0; + + // If the kart is reversing, + if (m_speed < 0 && m_controls.getBrake()) { - if(m_speed <= m_kart_properties->getEngineMaxSpeed() * gear_ratio[i]) + assert(!std::isnan(m_kart_properties->getGearReversePower())); + gear_factor = m_kart_properties->getGearReversePower(); + } + else // Kart going forward, we use gears + { + for(unsigned int i=0; igetEnginePower())); - assert(!std::isnan(m_kart_properties->getGearPowerIncrease()[i])); - return m_kart_properties->getEnginePower() - * m_kart_properties->getGearPowerIncrease()[i] - + add_force; + if(m_speed <= m_kart_properties->getEngineMaxSpeed() * gear_ratio[i]) + { + assert(!std::isnan(m_kart_properties->getGearPowerIncrease()[i])); + gear_factor = m_kart_properties->getGearPowerIncrease()[i]; + break; + } } } assert(!std::isnan(m_kart_properties->getEnginePower())); - return m_kart_properties->getEnginePower() + add_force * 2; - + return m_kart_properties->getEnginePower() * gear_factor + extra_force; } // getActualWheelForce //----------------------------------------------------------------------------- @@ -3244,7 +3253,9 @@ void Kart::updateEnginePowerAndBrakes(int ticks) // Lose some traction when skidding, to balance the advantage if (m_controls.getSkidControl() && m_kart_properties->getSkidVisualTime() == 0) + { engine_power *= 0.5f; + } // In case the acceleration value is analog, // moderate the engine power accordingly @@ -3303,10 +3314,11 @@ void Kart::updateEnginePowerAndBrakes(int ticks) reverse_speed_ratio = std::min(soft_ratio, hard_ratio); } - // When going backward, we apply the reverse gear ratio, unless - // the velocity is too high. + // When going backward, we apply the reverse gear, unless exceeding the max reverse speed + // The reverse gear ratio is already applied to engine power in getActualWheelForce(), + // so we just make it negative so that it makes the kart go backwards. if ( -m_speed < m_max_speed->getCurrentMaxSpeed() * reverse_speed_ratio) - engine_power = -2 * base_engine_power; + engine_power = -base_engine_power; else // -m_speed >= max speed on this terrain engine_power = 0; } // m_speed <00 diff --git a/src/karts/kart_properties.cpp b/src/karts/kart_properties.cpp index d9f5c57502e..cba67907088 100644 --- a/src/karts/kart_properties.cpp +++ b/src/karts/kart_properties.cpp @@ -893,6 +893,12 @@ std::vector KartProperties::getGearPowerIncrease() const return m_cached_characteristic->getGearPowerIncrease(); } // getGearPowerIncrease +// ---------------------------------------------------------------------------- +float KartProperties::getGearReversePower() const +{ + return m_cached_characteristic->getGearReversePower(); +} // getGearReversePower + // ---------------------------------------------------------------------------- float KartProperties::getMass() const { diff --git a/src/karts/kart_properties.hpp b/src/karts/kart_properties.hpp index 1a7696c3d71..de7d735c5f8 100644 --- a/src/karts/kart_properties.hpp +++ b/src/karts/kart_properties.hpp @@ -412,6 +412,7 @@ class KartProperties std::vector getGearSwitchRatio() const; std::vector getGearPowerIncrease() const; + float getGearReversePower() const; float getMass() const; diff --git a/src/karts/xml_characteristic.cpp b/src/karts/xml_characteristic.cpp index 43a69c4b7dd..d878f14e816 100644 --- a/src/karts/xml_characteristic.cpp +++ b/src/karts/xml_characteristic.cpp @@ -379,6 +379,8 @@ void XmlCharacteristic::load(const XMLNode *node) &m_values[GEAR_SWITCH_RATIO]); sub_node->get("power-increase", &m_values[GEAR_POWER_INCREASE]); + sub_node->get("reverse-power", + &m_values[GEAR_REVERSE_POWER]); } if (const XMLNode *sub_node = node->getNode("mass")) From 0b38eed288dae1d9b7c1a335175203f8c1325447 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Wed, 24 Sep 2025 18:34:09 +0200 Subject: [PATCH 129/188] Minor tweaks and cleanup to the acceleration logic - Reduce the parking speed from 5 to 3 - Move the nitro and electro-shield multiplier to GetActualWheelForce - Remove the non-functional code that was supposed to halve engine power when skidding (it didn't work, and it's not desirable anyways) --- src/karts/kart.cpp | 42 +++++++++++++++--------------------------- 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 436e3aac55e..c69f59f0891 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1383,11 +1383,9 @@ void Kart::setStartupBoost(uint8_t boost_level) */ float Kart::getActualWheelForce() { + float gear_factor = 1.0; float extra_force = m_max_speed->getCurrentAdditionalEngineForce(); assert(!std::isnan(extra_force)); - const std::vector& gear_ratio = m_kart_properties->getGearSwitchRatio(); - - float gear_factor = 1.0; // If the kart is reversing, if (m_speed < 0 && m_controls.getBrake()) @@ -1397,6 +1395,7 @@ float Kart::getActualWheelForce() } else // Kart going forward, we use gears { + const std::vector& gear_ratio = m_kart_properties->getGearSwitchRatio(); for(unsigned int i=0; igetEngineMaxSpeed() * gear_ratio[i]) @@ -1408,7 +1407,17 @@ float Kart::getActualWheelForce() } } assert(!std::isnan(m_kart_properties->getEnginePower())); - return m_kart_properties->getEnginePower() * gear_factor + extra_force; + float engine_power = m_kart_properties->getEnginePower() * gear_factor + extra_force; + + // apply nitro boost if relevant + if(getSpeedIncreaseTicksLeft(MaxSpeed::MS_INCREASE_NITRO) > 0) + engine_power*= m_kart_properties->getNitroEngineMult(); + + // apply electro-shield engine boost if relevant + if(getSpeedIncreaseTicksLeft(MaxSpeed::MS_INCREASE_ELECTRO) > 0) + engine_power*= m_kart_properties->getElectroEngineMult(); + + return engine_power; } // getActualWheelForce //----------------------------------------------------------------------------- @@ -3197,9 +3206,8 @@ float Kart::applyAirFriction(float engine_power) // 1)This is the logical behavior // 2)That way, engine boosts remain useful at high speed // 3)It helps heavier karts, who have an higher engine power - float sign = (getSpeed() < 0) ? -1.0f : 1.0f; - engine_power-=friction_intensity*sign; + engine_power -= friction_intensity*sign; return engine_power; } //applyAirFriction @@ -3214,18 +3222,6 @@ void Kart::updateEnginePowerAndBrakes(int ticks) updateNitro(ticks); float engine_power = getActualWheelForce(); - // apply nitro boost if relevant - if(getSpeedIncreaseTicksLeft(MaxSpeed::MS_INCREASE_NITRO) > 0) - { - engine_power*= m_kart_properties->getNitroEngineMult(); - } - - // apply electro-shield engine boost if relevant - if(getSpeedIncreaseTicksLeft(MaxSpeed::MS_INCREASE_ELECTRO) > 0) - { - engine_power*= m_kart_properties->getElectroEngineMult(); - } - // apply bubblegum physics if relevant if (m_bubblegum_ticks > 0) { @@ -3250,13 +3246,6 @@ void Kart::updateEnginePowerAndBrakes(int ticks) else if(m_speed < 0.0f) engine_power *= 4.0f; - // Lose some traction when skidding, to balance the advantage - if (m_controls.getSkidControl() && - m_kart_properties->getSkidVisualTime() == 0) - { - engine_power *= 0.5f; - } - // In case the acceleration value is analog, // moderate the engine power accordingly engine_power *= m_controls.getAccel(); @@ -3329,7 +3318,7 @@ void Kart::updateEnginePowerAndBrakes(int ticks) // If not giving power (forward or reverse gear) or braking, and speed is low // we are "parking" the kart, so in battle mode we can ambush people - if (!m_controls.getAccel() && std::abs(m_speed) < 5.0f) + if (!m_controls.getAccel() && std::abs(m_speed) < 3.0f) m_vehicle->setAllBrakes(20.0f); // Either all or no brake is set, so test only one to avoid // resetting all brakes most of the time. @@ -3344,7 +3333,6 @@ void Kart::updateEnginePowerAndBrakes(int ticks) // after correcting for the linear slowdown. engine_power = compensateLinearSlowdown(engine_power); - applyEngineForce(engine_power); } // updateEnginePowerAndBrakes From 926ca63729aca1db08d2dbb86b2cb8ba5802e91a Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Wed, 24 Sep 2025 19:35:28 +0200 Subject: [PATCH 130/188] Transfer some skidding bonus power into base engine power Before this change, skidding did not only help improve max speed, it made an enormous difference in the time needed to accelerate to max speed. Even with this change, having an active skid or completing a skid is clearly helpful, but the difference isn't as dramatic. Engine power from skidding doesn't benefit from gear boosts, while base engine power does. Therefore, while the power is exactly equivalent with a skid boost active at the highest gear (which has a ratio of 1), it increases even beyond what an active skid would have provided in lower gears. Therefore, to mostly compensate this effect, gear ratios for kart classes have been revised, keeping the effective engine boost close to the value substracted An exception is seen in the first gear, where the light multiplier sees an extra reduction and the heavy multiplier is unchanged. This has little effect on acceleration performance, but addresses #5469, although the edge for heavier karts is small. --- data/kart_characteristics.xml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 688ffe173f8..5e668f433b1 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -98,7 +98,7 @@ max speed penalties. It is otherwise adjusted in code to dampen the effects of e.g. terrain penalties. --> - + diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 5e668f433b1..9b04e5f6308 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -330,6 +330,7 @@ to need more nitro than lighter character. small-container: how much energy a small container gives. big-container: how much energy a big container gives. + air-container: how much energy an air container gives. max-speed-increase: How much the speed of a kart might exceed its maximum speed (in m/s). min-burst: The minimum duration of a nitro burst, @@ -340,7 +341,7 @@ speed due to nitro fades out. max: How much nitro a kart can store. --> diff --git a/data/stk_config.xml b/data/stk_config.xml index 371fa6ebbb5..e442a7f4524 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -241,9 +241,9 @@ - + Order: giftbox, banana, big-nitro, small-nitro, air-nitro, bubble-gum, + nolok-bubble-gum, small-bubble-gum, small-nolok-bubble-gum, easter egg--> + + time-full-steer="0:0.1 0.3:0.1 0.3:0.14 0.6:0.14 0.6:0.21 1.0:0.21" + speed-factor="1.0"/> @@ -493,6 +497,7 @@ + @@ -501,6 +506,7 @@ + @@ -509,6 +515,7 @@ + @@ -517,6 +524,7 @@ + @@ -525,6 +533,7 @@ + @@ -533,6 +542,7 @@ + From f62bab273e0c58e7c5739fbac37d8e2ec68e0ed4 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Wed, 1 Oct 2025 23:27:06 +0200 Subject: [PATCH 137/188] Tweak turn radius and other characteristics - Increase max speed from 27.75 to 28. - As acceleration from a standstill is around 0.1s faster due to the previous transfer of skidding to base engine power, increase explosion and rescue duration by 0.15s and 0.1s respectively, mostly because it's nicer if the animations are not rushed. - Reduce slightly the advantage in explosion radius from heavier karts - Revise turn radiuses by making all kart classes more similar at low speeds. This makes medium and heavy karts more useful in non-racing game modes, while having little effect on racing balance, including in lower difficulties thanks to the recently introduced turn speed-factor. The very slight reduction at the 30 marker mostly compensates the max speed increase by 0.25. - Reduce the max-speed-reverse-ratio for karts turning at max reverse speed to be closer to their optimal angular speed - Relax the time-full-steer restrictions of the lowest difficulties : after the introduction of the turn speed-factor, oversteering by newbies is less likely. --- data/kart_characteristics.xml | 36 +++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 0e07a0c6a68..ab75a4d69a7 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -104,8 +104,8 @@ max speed penalties. It is otherwise adjusted in code to dampen the effects of e.g. terrain penalties. --> - + - + - From cce7121ddf7e1f757c1152f5f204342d049a4f88 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 9 Nov 2025 11:14:09 +0100 Subject: [PATCH 144/188] Prevent unlimited speeds when the kart is falling (Fix #5237) When falling down in the air, karts would accumulate vertical speed over time without any limit. In most jumps it wouldn't matter, but in really big jumps this could lead to extreme falling speeds and cause physics issues such as karts going through the ground. This commit: - Moves the base air friction formulas to btKart - Computes the kart's vertical movement based on the direction of gravity. - Computes air friction for the vertical component of a flying kart's movement. We purposefully only use the vertical component so that the forward movement of the kart is more or less unaffected: losing a lot of speed when jumping would make jumps much less fun. - Applies an impulse to the kart to apply the computed force. - Also silences an unrelated warning. Because the friction force first slows down the kart's vertical ascent before slowing down its fall, and because the forces involved are small at low vertical speeds, the effect on small and medium jumps is very limited. The effect is most noticeable when the jump is big and when the heights of the take-off point and of the landing point are significantly different. As the same force is currently applied to all kart classes, the terminal vertical velocity of a kart depends on its class (~31 for lights, ~36 for medium, ~41 for heavies). This could be changed if needed, but it's probably fine as is. This commit has been tested in various situations, including huge jumps (about 3.5s falling from a neutral vertical speed, leading to a falling speed of over 60 before this commit) and situations with gravity textures and changing gravity (flying around Interplanetary's moon). --- src/karts/kart.cpp | 6 +----- src/physics/btKart.cpp | 45 +++++++++++++++++++++++++++++++++++++++++- src/physics/btKart.hpp | 2 ++ 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 8e571a405e1..15ba70c409c 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -3243,11 +3243,7 @@ float Kart::compensateLinearSlowdown(float engine_power) */ float Kart::applyAirFriction(float engine_power) { - // The result will always be a positive number - float friction_intensity = fabsf(getSpeed()); - - // Not a pure quadratic evolution as it would be too brutal - friction_intensity *= sqrt(friction_intensity)*5.0f; + float friction_intensity = m_vehicle->getAirFriction(getSpeed()); // Apply parachute physics // Currently, all karts have the same base friction diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp index 9762cbffaca..f3029a9c63c 100644 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -86,7 +86,7 @@ btWheelInfo& btKart::addWheel(const btVector3& connectionPointCS, m_wheelInfo.push_back( btWheelInfo(ci)); btWheelInfo& wheel = m_wheelInfo[getNumWheels()-1]; - memset(&wheel.m_raycastInfo, 0, sizeof(wheel.m_raycastInfo)); + memset((void*)&wheel.m_raycastInfo, 0, sizeof(wheel.m_raycastInfo)); updateWheelTransformsWS(wheel, getChassisWorldTransform(), false); updateWheelTransform(getNumWheels()-1,false); @@ -460,6 +460,37 @@ void btKart::updateVehicle( btScalar step ) } + // Limit falling speeds by applying air friction to the vertical component + // ------------------------------------ + if (m_num_wheels_on_ground == 0) + { + btVector3 v = btVector3(0,0,0); + // Engine force and friction when on the ground are also applied per-wheel + for (int i=0;igetVelocityInLocalPoint(m_wheelInfo[i] + .m_chassisConnectionPointCS); + } + + btVector3 up = -m_chassisBody->getGravity(); + up.normalize(); + btVector3 v_up = (v * up) * up; + + // The scalar product is positive if the two vectors are rather pointing + // in a similar direction and is negative if they are rather pointing in + // an opposite direction. + // We simulate computing per-wheel (air-friction is non-linear with speed, + // so using the sum of the speeds at each wheel would be incorrect) + float v_speed = v_up.dot(up) / m_wheelInfo.size(); + float friction = m_wheelInfo.size() * getAirFriction(v_speed); + + //Log::info("Flying friction debug", "Vspeed is %f, friction is %f", v_speed, friction); + + btVector3 impulse = up * step * friction; + impulse *= (v_speed > 0) ? -1 : 1; + m_chassisBody->applyCentralImpulse(impulse); + } + // Update friction (i.e. forward force) // ------------------------------------ updateFriction( step); @@ -1096,6 +1127,18 @@ void btKart::setSliding(bool active) m_allow_sliding = active; } // setSliding +// ---------------------------------------------------------------------------- +float btKart::getAirFriction(float speed) +{ + // The result will always be a positive number + float friction_intensity = fabsf(speed); + + // Not a pure quadratic evolution as it would be too brutal + friction_intensity *= sqrt(friction_intensity)*5.0f; + + return friction_intensity; +} // getAirFriction + // ---------------------------------------------------------------------------- /** Adjusts the velocity of this kart to be at least the specified minimum, * and less than or equal to the maximum. If necessary the kart will diff --git a/src/physics/btKart.hpp b/src/physics/btKart.hpp index 70164b58813..8288edee8a0 100644 --- a/src/physics/btKart.hpp +++ b/src/physics/btKart.hpp @@ -292,6 +292,8 @@ class btKart : public btActionInterface m_max_speed = new_max_speed; } // setMaxSpeed // ------------------------------------------------------------------------ + float getAirFriction(float speed); + // ------------------------------------------------------------------------ /** Resets the maximum so any new maximum value from the application will * be accepted. */ virtual void resetMaxSpeed() { m_max_speed = -1.0f; } From bf9204f0a3c329386fdfea5c744afd512f72b47f Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Mon, 10 Nov 2025 21:14:20 +0100 Subject: [PATCH 145/188] Don't recompute the turn radius arrays on every frame Also: - Improve comments - Avoid dividing by 0 when the speed matching a radius of 0 is requested. - Update the default turn radius value to again match light kart values and update the comment to better explain what happens for speeds exceeding what's defined in the array. --- data/kart_characteristics.xml | 14 ++++---- src/karts/kart.cpp | 60 ++++++++++++++++++++--------------- src/karts/kart.hpp | 6 ++++ 3 files changed, 49 insertions(+), 31 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index e21eece52fe..5aa71499c49 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -62,12 +62,14 @@ radius: The turn radius of the kart at a given speed. The actual steering angle is dependent on the wheel base of the kart: radius = wheel_base/sin(steering_angle). - The values below define that at speed 0 the turn radius is 2, at - speed 10 the radius is 7.5 etc. + The values below define that at speed 0 the turn radius is 3, at + speed 4 the radius is 3.6, at speed 8 the radius is 5, etc. The actual turn radius is piece-wise linearly interpolated. This - allows for tighter turning at lower speeds, and also avoids that - the kart becomes too hard to control at high speed (speeds - higher than 25 can only be reached with powerups). + allows for tighter turning at lower speeds. If the speed is higher + than the highest defined one in the turn-radius array, the radius + will be the same as at this highest defined speed. For example, + a speed of 55 will have the same turn radius as the speed of 50 + for which a turn radius value is defined below. time-full-steer: This is the amount of change in steering depending on current steering. So if the steering is between 0 and 0.5, the time-for-steering-change is 0.17. If the current steering is @@ -85,7 +87,7 @@ abilities between kart classes keep more relevance even with lower overall speeds. --> - diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 15ba70c409c..8981ad16120 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -534,6 +534,8 @@ void Kart::reset() } m_time_previous_counter = 0.0f; + computeTurnAngleArray(); + // In case that the kart was in the air, in which case its // linear damping is 0 if(m_body) @@ -1036,47 +1038,55 @@ void Kart::updateWeight() } // updateWeight // ------------------------------------------------------------------------ -/** Returns the (maximum) speed for a given turn radius. - * \param radius The radius for which the speed needs to be computed. */ -float Kart::getSpeedForTurnRadius(float radius) const +/** Computes this kart's turn angle arrays and store them for reuse + * m_turn_angle_at_speed_AI contains the theoretical values, it should + * be used when trying to know what's the (max) speed for a given turn radius. + * m_turn_angle_at_speed values : this modified array is used to actually detemrine + * kart behavior: an effect in physics give shorter karts a bigger turn radius + * (see #3271). Multiplying by wheel base (kart length) allows to keep turn + * radius across karts of different lengths sharing the same turn properties. */ +void Kart::computeTurnAngleArray() { - // TODO - Compute once and save the result for the kart until the end of the race - // instead of recomputing this interpolation array every frame - InterpolationArray turn_angle_at_speed = m_kart_properties->getTurnRadius(); - float angle = sinf(1.0f / radius); + m_turn_angle_at_speed = m_kart_properties->getTurnRadius(); // Adjust the reference speeds in the array float speed_factor = std::max(0.1f, 1.0f / m_kart_properties->getTurnSpeedFactor()); - turn_angle_at_speed.adjustX(speed_factor); + m_turn_angle_at_speed.adjustX(speed_factor); + m_turn_angle_at_speed_AI = m_turn_angle_at_speed; + + // Convert the turn radius into turn angle. See the function's main comment for details. + for (int i = 0; i < (int)m_turn_angle_at_speed.size(); i++) + { + m_turn_angle_at_speed.setY(i, sinf( 1.0f / m_turn_angle_at_speed.getY(i)) + * m_kart_properties->getWheelBase()); + m_turn_angle_at_speed_AI.setY(i, sinf( 1.0f / m_turn_angle_at_speed_AI.getY(i))); + } +} // computeTurnAngleArray - // Convert the turn radius into turn angle - for(int i = 0; i < (int)turn_angle_at_speed.size(); i++) - turn_angle_at_speed.setY(i, sinf( 1.0f / turn_angle_at_speed.getY(i))); +// ------------------------------------------------------------------------ +/** Returns the (maximum) speed for a given turn radius. + * \param radius The radius for which the speed needs to be computed. */ +float Kart::getSpeedForTurnRadius(float radius) const +{ + // Avoid dividing by 0. + if (radius = 0.0f) + return 0.0f; - return turn_angle_at_speed.getReverse(angle); + float angle = sinf(1.0f / radius); + return m_turn_angle_at_speed_AI.getReverse(angle); } // getSpeedForTurnRadius // ------------------------------------------------------------------------ /** Returns the maximum steering angle (depending on speed). - This is proportional to kart length because physics reverse this effect, - the results of this function should not be used to determine the - real raw steer angle. */ + The results of this function should NOT be used to determine the real + raw steer angle, see computeTurneAngleArray() for why. */ float Kart::getMaxSteerAngle(float speed) const { - InterpolationArray turn_angle_at_speed = m_kart_properties->getTurnRadius(); - // Convert the turn radius into turn angle - // We multiply by wheel base to keep turn radius identical - // across karts of different lengths sharing the same - // turn radius properties - for(int i = 0; i < (int)turn_angle_at_speed.size(); i++) - turn_angle_at_speed.setY(i, sinf( 1.0f / turn_angle_at_speed.getY(i)) - * m_kart_properties->getWheelBase()); - // Make reverse mode turn the same way as forward if (speed < 0.0f) speed = -speed; - return turn_angle_at_speed.get(speed * m_kart_properties->getTurnSpeedFactor()); + return m_turn_angle_at_speed.get(speed); } // getMaxSteerAngle //----------------------------------------------------------------------------- diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index b263be358f4..df2c6493de9 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -35,6 +35,7 @@ #include "LinearMath/btTransform.h" #include "race/race_manager.hpp" #include "utils/cpp2011.hpp" +#include "utils/interpolation_array.hpp" #include "utils/no_copy.hpp" namespace irr @@ -354,6 +355,10 @@ class Kart : public Moveable /** Reset position. */ btTransform m_reset_transform; + /** Arrays allowing to convert between speed and turn radius */ + InterpolationArray m_turn_angle_at_speed; + InterpolationArray m_turn_angle_at_speed_AI; + std::vector m_custom_sounds; int m_emitter_id = 0; static const int EMITTER_COUNT = 3; @@ -399,6 +404,7 @@ class Kart : public Moveable void loadData(RaceManager::KartType type, bool animatedModel); void updateWeight(); void initSound(); + void computeTurnAngleArray(); public: Kart(const std::string& ident, unsigned int world_kart_id, int position, const btTransform& init_transform, From c837f4a654866977d4d2c0051bf73be11fb3b25e Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Mon, 10 Nov 2025 22:47:07 +0100 Subject: [PATCH 146/188] Only apply the increased turn radius in lower difficulties above a threshold speed We use a lowerbound value at which the increased turn radius starts being applied and an upperbound value above which it is fully applied. This change allows to preserve the intended racing behavior (turn radius characteristics of karts keep relevance at lower difficulties or with handicap, as without the speed factor heavies are obviously superior) while significantly improving low-speed maneuvers (used especially in battle and soccer) that had become too sluggish as a side-effect. Also: - Add error logs to catch misuse of the lowerbound and upperbound values - Fix a comparison that was mistakenly written as an assignation in the previous commit. --- data/kart_characteristics.xml | 15 +++++++++++-- src/karts/abstract_characteristic.cpp | 32 +++++++++++++++++++++++++++ src/karts/abstract_characteristic.hpp | 4 ++++ src/karts/kart.cpp | 8 +++++-- src/karts/kart_properties.cpp | 12 ++++++++++ src/karts/kart_properties.hpp | 2 ++ src/karts/xml_characteristic.cpp | 4 ++++ src/utils/interpolation_array.hpp | 31 ++++++++++++++++++++++++-- 8 files changed, 102 insertions(+), 6 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 5aa71499c49..f37b22cec63 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -81,15 +81,26 @@ If the kart is currently in the direction opposite to the required steering ("negative steering"), the time-full-steer at 0 is used. - speed-factor The real speed of the kart is multiplied by this factor + speed-factor: The real speed of the kart is multiplied by this factor before checking the turn radius tables. This is meant to be used by difficulty settings or handicap, so that differences in turning abilities between kart classes keep more relevance even with lower overall speeds. + speed-factor-partial-lb/ub: below the lower-bound, the speed-factor + is not applied (it's 1.0), above the upper-bound the speed-factor + is fully applied. It is recommended to set them to speeds which + serve as reference points for the radius array (otherwise, values + outside of the defined bounds will also be affected), and it is + crucial to ensure that speed-factor-partial-ub divided by the highest + possible speed-factor (with default speed-factor settings 1.4336) + is higher than speed-factor-partial-lb, preferrably significantly + so to avoid small speed increases having a strong effect on turn + radius. --> + speed-factor="1.0" speed-factor-partial-lb="8.0" + speed-factor-partial-ub="16.0"/> - + Older versions will be ignored. --> + - diff --git a/src/karts/kart_model.cpp b/src/karts/kart_model.cpp index 7a5d22c55a4..480887b8a19 100644 --- a/src/karts/kart_model.cpp +++ b/src/karts/kart_model.cpp @@ -167,17 +167,20 @@ KartModel::KartModel(bool is_master) void KartModel::loadInfo(const XMLNode &node) { node.get("model-file", &m_model_filename); + // Version 2 corresponds to old .b3d karts. Animations may have some issues (see #5477). + // This format is deprecated and support will be dropped at some point. + // Version 3 corresponds to .spm karts, compared to version 2 + // it also supports headlights and speed-weighted objets. + // Version 4 corresponds to .spm karts, compared to version 3 + // it uses Evolution's animation markers. + node.get("version", &m_version); - int version = 1; - if(const XMLNode *format_node=node.getNode("format")) - format_node->get("version", &version); - - //Log::verbose("Kart_Model", "Kart %s is using format version %i", m_model_filename.c_str(), version); + //Log::verbose("Kart_Model", "Kart %s is using format version %i", m_model_filename.c_str(), m_version); if(const XMLNode *animation_node=node.getNode("animations")) { // Compatibility-layer for 1.x and older karts - if (version == 1) + if (m_version == 2 || m_version == 3) { animation_node->get("left", &m_animation_frame[AF_LEFT] ); animation_node->get("straight", &m_animation_frame[AF_STRAIGHT] ); @@ -204,7 +207,7 @@ void KartModel::loadInfo(const XMLNode &node) animation_node->get("speed", &m_animation_speed ); } // Version used for SuperTuxKart Evolution - else if (version == 2) + else if (m_version == 4) { animation_node->get("left", &m_animation_frame[AF_LEFT] ); animation_node->get("straight", &m_animation_frame[AF_STRAIGHT] ); @@ -250,7 +253,7 @@ void KartModel::loadInfo(const XMLNode &node) else { Log::error("Kart_Model", "The kart.xml format of %s (version %i) is unsupported!", - m_model_filename.c_str(), version); + m_model_filename.c_str(), m_version); } // Extra-logging in artist-debug mode to easily check for missing animations @@ -259,7 +262,7 @@ void KartModel::loadInfo(const XMLNode &node) std::vector missing_animations; for (unsigned int i = AF_BEGIN + 1; i <= AF_END; i++) { - if (m_animation_frame[i] == -1) + if (i != AF_PLACEHOLDER && m_animation_frame[i] == -1) missing_animations.push_back(i); } if (!missing_animations.empty()) @@ -295,7 +298,6 @@ void KartModel::loadInfo(const XMLNode &node) m_has_nitro_emitter = true; } - node.get("version", &m_version); if (m_version > 2) { if (const XMLNode *speed_weighted_objects_node = node.getNode("speed-weighted-objects")) From ad49b2fbfd5fcd70f6e09f92c44390f5c6d642da Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Fri, 14 Nov 2025 14:12:05 +0100 Subject: [PATCH 156/188] Allow enabling kart headlights for all karts - Add a new UserConfig setting to manage which kart headlights should be shown (none, player-only, all) - Update the advanced video settings dialog to support changing this option - Use the setting to pick which headlights are shown. - Also fix some LoD comments In my limited local testing at graphics 6, I didn't observe a noticeabe drop in framerate when headlights are activated for all karts (playing with 20 karts, of which the majority had headlights available). The main graphics slider should also handle this new option in the future. --- data/gui/dialogs/custom_video_settings.stkgui | 10 ++++++- src/config/user_config.hpp | 7 +++-- src/karts/kart_model.cpp | 3 +- .../dialogs/custom_video_settings.cpp | 29 ++++++++++++++----- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/data/gui/dialogs/custom_video_settings.stkgui b/data/gui/dialogs/custom_video_settings.stkgui index 8c01d3f686d..e5a99d82d4a 100644 --- a/data/gui/dialogs/custom_video_settings.stkgui +++ b/data/gui/dialogs/custom_video_settings.stkgui @@ -175,7 +175,15 @@ - + + +

+ +
diff --git a/src/config/user_config.hpp b/src/config/user_config.hpp index 4495c6785da..a8a935f1df9 100644 --- a/src/config/user_config.hpp +++ b/src/config/user_config.hpp @@ -970,10 +970,13 @@ namespace UserConfigParams &m_graphics_quality, "Easter Bunny Ears: 0 use current date, 1 always on, 2 always off")); PARAM_PREFIX BoolUserConfigParam m_animated_characters - PARAM_DEFAULT( BoolUserConfigParam(true, - "animated-characters", &m_graphics_quality, + PARAM_DEFAULT( BoolUserConfigParam(true, "animated-characters", &m_graphics_quality, "Whether to display animated characters") ); + PARAM_PREFIX IntUserConfigParam m_kart_lights + PARAM_DEFAULT( IntUserConfigParam(1, "kart-lights", &m_graphics_quality, + "Kart lights: 0 = off, 1 = human only, 2 = all karts") ); + PARAM_PREFIX IntUserConfigParam m_geometry_level PARAM_DEFAULT( IntUserConfigParam(2, "geometry-level", &m_graphics_quality, diff --git a/src/karts/kart_model.cpp b/src/karts/kart_model.cpp index 480887b8a19..a010508e404 100644 --- a/src/karts/kart_model.cpp +++ b/src/karts/kart_model.cpp @@ -632,7 +632,8 @@ scene::ISceneNode* KartModel::attachModel(bool animated_models, bool human_playe } else { - if (!human_player || !supports_light) + if ((UserConfigParams::m_kart_lights == 0) || !supports_light || + (UserConfigParams::m_kart_lights == 1 && !human_player)) continue; obj.setLight(parent); node = obj.getLightNode(); diff --git a/src/states_screens/dialogs/custom_video_settings.cpp b/src/states_screens/dialogs/custom_video_settings.cpp index 681305544bb..78a7d2d5a32 100644 --- a/src/states_screens/dialogs/custom_video_settings.cpp +++ b/src/states_screens/dialogs/custom_video_settings.cpp @@ -70,18 +70,30 @@ void CustomVideoSettingsDialog::beforeAddingWidgets() particles_effects->addLabel(_("Enabled")); particles_effects->setValue(UserConfigParams::m_particles_effects); + SpinnerWidget* kart_lights = getWidget("kart_lights"); + //I18N: No kart headlights + kart_lights->addLabel(_("Disabled")); + //I18N: Kart headlights for human players only + kart_lights->addLabel(_("Player kart only")); + //I18N: Kart headlights enabled for all karts + kart_lights->addLabel(_("All karts")); + // TODO: ideally, we should turn this off and show the value as disabled when + // dynamic lights are off. However, because a full UI rework is coming, + // there is little point in doing it now and redoing it later. + kart_lights->setValue(UserConfigParams::m_kart_lights); + SpinnerWidget* geometry_level = getWidget("geometry_detail"); - //I18N: Geometry level disabled : lowest level, Level-of-Details distances are very low + //I18N: Geometry level very low : lowest level, Level-of-Details distances are very low geometry_level->addLabel(_("Very Low")); - //I18N: Geometry level low : everything is displayed, Level-of-Details distances are low + //I18N: Geometry level low : Level-of-Details distances are low geometry_level->addLabel(_("Low")); - //I18N: Geometry level medium : everything is displayed, Level-of-Details distances are medium + //I18N: Geometry level medium : Level-of-Details distances are medium geometry_level->addLabel(_("Medium")); - //I18N: Geometry level high : everything is displayed, Level-of-Details distances are high + //I18N: Geometry level high : Level-of-Details distances are high geometry_level->addLabel(_("High")); - //I18N: Geometry level very high : everything is displayed, Level-of-Details distances are very high + //I18N: Geometry level very high : Level-of-Details distances are very high geometry_level->addLabel(_("Very High")); - //I18N: Geometry level ultra : everything is displayed, Level-of-Details distances are extremely high + //I18N: Geometry level ultra : Level-of-Details distances are extremely high geometry_level->addLabel(_("Ultra")); geometry_level->setValue(UserConfigParams::m_geometry_level); @@ -249,8 +261,11 @@ GUIEngine::EventPropagation CustomVideoSettingsDialog::processEvent(const std::s UserConfigParams::m_animated_characters = getWidget("animated_characters")->getState(); + UserConfigParams::m_kart_lights = + getWidget("kart_lights")->getValue(); + UserConfigParams::m_geometry_level = - getWidget("geometry_detail")->getValue();; + getWidget("geometry_detail")->getValue(); int quality = getWidget("image_quality")->getValue(); std::string rd = StringUtils::wideToUtf8( From 4c8332ab3a7dc5daec7f3229215d0a043e597f87 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Fri, 14 Nov 2025 21:36:01 +0100 Subject: [PATCH 157/188] Have both start and end frames for the happy and hit animations This matches the format adopted in the recent update of the Blender exporter scripts. Having both a start and an end allows for higher quality animations. --- src/karts/kart_model.cpp | 6 ++++-- src/karts/kart_model.hpp | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/karts/kart_model.cpp b/src/karts/kart_model.cpp index a010508e404..014c0f27c05 100644 --- a/src/karts/kart_model.cpp +++ b/src/karts/kart_model.cpp @@ -242,8 +242,10 @@ void KartModel::loadInfo(const XMLNode &node) animation_node->get("bump-left", &m_animation_frame[AF_BUMP_LEFT] ); animation_node->get("bump-right", &m_animation_frame[AF_BUMP_RIGHT] ); animation_node->get("bump-back", &m_animation_frame[AF_BUMP_BACK] ); - animation_node->get("happy", &m_animation_frame[AF_HAPPY] ); - animation_node->get("hit", &m_animation_frame[AF_HIT] ); + animation_node->get("happy-start", &m_animation_frame[AF_HAPPY_START] ); + animation_node->get("happy-end", &m_animation_frame[AF_HAPPY_END] ); + animation_node->get("hit-start", &m_animation_frame[AF_HIT_START] ); + animation_node->get("hit-end", &m_animation_frame[AF_HIT_END] ); animation_node->get("false-accel-start", &m_animation_frame[AF_FALSE_ACCEL_START] ); animation_node->get("false-accel-end", &m_animation_frame[AF_FALSE_ACCEL_END] ); diff --git a/src/karts/kart_model.hpp b/src/karts/kart_model.hpp index 964c45bc95f..49e93d94d0d 100644 --- a/src/karts/kart_model.hpp +++ b/src/karts/kart_model.hpp @@ -273,8 +273,10 @@ class KartModel : public scene::IAnimationEndCallBack, public NoCopy AF_BUMP_LEFT, // Played if the kart hits something in front AF_BUMP_RIGHT, // Played if the kart hits something in front AF_BUMP_BACK, // Played if the kart hits something in front - AF_HAPPY, // Played when hitting a rival (or some overtaking) - AF_HIT, // Played when being hit + AF_HAPPY_START, // Start of the happy animation, played when hitting a rival (or overtaking) + AF_HAPPY_END, // Last frame of the happy animation + AF_HIT_START, // Start of the hit animation, played when being hit + AF_HIT_END, // Last frame of the hit animation AF_FALSE_ACCEL_START, // Played if there is a penalty for early accel AF_PLACEHOLDER, // A placeholder so that the assumption end = (start + 2) holds From 547af3f4b6c83d4d98d6e9fa03298e0b5d2ef6ab Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Fri, 14 Nov 2025 22:26:48 +0100 Subject: [PATCH 158/188] Explicitely associate AF types with their end or loop start types The code assuming that an animation type + 1 is a matching loop start type or that an animation type + 2 is a matching end start type has been replaced. This removes the need for placeholder AnimationFrameType and ensure animations cannot pick an incorrect AnimationFrameType as end or loop start marker. --- src/karts/kart_model.cpp | 21 ++++++----------- src/karts/kart_model.hpp | 51 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 18 deletions(-) diff --git a/src/karts/kart_model.cpp b/src/karts/kart_model.cpp index 014c0f27c05..84ab18cbdf4 100644 --- a/src/karts/kart_model.cpp +++ b/src/karts/kart_model.cpp @@ -264,7 +264,7 @@ void KartModel::loadInfo(const XMLNode &node) std::vector missing_animations; for (unsigned int i = AF_BEGIN + 1; i <= AF_END; i++) { - if (i != AF_PLACEHOLDER && m_animation_frame[i] == -1) + if (m_animation_frame[i] == -1) missing_animations.push_back(i); } if (!missing_animations.empty()) @@ -1196,8 +1196,7 @@ void KartModel::setAnimation(AnimationFrameType type, bool no_loop) m_kart_model->setAnimation(AF_DEFAULT); } }; - AnimationFrameType end = (type == AF_WIN_START) ? AF_WIN_LOOP_END - : AF_LOSE_LOOP_END; + AnimationFrameType end = getEndFrameType(type); if (m_animation_frame[end] == -1) return; // If the end isn't correctly defined, don't play the animation AnimationFrameType to_straight = (type == AF_WIN_START) ? AF_WIN_TO_STRAIGHT @@ -1246,11 +1245,9 @@ void KartModel::setAnimation(AnimationFrameType type, bool no_loop) } else if(m_animation_frame[type]>-1) { - // 'type' is the start frame of the animation, type + 1 the frame - // to begin the loop with, type + 2 to end the frame with - AnimationFrameType end = (AnimationFrameType)(type+2); + AnimationFrameType end = getEndFrameType(type); if(m_animation_frame[end]==-1) - end = (AnimationFrameType)((int)end-1); + return; // If the end isn't correctly defined, don't play the animation m_animated_node->setAnimationSpeed(m_animation_speed); m_animated_node->setFrameLoop(m_animation_frame[type], m_animation_frame[end] ); @@ -1273,8 +1270,7 @@ void KartModel::setAnimation(AnimationFrameType type, bool no_loop) */ void KartModel::setAnimationLoop(AnimationFrameType start) { - // loop-start + 1 corresponds to the matching loop-end type - AnimationFrameType end = (AnimationFrameType)(start + 1); + AnimationFrameType end = getEndFrameType(start); if(m_animation_frame[start] > -1 && m_animation_frame[end] > -1) { @@ -1299,11 +1295,8 @@ void KartModel::OnAnimationEnd(scene::IAnimatedMeshSceneNode *node) // It should only be called for the animated node of this kart_model assert(node == m_animated_node); - // 'type' is the start frame of the animation, type + 1 the frame - // to begin the loop with, type + 2 to end the frame with - AnimationFrameType start = (AnimationFrameType)(m_current_animation + 1); - - setAnimationLoop(start); + AnimationFrameType loop_start = getLoopStartFrameType(m_current_animation); + setAnimationLoop(loop_start); m_animated_node->setAnimationEndCallback(NULL); } // OnAnimationEnd diff --git a/src/karts/kart_model.hpp b/src/karts/kart_model.hpp index 49e93d94d0d..0e275f7e9da 100644 --- a/src/karts/kart_model.hpp +++ b/src/karts/kart_model.hpp @@ -270,16 +270,15 @@ class KartModel : public scene::IAnimationEndCallBack, public NoCopy AF_SELECTION_LOOP_END, // End of the kart selection screen loop AF_BUMP_FRONT, // Played if the kart hits something in front - AF_BUMP_LEFT, // Played if the kart hits something in front - AF_BUMP_RIGHT, // Played if the kart hits something in front - AF_BUMP_BACK, // Played if the kart hits something in front + AF_BUMP_LEFT, // Played if the kart hits something to the left + AF_BUMP_RIGHT, // Played if the kart hits something to the right + AF_BUMP_BACK, // Played if the kart hits something to the back AF_HAPPY_START, // Start of the happy animation, played when hitting a rival (or overtaking) AF_HAPPY_END, // Last frame of the happy animation AF_HIT_START, // Start of the hit animation, played when being hit AF_HIT_END, // Last frame of the hit animation AF_FALSE_ACCEL_START, // Played if there is a penalty for early accel - AF_PLACEHOLDER, // A placeholder so that the assumption end = (start + 2) holds AF_FALSE_ACCEL_END, // Played if there is a penalty for early accel AF_END=AF_FALSE_ACCEL_END, // Last animation frame @@ -464,6 +463,50 @@ class KartModel : public scene::IAnimationEndCallBack, public NoCopy * to use. */ int getBaseFrame() const { return m_animation_frame[AF_STRAIGHT]; } // ------------------------------------------------------------------------ + /* Returns the AnimationFrameType which corresponds to the end of + * the animation passed as parameter. + * Returns AF_BEGIN if there is no valid end frame type. */ + AnimationFrameType getEndFrameType(AnimationFrameType f) + { + if (f == AF_WIN_START) return AF_WIN_LOOP_END; + if (f == AF_WIN_LOOP_START) return AF_WIN_LOOP_END; + if (f == AF_NEUTRAL_START) return AF_NEUTRAL_LOOP_END; + if (f == AF_NEUTRAL_LOOP_START) return AF_NEUTRAL_LOOP_END; + if (f == AF_LOSE_START) return AF_LOSE_LOOP_END; + if (f == AF_LOSE_LOOP_START) return AF_LOSE_LOOP_END; + if (f == AF_PODIUM_START) return AF_PODIUM_LOOP_END; + if (f == AF_PODIUM_LOOP_START) return AF_PODIUM_LOOP_END; + if (f == AF_JUMP_START) return AF_JUMP_LOOP_END; + if (f == AF_JUMP_LOOP_START) return AF_JUMP_LOOP_END; + if (f == AF_SELECTION_START) return AF_SELECTION_LOOP_END; + if (f == AF_SELECTION_LOOP_START) return AF_SELECTION_LOOP_END; + if (f == AF_HAPPY_START) return AF_HAPPY_END; + if (f == AF_HIT_START) return AF_HIT_END; + if (f == AF_FALSE_ACCEL_START) return AF_FALSE_ACCEL_END; + + // No valid end animation for this frame type + // m_animation_frame[AF_BEGIN] is legal to access but always -1. + return AF_BEGIN; + } // getEndFrameType + // ------------------------------------------------------------------------ + /* Returns the AnimationFrameType which corresponds to the start + * of the loop for the animation type passed as parameter. + * There is a valid result only if the parameter frame type serves + * as an introductory sequence to the loop. + * Returns AF_BEGIN if there is no valid loop start frame type. */ + AnimationFrameType getLoopStartFrameType(AnimationFrameType f) + { + if (f == AF_WIN_START) return AF_WIN_LOOP_START; + if (f == AF_NEUTRAL_START) return AF_NEUTRAL_LOOP_START; + if (f == AF_LOSE_START) return AF_LOSE_LOOP_START; + if (f == AF_PODIUM_START) return AF_PODIUM_LOOP_START; + if (f == AF_JUMP_START) return AF_JUMP_LOOP_START; + if (f == AF_SELECTION_START) return AF_SELECTION_LOOP_START; + + // No valid loop start animation for this frame type + return AF_BEGIN; + } // getLoopStartFrameType + // ------------------------------------------------------------------------ int getFrame(AnimationFrameType f) const { return m_animation_frame[f]; } // ------------------------------------------------------------------------ float getAnimationSpeed() const { return m_animation_speed; } From f4414b2220ede46ef943c156d4827c3e8ca9aaa5 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sat, 15 Nov 2025 19:25:32 +0100 Subject: [PATCH 159/188] Prevent rescues from being triggered before the race starts Pressing the rescue key before start was initiating a rescue animation. This regression was likely introduced with the recent general rescue code improvement in 509f447. --- src/karts/kart.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 977acefac8a..745b4442bd5 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -2964,6 +2964,10 @@ void Kart::crashed(const Material *m, const Vec3 &normal) * World takes care of moving the kart to its rescue position. */ void Kart::applyRescue(bool auto_rescue) { + // Don't allow triggering a rescue before the race starts + if (World::getWorld()->getTicksSinceStart() <= 0) + return; + RescueAnimation::create(this, /*is_auto_rescue*/ auto_rescue); // Reset some gameplay variables such as attachments From 199b4dec16936bbcbe4be1730f7bea6b1c74019f Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sat, 15 Nov 2025 23:11:11 +0100 Subject: [PATCH 160/188] Improve the explosion code - Add a new KartUtils namespace for some functions operating on karts that don't need an object - Add a new createExplosion() function which handles all the key checks before creating an ExplosionAnimation, and which handles various updates that are needed if the explosion is successful (registering the hit with the world, applying controller rumble) - in ExplosionAnimation, simplify drastically the create function. createExplosion() handles all the checks and should always be used, except when triggering the animation explosion from the debug menu. - In Flyable, remove all the direct handling of the explosion now done in createExplosion() - in Physics, use createExplosion() - In Attachment, add a new bombExplode() function that calls createExplosion() and handles the extra graphical effect, removing duplicated code. - In Kart, call createExplosion() - Minor improvements to comment and code-style There is currently a weird issue with the rumble: if rumble was already triggered in a race (by hitting a wall for example), explosions will properly trigger the rumble effect. But otherwise, no rumble is observed. --- src/items/attachment.cpp | 62 +++++-------- src/items/attachment.hpp | 2 + src/items/cake.cpp | 1 + src/items/flyable.cpp | 30 ++----- src/karts/explosion_animation.cpp | 50 +---------- src/karts/explosion_animation.hpp | 5 +- src/karts/kart.cpp | 9 +- src/karts/kart_utils.cpp | 87 +++++++++++++++++++ src/karts/kart_utils.hpp | 30 +++++++ src/physics/physics.cpp | 14 +-- src/states_screens/options/options_common.cpp | 2 +- src/utils/debug.cpp | 3 +- 12 files changed, 165 insertions(+), 130 deletions(-) create mode 100644 src/karts/kart_utils.cpp create mode 100644 src/karts/kart_utils.hpp diff --git a/src/items/attachment.cpp b/src/items/attachment.cpp index c3797e74f68..38469b139b7 100644 --- a/src/items/attachment.cpp +++ b/src/items/attachment.cpp @@ -33,6 +33,7 @@ #include "items/projectile_manager.hpp" #include "items/swatter.hpp" #include "karts/kart.hpp" +#include "karts/kart_utils.hpp" #include "karts/controller/controller.hpp" #include "karts/explosion_animation.hpp" #include "karts/kart_properties.hpp" @@ -282,7 +283,7 @@ void Attachment::hitBanana(ItemState *item_state) if (RaceManager::get()->isLinearRaceMode()) PlayerManager::increaseAchievement(AchievementsStatus::BANANA_1RACE, 1); } - //Shield effect: protect but is used + // Shield effect: protect but is used if(m_kart->isShielded()) { m_kart->decreaseShieldTime(); @@ -295,9 +296,7 @@ void Attachment::hitBanana(ItemState *item_state) if (RaceManager::get()->isBattleMode()) { - World::getWorld()->kartHit(m_kart->getWorldKartId()); - if (m_kart->getKartAnimation() == NULL) - ExplosionAnimation::create(m_kart); + KartUtils::createExplosion(m_kart); return; } @@ -312,30 +311,15 @@ void Attachment::hitBanana(ItemState *item_state) case ATTACH_BOMB: { add_a_new_item = false; - if (!GUIEngine::isNoGraphics() && !RewindManager::get()->isRewinding()) - { - HitEffect* he = new Explosion(m_kart->getXYZ(), "explosion", - "explosion_bomb.xml"); - // Rumble! - Controller* controller = m_kart->getController(); - if (controller && controller->isLocalPlayerController()) - { - controller->rumble(0, 0.8f, 500); - } - if (m_kart->getController()->isLocalPlayerController()) - he->setLocalPlayerKartHit(); - ProjectileManager::get()->addHitEffect(he); - } - if (m_kart->getKartAnimation() == NULL) - ExplosionAnimation::create(m_kart); + bombExplode(); clear(); new_attachment = AttachmentType(ticks % 3); + // Disable the banana on which the kart just is for more than the // default time. This is necessary to avoid that a kart lands on the // same banana again once the explosion animation is finished, giving // the kart the same penalty twice. - int ticks = - std::max(item_state->getTicksTillReturn(), + int ticks = std::max(item_state->getTicksTillReturn(), stk_config->time2Ticks(kp->getExplosionDuration() + 2.0f)); item_state->setTicksTillReturn(ticks); break; @@ -528,24 +512,7 @@ void Attachment::update(int ticks) { m_initial_speed = 0; if (m_ticks_left <= 0) - { - if (!GUIEngine::isNoGraphics() && !RewindManager::get()->isRewinding()) - { - HitEffect* he = new Explosion(m_kart->getXYZ(), "explosion", - "explosion_bomb.xml"); - // Rumble! - Controller* controller = m_kart->getController(); - if (controller && controller->isLocalPlayerController()) - { - controller->rumble(0, 0.8f, 500); - } - if (m_kart->getController()->isLocalPlayerController()) - he->setLocalPlayerKartHit(); - ProjectileManager::get()->addHitEffect(he); - } - if (m_kart->getKartAnimation() == NULL) - ExplosionAnimation::create(m_kart); - } + bombExplode(); break; } case ATTACH_BUBBLEGUM_SHIELD: @@ -734,6 +701,21 @@ float Attachment::weightAdjust() const : 0.0f; } // weightAdjust +// ---------------------------------------------------------------------------- +/** Creates an explosion animation and explosion graphics. The explosion + * animation takes care of rumble and registering the hit with world. */ +void Attachment::bombExplode() +{ + KartUtils::createExplosion(m_kart); + if (!GUIEngine::isNoGraphics() && !RewindManager::get()->isRewinding()) + { + HitEffect* he = new Explosion(m_kart->getXYZ(), "explosion", "explosion_bomb.xml"); + if (m_kart->getController()->isLocalPlayerController()) + he->setLocalPlayerKartHit(); + ProjectileManager::get()->addHitEffect(he); + } +} // bombExplode + // ---------------------------------------------------------------------------- /** Performs all actions necessary for a gum shield to leave a ground gum behind. * The expiration of the gum shield is not managed here. diff --git a/src/items/attachment.hpp b/src/items/attachment.hpp index 8fb97bd8fcb..41b16ed9af9 100644 --- a/src/items/attachment.hpp +++ b/src/items/attachment.hpp @@ -115,6 +115,8 @@ class Attachment: public NoCopy /** Sound for exploding bubble gum shield */ SFXBase *m_bubble_explode_sound; + void bombExplode(); + public: Attachment(Kart* kart); ~Attachment(); diff --git a/src/items/cake.cpp b/src/items/cake.cpp index 5c3024f64ec..4165c22fac1 100644 --- a/src/items/cake.cpp +++ b/src/items/cake.cpp @@ -69,6 +69,7 @@ bool Cake::hit(Kart* kart, PhysicalObject* obj) if(kart && kart->isShielded()) { kart->decreaseShieldTime(); + // FIXME This is inconsistent with bowling.cpp handling of shielded targets return false; //Not sure if a shield hit is a real hit. } if (!m_mini) diff --git a/src/items/flyable.cpp b/src/items/flyable.cpp index fdc303c587d..bb14e132648 100644 --- a/src/items/flyable.cpp +++ b/src/items/flyable.cpp @@ -37,6 +37,7 @@ #include "io/xml_node.hpp" #include "items/projectile_manager.hpp" #include "karts/kart.hpp" +#include "karts/kart_utils.hpp" #include "karts/cannon_animation.hpp" #include "karts/controller/controller.hpp" #include "karts/explosion_animation.hpp" @@ -567,6 +568,7 @@ bool Flyable::hit(Kart *kart_hit, PhysicalObject* object) * \param object If non-NULL a physical item that was hit directly. * \param secondary_hits True if items that are not directly hit should * also be affected. + * \param indirect_damage If true, a direct hit will do indirect hit damages */ void Flyable::explode(Kart *kart_hit, PhysicalObject *object, bool secondary_hits, bool indirect_damage) @@ -592,33 +594,13 @@ void Flyable::explode(Kart *kart_hit, PhysicalObject *object, // Handle the actual explosion. The kart that fired a flyable will // only be affected if it's a direct hit. This allows karts to use - // rockets on short distance. - if( (m_owner!=kart || m_owner==kart_hit) && !kart->getKartAnimation()) + // cakes on short distance. + if( (m_owner != kart || m_owner == kart_hit) && !kart->getKartAnimation()) { // The explosion animation will register itself with the kart // and will free it later. - ExplosionAnimation::create(kart, getXYZ(), indirect_damage ? false : kart==kart_hit); - if (kart == kart_hit) - { - world->kartHit(kart->getWorldKartId(), - m_owner->getWorldKartId()); - - if (m_owner->getController()->canGetAchievements()) - { - if (m_owner->getWorldKartId() != kart->getWorldKartId()) - PlayerManager::addKartHit(kart->getWorldKartId()); - PlayerManager::increaseAchievement(AchievementsStatus::ALL_HITS, 1); - if (RaceManager::get()->isLinearRaceMode()) - PlayerManager::increaseAchievement(AchievementsStatus::ALL_HITS_1RACE, 1); - } - - // Rumble! - Controller* controller = kart->getController(); - if (controller && controller->isLocalPlayerController()) - { - controller->rumble(0, 0.8f, 500); - } - } + bool direct_hit = indirect_damage ? false : kart == kart_hit; + KartUtils::createExplosion(kart, getXYZ(), direct_hit, m_owner); } } Track::getCurrentTrack()->handleExplosion(getXYZ(), object,secondary_hits); diff --git a/src/karts/explosion_animation.cpp b/src/karts/explosion_animation.cpp index 0df397d0692..f2c948ced0c 100644 --- a/src/karts/explosion_animation.cpp +++ b/src/karts/explosion_animation.cpp @@ -35,60 +35,18 @@ #include -/** A static create function that does only create an explosion if - * the explosion happens to be close enough to affect the kart. - * Otherwise, NULL is returned. +/** A static create function that creates an explosion animation * \param kart The kart that is exploded. - * \param pos The position where the explosion happened. * \param direct_hit If the kart was hit directly. */ -ExplosionAnimation *ExplosionAnimation::create(Kart *kart, - const Vec3 &pos, - bool direct_hit) +ExplosionAnimation *ExplosionAnimation::create(Kart *kart, bool direct_hit) { - // When goal phase is happening karts is made stationary, so no animation - // will be created - if (kart->isInvulnerable() || World::getWorld()->isGoalPhase()) - return NULL; - - float r = kart->getKartProperties()->getExplosionRadius(); - - // Ignore explosion that are too far away. - if(!direct_hit && pos.distance2(kart->getXYZ())>r*r) return NULL; - - if(kart->isShielded()) - { - kart->decreaseShieldTime(); - return NULL; - } - - if (RaceManager::get()->isFollowMode()) - { - FollowTheLeaderRace *ftl_world = - dynamic_cast(World::getWorld()); - if(ftl_world->isLeader(kart->getWorldKartId())) - ftl_world->leaderHit(); - } + if (kart->getKartAnimation() != nullptr) + return nullptr; return new ExplosionAnimation(kart, direct_hit); } // create -// ---------------------------------------------------------------------------- -/** A static create function that does only create an explosion if - * the explosion happens to be close enough to affect the kart. - * Otherwise, NULL is returned. */ -ExplosionAnimation *ExplosionAnimation::create(Kart *kart) -{ - if (kart->isInvulnerable() || World::getWorld()->isGoalPhase()) - return NULL; - else if (kart->isShielded()) - { - kart->decreaseShieldTime(); - return NULL; - } - return new ExplosionAnimation(kart, /*direct hit*/true); -} // create - // ---------------------------------------------------------------------------- ExplosionAnimation::ExplosionAnimation(Kart* kart, bool direct_hit) : AbstractKartAnimation(kart, "ExplosionAnimation") diff --git a/src/karts/explosion_animation.hpp b/src/karts/explosion_animation.hpp index 8cea2c63323..6dc0e9a80c6 100644 --- a/src/karts/explosion_animation.hpp +++ b/src/karts/explosion_animation.hpp @@ -74,10 +74,7 @@ friend class KartRewinder; ExplosionAnimation(Kart* kart, bool direct_hit); public: // ------------------------------------------------------------------------ - static ExplosionAnimation *create(Kart* kart, const Vec3 &pos, - bool direct_hit); - // ------------------------------------------------------------------------ - static ExplosionAnimation *create(Kart *kart); + static ExplosionAnimation *create(Kart* kart, bool direct_hit); // ------------------------------------------------------------------------ virtual ~ExplosionAnimation(); // ------------------------------------------------------------------------ diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 745b4442bd5..e15f3d1a0e8 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -58,6 +58,7 @@ #include "karts/kart_properties.hpp" #include "karts/kart_properties_manager.hpp" #include "karts/kart_rewinder.hpp" +#include "karts/kart_utils.hpp" #include "karts/max_speed.hpp" #include "karts/official_karts.hpp" #include "karts/rescue_animation.hpp" @@ -1488,6 +1489,8 @@ float Kart::getActualWheelForce() */ bool Kart::isOnGround() const { + // getKartAnimation refers to KartAnimation objects like the explode or rescue + // animations, not to animations of the kart model like the win animation. return ((int)m_vehicle->getNumWheelsOnGround() == m_vehicle->getNumWheels() && !getKartAnimation()); } // isOnGround @@ -2130,8 +2133,7 @@ void Kart::update(int ticks) if (!GUIEngine::isNoGraphics() && !has_animation_before) { - HitEffect *effect = new Explosion(getXYZ(), "jump", - "jump_explosion.xml"); + HitEffect *effect = new Explosion(getXYZ(), "jump", "jump_explosion.xml"); ProjectileManager::get()->addHitEffect(effect); } } @@ -2253,7 +2255,8 @@ bool Kart::setSquash(float time, float slowdown) if(m_attachment->getType()==Attachment::ATTACH_BOMB && time>0) { - ExplosionAnimation::create(this); + // TODO : check how the world kart hit notification should be handled + KartUtils::createExplosion(this); return true; } diff --git a/src/karts/kart_utils.cpp b/src/karts/kart_utils.cpp new file mode 100644 index 00000000000..257a8562221 --- /dev/null +++ b/src/karts/kart_utils.cpp @@ -0,0 +1,87 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2025 Alayan +// +// 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 "karts/kart_utils.hpp" + +#include "achievements/achievements_status.hpp" +#include "config/player_manager.hpp" +#include "karts/explosion_animation.hpp" +#include "karts/kart_properties.hpp" +#include "karts/controller/controller.hpp" +#include "modes/follow_the_leader.hpp" +#include "modes/world.hpp" + +namespace KartUtils +{ + void createExplosion(Kart *kart, const Vec3 &pos, bool direct_hit, Kart *author) + { + World *world = World::getWorld(); + // No explosion on invulnerable karts or during the goal phase + if (kart->isInvulnerable() || world->isGoalPhase()) + return; + + // Ignore explosions that are too far away. + float r = kart->getKartProperties()->getExplosionRadius(); + if(!direct_hit && pos.distance2(kart->getXYZ()) > r*r) + return; + + if(kart->isShielded()) + { + kart->decreaseShieldTime(); + return; + } + + // If the kart is already being exploded or rescued, do nothing + if (kart->getKartAnimation() != nullptr) + return; + + ExplosionAnimation::create(kart, direct_hit); + + // Rumble! + // TODO : different rumble strength on direct_hit? + Controller *controller = kart->getController(); + if (controller && controller->isLocalPlayerController()) + controller->rumble(0, 0.8f, 500); + + if (RaceManager::get()->isFollowMode()) + { + FollowTheLeaderRace *ftl_world = dynamic_cast(world); + if(ftl_world->isLeader(kart->getWorldKartId())) + ftl_world->leaderHit(); + } + + // Register the hit with the world (for example for battle scoring) + if (direct_hit && author != nullptr) + { + world->kartHit(kart->getWorldKartId(), author->getWorldKartId()); + + if (author->getController()->canGetAchievements()) + { + if (author->getWorldKartId() != kart->getWorldKartId()) + PlayerManager::addKartHit(kart->getWorldKartId()); + PlayerManager::increaseAchievement(AchievementsStatus::ALL_HITS, 1); + if (RaceManager::get()->isLinearRaceMode()) + PlayerManager::increaseAchievement(AchievementsStatus::ALL_HITS_1RACE, 1); + } + } + else if (direct_hit) + { + world->kartHit(kart->getWorldKartId()); + } + } // createExplosion +} \ No newline at end of file diff --git a/src/karts/kart_utils.hpp b/src/karts/kart_utils.hpp new file mode 100644 index 00000000000..6741b21c791 --- /dev/null +++ b/src/karts/kart_utils.hpp @@ -0,0 +1,30 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2025 Alayan +// +// 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_KART_UTILS_HPP__ +#define __HEADER_KART_UTILS_HPP__ + +#include "karts/kart.hpp" + +namespace KartUtils +{ + void createExplosion(Kart *kart, const Vec3 &pos = Vec3(0,0,0), + bool direct_hit = true, Kart *author = nullptr); +} + +#endif \ No newline at end of file diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index 7c6db840829..92e383a84dd 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -23,6 +23,7 @@ #include "config/player_profile.hpp" #include "config/user_config.hpp" #include "karts/kart.hpp" +#include "karts/kart_utils.hpp" #include "graphics/irr_driver.hpp" #include "graphics/stars.hpp" #include "items/flyable.hpp" @@ -31,7 +32,6 @@ #include "modes/soccer_world.hpp" #include "modes/world.hpp" #include "network/network_config.hpp" -#include "karts/explosion_animation.hpp" #include "physics/btKart.hpp" #include "physics/irr_debug_drawer.hpp" #include "physics/physical_object.hpp" @@ -269,11 +269,7 @@ void Physics::update(int ticks) } else if (obj->isExplodeKartObject()) { - ExplosionAnimation::create(kart); - if (kart->getKartAnimation() != NULL) - { - World::getWorld()->kartHit(kart->getWorldKartId()); - } + KartUtils::createExplosion(kart); } else if (obj->isFlattenKartObject()) { @@ -307,11 +303,7 @@ void Physics::update(int ticks) else if (anim->isExplodeKartObject()) { Kart *kart = p->getUserPointer(1)->getPointerKart(); - ExplosionAnimation::create(kart); - if (kart->getKartAnimation() != NULL) - { - World::getWorld()->kartHit(kart->getWorldKartId()); - } + KartUtils::createExplosion(kart); } else if (anim->isFlattenKartObject()) { diff --git a/src/states_screens/options/options_common.cpp b/src/states_screens/options/options_common.cpp index 104fedc09ed..5db987d1638 100644 --- a/src/states_screens/options/options_common.cpp +++ b/src/states_screens/options/options_common.cpp @@ -42,7 +42,7 @@ namespace OptionsCommon screen = OptionsScreenLanguage::getInstance(); if(screen) StateManager::get()->replaceTopMostScreen(screen); - } + } // switchTab // In the in-game pause options, disable the players and language tabs void setTabStatus() diff --git a/src/utils/debug.cpp b/src/utils/debug.cpp index dd9d735ef2b..2a6694af773 100644 --- a/src/utils/debug.cpp +++ b/src/utils/debug.cpp @@ -706,7 +706,8 @@ bool handleContextMenuAction(s32 cmd_id) for (unsigned int i = 0; i < RaceManager::get()->getNumLocalPlayers(); i++) { Kart* kart = world->getLocalPlayerKart(i); - ExplosionAnimation::create(kart, kart->getXYZ(), true); + // Most conditions that might usually prevent an explosion are bypassed + ExplosionAnimation::create(kart, true); } break; case DEBUG_ATTACHMENT_NOTHING: From 9152b1f24bf5577fe3d59ad1f7795ca93a02a388 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 16 Nov 2025 13:08:37 +0100 Subject: [PATCH 161/188] More explosion code improvement - Do the CTF powerup reset in createExplosion() - Use isCTFMode() when appropriate - Use a 'small' parameter for explosions that are direct hits but should give a short explosion period instead of passing them as indirect hits. This allows things that rely on direct hits, such as battle mode scoring and the upcoming happy animation to still work. - Move the restoreData() function next to saveState(). As both functions needs to stay in sync, it's easier to cross-reference them when they are next to each other. - Update the networking protocol to transmit the 'small' parameter. --- src/karts/explosion_animation.cpp | 95 ++++++++++++++----------------- src/karts/explosion_animation.hpp | 7 ++- src/karts/kart_utils.cpp | 31 +++++----- src/karts/kart_utils.hpp | 2 +- src/utils/debug.cpp | 2 +- 5 files changed, 66 insertions(+), 71 deletions(-) diff --git a/src/karts/explosion_animation.cpp b/src/karts/explosion_animation.cpp index f2c948ced0c..656041de5a8 100644 --- a/src/karts/explosion_animation.cpp +++ b/src/karts/explosion_animation.cpp @@ -23,7 +23,6 @@ #include "graphics/stars.hpp" #include "guiengine/engine.hpp" #include "items/attachment.hpp" -#include "items/powerup.hpp" #include "karts/kart.hpp" #include "karts/kart_properties.hpp" #include "modes/follow_the_leader.hpp" @@ -39,24 +38,22 @@ * \param kart The kart that is exploded. * \param direct_hit If the kart was hit directly. */ -ExplosionAnimation *ExplosionAnimation::create(Kart *kart, bool direct_hit) +ExplosionAnimation *ExplosionAnimation::create(Kart *kart, bool direct_hit, bool small) { if (kart->getKartAnimation() != nullptr) return nullptr; - return new ExplosionAnimation(kart, direct_hit); + return new ExplosionAnimation(kart, direct_hit, small); } // create // ---------------------------------------------------------------------------- -ExplosionAnimation::ExplosionAnimation(Kart* kart, bool direct_hit) +ExplosionAnimation::ExplosionAnimation(Kart* kart, bool direct_hit, bool small) : AbstractKartAnimation(kart, "ExplosionAnimation") { memset(m_reset_trans_compressed, 0, 16); Vec3 normal = m_created_transform.getBasis().getColumn(1).normalized(); // Put the kart back to its own flag base like rescue if direct hit in CTF - bool reset = RaceManager::get()->getMinorMode() == - RaceManager::MINOR_MODE_CAPTURE_THE_FLAG && direct_hit; - if (reset) + if (direct_hit && RaceManager::get()->isCTFMode()) { btTransform prev_trans = m_kart->getTrans(); World::getWorld()->moveKartAfterRescue(m_kart); @@ -65,11 +62,11 @@ ExplosionAnimation::ExplosionAnimation(Kart* kart, bool direct_hit) m_kart->setTrans(prev_trans); MiniGLM::compressbtTransform(reset_trans, m_reset_trans_compressed); - init(direct_hit, normal, reset_trans); + init(direct_hit, small, normal, reset_trans); } else { - init(direct_hit, normal, + init(direct_hit, small, normal, btTransform(btQuaternion(0.0f, 0.0f, 0.0f, 1.0f))); } @@ -77,10 +74,6 @@ ExplosionAnimation::ExplosionAnimation(Kart* kart, bool direct_hit) m_kart->setInvulnerableTicks(stk_config->time2Ticks(t)); m_kart->playCustomSFX(SFXManager::CUSTOM_EXPLODE); m_kart->getAttachment()->clear(); - // Clear powerups when direct hit in CTF - // TODO: move this out of this code file - if (reset) - m_kart->getPowerup()->reset(); } // ExplosionAnimation //----------------------------------------------------------------------------- @@ -91,27 +84,6 @@ ExplosionAnimation::ExplosionAnimation(Kart* kart, BareNetworkString* b) restoreData(b); } // RescueAnimation -//----------------------------------------------------------------------------- -void ExplosionAnimation::restoreData(BareNetworkString* b) -{ - bool direct_hit = b->getUInt8() == 1; - Vec3 normal = m_created_transform.getBasis().getColumn(1).normalized(); - btTransform reset_transform = - btTransform(btQuaternion(0.0f, 0.0f, 0.0f, 1.0f)); - - if (RaceManager::get()->getMinorMode() == - RaceManager::MINOR_MODE_CAPTURE_THE_FLAG && direct_hit) - { - m_reset_trans_compressed[0] = b->getInt24(); - m_reset_trans_compressed[1] = b->getInt24(); - m_reset_trans_compressed[2] = b->getInt24(); - m_reset_trans_compressed[3] = b->getUInt32(); - reset_transform = - MiniGLM::decompressbtTransform(m_reset_trans_compressed); - } - init(direct_hit, normal, reset_transform); -} // restoreData - //----------------------------------------------------------------------------- ExplosionAnimation::~ExplosionAnimation() { @@ -138,27 +110,23 @@ ExplosionAnimation::~ExplosionAnimation() } // ~ExplosionAnimation // ---------------------------------------------------------------------------- -void ExplosionAnimation::init(bool direct_hit, const Vec3& normal, +void ExplosionAnimation::init(bool direct_hit, bool small, const Vec3& normal, const btTransform& reset_trans) { m_direct_hit = direct_hit; + m_small = small; m_reset_ticks = -1; float timer = m_kart->getKartProperties()->getExplosionDuration(); m_normal = normal; - // Non-direct hits will be only affected half as much. - if (!direct_hit) - { + // If the hit is small or indirect, the duration is halved + if (small || !direct_hit) timer *= 0.5f; - } // Put the kart back to its own flag base like rescue if direct hit in CTF - if (RaceManager::get()->getMinorMode() == - RaceManager::MINOR_MODE_CAPTURE_THE_FLAG && direct_hit) - { - m_reset_ticks = m_created_ticks + - stk_config->time2Ticks(timer * 0.8f); - } + if (RaceManager::get()->isCTFMode() && direct_hit) + m_reset_ticks = m_created_ticks + stk_config->time2Ticks(timer * 0.8f); + m_end_ticks = m_created_ticks + stk_config->time2Ticks(timer); if (m_reset_ticks != -1) @@ -166,13 +134,12 @@ void ExplosionAnimation::init(bool direct_hit, const Vec3& normal, else m_reset_trans = btTransform(btQuaternion(0.0f, 0.0f, 0.0f, 1.0f)); - // Half of the overall time is spent in raising, so only use - // half of the explosion time here. + // Half of the overall time is spent in raising, so only use half of the + // explosion time here. // Velocity after t seconds is: // v(t) = m_velocity + t*gravity - // Since v(explosion_time*0.5) = 0, the following forumla computes - // the right initial velocity for a kart to land back after - // the specified time. + // Since v(explosion_time*0.5) = 0, the following forumla computes the + // right initial velocity for a kart to land back after the specified time. m_velocity = 0.5f * timer * Track::getCurrentTrack()->getGravity(); // From moveable::updatePosition @@ -267,13 +234,35 @@ bool ExplosionAnimation::hasResetAlready() const World::getWorld()->getTicksSinceStart() > m_reset_ticks; } // update +//----------------------------------------------------------------------------- +void ExplosionAnimation::restoreData(BareNetworkString* b) +{ + uint8_t small_and_hit = b->getUInt8(); + bool direct_hit = (small_and_hit % 2) == 1; + bool small = small_and_hit >= 2; + Vec3 normal = m_created_transform.getBasis().getColumn(1).normalized(); + btTransform reset_transform = + btTransform(btQuaternion(0.0f, 0.0f, 0.0f, 1.0f)); + + if (RaceManager::get()->isCTFMode() && direct_hit) + { + m_reset_trans_compressed[0] = b->getInt24(); + m_reset_trans_compressed[1] = b->getInt24(); + m_reset_trans_compressed[2] = b->getInt24(); + m_reset_trans_compressed[3] = b->getUInt32(); + reset_transform = + MiniGLM::decompressbtTransform(m_reset_trans_compressed); + } + init(direct_hit, small, normal, reset_transform); +} // restoreData + // ---------------------------------------------------------------------------- void ExplosionAnimation::saveState(BareNetworkString* buffer) { AbstractKartAnimation::saveState(buffer); - buffer->addUInt8(m_direct_hit ? 1 : 0); - if (RaceManager::get()->getMinorMode() == - RaceManager::MINOR_MODE_CAPTURE_THE_FLAG && m_direct_hit) + int small_and_hit = (m_small ? 2 : 0) + (m_direct_hit ? 1 : 0); + buffer->addUInt8(small_and_hit); + if (RaceManager::get()->isCTFMode() && m_direct_hit) { buffer->addInt24(m_reset_trans_compressed[0]) .addInt24(m_reset_trans_compressed[1]) diff --git a/src/karts/explosion_animation.hpp b/src/karts/explosion_animation.hpp index 6dc0e9a80c6..e22542fe727 100644 --- a/src/karts/explosion_animation.hpp +++ b/src/karts/explosion_animation.hpp @@ -62,19 +62,20 @@ friend class KartRewinder; int m_reset_trans_compressed[4]; bool m_direct_hit; + bool m_small; // ------------------------------------------------------------------------ void restoreData(BareNetworkString* b); // ------------------------------------------------------------------------ - void init(bool direct_hit, const Vec3& normal, + void init(bool direct_hit, bool small, const Vec3& normal, const btTransform& reset_trans); // ------------------------------------------------------------------------ ExplosionAnimation(Kart* kart, BareNetworkString* buffer); // ------------------------------------------------------------------------ - ExplosionAnimation(Kart* kart, bool direct_hit); + ExplosionAnimation(Kart* kart, bool direct_hit, bool small); public: // ------------------------------------------------------------------------ - static ExplosionAnimation *create(Kart* kart, bool direct_hit); + static ExplosionAnimation *create(Kart* kart, bool direct_hit, bool small); // ------------------------------------------------------------------------ virtual ~ExplosionAnimation(); // ------------------------------------------------------------------------ diff --git a/src/karts/kart_utils.cpp b/src/karts/kart_utils.cpp index 257a8562221..4f35060e994 100644 --- a/src/karts/kart_utils.cpp +++ b/src/karts/kart_utils.cpp @@ -20,6 +20,7 @@ #include "achievements/achievements_status.hpp" #include "config/player_manager.hpp" +#include "items/powerup.hpp" #include "karts/explosion_animation.hpp" #include "karts/kart_properties.hpp" #include "karts/controller/controller.hpp" @@ -28,7 +29,7 @@ namespace KartUtils { - void createExplosion(Kart *kart, const Vec3 &pos, bool direct_hit, Kart *author) + void createExplosion(Kart *kart, bool small, const Vec3 &pos, bool direct_hit, Kart *author) { World *world = World::getWorld(); // No explosion on invulnerable karts or during the goal phase @@ -46,11 +47,11 @@ namespace KartUtils return; } - // If the kart is already being exploded or rescued, do nothing + // If the kart is already being exploded or rescued, or in a cannon, do nothing if (kart->getKartAnimation() != nullptr) return; - ExplosionAnimation::create(kart, direct_hit); + ExplosionAnimation::create(kart, direct_hit, small); // Rumble! // TODO : different rumble strength on direct_hit? @@ -65,12 +66,16 @@ namespace KartUtils ftl_world->leaderHit(); } - // Register the hit with the world (for example for battle scoring) - if (direct_hit && author != nullptr) + // Special actions when there is a direct hit + if (direct_hit) { - world->kartHit(kart->getWorldKartId(), author->getWorldKartId()); + // Register the hit with the world (for example for battle scoring) + if (author != nullptr) + world->kartHit(kart->getWorldKartId(), author->getWorldKartId()); + else + world->kartHit(kart->getWorldKartId()); - if (author->getController()->canGetAchievements()) + if (author != nullptr && author->getController()->canGetAchievements()) { if (author->getWorldKartId() != kart->getWorldKartId()) PlayerManager::addKartHit(kart->getWorldKartId()); @@ -78,10 +83,10 @@ namespace KartUtils if (RaceManager::get()->isLinearRaceMode()) PlayerManager::increaseAchievement(AchievementsStatus::ALL_HITS_1RACE, 1); } - } - else if (direct_hit) - { - world->kartHit(kart->getWorldKartId()); - } + + // Clear the powerups of the exploding kart in CTF + if (RaceManager::get()->isCTFMode()) + kart->getPowerup()->reset(); + } // direct_hit } // createExplosion -} \ No newline at end of file +} // namespace KartUtils \ No newline at end of file diff --git a/src/karts/kart_utils.hpp b/src/karts/kart_utils.hpp index 6741b21c791..1278667cbd6 100644 --- a/src/karts/kart_utils.hpp +++ b/src/karts/kart_utils.hpp @@ -23,7 +23,7 @@ namespace KartUtils { - void createExplosion(Kart *kart, const Vec3 &pos = Vec3(0,0,0), + void createExplosion(Kart *kart, bool small = false, const Vec3 &pos = Vec3(0,0,0), bool direct_hit = true, Kart *author = nullptr); } diff --git a/src/utils/debug.cpp b/src/utils/debug.cpp index 2a6694af773..62d763d4456 100644 --- a/src/utils/debug.cpp +++ b/src/utils/debug.cpp @@ -707,7 +707,7 @@ bool handleContextMenuAction(s32 cmd_id) { Kart* kart = world->getLocalPlayerKart(i); // Most conditions that might usually prevent an explosion are bypassed - ExplosionAnimation::create(kart, true); + ExplosionAnimation::create(kart, /* direct hit */ true, /* small */ false); } break; case DEBUG_ATTACHMENT_NOTHING: From 4cc6b06ffdb2176c5a07641ae9c13475a9440745 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 16 Nov 2025 13:58:46 +0100 Subject: [PATCH 162/188] Add happy and hurt animations for explosions - If a kart is the victim of an explosion, his kart model will display the hurt animation. - If a kart successfully registers a direct hit on an other kart, his kart model will display the happy animation. - Fix the Flyable call to createExplosion() and properly use the small parameter --- src/items/flyable.cpp | 13 ++++++------- src/items/flyable.hpp | 2 +- src/karts/kart_utils.cpp | 24 ++++++++++++++++++++---- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/items/flyable.cpp b/src/items/flyable.cpp index bb14e132648..2b666b1cdba 100644 --- a/src/items/flyable.cpp +++ b/src/items/flyable.cpp @@ -568,10 +568,10 @@ bool Flyable::hit(Kart *kart_hit, PhysicalObject* object) * \param object If non-NULL a physical item that was hit directly. * \param secondary_hits True if items that are not directly hit should * also be affected. - * \param indirect_damage If true, a direct hit will do indirect hit damages + * \param small_damage If true, a direct hit will do reduced (indirect hit) damage */ void Flyable::explode(Kart *kart_hit, PhysicalObject *object, - bool secondary_hits, bool indirect_damage) + bool secondary_hits, bool small_damage) { // Apply explosion effect // ---------------------- @@ -587,9 +587,9 @@ void Flyable::explode(Kart *kart_hit, PhysicalObject *object, if (kart->isGhostKart()) continue; - // If no secondary hits should be done, only hit the - // direct hit kart. - if(!secondary_hits && kart!=kart_hit) + // If no secondary hits should be done, only hit the directly hit kart. + bool direct_hit = (kart == kart_hit); + if(!secondary_hits && !direct_hit) continue; // Handle the actual explosion. The kart that fired a flyable will @@ -599,8 +599,7 @@ void Flyable::explode(Kart *kart_hit, PhysicalObject *object, { // The explosion animation will register itself with the kart // and will free it later. - bool direct_hit = indirect_damage ? false : kart == kart_hit; - KartUtils::createExplosion(kart, getXYZ(), direct_hit, m_owner); + KartUtils::createExplosion(kart, small_damage, getXYZ(), direct_hit, m_owner); } } Track::getCurrentTrack()->handleExplosion(getXYZ(), object,secondary_hits); diff --git a/src/items/flyable.hpp b/src/items/flyable.hpp index 58188a7ff57..7237b79b06c 100644 --- a/src/items/flyable.hpp +++ b/src/items/flyable.hpp @@ -201,7 +201,7 @@ class Flyable : public Moveable, public TerrainInfo, bool isOwnerImmunity(const Kart *kart_hit) const; virtual bool hit(Kart* kart, PhysicalObject* obj=NULL); void explode(Kart* kart, PhysicalObject* obj=NULL, - bool secondary_hits=true, bool indirect_damage=false); + bool secondary_hits=true, bool small_damage=false); unsigned int getOwnerId(); // ------------------------------------------------------------------------ /** Returns if this flyable has an animation playing (e.g. cannon). */ diff --git a/src/karts/kart_utils.cpp b/src/karts/kart_utils.cpp index 4f35060e994..a7eaa1d61fc 100644 --- a/src/karts/kart_utils.cpp +++ b/src/karts/kart_utils.cpp @@ -22,6 +22,7 @@ #include "config/player_manager.hpp" #include "items/powerup.hpp" #include "karts/explosion_animation.hpp" +#include "karts/kart_model.hpp" #include "karts/kart_properties.hpp" #include "karts/controller/controller.hpp" #include "modes/follow_the_leader.hpp" @@ -59,6 +60,15 @@ namespace KartUtils if (controller && controller->isLocalPlayerController()) controller->rumble(0, 0.8f, 500); + // Play a hurt kart-model animation for the kart that was hit + // TODO : because of the heavy kart movement due to the main explosion + // animation, little is visible from the kart model movement. + // Either the main explosion animation should move less, or + // the kart hurt animation should last long enough to exceed the + // kart animation, or the start of the kart-model animation should + // be delayed, or some mix of all three solutions. + kart->getKartModel()->setAnimation(KartModel::AF_HIT_START); + if (RaceManager::get()->isFollowMode()) { FollowTheLeaderRace *ftl_world = dynamic_cast(world); @@ -70,14 +80,20 @@ namespace KartUtils if (direct_hit) { // Register the hit with the world (for example for battle scoring) + int author_id = (author != nullptr) ? author->getWorldKartId() : -1; + world->kartHit(kart->getWorldKartId(), author_id); + + // Play a happy kart-model animation for the kart that hit the other kart + // TODO : prevent the interruption of another kart model animation + // The interruption is easily reproducible by simply bowling two + // karts in a short time interval. if (author != nullptr) - world->kartHit(kart->getWorldKartId(), author->getWorldKartId()); - else - world->kartHit(kart->getWorldKartId()); + author->getKartModel()->setAnimation(KartModel::AF_HAPPY_START); + // Increase achievement counters as appropriate if (author != nullptr && author->getController()->canGetAchievements()) { - if (author->getWorldKartId() != kart->getWorldKartId()) + if (author_id != (int)kart->getWorldKartId()) PlayerManager::addKartHit(kart->getWorldKartId()); PlayerManager::increaseAchievement(AchievementsStatus::ALL_HITS, 1); if (RaceManager::get()->isLinearRaceMode()) From e843eabc06890720bd222aaa7d3c4ddaa279a5b2 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 16 Nov 2025 18:33:29 +0100 Subject: [PATCH 163/188] Fix #5108 - Use a new partialReset function to restore kart state after a soccer goal - Also call the new function from ApplyRescue - Remove some duplicate resets Co-authored-by: Ban1p7 <112499126+Ban1p7@users.noreply.github.com> --- src/karts/kart.cpp | 48 +++++++++++++++++++++++--------------- src/karts/kart.hpp | 1 + src/modes/soccer_world.cpp | 2 ++ 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index e15f3d1a0e8..629f8968d9a 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -467,10 +467,6 @@ void Kart::reset() m_powerup_mask = 0; m_powerup_buckets.clear(); - // Reset star effect in case that it is currently being shown. - if (m_stars_effect) - m_stars_effect->reset(); - m_max_speed->reset(); m_powerup->reset(); // Reset animations and wheels @@ -485,7 +481,6 @@ void Kart::reset() m_saved_controller = NULL; } m_kart_model->setAnimation(KartModel::AF_DEFAULT); - m_attachment->reset(); m_kart_gfx->reset(); m_skidding->reset(); @@ -497,7 +492,9 @@ void Kart::reset() m_collision_particles->setCreationRateAbsolute(0.0f); #endif - unsetSquash(); + // Various elements that are also reset after a rescue or + // a soccer goal: invulnerability, attachment, speed boosts... + partialReset(); m_last_used_powerup = PowerupManager::POWERUP_NOTHING; m_race_position = m_initial_position; @@ -506,10 +503,7 @@ void Kart::reset() m_finish_time = 0.0f; m_bubblegum_ticks = 0; m_bubblegum_torque_sign = true; - m_invulnerable_ticks = 0; m_basket_squash_invulnerable_ticks = 0; - m_min_nitro_ticks = 0; - m_energy_to_min_ratio = 0; m_collected_energy = 0; m_bounce_back_ticks = 0; m_brake_ticks = 0; @@ -561,8 +555,6 @@ void Kart::reset() m_engine_sound->stop(); m_controls.reset(); - m_effective_steer = 0.0f; - m_slipstream->reset(); if(m_vehicle) { @@ -618,6 +610,29 @@ void Kart::reset() } // reset +// ----------------------------------------------------------------------------- +/** A partial reset applied after rescues and in soccer mode after goals */ +void Kart::partialReset(bool affect_squash) +{ + // Reset star effect in case that it is currently being shown. + if (m_stars_effect) + m_stars_effect->reset(); + m_invulnerable_ticks = 0; + + // Reset speed boosts and penalties + m_max_speed->reset(affect_squash); + if (affect_squash) + unsetSquash(); + + // Remove any leftover attachment + m_attachment->reset(); + + // Misc. + m_slipstream->reset(); + m_last_factor_engine_sound = 0.0f; + m_effective_steer = 0.0f; +} // partialReset + // ----------------------------------------------------------------------------- void Kart::setXYZ(const Vec3& a) { @@ -2973,13 +2988,8 @@ void Kart::applyRescue(bool auto_rescue) RescueAnimation::create(this, /*is_auto_rescue*/ auto_rescue); - // Reset some gameplay variables such as attachments - m_last_factor_engine_sound = 0.0f; - m_effective_steer = 0.0f; - getAttachment()->clear(); - if (m_stars_effect) - m_stars_effect->reset(); - m_max_speed->reset(/* don't affect squashing */ true); + // Reset some gameplay variables such as attachments and speed boosts + partialReset(/* affect squashing */ false); // Apply game-mode specific effects // In battle mode, add a hit unless it was auto-rescue @@ -3005,7 +3015,7 @@ void Kart::applyRescue(bool auto_rescue) } // Clear powerups when rescued in CTF - if (RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_CAPTURE_THE_FLAG) + if (RaceManager::get()->isCTFMode()) getPowerup()->reset(); } // applyRescue diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 8d8e188012d..41af2b5d26f 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -515,6 +515,7 @@ class Kart : public Moveable virtual float getTerrainPitch(float heading) const; virtual void reset (); + virtual void partialReset (bool affect_squash = true); // ------------------------------------------------------------------------ /** Sets zipper time, and apply one time additional speed boost. It can be * used with a specific material, in which case the zipper parmaters are diff --git a/src/modes/soccer_world.cpp b/src/modes/soccer_world.cpp index 99c1798a6ff..5354beb56c8 100644 --- a/src/modes/soccer_world.cpp +++ b/src/modes/soccer_world.cpp @@ -726,6 +726,8 @@ void SoccerWorld::resetKartsToSelfGoals() unsigned index = m_kart_position_map.at(kart->getWorldKartId()); btTransform t = Track::getCurrentTrack()->getStartTransform(index); moveKartTo(kart.get(), t); + // Reset attachments, speed boosts, invulnerability and misc. + kart->partialReset(); } } // resetKartsToSelfGoals From 45e31221c22d9a4e1a590b64c5a906e906b5def3 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 16 Nov 2025 21:40:41 +0100 Subject: [PATCH 164/188] Add kart exploding to scripting The explosion can be small (like that of the mini-cake or an indirect hit) or normal. Also set the squash slowdown to be consistent with the value from the swatter. --- src/scriptengine/script_kart.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/scriptengine/script_kart.cpp b/src/scriptengine/script_kart.cpp index 0d57f2676b4..be6685146aa 100644 --- a/src/scriptengine/script_kart.cpp +++ b/src/scriptengine/script_kart.cpp @@ -19,6 +19,7 @@ #include "script_kart.hpp" #include "karts/kart.hpp" +#include "karts/kart_utils.hpp" #include "karts/kart_model.hpp" #include "karts/kart_properties.hpp" #include "modes/world.hpp" @@ -48,8 +49,15 @@ namespace Scripting void squash(int idKart, float time) { ::Kart* kart = World::getWorld()->getKart(idKart); - kart->setSquash(time, 0.5); //0.5 * max speed is new max for squashed duration - } + kart->setSquash(time, 0.6); //0.6 * max speed is new max for squashed duration + } // squash + + /** Explodes the specified kart, using either a small or normal explosion */ + void explode(int idKart, bool small) + { + ::Kart* kart = World::getWorld()->getKart(idKart); + KartUtils::createExplosion(kart, small); + } // explode /** Teleports the kart to the specified Vec3 location */ void teleport(int idKart, SimpleVec3* position) @@ -61,7 +69,7 @@ namespace Scripting btTransform s = World::getWorld()->getRescueTransform(index); s.setRotation(btQuaternion(btVector3(0.0f, 1.0f, 0.0f), 0.0f)); World::getWorld()->moveKartTo(kart, s); - } + } // teleport /** Teleports the kart to the specified Vec3 location */ void teleportExact(int idKart, SimpleVec3* position) @@ -73,7 +81,7 @@ namespace Scripting s.setRotation(kart->getRotation()); s.setOrigin(v); World::getWorld()->moveKartTo(kart, s); - } + } // teleportExact /** Attempts to project kart to the given 2D location, to the position * with height 0, at a 45 degree angle. @@ -163,6 +171,10 @@ namespace Scripting r = engine->RegisterGlobalFunction("void squash(int id, float time)", mp ? WRAP_FN(squash) : asFUNCTION(squash), call_conv); assert(r >= 0); + + r = engine->RegisterGlobalFunction("void explode(int id, bool small)", + mp ? WRAP_FN(explode) : asFUNCTION(explode), + call_conv); assert(r >= 0); r = engine->RegisterGlobalFunction("void teleport(int id, const Vec3 &in)", mp ? WRAP_FN(teleport) : asFUNCTION(teleport), From 4499ca58445200625f1f76e0d71e14214f5da7b3 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 16 Nov 2025 22:07:21 +0100 Subject: [PATCH 165/188] Don't display the wrong way message for near unmoving karts Sometimes, if a kart was stopped on a slope, it would very slowly slide and trigger the wrong way message. While this probably happens much more in testing than in any sort of racing where the karts keep moving, it's annoying when it happens and the fix is trivial. --- src/modes/linear_world.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index 0c615567dce..29a3b48c89d 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -1069,10 +1069,12 @@ void LinearWorld::checkForWrongDirection(unsigned int i, float dt) angle_diff += 2*M_PI; // Display a warning message if the kart is going back way, i.e. if angle - // is too big(unless the kart has already finished the race). + // is too big (unless the kart has already finished the race). + // We also avoid displaying the message if the kart is almost not moving. if ((angle_diff > DEGREE_TO_RAD * 120.0f || angle_diff < -DEGREE_TO_RAD * 120.0f) && kart->getVelocityLC().getY() > 0.0f && + fabsf(kart->getSpeed()) > 2.0f && !kart->hasFinishedRace()) { ki.m_wrong_way_timer += dt; From 3f7792f0e94a13a6b808119f8953d42a66dcffd6 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 16 Nov 2025 22:43:21 +0100 Subject: [PATCH 166/188] Clean up the kart model display code kart_selection.cpp almost entirely duplicated a massive section of code from player_kart_widget.cpp ; now it simply calls a function which does the exact same thing. No functional change observed in both singleplayer and multiplayer kart selection. --- src/guiengine/widgets/player_kart_widget.cpp | 123 ++++++++++--------- src/guiengine/widgets/player_kart_widget.hpp | 3 + src/states_screens/kart_selection.cpp | 65 +--------- 3 files changed, 70 insertions(+), 121 deletions(-) diff --git a/src/guiengine/widgets/player_kart_widget.cpp b/src/guiengine/widgets/player_kart_widget.cpp index 3721a2d5033..867d3c2fe0a 100644 --- a/src/guiengine/widgets/player_kart_widget.cpp +++ b/src/guiengine/widgets/player_kart_widget.cpp @@ -198,8 +198,7 @@ PlayerKartWidget::PlayerKartWidget(KartSelectionScreen* parent, // Init kart model const std::string default_kart = UserConfigParams::m_default_kart; - const KartProperties* props = - kart_properties_manager->getKart(default_kart); + const KartProperties* props = kart_properties_manager->getKart(default_kart); if(!props) { @@ -224,62 +223,7 @@ PlayerKartWidget::PlayerKartWidget(KartSelectionScreen* parent, } m_kart_internal_name = props->getIdent(); - const KartModel &kart_model = props->getMasterKartModel(); - - float scale = 35.0f; - if (kart_model.getLength() > 1.45f) - { - // if kart is too long, size it down a bit so that it fits - scale = 30.0f; - } - - core::matrix4 model_location; - model_location.setScale(core::vector3df(scale, scale, scale)); - const bool has_win_anime = - UserConfigParams::m_animated_characters && - (((kart_model.getFrame(KartModel::AF_WIN_LOOP_START) > -1 || - kart_model.getFrame(KartModel::AF_WIN_START) > -1) && - kart_model.getFrame(KartModel::AF_WIN_LOOP_END) > -1) || - (kart_model.getFrame(KartModel::AF_SELECTION_LOOP_START) > -1 && - kart_model.getFrame(KartModel::AF_SELECTION_LOOP_END) > -1)); - m_model_view->addModel( kart_model.getModel(), model_location, - has_win_anime ? - kart_model.getFrame(KartModel::AF_SELECTION_LOOP_START) > -1 ? - kart_model.getFrame(KartModel::AF_SELECTION_LOOP_START) : - kart_model.getFrame(KartModel::AF_WIN_LOOP_START) > -1 ? - kart_model.getFrame(KartModel::AF_WIN_LOOP_START) : - kart_model.getFrame(KartModel::AF_WIN_START) : - kart_model.getBaseFrame(), - has_win_anime ? - kart_model.getFrame(KartModel::AF_SELECTION_LOOP_END) > -1 ? - kart_model.getFrame(KartModel::AF_SELECTION_LOOP_END) : - kart_model.getFrame(KartModel::AF_WIN_LOOP_END) : - kart_model.getBaseFrame(), - kart_model.getAnimationSpeed()); - m_model_view->getModelViewRenderInfo()->setHue( - m_associated_player->getConstProfile()->getDefaultKartColor()); - model_location.setScale(core::vector3df(1.0f, 1.0f, 1.0f)); - for (unsigned i = 0; i < 4; i++) - { - model_location.setTranslation(kart_model - .getWheelGraphicsPosition(i).toIrrVector()); - m_model_view->addModel(kart_model.getWheelModel(i), model_location); - } - - for (unsigned i = 0; i < kart_model.getSpeedWeightedObjectsCount(); i++) - { - const SpeedWeightedObject& obj = kart_model.getSpeedWeightedObject(i); - core::matrix4 swol = obj.m_location; - if (!obj.m_bone_name.empty()) - { - core::matrix4 inv = - kart_model.getInverseBoneMatrix(obj.m_bone_name); - swol = inv * obj.m_location; - } - m_model_view->addModel(obj.m_model, swol, -1, -1, 0.0f, - obj.m_bone_name); - } - m_model_view->setRotateContinuously( 35.0f ); + setupKartModel(props); // ---- Kart name label m_kart_name = new LabelWidget( @@ -339,6 +283,69 @@ PlayerKartWidget::~PlayerKartWidget() #endif } // ~PlayerKartWidget +// ------------------------------------------------------------------------ +/** Handles all the operations for the display of the kart model */ +void PlayerKartWidget::setupKartModel(const KartProperties* properties) +{ + const KartModel &kart_model = properties->getMasterKartModel(); + + float scale = 35.0f; + if (kart_model.getLength() > 1.45f) + { + // If the kart is too long, size it down a bit so that it fits + scale = 30.0f; + } + + core::matrix4 model_location; + model_location.setScale(core::vector3df(scale, scale, scale)); + m_model_view->clearModels(); + const bool has_win_anime = + UserConfigParams::m_animated_characters && + (((kart_model.getFrame(KartModel::AF_WIN_LOOP_START) > -1 || + kart_model.getFrame(KartModel::AF_WIN_START) > -1) && + kart_model.getFrame(KartModel::AF_WIN_LOOP_END) > -1) || + (kart_model.getFrame(KartModel::AF_SELECTION_LOOP_START) > -1 && + kart_model.getFrame(KartModel::AF_SELECTION_LOOP_END) > -1)); + m_model_view->addModel( kart_model.getModel(), model_location, + has_win_anime ? + kart_model.getFrame(KartModel::AF_SELECTION_LOOP_START) > -1 ? + kart_model.getFrame(KartModel::AF_SELECTION_LOOP_START) : + kart_model.getFrame(KartModel::AF_WIN_LOOP_START) > -1 ? + kart_model.getFrame(KartModel::AF_WIN_LOOP_START) : + kart_model.getFrame(KartModel::AF_WIN_START) : + kart_model.getBaseFrame(), + has_win_anime ? + kart_model.getFrame(KartModel::AF_SELECTION_LOOP_END) > -1 ? + kart_model.getFrame(KartModel::AF_SELECTION_LOOP_END) : + kart_model.getFrame(KartModel::AF_WIN_LOOP_END) : + kart_model.getBaseFrame(), + kart_model.getAnimationSpeed()); + m_model_view->getModelViewRenderInfo()->setHue( + m_associated_player->getConstProfile()->getDefaultKartColor()); + model_location.setScale(core::vector3df(1.0f, 1.0f, 1.0f)); + for (unsigned i = 0; i < 4; i++) + { + model_location.setTranslation(kart_model + .getWheelGraphicsPosition(i).toIrrVector()); + m_model_view->addModel(kart_model.getWheelModel(i), model_location); + } + + for (unsigned i = 0; i < kart_model.getSpeedWeightedObjectsCount(); i++) + { + const SpeedWeightedObject& obj = kart_model.getSpeedWeightedObject(i); + core::matrix4 swol = obj.m_location; + if (!obj.m_bone_name.empty()) + { + core::matrix4 inv = + kart_model.getInverseBoneMatrix(obj.m_bone_name); + swol = inv * obj.m_location; + } + m_model_view->addModel(obj.m_model, swol, -1, -1, 0.0f, + obj.m_bone_name); + } + m_model_view->setRotateContinuously(35.0f); +} // setupKartModel + // ------------------------------------------------------------------------ /** Called when players are renumbered (changes the player ID) */ void PlayerKartWidget::setPlayerID(const int newPlayerID) diff --git a/src/guiengine/widgets/player_kart_widget.hpp b/src/guiengine/widgets/player_kart_widget.hpp index ddd35e6c256..0f4a8392fae 100644 --- a/src/guiengine/widgets/player_kart_widget.hpp +++ b/src/guiengine/widgets/player_kart_widget.hpp @@ -108,6 +108,9 @@ namespace GUIEngine ~PlayerKartWidget(); + // ------------------------------------------------------------------------ + /** Called to update the displayed kart model. */ + void setupKartModel(const KartProperties* properties); // ------------------------------------------------------------------------ /** Called when players are renumbered (changes the player ID) */ void setPlayerID(const int newPlayerID); diff --git a/src/states_screens/kart_selection.cpp b/src/states_screens/kart_selection.cpp index bda90805436..612bddc80bd 100644 --- a/src/states_screens/kart_selection.cpp +++ b/src/states_screens/kart_selection.cpp @@ -1015,71 +1015,10 @@ void KartSelectionScreen::updateKartWidgetModel(int widget_id, } else { - const KartProperties *kp = - kart_properties_manager->getKart(selection); + const KartProperties *kp = kart_properties_manager->getKart(selection); if (kp != NULL) { - const KartModel &kart_model = kp->getMasterKartModel(); - - float scale = 35.0f; - if (kart_model.getLength() > 1.45f) - { - // if kart is too long, size it down a bit so that it fits - scale = 30.0f; - } - - core::matrix4 model_location; - model_location.setScale(core::vector3df(scale, scale, scale)); - w3->clearModels(); - // FIXME - Identical code appears in player_kart_widget.cpp - // Avoid this duplication! - const bool has_win_anime = - UserConfigParams::m_animated_characters && - (((kart_model.getFrame(KartModel::AF_WIN_LOOP_START) > -1 || - kart_model.getFrame(KartModel::AF_WIN_START) > -1) && - kart_model.getFrame(KartModel::AF_WIN_LOOP_END) > -1) || - (kart_model.getFrame(KartModel::AF_SELECTION_LOOP_START) > -1 && - kart_model.getFrame(KartModel::AF_SELECTION_LOOP_END) > -1)); - w3->addModel( kart_model.getModel(), model_location, - has_win_anime ? - kart_model.getFrame(KartModel::AF_SELECTION_LOOP_START) > -1 ? - kart_model.getFrame(KartModel::AF_SELECTION_LOOP_START) : - kart_model.getFrame(KartModel::AF_WIN_LOOP_START) > -1 ? - kart_model.getFrame(KartModel::AF_WIN_LOOP_START) : - kart_model.getFrame(KartModel::AF_WIN_START) : - kart_model.getBaseFrame(), - has_win_anime ? - kart_model.getFrame(KartModel::AF_SELECTION_LOOP_END) > -1 ? - kart_model.getFrame(KartModel::AF_SELECTION_LOOP_END) : - kart_model.getFrame(KartModel::AF_WIN_LOOP_END) : - kart_model.getBaseFrame(), - kart_model.getAnimationSpeed()); - - w3->getModelViewRenderInfo()->setHue(kart_color); - model_location.setScale(core::vector3df(1.0f, 1.0f, 1.0f)); - for (unsigned i = 0; i < 4; i++) - { - model_location.setTranslation(kart_model - .getWheelGraphicsPosition(i).toIrrVector()); - w3->addModel(kart_model.getWheelModel(i), model_location); - } - - for (unsigned i = 0; - i < kart_model.getSpeedWeightedObjectsCount(); i++) - { - const SpeedWeightedObject& obj = - kart_model.getSpeedWeightedObject(i); - core::matrix4 swol = obj.m_location; - if (!obj.m_bone_name.empty()) - { - core::matrix4 inv = - kart_model.getInverseBoneMatrix(obj.m_bone_name); - swol = inv * obj.m_location; - } - w3->addModel(obj.m_model, swol, -1, -1, 0.0f, obj.m_bone_name); - } - //w3->update(0); - + m_kart_widgets[widget_id].setupKartModel(kp), m_kart_widgets[widget_id].m_kart_name ->setText( selectionText.c_str(), false ); } From 9f223f495df88ffca8c4d47ac7c89405b704d2ac Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Mon, 17 Nov 2025 16:06:13 +0100 Subject: [PATCH 167/188] Remove redundancy in the ModelView code - Reorganize the code setting up the model nodes and move it to a dedicated function. For simplicity's sake, we use an AnimatedMeshSceneNode for each node, even those that aren't animated, this has no effect except slightly changing the function call to perform when adding an animated model that shouldn't be animated. - Move duplicated clearing code in a common function --- src/guiengine/widgets/model_view_widget.cpp | 112 ++++++++------------ src/guiengine/widgets/model_view_widget.hpp | 6 +- src/states_screens/kart_selection.cpp | 4 +- 3 files changed, 54 insertions(+), 68 deletions(-) diff --git a/src/guiengine/widgets/model_view_widget.cpp b/src/guiengine/widgets/model_view_widget.cpp index 5dbb4f65a8b..d4d14af4758 100644 --- a/src/guiengine/widgets/model_view_widget.cpp +++ b/src/guiengine/widgets/model_view_widget.cpp @@ -44,6 +44,7 @@ IconButtonWidget(IconButtonWidget::SCALE_MODE_KEEP_TEXTURE_ASPECT_RATIO, false, m_rtt_size(rtt_size) { m_rtt_main_node = NULL; + m_main_node_animated = false; m_camera = NULL; m_light = NULL; m_type = WTYPE_MODEL_VIEW; @@ -91,16 +92,22 @@ void ModelViewWidget::clearModels() m_model_frames.clear(); m_model_animation_speed.clear(); m_bone_attached.clear(); + clearMainCameraLights(); +} // clearModels +// ----------------------------------------------------------------------------- +void ModelViewWidget::clearMainCameraLights() +{ if (m_rtt_main_node != NULL) m_rtt_main_node->remove(); if (m_light != NULL) m_light->remove(); if (m_camera != NULL) m_camera->remove(); irr_driver->clearLights(); m_rtt_main_node = NULL; + m_main_node_animated = false; m_camera = NULL; m_light = NULL; -} // clearModels +} // clearMainCameraLights // ----------------------------------------------------------------------------- @@ -209,41 +216,9 @@ void ModelViewWidget::setupRTTScene() { #ifndef SERVER_ONLY irr_driver->suppressSkyBox(); + clearMainCameraLights(); - if (m_rtt_main_node != NULL) m_rtt_main_node->remove(); - if (m_light != NULL) m_light->remove(); - if (m_camera != NULL) m_camera->remove(); - - m_rtt_main_node = NULL; - m_camera = NULL; - m_light = NULL; - - irr_driver->clearLights(); - scene::IAnimatedMeshSceneNode* animated_node = NULL; - - if (m_model_frames[0].first == -1) - { - scene::ISceneNode* node = irr_driver->addMesh(m_models.get(0), "rtt_mesh", - NULL, m_render_info); - node->setPosition(m_model_location[0].getTranslation()); - node->setRotation(m_model_location[0].getRotationDegrees()); - node->setScale(m_model_location[0].getScale()); - node->setMaterialFlag(video::EMF_FOG_ENABLE, false); - m_rtt_main_node = node; - } - else - { - animated_node = - irr_driver->addAnimatedMesh((scene::IAnimatedMesh*)m_models.get(0), "rtt_mesh", - NULL, m_render_info); - animated_node->setPosition(m_model_location[0].getTranslation()); - animated_node->setRotation(m_model_location[0].getRotationDegrees()); - animated_node->setScale(m_model_location[0].getScale()); - animated_node->setFrameLoop(m_model_frames[0].first, m_model_frames[0].second); - animated_node->setAnimationSpeed(m_model_animation_speed[0]); - animated_node->setMaterialFlag(video::EMF_FOG_ENABLE, false); - m_rtt_main_node = animated_node; - } + setupNode(0, /* main */ true, /* animated */ m_model_frames[0].first != -1); assert(m_rtt_main_node != NULL); assert(m_models.size() == m_model_location.size()); @@ -253,36 +228,7 @@ void ModelViewWidget::setupRTTScene() const int mesh_amount = m_models.size(); for (int n = 1; n < mesh_amount; n++) { - const bool bone_attachment = - animated_node && !m_bone_attached[n].empty(); - scene::ISceneNode* parent = bone_attachment ? - animated_node->getJointNode(m_bone_attached[n].c_str()) : - m_rtt_main_node; - if (!parent) - continue; - if (m_model_frames[n].first == -1) - { - scene::ISceneNode* node = - irr_driver->addMesh(m_models.get(n), "rtt_node", parent, - m_render_info); - node->setPosition(m_model_location[n].getTranslation()); - node->setRotation(m_model_location[n].getRotationDegrees()); - node->setScale(m_model_location[n].getScale()); - node->updateAbsolutePosition(); - } - else - { - scene::IAnimatedMeshSceneNode* node = - irr_driver->addAnimatedMesh((scene::IAnimatedMesh*)m_models.get(n), - "modelviewrtt", parent, m_render_info); - node->setPosition(m_model_location[n].getTranslation()); - node->setRotation(m_model_location[n].getRotationDegrees()); - node->setScale(m_model_location[n].getScale()); - node->setFrameLoop(m_model_frames[n].first, m_model_frames[n].second); - node->setAnimationSpeed(m_model_animation_speed[n]); - node->updateAbsolutePosition(); - //Log::info("ModelViewWidget", "Set frame %d", m_model_frames[n]); - } + setupNode(n, /* main */false, /* animated */ (m_model_frames[n].first != -1)); } irr_driver->setAmbientLight(video::SColor(255, 35, 35, 35)); @@ -333,6 +279,42 @@ void ModelViewWidget::setupRTTScene() #endif } // setupRTTScene +// ---------------------------------------------------------------------------- +void ModelViewWidget::setupNode(int index, bool main, bool animated) +{ + const bool bone_attachment = m_main_node_animated && !m_bone_attached[index].empty(); + scene::ISceneNode* parent = (main) ? NULL : bone_attachment ? + m_rtt_main_node->getJointNode(m_bone_attached[index].c_str()) : + (scene::ISceneNode*)m_rtt_main_node; + + if (!main && !parent) + return; + + scene::IAnimatedMeshSceneNode* node = // It's fine even if the mesh is not animated + irr_driver->addAnimatedMesh((scene::IAnimatedMesh*)m_models.get(index), + main ? "rtt_mesh" : animated ? "modelviewrtt" : "rtt_node", + main ? NULL : parent, m_render_info); + + node->setPosition(m_model_location[index].getTranslation()); + node->setRotation(m_model_location[index].getRotationDegrees()); + node->setScale(m_model_location[index].getScale()); + node->updateAbsolutePosition(); + + if (animated) + { + node->setAnimationSpeed(m_model_animation_speed[index]); + node->setFrameLoop(m_model_frames[index].first, m_model_frames[index].second); + } + + if (main) + { + node->setMaterialFlag(video::EMF_FOG_ENABLE, false); + m_rtt_main_node = node; + m_main_node_animated = animated; + } + //Log::info("ModelViewWidget", "Set frame %d", m_model_frames[index]); +} // setupNode + // ---------------------------------------------------------------------------- void ModelViewWidget::setRotateOff() { diff --git a/src/guiengine/widgets/model_view_widget.hpp b/src/guiengine/widgets/model_view_widget.hpp index d306ea822a2..50d0b672ac3 100644 --- a/src/guiengine/widgets/model_view_widget.hpp +++ b/src/guiengine/widgets/model_view_widget.hpp @@ -55,7 +55,8 @@ namespace GUIEngine bool m_rtt_unsupported; - scene::ISceneNode *m_rtt_main_node; + scene::IAnimatedMeshSceneNode *m_rtt_main_node; + bool m_main_node_animated; scene::ICameraSceneNode *m_camera; @@ -74,6 +75,7 @@ namespace GUIEngine void add(); void clearModels(); + void clearMainCameraLights(); void addModel(irr::scene::IMesh* mesh, const core::matrix4& location = core::matrix4(), const int start_loop_frame=-1, @@ -103,6 +105,8 @@ namespace GUIEngine void setupRTTScene(); + void setupNode(int index, bool main, bool animated); + void drawRTTScene(const irr::core::rect& dest_rect) const; std::shared_ptr getModelViewRenderInfo() { return m_render_info; } diff --git a/src/states_screens/kart_selection.cpp b/src/states_screens/kart_selection.cpp index 612bddc80bd..0ee217478ac 100644 --- a/src/states_screens/kart_selection.cpp +++ b/src/states_screens/kart_selection.cpp @@ -997,8 +997,8 @@ void KartSelectionScreen::updateKartWidgetModel(int widget_id, file_manager->pushTextureSearchPath (file_manager->getAsset(FileManager::MODEL,""), "models"); w3->addModel(irr_driver->getAnimatedMesh( - file_manager->getAsset(FileManager::MODEL, "chest.spm")) - ->getMesh(20), model_location); + file_manager->getAsset(FileManager::MODEL, "chest.spm")), + model_location, 20, 20, 0); file_manager->popTextureSearchPath(); w3->update(0); From 99f4c6e6c60ebf80b130ddeebf75d67a82a1100c Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Mon, 17 Nov 2025 17:55:34 +0100 Subject: [PATCH 168/188] Implement the selection introductory animation - Transmit a loop start frame to KartModelView, adjust the internal members and function calls to handle it. - Allow ModelViewWidget to receive a callback when an animation end, and use that to go from the introductory animation to the looped sequence - Cleaner code to detect which animation should be used based on what's available, and detection of the loop start frame based on the chosen start frame. - Drop support for showing win animations without a loop start point. --- src/guiengine/widgets/model_view_widget.cpp | 37 +++++++++++--- src/guiengine/widgets/model_view_widget.hpp | 19 +++++-- src/guiengine/widgets/player_kart_widget.cpp | 51 ++++++++++--------- src/karts/kart_model.hpp | 4 +- .../dialogs/kart_color_slider_dialog.cpp | 4 +- src/states_screens/kart_selection.cpp | 2 +- src/states_screens/soccer_setup_screen.cpp | 4 +- 7 files changed, 80 insertions(+), 41 deletions(-) diff --git a/src/guiengine/widgets/model_view_widget.cpp b/src/guiengine/widgets/model_view_widget.cpp index d4d14af4758..e230a59fd27 100644 --- a/src/guiengine/widgets/model_view_widget.cpp +++ b/src/guiengine/widgets/model_view_widget.cpp @@ -89,7 +89,9 @@ void ModelViewWidget::clearModels() m_render_info->setHue(0.0f); m_models.clearWithoutDeleting(); m_model_location.clear(); - m_model_frames.clear(); + m_model_start_frames.clear(); + m_model_loop_start_frames.clear(); + m_model_loop_end_frames.clear(); m_model_animation_speed.clear(); m_bone_attached.clear(); clearMainCameraLights(); @@ -113,6 +115,7 @@ void ModelViewWidget::clearMainCameraLights() void ModelViewWidget::addModel(irr::scene::IMesh* mesh, const core::matrix4& location, + const int start_frame, const int start_loop_frame, const int end_loop_frame, float animation_speed, const std::string& bone_name) @@ -121,7 +124,9 @@ void ModelViewWidget::addModel(irr::scene::IMesh* mesh, m_models.push_back(mesh); m_model_location.push_back(location); - m_model_frames.emplace_back(start_loop_frame, end_loop_frame); + m_model_start_frames.push_back(start_frame); + m_model_loop_start_frames.push_back(start_loop_frame); + m_model_loop_end_frames.push_back(end_loop_frame); m_model_animation_speed.push_back(animation_speed); m_bone_attached.push_back(bone_name); #ifndef SERVER_ONLY @@ -218,17 +223,16 @@ void ModelViewWidget::setupRTTScene() irr_driver->suppressSkyBox(); clearMainCameraLights(); - setupNode(0, /* main */ true, /* animated */ m_model_frames[0].first != -1); + setupNode(0, /* main */ true, /* animated */ m_model_start_frames[0] != -1); assert(m_rtt_main_node != NULL); assert(m_models.size() == m_model_location.size()); - assert(m_models.size() == m_model_frames.size()); assert(m_models.size() == m_model_animation_speed.size()); assert(m_models.size() == m_bone_attached.size()); const int mesh_amount = m_models.size(); for (int n = 1; n < mesh_amount; n++) { - setupNode(n, /* main */false, /* animated */ (m_model_frames[n].first != -1)); + setupNode(n, /* main */false, /* animated */ (m_model_start_frames[n] != -1)); } irr_driver->setAmbientLight(video::SColor(255, 35, 35, 35)); @@ -303,7 +307,14 @@ void ModelViewWidget::setupNode(int index, bool main, bool animated) if (animated) { node->setAnimationSpeed(m_model_animation_speed[index]); - node->setFrameLoop(m_model_frames[index].first, m_model_frames[index].second); + node->setFrameLoop(m_model_start_frames[index], m_model_loop_end_frames[index]); + // Special case if we have an introductory sequence + // Loop mode must be set to false so that we get a callback + if (main && (m_model_start_frames[0] != m_model_loop_start_frames[0])) + { + node->setLoopMode(false); + node->setAnimationEndCallback(this); + } } if (main) @@ -312,9 +323,21 @@ void ModelViewWidget::setupNode(int index, bool main, bool animated) m_rtt_main_node = node; m_main_node_animated = animated; } - //Log::info("ModelViewWidget", "Set frame %d", m_model_frames[index]); + //Log::info("ModelViewWidget", "Set frame %d", m_model_start_frames[index]); } // setupNode +// ---------------------------------------------------------------------------- +/** Called from irrlicht when a non-looped animation ends. This is used to + * implement an introductory frame sequence before the actual loop can start. + * \param node The node for which the animation ended. + */ +void ModelViewWidget::OnAnimationEnd(scene::IAnimatedMeshSceneNode *node) +{ + node->setFrameLoop(m_model_loop_start_frames[0], m_model_loop_end_frames[0]); + node->setLoopMode(true); + node->setAnimationEndCallback(NULL); +} // OnAnimationEnd + // ---------------------------------------------------------------------------- void ModelViewWidget::setRotateOff() { diff --git a/src/guiengine/widgets/model_view_widget.hpp b/src/guiengine/widgets/model_view_widget.hpp index 50d0b672ac3..a69f6cce368 100644 --- a/src/guiengine/widgets/model_view_widget.hpp +++ b/src/guiengine/widgets/model_view_widget.hpp @@ -15,8 +15,6 @@ // 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_MODELVIEW_HPP #define HEADER_MODELVIEW_HPP @@ -26,6 +24,14 @@ #include "utils/leak_check.hpp" #include "utils/ptr_vector.hpp" +#include +namespace irr +{ + namespace scene { class IAnimatedMesh; class IMesh; + class ISceneNode; class IMeshSceneNode; } +} +using namespace irr; + namespace GE { class GERenderInfo; } namespace GUIEngine @@ -33,7 +39,7 @@ namespace GUIEngine /** \brief A model view widget. * \ingroup widgetsgroup */ - class ModelViewWidget : public IconButtonWidget + class ModelViewWidget : public scene::IAnimationEndCallBack, public IconButtonWidget { enum RotationMode { @@ -47,7 +53,9 @@ namespace GUIEngine PtrVector m_models; std::vector m_model_location; - std::vector > m_model_frames; + std::vector m_model_start_frames; + std::vector m_model_loop_start_frames; + std::vector m_model_loop_end_frames; std::vector m_model_animation_speed; std::vector m_bone_attached; std::unique_ptr m_render_target; @@ -66,6 +74,8 @@ namespace GUIEngine unsigned m_rtt_size; + void OnAnimationEnd(scene::IAnimatedMeshSceneNode *node); + public: LEAK_CHECK() @@ -78,6 +88,7 @@ namespace GUIEngine void clearMainCameraLights(); void addModel(irr::scene::IMesh* mesh, const core::matrix4& location = core::matrix4(), + const int start_frame=-1, const int start_loop_frame=-1, const int end_loop_frame=-1, float animation_speed = 0.0f, diff --git a/src/guiengine/widgets/player_kart_widget.cpp b/src/guiengine/widgets/player_kart_widget.cpp index 867d3c2fe0a..7f997f20d54 100644 --- a/src/guiengine/widgets/player_kart_widget.cpp +++ b/src/guiengine/widgets/player_kart_widget.cpp @@ -299,30 +299,36 @@ void PlayerKartWidget::setupKartModel(const KartProperties* properties) core::matrix4 model_location; model_location.setScale(core::vector3df(scale, scale, scale)); m_model_view->clearModels(); - const bool has_win_anime = - UserConfigParams::m_animated_characters && - (((kart_model.getFrame(KartModel::AF_WIN_LOOP_START) > -1 || - kart_model.getFrame(KartModel::AF_WIN_START) > -1) && - kart_model.getFrame(KartModel::AF_WIN_LOOP_END) > -1) || - (kart_model.getFrame(KartModel::AF_SELECTION_LOOP_START) > -1 && - kart_model.getFrame(KartModel::AF_SELECTION_LOOP_END) > -1)); + + int start_frame = kart_model.getBaseFrame(); + int loop_frame = kart_model.getBaseFrame(); + int end_frame = kart_model.getBaseFrame(); + + // Use the selection animations if possible + if (UserConfigParams::m_animated_characters) + { + KartModel::AnimationFrameType st_type = KartModel::AF_SELECTION_START; + if (kart_model.getFrame(st_type) < 0) + st_type = KartModel::AF_SELECTION_LOOP_START; + if (kart_model.getFrame(st_type) < 0) + st_type = KartModel::AF_WIN_LOOP_START; + int start = kart_model.getFrame(st_type); + int end = kart_model.getFrame(kart_model.getEndFrameType(st_type)); + if (start > -1 && end > -1) + { + start_frame = start; + end_frame = end; + } + int loop = kart_model.getFrame(kart_model.getLoopStartFrameType(st_type)); + loop_frame = (loop > -1) ? loop : start_frame; + } + m_model_view->addModel( kart_model.getModel(), model_location, - has_win_anime ? - kart_model.getFrame(KartModel::AF_SELECTION_LOOP_START) > -1 ? - kart_model.getFrame(KartModel::AF_SELECTION_LOOP_START) : - kart_model.getFrame(KartModel::AF_WIN_LOOP_START) > -1 ? - kart_model.getFrame(KartModel::AF_WIN_LOOP_START) : - kart_model.getFrame(KartModel::AF_WIN_START) : - kart_model.getBaseFrame(), - has_win_anime ? - kart_model.getFrame(KartModel::AF_SELECTION_LOOP_END) > -1 ? - kart_model.getFrame(KartModel::AF_SELECTION_LOOP_END) : - kart_model.getFrame(KartModel::AF_WIN_LOOP_END) : - kart_model.getBaseFrame(), - kart_model.getAnimationSpeed()); + start_frame, loop_frame, end_frame, kart_model.getAnimationSpeed()); m_model_view->getModelViewRenderInfo()->setHue( m_associated_player->getConstProfile()->getDefaultKartColor()); model_location.setScale(core::vector3df(1.0f, 1.0f, 1.0f)); + for (unsigned i = 0; i < 4; i++) { model_location.setTranslation(kart_model @@ -336,11 +342,10 @@ void PlayerKartWidget::setupKartModel(const KartProperties* properties) core::matrix4 swol = obj.m_location; if (!obj.m_bone_name.empty()) { - core::matrix4 inv = - kart_model.getInverseBoneMatrix(obj.m_bone_name); + core::matrix4 inv = kart_model.getInverseBoneMatrix(obj.m_bone_name); swol = inv * obj.m_location; } - m_model_view->addModel(obj.m_model, swol, -1, -1, 0.0f, + m_model_view->addModel(obj.m_model, swol, -1, -1, -1, 0.0f, obj.m_bone_name); } m_model_view->setRotateContinuously(35.0f); diff --git a/src/karts/kart_model.hpp b/src/karts/kart_model.hpp index 0e275f7e9da..d9b0f3189b3 100644 --- a/src/karts/kart_model.hpp +++ b/src/karts/kart_model.hpp @@ -466,7 +466,7 @@ class KartModel : public scene::IAnimationEndCallBack, public NoCopy /* Returns the AnimationFrameType which corresponds to the end of * the animation passed as parameter. * Returns AF_BEGIN if there is no valid end frame type. */ - AnimationFrameType getEndFrameType(AnimationFrameType f) + AnimationFrameType getEndFrameType(AnimationFrameType f) const { if (f == AF_WIN_START) return AF_WIN_LOOP_END; if (f == AF_WIN_LOOP_START) return AF_WIN_LOOP_END; @@ -494,7 +494,7 @@ class KartModel : public scene::IAnimationEndCallBack, public NoCopy * There is a valid result only if the parameter frame type serves * as an introductory sequence to the loop. * Returns AF_BEGIN if there is no valid loop start frame type. */ - AnimationFrameType getLoopStartFrameType(AnimationFrameType f) + AnimationFrameType getLoopStartFrameType(AnimationFrameType f) const { if (f == AF_WIN_START) return AF_WIN_LOOP_START; if (f == AF_NEUTRAL_START) return AF_NEUTRAL_LOOP_START; diff --git a/src/states_screens/dialogs/kart_color_slider_dialog.cpp b/src/states_screens/dialogs/kart_color_slider_dialog.cpp index f6d87ac58a9..c926f38b9ee 100644 --- a/src/states_screens/dialogs/kart_color_slider_dialog.cpp +++ b/src/states_screens/dialogs/kart_color_slider_dialog.cpp @@ -114,7 +114,7 @@ void KartColorSliderDialog::beforeAddingWidgets() model_location.setScale(core::vector3df(scale, scale, scale)); // Add the kart model (including wheels and speed weight objects) - m_model_view->addModel(kart_model.getModel(), model_location, + m_model_view->addModel(kart_model.getModel(), model_location, kart_model.getBaseFrame(), kart_model.getBaseFrame(), kart_model.getBaseFrame()); model_location.setScale(core::vector3df(1.0f, 1.0f, 1.0f)); @@ -137,7 +137,7 @@ void KartColorSliderDialog::beforeAddingWidgets() kart_model.getInverseBoneMatrix(obj.m_bone_name); swol = inv * obj.m_location; } - m_model_view->addModel(obj.m_model, swol, -1, -1, 0.0f, + m_model_view->addModel(obj.m_model, swol, -1, -1, -1, 0.0f, obj.m_bone_name); } diff --git a/src/states_screens/kart_selection.cpp b/src/states_screens/kart_selection.cpp index 0ee217478ac..22ceaff7d74 100644 --- a/src/states_screens/kart_selection.cpp +++ b/src/states_screens/kart_selection.cpp @@ -998,7 +998,7 @@ void KartSelectionScreen::updateKartWidgetModel(int widget_id, (file_manager->getAsset(FileManager::MODEL,""), "models"); w3->addModel(irr_driver->getAnimatedMesh( file_manager->getAsset(FileManager::MODEL, "chest.spm")), - model_location, 20, 20, 0); + model_location, 20, 20, 20, 0); file_manager->popTextureSearchPath(); w3->update(0); diff --git a/src/states_screens/soccer_setup_screen.cpp b/src/states_screens/soccer_setup_screen.cpp index 8e723a54801..994e11fcd3c 100644 --- a/src/states_screens/soccer_setup_screen.cpp +++ b/src/states_screens/soccer_setup_screen.cpp @@ -158,7 +158,7 @@ void SoccerSetupScreen::beforeAddingWidget() core::matrix4 model_location; model_location.setScale(core::vector3df(35.0f, 35.0f, 35.0f)); // Add the kart model (including wheels and speed weight objects) - kart_view->addModel(kart_model.getModel(), model_location, + kart_view->addModel(kart_model.getModel(), model_location, kart_model.getBaseFrame(), kart_model.getBaseFrame(), kart_model.getBaseFrame()); model_location.setScale(core::vector3df(1.0f, 1.0f, 1.0f)); @@ -181,7 +181,7 @@ void SoccerSetupScreen::beforeAddingWidget() kart_model.getInverseBoneMatrix(obj.m_bone_name); swol = inv * obj.m_location; } - kart_view->addModel(obj.m_model, swol, -1, -1, 0.0f, + kart_view->addModel(obj.m_model, swol, -1, -1, -1, 0.0f, obj.m_bone_name); } From 7c63bcbc11637b887463d6686f3e7f35f879f0ff Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:00:02 +0100 Subject: [PATCH 169/188] Adjust speed and drift for low difficulties The goal of these changes is to slightly reduce the gaps between people using drifting and those not using it, and to raise slightly the floor of expected speeds in novice without raising the ceiling. - Reduce the skid speed bonus for novice and casual. - Increase the base speed for novice (SuperTux is now 60% faster instead of 66.6%) - Adjust the turn speed-factor in consequence --- data/kart_characteristics.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index f37b22cec63..24b7f7cb5fb 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -393,14 +393,14 @@ - - + + - + + speed-factor="*1.26" /> @@ -409,7 +409,7 @@ - + From 04bb887e67675604c268f3ecda45b9c3d1f30f40 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 23 Nov 2025 00:52:32 +0100 Subject: [PATCH 170/188] Better account for speed in kart on kart collisions - Some stylistic changes and tweaks to make the code more concise. We can delay computing f_left until after the ramming_factor is known. - The cap on the speed factor is increased, from a max value of 1.6 to a max value of 2.0f. - The speed factor logic is changed to look at the absolute speed difference instead of the ratio. The ratio tended to overemphasize small speed differences at low speeds and to undervalue moderate speed differences at high speeds. The absolute speed difference model fixes that, and is arguably just as intuitive and more physically correct. To give some examples, a speed of 36 against a speed of 27 now gives a factor of 1.5 instead of 1.33, while a speed of 15 against a speed of 9 gives a factor of 1.33 instead of 1.6. --- src/physics/physics.cpp | 58 +++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index 92e383a84dd..8787f401877 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -426,10 +426,8 @@ void Physics::update(int ticks) * \param contact_point_b Location of collision at second kart (in kart * coordinates). */ -void Physics::KartKartCollision(Kart *kart_a, - const Vec3 &contact_point_a, - Kart *kart_b, - const Vec3 &contact_point_b) +void Physics::KartKartCollision(Kart *kart_a, const Vec3 &contact_point_a, + Kart *kart_b, const Vec3 &contact_point_b) { // Only one kart needs to handle the attachments, it will // fix the attachments for the other kart. @@ -445,8 +443,7 @@ void Physics::KartKartCollision(Kart *kart_a, // push to right), but that can lead to both karts being pushed in the // same direction (front left of kart hits rear left). // So we just use a simple test (which does the right thing in ideal - // crashes, but avoids pushing both karts in corner cases - // - pun intended ;) ). + // crashes, but avoids pushing both karts in corner cases - pun intended ;) ). if(contact_point_a.getX() < contact_point_b.getX()) { @@ -476,30 +473,24 @@ void Physics::KartKartCollision(Kart *kart_a, ? left_kart->getKartProperties()->getMass() / right_kart->getKartProperties()->getMass() : 2.0f; - - if (f_right > 2.0f) - f_right = 2.0f; - if (f_right < 0.5f) - f_right = 0.5f; - - // Add a scaling factor depending on speed (avoid div by 0) - // The impulse is weaker for the kart going faster, - // stronger for the kart going slower. - float speed_impulse_factor = right_kart->getSpeed() > 0 ? - left_kart->getSpeed() / right_kart->getSpeed() : 1.6f; - if (speed_impulse_factor > 1.6f) - speed_impulse_factor = 1.6f; - if (speed_impulse_factor < 0.625f) - speed_impulse_factor = 0.625f; + f_right = std::min(2.0f, std::max(0.5f, f_right)); + + // Add a scaling factor depending on speed, making the impulse + // weaker for the kart going faster and stronger for the kart going slower. + // Negative speeds (going backwards) count as 0 speed. + // We compute the speed difference and clamp it to [-18, 18]. + // We then obtain the strength of the effect in the [1, 2] range, + // and invert the result if the right kart is the faster one. + // The final result is in the [0.5, 2] range. + float left_ref_speed = std::max(0.0f, left_kart->getSpeed()); + float right_ref_speed = std::max(0.0f, right_kart->getSpeed()); + float speed_diff = std::min(18.0f, std::max(-18.0f, (left_ref_speed - right_ref_speed))); + float speed_impulse_factor = fabsf(speed_diff / 18.0f) + 1.0f; + if (speed_diff < 0) + speed_impulse_factor = 1 / speed_impulse_factor; f_right *= speed_impulse_factor; - // Because of the caps on the mass and speed factors, - // f_right is effectively capped to the interval [0.3125, 3.2] - // f_left is capped in the same interval as 3.2*0.3125 = 1 - - float f_left = 1/f_right; - // Check if a kart is more 'actively' trying to push another kart // To do this we check two things: // - The position of the contact point on the kart body. @@ -540,18 +531,17 @@ void Physics::KartKartCollision(Kart *kart_a, // Now heading difference is in the range [0, pi/2] (approximately) // With 0 indicating parallel karts and pi/2 perpendicular karts // We set ramming_factor in the [1, 1+pi/2] range - float ramming_factor = 1.0f + (heading_difference * 1.0f); + float ramming_factor = 1.0f + (heading_difference); if (left_kart_ramming) - { - f_left = f_left / ramming_factor; f_right = f_right * ramming_factor; - } else if (right_kart_ramming) - { f_right = f_right / ramming_factor; - f_left = f_left * ramming_factor; - } + + // Because the speed, mass and ramming factors are + // each in an interval of the form [1/x; x] (with x > 1), + // f_left is capped in the same interval as f_right + float f_left = 1/f_right; // Reduce the bouncing if relative velocity is low const Vec3 right_velocity = right_kart->getVelocity(); From d83aacc6e3042b51540b1bd648bb660f167913df Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 23 Nov 2025 01:15:36 +0100 Subject: [PATCH 171/188] Minor cleanup in the kart-kart collision code - Add a separate function to apply the kart collision impulse, and call it on both the left and right kart instead of using mostly duplicated code. - Also silence a warning. - Add a comment to clarify the difference between velocity and speed and improve some other comments. --- src/physics/physics.cpp | 57 +++++++++++++++++++---------------------- src/physics/physics.hpp | 3 +++ 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index 8787f401877..09fd279c654 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -192,11 +192,12 @@ void Physics::update(int ticks) // fixed frequency necessary for the physics update, we need to do exactly // one physic step only. double start; - if(UserConfigParams::m_physics_debug) start = StkTime::getRealTime(); + bool const physics_debug = UserConfigParams::m_physics_debug; // Used to silence a warning + if (physics_debug) start = StkTime::getRealTime(); m_dynamics_world->stepSimulation(stk_config->ticks2Time(1), 1, stk_config->ticks2Time(1) ); - if (UserConfigParams::m_physics_debug) + if (physics_debug) { Log::verbose("Physics", "At %d physics duration %12.8f", World::getWorld()->getTicksSinceStart(), @@ -544,6 +545,7 @@ void Physics::KartKartCollision(Kart *kart_a, const Vec3 &contact_point_a, float f_left = 1/f_right; // Reduce the bouncing if relative velocity is low + // Relative velocity accounts for both speed and direction const Vec3 right_velocity = right_kart->getVelocity(); const Vec3 left_velocity = left_kart->getVelocity(); const Vec3 velocity_difference = right_velocity - left_velocity; @@ -560,41 +562,34 @@ void Physics::KartKartCollision(Kart *kart_a, const Vec3 &contact_point_a, float lean_factor = std::min(1.25f, std::max(f_right, f_left)) * 0.8f; - // First push one kart to the left (if there is not already + // First push one kart to the right (if there is not already // an impulse happening - one collision might cause more // than one impulse otherwise) if(right_kart->getVehicle()->getCentralImpulseTicks()<=0) - { - const KartProperties *kp = left_kart->getKartProperties(); - Vec3 impulse(kp->getCollisionImpulse()*f_right, 0, 0); - impulse = right_kart->getTrans().getBasis() * impulse; - right_kart->getVehicle() - ->setTimedCentralImpulse( - (uint16_t)stk_config->time2Ticks(kp->getCollisionImpulseTime() * impulse_time_factor), - impulse); - right_kart->getVehicle()->setCollisionLean(/* towards the right*/ true); - right_kart->getVehicle()->setCollisionLeanFactor(lean_factor); - // The kart rotation will be prevented as the impulse is applied - } - - // Then push the other kart to the right (if there is no - // impulse happening atm). + applyKartCollisionImpulse(right_kart, true, f_right, lean_factor, impulse_time_factor); + + // Then push the other kart to the left (if there is no impulse happening atm). + // The force is made negative to obtain the opposite impulse direction. if(left_kart->getVehicle()->getCentralImpulseTicks()<=0) - { - const KartProperties *kp = right_kart->getKartProperties(); - Vec3 impulse = Vec3(-kp->getCollisionImpulse()*f_left, 0, 0); - impulse = left_kart->getTrans().getBasis() * impulse; - left_kart->getVehicle() - ->setTimedCentralImpulse( - (uint16_t)stk_config->time2Ticks(kp->getCollisionImpulseTime() * impulse_time_factor), - impulse); - left_kart->getVehicle()->setCollisionLean(/* towards the right*/ false); - left_kart->getVehicle()->setCollisionLeanFactor(lean_factor); - // The kart rotation will be prevented as the impulse is applied - } - + applyKartCollisionImpulse(left_kart, false, -f_left, lean_factor, impulse_time_factor); } // KartKartCollision +//----------------------------------------------------------------------------- +/** This function applies the impulse for a collision between karts + * */ +void Physics::applyKartCollisionImpulse(Kart *kart, bool right, float force, + float lean_factor, float time_factor) +{ + const KartProperties *kp = kart->getKartProperties(); + Vec3 impulse(kp->getCollisionImpulse() * force, 0, 0); + impulse = kart->getTrans().getBasis() * impulse; + uint16_t duration = (uint16_t)stk_config->time2Ticks(kp->getCollisionImpulseTime() * time_factor); + kart->getVehicle()->setTimedCentralImpulse(duration, impulse); + kart->getVehicle()->setCollisionLean(/* towards the right*/ right); + kart->getVehicle()->setCollisionLeanFactor(lean_factor); + // The kart rotation will be prevented as the impulse is applied +} // applyKartCollisionImpulse + //----------------------------------------------------------------------------- /** This function is called at each internal bullet timestep. It is used * here to do the collision handling: using the contact manifolds after a diff --git a/src/physics/physics.hpp b/src/physics/physics.hpp index 1bfb3ea9624..cd958cbf587 100644 --- a/src/physics/physics.hpp +++ b/src/physics/physics.hpp @@ -150,6 +150,9 @@ class Physics : public btSequentialImpulseConstraintSolver Physics(); virtual ~Physics(); + void applyKartCollisionImpulse(Kart *kart, bool right, float force, + float lean_factor, float time_factor); + public: // ---------------------------------------------------------------------------------------- static Physics* get(); From 92e8f7d849c8567c87df31e4261e9cbf538c5ad2 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Mon, 24 Nov 2025 10:22:49 +0100 Subject: [PATCH 172/188] Change how mass influences kart collisions Previously, a kart that was X times heavier than another would receive an impulsion 1/X times stronger while the other kart would receive an impulsion X times stronger, leading to a high compounded differences. For example, a 40% disparity in mass would result for example in an almost 100% disparity in impulse strength. It now results in a 65% disparity in impulse strength. This commit also makes related adjustments: - As the peak impulse strength (with a heavy hitting a light kart) has been reduced by 13%, the base strength of collisions is increased by ~8%. - The peak bonus from speed difference is obtained with a differential of 21 or more, instead of 18 or more. This reduces somewhat the capacity of a lighter kart with some extra speed to push off a heavier kart. --- data/stk_config.xml | 2 +- src/physics/physics.cpp | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/data/stk_config.xml b/data/stk_config.xml index 778a9b93f92..0409bf8f611 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -495,7 +495,7 @@ outside of the chassis and results in more stable physical behaviour of the karts. --> diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index 09fd279c654..e76e14a3423 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -473,20 +473,29 @@ void Physics::KartKartCollision(Kart *kart_a, const Vec3 &contact_point_a, float f_right = right_kart->getKartProperties()->getMass() > 0 ? left_kart->getKartProperties()->getMass() / right_kart->getKartProperties()->getMass() - : 2.0f; + : 4.0f; + // The impulse being X times stronger for one kart means it will + // be 1/X times stronger for the other kart, leading to a X² ratio. + // To keep impulse strength ratio and mass ratio equal, we should + // take the square root. + // However, with the current kart classes, that would make differences + // feel too weak, and changing masses would require a lot of adjustments. + // We currently take a middle ground approach for gameplay reasons. + float sqrt_right = sqrtf(f_right); + f_right = sqrt_right * sqrtf(sqrt_right); f_right = std::min(2.0f, std::max(0.5f, f_right)); // Add a scaling factor depending on speed, making the impulse // weaker for the kart going faster and stronger for the kart going slower. // Negative speeds (going backwards) count as 0 speed. - // We compute the speed difference and clamp it to [-18, 18]. + // We compute the speed difference and clamp it to [-21, 21]. // We then obtain the strength of the effect in the [1, 2] range, // and invert the result if the right kart is the faster one. // The final result is in the [0.5, 2] range. float left_ref_speed = std::max(0.0f, left_kart->getSpeed()); float right_ref_speed = std::max(0.0f, right_kart->getSpeed()); - float speed_diff = std::min(18.0f, std::max(-18.0f, (left_ref_speed - right_ref_speed))); - float speed_impulse_factor = fabsf(speed_diff / 18.0f) + 1.0f; + float speed_diff = std::min(21.0f, std::max(-21.0f, (left_ref_speed - right_ref_speed))); + float speed_impulse_factor = fabsf(speed_diff / 21.0f) + 1.0f; if (speed_diff < 0) speed_impulse_factor = 1 / speed_impulse_factor; From 68b557beb8d98bda13cdc22bd245666f95c087c7 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:40:35 +0100 Subject: [PATCH 173/188] Remove all the code for terrain impulses This code was supposed to help improve the outcome of karts colliding against track obstacles, but it didn't work properly. The 'normal' impulse type was already de facto disabled, as the game was applying an impulse of duration 0 and 0 strength. Meanwhile, the 'impulse to driveline' was a fundamentally flawed concept as it came with unescapable edge cases, and it was not enabled either (since stk_config was set to use the normal impulse). --- data/stk_config.xml | 15 +--------- src/karts/kart.cpp | 55 ----------------------------------- src/karts/kart_properties.cpp | 20 +------------ src/karts/kart_properties.hpp | 27 ----------------- src/physics/btKart.hpp | 6 ++-- 5 files changed, 4 insertions(+), 119 deletions(-) diff --git a/data/stk_config.xml b/data/stk_config.xml index 0409bf8f611..7d43c94beba 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -454,18 +454,6 @@ - diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 629f8968d9a..88fda956fd1 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -2866,61 +2866,6 @@ void Kart::crashed(const Material *m, const Vec3 &normal) } #endif - const LinearWorld *lw = dynamic_cast(World::getWorld()); - if(m_kart_properties->getTerrainImpulseType() - ==KartProperties::IMPULSE_NORMAL && - m_vehicle->getCentralImpulseTicks()<=0 ) - { - // Restrict impule to plane defined by gravity (i.e. X/Z plane). - // This avoids the problem that karts can be pushed up, e.g. above - // a fence. - btVector3 gravity = m_body->getGravity(); - gravity.normalize(); - Vec3 impulse = normal - gravity* btDot(normal, gravity); - if(impulse.getX() || impulse.getZ()) - impulse.normalize(); - else - impulse = Vec3(0, 0, -1); // Arbitrary - // Impulse depends of kart speed - and speed can be negative - // If the speed is too low, karts can still get stuck into a wall - // so make sure there is always enough impulse to avoid this - float abs_speed = fabsf(getSpeed()); - impulse *= ( abs_speed<10 ? 10.0f : sqrt(abs_speed) ) - * m_kart_properties->getCollisionTerrainImpulse(); - m_bounce_back_ticks = 0; - impulse = Vec3(0, 0, 0); - //m_vehicle->setTimedCentralImpulse(0.1f, impulse); - m_vehicle->setTimedCentralImpulse(0, impulse); - } - // If there is a quad graph, push the kart towards the previous - // graph node center (we have to use the previous point since the - // kart might have only now reached the new quad, meaning the kart - // would be pushed forward). - else if(m_kart_properties->getTerrainImpulseType() - ==KartProperties::IMPULSE_TO_DRIVELINE && - lw && m_vehicle->getCentralImpulseTicks()<=0 && - Track::getCurrentTrack()->isPushBackEnabled()) - { - int sector = lw->getSectorForKart(this); - if(sector!=Graph::UNKNOWN_SECTOR) - { - // Use the first predecessor node, which is the most - // natural one (i.e. the one on the main driveline). - const DriveNode* dn = DriveGraph::get()->getNode( - DriveGraph::get()->getNode(sector)->getPredecessor(0)); - Vec3 impulse = dn->getCenter() - getXYZ(); - impulse.setY(0); - if(impulse.getX() || impulse.getZ()) - impulse.normalize(); - else - impulse = Vec3(0, 0, -1); // Arbitrary - impulse *= m_kart_properties->getCollisionTerrainImpulse(); - m_bounce_back_ticks = (uint8_t)stk_config->time2Ticks(0.2f); - m_vehicle->setTimedCentralImpulse( - (uint16_t)stk_config->time2Ticks(0.1f), impulse); - } - - } /** If a kart is crashing against the track, the collision is often * reported more than once, resulting in a machine gun effect, and too * long disabling of the engine. Therefore, this reaction is disabled diff --git a/src/karts/kart_properties.cpp b/src/karts/kart_properties.cpp index 2894dfac166..462385327a2 100644 --- a/src/karts/kart_properties.cpp +++ b/src/karts/kart_properties.cpp @@ -94,11 +94,10 @@ KartProperties::KartProperties(const std::string &filename) // Set all other values to undefined, so that it can later be tested // if everything is defined properly. - m_wheel_base = m_friction_slip = m_collision_terrain_impulse = + m_wheel_base = m_friction_slip = m_collision_impulse = m_collision_impulse_time = m_physical_wheel_position = UNDEFINED; - m_terrain_impulse_type = IMPULSE_NONE; m_gravity_center_shift = Vec3(UNDEFINED); m_bevel_factor = Vec3(UNDEFINED); m_version = 0; @@ -526,25 +525,9 @@ void KartProperties::getAllData(const XMLNode * root, bool called_from_stk_confi { collision_node->get("impulse", &m_collision_impulse ); collision_node->get("impulse-time", &m_collision_impulse_time ); - collision_node->get("terrain-impulse", &m_collision_terrain_impulse); collision_node->get("restitution", &m_restitution ); collision_node->get("bevel-factor", &m_bevel_factor ); collision_node->get("physical-wheel-position",&m_physical_wheel_position); - std::string s; - collision_node->get("impulse-type", &s ); - s = StringUtils::toLowerCase(s); - if(s=="none") - m_terrain_impulse_type = IMPULSE_NONE; - else if(s=="normal") - m_terrain_impulse_type = IMPULSE_NORMAL; - else if(s=="driveline") - m_terrain_impulse_type = IMPULSE_TO_DRIVELINE; - else - { - Log::fatal("[KartProperties]", - "Missing or incorrect value for impulse-type: '%s'.", - s.c_str()); - } } //TODO: wheel front right and wheel front left is not loaded, yet is @@ -676,7 +659,6 @@ void KartProperties::checkAllSet(const std::string &filename) } CHECK_NEG(m_friction_slip, "friction slip" ); - CHECK_NEG(m_collision_terrain_impulse, "collision terrain-impulse" ); CHECK_NEG(m_collision_impulse, "collision impulse" ); CHECK_NEG(m_collision_impulse_time, "collision impulse-time" ); CHECK_NEG(m_physical_wheel_position, "collision physical-wheel-position"); diff --git a/src/karts/kart_properties.hpp b/src/karts/kart_properties.hpp index 5a6566f0440..5b952cdfad2 100644 --- a/src/karts/kart_properties.hpp +++ b/src/karts/kart_properties.hpp @@ -166,21 +166,6 @@ class KartProperties /** Shift of center of gravity. */ Vec3 m_gravity_center_shift; -public: - /** STK can add an impulse to push karts away from the track in case - * of a kart-track collision. This can be done in two ways: either - * apply the impulse in the direction of the normal, or towards the - * driveline. The later works nice as long as the kart is driving - * on the main track, but can work very bad if the kart is drivling - * off-track (and a wrong driveline is selected). */ - enum TerrainImpulseType {IMPULSE_NONE, IMPULSE_NORMAL, - IMPULSE_TO_DRIVELINE}; -private: - TerrainImpulseType m_terrain_impulse_type; - - /** An additional impulse to push a kart away if it hits terrain */ - float m_collision_terrain_impulse; - /** An additiojnal artificial impulse that pushes two karts in a * side-side collision away from each other. */ float m_collision_impulse; @@ -324,18 +309,6 @@ class KartProperties /** Returns a shift of the center of mass (lowering the center of mass * makes the karts more stable. */ const Vec3&getGravityCenterShift() const {return m_gravity_center_shift; } - - // ------------------------------------------------------------------------ - /** Returns an artificial impulse to push karts away from the terrain - * it hits. */ - float getCollisionTerrainImpulse() const - {return m_collision_terrain_impulse;} - - // ------------------------------------------------------------------------ - /** Returns what kind of impulse STK should use in case of a kart-track - * collision. */ - TerrainImpulseType getTerrainImpulseType() const - { return m_terrain_impulse_type; } // ------------------------------------------------------------------------ /** Returns the (artificial) collision impulse this kart will apply * to another kart in case of a non-frontal collision. */ diff --git a/src/physics/btKart.hpp b/src/physics/btKart.hpp index 8288edee8a0..d427727f5c2 100644 --- a/src/physics/btKart.hpp +++ b/src/physics/btKart.hpp @@ -252,14 +252,12 @@ class btKart : public btActionInterface void setCollisionLeanFactor(float lean_factor) { m_leaning_factor = lean_factor; } // ------------------------------------------------------------------------ /** Returns the time an additional impulse is activated. */ - uint16_t getCentralImpulseTicks() const - { return m_ticks_additional_impulse; } + uint16_t getCentralImpulseTicks() const { return m_ticks_additional_impulse; } // ------------------------------------------------------------------------ /** Returns the collision (visual) lean. */ float getCollisionLean() const; // ------------------------------------------------------------------------ - const btVector3& getAdditionalImpulse() const - { return m_additional_impulse; } + const btVector3& getAdditionalImpulse() const { return m_additional_impulse; } // ------------------------------------------------------------------------ /** Sets a rotation that is applied over a certain amount of time (to avoid * a too rapid changes in the kart). From 4fbe4eb411f013053d31e5be2974def7f5d02023 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Mon, 24 Nov 2025 15:51:24 +0100 Subject: [PATCH 174/188] Allow karts to bounce again before they finish their current bounce - We add a separate impulse lock counter, to prevent triggering multiple impulses from the same kart-kart collision. - Karts are prevented from bouncing from another kart only if the impulse lock is still on, irrespective of the remaaining impulse time. This means that if another kart collides with a kart that just got bounced before it has finished with its impulse, it will be bounced back. The new impulse overrides any remnant of the previous impulse. - If a kart is still under collision impulse and collides with another kart, its pushing strength is reduced by half. - If the bounce force is not too strong, we shorten the duration somewhat and increase the impulse intensity. The special-case duration shortening for low-velocity impact is removed. --- src/physics/btKart.cpp | 3 +++ src/physics/btKart.hpp | 16 +++++++++++--- src/physics/physics.cpp | 49 ++++++++++++++++++++++++----------------- src/physics/physics.hpp | 3 +-- 4 files changed, 46 insertions(+), 25 deletions(-) diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp index f3029a9c63c..15bb342f291 100644 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -117,6 +117,7 @@ void btKart::reset() m_additional_impulse = btVector3(0,0,0); m_ticks_additional_impulse = 0; m_ticks_total_impulse = 0; + m_ticks_lock_impulse = 0; m_additional_rotation = 0; m_ticks_additional_rotation = 0; m_max_speed = -1.0f; @@ -540,6 +541,8 @@ void btKart::updateVehicle( btScalar step ) // Prevent the kart from getting rotated m_kart->getBody()->setAngularVelocity(btVector3(0,0,0)); m_ticks_additional_impulse--; + if (m_ticks_lock_impulse != 0) + m_ticks_lock_impulse--; } // Apply additional rotation set by supertuxkart diff --git a/src/physics/btKart.hpp b/src/physics/btKart.hpp index d427727f5c2..5c19f77bf42 100644 --- a/src/physics/btKart.hpp +++ b/src/physics/btKart.hpp @@ -94,6 +94,13 @@ class btKart : public btActionInterface * impulse suddenly stopping is very noticeable with strong impulses. */ uint16_t m_ticks_total_impulse; + /** The duration during which the active impulse cannot be replaced by another. + * When two karts collide, they keep touching for several consecutive frames + * and reapplying the impulse after the first touch would be wrong. + * But if a kart got an impulse from one kart, we don't want a contact with + * another kart soon after to not generate any impulse.*/ + uint16_t m_ticks_lock_impulse; + /** Additional rotation in y-axis that is applied over a certain amount of time. */ float m_additional_rotation; @@ -240,12 +247,13 @@ class btKart : public btActionInterface void setTimedCentralImpulse(uint16_t t, const btVector3 &imp, bool rewind = false) { - // Only add impulse if no other impulse is active. - if (m_ticks_additional_impulse > 0 && !rewind) return; + // Only add impulse if the impulse lock is not on. + if (isImpulseLocked() && !rewind) return; m_additional_impulse = imp; m_ticks_total_impulse = t; m_ticks_additional_impulse = t; - } // setTimedImpulse + m_ticks_lock_impulse = std::min(t, (uint16_t)stk_config->time2Ticks(0.075f)); + } // setTimedCentralImpulse // ------------------------------------------------------------------------ void setCollisionLean(bool lean_right) { m_leaning_right = lean_right; } // ------------------------------------------------------------------------ @@ -254,6 +262,8 @@ class btKart : public btActionInterface /** Returns the time an additional impulse is activated. */ uint16_t getCentralImpulseTicks() const { return m_ticks_additional_impulse; } // ------------------------------------------------------------------------ + bool isImpulseLocked() const { return m_ticks_lock_impulse > 0; } + // ------------------------------------------------------------------------ /** Returns the collision (visual) lean. */ float getCollisionLean() const; // ------------------------------------------------------------------------ diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index e76e14a3423..00b8a66409c 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -461,9 +461,9 @@ void Physics::KartKartCollision(Kart *kart_a, const Vec3 &contact_point_a, right_kart_contact_Z = (kart_b->getKartLength()/2 + contact_point_b.getZ()) / kart_b->getKartLength(); } - // If both karts have an active impulse, we can return immediately - if(right_kart->getVehicle()->getCentralImpulseTicks() > 0 && - left_kart->getVehicle()->getCentralImpulseTicks() > 0) + // If both karts are impulse-locked, we can return immediately + if(right_kart->getVehicle()->isImpulseLocked() && + left_kart->getVehicle()->isImpulseLocked()) return; // Add a scaling factor depending on the mass (avoid div by zero). @@ -553,42 +553,51 @@ void Physics::KartKartCollision(Kart *kart_a, const Vec3 &contact_point_a, // f_left is capped in the same interval as f_right float f_left = 1/f_right; - // Reduce the bouncing if relative velocity is low + // Reduce the bouncing under some conditions + // 1 - If the other kart is still under the influence of a bounce + if(left_kart->getVehicle()->getCentralImpulseTicks() > 0) + f_right *= 0.5f; + if(right_kart->getVehicle()->getCentralImpulseTicks() > 0) + f_left *= 0.5f; + + // 2 - If relative velocity is low // Relative velocity accounts for both speed and direction - const Vec3 right_velocity = right_kart->getVelocity(); - const Vec3 left_velocity = left_kart->getVelocity(); - const Vec3 velocity_difference = right_velocity - left_velocity; + const Vec3 velocity_difference = right_kart->getVelocity() - left_kart->getVelocity(); const float velocity_value = velocity_difference.length(); - float impulse_time_factor = 1.0f; - if (velocity_value < 8.0f) // velocity_value is never negative { f_right *= 0.375f + velocity_value / 12.8f; f_left *= 0.375f + velocity_value / 12.8f; - impulse_time_factor = 0.6875f + velocity_value / 25.6f; } float lean_factor = std::min(1.25f, std::max(f_right, f_left)) * 0.8f; - // First push one kart to the right (if there is not already - // an impulse happening - one collision might cause more - // than one impulse otherwise) - if(right_kart->getVehicle()->getCentralImpulseTicks()<=0) - applyKartCollisionImpulse(right_kart, true, f_right, lean_factor, impulse_time_factor); + // First push one kart to the right + applyKartCollisionImpulse(right_kart, true, f_right, lean_factor); - // Then push the other kart to the left (if there is no impulse happening atm). + // Then push the other kart to the left. // The force is made negative to obtain the opposite impulse direction. - if(left_kart->getVehicle()->getCentralImpulseTicks()<=0) - applyKartCollisionImpulse(left_kart, false, -f_left, lean_factor, impulse_time_factor); + applyKartCollisionImpulse(left_kart, false, -f_left, lean_factor); } // KartKartCollision //----------------------------------------------------------------------------- /** This function applies the impulse for a collision between karts * */ -void Physics::applyKartCollisionImpulse(Kart *kart, bool right, float force, - float lean_factor, float time_factor) +void Physics::applyKartCollisionImpulse(Kart *kart, bool right, float force, float lean_factor) { + if(kart->getVehicle()->isImpulseLocked()) + return; + + // If the force is weak, we increase the intensity but decrease the duration + float time_factor = 1.0f; + if (fabsf(force) < 2.0f) + { + float shorten_factor = (4.0f - fabsf(force)) / 2.0f; + force = force * shorten_factor; + time_factor = time_factor / shorten_factor; + } + const KartProperties *kp = kart->getKartProperties(); Vec3 impulse(kp->getCollisionImpulse() * force, 0, 0); impulse = kart->getTrans().getBasis() * impulse; diff --git a/src/physics/physics.hpp b/src/physics/physics.hpp index cd958cbf587..b8d9babf887 100644 --- a/src/physics/physics.hpp +++ b/src/physics/physics.hpp @@ -150,8 +150,7 @@ class Physics : public btSequentialImpulseConstraintSolver Physics(); virtual ~Physics(); - void applyKartCollisionImpulse(Kart *kart, bool right, float force, - float lean_factor, float time_factor); + void applyKartCollisionImpulse(Kart *kart, bool right, float force, float lean_factor); public: // ---------------------------------------------------------------------------------------- From cd926101bf1dbd40f73a6fdfe6702ae065e3573f Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Tue, 25 Nov 2025 18:07:52 +0100 Subject: [PATCH 175/188] Code cleanup in SoccerWorld - Force GCC to not spit out 'maybe unitialized' warnings for sd.m_country_code. Even with clear unambiguous initialization, a spurious and verbose warning is being produced with GCC 12.5 - Remove all the extra code that was handling compatibility with pre-1.1 releases that had some missing information in the network string. - Reduce code duplication in multiple spots - Improve some comments --- data/stk_config.xml | 1 - src/modes/soccer_world.cpp | 136 +++++++++++++------------------------ src/modes/soccer_world.hpp | 2 +- 3 files changed, 49 insertions(+), 90 deletions(-) diff --git a/data/stk_config.xml b/data/stk_config.xml index 7d43c94beba..ff148dd934c 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -541,7 +541,6 @@ --> - diff --git a/src/modes/soccer_world.cpp b/src/modes/soccer_world.cpp index de9c403e46c..a9011afc128 100644 --- a/src/modes/soccer_world.cpp +++ b/src/modes/soccer_world.cpp @@ -514,11 +514,13 @@ void SoccerWorld::onCheckGoalTriggered(bool first_goal) sd.m_player = getKart(m_ball_hitter)->getController() ->getName(false/*include_handicap_string*/); sd.m_handicap_level = getKart(m_ball_hitter)->getHandicap(); + sd.m_country_code = ""; if (RaceManager::get()->getKartGlobalPlayerId(m_ball_hitter) > -1) { sd.m_country_code = RaceManager::get()->getKartInfo(m_ball_hitter).getCountryCode(); } + if (sd.m_correct_goal) { m_karts[m_ball_hitter]->getKartModel() @@ -545,28 +547,22 @@ void SoccerWorld::onCheckGoalTriggered(bool first_goal) } #endif + if (RaceManager::get()->hasTimeTarget()) + sd.m_time = RaceManager::get()->getTimeTarget() - getTime(); + else + sd.m_time = getTime(); + +// Shut up GCC complaining incorrectly about sd.m_country_code +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" + // First goal being true means that the ball entered the + // blue goal, meaning that the red team scored. if (first_goal) - { - if (RaceManager::get()->hasTimeTarget()) - { - sd.m_time = RaceManager::get()->getTimeTarget() - getTime(); - } - else - sd.m_time = getTime(); - // Notice: true first_goal means it's blue goal being shoot, - // so red team can score m_red_scorers.push_back(sd); - } else - { - if (RaceManager::get()->hasTimeTarget()) - { - sd.m_time = RaceManager::get()->getTimeTarget() - getTime(); - } - else - sd.m_time = getTime(); m_blue_scorers.push_back(sd); - } +#pragma GCC diagnostic pop + if (NetworkConfig::get()->isNetworking() && NetworkConfig::get()->isServer()) { @@ -576,26 +572,14 @@ void SoccerWorld::onCheckGoalTriggered(bool first_goal) .addUInt8((uint8_t)sd.m_id).addUInt8(sd.m_correct_goal) .addUInt8(first_goal).addFloat(sd.m_time) .addTime(m_ticks_back_to_own_goal) - .encodeString(sd.m_kart).encodeString(sd.m_player); - // Added in 1.1, add missing handicap info and country code - NetworkString p_1_1 = p; - p_1_1.encodeString(sd.m_country_code) + .encodeString(sd.m_kart).encodeString(sd.m_player) + .encodeString(sd.m_country_code) .addUInt8(sd.m_handicap_level); auto peers = STKHost::get()->getPeers(); for (auto& peer : peers) { if (peer->isValidated() && !peer->isWaitingForGame()) - { - if (peer->getClientCapabilities().find("soccer_fixes") != - peer->getClientCapabilities().end()) - { - peer->sendPacket(&p_1_1, true/*reliable*/); - } - else - { - peer->sendPacket(&p, true/*reliable*/); - } - } + peer->sendPacket(&p, true/*reliable*/); } } } @@ -634,22 +618,17 @@ void SoccerWorld::handlePlayerGoalFromServer(const NetworkString& ns) int ticks_back_to_own_goal = ns.getTime(); ns.decodeString(&sd.m_kart); ns.decodeStringW(&sd.m_player); - // Added in 1.1, add missing handicap info and country code - if (NetworkConfig::get()->getServerCapabilities().find("soccer_fixes") - != NetworkConfig::get()->getServerCapabilities().end()) - { - ns.decodeString(&sd.m_country_code); - sd.m_handicap_level = (HandicapLevel)ns.getUInt8(); - } + ns.decodeString(&sd.m_country_code); + sd.m_handicap_level = (HandicapLevel)ns.getUInt8(); +// Shut up GCC complaining incorrectly about sd.m_country_code +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" if (first_goal) - { m_red_scorers.push_back(sd); - } else - { m_blue_scorers.push_back(sd); - } +#pragma GCC diagnostic pop if (ticks_now >= ticks_back_to_own_goal && !isStartPhase()) { @@ -1069,38 +1048,31 @@ void SoccerWorld::enterRaceOverState() // ---------------------------------------------------------------------------- void SoccerWorld::saveCompleteState(BareNetworkString* bns, STKPeer* peer) { - const unsigned red_scorers = (unsigned)m_red_scorers.size(); - bns->addUInt32(red_scorers); + const uint8_t red_scorers = (uint8_t) m_red_scorers.size(); + bns->addUInt8(red_scorers); + const uint8_t blue_scorers = (uint8_t)m_blue_scorers.size(); + bns->addUInt8(blue_scorers); + for (unsigned i = 0; i < red_scorers; i++) { bns->addUInt8((uint8_t)m_red_scorers[i].m_id) .addUInt8(m_red_scorers[i].m_correct_goal) .addFloat(m_red_scorers[i].m_time) .encodeString(m_red_scorers[i].m_kart) - .encodeString(m_red_scorers[i].m_player); - if (peer->getClientCapabilities().find("soccer_fixes") != - peer->getClientCapabilities().end()) - { - bns->encodeString(m_red_scorers[i].m_country_code) - .addUInt8(m_red_scorers[i].m_handicap_level); - } + .encodeString(m_red_scorers[i].m_player) + .encodeString(m_red_scorers[i].m_country_code) + .addUInt8(m_red_scorers[i].m_handicap_level); } - const unsigned blue_scorers = (unsigned)m_blue_scorers.size(); - bns->addUInt32(blue_scorers); for (unsigned i = 0; i < blue_scorers; i++) { bns->addUInt8((uint8_t)m_blue_scorers[i].m_id) .addUInt8(m_blue_scorers[i].m_correct_goal) .addFloat(m_blue_scorers[i].m_time) .encodeString(m_blue_scorers[i].m_kart) - .encodeString(m_blue_scorers[i].m_player); - if (peer->getClientCapabilities().find("soccer_fixes") != - peer->getClientCapabilities().end()) - { - bns->encodeString(m_blue_scorers[i].m_country_code) - .addUInt8(m_blue_scorers[i].m_handicap_level); - } + .encodeString(m_blue_scorers[i].m_player) + .encodeString(m_blue_scorers[i].m_country_code) + .addUInt8(m_blue_scorers[i].m_handicap_level); } bns->addTime(m_reset_ball_ticks).addTime(m_ticks_back_to_own_goal); } // saveCompleteState @@ -1111,8 +1083,10 @@ void SoccerWorld::restoreCompleteState(const BareNetworkString& b) m_red_scorers.clear(); m_blue_scorers.clear(); - const unsigned red_size = b.getUInt32(); - for (unsigned i = 0; i < red_size; i++) + const unsigned red_size = (unsigned)b.getUInt8(); + const unsigned blue_size = (unsigned)b.getUInt8(); + const unsigned total_size = red_size + blue_size; + for (unsigned i = 0; i < total_size; i++) { ScorerData sd; sd.m_id = b.getUInt8(); @@ -1120,32 +1094,18 @@ void SoccerWorld::restoreCompleteState(const BareNetworkString& b) sd.m_time = b.getFloat(); b.decodeString(&sd.m_kart); b.decodeStringW(&sd.m_player); - if (NetworkConfig::get()->getServerCapabilities().find("soccer_fixes") - != NetworkConfig::get()->getServerCapabilities().end()) - { - b.decodeString(&sd.m_country_code); - sd.m_handicap_level = (HandicapLevel)b.getUInt8(); - } - m_red_scorers.push_back(sd); + b.decodeString(&sd.m_country_code); + sd.m_handicap_level = (HandicapLevel)b.getUInt8(); +// Shut up GCC complaining incorrectly about sd.m_country_code +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" + if (i < red_size) + m_red_scorers.push_back(sd); + else + m_blue_scorers.push_back(sd); +#pragma GCC diagnostic pop } - const unsigned blue_size = b.getUInt32(); - for (unsigned i = 0; i < blue_size; i++) - { - ScorerData sd; - sd.m_id = b.getUInt8(); - sd.m_correct_goal = b.getUInt8() == 1; - sd.m_time = b.getFloat(); - b.decodeString(&sd.m_kart); - b.decodeStringW(&sd.m_player); - if (NetworkConfig::get()->getServerCapabilities().find("soccer_fixes") - != NetworkConfig::get()->getServerCapabilities().end()) - { - b.decodeString(&sd.m_country_code); - sd.m_handicap_level = (HandicapLevel)b.getUInt8(); - } - m_blue_scorers.push_back(sd); - } m_reset_ball_ticks = b.getTime(); m_ticks_back_to_own_goal = b.getTime(); } // restoreCompleteState diff --git a/src/modes/soccer_world.hpp b/src/modes/soccer_world.hpp index 45d3e53490c..bd7d133a180 100644 --- a/src/modes/soccer_world.hpp +++ b/src/modes/soccer_world.hpp @@ -43,7 +43,7 @@ class SoccerWorld : public WorldWithRank { /** World ID of kart which scores. */ unsigned int m_id; - /** Whether this goal is socred correctly (identify for own goal). */ + /** Whether this goal is scored correctly (identify for own goal). */ bool m_correct_goal; /** Time goal. */ float m_time; From d4654444a1118c37f167da7a77cdc9781c33fcc9 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Wed, 26 Nov 2025 21:54:41 +0100 Subject: [PATCH 176/188] Implement per-difficulty default laps - Bump support for tracks version to version 8 - Add a parameter to know if a track allows per-difficulty default laps. If unspecified in the XML, the game assumes it is allowed. - Add a getDefaultNumberOfLaps function taking difficulty as an input. It returns the appropriate value based on the input difficulty, the general default lap number for the track, and on if per-difficulty default laps is allowed. It uses a lambda expression for concision. - Novice targets a length of 2/3rd, Casual a length of 3/4th and Intermediate a length of 7/8th. If these ratios don't produce an integer, the result is rounded up. For example, 2/3 of 3 laps gives 2 laps, but 3/4th of 3 laps gives 3 laps (9/4th rounded up). - The 'actual number of laps' parameter that is used in the track info screen is made to work per difficulty - The world query of the default lap number for the track for achievement purposes Other uses of the default lap value do not yet take into account the difficulty. --- data/stk_config.xml | 2 +- src/modes/world.cpp | 2 +- src/race/race_manager.hpp | 3 +- src/states_screens/track_info_screen.cpp | 5 +-- src/tracks/track.cpp | 42 ++++++++++++++++++++++-- src/tracks/track.hpp | 16 ++++++--- 6 files changed, 59 insertions(+), 11 deletions(-) diff --git a/data/stk_config.xml b/data/stk_config.xml index ff148dd934c..4ac0b0698a3 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -7,7 +7,7 @@ - + diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 8d74d225219..57eddf17eba 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -1738,7 +1738,7 @@ void World::updateAchievementDataEndRace() if (RaceManager::get()->modeHasLaps()) { Track* track = track_manager->getTrack(RaceManager::get()->getTrackName()); - int default_lap_num = track->getDefaultNumberOfLaps(); + int default_lap_num = track->getDefaultNumberOfLaps(RaceManager::get()->getDifficulty()); if (RaceManager::get()->getNumLaps() < default_lap_num) { PlayerManager::trackEvent(RaceManager::get()->getTrackName(), ACS::TR_LESS_LAPS); diff --git a/src/race/race_manager.hpp b/src/race/race_manager.hpp index 342f2f0e734..58206b663b6 100644 --- a/src/race/race_manager.hpp +++ b/src/race/race_manager.hpp @@ -227,7 +227,8 @@ class RaceManager #undef BATTLE_ARENA #undef MISC - /** Game difficulty. */ + /** Game difficulty. + * The values need to start at 0 and be consecutive. */ enum Difficulty { DIFFICULTY_EASY = 0, DIFFICULTY_FIRST = DIFFICULTY_EASY, DIFFICULTY_CASUAL, diff --git a/src/states_screens/track_info_screen.cpp b/src/states_screens/track_info_screen.cpp index dcd8e5081db..2cf918e5b97 100644 --- a/src/states_screens/track_info_screen.cpp +++ b/src/states_screens/track_info_screen.cpp @@ -268,7 +268,8 @@ void TrackInfoScreen::init() m_target_value_spinner->setMin(0); else m_target_value_spinner->setMin(1); - m_target_value_spinner->setValue(m_track->getActualNumberOfLap()); + m_target_value_spinner->setValue( + m_track->getActualNumberOfLaps(RaceManager::get()->getDifficulty())); RaceManager::get()->setNumLaps(m_target_value_spinner->getValue()); m_target_value_label->setText(_("Number of laps"), false); @@ -560,7 +561,7 @@ void TrackInfoScreen::onEnterPressedInternal() : m_option->getState(); // Avoid negative lap numbers (after e.g. easter egg mode). if(num_laps>=0) - m_track->setActualNumberOfLaps(num_laps); + m_track->setActualNumberOfLaps(RaceManager::get()->getDifficulty(), num_laps); if(m_track->hasNavMesh()) UserConfigParams::m_random_arena_item = option_state; diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index b5a3a0d4c57..3f88c9bfdf7 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -68,7 +68,6 @@ #include "physics/physical_object.hpp" #include "physics/physics.hpp" #include "physics/triangle_mesh.hpp" -#include "race/race_manager.hpp" #include "scriptengine/script_engine.hpp" #include "tracks/arena_graph.hpp" #include "tracks/bezier_curve.hpp" @@ -188,6 +187,10 @@ Track::Track(const std::string &filename) m_red_flag = m_blue_flag = btTransform(btQuaternion(0.0f, 0.0f, 0.0f, 1.0f)); m_default_number_of_laps = 3; + m_dynamic_laps = true; + for (unsigned i = 0 ; i < (int)RaceManager::DIFFICULTY_COUNT ; i++) + m_actual_number_of_laps.push_back(-1); + m_all_nodes.clear(); m_static_physics_only_nodes.clear(); m_all_cached_meshes.clear(); @@ -579,6 +582,7 @@ void Track::loadTrackInfo() root->get("internal", &m_internal); root->get("reverse", &m_reverse_available); root->get("default-number-of-laps",&m_default_number_of_laps); + root->get("dynamic-laps", &m_dynamic_laps); root->get("push-back", &m_enable_push_back); root->get("bloom", &m_bloom); root->get("bloom-threshold", &m_bloom_threshold); @@ -591,7 +595,9 @@ void Track::loadTrackInfo() getMusicInformation(filenames, m_music); if (m_default_number_of_laps <= 0) m_default_number_of_laps = 3; - m_actual_number_of_laps = m_default_number_of_laps; + + for (unsigned i = 0 ; i < (int)RaceManager::DIFFICULTY_COUNT ; i++) + m_actual_number_of_laps[i] = getDefaultNumberOfLaps((RaceManager::Difficulty) i); // Make the default for auto-rescue in battle mode and soccer mode to be false if(m_is_arena || m_is_soccer) @@ -3076,6 +3082,38 @@ void Track::copyFromMainProcess() m_item_manager = nim; } // copyFromMainProcess +//----------------------------------------------------------------------------- +const int Track::getDefaultNumberOfLaps(RaceManager::Difficulty difficulty) +{ + if (!m_dynamic_laps || difficulty == RaceManager::DIFFICULTY_HARD + || difficulty == RaceManager::DIFFICULTY_BEST) + return m_default_number_of_laps; + + // If dynamic adjustment of the default lap number is on for this track + // and the difficulty is low, we reduce the lap numbers proportionally. + // This allows race duration to be neither too short at high difficulties + // nor too long at low difficulties. + auto lowerDefault + { + [](int default_laps, int numerator, int denominator) + { + int lowered_default = (default_laps * numerator) / denominator; + if (lowered_default * denominator < default_laps * numerator) + lowered_default++; + return lowered_default; + } + }; + + if (difficulty == RaceManager::DIFFICULTY_MEDIUM) + return lowerDefault(m_default_number_of_laps, 7, 8); + else if (difficulty == RaceManager::DIFFICULTY_CASUAL) + return lowerDefault(m_default_number_of_laps, 3, 4); + else if (difficulty == RaceManager::DIFFICULTY_EASY) + return lowerDefault(m_default_number_of_laps, 2, 3); + + return m_default_number_of_laps; +} // getDefaultNumberOfLaps + //----------------------------------------------------------------------------- void Track::initChildTrack() { diff --git a/src/tracks/track.hpp b/src/tracks/track.hpp index 50b1448f168..f4d9926ac6d 100644 --- a/src/tracks/track.hpp +++ b/src/tracks/track.hpp @@ -53,6 +53,7 @@ using namespace irr; #include "LinearMath/btTransform.h" +#include "race/race_manager.hpp" #include "utils/aligned_array.hpp" #include "utils/log.hpp" #include "utils/random_generator.hpp" @@ -408,8 +409,12 @@ class Track * m_actual_number_of_laps is initialised with this value.*/ int m_default_number_of_laps; + /* If true, lower difficulties will dynamically compute the + * default number of laps, based on the main default value. */ + bool m_dynamic_laps; + /** The number of laps that is predefined in a track info dialog. */ - int m_actual_number_of_laps; + std::vector m_actual_number_of_laps; void loadTrackInfo(); void loadDriveGraph(unsigned int mode_id, const bool reverse); @@ -723,10 +728,13 @@ class Track // ------------------------------------------------------------------------ const int getDefaultNumberOfLaps() const { return m_default_number_of_laps;} // ------------------------------------------------------------------------ - const int getActualNumberOfLap() const { return m_actual_number_of_laps; } + const int getDefaultNumberOfLaps(RaceManager::Difficulty difficulty); + // ------------------------------------------------------------------------ + const int getActualNumberOfLaps(RaceManager::Difficulty difficulty) const + { return m_actual_number_of_laps[(int) difficulty]; } // ------------------------------------------------------------------------ - void setActualNumberOfLaps(unsigned int laps) - { m_actual_number_of_laps = laps; } + void setActualNumberOfLaps(RaceManager::Difficulty difficulty, unsigned int laps) + { m_actual_number_of_laps[(int) difficulty] = laps; } // ------------------------------------------------------------------------ bool operator<(const Track &other) const; // ------------------------------------------------------------------------ From fc63537e3bb5ea549c5d0ddc1d45d5ad220ddab2 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Thu, 27 Nov 2025 13:02:44 +0100 Subject: [PATCH 177/188] Adjust drifting bonuses for lower difficulties In lower difficulties, it becomes much easier to get the stronger boost levels, as the time required to get a boost level are the same but slower karts mean more time spent in a given curve. Therefore, the speed advantage of 2nd and 3rd tier drifts are made less significant. Those changes also reduce the advantage of good drifting usage in low difficulties, and make the completion of a 3 laps race in Casual less likely to be faster than a 4 laps SuperTux race. However, it's still not quite enough for this latter purpose. This commit also fixes the max-speed-reverse-ratio for the Novice difficulty, which was erroneously increased instead of decreased when the base max speed was increased. --- data/kart_characteristics.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 24b7f7cb5fb..103c7ab907c 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -394,11 +394,11 @@ + max-speed-reverse-ratio="0.58"/> - + @@ -409,7 +409,7 @@ - + @@ -419,7 +419,7 @@ max-speed-reverse-ratio="0.5"/> - + From 2d138f47fed33a70c152f54e5bba87e0777ded7a Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sat, 29 Nov 2025 12:23:28 +0100 Subject: [PATCH 178/188] Keep the nitro light on if there is a nitro speed boost The full nitro boost continues longer than just the period during which nitro is actively spent, and until now there were no direct visual cues for this effect. While attentive players would notice the speed boost lasting longer, this was not obvious for many. By making the nitro light stay on as long as the max speed boost is fully active, new players get another hint that continuous nitro use is not optimal, while good players can use the visual help for more consistent chain-boosting instead of relying on rote timing memorization. Note that unless there is excess nitro in the track, the optimal boosting pattern involves taking advantage of the fade-out time during which there is no nitro light. The nitro particles however are only emitted while nitro is actively spent. --- src/karts/kart.cpp | 5 +++-- src/karts/kart_gfx.cpp | 29 ++++++++++------------------- src/karts/kart_gfx.hpp | 2 +- 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 88fda956fd1..5381723bc4b 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -3900,9 +3900,10 @@ void Kart::updateGraphics(float dt) nitro_frac = fabsf(getSpeed()) / (m_kart_properties->getEngineMaxSpeed()); // The speed of the kart can be higher (due to powerups) than // the normal maximum speed of the kart. - if(nitro_frac>1.0f) nitro_frac = 1.0f; + if(nitro_frac > 1.0f) nitro_frac = 1.0f; } - m_kart_gfx->updateNitroGraphics(nitro_frac, m_nitro_hack_ticks > 0); + bool active_nitro = getSpeedIncreaseTicksLeft(MaxSpeed::MS_INCREASE_NITRO) > 0; + m_kart_gfx->updateNitroGraphics(nitro_frac, m_nitro_hack_ticks > 0, active_nitro); // Handle leaning of karts // ----------------------- diff --git a/src/karts/kart_gfx.cpp b/src/karts/kart_gfx.cpp index 7262f6963ea..566ad5e0fb0 100644 --- a/src/karts/kart_gfx.cpp +++ b/src/karts/kart_gfx.cpp @@ -505,7 +505,7 @@ void KartGFX::update(float dt) /** Updates nitro dependent particle effects. * \param nitro_frac Nitro fraction/ */ -void KartGFX::updateNitroGraphics(float nitro_frac, bool isNitroHackOn) +void KartGFX::updateNitroGraphics(float nitro_frac, bool isNitroHackOn, bool activeNitro) { #ifndef SERVER_ONLY if (GUIEngine::isNoGraphics()) @@ -514,7 +514,7 @@ void KartGFX::updateNitroGraphics(float nitro_frac, bool isNitroHackOn) // Upate particle effects (creation rate, and emitter size // depending on speed) // -------------------------------------------------------- - if (nitro_frac > 0) + if (nitro_frac > 0) // There is an input to use nitro { if(isNitroHackOn) { @@ -522,11 +522,6 @@ void KartGFX::updateNitroGraphics(float nitro_frac, bool isNitroHackOn) setCreationRateAbsolute(KartGFX::KGFX_NITRO2, 0); setCreationRateRelative(KartGFX::KGFX_NITROHACK1, nitro_frac); setCreationRateRelative(KartGFX::KGFX_NITROHACK2, nitro_frac); - if (supportsLight()) - { - m_nitro_light->setVisible(false); - m_nitro_hack_light->setVisible(true); - } } else { @@ -534,11 +529,6 @@ void KartGFX::updateNitroGraphics(float nitro_frac, bool isNitroHackOn) setCreationRateRelative(KartGFX::KGFX_NITRO2, nitro_frac); setCreationRateAbsolute(KartGFX::KGFX_NITROHACK1, 0); setCreationRateAbsolute(KartGFX::KGFX_NITROHACK2, 0); - if (supportsLight()) - { - m_nitro_light->setVisible(true); - m_nitro_hack_light->setVisible(false); - } } setCreationRateRelative(KartGFX::KGFX_NITROSMOKE1, nitro_frac); setCreationRateRelative(KartGFX::KGFX_NITROSMOKE2, nitro_frac); @@ -551,19 +541,20 @@ void KartGFX::updateNitroGraphics(float nitro_frac, bool isNitroHackOn) setCreationRateAbsolute(KartGFX::KGFX_NITROHACK2, 0); setCreationRateAbsolute(KartGFX::KGFX_NITROSMOKE1, 0); setCreationRateAbsolute(KartGFX::KGFX_NITROSMOKE2, 0); - - if (supportsLight()) - { - m_nitro_light->setVisible(false); - m_nitro_hack_light->setVisible(false); - } + } + + // Add a colored light effect if the kart has an active nitro boost + if (supportsLight()) + { + m_nitro_light->setVisible(activeNitro && !isNitroHackOn); + m_nitro_hack_light->setVisible(activeNitro && isNitroHackOn); } // Exhaust is always emitting setCreationRateRelative(KartGFX::KGFX_EXHAUST1, 1.0); setCreationRateRelative(KartGFX::KGFX_EXHAUST2, 1.0); #endif -} // updateGraphics +} // updateNitroGraphics // ---------------------------------------------------------------------------- /** Updates the skiddng light (including disabling it). diff --git a/src/karts/kart_gfx.hpp b/src/karts/kart_gfx.hpp index f975ba7d5aa..c65c9f768d0 100644 --- a/src/karts/kart_gfx.hpp +++ b/src/karts/kart_gfx.hpp @@ -129,7 +129,7 @@ class KartGFX void setCreationRateRelative(const KartGFXType type, float f); void updateTerrain(const ParticleKind *pk); void update(float dt); - void updateNitroGraphics(float f, bool isNitroHackOn); + void updateNitroGraphics(float f, bool isNitroHackOn, bool activeNitro); void updateSkidLight(unsigned int level); void getGFXStatus(int* nitro, bool* zipper, int* skidding, bool* red_skidding, bool* purple_skidding) const; From 3f1a992bfedbcdf37093aa20c196b254d7093c3c Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sat, 29 Nov 2025 17:38:42 +0100 Subject: [PATCH 179/188] Rework nitro consumption - The extra duration of nitro boost beyond the request usage by the player is shortened from 0.9s to 0.45s and the minimum burst is lengthened to 0.55s. This means that the full-boost duration from a minimum burst decreases from 1.4s to 1.0s. The main motivation behind this change is to reduce the gap between players that know how to use nitro by burst and players that don't. It still remains very advantageous to use nitro properly, but the gap is made less extreme. - Base nitro consumption is decreased from 1.0/s to 0.72/s. As the duration paid for a minimum burst has been increased by 10%, this has to be compensated in addition to the reduction in the boost time. This ~39% increase in efficiency roughly keeps the amount of speed derived from nitro similar for good players (54% would be needed purely to match the active boost duration phase, however a shorter minimum boost duration means more occasions to be at a higher speed from the 1.45s fade-out). - Adjust the nitro efficiency display used in kart selection. - The max nitro stockpile is set to 15 instead of 20. Each block of the nitro gauge now corresponds to 3 nitro units instead of 4. This increases the visibility of small nitro amounts while decreasing the potential of max nitro stockpiling (at the beginning of a long race hoping to get better powerups to catchup, during soccer games hoping to not have to refill often). In most practical game situations, the new limit of 15 was only rarely exceeded to begin with. --- data/kart_characteristics.xml | 6 +++--- src/guiengine/widgets/kart_stats_widget.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 103c7ab907c..a1afa4921c0 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -361,10 +361,10 @@ fade-out-time: Duration during which the increased maximum speed due to nitro fades out. max: How much nitro a kart can store. --> - + max-speed-increase="5" min-burst="0.55" duration="0.45" + fade-out-time="1.45" max="15" /> - @@ -449,7 +449,8 @@ - + @@ -467,7 +468,8 @@ time-till-bonus="*0.9 *0.9 *0.9" /> - + @@ -483,7 +485,8 @@ - + @@ -499,7 +502,7 @@ - + From f171de1799c183057a70c30063ae339736365ff6 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 30 Nov 2025 14:56:36 +0100 Subject: [PATCH 181/188] Update kart_properties.py If someone wants to use it to generate the kart properties code, it's now up to date with the added, changed and removed properties in the Evolution code. Also change a few comments about the anvil to call it anchor. --- data/kart_characteristics.xml | 6 +++--- tools/create_kart_properties.py | 20 +++++++++++--------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 93b394dbe82..5e1ab04ba41 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -229,10 +229,10 @@ - + duration: The time an anchor is active. --> From 7b3f17f61c33c1a3fde3ae8b9f596e443a995c6a Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 7 Dec 2025 18:46:02 +0100 Subject: [PATCH 185/188] Make the medium-h karts lighter Increase the differentiation between standard medium karts and high-handling karts by making the weight different. This notably affects collisions, explosion duration, parachutes, and slipstream. Medium-h retains the gear ratios of the medium class as well as, after the adjustment of other values, a similar weight-to-power ratio. Some boosts such as zipper cna provide a slightly higher weight-to-power ratio as their engine force boost is identical for all kart classes. Nitro-efficiency remains nominally similar, but with an atypical profile: - A lower maximum speed boost than would be expected from a linear interpolation between light and medium (it's lower than even medium's value) - It otherwise follows existing patterns on the relationship between weight, consumption and speed (consumption is proportional to weight, consumption increases by 2/3rds of the max speed increase). - The min-burst factor is increased to keep the minimum burst consumption nearly identical to that of medium karts. - Overall, for less skilled users, this is probably a buff, as the consumption/speed ratio is slightly better (but those users are less likely to take advantage of the skidding bonuses of this class), while for more skilled players it should not make much of a difference. Also remove the '*1' fade-out-time multipliers for splistream as all difficulties used the same value. --- data/kart_characteristics.xml | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index 0838368c8b2..4f14e2a82d2 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -456,27 +456,26 @@ - + - + - - - + - - - - + + + + @@ -492,7 +491,7 @@ - + @@ -508,8 +507,7 @@ - + From 26ebd89a985a8bcd41da7cd005524ea1a180ba42 Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Sun, 7 Dec 2025 20:27:49 +0100 Subject: [PATCH 186/188] Improve the kart stats display - Use a logarithm to better show the relative gameplay value of a medium weight compared to a high weight and a low weight. - Tweak the scaling of the mass and nitro efficiency skills. - Reduce the advantage of shorter boost times (or penalty of longer ones) for the drifting skill when the kart's speed is low compared to the generic max speed. This probably doesn't work well with kart parameters that are very different from the existing ones, but with the existing karts, the main effect is to reduce the drifting skill of medium-h karts when handicap is on, as a shorter time to trigger skids becomes less valuable. - Add some commented logging code for a kart's total displayed skill points. After those changes, the four existing classes get very similar total displayed skill points (351 for medium karts and 357 for all three other classes). While the displayed skill points are in large part arbitrary, similar totals help to convey that the classes are of similar strength. --- src/guiengine/widgets/kart_stats_widget.cpp | 37 +++++++++++++++------ src/guiengine/widgets/kart_stats_widget.hpp | 2 +- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/guiengine/widgets/kart_stats_widget.cpp b/src/guiengine/widgets/kart_stats_widget.cpp index d9624db354f..a2d36c8be9f 100644 --- a/src/guiengine/widgets/kart_stats_widget.cpp +++ b/src/guiengine/widgets/kart_stats_widget.cpp @@ -88,6 +88,8 @@ void KartStatsWidget::setValues(const KartProperties* props, HandicapLevel h) // If props is NULL (e.g., when kart selection is "?" (random)), // skip this function to avoid errors. if( props == NULL ) return; + + float skill_pts = 0; // Use kart properties computed for best difficulty to show the user, so // that properties don't change according to the the last used difficulty @@ -102,13 +104,18 @@ void KartStatsWidget::setValues(const KartProperties* props, HandicapLevel h) // Scale the values so they look better // A value of 100 takes the whole bar width, including borders. // So values should be in the 0-99 range + // TODO: pull some parameters from a config file, to allow mods that change + // various gameplay value to still offer a working skill display. - // The base mass is of 350 ; 250/2.78 ~= 90 - setSkillValues(SKILL_MASS, (kpc->getMass() - 100.0f)/2.78f, - "mass.png", "mass", _("Mass")); + // We use a logarithm so that a given width corresponds to a given weight ratio + // instead of a given absolute weight difference. This allows for a more meaningful + // display, as the gameplay effects are proportional to the weight ratio. + float mass_log = std::logf(kpc->getMass()); + float mass_display = (mass_log * 109.0f) - 545.0f; + skill_pts += setSkillValues(SKILL_MASS, mass_display, "mass.png", "mass", _("Mass")); // The base speed is of 28 - // Speed is the characteristics most affected by handicap, but it is also + // Speed is the characteristic most affected by handicap, but it is also // important to display the base differences between classes as significant, // as small differences matter a lot. // Therefore, a non-linear formula is used. @@ -117,13 +124,13 @@ void KartStatsWidget::setValues(const KartProperties* props, HandicapLevel h) speed_power_four *= speed_power_four; // squaring again speed_power_four *= 0.001f; // divide by 1000 to improve readbility of the formula float speed_composite = (speed_power_four - 100.0f) * 0.1924f; - setSkillValues(SKILL_SPEED, speed_composite, + skill_pts += setSkillValues(SKILL_SPEED, speed_composite, "speed.png", "speed", _("Maximum speed")); // The acceleration depend on power and mass, and it changes depending on speed // We call a function which gives us a single number to represent it // power/mass gives numbers in the 1-11 range, so we multiply it by 9. - setSkillValues(SKILL_ACCELERATION, + skill_pts += setSkillValues(SKILL_ACCELERATION, kp_computed.getAccelerationEfficiency()*9.0f, "power.png", "acceleration", _("Acceleration")); @@ -136,8 +143,8 @@ void KartStatsWidget::setValues(const KartProperties* props, HandicapLevel h) efficiency *= (kpc->getNitroMinBurst() + kpc->getNitroDuration()) / kpc->getNitroMinBurst(); // Magnify the differences - efficiency = (efficiency - 25.0f) * 1.333f; - setSkillValues(SKILL_NITRO_EFFICIENCY, efficiency, + efficiency = (efficiency - 20.0f) * 1.25f; + skill_pts += setSkillValues(SKILL_NITRO_EFFICIENCY, efficiency, "nitro.png", "nitro", _("Nitro efficiency")); // The base time for the skidding bonuses are 1, 2.7 and 4 @@ -147,6 +154,13 @@ void KartStatsWidget::setValues(const KartProperties* props, HandicapLevel h) float aggregate_skid_time = 4 * kpc->getSkidTimeTillBonus()[0] + 2 * kpc->getSkidTimeTillBonus()[1] + kpc->getSkidTimeTillBonus()[2]; + // The lower the kart speed, the easier it is to fit long boosts in various curves, + // and the less a shorter boost time gives an advantage. + float missing_speed_frac = (kpc->getEngineGenericMaxSpeed() - kpc->getEngineMaxSpeed()) + / kpc->getEngineGenericMaxSpeed(); + aggregate_skid_time = aggregate_skid_time * (1.0f - missing_speed_frac) + + 13.4f * missing_speed_frac; // 13.4 is the generic value + // Make this parameter more significant aggregate_skid_time *= sqrtf(aggregate_skid_time); float aggregate_bonus_speed = 4 * kpc->getSkidBonusSpeed()[0] + @@ -161,14 +175,16 @@ void KartStatsWidget::setValues(const KartProperties* props, HandicapLevel h) float drift_composite = -30.0f + (1100.0f * aggregate_bonus_speed) / (aggregate_skid_time * sqrtf(aggregate_turn_radius)); - setSkillValues(SKILL_SKIDDING, drift_composite, + skill_pts += setSkillValues(SKILL_SKIDDING, drift_composite, "android/drift.png", "skidding", _("Drifting bonuses")); + + //Log::verbose("Stats Widget", "The sum of displayed skill values for the selected kart is %f", skill_pts); RaceManager::get()->setDifficulty(previous_difficulty); } // setValues // ----------------------------------------------------------------------------- -void KartStatsWidget::setSkillValues(Stats skill_type, float value, const std::string icon_name, +float KartStatsWidget::setSkillValues(Stats skill_type, float value, const std::string icon_name, const std::string skillbar_propID, const irr::core::stringw icon_tooltip) { // Beautify the display by limiting the possibility of very close values @@ -179,6 +195,7 @@ void KartStatsWidget::setSkillValues(Stats skill_type, float value, const std::s file_manager->getAsset(FileManager::GUI_ICON, icon_name).c_str())); m_skills[skill_type]->m_properties[PROP_ID] = StringUtils::insertValues("@p%i_"+skillbar_propID, m_player_id); m_skills[skill_type]->m_iconbutton->setTooltip(icon_tooltip); + return value; } // setSkillValues // ----------------------------------------------------------------------------- diff --git a/src/guiengine/widgets/kart_stats_widget.hpp b/src/guiengine/widgets/kart_stats_widget.hpp index fc3adbc5437..91f1ecb4215 100644 --- a/src/guiengine/widgets/kart_stats_widget.hpp +++ b/src/guiengine/widgets/kart_stats_widget.hpp @@ -71,7 +71,7 @@ namespace GUIEngine std::vector m_skills; - void setSkillValues(Stats skill_type, float value, const std::string icon_name, + float setSkillValues(Stats skill_type, float value, const std::string icon_name, const std::string skillbar_propID, const irr::core::stringw icon_tooltip); From fd78331dad94ea21957fe204632099a89dd5e1ab Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Thu, 15 Jan 2026 16:05:53 +0100 Subject: [PATCH 187/188] Add a small off-road bonus to the electro-shield and purple skid - The electro-shield bonus (35%) is primarily aimed at helping to reduce the consequences of noob errors and making some small shortcuts viable. - The purple skid bonus (20%) is primarily aimed at allowing some jump ramps to work with a purple boost but not a red one. It still helps a bit for accidental off-road incursions and in select off-road cuts, but the goal is to keep off-road penalizing. --- src/karts/kart.cpp | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 5381723bc4b..98d2183d3d8 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -2068,11 +2068,38 @@ void Kart::update(int ticks) } else { - // If a zipper boost (from the zipper item, texture or a startup boost) - // is active, reduce the texture penalty by half - float terrain_speed_fraction = material->getMaxSpeedFraction();; + // Some max speed boost types offer a reduction in the off-road (texture) penalty. + // This allows: + // - The design of special shortcuts that only work having a special boost to take. + // This can either be a general cut through a large off-road area or using + // a jump ramp with a small off-road penalty and carefully tuned distances. + // - Reducing the loss of speed and time from accidental minor encroachments on + // off-road areas. + // + // Only the biggest reduction value is applied, as applying multiple reductions + // in a row would be too powerful. + // + // We use a different reduction value for the different boost types: + // - Zipper gets a 50% reduction, that allows track designers to create somewhat + // long shortcuts through off-road. + // - Electro-Shield gets a 35% reduction, that's primarily aimed at reinforcing + // the powerup's nature as something that helps to attenuate mistakes from + // players in the back, but also making some shortcuts viable. + // - The purple drift gets a 20% reduction, which is low enough that intentionally + // going through a wide off-road area is ineffective. It allows however to + // calibrate jump ramps that need a pruple drift + nitro boost to take. + // TODO : Make the values configurable in kart_characteristics.xml + // Do not forget that only the strongest effect should be applied, + // and we cannot assume which is strongest when pulling from config. + // TODO : Add a visual effect when driving off-road with an off-road bonus + + float terrain_speed_fraction = material->getMaxSpeedFraction(); if (m_max_speed->isSpeedIncreaseActive(MaxSpeed::MS_INCREASE_ZIPPER) > 0) - terrain_speed_fraction += (1.0f - terrain_speed_fraction)*0.5f; + terrain_speed_fraction += (1.0f - terrain_speed_fraction) * 0.5f; + else if (m_max_speed->isSpeedIncreaseActive(MaxSpeed::MS_INCREASE_ELECTRO) > 0) + terrain_speed_fraction += (1.0f - terrain_speed_fraction) * 0.35f; + else if (m_max_speed->isSpeedIncreaseActive(MaxSpeed::MS_INCREASE_PURPLE_SKIDDING) > 0) + terrain_speed_fraction += (1.0f - terrain_speed_fraction) * 0.2f; m_max_speed->setSlowdown(MaxSpeed::MS_DECREASE_TERRAIN, terrain_speed_fraction, From 0c4675c76da23fea3f533667ad5877c258d1954a Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Thu, 15 Jan 2026 16:21:07 +0100 Subject: [PATCH 188/188] Change the boost type of the startup boost We now use the ground zipper boost instead of the zipper boost: - This prevents the startup boost from being used to easily take off-road cuts located soon after the start - If there are ground zippers near the start line, their effect would supersede that of the startup boost anyways, except in the rare case where they give a weaker boost strength or a duration shorter than the startup boost duration minus the time required to get to the zipper pad. And zipper pads very close to the start are a worse and less relevant design pattern than off-road areas and even off-road cuts near the start line. --- src/karts/kart.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 98d2183d3d8..14d530f4172 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -3151,7 +3151,7 @@ void Kart::updatePhysics(int ticks) { m_kart_gfx->setCreationRateAbsolute(KartGFX::KGFX_ZIPPER, 100.0f * m_startup_boost); - m_max_speed->instantSpeedIncrease(MaxSpeed::MS_INCREASE_ZIPPER, + m_max_speed->instantSpeedIncrease(MaxSpeed::MS_INCREASE_GROUND_ZIPPER, /*instant speed*/ m_startup_boost, /*max speed*/ m_startup_boost, /*engine_force*/ m_startup_engine_force, /*duration*/ stk_config->time2Ticks(m_kart_properties->getStartupDuration()),