/** Is called when this check structure is triggered. This then can cause * a lap to be counted, animation to be started etc. */ void CheckStructure::trigger(unsigned int kart_index) { World* w = World::getWorld(); LinearWorld* lw = dynamic_cast<LinearWorld*>(w); if (lw != NULL) { lw->getTrackSector(kart_index).setLastTriggeredCheckline(m_index); } switch(m_check_type) { case CT_NEW_LAP : World::getWorld()->newLap(kart_index); if(UserConfigParams::m_check_debug) { printf("CHECK: %s new lap %d triggered\n", World::getWorld()->getKart(kart_index)->getIdent().c_str(), m_index); } changeStatus(m_check_structures_to_change_state, kart_index, CS_ACTIVATE); break; case CT_ACTIVATE: changeStatus(m_check_structures_to_change_state, kart_index, CS_ACTIVATE); break; case CT_TOGGLE: changeStatus(m_check_structures_to_change_state, kart_index, CS_TOGGLE); break; default: break; } // switch m_check_type changeStatus(m_same_group, kart_index, CS_DEACTIVATE); } // trigger
/** Determines the first kart that is still in the race. */ void RubberBall::computeTarget() { LinearWorld *world = dynamic_cast<LinearWorld*>(World::getWorld()); for(unsigned int p = race_manager->getFinishedKarts()+1; p < world->getNumKarts()+1; p++) { m_target = world->getKartAtPosition(p); if(!m_target->isEliminated() && !m_target->hasFinishedRace()) { // If the firing kart itself is the first kart (that is // still driving), prepare to remove the rubber ball if(m_target==m_owner && m_delete_timer < 0) { #ifdef PRINT_BALL_REMOVE_INFO Log::debug("RubberBall", "ball %d removed because owner is target.", m_id); #endif m_delete_timer = m_st_delete_time; } return; } } // for p > num_karts // This means it must be the end-animation phase. Now just // aim at the owner (the ball is unlikely to hit it), and // this will trigger the usage of the delete time in updateAndDelete #ifdef PRINT_BALL_REMOVE_INFO Log::debug("RubberBall" "ball %d removed because no more active target.", m_id); #endif m_delete_timer = m_st_delete_time; m_target = m_owner; } // computeTarget
/** This function is called each timestep, and it collects most of the * statistics for this kart. * \param dt Time step size. */ void KartWithStats::update(int ticks) { Kart::update(ticks); if(getSpeed()>m_top_speed ) m_top_speed = getSpeed(); float dt = stk_config->ticks2Time(ticks); if(getControls().getSkidControl()) m_skidding_time += dt; if(getControls().getBrake() ) m_brake_count ++; LinearWorld *world = dynamic_cast<LinearWorld*>(World::getWorld()); if(world && !world->isOnRoad(getWorldKartId())) m_off_track_count ++; } // update
/** This function is called each timestep, and it collects most of the * statistics for this kart. * \param dt Time step size. */ void KartWithStats::update(float dt) { Kart::update(dt); if(getSpeed()>m_top_speed) m_top_speed = getSpeed(); if(getControls().m_skid) m_skidding_time += dt; if(getControls().m_brake) m_brake_count ++; LinearWorld *world = dynamic_cast<LinearWorld*>(World::getWorld()); if(world && !world->isOnRoad(getWorldKartId())) m_off_track_count ++; } // update
/** Returns true if this (non-GP) challenge is fulfilled. */ bool ChallengeData::isChallengeFulfilled() const { // GP's use the grandPrixFinished() function, // so they can't be fulfilled here. if(m_mode==CM_GRAND_PRIX) return false; // Single races // ------------ World *world = World::getWorld(); std::string track_name = world->getTrack()->getIdent(); int d = race_manager->getDifficulty(); AbstractKart* kart = world->getPlayerKart(0); if (kart->isEliminated() ) return false; if (track_name != m_track_id ) return false; if ((int)world->getNumKarts() < m_num_karts[d] ) return false; if (m_energy[d] > 0 && kart->getEnergy() < m_energy[d] ) return false; if (m_position[d] > 0 && kart->getPosition() > m_position[d]) return false; // Follow the leader // ----------------- if(m_minor==RaceManager::MINOR_MODE_FOLLOW_LEADER) { // All possible conditions were already checked, so: // must have been successful return true; } // Quickrace / Timetrial // --------------------- // FIXME - encapsulate this better, each race mode needs to be able // to specify its own challenges and deal with them LinearWorld* lworld = dynamic_cast<LinearWorld*>(world); if(lworld != NULL) { // wrong number of laps if(lworld->getLapForKart( kart->getWorldKartId() ) != m_num_laps) return false; } // too slow if (m_time[d] > 0.0f && kart->getFinishTime() > m_time[d]) return false; if (m_ai_superpower[d] != RaceManager::SUPERPOWER_NONE && race_manager->getAISuperPower() != m_ai_superpower[d]) { return false; } return true; } // isChallengeFulfilled
// ---------------------------------------------------------------------------- bool GameEventsProtocol::notifyEvent(Event* event) { // Avoid crash in case that we still receive race events when // the race is actually over. if (event->getType() != EVENT_TYPE_MESSAGE || !World::getWorld()) return true; NetworkString &data = event->data(); if (data.size() < 1) // for type { Log::warn("GameEventsProtocol", "Too short message."); return true; } uint8_t type = data.getUInt8(); CaptureTheFlag* ctf = dynamic_cast<CaptureTheFlag*>(World::getWorld()); FreeForAll* ffa = dynamic_cast<FreeForAll*>(World::getWorld()); SoccerWorld* sw = dynamic_cast<SoccerWorld*>(World::getWorld()); LinearWorld* lw = dynamic_cast<LinearWorld*>(World::getWorld()); switch (type) { case GE_KART_FINISHED_RACE: kartFinishedRace(data); break; case GE_RESET_BALL: { if (!sw) throw std::invalid_argument("No soccer world"); sw->handleResetBallFromServer(data); break; } case GE_PLAYER_GOAL: { if (!sw) throw std::invalid_argument("No soccer world"); sw->handlePlayerGoalFromServer(data); break; } case GE_BATTLE_KART_SCORE: { if (!ffa) throw std::invalid_argument("No free-for-all world"); ffa->setKartScoreFromServer(data); break; } case GE_CTF_SCORED: { if (!ctf) throw std::invalid_argument("No CTF world"); uint8_t kart_id = data.getUInt8(); bool red_team_scored = data.getUInt8() == 1; int16_t new_kart_scores = data.getUInt16(); int new_red_scores = data.getUInt8(); int new_blue_scores = data.getUInt8(); ctf->ctfScored(kart_id, red_team_scored, new_kart_scores, new_red_scores, new_blue_scores); break; } case GE_STARTUP_BOOST: { if (NetworkConfig::get()->isServer()) { uint8_t kart_id = data.getUInt8(); if (!event->getPeer()->availableKartID(kart_id)) { Log::warn("GameProtocol", "Wrong kart id %d from %s.", kart_id, event->getPeer()->getAddress().toString().c_str()); return true; } float f = LobbyProtocol::get<ServerLobby>() ->getStartupBoostOrPenaltyForKart( event->getPeer()->getAveragePing(), kart_id); NetworkString *ns = getNetworkString(); ns->setSynchronous(true); ns->addUInt8(GE_STARTUP_BOOST).addUInt8(kart_id).addFloat(f); sendMessageToPeers(ns, true); delete ns; } else { uint8_t kart_id = data.getUInt8(); float boost = data.getFloat(); AbstractKart* k = World::getWorld()->getKart(kart_id); if (boost < 0.0f) { PlayerController* pc = dynamic_cast<PlayerController*>(k->getController()); pc->displayPenaltyWarning(); } else k->setStartupBoost(boost); } break; } case GE_CHECK_LINE: { if (!lw) throw std::invalid_argument("No linear world"); if (NetworkConfig::get()->isClient()) lw->updateCheckLinesClient(data); break; } default: Log::warn("GameEventsProtocol", "Unkown message type."); break; } return true; } // notifyEvent
/** True if going from old_pos to new_pos crosses this checkline. This function * is called from update (of the checkline structure). * \param old_pos Position in previous frame. * \param new_pos Position in current frame. * \param indx Index of the kart, can be used to store kart specific * additional data. */ bool CheckLine::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, unsigned int kart_index) { World* w = World::getWorld(); core::vector2df p=new_pos.toIrrVector2d(); bool sign = m_line.getPointOrientation(p)>=0; bool result; bool previous_sign; if (kart_index == -1) { core::vector2df p = old_pos.toIrrVector2d(); previous_sign = (m_line.getPointOrientation(p) >= 0); } else { previous_sign = m_previous_sign[kart_index]; } // If the sign has changed, i.e. the infinite line was crossed somewhere, // check if the finite line was actually crossed: if (sign != previous_sign && m_line.intersectWith(core::line2df(old_pos.toIrrVector2d(), new_pos.toIrrVector2d()), m_cross_point) ) { // Now check the minimum height: the kart position must be within a // reasonable distance in the Z axis - 'reasonable' for now to be // between -1 and 4 units (negative numbers are unlikely, but help // in case that the kart is 'somewhat' inside of the track, or the // checklines are a bit off in Z direction. result = new_pos.getY()-m_min_height<m_over_min_height && new_pos.getY()-m_min_height>-m_under_min_height; if(UserConfigParams::m_check_debug && !result) { if(World::getWorld()->getNumKarts()>0) Log::info("CheckLine", "Kart %s crosses line, but wrong height " "(%f vs %f).", World::getWorld()->getKart(kart_index)->getIdent().c_str(), new_pos.getY(), m_min_height); else Log::info("CheckLine", "Kart %d crosses line, but wrong height " "(%f vs %f).", kart_index, new_pos.getY(), m_min_height); } } else result = false; if (kart_index != -1) m_previous_sign[kart_index] = sign; if (result && kart_index != -1) { LinearWorld* lw = dynamic_cast<LinearWorld*>(w); if (lw != NULL) lw->setLastTriggeredCheckline(kart_index, m_index); } return result; } // isTriggered