/** Called at the end of a race. Checks if the current times are worth a new * score, if so it notifies the HighscoreManager so the new score is added * and saved. */ void World::updateHighscores(int* best_highscore_rank, int* best_finish_time, std::string* highscore_who, StateManager::ActivePlayer** best_player) { *best_highscore_rank = -1; *best_player = NULL; if(!m_use_highscores) return; // Add times to highscore list. First compute the order of karts, // so that the timing of the fastest kart is added first (otherwise // someone might get into the highscore list, only to be kicked out // again by a faster kart in the same race), which might be confusing // if we ever decide to display a message (e.g. during a race) unsigned int *index = new unsigned int[m_karts.size()]; const unsigned int kart_amount = (unsigned int) m_karts.size(); for (unsigned int i=0; i<kart_amount; i++ ) { index[i] = 999; // first reset the contents of the array } for (unsigned int i=0; i<kart_amount; i++ ) { const int pos = m_karts[i]->getPosition()-1; if(pos < 0 || pos >= (int)kart_amount) continue; // wrong position index[pos] = i; } for (unsigned int pos=0; pos<kart_amount; pos++) { if(index[pos] == 999) { // no kart claimed to be in this position, most likely means // the kart location data is wrong #ifdef DEBUG Log::error("[World]", "Incorrect kart positions:"); for (unsigned int i=0; i<m_karts.size(); i++ ) { Log::error("[World]", "i=%d position %d.",i, m_karts[i]->getPosition()); } #endif continue; } // Only record times for player karts and only if // they finished the race if(!m_karts[index[pos]]->getController()->isPlayerController()) continue; if (!m_karts[index[pos]]->hasFinishedRace()) continue; assert(index[pos] < m_karts.size()); Kart *k = (Kart*)m_karts[index[pos]]; Highscores* highscores = getHighscores(); PlayerController *controller = (PlayerController*)(k->getController()); int highscore_rank = 0; if (controller->getPlayer()->getProfile() != NULL) // if we have the player profile here highscore_rank = highscores->addData(k->getIdent(), controller->getPlayer()->getProfile()->getName(), k->getFinishTime()); if (highscore_rank > 0) { if (*best_highscore_rank == -1 || highscore_rank < *best_highscore_rank) { *best_highscore_rank = highscore_rank; *best_finish_time = (int)(k->getFinishTime()); *best_player = controller->getPlayer(); *highscore_who = k->getIdent(); } highscore_manager->saveHighscores(); } } // next position delete []index; } // updateHighscores
/** Updates the physics simulation and handles all collisions. * \param dt Time step. */ void Physics::update(float dt) { PROFILER_PUSH_CPU_MARKER("Physics", 0, 0, 0); m_physics_loop_active = true; // Bullet can report the same collision more than once (up to 4 // contact points per collision). Additionally, more than one internal // substep might be taken, resulting in potentially even more // duplicates. To handle this, all collisions (i.e. pair of objects) // are stored in a vector, but only one entry per collision pair // of objects. m_all_collisions.clear(); // Maximum of three substeps. This will work for framerate down to // 20 FPS (bullet default frequency is 60 HZ). m_dynamics_world->stepSimulation(dt, 3); // Now handle the actual collision. Note: flyables can not be removed // inside of this loop, since the same flyables might hit more than one // other object. So only a flag is set in the flyables, the actual // clean up is then done later in the projectile manager. std::vector<CollisionPair>::iterator p; for(p=m_all_collisions.begin(); p!=m_all_collisions.end(); ++p) { // Kart-kart collision // -------------------- if(p->getUserPointer(0)->is(UserPointer::UP_KART)) { KartKartCollision(p->getUserPointer(0)->getPointerKart(), p->getContactPointCS(0), p->getUserPointer(1)->getPointerKart(), p->getContactPointCS(1) ); Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); int kartid1 = p->getUserPointer(0)->getPointerKart()->getWorldKartId(); int kartid2 = p->getUserPointer(1)->getPointerKart()->getWorldKartId(); script_engine->runFunction("void onKartKartCollision(int, int)", [=](asIScriptContext* ctx) { ctx->SetArgDWord(0, kartid1); ctx->SetArgDWord(1, kartid2); }); continue; } // if kart-kart collision if(p->getUserPointer(0)->is(UserPointer::UP_PHYSICAL_OBJECT)) { // Kart hits physical object // ------------------------- Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); int kartId = kart->getWorldKartId(); PhysicalObject* obj = p->getUserPointer(0)->getPointerPhysicalObject(); std::string obj_id = obj->getID(); std::string scripting_function = obj->getOnKartCollisionFunction(); if (scripting_function.size() > 0) { script_engine->runFunction("void " + scripting_function + "(int, const string)", [&](asIScriptContext* ctx) { ctx->SetArgDWord(0, kartId); ctx->SetArgObject(1, &obj_id); }); } if (obj->isCrashReset()) { new RescueAnimation(kart); } else if (obj->isExplodeKartObject()) { ExplosionAnimation::create(kart); } else if (obj->isFlattenKartObject()) { const KartProperties* kp = kart->getKartProperties(); kart->setSquash(kp->getSquashDuration() * kart->getPlayerDifficulty()->getSquashDuration(), kp->getSquashSlowdown() * kart->getPlayerDifficulty()->getSquashSlowdown()); } else if(obj->isSoccerBall() && race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER) { SoccerWorld* soccerWorld = (SoccerWorld*)World::getWorld(); soccerWorld->setLastKartTohitBall(kartId); } continue; } if (p->getUserPointer(0)->is(UserPointer::UP_ANIMATION)) { // Kart hits animation ThreeDAnimation *anim=p->getUserPointer(0)->getPointerAnimation(); if(anim->isCrashReset()) { AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); new RescueAnimation(kart); } else if (anim->isExplodeKartObject()) { AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); ExplosionAnimation::create(kart); } else if (anim->isFlattenKartObject()) { AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); const KartProperties* kp = kart->getKartProperties(); kart->setSquash(kp->getSquashDuration() * kart->getPlayerDifficulty()->getSquashDuration(), kp->getSquashSlowdown() * kart->getPlayerDifficulty()->getSquashSlowdown()); } continue; } // now the first object must be a projectile // ========================================= if(p->getUserPointer(1)->is(UserPointer::UP_TRACK)) { // Projectile hits track // --------------------- p->getUserPointer(0)->getPointerFlyable()->hitTrack(); } else if(p->getUserPointer(1)->is(UserPointer::UP_PHYSICAL_OBJECT)) { // Projectile hits physical object // ------------------------------- Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); Flyable* flyable = p->getUserPointer(0)->getPointerFlyable(); PhysicalObject* obj = p->getUserPointer(1)->getPointerPhysicalObject(); std::string obj_id = obj->getID(); std::string scripting_function = obj->getOnItemCollisionFunction(); if (scripting_function.size() > 0) { script_engine->runFunction("void " + scripting_function + "(int, int, const string)", [&](asIScriptContext* ctx) { ctx->SetArgDWord(0, (int)flyable->getType()); ctx->SetArgDWord(1, flyable->getOwnerId()); ctx->SetArgObject(2, &obj_id); }); } flyable->hit(NULL, obj); if (obj->isSoccerBall() && race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER) { int kartId = p->getUserPointer(0)->getPointerFlyable()->getOwnerId(); SoccerWorld* soccerWorld = (SoccerWorld*)World::getWorld(); soccerWorld->setLastKartTohitBall(kartId); } } else if(p->getUserPointer(1)->is(UserPointer::UP_KART)) { // Projectile hits kart // -------------------- // Only explode a bowling ball if the target is // not invulnerable AbstractKart* target_kart = p->getUserPointer(1)->getPointerKart(); PowerupManager::PowerupType type = p->getUserPointer(0)->getPointerFlyable()->getType(); if(type != PowerupManager::POWERUP_BOWLING || !target_kart->isInvulnerable()) { Flyable *f = p->getUserPointer(0)->getPointerFlyable(); f->hit(target_kart); // Check for achievements AbstractKart * kart = World::getWorld()->getKart(f->getOwnerId()); PlayerController *c = dynamic_cast<PlayerController*>(kart->getController()); // Check that it's not a kart hitting itself (this can // happen at the time a flyable is shot - release too close // to the kart, and it's the current player. At this stage // only the current player can get achievements. if (target_kart != kart && c && c->getPlayer()->getConstProfile() == PlayerManager::getCurrentPlayer()) { // Compare the current value of hits with the 'hit' goal value // (otherwise it would be compared with the kart id goal value, // which doesn't exist. PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_ARCH_ENEMY, target_kart->getIdent(), 1, "hit"); if (type == PowerupManager::POWERUP_BOWLING) { PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_STRIKE, "ball", 1); } // is bowling ball } // if target_kart != kart && is a player kart and is current player } } else { // Projectile hits projectile // -------------------------- p->getUserPointer(0)->getPointerFlyable()->hit(NULL); p->getUserPointer(1)->getPointerFlyable()->hit(NULL); } } // for all p in m_all_collisions m_physics_loop_active = false; // Now remove the karts that were removed while the above loop // was active. Now we can safely call removeKart, since the loop // is finished and m_physics_world_active is not set anymore. for(unsigned int i=0; i<m_karts_to_delete.size(); i++) removeKart(m_karts_to_delete[i]); m_karts_to_delete.clear(); PROFILER_POP_CPU_MARKER(); } // update