/** Shows the current soccer result. */ void RaceGUI::drawScores() { SoccerWorld* soccerWorld = (SoccerWorld*)World::getWorld(); int offsetY = 5; int offsetX = 5; gui::ScalableFont* font = GUIEngine::getFont(); static video::SColor color = video::SColor(255,255,255,255); //Draw kart icons above score(denoting teams) irr::video::ITexture *red_team = irr_driver->getTexture(FileManager::GUI, "soccer_ball_red.png"); irr::video::ITexture *blue_team = irr_driver->getTexture(FileManager::GUI, "soccer_ball_blue.png"); irr::video::ITexture *team_icon; int numLeader = 1; for(unsigned int i=0; i<soccerWorld->getNumKarts(); i++) { int j = soccerWorld->getTeamLeader(i); if(j < 0) break; AbstractKart* kart = soccerWorld->getKart(i); video::ITexture* icon = kart->getKartProperties()->getMinimapIcon(); core::rect<s32> source(core::position2di(0, 0), icon->getSize()); core::recti position(offsetX, offsetY, offsetX + 2*m_minimap_player_size, offsetY + 2*m_minimap_player_size); draw2DImage(icon, position, source, NULL, NULL, true); core::stringw score = StringUtils::toWString(soccerWorld->getScore(i)); int string_height = GUIEngine::getFont()->getDimension(score.c_str()).Height; core::recti pos(position.UpperLeftCorner.X + 5, position.LowerRightCorner.Y + offsetY, position.LowerRightCorner.X, position.LowerRightCorner.Y + string_height); font->draw(score.c_str(),pos,color); switch(numLeader) { case 1: team_icon = red_team; break; case 2: team_icon = blue_team; break; default: break; } core::rect<s32> indicatorPos(offsetX, offsetY, offsetX + (int)(m_minimap_player_size/1.25f), offsetY + (int)(m_minimap_player_size/1.25f)); core::rect<s32> sourceRect(core::position2d<s32>(0,0), team_icon->getOriginalSize()); draw2DImage(team_icon,indicatorPos,sourceRect, NULL,NULL,true); numLeader++; offsetX += position.LowerRightCorner.X; } } // drawScores
/** Called when the check line is triggered. This function creates a cannon * animation object and attaches it to the kart. * \param kart_index The index of the kart that triggered the check line. */ void CheckGoal::trigger(unsigned int kart_index) { SoccerWorld* world = dynamic_cast<SoccerWorld*>(World::getWorld()); if(!world) { fprintf(stderr, "WARNING: no soccer world found, cannot count the points\n"); return; } world->onCheckGoalTriggered(m_first_goal); } // CheckGoal
/** Called when the check line is triggered. This function creates a cannon * animation object and attaches it to the kart. * \param kart_index The index of the kart that triggered the check line. */ void CheckGoal::trigger(unsigned int kart_index) { SoccerWorld* world = dynamic_cast<SoccerWorld*>(World::getWorld()); if(!world) { Log::warn("CheckGoal", "No soccer world found, cannot count the points."); return; } world->onCheckGoalTriggered(m_first_goal); } // trigger
/** Shows the current soccer result. */ void RaceGUI::drawScores() { #ifndef SERVER_ONLY SoccerWorld* sw = dynamic_cast<SoccerWorld*>(World::getWorld()); int offset_y = 5; int offset_x = 5; gui::ScalableFont* font = GUIEngine::getTitleFont(); static video::SColor color = video::SColor(255,255,255,255); //Draw two teams score irr::video::ITexture *red_team = irr_driver->getTexture(FileManager::GUI, "soccer_ball_red.png"); irr::video::ITexture *blue_team = irr_driver->getTexture(FileManager::GUI, "soccer_ball_blue.png"); irr::video::ITexture *team_icon = red_team; for(unsigned int i=0; i<2; i++) { core::recti position(offset_x, offset_y, offset_x + 2*m_minimap_player_size, offset_y + 2*m_minimap_player_size); core::stringw score = StringUtils::toWString(sw->getScore((SoccerTeam)i)); int string_height = GUIEngine::getFont()->getDimension(score.c_str()).Height; core::recti pos(position.UpperLeftCorner.X + 5, position.LowerRightCorner.Y + offset_y, position.LowerRightCorner.X, position.LowerRightCorner.Y + string_height); font->draw(score.c_str(),pos,color); if (i == 1) { team_icon = blue_team; } core::rect<s32> indicator_pos(offset_x, offset_y, offset_x + (int)(m_minimap_player_size*2), offset_y + (int)(m_minimap_player_size*2)); core::rect<s32> source_rect(core::position2d<s32>(0,0), team_icon->getSize()); draw2DImage(team_icon,indicator_pos,source_rect, NULL,NULL,true); offset_x += position.LowerRightCorner.X + 30; } #endif } // drawScores
// ---------------------------------------------------------------------------- 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
/** 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) ); continue; } // if kart-kart collision if(p->getUserPointer(0)->is(UserPointer::UP_PHYSICAL_OBJECT)) { // Kart hits physical object // ------------------------- PhysicalObject *obj = p->getUserPointer(0) ->getPointerPhysicalObject(); if(obj->isCrashReset()) { AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); new RescueAnimation(kart); } else if (obj->isExplodeKartObject()) { AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); ExplosionAnimation::create(kart); } else if (obj->isFlattenKartObject()) { AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); const KartProperties* kp = kart->getKartProperties(); kart->setSquash(kp->getSquashDuration(), kp->getSquashSlowdown()); } else if(obj->isSoccerBall()) { int kartId = p->getUserPointer(1)->getPointerKart()->getWorldKartId(); 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(), kp->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 // ------------------------------- p->getUserPointer(0)->getPointerFlyable() ->hit(NULL, p->getUserPointer(1)->getPointerPhysicalObject()); PhysicalObject* obj = p->getUserPointer(1)->getPointerPhysicalObject(); if(obj->isSoccerBall()) { 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()) { p->getUserPointer(0)->getPointerFlyable()->hit(target_kart); if ( type ==PowerupManager::POWERUP_BOWLING ) ((SingleAchievement *) AchievementsManager::get()->getActive()->getAchievement(2))->increase(1); } } 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
/** This function is called after instanciating. The code here can't be moved * to the contructor as child classes must be instanciated, otherwise * polymorphism will fail and the results will be incorrect . Also in init() * functions can be called that use World::getWorld(). */ void World::init() { m_faster_music_active = false; m_fastest_kart = 0; m_eliminated_karts = 0; m_eliminated_players = 0; m_num_players = 0; unsigned int gk = 0; if (race_manager->hasGhostKarts()) gk = ReplayPlay::get()->getNumGhostKart(); // Create the race gui before anything else is attached to the scene node // (which happens when the track is loaded). This allows the race gui to // do any rendering on texture. Note that this function can NOT be called // in the World constuctor, since it might be overwritten by a the game // mode class, which would not have been constructed at the time that this // constructor is called, so the wrong race gui would be created. createRaceGUI(); RewindManager::create(); // Grab the track file Track *track = track_manager->getTrack(race_manager->getTrackName()); Scripting::ScriptEngine::getInstance<Scripting::ScriptEngine>(); if(!track) { std::ostringstream msg; msg << "Track '" << race_manager->getTrackName() << "' not found.\n"; throw std::runtime_error(msg.str()); } std::string script_path = track->getTrackFile("scripting.as"); Scripting::ScriptEngine::getInstance()->loadScript(script_path, true); // Create the physics Physics::getInstance<Physics>(); unsigned int num_karts = race_manager->getNumberOfKarts(); //assert(num_karts > 0); // Load the track models - this must be done before the karts so that the // karts can be positioned properly on (and not in) the tracks. // This also defines the static Track::getCurrentTrack function. track->loadTrackModel(race_manager->getReverseTrack()); if (gk > 0) { ReplayPlay::get()->load(); for (unsigned int k = 0; k < gk; k++) m_karts.push_back(ReplayPlay::get()->getGhostKart(k)); } // Assign team of AIs for soccer mode before createKart SoccerWorld* sw = dynamic_cast<SoccerWorld*>(this); if (sw) sw->setAITeam(); for(unsigned int i=0; i<num_karts; i++) { if (race_manager->getKartType(i) == RaceManager::KT_GHOST) continue; std::string kart_ident = history->replayHistory() ? history->getKartIdent(i) : race_manager->getKartIdent(i); int local_player_id = race_manager->getKartLocalPlayerId(i); int global_player_id = race_manager->getKartGlobalPlayerId(i); AbstractKart* newkart = createKart(kart_ident, i, local_player_id, global_player_id, race_manager->getKartType(i), race_manager->getPlayerDifficulty(i)); m_karts.push_back(newkart); track->adjustForFog(newkart->getNode()); } // for i // Load other custom models if needed loadCustomModels(); #ifndef SERVER_ONLY // Now that all models are loaded, apply the overrides irr_driver->applyObjectPassShader(); #endif // Must be called after all karts are created m_race_gui->init(); powerup_manager->updateWeightsForRace(race_manager->getNumberOfKarts()); if (UserConfigParams::m_weather_effects) { Weather::getInstance<Weather>(); // create Weather instance } } // init
/** Draws the mini map and the position of all karts on it. */ void RaceGUI::drawGlobalMiniMap() { #ifndef SERVER_ONLY // draw a map when arena has a navigation mesh. Track *track = Track::getCurrentTrack(); if ( (track->isArena() || track->isSoccer()) && !(track->hasNavMesh()) ) return; int upper_y = irr_driver->getActualScreenSize().Height - m_map_bottom - m_map_height; int lower_y = irr_driver->getActualScreenSize().Height - m_map_bottom; core::rect<s32> dest(m_map_left, upper_y, m_map_left + m_map_width, lower_y); track->drawMiniMap(dest); World *world = World::getWorld(); for(unsigned int i=0; i<world->getNumKarts(); i++) { const AbstractKart *kart = world->getKart(i); const SpareTireAI* sta = dynamic_cast<const SpareTireAI*>(kart->getController()); // don't draw eliminated kart if(kart->isEliminated() && !(sta && sta->isMoving())) continue; const Vec3& xyz = kart->getXYZ(); Vec3 draw_at; track->mapPoint2MiniMap(xyz, &draw_at); draw_at *= UserConfigParams::m_scale_rtts_factor; video::ITexture* icon = sta ? irr_driver->getTexture(FileManager::GUI, "heart.png") : kart->getKartProperties()->getMinimapIcon(); // int marker_height = m_marker->getSize().Height; core::rect<s32> source(core::position2di(0, 0), icon->getSize()); int marker_half_size = (kart->getController()->isLocalPlayerController() ? m_minimap_player_size : m_minimap_ai_size )>>1; core::rect<s32> position(m_map_left+(int)(draw_at.getX()-marker_half_size), lower_y -(int)(draw_at.getY()+marker_half_size), m_map_left+(int)(draw_at.getX()+marker_half_size), lower_y -(int)(draw_at.getY()-marker_half_size)); draw2DImage(icon, position, source, NULL, NULL, true); } // for i<getNumKarts SoccerWorld *sw = dynamic_cast<SoccerWorld*>(World::getWorld()); if (sw) { Vec3 draw_at; track->mapPoint2MiniMap(sw->getBallPosition(), &draw_at); draw_at *= UserConfigParams::m_scale_rtts_factor; video::ITexture* icon = irr_driver->getTexture(FileManager::GUI, "soccer_ball_normal.png"); core::rect<s32> source(core::position2di(0, 0), icon->getSize()); core::rect<s32> position(m_map_left+(int)(draw_at.getX()-(m_minimap_player_size/2.5f)), lower_y -(int)(draw_at.getY()+(m_minimap_player_size/2.5f)), m_map_left+(int)(draw_at.getX()+(m_minimap_player_size/2.5f)), lower_y -(int)(draw_at.getY()-(m_minimap_player_size/2.5f))); draw2DImage(icon, position, source, NULL, NULL, true); } #endif } // drawGlobalMiniMap
/** 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
/** 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
/** Draws the mini map and the position of all karts on it. */ void RaceGUI::drawGlobalMiniMap() { World *world = World::getWorld(); // draw a map when arena has a navigation mesh. if ((world->getTrack()->isArena() || world->getTrack()->isSoccer()) && !(world->getTrack()->hasNavMesh())) return; const video::ITexture *old_rtt_mini_map = world->getTrack()->getOldRttMiniMap(); const FrameBuffer* new_rtt_mini_map = world->getTrack()->getNewRttMiniMap(); int upper_y = irr_driver->getActualScreenSize().Height - m_map_bottom - m_map_height; int lower_y = irr_driver->getActualScreenSize().Height - m_map_bottom; core::rect<s32> dest(m_map_left, upper_y, m_map_left + m_map_width, lower_y); if (old_rtt_mini_map != NULL) { core::rect<s32> source(core::position2di(0, 0), old_rtt_mini_map->getSize()); draw2DImage(old_rtt_mini_map, dest, source, NULL, NULL, true); } else if (new_rtt_mini_map != NULL) { core::rect<s32> source(0, 0, (int)new_rtt_mini_map->getWidth(), (int)new_rtt_mini_map->getHeight()); draw2DImageFromRTT(new_rtt_mini_map->getRTT()[0], new_rtt_mini_map->getWidth(), new_rtt_mini_map->getHeight(), dest, source, NULL, video::SColor(127, 255, 255, 255), true); } for(unsigned int i=0; i<world->getNumKarts(); i++) { const AbstractKart *kart = world->getKart(i); if(kart->isEliminated()) continue; // don't draw eliminated kart 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(); // int marker_height = m_marker->getSize().Height; core::rect<s32> source(core::position2di(0, 0), icon->getSize()); int marker_half_size = (kart->getController()->isLocalPlayerController() ? m_minimap_player_size : m_minimap_ai_size )>>1; core::rect<s32> position(m_map_left+(int)(draw_at.getX()-marker_half_size), lower_y -(int)(draw_at.getY()+marker_half_size), m_map_left+(int)(draw_at.getX()+marker_half_size), lower_y -(int)(draw_at.getY()-marker_half_size)); draw2DImage(icon, position, source, NULL, NULL, true); } // for i<getNumKarts SoccerWorld *sw = dynamic_cast<SoccerWorld*>(World::getWorld()); if (sw) { Vec3 draw_at; world->getTrack()->mapPoint2MiniMap(sw->getBallPosition(), &draw_at); draw_at *= UserConfigParams::m_scale_rtts_factor; video::ITexture* icon = irr_driver->getTexture(FileManager::GUI, "soccer_ball_normal.png"); core::rect<s32> source(core::position2di(0, 0), icon->getSize()); core::rect<s32> position(m_map_left+(int)(draw_at.getX()-(m_minimap_player_size/2.5f)), lower_y -(int)(draw_at.getY()+(m_minimap_player_size/2.5f)), m_map_left+(int)(draw_at.getX()+(m_minimap_player_size/2.5f)), lower_y -(int)(draw_at.getY()-(m_minimap_player_size/2.5f))); draw2DImage(icon, position, source, NULL, NULL, true); } } // drawGlobalMiniMap
//----------------------------------------------------------------------------- void RaceResultGUI::displaySoccerResults() { //Draw win text core::stringw resultText; static video::SColor color = video::SColor(255, 255, 255, 255); gui::IGUIFont* font = GUIEngine::getTitleFont(); int currX = UserConfigParams::m_width/2; RowInfo *ri = &(m_all_row_infos[0]); int currY = (int)ri->m_y_pos; SoccerWorld* soccerWorld = (SoccerWorld*)World::getWorld(); int teamScore[2] = {soccerWorld->getScore(0), soccerWorld->getScore(1)}; GUIEngine::Widget *table_area = getWidget("result-table"); int height = table_area->m_h + table_area->m_y; if(teamScore[0] > teamScore[1]) { resultText = _("Red Team Wins"); } else if(teamScore[1] > teamScore[0]) { resultText = _("Blue Team Wins"); } else { //Cannot really happen now. Only in time limited matches. resultText = _("It's a draw"); } core::rect<s32> pos(currX, currY, currX, currY); font->draw(resultText.c_str(), pos, color, true, true); core::dimension2du rect = m_font->getDimension(resultText.c_str()); //Draw team scores: currY += rect.Height; currX /= 2; irr::video::ITexture* redTeamIcon = irr_driver->getTexture(FileManager::GUI, "soccer_ball_red.png"); irr::video::ITexture* blueTeamIcon = irr_driver->getTexture(FileManager::GUI, "soccer_ball_blue.png"); core::recti sourceRect(core::vector2di(0,0), redTeamIcon->getSize()); core::recti destRect(currX, currY, currX+redTeamIcon->getSize().Width/2, currY+redTeamIcon->getSize().Height/2); draw2DImage(redTeamIcon, destRect,sourceRect, NULL,NULL, true); currX += UserConfigParams::m_width/2 - redTeamIcon->getSize().Width/2; destRect = core::recti(currX, currY, currX+redTeamIcon->getSize().Width/2, currY+redTeamIcon->getSize().Height/2); draw2DImage(blueTeamIcon,destRect,sourceRect, NULL, NULL, true); resultText = StringUtils::toWString(teamScore[1]); rect = m_font->getDimension(resultText.c_str()); currX += redTeamIcon->getSize().Width/4; currY += redTeamIcon->getSize().Height/2 + rect.Height/4; pos = core::rect<s32>(currX, currY, currX, currY); color = video::SColor(255,255,255,255); font->draw(resultText.c_str(), pos, color, true, false); currX -= UserConfigParams::m_width/2 - redTeamIcon->getSize().Width/2; resultText = StringUtils::toWString(teamScore[0]); pos = core::rect<s32>(currX,currY,currX,currY); font->draw(resultText.c_str(), pos, color, true, false); int centerX = UserConfigParams::m_width/2; pos = core::rect<s32>(centerX, currY, centerX, currY); font->draw("-", pos, color, true, false); //Draw goal scorers: //The red scorers: currY += rect.Height/2 + rect.Height/4; font = GUIEngine::getSmallFont(); std::vector<int> scorers = soccerWorld->getScorers(0); std::vector<float> scoreTimes = soccerWorld->getScoreTimes(0); irr::video::ITexture* scorerIcon; int prevY = currY; for(unsigned int i=0; i<scorers.size(); i++) { resultText = soccerWorld->getKart(scorers.at(i))-> getKartProperties()->getName(); resultText.append(" "); resultText.append(StringUtils::timeToString(scoreTimes.at(i)).c_str()); rect = m_font->getDimension(resultText.c_str()); if(height-prevY < ((short)scorers.size()+1)*(short)rect.Height) currY += (height-prevY)/((short)scorers.size()+1); else currY += rect.Height; if(currY > height) break; pos = core::rect<s32>(currX,currY,currX,currY); font->draw(resultText,pos, color, true, false); scorerIcon = soccerWorld->getKart(scorers.at(i)) ->getKartProperties()->getIconMaterial()->getTexture(); sourceRect = core::recti(core::vector2di(0,0), scorerIcon->getSize()); irr::u32 offsetX = GUIEngine::getFont()->getDimension(resultText.c_str()).Width/2; destRect = core::recti(currX-offsetX-30, currY, currX-offsetX, currY+ 30); draw2DImage(scorerIcon, destRect, sourceRect, NULL, NULL, true); } //The blue scorers: currY = prevY; currX += UserConfigParams::m_width/2 - redTeamIcon->getSize().Width/2; scorers = soccerWorld->getScorers(1); scoreTimes = soccerWorld->getScoreTimes(1); for(unsigned int i=0; i<scorers.size(); i++) { resultText = soccerWorld->getKart(scorers.at(i))-> getKartProperties()->getName(); resultText.append(" "); resultText.append(StringUtils::timeToString(scoreTimes.at(i)).c_str()); rect = m_font->getDimension(resultText.c_str()); if(height-prevY < ((short)scorers.size()+1)*(short)rect.Height) currY += (height-prevY)/((short)scorers.size()+1); else currY += rect.Height; if(currY > height) break; pos = core::rect<s32>(currX,currY,currX,currY); font->draw(resultText,pos, color, true, false); scorerIcon = soccerWorld->getKart(scorers.at(i))-> getKartProperties()->getIconMaterial()->getTexture(); sourceRect = core::recti(core::vector2di(0,0), scorerIcon->getSize()); irr::u32 offsetX = GUIEngine::getFont()->getDimension(resultText.c_str()).Width/2; destRect = core::recti(currX-offsetX-30, currY, currX-offsetX, currY+ 30); draw2DImage(scorerIcon, destRect, sourceRect, NULL, NULL, true); } }