/** Updates all track objects. * \param dt Time step size. */ void TrackObjectManager::updateGraphics(float dt) { TrackObject* curr; for_in(curr, m_all_objects) { curr->updateGraphics(dt); }
/** * Checks the soccer balls to see if they crossed the line and trigger the goal accordingly. */ void CheckGoal::update(float dt) { World *world = World::getWorld(); assert(world); Track* track = world->getTrack(); assert(track); TrackObjectManager* tom = track->getTrackObjectManager(); assert(tom); PtrVector<TrackObject>& objects = tom->getObjects(); unsigned int ball_index = 0; for(int i=0; i<objects.size(); i++) { TrackObject* obj = objects.get(i); if(!obj->isSoccerBall()) continue; const Vec3 &xyz = obj->getPresentation<TrackObjectPresentationMesh>()->getNode()->getPosition(); if(isTriggered(m_previous_position[ball_index], xyz, ball_index)) { if(UserConfigParams::m_check_debug) printf("CHECK: Goal check structure %d triggered for object %s.\n", m_index, obj->getPresentation<TrackObjectPresentationMesh>()->getNode()->getDebugName()); trigger(ball_index); } m_previous_position[ball_index] = xyz; ball_index++; } }
/** Initializes the soccer world. It sets up the data structure * to keep track of points etc. for each kart. */ void SoccerWorld::init() { m_kart_team_map.clear(); m_kart_position_map.clear(); WorldWithRank::init(); m_display_rank = false; m_goal_timer = 0.0f; m_ball_hitter = -1; m_ball = NULL; m_ball_body = NULL; m_goal_target = race_manager->getMaxGoal(); m_goal_sound = SFXManager::get()->createSoundSource("goal_scored"); TrackObjectManager* tom = getTrack()->getTrackObjectManager(); assert(tom); PtrVector<TrackObject>& objects = tom->getObjects(); for (unsigned int i = 0; i < objects.size(); i++) { TrackObject* obj = objects.get(i); if(!obj->isSoccerBall()) continue; m_ball = obj; m_ball_body = m_ball->getPhysicalObject()->getBody(); // Handle one ball only break; } if (!m_ball) Log::fatal("SoccerWorld","Ball is missing in soccer field, abort."); m_bgd.init(m_ball->getPhysicalObject()->getRadius()); } // init
/** Initialises all track objects. */ void TrackObjectManager::reset() { TrackObject* curr; for_in (curr, m_all_objects) { curr->reset(); } } // reset
/** General update function called once per frame. * \param dt Time step size. */ void OverWorld::update(float dt) { // Skip annoying waiting without a purpose // Make sure to do all things that would normally happen in the // update() method of the base classes. if (getPhase() < GO_PHASE) { setPhase(RACE_PHASE); // Normally done in WorldStatus::update(), during phase SET_PHASE, // so we have to start music 'manually', since we skip all phases. World::getWorld()->getTrack()->startMusic(); if (music_manager->getCurrentMusic() != NULL && UserConfigParams::m_music) music_manager->getCurrentMusic()->startMusic(); m_karts[0]->startEngineSFX(); } WorldWithRank::update(dt); WorldWithRank::updateTrack(dt); const unsigned int kart_amount = m_karts.size(); // isn't it cool, on the overworld nitro is free! for(unsigned int n=0; n<kart_amount; n++) { m_karts[n]->setEnergy(100.0f); } TrackObjectManager* tom = getTrack()->getTrackObjectManager(); PtrVector<TrackObject>& objects = tom->getObjects(); for(unsigned int i=0; i<objects.size(); i++) { TrackObject* obj = objects.get(i); if(!obj->isGarage()) continue; float m_distance = obj->getDistance(); Vec3 m_garage_pos = obj->getPosition(); Vec3 m_kart_pos = getKart(0)->getXYZ(); if ((m_garage_pos-m_kart_pos).length_2d() > m_distance) { obj->reset(); } } if (m_return_to_garage) { m_return_to_garage = false; delayedSelfDestruct(); race_manager->exitRace(false); KartSelectionScreen* s = OfflineKartSelectionScreen::getInstance(); s->setMultiplayer(false); s->setFromOverworld(true); StateManager::get()->resetAndGoToScreen(s); } } // update
void TrackObjectManager::handleExplosion(const Vec3 &pos, const PhysicalObject *mp, bool secondary_hits) { TrackObject* curr; for_in (curr, m_all_objects) { if(secondary_hits || mp == curr->getPhysicalObject()) curr->handleExplosion(pos, mp == curr->getPhysicalObject()); } } // handleExplosion
/** Initialises all track objects. */ void TrackObjectManager::init() { int moveable_objects = 0; bool warned = false; for (unsigned i = 0; i < m_all_objects.m_contents_vector.size(); i++) { TrackObject* curr = m_all_objects.m_contents_vector[i]; curr->onWorldReady(); if (moveable_objects > stk_config->m_max_moveable_objects) { if (!warned) { Log::warn("TrackObjectManager", "Too many moveable objects (>%d) in networking.", stk_config->m_max_moveable_objects); warned = true; } curr->setInitiallyVisible(false); curr->setEnabled(false); continue; } // onWorldReady will hide some track objects using scripting if (NetworkConfig::get()->isNetworking() && curr->isEnabled() && curr->getPhysicalObject() && curr->getPhysicalObject()->isDynamic()) { curr->getPhysicalObject()->getBody() ->setActivationState(DISABLE_DEACTIVATION); curr->getPhysicalObject()->addForRewind(); moveable_objects++; } } } // init
/** Called when a soccer game is restarted. */ void SoccerWorld::reset() { WorldWithRank::reset(); if(race_manager->hasTimeTarget()) { WorldStatus::setClockMode(WorldStatus::CLOCK_COUNTDOWN, race_manager->getTimeTarget()); m_count_down_reached_zero = false; } else WorldStatus::setClockMode(CLOCK_CHRONO); m_can_score_points = true; m_red_goal = 0; m_blue_goal = 0; m_red_scorers.clear(); m_red_score_times.clear(); m_blue_scorers.clear(); m_blue_score_times.clear(); m_ball_hitter = -1; m_ball = NULL; m_red_defender = -1; m_blue_defender = -1; m_ball_invalid_timer = 0.0f; TrackObjectManager* tom = getTrack()->getTrackObjectManager(); assert(tom); PtrVector<TrackObject>& objects = tom->getObjects(); for (unsigned int i = 0; i < objects.size(); i++) { TrackObject* obj = objects.get(i); if(!obj->isSoccerBall()) continue; m_ball = obj; // Handle one ball only break; } if (!m_ball) Log::fatal("SoccerWorld","Ball is missing in soccer field, abort."); if (m_goal_sound != NULL && m_goal_sound->getStatus() == SFXBase::SFX_PLAYING) { m_goal_sound->stop(); } initKartList(); resetAllPosition(); resetBall(); } // reset
/** Adds an object to the track object manager. The type to add is specified * in the xml_node. * \note If you add add any objects with LOD, don't forget to call * TrackObjectManager::assingLodNodes after everything is loaded * to finalize their creation. * * FIXME: all of this is horrible, just make the exporter write LOD definitions * in a separate section that's read before everything and remove all this * crap */ void TrackObjectManager::add(const XMLNode &xml_node, scene::ISceneNode* parent, ModelDefinitionLoader& model_def_loader) { try { TrackObject *obj = new TrackObject(xml_node, parent, model_def_loader); m_all_objects.push_back(obj); if(obj->isDriveable()) m_driveable_objects.push_back(obj); } catch (std::exception& e) { Log::warn("TrackObjectManager", "Could not load track object. Reason : %s", e.what()); } } // add
/** Called when a battle is restarted. */ void SoccerWorld::reset() { WorldWithRank::reset(); if(race_manager->hasTimeTarget()) { WorldStatus::setClockMode(WorldStatus::CLOCK_COUNTDOWN, race_manager->getTimeTarget()); countDownReachedZero = false; } else WorldStatus::setClockMode(CLOCK_CHRONO); m_can_score_points = true; memset(m_team_goals, 0, sizeof(m_team_goals)); // Reset original positions for the soccer balls TrackObjectManager* tom = getTrack()->getTrackObjectManager(); assert(tom); m_redScorers.clear(); m_redScoreTimes.clear(); m_blueScorers.clear(); m_blueScoreTimes.clear(); m_lastKartToHitBall = -1; PtrVector<TrackObject>& objects = tom->getObjects(); for(unsigned int i=0; i<objects.size(); i++) { TrackObject* obj = objects.get(i); if(!obj->isSoccerBall()) continue; obj->reset(); obj->getPhysicalObject()->reset(); } if (m_goal_sound != NULL && m_goal_sound->getStatus() == SFXBase::SFX_PLAYING) { m_goal_sound->stop(); } initKartList(); } // reset
/** Initialises all track objects. */ void TrackObjectManager::reset() { TrackObject* curr; for_in (curr, m_all_objects) { curr->reset(); if (!curr->isEnabled()) { //PhysicalObjects may need to be added if (curr->getType() == "mesh") { if (curr->getPhysicalObject() != NULL) curr->getPhysicalObject()->addBody(); } } curr->setEnable(true); }
//----------------------------------------------------------------------------- void SoccerWorld::onCheckGoalTriggered(bool first_goal) { if (isRaceOver()) return; if (m_can_score_points) { m_team_goals[first_goal ? 0 : 1]++; World *world = World::getWorld(); world->setPhase(WorldStatus::GOAL_PHASE); m_goal_sound->play(); if(m_lastKartToHitBall != -1) { if(first_goal) { m_redScorers.push_back(m_lastKartToHitBall); if(race_manager->hasTimeTarget()) m_redScoreTimes.push_back(race_manager->getTimeTarget() - world->getTime()); else m_redScoreTimes.push_back(world->getTime()); } else { m_blueScorers.push_back(m_lastKartToHitBall); if(race_manager->hasTimeTarget()) m_blueScoreTimes.push_back(race_manager->getTimeTarget() - world->getTime()); else m_blueScoreTimes.push_back(world->getTime()); } } } // Reset original positions for the soccer balls TrackObjectManager* tom = getTrack()->getTrackObjectManager(); assert(tom); PtrVector<TrackObject>& objects = tom->getObjects(); for(unsigned int i=0; i<objects.size(); i++) { TrackObject* obj = objects.get(i); if(!obj->isSoccerBall()) continue; obj->reset(); obj->getPhysicalObject()->reset(); } //Resetting the ball triggers the goal check line one more time. //This ensures that only one goal is counted, and the second is ignored. m_can_score_points = !m_can_score_points; //for(int i=0 ; i < getNumKarts() ; i++ /*if(World::getWorld()->getTrack()->isAutoRescueEnabled() && !getKartAnimation() && fabs(getRoll())>60*DEGREE_TO_RAD && fabs(getSpeed())<3.0f ) { new RescueAnimation(this, true); }*/ // TODO: rescue the karts } // onCheckGoalTriggered
/** Updates the physics simulation and handles all collisions. * \param ticks Number of physics steps to simulate. */ void Physics::update(int ticks) { 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(); // Since the world update (which calls physics update) is called at the // 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(); m_dynamics_world->stepSimulation(stk_config->ticks2Time(1), 1, stk_config->ticks2Time(1) ); if (UserConfigParams::m_physics_debug) { Log::verbose("Physics", "At %d physics duration %12.8f", World::getWorld()->getTicksSinceStart(), StkTime::getRealTime() - start); } // 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 = Scripting::ScriptEngine::getInstance(); int kartid1 = p->getUserPointer(0)->getPointerKart()->getWorldKartId(); int kartid2 = p->getUserPointer(1)->getPointerKart()->getWorldKartId(); script_engine->runFunction(false, "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 = Scripting::ScriptEngine::getInstance(); 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(); TrackObject* to = obj->getTrackObject(); TrackObject* library = to->getParentLibrary(); std::string lib_id; std::string* lib_id_ptr = NULL; if (library != NULL) lib_id = library->getID(); lib_id_ptr = &lib_id; if (scripting_function.size() > 0) { script_engine->runFunction(true, "void " + scripting_function + "(int, const string, const string)", [&](asIScriptContext* ctx) { ctx->SetArgDWord(0, kartId); ctx->SetArgObject(1, lib_id_ptr); ctx->SetArgObject(2, &obj_id); }); } if (obj->isCrashReset()) { new RescueAnimation(kart); } else if (obj->isExplodeKartObject()) { ExplosionAnimation::create(kart); if (kart->getKartAnimation() != NULL) { World::getWorld()->kartHit(kart->getWorldKartId()); } } else if (obj->isFlattenKartObject()) { const KartProperties *kp = kart->getKartProperties(); // Count squash only once from original state bool was_squashed = kart->isSquashed(); if (kart->setSquash(kp->getSwatterSquashDuration(), kp->getSwatterSquashSlowdown()) && !was_squashed) { World::getWorld()->kartHit(kart->getWorldKartId()); } } else if(obj->isSoccerBall() && race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER) { SoccerWorld* soccerWorld = (SoccerWorld*)World::getWorld(); soccerWorld->setBallHitter(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); if (kart->getKartAnimation() != NULL) { World::getWorld()->kartHit(kart->getWorldKartId()); } } else if (anim->isFlattenKartObject()) { AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); const KartProperties *kp = kart->getKartProperties(); // Count squash only once from original state bool was_squashed = kart->isSquashed(); if (kart->setSquash(kp->getSwatterSquashDuration(), kp->getSwatterSquashSlowdown()) && !was_squashed) { World::getWorld()->kartHit(kart->getWorldKartId()); } } 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 = Scripting::ScriptEngine::getInstance(); 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(true, "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->setBallHitter(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()); LocalPlayerController *lpc = dynamic_cast<LocalPlayerController*>(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 && lpc && lpc->canGetAchievements()) { if (type == PowerupManager::POWERUP_BOWLING) { PlayerManager::increaseAchievement(AchievementsStatus::BOWLING_HIT, 1); if (race_manager->isLinearRaceMode()) PlayerManager::increaseAchievement(AchievementsStatus::BOWLING_HIT_1RACE, 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
/** 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(false, "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(); TrackObject* to = obj->getTrackObject(); TrackObject* library = to->getParentLibrary(); std::string lib_id; std::string* lib_id_ptr = NULL; if (library != NULL) lib_id = library->getID(); lib_id_ptr = &lib_id; if (scripting_function.size() > 0) { script_engine->runFunction(true, "void " + scripting_function + "(int, const string, const string)", [&](asIScriptContext* ctx) { ctx->SetArgDWord(0, kartId); ctx->SetArgObject(1, lib_id_ptr); ctx->SetArgObject(2, &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->getSwatterSquashDuration(), kp->getSwatterSquashSlowdown()); } 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->getSwatterSquashDuration(), kp->getSwatterSquashSlowdown()); } 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(true, "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()); LocalPlayerController *c = dynamic_cast<LocalPlayerController*>(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