//----------------------------------------------------------------------------- int SoccerWorld::getAttacker(SoccerTeam team) const { if (team == SOCCER_TEAM_BLUE && m_blue_kdm.size() > 1) { for (unsigned int i = 1; i < m_blue_kdm.size(); i++) { // Only AI will do the attack job if (getKart(m_blue_kdm[i].m_kart_id) ->getController()->isPlayerController()) continue; return m_blue_kdm[i].m_kart_id; } } else if (team == SOCCER_TEAM_RED && m_red_kdm.size() > 1) { for (unsigned int i = 1; i < m_red_kdm.size(); i++) { if (getKart(m_red_kdm[i].m_kart_id) ->getController()->isPlayerController()) continue; return m_red_kdm[i].m_kart_id; } } // No attacker return -1; } // getAttacker
/** Localize the ball on the navigation mesh. */ void SoccerWorld::updateBallPosition(int ticks) { if (isRaceOver()) return; if (!ballNotMoving()) { // Only update heading if the ball is moving m_ball_heading = atan2f(m_ball_body->getLinearVelocity().getX(), m_ball_body->getLinearVelocity().getZ()); } if (Track::getCurrentTrack()->hasNavMesh()) { m_ball_track_sector ->update(getBallPosition(), true/*ignore_vertical*/); if (!m_ball_track_sector->isOnRoad() && getPhase() == RACE_PHASE) { m_ball_invalid_timer += ticks; // Reset the ball and karts if out of navmesh after 2 seconds if (m_ball_invalid_timer >= stk_config->time2Ticks(2.0f)) { m_ball_invalid_timer = 0; m_ball->reset(); for (unsigned int i = 0; i < m_karts.size(); i++) moveKartAfterRescue(m_karts[i]); if (UserConfigParams::m_arena_ai_stats) getKart(8)->flyUp(); } } else m_ball_invalid_timer = 0; } } // updateBallPosition
/** Determine the camera settings for the current frame. * \param above_kart How far above the camera should aim at. * \param cam_angle Angle above the kart plane for the camera. * \param sideway Sideway movement of the camera. * \param distance Distance from kart. */ void CameraDebug::getCameraSettings(float *above_kart, float *cam_angle, float *sideway, float *distance ) { const KartProperties *kp = getKart()->getKartProperties(); // Set some default values float steering = m_kart->getSteerPercent() * (1.0f + (m_kart->getSkidding()->getSkidFactor() - 1.0f) / 2.3f); // quadratically to dampen small variations (but keep sign) float dampened_steer = fabsf(steering) * steering; *sideway = -m_rotation_range*dampened_steer*0.5f; *above_kart = 0; *cam_angle = 0; switch(m_default_debug_Type) { case CM_DEBUG_BEHIND_KART: *distance = -0.5f*m_kart->getKartModel()->getLength()-1.0f; break; case CM_DEBUG_GROUND: *distance = -m_kart->getKartModel()->getLength()-1.0f; break; case CM_DEBUG_SIDE_OF_KART: case CM_DEBUG_TOP_OF_KART: *above_kart = 0.75f; *cam_angle = kp->getCameraForwardUpAngle() * DEGREE_TO_RAD; *distance = -m_distance; break; } // switch } // getCameraSettings
/** Remove a kart from the kart manager. * \param id The kart id (i.e. name of the directory) to remove. */ void KartPropertiesManager::removeKart(const std::string &ident) { // Remove the kart properties from the vector of all kart properties int index = getKartId(ident); const KartProperties *kp = getKart(ident); // must be done before remove m_karts_properties.remove(index); m_all_kart_dirs.erase(m_all_kart_dirs.begin()+index); m_kart_available.erase(m_kart_available.begin()+index); // Remove the just removed kart from the 'group-name to kart property // index' mapping. If a group is now empty (i.e. the removed kart was // the only member of this group), remove the group const std::vector<std::string> &groups = kp->getGroups(); for (unsigned int i=0; i<groups.size(); i++) { std::vector<int> ::iterator it; it = std::find(m_groups_2_indices[groups[i]].begin(), m_groups_2_indices[groups[i]].end(), index); // Since we are iterating over all groups the kart belongs to, // there must be an entry found assert(it!=m_groups_2_indices[groups[i]].end()); m_groups_2_indices[groups[i]].erase(it); // Check if the last kart of a group was removed if(m_groups_2_indices[groups[i]].size()==0) { m_groups_2_indices.erase(groups[i]); std::vector<std::string>::iterator its; its = std::find(m_all_groups.begin(), m_all_groups.end(), groups[i]); assert(its!=m_all_groups.end()); m_all_groups.erase(its); } // if m_groups_2_indices[groups[i]].size()==0) } // for i in all groups the kart belongs to // Adjust the indices of all kart properties in the 'group name to // kart property index' mapping: all kart properties with an index // greater than index were moved one position further to the beginning std::map<std::string, std::vector<int> >::iterator it_gr; for(it_gr=m_groups_2_indices.begin(); it_gr != m_groups_2_indices.end(); it_gr++) { for(unsigned int i=0; i<(*it_gr).second.size(); i++) { if( (*it_gr).second[i]>index) (*it_gr).second[i]--; } } delete kp; // Only used for networking and it is safe to just clear it. // If a networking game is started it will be initialised properly. m_selected_karts.clear(); } // removeKart
/** 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 OverWorld::onFirePressed(Controller* who) { const std::vector<OverworldChallenge>& challenges = Track::getCurrentTrack()->getChallengeList(); AbstractKart* k = getKart(0); Vec3 kart_xyz = k->getXYZ(); if (dynamic_cast<RescueAnimation*>(k->getKartAnimation()) != NULL) { // you can't start a race while being rescued return; } for (unsigned int n=0; n<challenges.size(); n++) { if ( (kart_xyz - Vec3(challenges[n].m_position)).length2_2d() < CHALLENGE_DISTANCE_SQUARED) { if (challenges[n].m_challenge_id == "tutorial") { scheduleTutorial(); return; } else { const ChallengeData* challenge = unlock_manager->getChallengeData(challenges[n].m_challenge_id); if (challenge == NULL) { Log::error("track", "Cannot find challenge named '%s'\n", challenges[n].m_challenge_id.c_str()); continue; } const unsigned int val = challenge->getNumTrophies(); bool unlocked = (PlayerManager::getCurrentPlayer()->getPoints() >= val); if (UserConfigParams::m_everything_unlocked) unlocked = true; if (unlocked) { race_manager->setKartLastPositionOnOverworld(kart_xyz); new SelectChallengeDialog(0.8f, 0.8f, challenges[n].m_challenge_id); } } } // end if } // end for } // onFirePressed
/** Called when a soccer game is restarted. */ void SoccerWorld::reset() { WorldWithRank::reset(); if (race_manager->hasTimeTarget()) { WorldStatus::setClockMode(WorldStatus::CLOCK_COUNTDOWN, race_manager->getTimeTarget()); } else { WorldStatus::setClockMode(CLOCK_CHRONO); } m_count_down_reached_zero = false; m_red_scorers.clear(); m_red_score_times.clear(); m_blue_scorers.clear(); m_blue_score_times.clear(); m_ball_hitter = -1; m_red_kdm.clear(); m_blue_kdm.clear(); m_ball_heading = 0.0f; m_ball_invalid_timer = 0; if (m_goal_sound != NULL && m_goal_sound->getStatus() == SFXBase::SFX_PLAYING) { m_goal_sound->stop(); } if (Track::getCurrentTrack()->hasNavMesh()) { m_ball_track_sector->reset(); } initKartList(); m_ball->reset(); m_bgd.reset(); // Make the player kart in profiling mode up // ie make this kart less likely to affect gaming result if (UserConfigParams::m_arena_ai_stats) getKart(8)->flyUp(); } // reset
/** Update the world and the track. * \param dt Time step size. */ void SoccerWorld::update(float dt) { updateBallPosition(dt); if (m_track->hasNavMesh()) { updateKartNodes(); updateAIData(); } WorldWithRank::update(dt); WorldWithRank::updateTrack(dt); if (getPhase() == World::GOAL_PHASE) { if (m_goal_timer == 0.0f) { // Stop all karts for (unsigned int i = 0; i < m_karts.size(); i++) m_karts[i]->setVelocity(btVector3(0, 0, 0)); } m_goal_timer += dt; if (m_goal_timer > 3.0f) { setPhase(WorldStatus::RACE_PHASE); m_goal_timer = 0.0f; if (!isRaceOver()) { // Reset all karts for (unsigned int i = 0; i < m_karts.size(); i++) moveKartAfterRescue(m_karts[i]); if (UserConfigParams::m_arena_ai_stats) getKart(8)->flyUp(); } } } if (UserConfigParams::m_arena_ai_stats) m_frame_count++; } // update
/** Update the world and the track. * \param ticks Physics time steps - should be 1. */ void SoccerWorld::update(int ticks) { updateBallPosition(ticks); if (Track::getCurrentTrack()->hasNavMesh()) { updateSectorForKarts(); updateAIData(); } WorldWithRank::update(ticks); WorldWithRank::updateTrack(ticks); if (getPhase() == World::GOAL_PHASE) { if (m_goal_timer == 0) { // Stop all karts for (unsigned int i = 0; i < m_karts.size(); i++) m_karts[i]->setVelocity(btVector3(0, 0, 0)); } m_goal_timer += ticks; if (m_goal_timer > stk_config->time2Ticks(3.0f)) { setPhase(WorldStatus::RACE_PHASE); m_goal_timer = 0; if (!isRaceOver()) { // Reset all karts for (unsigned int i = 0; i < m_karts.size(); i++) moveKartAfterRescue(m_karts[i]); if (UserConfigParams::m_arena_ai_stats) getKart(8)->flyUp(); } } } if (UserConfigParams::m_arena_ai_stats) m_frame_count++; } // update
unsigned int WorldWithRank::getRescuePositionIndex(AbstractKart *kart) { const int start_spots_amount = getTrack()->getNumberOfStartPositions(); assert(start_spots_amount > 0); float largest_accumulated_distance_found = -1; int furthest_id_found = -1; for(int n=0; n<start_spots_amount; n++) { const btTransform &s = getStartTransform(n); const Vec3 &v=s.getOrigin(); float accumulated_distance = .0f; bool spawn_point_clear = true; for(unsigned int k=0; k<getCurrentNumKarts(); k++) { if(kart->getWorldKartId()==k) continue; float abs_distance2 = (getKart(k)->getXYZ()-v).length2(); const float CLEAR_SPAWN_RANGE2 = 5*5; if( abs_distance2 < CLEAR_SPAWN_RANGE2) { spawn_point_clear = false; break; } accumulated_distance += sqrt(abs_distance2); } if(accumulated_distance > largest_accumulated_distance_found && spawn_point_clear) { furthest_id_found = n; largest_accumulated_distance_found = accumulated_distance; } } assert(furthest_id_found != -1); return furthest_id_found; } // getRescuePositionIndex
/** Called when a mouse click happens. If the click happened while the mouse * was hovering on top of a challenge, the kart will be teleported to * the challenge. * \param x,y Mouse coordinates. */ void OverWorld::onMouseClick(int x, int y) { const OverworldChallenge *challenge = ((RaceGUIOverworld*)getRaceGUI())->getCurrentChallenge(); if(challenge) { // Use the 'get closest start point' rescue function // from WorldWithRank by setting the kart's position to // be the location of the challenge bubble. AbstractKart* kart = getKart(0); kart->setXYZ(challenge->m_position); unsigned int index = getRescuePositionIndex(kart); btTransform s = getRescueTransform(index); const btVector3 &xyz = s.getOrigin(); float angle = atan2(challenge->m_position.X - xyz[0], challenge->m_position.Z - xyz[2]); s.setRotation( btQuaternion(btVector3(0.0f, 1.0f, 0.0f), angle) ); moveKartTo(kart, s); return; } } // onMouseClick
void OverWorld::onFirePressed(Controller* who) { const std::vector<OverworldChallenge>& challenges = m_track->getChallengeList(); AbstractKart* k = getKart(0); Vec3 kart_xyz = k->getXYZ(); if (dynamic_cast<RescueAnimation*>(k->getKartAnimation()) != NULL) { // you can't start a race while being rescued return; } for (unsigned int n=0; n<challenges.size(); n++) { if ( challenges[n].isForceFieldSet() && challenges[n].getForceField().m_is_locked ) continue; if ( (kart_xyz - Vec3(challenges[n].m_position)).length2_2d() < CHALLENGE_DISTANCE_SQUARED) { if (challenges[n].m_challenge_id == "tutorial") { scheduleTutorial(); return; } else { race_manager->setKartLastPositionOnOverworld(kart_xyz); new SelectChallengeDialog(0.8f, 0.8f, challenges[n].m_challenge_id); } } // end if } // end for } // onFirePressed
/** Localize the ball on the navigation mesh. */ void SoccerWorld::updateBallPosition(float dt) { if (isRaceOver()) return; if (!ballNotMoving()) { // Only update heading if the ball is moving m_ball_heading = atan2f(m_ball_body->getLinearVelocity().getX(), m_ball_body->getLinearVelocity().getZ()); } if (m_track->hasNavMesh()) { m_ball_on_node = BattleGraph::get()->pointToNode(m_ball_on_node, getBallPosition(), true/*ignore_vertical*/); if (m_ball_on_node == BattleGraph::UNKNOWN_POLY && getPhase() == RACE_PHASE) { m_ball_invalid_timer += dt; // Reset the ball and karts if out of navmesh after 2 seconds if (m_ball_invalid_timer >= 2.0f) { m_ball_invalid_timer = 0.0f; m_ball->reset(); for (unsigned int i = 0; i < m_karts.size(); i++) moveKartAfterRescue(m_karts[i]); if (UserConfigParams::m_arena_ai_stats) getKart(8)->flyUp(); } } else m_ball_invalid_timer = 0.0f; } } // updateBallPosition
//----------------------------------------------------------------------------- OverWorld::~OverWorld() { Vec3 kart_xyz = getKart(0)->getXYZ(); race_manager->setKartLastPositionOnOverworld(kart_xyz); } // ~OverWorld
/** Called when a kart must be eliminated. */ void FollowTheLeaderRace::countdownReachedZero() { if(m_leader_intervals.size()>1) m_leader_intervals.erase(m_leader_intervals.begin()); WorldStatus::setTime(m_leader_intervals[0]); // If the leader kart is not the first kart, remove the first // kart, otherwise remove the last kart. int position_to_remove = m_karts[0]->getPosition()==1 ? getCurrentNumKarts() : 1; AbstractKart *kart = getKartAtPosition(position_to_remove); if(!kart || kart->isEliminated()) { fprintf(stderr,"Problem with removing leader: position %d not found\n", position_to_remove); for(unsigned int i=0; i<m_karts.size(); i++) { fprintf(stderr,"kart %d: eliminated %d position %d\n", i,m_karts[i]->isEliminated(), m_karts[i]->getPosition()); } // for i } // else { if(UserConfigParams::m_ftl_debug) { printf("[ftl] Eliminiating kart '%s' at position %d.\n", kart->getIdent().c_str(), position_to_remove); } eliminateKart(kart->getWorldKartId()); // In case that the kart on position 1 was removed, we have // to set the correct position (which equals the remaining // number of karts) for the removed kart, as well as recompute // the position for all other karts if(position_to_remove==1) { // We have to add 1 to the number of karts to get the correct // position, since the eliminated kart was already removed // from the value returned by getCurrentNumKarts (and we have // to remove the kart before we can call updateRacePosition). // Note that we can not call WorldWithRank::setKartPosition // here, since it would not properly support debugging kart // ranks (since this kart would get its position set again // in updateRacePosition). We only set the rank of the eliminated // kart, and updateRacePosition will then call setKartPosition // for the now eliminated kart. kart->setPosition(getCurrentNumKarts()+1); updateRacePosition(); } // Time doesn't make any sense in FTL (and it is not displayed) kart->finishedRace(-1.0f); // Move any camera for this kart to the leader, facing backwards, // so that the eliminated player has something to watch. if (race_manager->getNumPlayers() > 1) { for(unsigned int i=0; i<Camera::getNumCameras(); i++) { Camera *camera = Camera::getCamera(i); if(camera->getKart()==kart) { camera->setMode(Camera::CM_LEADER_MODE); camera->setKart(getKart(0)); } } // for i<number of cameras } } // if kart to eliminate exists // almost over, use fast music if(getCurrentNumKarts()==3) { music_manager->switchToFastMusic(); } if (isRaceOver()) { // Handle special FTL situation: the leader is kart number 3 when // the last kart gets eliminated. In this case kart on position 1 // is eliminated, and the kart formerly on position 2 is on // position 1, the leader now position 2. In this case the kart // on position 1 would get more points for this victory. So if // this is the case, change the position if(m_karts[0]->getPosition()!=1) { // Adjust the position of all still driving karts that // are ahead of the leader by +1, and move the leader // to position 1. for (unsigned int i=1; i<m_karts.size(); i++) { if(!m_karts[i]->hasFinishedRace() && !m_karts[i]->isEliminated() && m_karts[i]->getPosition()<m_karts[0]->getPosition()) { m_karts[i]->setPosition(m_karts[i]->getPosition()+1); } } m_karts[0]->setPosition(1); } // Mark all still racing karts to be finished. for (unsigned int n=0; n<m_karts.size(); n++) { if (!m_karts[n]->isEliminated() && !m_karts[n]->hasFinishedRace()) { m_karts[n]->finishedRace(getTime()); } } } // End of race is detected from World::updateWorld() } // countdownReachedZero
/** Called immediately after the constructor. Here functions that use * World::getWorld() as well as overridden functions. */ void FollowTheLeaderRace::init() { LinearWorld::init(); getKart(0)->setOnScreenText(_("Leader")); }