diff --git a/data/gui/heart.png b/data/gui/heart.png new file mode 100644 index 000000000..bc4dff536 Binary files /dev/null and b/data/gui/heart.png differ diff --git a/sources.cmake b/sources.cmake index ddc029d4f..f484b15d5 100644 --- a/sources.cmake +++ b/sources.cmake @@ -1,5 +1,5 @@ # Modify this file to change the last-modified date when you add/remove a file. -# This will then trigger a new cmake run automatically. +# This will then trigger a new cmake run automatically. file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp") file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp") file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*") diff --git a/src/items/item.cpp b/src/items/item.cpp index 389154e2a..8f3eb7d07 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -43,10 +43,7 @@ Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal, m_distance_2 = 1.2f; initItem(type, xyz); - Vec3 axis = -normal.cross(Vec3(0, 1, 0)); - if (axis.length() == 0) - axis = Vec3(0, 0, 1); - m_original_rotation = btQuaternion(axis, normal.angle(Vec3(0, 1, 0))); + m_original_rotation = Track::createRotationFromNormal(normal); m_rotation_angle = 0.0f; m_original_mesh = mesh; m_original_lowmesh = lowres_mesh; diff --git a/src/items/item_manager.cpp b/src/items/item_manager.cpp index 61c5e3232..6afb9733d 100644 --- a/src/items/item_manager.cpp +++ b/src/items/item_manager.cpp @@ -29,6 +29,7 @@ #include "graphics/material_manager.hpp" #include "io/file_manager.hpp" #include "karts/abstract_kart.hpp" +#include "karts/controller/spare_tire_ai.hpp" #include "modes/linear_world.hpp" #include "network/network_config.hpp" #include "network/race_event_manager.hpp" @@ -287,6 +288,8 @@ Item* ItemManager::newItem(const Vec3& xyz, float distance, void ItemManager::collectedItem(Item *item, AbstractKart *kart, int add_info) { assert(item); + // Spare tire karts don't collect items + if (dynamic_cast(kart->getController()) != NULL) return; if( (item->getType() == Item::ITEM_BUBBLEGUM || item->getType() == Item::ITEM_BUBBLEGUM_NOLOK) && kart->isShielded()) { diff --git a/src/karts/controller/arena_ai.cpp b/src/karts/controller/arena_ai.cpp index 4f33f1d6a..fccfd3c96 100644 --- a/src/karts/controller/arena_ai.cpp +++ b/src/karts/controller/arena_ai.cpp @@ -119,7 +119,6 @@ void ArenaAI::update(float dt) if (gettingUnstuck(dt)) return; - findClosestKart(true); findTarget(); // After found target, convert it to local coordinate, used for skidding or @@ -395,7 +394,7 @@ void ArenaAI::useItems(const float dt) return; // Find a closest kart again, this time we ignore difficulty - findClosestKart(false); + findClosestKart(false/*use_difficulty*/, false/*find_sta*/); if (!m_closest_kart) return; Vec3 closest_kart_point_lc = diff --git a/src/karts/controller/arena_ai.hpp b/src/karts/controller/arena_ai.hpp index c0e0db2fe..de1b3a529 100644 --- a/src/karts/controller/arena_ai.hpp +++ b/src/karts/controller/arena_ai.hpp @@ -65,7 +65,8 @@ protected: bool m_mini_skid; - void collectItemInArena(Vec3*, int*) const; + void collectItemInArena(Vec3*, int*) const; + virtual void findClosestKart(bool use_difficulty, bool find_sta) = 0; private: Vec3 m_target_point_lc; @@ -115,7 +116,6 @@ private: void useItems(const float dt); virtual bool canSkid(float steer_fraction) OVERRIDE { return m_mini_skid; } - virtual void findClosestKart(bool use_difficulty) = 0; virtual void findTarget() = 0; virtual bool forceBraking() { return false; } virtual int getCurrentNode() const = 0; diff --git a/src/karts/controller/battle_ai.cpp b/src/karts/controller/battle_ai.cpp index f3cfe7aa7..c2278e9c4 100644 --- a/src/karts/controller/battle_ai.cpp +++ b/src/karts/controller/battle_ai.cpp @@ -24,6 +24,7 @@ #include "items/powerup.hpp" #include "karts/abstract_kart.hpp" #include "karts/controller/kart_control.hpp" +#include "karts/controller/spare_tire_ai.hpp" #include "modes/three_strikes_battle.hpp" #include "tracks/arena_graph.hpp" @@ -65,50 +66,42 @@ BattleAI::~BattleAI() } // ~BattleAI //----------------------------------------------------------------------------- -/** Resets the AI when a race is restarted. - */ -void BattleAI::reset() -{ - ArenaAI::reset(); -} // reset - -//----------------------------------------------------------------------------- -void BattleAI::update(float dt) -{ - ArenaAI::update(dt); -} // update - -//----------------------------------------------------------------------------- -void BattleAI::findClosestKart(bool use_difficulty) +void BattleAI::findClosestKart(bool use_difficulty, bool find_sta) { float distance = 99999.9f; - const unsigned int n = m_world->getNumKarts(); int closest_kart_num = 0; + const int end = m_world->getNumKarts(); - for (unsigned int i = 0; i < n; i++) + for (int start_id = + find_sta ? end - race_manager->getNumSpareTireKarts() : 0; + start_id < end; start_id++) { - const AbstractKart* kart = m_world->getKart(i); - if (kart->isEliminated()) continue; + const AbstractKart* kart = m_world->getKart(start_id); + const SpareTireAI* sta = + dynamic_cast(kart->getController()); + if (kart->isEliminated() && !(find_sta && sta && sta->isMoving())) + continue; if (kart->getWorldKartId() == m_kart->getWorldKartId()) continue; // Skip the same kart // Test whether takes current difficulty into account for closest kart // Notice: it don't affect aiming, this function will be called once - // more in handleArenaItems, which ignore difficulty. + // more when use items, which ignore difficulty. if (m_cur_difficulty == RaceManager::DIFFICULTY_EASY && use_difficulty) { - // Skip human players for novice mode unless only human players left - const AbstractKart* temp = m_world->getKart(i); + // Skip human players for novice mode unless only they are left + const AbstractKart* temp = m_world->getKart(start_id); if (temp->getController()->isPlayerController() && (m_world->getCurrentNumKarts() - m_world->getCurrentNumPlayers()) > 1) continue; } - else if (m_cur_difficulty == RaceManager::DIFFICULTY_BEST && use_difficulty) + else if (m_cur_difficulty == RaceManager::DIFFICULTY_BEST && + use_difficulty) { // Skip AI players for supertux mode - const AbstractKart* temp = m_world->getKart(i); + const AbstractKart* temp = m_world->getKart(start_id); if (!(temp->getController()->isPlayerController())) continue; } @@ -118,7 +111,7 @@ void BattleAI::findClosestKart(bool use_difficulty) if (dist_to_kart <= distance) { distance = dist_to_kart; - closest_kart_num = i; + closest_kart_num = start_id; } } @@ -131,9 +124,30 @@ void BattleAI::findClosestKart(bool use_difficulty) //----------------------------------------------------------------------------- void BattleAI::findTarget() { + // Find the closest kart first, it's used as fallback if no item is found. + // It takes the current difficulty into account, also collect life from + // spare tire karts when neccessary + + // Collect life depends on current difficulty: + // Novice and intermediate - collect them only AI has 1 life only + // Expert and supertux - collect them if AI dones't have 3 lives + // Also when actually spare tire karts are spawned + bool find_sta = m_world->spareTireKartsSpawned() ? + ((m_cur_difficulty == RaceManager::DIFFICULTY_EASY || + m_cur_difficulty == RaceManager::DIFFICULTY_MEDIUM) && + m_world->getKartLife(m_kart->getWorldKartId()) == 1 ? + true : + (m_cur_difficulty == RaceManager::DIFFICULTY_HARD || + m_cur_difficulty == RaceManager::DIFFICULTY_BEST) && + m_world->getKartLife(m_kart->getWorldKartId()) != 3 ? + true : false) : false; + + findClosestKart(find_sta ? false : true/*use_difficulty*/, find_sta); + // Find a suitable target to drive to, either powerup or kart if (m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING && - m_kart->getAttachment()->getType() != Attachment::ATTACH_SWATTER) + m_kart->getAttachment()->getType() != Attachment::ATTACH_SWATTER && + !find_sta) collectItemInArena(&m_target_point , &m_target_node); else { diff --git a/src/karts/controller/battle_ai.hpp b/src/karts/controller/battle_ai.hpp index 5a89a5112..915f34d15 100644 --- a/src/karts/controller/battle_ai.hpp +++ b/src/karts/controller/battle_ai.hpp @@ -30,21 +30,19 @@ class ThreeStrikesBattle; */ class BattleAI : public ArenaAI { -private: +protected: /** Keep a pointer to world. */ ThreeStrikesBattle *m_world; - - virtual void findClosestKart(bool use_difficulty) OVERRIDE; - virtual void findTarget() OVERRIDE; + virtual void findClosestKart(bool use_difficulty, bool find_sta) OVERRIDE; virtual int getCurrentNode() const OVERRIDE; +private: + virtual void findTarget() OVERRIDE; virtual float getKartDistance(const AbstractKart* kart) const OVERRIDE; virtual bool isKartOnRoad() const OVERRIDE; virtual bool isWaiting() const OVERRIDE; public: - BattleAI(AbstractKart *kart); - ~BattleAI(); - virtual void update (float delta) OVERRIDE; - virtual void reset () OVERRIDE; + BattleAI(AbstractKart *kart); + ~BattleAI(); }; #endif diff --git a/src/karts/controller/soccer_ai.cpp b/src/karts/controller/soccer_ai.cpp index 5caf323be..af4e6925a 100644 --- a/src/karts/controller/soccer_ai.cpp +++ b/src/karts/controller/soccer_ai.cpp @@ -129,7 +129,7 @@ void SoccerAI::update(float dt) } // update //----------------------------------------------------------------------------- -void SoccerAI::findClosestKart(bool use_difficulty) +void SoccerAI::findClosestKart(bool use_difficulty, bool find_sta) { float distance = 99999.9f; const unsigned int n = m_world->getNumKarts(); @@ -165,6 +165,7 @@ void SoccerAI::findClosestKart(bool use_difficulty) //----------------------------------------------------------------------------- void SoccerAI::findTarget() { + findClosestKart(true/*use_difficulty*/, false/*find_sta*/); // Check if this AI kart is the one who will chase the ball if (m_world->getBallChaser(m_cur_team) == (signed)m_kart->getWorldKartId()) { diff --git a/src/karts/controller/soccer_ai.hpp b/src/karts/controller/soccer_ai.hpp index b9a3bcd1d..a93832781 100644 --- a/src/karts/controller/soccer_ai.hpp +++ b/src/karts/controller/soccer_ai.hpp @@ -65,7 +65,7 @@ private: virtual bool canSkid(float steer_fraction) OVERRIDE { return m_mini_skid && !(m_overtake_ball || m_chasing_ball); } - virtual void findClosestKart(bool use_difficulty) OVERRIDE; + virtual void findClosestKart(bool use_difficulty, bool find_sta) OVERRIDE; virtual void findTarget() OVERRIDE; virtual bool forceBraking() OVERRIDE { return m_force_brake; } virtual int getCurrentNode() const OVERRIDE; diff --git a/src/karts/controller/spare_tire_ai.cpp b/src/karts/controller/spare_tire_ai.cpp new file mode 100644 index 000000000..18a9f5123 --- /dev/null +++ b/src/karts/controller/spare_tire_ai.cpp @@ -0,0 +1,136 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "karts/controller/spare_tire_ai.hpp" + +#include "karts/abstract_kart.hpp" +#include "karts/kart_gfx.hpp" +#include "karts/max_speed.hpp" +#include "modes/three_strikes_battle.hpp" +#include "states_screens/race_gui.hpp" +#include "tracks/arena_graph.hpp" +#include "tracks/arena_node.hpp" +#include "physics/physics.hpp" +#include "utils/random_generator.hpp" + +#include + +SpareTireAI::SpareTireAI(AbstractKart *kart) + : BattleAI(kart) +{ + reset(); + // Don't call our own setControllerName, since this will add a + // billboard showing 'AIBaseController' to the kart. + Controller::setControllerName("SpareTireAI"); + + // Pre-load the 4 nodes of bounding box defined by battle world + memcpy(m_fixed_target_nodes, m_graph->getBBNodes(), 4 * sizeof(int)); + + // Reverse the order depends on world ID, so not all spare tire karts go + // the same way + if (m_kart->getWorldKartId() % 2 != 0) + { + std::reverse(std::begin(m_fixed_target_nodes), + std::end(m_fixed_target_nodes)); + } +} // SpareTireAI + +//----------------------------------------------------------------------------- +/** Resets the AI when a race is restarted. + */ +void SpareTireAI::reset() +{ + BattleAI::reset(); + m_idx = 0; + m_timer = 0.0f; +} // reset + +//----------------------------------------------------------------------------- +void SpareTireAI::update(float dt) +{ + BattleAI::update(dt); + m_kart->setSlowdown(MaxSpeed::MS_DECREASE_AI, 0.5f, /*fade_in_time*/0.0f); + m_timer -= dt; + if (m_timer < 0.0f) + unspawn(); +} // update + +//----------------------------------------------------------------------------- +void SpareTireAI::findDefaultPath() +{ + // Randomly find a start node for spare tire kart to move + assert(m_idx == -1); + + RandomGenerator random; + m_idx = random.get(4); + m_target_node = m_fixed_target_nodes[m_idx]; + +} // findDefaultPath + +//----------------------------------------------------------------------------- +void SpareTireAI::findTarget() +{ + assert(m_idx != -1 && m_idx < 4); + if (getCurrentNode() == m_fixed_target_nodes[m_idx]) + m_idx = m_idx == 3 ? 0 : m_idx + 1; + + const int chosen_node = m_fixed_target_nodes[m_idx]; + m_target_node = chosen_node; + m_target_point = m_graph->getNode(chosen_node)->getCenter(); +} // findTarget + +//----------------------------------------------------------------------------- +void SpareTireAI::spawn(float time_to_last) +{ + findDefaultPath(); + m_timer = time_to_last; + + World::getWorld()->getPhysics()->addKart(m_kart); + m_kart->startEngineSFX(); + m_kart->getKartGFX()->reset(); + m_kart->getNode()->setVisible(true); + +} // spawn + +//----------------------------------------------------------------------------- +void SpareTireAI::unspawn() +{ + m_idx = -1; + m_kart->eliminate(); +} // unspawn + +//----------------------------------------------------------------------------- +void SpareTireAI::crashed(const AbstractKart *k) +{ + // Nothing happen when two spare tire karts crash each other + if (dynamic_cast(k->getController()) != NULL) return; + + // Tell player that they have max 3 lives only + if (m_world->getKartLife(k->getWorldKartId()) == 3) + { + World::getWorld()->getRaceGUI()->addMessage + (_("You can have at most 3 lives!"), k, 2.0f); + return; + } + + // Otherwise increase one life for that kart and unspawn + m_world->addKartLife(k->getWorldKartId()); + World::getWorld()->getRaceGUI()->addMessage(_("+1 life."), k, 2.0f); + unspawn(); + +} // crashed diff --git a/src/karts/controller/spare_tire_ai.hpp b/src/karts/controller/spare_tire_ai.hpp new file mode 100644 index 000000000..8aa7c7924 --- /dev/null +++ b/src/karts/controller/spare_tire_ai.hpp @@ -0,0 +1,48 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_SPARE_TIRE_AI_HPP +#define HEADER_SPARE_TIRE_AI_HPP + +#include "karts/controller/battle_ai.hpp" + +/** The AI for spare tire karts in battle mode, allowing kart to gain life. + * \ingroup controller + */ +class SpareTireAI : public BattleAI +{ +private: + int m_fixed_target_nodes[4]; + + int m_idx; + + float m_timer; + + virtual void findTarget() OVERRIDE; + void findDefaultPath(); +public: + SpareTireAI(AbstractKart *kart); + virtual void crashed(const AbstractKart *k) OVERRIDE; + virtual void update(float delta) OVERRIDE; + virtual void reset() OVERRIDE; + void spawn(float time_to_last); + void unspawn(); + bool isMoving() const { return m_idx != -1; } +}; + +#endif diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index c2dd039f7..64c14f204 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -50,6 +50,7 @@ #include "karts/abstract_kart_animation.hpp" #include "karts/cached_characteristic.hpp" #include "karts/controller/end_controller.hpp" +#include "karts/controller/spare_tire_ai.hpp" #include "karts/explosion_animation.hpp" #include "karts/kart_gfx.hpp" #include "karts/kart_model.hpp" @@ -862,6 +863,9 @@ void Kart::finishedRace(float time, bool from_server) m_kart_model->finishedRace(); race_manager->kartFinishedRace(this, time); + // If this is spare tire kart, end now + if (dynamic_cast(m_controller) != NULL) return; + if ((race_manager->getMinorMode() == RaceManager::MINOR_MODE_NORMAL_RACE || race_manager->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL || race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER) diff --git a/src/karts/kart_gfx.cpp b/src/karts/kart_gfx.cpp index 829a6749b..4bbe5fde0 100644 --- a/src/karts/kart_gfx.cpp +++ b/src/karts/kart_gfx.cpp @@ -149,7 +149,8 @@ void KartGFX::addEffect(KartGFXType type, const std::string &file_name, const Vec3 &position, bool important) { if (!UserConfigParams::m_graphical_effects && - (!important || m_kart->getType() == RaceManager::KT_AI)) + (!important || m_kart->getType() == RaceManager::KT_AI || + m_kart->getType() == RaceManager::KT_SPARE_TIRE)) { m_all_emitters.push_back(NULL); return; diff --git a/src/karts/kart_properties_manager.cpp b/src/karts/kart_properties_manager.cpp index a9f58429c..3398794d8 100644 --- a/src/karts/kart_properties_manager.cpp +++ b/src/karts/kart_properties_manager.cpp @@ -494,7 +494,7 @@ const std::vector KartPropertiesManager::getKartsInGroup( * to this list. */ void KartPropertiesManager::getRandomKartList(int count, - RemoteKartInfoList& existing_karts, + RemoteKartInfoList* existing_karts, std::vector *ai_list) { // First: set up flags (based on global kart @@ -504,18 +504,22 @@ void KartPropertiesManager::getRandomKartList(int count, used.resize(getNumberOfKarts(), false); std::vector random_kart_queue; - for (unsigned int i=0; isize(); i++) { - int id = getKartId(existing_karts[i].getKartName()); - used[id] = true; - } - catch (std::runtime_error& ex) - { - (void)ex; - Log::error("[KartPropertiesManager]", "getRandomKartList : WARNING, " - "can't find kart '%s'", existing_karts[i].getKartName().c_str()); + try + { + int id = getKartId((*existing_karts)[i].getKartName()); + used[id] = true; + } + catch (std::runtime_error& ex) + { + (void)ex; + Log::error("[KartPropertiesManager]", "getRandomKartList : " + "WARNING, can't find kart '%s'", + (*existing_karts)[i].getKartName().c_str()); + } } } for(unsigned int i=0; isize(); i++) diff --git a/src/karts/kart_properties_manager.hpp b/src/karts/kart_properties_manager.hpp index af621b4e7..240413a80 100644 --- a/src/karts/kart_properties_manager.hpp +++ b/src/karts/kart_properties_manager.hpp @@ -94,7 +94,7 @@ public: void selectKartName(const std::string &kart_name); bool testAndSetKart(int kartid); void getRandomKartList(int count, - RemoteKartInfoList& existing_karts, + RemoteKartInfoList* existing_karts, std::vector *ai_list); void setHatMeshName(const std::string &hat_name); // ------------------------------------------------------------------------ diff --git a/src/modes/soccer_world.cpp b/src/modes/soccer_world.cpp index b7f565805..bb6422486 100644 --- a/src/modes/soccer_world.cpp +++ b/src/modes/soccer_world.cpp @@ -30,6 +30,7 @@ #include "karts/kart_properties.hpp" #include "karts/rescue_animation.hpp" #include "karts/controller/local_player_controller.hpp" +#include "karts/controller/network_player_controller.hpp" #include "physics/physics.hpp" #include "states_screens/race_gui_base.hpp" #include "tracks/track.hpp" @@ -421,11 +422,9 @@ AbstractKart *SoccerWorld::createKart(const std::string &kart_ident, int index, m_num_players ++; break; case RaceManager::KT_NETWORK_PLAYER: - break; // Avoid compiler warning about enum not handled. - //controller = new NetworkController(kart_ident, position, init_pos, - // global_player_id); - //m_num_players++; - //break; + controller = new NetworkPlayerController(new_kart); + m_num_players++; + break; case RaceManager::KT_AI: controller = loadAIController(new_kart); break; @@ -433,6 +432,8 @@ AbstractKart *SoccerWorld::createKart(const std::string &kart_ident, int index, break; case RaceManager::KT_LEADER: break; + case RaceManager::KT_SPARE_TIRE: + break; } new_kart->setController(controller); diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index f83375dad..e714abf66 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -23,15 +23,20 @@ #include "graphics/camera.hpp" #include "graphics/irr_driver.hpp" #include "io/file_manager.hpp" -#include "karts/abstract_kart.hpp" +#include "karts/kart.hpp" +#include "karts/controller/spare_tire_ai.hpp" #include "karts/kart_model.hpp" #include "karts/kart_properties.hpp" +#include "karts/kart_properties_manager.hpp" #include "physics/physics.hpp" #include "states_screens/race_gui_base.hpp" +#include "tracks/arena_graph.hpp" +#include "tracks/arena_node.hpp" #include "tracks/track.hpp" #include "tracks/track_object_manager.hpp" #include "utils/constants.hpp" +#include #include #include @@ -73,6 +78,7 @@ void ThreeStrikesBattle::init() ThreeStrikesBattle::~ThreeStrikesBattle() { m_tires.clearWithoutDeleting(); + m_spare_tire_karts.clear(); irr_driver->dropAllTextures(m_tire); // Remove the mesh from the cache so that the mesh is properly @@ -88,11 +94,25 @@ void ThreeStrikesBattle::reset() { WorldWithRank::reset(); + m_next_sta_spawn_time = + race_manager->getDifficulty() == RaceManager::DIFFICULTY_BEST ? 40.0f : + race_manager->getDifficulty() == RaceManager::DIFFICULTY_HARD ? 30.0f : + race_manager->getDifficulty() == RaceManager::DIFFICULTY_MEDIUM ? + 25.0f : 20.0f; + const unsigned int kart_amount = (unsigned int)m_karts.size(); for(unsigned int n=0; n(m_karts[n]->getController()) != NULL) + { + // STA has no life + m_kart_info[n].m_lives = 0; + } + else + { + m_kart_info[n].m_lives = 3; + } // no positions in this mode m_karts[n]->setPosition(-1); @@ -132,6 +152,18 @@ void ThreeStrikesBattle::reset() m_track->getTrackObjectManager()->removeObject(obj); } m_tires.clearWithoutDeleting(); + + // Finish all spare tire karts first + if (!m_spare_tire_karts.empty()) + { + updateKartRanks(); + for (unsigned int i = 0; i < m_spare_tire_karts.size(); i++) + { + m_spare_tire_karts[i]->finishedRace(0.0f); + m_spare_tire_karts[i]->getNode()->setVisible(false); + m_eliminated_karts++; + } + } } // reset //----------------------------------------------------------------------------- @@ -142,6 +174,20 @@ void ThreeStrikesBattle::reset() */ void ThreeStrikesBattle::kartAdded(AbstractKart* kart, scene::ISceneNode* node) { + if (kart->getType() == RaceManager::KartType::KT_SPARE_TIRE) + { + // Add heart billboard above it + video::ITexture *heart = + irr_driver->getTexture(FileManager::GUI, "heart.png"); + float height = kart->getKartHeight() + 0.5f; + + scene::ISceneNode* billboard = irr_driver->addBillboard + (core::dimension2d(0.8f, 0.8f), heart, kart->getNode(), + true); + billboard->setPosition(core::vector3df(0, height, 0)); + return; + } + float coord = -kart->getKartLength()*0.5f; scene::IMeshSceneNode* tire_node = irr_driver->addMesh(m_tire, "3strikestire", node); @@ -165,6 +211,15 @@ void ThreeStrikesBattle::kartHit(const unsigned int kart_id) { if (isRaceOver()) return; + SpareTireAI* sta = + dynamic_cast(m_karts[kart_id]->getController()); + if (sta) + { + // Unspawn the spare tire kart if it get hit + sta->unspawn(); + return; + } + assert(kart_id < m_karts.size()); // make kart lose a life, ignore if in profiling mode if (!UserConfigParams::m_arena_ai_stats) @@ -305,6 +360,7 @@ void ThreeStrikesBattle::update(float dt) WorldWithRank::update(dt); WorldWithRank::updateTrack(dt); + spawnSpareTireKarts(); if (m_track->hasNavMesh()) updateSectorForKarts(); @@ -446,7 +502,7 @@ bool ThreeStrikesBattle::isRaceOver() return (irr_driver->getRealTime()-m_start_time)*0.001f > 20.0f; // for tests : never over when we have a single player there :) - if (race_manager->getNumberOfKarts()==1 && + if (race_manager->getNumberOfKarts() - m_spare_tire_karts.size () ==1 && getCurrentNumKarts()==1 && UserConfigParams::m_artist_debug_mode) { @@ -509,6 +565,16 @@ void ThreeStrikesBattle::enterRaceOverState() { WorldWithRank::enterRaceOverState(); + // Unspawn all spare tire karts if neccesary + for (unsigned int i = 0; i < m_spare_tire_karts.size(); i++) + { + SpareTireAI* sta = + dynamic_cast(m_spare_tire_karts[i]->getController()); + assert(sta); + if (sta->isMoving()) + sta->unspawn(); + } + if (UserConfigParams::m_arena_ai_stats) { float runtime = (irr_driver->getRealTime()-m_start_time)*0.001f; @@ -521,3 +587,164 @@ void ThreeStrikesBattle::enterRaceOverState() } } // enterRaceOverState + +//----------------------------------------------------------------------------- +bool ThreeStrikesBattle::spareTireKartsSpawned() const +{ + if (m_spare_tire_karts.empty()) return false; + + // Spare tire karts are spawned if at least 1 of them needs update + SpareTireAI* sta = + dynamic_cast(m_spare_tire_karts[0]->getController()); + assert(sta); + + return sta->isMoving(); +} // spareTireKartsSpawned + +//----------------------------------------------------------------------------- +void ThreeStrikesBattle::addKartLife(unsigned int id) +{ + m_kart_info[id].m_lives++; + updateKartRanks(); + + scene::ISceneNode* kart_node = m_karts[id]->getNode(); + core::list& children = + const_cast&>(kart_node->getChildren()); + for (core::list::Iterator it = children.begin(); + it != children.end(); it++) + { + scene::ISceneNode* curr = *it; + if (core::stringc(curr->getName()) == "tire1") + { + curr->setVisible(m_kart_info[id].m_lives >= 3); + } + else if (core::stringc(curr->getName()) == "tire2") + { + curr->setVisible(m_kart_info[id].m_lives >= 2); + } + } + +} // addKartLife + +//----------------------------------------------------------------------------- +void ThreeStrikesBattle::spawnSpareTireKarts() +{ + if (m_spare_tire_karts.empty() || + getTimeSinceStart() < m_next_sta_spawn_time) + return; + + const float period = + race_manager->getDifficulty() == RaceManager::DIFFICULTY_BEST ? 40.0f : + race_manager->getDifficulty() == RaceManager::DIFFICULTY_HARD ? 30.0f : + race_manager->getDifficulty() == RaceManager::DIFFICULTY_MEDIUM ? + 25.0f : 20.0f; + const float inc_factor = + race_manager->getDifficulty() == RaceManager::DIFFICULTY_BEST ? 0.7f : + race_manager->getDifficulty() == RaceManager::DIFFICULTY_HARD ? 0.65f : + race_manager->getDifficulty() == RaceManager::DIFFICULTY_MEDIUM ? + 0.6f : 0.55f; + + // Spawn spare tire kart when necessary + // The lifespan for sta: inc_factor / period * 1000 / 2 + // So in easier mode the sta lasts longer than spawn period + const float lifespan = inc_factor / period * 1000; + m_next_sta_spawn_time = lifespan + (getTimeSinceStart() * inc_factor) + + getTimeSinceStart(); + int kart_has_few_lives = 0; + for (unsigned int i = 0; i < m_kart_info.size(); i++) + { + if (m_kart_info[i].m_lives > 0 && m_kart_info[i].m_lives < 3) + kart_has_few_lives++; + } + + float ratio = kart_has_few_lives / (inc_factor * 2); + if (ratio < 1.5f) return; + unsigned int spawn_sta = unsigned(ratio); + if (spawn_sta > m_spare_tire_karts.size()) + spawn_sta = m_spare_tire_karts.size(); + m_race_gui->addMessage(_P("%i spare tire kart has been spawned!", + "%i spare tire karts have been spawned!", + spawn_sta), NULL, 2.0f); + for (unsigned int i = 0; i < spawn_sta; i++) + { + SpareTireAI* sta = dynamic_cast + (m_spare_tire_karts[i]->getController()); + assert(sta); + sta->spawn(lifespan); + } +} // spawnSpareTireKarts + +//----------------------------------------------------------------------------- +void ThreeStrikesBattle::loadCustomModels() +{ + // Pre-add spare tire karts if there are more than certain number of karts + ArenaGraph* ag = ArenaGraph::get(); + if (ag && m_karts.size() > 4) + { + // Spare tire karts only added with large arena + const int all_nodes = ag->getNumNodes(); + if (all_nodes > 500) + { + // Don't create too many spare tire karts + const unsigned int max_sta_num = unsigned(m_karts.size() * 0.8f); + unsigned int pos_created = 0; + std::vector used; + std::vector pos; + + // Fill all current starting position into used first + for (unsigned int i = 0; i < getNumberOfRescuePositions(); i++) + { + int node = -1; + ag->findRoadSector(getRescueTransform(i).getOrigin(), &node, + NULL, true); + assert(node != -1); + used.push_back(node); + } + + // Find random nodes to pre-spawn spare tire karts + RandomGenerator random; + while (true) + { + const int node = random.get(all_nodes); + if (std::find(used.begin(), used.end(), node) != used.end()) + continue; + const ArenaNode* n = ag->getNode(node); + btTransform t; + t.setOrigin(n->getCenter()); + t.setRotation(Track::createRotationFromNormal(n->getNormal())); + pos.push_back(t); + pos_created++; + used.push_back(node); + if (pos_created >= max_sta_num) break; + } + + // Compute a random kart list + std::vector sta_list; + kart_properties_manager->getRandomKartList(pos.size(), NULL, + &sta_list); + + assert(sta_list.size() == pos.size()); + // Now add them + for (unsigned int i = 0; i < pos.size(); i++) + { + AbstractKart* sta = new Kart(sta_list[i], m_karts.size(), + m_karts.size() + 1, pos[i], PLAYER_DIFFICULTY_NORMAL, + KRT_RED); + sta->init(RaceManager::KartType::KT_SPARE_TIRE); + sta->setController(new SpareTireAI(sta)); + + m_karts.push_back(sta); + race_manager->addSpareTireKart(sta_list[i]); + m_track->adjustForFog(sta->getNode()); + + // Copy STA pointer to m_spare_tire_karts array, allowing them + // to respawn easily + m_spare_tire_karts.push_back(sta); + } + unsigned int sta_num = race_manager->getNumSpareTireKarts(); + assert(m_spare_tire_karts.size() == sta_num); + Log::info("ThreeStrikesBattle","%d spare tire kart(s) created.", + sta_num); + } + } +} // loadCustomModels diff --git a/src/modes/three_strikes_battle.hpp b/src/modes/three_strikes_battle.hpp index 0acfffd43..184b9dc73 100644 --- a/src/modes/three_strikes_battle.hpp +++ b/src/modes/three_strikes_battle.hpp @@ -77,44 +77,63 @@ private: int m_start_time; int m_total_hit; -public: + std::vector m_spare_tire_karts; + float m_next_sta_spawn_time; +public: /** Used to show a nice graph when battle is over */ struct BattleEvent { float m_time; std::vector m_kart_info; }; + // ------------------------------------------------------------------------ std::vector m_battle_events; - + // ------------------------------------------------------------------------ ThreeStrikesBattle(); + // ------------------------------------------------------------------------ virtual ~ThreeStrikesBattle(); - + // ------------------------------------------------------------------------ virtual void init() OVERRIDE; - + // ------------------------------------------------------------------------ // clock events virtual bool isRaceOver() OVERRIDE; + // ------------------------------------------------------------------------ virtual void terminateRace() OVERRIDE; - + // ------------------------------------------------------------------------ // overriding World methods virtual void reset() OVERRIDE; - - //virtual void getDefaultCollectibles(int& collectible_type, int& amount); - virtual bool useFastMusicNearEnd() const OVERRIDE { return false; } + // ------------------------------------------------------------------------ virtual void getKartsDisplayInfo( std::vector *info) OVERRIDE; - virtual bool raceHasLaps() OVERRIDE { return false; } - + // ------------------------------------------------------------------------ + virtual bool raceHasLaps() OVERRIDE { return false; } + // ------------------------------------------------------------------------ virtual const std::string& getIdent() const OVERRIDE; - + // ------------------------------------------------------------------------ virtual void kartHit(const unsigned int kart_id) OVERRIDE; + // ------------------------------------------------------------------------ virtual void update(float dt) OVERRIDE; - - virtual void kartAdded(AbstractKart* kart, scene::ISceneNode* node) OVERRIDE; + // ------------------------------------------------------------------------ + virtual void kartAdded(AbstractKart* kart, scene::ISceneNode* node) + OVERRIDE; + // ------------------------------------------------------------------------ virtual void enterRaceOverState() OVERRIDE; - + // ------------------------------------------------------------------------ + virtual void loadCustomModels() OVERRIDE; + // ------------------------------------------------------------------------ void updateKartRanks(); - void increaseRescueCount() { m_total_rescue++; } + // ------------------------------------------------------------------------ + void increaseRescueCount() { m_total_rescue++; } + // ------------------------------------------------------------------------ + void addKartLife(unsigned int id); + // ------------------------------------------------------------------------ + int getKartLife(unsigned int id) const { return m_kart_info[id].m_lives; } + // ------------------------------------------------------------------------ + bool spareTireKartsSpawned() const; + // ------------------------------------------------------------------------ + void spawnSpareTireKarts(); + }; // ThreeStrikesBattles diff --git a/src/modes/world.cpp b/src/modes/world.cpp index a11f454be..1963381d1 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -38,6 +38,7 @@ #include "karts/controller/end_controller.hpp" #include "karts/controller/local_player_controller.hpp" #include "karts/controller/skidding_ai.hpp" +#include "karts/controller/spare_tire_ai.hpp" #include "karts/controller/test_ai.hpp" #include "karts/controller/network_player_controller.hpp" #include "karts/kart.hpp" @@ -220,13 +221,16 @@ void World::init() } // for i + // Load other custom models if needed + loadCustomModels(); + // Now that all models are loaded, apply the overrides irr_driver->applyObjectPassShader(); // Must be called after all karts are created m_race_gui->init(); - powerup_manager->updateWeightsForRace(num_karts); + powerup_manager->updateWeightsForRace(race_manager->getNumberOfKarts()); if (UserConfigParams::m_weather_effects) { @@ -368,6 +372,8 @@ AbstractKart *World::createKart(const std::string &kart_ident, int index, break; case RaceManager::KT_LEADER: break; + case RaceManager::KT_SPARE_TIRE: + break; } new_kart->setController(controller); @@ -477,6 +483,7 @@ World::~World() race_manager->setRecordRace(false); race_manager->setWatchingReplay(false); race_manager->setTimeTarget(0.0f); + race_manager->setSpareTireKartNum(0); Camera::removeAllCameras(); @@ -973,8 +980,11 @@ void World::update(float dt) const int kart_amount = (int)m_karts.size(); for (int i = 0 ; i < kart_amount; ++i) { + SpareTireAI* sta = + dynamic_cast(m_karts[i]->getController()); // Update all karts that are not eliminated - if(!m_karts[i]->isEliminated()) m_karts[i]->update(dt) ; + if(!m_karts[i]->isEliminated() || (sta && sta->isMoving())) + m_karts[i]->update(dt); } PROFILER_POP_CPU_MARKER(); diff --git a/src/modes/world.hpp b/src/modes/world.hpp index f74e7c6cd..fedeb4e79 100644 --- a/src/modes/world.hpp +++ b/src/modes/world.hpp @@ -360,6 +360,8 @@ public: void delayedSelfDestruct(); // ------------------------------------------------------------------------ virtual void escapePressed(); + // ------------------------------------------------------------------------ + virtual void loadCustomModels() {} /** Set the network mode (true if networked) */ void setNetworkWorld(bool is_networked) { m_is_network_world = is_networked; } diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp index 6cf5cd65d..9a5f78304 100644 --- a/src/modes/world_status.cpp +++ b/src/modes/world_status.cpp @@ -199,7 +199,8 @@ void WorldStatus::updateTime(const float dt) m_auxiliary_timer += dt; if (UserConfigParams::m_artist_debug_mode && - race_manager->getNumberOfKarts() == 1 && + race_manager->getNumberOfKarts() - + race_manager->getNumSpareTireKarts() == 1 && race_manager->getTrackName() != "tutorial") { m_auxiliary_timer += dt * 6; @@ -276,7 +277,8 @@ void WorldStatus::updateTime(const float dt) // In artist debug mode, when without opponents, skip the // ready/set/go counter faster if (UserConfigParams::m_artist_debug_mode && - race_manager->getNumberOfKarts() == 1 && + race_manager->getNumberOfKarts() - + race_manager->getNumSpareTireKarts() == 1 && race_manager->getTrackName() != "tutorial") { m_auxiliary_timer += dt*6; @@ -302,7 +304,8 @@ void WorldStatus::updateTime(const float dt) // In artist debug mode, when without opponents, // skip the ready/set/go counter faster if (UserConfigParams::m_artist_debug_mode && - race_manager->getNumberOfKarts() == 1 && + race_manager->getNumberOfKarts() - + race_manager->getNumSpareTireKarts() == 1 && race_manager->getTrackName() != "tutorial") { m_auxiliary_timer += dt*6; @@ -327,7 +330,8 @@ void WorldStatus::updateTime(const float dt) // In artist debug mode, when without opponents, // skip the ready/set/go counter faster if (UserConfigParams::m_artist_debug_mode && - race_manager->getNumberOfKarts() == 1 && + race_manager->getNumberOfKarts() - + race_manager->getNumSpareTireKarts() == 1 && race_manager->getTrackName() != "tutorial") { m_auxiliary_timer += dt*6; diff --git a/src/modes/world_with_rank.cpp b/src/modes/world_with_rank.cpp index cb84ae2da..60f4ffe27 100644 --- a/src/modes/world_with_rank.cpp +++ b/src/modes/world_with_rank.cpp @@ -18,6 +18,7 @@ #include "modes/world_with_rank.hpp" #include "karts/abstract_kart.hpp" +#include "karts/controller/spare_tire_ai.hpp" #include "karts/kart_properties.hpp" #include "race/history.hpp" #include "tracks/graph.hpp" @@ -251,7 +252,9 @@ void WorldWithRank::updateSectorForKarts() assert(n == m_kart_track_sector.size()); for (unsigned int i = 0; i < n; i++) { - if (m_karts[i]->isEliminated()) continue; - getTrackSector(i)->update(m_karts[i]->getXYZ()); + SpareTireAI* sta = + dynamic_cast(m_karts[i]->getController()); + if (!m_karts[i]->isEliminated() || (sta && sta->isMoving())) + getTrackSector(i)->update(m_karts[i]->getXYZ()); } } // updateSectorForKarts diff --git a/src/race/race_manager.cpp b/src/race/race_manager.cpp index 84fe2dc4f..011208291 100644 --- a/src/race/race_manager.cpp +++ b/src/race/race_manager.cpp @@ -84,6 +84,7 @@ RaceManager::RaceManager() setTrack("jungle"); m_default_ai_list.clear(); setNumPlayers(0); + setSpareTireKartNum(0); } // RaceManager //----------------------------------------------------------------------------- @@ -288,7 +289,7 @@ void RaceManager::computeRandomKartList() } if(n>0) - kart_properties_manager->getRandomKartList(n, m_player_karts, + kart_properties_manager->getRandomKartList(n, &m_player_karts, &m_ai_kart_list ); if (m_ai_kart_override != "") diff --git a/src/race/race_manager.hpp b/src/race/race_manager.hpp index d4e166b26..7be0e8227 100644 --- a/src/race/race_manager.hpp +++ b/src/race/race_manager.hpp @@ -236,9 +236,10 @@ public: DIFFICULTY_COUNT}; /** Different kart types: A local player, a player connected via network, - * an AI kart, the leader kart (currently not used), a ghost kart. */ + * an AI kart, the leader kart (currently not used), a ghost kart and + * spare tire karts which allow gain life in battle mode */ enum KartType { KT_PLAYER, KT_NETWORK_PLAYER, KT_AI, KT_LEADER, - KT_GHOST }; + KT_GHOST, KT_SPARE_TIRE }; private: bool m_started_from_overworld; @@ -329,6 +330,7 @@ private: GrandPrixData m_grand_prix; SavedGrandPrix* m_saved_gp; int m_num_karts; + unsigned int m_num_spare_tire_karts; unsigned int m_num_finished_karts; unsigned int m_num_finished_players; int m_coin_target; @@ -751,6 +753,24 @@ public: { return m_watching_replay; } // isWatchingReplay + // ------------------------------------------------------------------------ + void addSpareTireKart(const std::string& name) + { + m_kart_status.push_back(KartStatus(name, 0, -1, -1, + -1, KT_SPARE_TIRE, PLAYER_DIFFICULTY_NORMAL)); + m_num_spare_tire_karts++; + m_num_karts++; + } // addSpareTireKart + // ------------------------------------------------------------------------ + void setSpareTireKartNum(unsigned int i) + { + m_num_spare_tire_karts = i; + } // setSpareTireKartNum + // ------------------------------------------------------------------------ + unsigned int getNumSpareTireKarts() const + { + return m_num_spare_tire_karts; + } // getNumSpareTireKarts }; // RaceManager diff --git a/src/states_screens/race_gui.cpp b/src/states_screens/race_gui.cpp index 573e3c1dd..8012947ef 100644 --- a/src/states_screens/race_gui.cpp +++ b/src/states_screens/race_gui.cpp @@ -39,7 +39,7 @@ using namespace irr; #include "items/attachment_manager.hpp" #include "items/powerup_manager.hpp" #include "karts/abstract_kart.hpp" -#include "karts/controller/controller.hpp" +#include "karts/controller/spare_tire_ai.hpp" #include "karts/kart_properties.hpp" #include "karts/kart_properties_manager.hpp" #include "modes/follow_the_leader.hpp" @@ -104,6 +104,18 @@ RaceGUI::RaceGUI() else m_lap_width = font->getDimension(L"9/9").Width; +} // RaceGUI + +//----------------------------------------------------------------------------- +RaceGUI::~RaceGUI() +{ +} // ~Racegui + + +//----------------------------------------------------------------------------- +void RaceGUI::init() +{ + RaceGUIBase::init(); // Technically we only need getNumLocalPlayers, but using the // global kart id to find the data for a specific kart. int n = race_manager->getNumberOfKarts(); @@ -111,12 +123,7 @@ RaceGUI::RaceGUI() m_animation_states.resize(n); m_rank_animation_duration.resize(n); m_last_ranks.resize(n); -} // RaceGUI - -//----------------------------------------------------------------------------- -RaceGUI::~RaceGUI() -{ -} // ~Racegui +} // init //----------------------------------------------------------------------------- /** Reset the gui before a race. It initialised all rank animation related @@ -369,13 +376,18 @@ void RaceGUI::drawGlobalMiniMap() for(unsigned int i=0; igetNumKarts(); i++) { const AbstractKart *kart = world->getKart(i); - if(kart->isEliminated()) continue; // don't draw eliminated kart + const SpareTireAI* sta = + dynamic_cast(kart->getController()); + // don't draw eliminated kart + if(kart->isEliminated() && !(sta && sta->isMoving())) continue; const Vec3& xyz = kart->getXYZ(); Vec3 draw_at; world->getTrack()->mapPoint2MiniMap(xyz, &draw_at); draw_at *= UserConfigParams::m_scale_rtts_factor; - video::ITexture* icon = kart->getKartProperties()->getMinimapIcon(); + video::ITexture* icon = sta ? + irr_driver->getTexture(FileManager::GUI, "heart.png") : + kart->getKartProperties()->getMinimapIcon(); // int marker_height = m_marker->getSize().Height; core::rect source(core::position2di(0, 0), icon->getSize()); diff --git a/src/states_screens/race_gui.hpp b/src/states_screens/race_gui.hpp index a7f01aed4..93e7e31e3 100644 --- a/src/states_screens/race_gui.hpp +++ b/src/states_screens/race_gui.hpp @@ -119,6 +119,7 @@ public: RaceGUI(); ~RaceGUI(); + virtual void init(); virtual void reset(); virtual void renderGlobal(float dt); virtual void renderPlayerView(const Camera *camera, float dt); diff --git a/src/states_screens/race_gui_base.cpp b/src/states_screens/race_gui_base.cpp index 492fe0587..930941f0d 100644 --- a/src/states_screens/race_gui_base.cpp +++ b/src/states_screens/race_gui_base.cpp @@ -638,8 +638,11 @@ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin) y_space = irr_driver->getActualScreenSize().Height - y_base; } + unsigned int sta = race_manager->getNumSpareTireKarts(); + const unsigned int num_karts = race_manager->getNumberOfKarts() - sta; + // -2 because that's the spacing further on - int ICON_PLAYER_WIDTH = y_space / race_manager->getNumberOfKarts() - 2; + int ICON_PLAYER_WIDTH = y_space / num_karts - 2; int icon_width_max = (int)(50*(irr_driver->getActualScreenSize().Width/800.0f)); int icon_width_min = (int)(35*(irr_driver->getActualScreenSize().Height/600.0f)); @@ -664,10 +667,11 @@ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin) int ICON_WIDTH = ICON_PLAYER_WIDTH * 4 / 5; WorldWithRank *world = (WorldWithRank*)(World::getWorld()); + //initialize m_previous_icons_position if(m_previous_icons_position.size()==0) { - for(unsigned int i=0; igetNumberOfKarts(); i++) + for(unsigned int i=0; igetKart(i); int position = kart->getPosition(); @@ -686,7 +690,7 @@ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin) int previous_y=y_base-ICON_PLAYER_WIDTH-2; gui::ScalableFont* font = GUIEngine::getFont(); - const unsigned int kart_amount = world->getNumKarts(); + const unsigned int kart_amount = world->getNumKarts() - sta; //where is the limit to hide last icons int y_icons_limit=irr_driver->getActualScreenSize().Height-bottom_margin-ICON_PLAYER_WIDTH; diff --git a/src/states_screens/race_result_gui.cpp b/src/states_screens/race_result_gui.cpp index 714f2987c..109c695f1 100644 --- a/src/states_screens/race_result_gui.cpp +++ b/src/states_screens/race_result_gui.cpp @@ -460,17 +460,17 @@ void RaceResultGUI::backToLobby() WorldWithRank *rank_world = (WorldWithRank*)World::getWorld(); unsigned int first_position = 1; + unsigned int sta = race_manager->getNumSpareTireKarts(); if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER) first_position = 2; // Use only the karts that are supposed to be displayed (and // ignore e.g. the leader in a FTL race). - unsigned int num_karts = race_manager->getNumberOfKarts() - first_position + 1; + unsigned int num_karts = race_manager->getNumberOfKarts() - first_position + 1 - sta; // In FTL races the leader kart is not displayed m_all_row_infos.resize(num_karts); - // Determine the kart to display in the right order, // and the maximum width for the kart name column // ------------------------------------------------- @@ -478,7 +478,7 @@ void RaceResultGUI::backToLobby() float max_finish_time = 0; for (unsigned int position = first_position; - position <= race_manager->getNumberOfKarts(); position++) + position <= race_manager->getNumberOfKarts() - sta; position++) { const AbstractKart *kart = rank_world->getKartAtPosition(position); diff --git a/src/tracks/arena_graph.cpp b/src/tracks/arena_graph.cpp index 53f3abd4a..d888e4c47 100644 --- a/src/tracks/arena_graph.cpp +++ b/src/tracks/arena_graph.cpp @@ -44,6 +44,8 @@ ArenaGraph::ArenaGraph(const std::string &navmesh, const XMLNode *node) if (node && race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER) loadGoalNodes(node); + loadBoundingBoxNodes(); + } // ArenaGraph // ----------------------------------------------------------------------------- diff --git a/src/tracks/drive_graph.cpp b/src/tracks/drive_graph.cpp index 2ee4259e4..e73b730fb 100644 --- a/src/tracks/drive_graph.cpp +++ b/src/tracks/drive_graph.cpp @@ -240,6 +240,8 @@ void DriveGraph::load(const std::string &quad_file_name, m_lap_length = l; } + loadBoundingBoxNodes(); + } // load // ---------------------------------------------------------------------------- diff --git a/src/tracks/graph.cpp b/src/tracks/graph.cpp index 7fd7610e8..4359f559e 100644 --- a/src/tracks/graph.cpp +++ b/src/tracks/graph.cpp @@ -46,6 +46,7 @@ Graph::Graph() m_new_rtt = NULL; m_bb_min = Vec3( 99999, 99999, 99999); m_bb_max = Vec3(-99999, -99999, -99999); + memset(m_bb_nodes, 0, 4 * sizeof(int)); } // Graph // ----------------------------------------------------------------------------- @@ -621,3 +622,16 @@ int Graph::findOutOfRoadSector(const Vec3& xyz, const int curr_sector, } return min_sector; } // findOutOfRoadSector + +//----------------------------------------------------------------------------- +void Graph::loadBoundingBoxNodes() +{ + m_bb_nodes[0] = findOutOfRoadSector(Vec3(m_bb_min.x(), 0, m_bb_min.z()), + -1/*curr_sector*/, NULL/*all_sectors*/, true/*ignore_vertical*/); + m_bb_nodes[1] = findOutOfRoadSector(Vec3(m_bb_min.x(), 0, m_bb_max.z()), + -1/*curr_sector*/, NULL/*all_sectors*/, true/*ignore_vertical*/); + m_bb_nodes[2] = findOutOfRoadSector(Vec3(m_bb_max.x(), 0, m_bb_min.z()), + -1/*curr_sector*/, NULL/*all_sectors*/, true/*ignore_vertical*/); + m_bb_nodes[3] = findOutOfRoadSector(Vec3(m_bb_max.x(), 0, m_bb_max.z()), + -1/*curr_sector*/, NULL/*all_sectors*/, true/*ignore_vertical*/); +} // loadBoundingBoxNodes diff --git a/src/tracks/graph.hpp b/src/tracks/graph.hpp index d132be82b..65baaed15 100644 --- a/src/tracks/graph.hpp +++ b/src/tracks/graph.hpp @@ -62,12 +62,18 @@ protected: void createQuad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, unsigned int node_index, bool invisible, bool ai_ignore, bool is_arena, bool ignore); + // ------------------------------------------------------------------------ + /** Map 4 bounding box points to 4 closest graph nodes. */ + void loadBoundingBoxNodes(); private: /** The 2d bounding box, used for hashing. */ Vec3 m_bb_min; Vec3 m_bb_max; + /** The 4 closest graph nodes to the bounding box. */ + int m_bb_nodes[4]; + RTT* m_new_rtt; /** The node of the graph mesh. */ @@ -149,6 +155,12 @@ public: const int curr_sector = UNKNOWN_SECTOR, std::vector *all_sectors = NULL, bool ignore_vertical = false) const; + // ------------------------------------------------------------------------ + const Vec3& getBBMin() const { return m_bb_min; } + // ------------------------------------------------------------------------ + const Vec3& getBBMax() const { return m_bb_max; } + // ------------------------------------------------------------------------ + const int* getBBNodes() const { return m_bb_nodes; } }; // Graph diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 39ce3d986..66ea1f310 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -702,11 +702,7 @@ btQuaternion Track::getArenaStartRotation(const Vec3& xyz, float heading) } const Vec3& normal = Graph::get()->getQuad(node)->getNormal(); - Vec3 axis = -normal.cross(Vec3(0, 1, 0)); - if (axis.length() == 0) - axis = Vec3(0, 0, 1); - - btQuaternion q(axis, normal.angle(Vec3(0, 1, 0))); + btQuaternion q = createRotationFromNormal(normal); btMatrix3x3 m; m.setRotation(q); return btQuaternion(m.getColumn(1), heading * DEGREE_TO_RAD) * q; diff --git a/src/tracks/track.hpp b/src/tracks/track.hpp index 03e4972f4..8f9bcd05a 100644 --- a/src/tracks/track.hpp +++ b/src/tracks/track.hpp @@ -397,6 +397,14 @@ public: static const float NOHIT; + static btQuaternion createRotationFromNormal(const Vec3& normal) + { + Vec3 axis = -normal.cross(Vec3(0, 1, 0)); + if (axis.length() == 0) + axis = Vec3(0, 0, 1); + return btQuaternion(axis, normal.angle(Vec3(0, 1, 0))); + } // createRotationFromNormal + Track (const std::string &filename); ~Track (); void cleanup (); diff --git a/src/utils/debug.cpp b/src/utils/debug.cpp index 7cd03a8c8..a83fbb161 100644 --- a/src/utils/debug.cpp +++ b/src/utils/debug.cpp @@ -182,12 +182,9 @@ void changeCameraTarget(u32 num) { AbstractKart* kart = world->getKart(num - 1); if (kart == NULL) return; - if (kart->isEliminated()) return; cam->setMode(Camera::CM_NORMAL); cam->setKart(kart); } - else - return; } // changeCameraTarget