stk-code_catmod/src/modes/soccer_world.cpp

963 lines
30 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2006-2015 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "modes/soccer_world.hpp"
#include "main_loop.hpp"
#include "audio/music_manager.hpp"
#include "audio/sfx_base.hpp"
#include "config/user_config.hpp"
#include "io/file_manager.hpp"
#include "graphics/central_settings.hpp"
#include "graphics/irr_driver.hpp"
#include "graphics/render_info.hpp"
#include "karts/kart_model.hpp"
#include "karts/kart_properties.hpp"
#include "karts/kart_rewinder.hpp"
#include "karts/rescue_animation.hpp"
#include "karts/controller/local_player_controller.hpp"
#include "karts/controller/network_player_controller.hpp"
#include "network/network_config.hpp"
#include "network/network_string.hpp"
#include "network/protocols/game_events_protocol.hpp"
#include "network/rewind_manager.hpp"
#include "network/stk_host.hpp"
#include "physics/physics.hpp"
#include "states_screens/race_gui_base.hpp"
#include "tracks/graph.hpp"
#include "tracks/quad.hpp"
#include "tracks/track.hpp"
#include "tracks/track_object_manager.hpp"
#include "tracks/track_sector.hpp"
#include "utils/constants.hpp"
#include <IMeshSceneNode.h>
#include <numeric>
#include <string>
//-----------------------------------------------------------------------------
/** Constructor. Sets up the clock mode etc.
*/
SoccerWorld::SoccerWorld() : WorldWithRank()
{
if (race_manager->hasTimeTarget())
{
WorldStatus::setClockMode(WorldStatus::CLOCK_COUNTDOWN,
race_manager->getTimeTarget());
}
else
{
WorldStatus::setClockMode(CLOCK_CHRONO);
}
m_frame_count = 0;
m_use_highscores = false;
m_red_ai = 0;
m_blue_ai = 0;
m_ball_track_sector = NULL;
} // SoccerWorld
//-----------------------------------------------------------------------------
/** The destructor frees all data structures.
*/
SoccerWorld::~SoccerWorld()
{
m_goal_sound->deleteSFX();
delete m_ball_track_sector;
m_ball_track_sector = NULL;
} // ~SoccerWorld
//-----------------------------------------------------------------------------
/** 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;
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");
Track *track = Track::getCurrentTrack();
if (track->hasNavMesh())
{
// Init track sector for ball if navmesh is found
m_ball_track_sector = new TrackSector();
}
TrackObjectManager* tom = track->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());
m_ball_body->setActivationState(DISABLE_DEACTIVATION);
} // 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());
}
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;
m_goal_transforms.clear();
m_goal_transforms.resize(m_karts.size());
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();
}
m_reset_ball_ticks = -1;
initKartList();
m_ball->reset();
m_bgd.reset();
m_ball->setEnabled(false);
// 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
//-----------------------------------------------------------------------------
void SoccerWorld::onGo()
{
m_ball->setEnabled(true);
m_ball->reset();
WorldWithRank::onGo();
} // onGo
//-----------------------------------------------------------------------------
void SoccerWorld::terminateRace()
{
const unsigned int kart_amount = getNumKarts();
for (unsigned int i = 0; i < kart_amount ; i++)
{
// Soccer mode use goal for race result, and each goal time is
// handled by handlePlayerGoalFromServer already
m_karts[i]->finishedRace(0.0f, true/*from_server*/);
} // i<kart_amount
WorldWithRank::terminateRace();
} // terminateRace
//-----------------------------------------------------------------------------
/** Returns the internal identifier for this race.
*/
const std::string& SoccerWorld::getIdent() const
{
return IDENT_SOCCER;
} // getIdent
//-----------------------------------------------------------------------------
/** Update the world and the track.
* \param ticks Physics time steps - should be 1.
*/
void SoccerWorld::update(int ticks)
{
updateBallPosition(ticks);
updateSectorForKarts();
if (Track::getCurrentTrack()->hasNavMesh() &&
!NetworkConfig::get()->isNetworking())
{
updateAIData();
}
WorldWithRank::update(ticks);
WorldWithRank::updateTrack(ticks);
if (getPhase() == World::GOAL_PHASE)
{
for (unsigned int i = 0; i < m_karts.size(); i++)
{
auto& kart = m_karts[i];
if (kart->isEliminated())
continue;
kart->getBody()->setLinearVelocity(Vec3(0.0f));
kart->getBody()->setAngularVelocity(Vec3(0.0f));
kart->getBody()->proceedToTransform(m_goal_transforms[i]);
kart->setTrans(m_goal_transforms[i]);
}
if (NetworkConfig::get()->isNetworking() &&
NetworkConfig::get()->isClient())
return;
m_goal_timer += ticks;
if (m_goal_timer > stk_config->time2Ticks(3.0f))
{
m_goal_timer = 0;
if (!isRaceOver())
{
// Reset all karts and ball
resetKartsToSelfGoals();
if (UserConfigParams::m_arena_ai_stats)
getKart(8)->flyUp();
}
}
}
if (UserConfigParams::m_arena_ai_stats)
m_frame_count++;
} // update
//-----------------------------------------------------------------------------
void SoccerWorld::onCheckGoalTriggered(bool first_goal)
{
if (isRaceOver() || isStartPhase() ||
(NetworkConfig::get()->isNetworking() &&
NetworkConfig::get()->isClient()))
return;
setPhase(WorldStatus::GOAL_PHASE);
m_goal_sound->play();
m_ball->setEnabled(false);
if (m_ball_hitter != -1)
{
if (UserConfigParams::m_arena_ai_stats)
{
const int elapsed_frame = m_goal_frame.empty() ? 0 :
std::accumulate(m_goal_frame.begin(), m_goal_frame.end(), 0);
m_goal_frame.push_back(m_frame_count - elapsed_frame);
}
ScorerData sd;
sd.m_id = m_ball_hitter;
sd.m_correct_goal = isCorrectGoal(m_ball_hitter, first_goal);
if (sd.m_correct_goal)
{
m_karts[m_ball_hitter]->getKartModel()
->setAnimation(KartModel::AF_WIN_START, true/* play_non_loop*/);
}
else if (!sd.m_correct_goal)
{
m_karts[m_ball_hitter]->getKartModel()
->setAnimation(KartModel::AF_LOSE_START, true/* play_non_loop*/);
}
float score_time = 0.0f;
if (first_goal)
{
// Notice: true first_goal means it's blue goal being shoot,
// so red team can score
m_red_scorers.push_back(sd);
if (race_manager->hasTimeTarget())
{
score_time = race_manager->getTimeTarget() - getTime();
}
else
score_time = getTime();
m_red_score_times.push_back(score_time);
}
else
{
m_blue_scorers.push_back(sd);
if (race_manager->hasTimeTarget())
{
score_time = race_manager->getTimeTarget() - getTime();
}
else
score_time = getTime();
m_blue_score_times.push_back(score_time);
}
if (NetworkConfig::get()->isNetworking() &&
NetworkConfig::get()->isServer())
{
NetworkString p(PROTOCOL_GAME_EVENTS);
p.setSynchronous(true);
p.addUInt8(GameEventsProtocol::GE_PLAYER_GOAL)
.addUInt8((uint8_t)sd.m_id).addUInt8(sd.m_correct_goal)
.addUInt8(first_goal).addFloat(score_time)
.addTime(World::getWorld()->getTicksSinceStart() +
stk_config->time2Ticks(3.0f));
STKHost::get()->sendPacketToAllPeers(&p, true);
}
}
for (unsigned i = 0; i < m_karts.size(); i++)
{
auto& kart = m_karts[i];
if (kart->isEliminated())
continue;
kart->getBody()->setLinearVelocity(Vec3(0.0f));
kart->getBody()->setAngularVelocity(Vec3(0.0f));
m_goal_transforms[i] = kart->getBody()->getWorldTransform();
}
} // onCheckGoalTriggered
//-----------------------------------------------------------------------------
void SoccerWorld::handleResetBallFromServer(const NetworkString& ns)
{
int ticks_now = World::getWorld()->getTicksSinceStart();
int ticks_back_to_own_goal = ns.getTime();
if (ticks_now >= ticks_back_to_own_goal)
{
Log::warn("SoccerWorld", "Server ticks %d is too close to client ticks "
"%d when reset player", ticks_back_to_own_goal, ticks_now);
return;
}
RewindManager::get()->addRewindInfoEventFunction(new
RewindInfoEventFunction(ticks_back_to_own_goal,
[](){}, std::bind(&SoccerWorld::resetKartsToSelfGoals, this)));
} // handleResetBallFromServer
//-----------------------------------------------------------------------------
void SoccerWorld::handlePlayerGoalFromServer(const NetworkString& ns)
{
ScorerData sd;
sd.m_id = ns.getUInt8();
sd.m_correct_goal = ns.getUInt8() == 1;
bool first_goal = ns.getUInt8() == 1;
float score_time = ns.getFloat();
if (sd.m_correct_goal)
{
m_karts[sd.m_id]->getKartModel()
->setAnimation(KartModel::AF_WIN_START, true/* play_non_loop*/);
}
else if (!sd.m_correct_goal)
{
m_karts[sd.m_id]->getKartModel()
->setAnimation(KartModel::AF_LOSE_START, true/* play_non_loop*/);
}
if (first_goal)
{
m_red_scorers.push_back(sd);
m_red_score_times.push_back(score_time);
}
else
{
m_blue_scorers.push_back(sd);
m_blue_score_times.push_back(score_time);
}
int ticks_now = World::getWorld()->getTicksSinceStart();
int ticks_back_to_own_goal = ns.getTime();
if (ticks_now >= ticks_back_to_own_goal)
{
Log::warn("SoccerWorld", "Server ticks %d is too close to client ticks "
"%d when goal", ticks_back_to_own_goal, ticks_now);
return;
}
setPhase(WorldStatus::GOAL_PHASE);
m_goal_sound->play();
m_ball->setEnabled(false);
for (unsigned i = 0; i < m_karts.size(); i++)
{
auto& kart = m_karts[i];
if (kart->isEliminated())
continue;
btTransform transform_now = kart->getBody()->getWorldTransform();
kart->getBody()->setLinearVelocity(Vec3(0.0f));
kart->getBody()->setAngularVelocity(Vec3(0.0f));
kart->getBody()->proceedToTransform(transform_now);
kart->setTrans(transform_now);
m_goal_transforms[i] = transform_now;
}
RewindManager::get()->addRewindInfoEventFunction(new
RewindInfoEventFunction(ticks_back_to_own_goal,
[](){}, std::bind(&SoccerWorld::resetKartsToSelfGoals, this)));
} // handlePlayerGoalFromServer
//-----------------------------------------------------------------------------
void SoccerWorld::resetKartsToSelfGoals()
{
m_ball->setEnabled(true);
m_ball->reset();
m_bgd.resetCheckGoal(Track::getCurrentTrack());
setPhase(WorldStatus::RACE_PHASE);
for (unsigned i = 0; i < m_karts.size(); i++)
{
auto& kart = m_karts[i];
if (kart->isEliminated())
continue;
kart->getBody()->setLinearVelocity(Vec3(0.0f));
kart->getBody()->setAngularVelocity(Vec3(0.0f));
unsigned index = m_kart_position_map.at(kart->getWorldKartId());
btTransform t = Track::getCurrentTrack()->getStartTransform(index);
moveKartTo(kart.get(), t);
}
} // resetKartsToSelfGoals
//-----------------------------------------------------------------------------
/** Sets the last kart that hit the ball, to be able to
* identify the scorer later.
*/
void SoccerWorld::setBallHitter(unsigned int kart_id)
{
m_ball_hitter = kart_id;
} // setBallHitter
//-----------------------------------------------------------------------------
/** The soccer game is over if time up or either team wins.
*/
bool SoccerWorld::isRaceOver()
{
if (m_unfair_team)
return true;
if (race_manager->hasTimeTarget())
{
return m_count_down_reached_zero;
}
// One team scored the target goals ...
else
{
return (getScore(SOCCER_TEAM_BLUE) >= m_goal_target ||
getScore(SOCCER_TEAM_RED) >= m_goal_target);
}
} // isRaceOver
//-----------------------------------------------------------------------------
/** Called when the match time ends.
*/
void SoccerWorld::countdownReachedZero()
{
// Prevent negative time in network soccer when finishing
m_time_ticks = 0;
m_time = 0.0f;
m_count_down_reached_zero = true;
} // countdownReachedZero
//-----------------------------------------------------------------------------
void SoccerWorld::initKartList()
{
#ifndef SERVER_ONLY
const unsigned int kart_amount = (unsigned int)m_karts.size();
//Loading the indicator textures
std::string red_path =
file_manager->getAsset(FileManager::GUI, "soccer_player_red.png");
std::string blue_path =
file_manager->getAsset(FileManager::GUI, "soccer_player_blue.png");
//Assigning indicators
for(unsigned int i = 0; i < kart_amount; i++)
{
scene::ISceneNode *arrow_node = NULL;
KartModel* km = m_karts[i]->getKartModel();
// Color of karts can be changed using shaders if the model supports
if (km->supportColorization() && CVS->isGLSL()) continue;
float arrow_pos_height = km->getHeight() + 0.5f;
SoccerTeam team = getKartTeam(i);
arrow_node = irr_driver->addBillboard(
core::dimension2d<irr::f32>(0.3f,0.3f),
team == SOCCER_TEAM_BLUE ? blue_path : red_path,
m_karts[i]->getNode());
arrow_node->setPosition(core::vector3df(0, arrow_pos_height, 0));
}
#endif
} // initKartList
//-----------------------------------------------------------------------------
bool SoccerWorld::getKartSoccerResult(unsigned int kart_id) const
{
if (m_red_scorers.size() == m_blue_scorers.size()) return true;
bool red_win = m_red_scorers.size() > m_blue_scorers.size();
SoccerTeam team = getKartTeam(kart_id);
if ((red_win && team == SOCCER_TEAM_RED) ||
(!red_win && team == SOCCER_TEAM_BLUE))
return true;
else
return false;
} // getKartSoccerResult
//-----------------------------------------------------------------------------
std::shared_ptr<AbstractKart> SoccerWorld::createKart
(const std::string &kart_ident, int index, int local_player_id,
int global_player_id, RaceManager::KartType kart_type,
PerPlayerDifficulty difficulty)
{
int cur_red = getTeamNum(SOCCER_TEAM_RED);
int cur_blue = getTeamNum(SOCCER_TEAM_BLUE);
int pos_index = 0;
int position = index + 1;
SoccerTeam team = SOCCER_TEAM_BLUE;
if (kart_type == RaceManager::KT_AI)
{
if (index < m_red_ai)
team = SOCCER_TEAM_RED;
else
team = SOCCER_TEAM_BLUE;
m_kart_team_map[index] = team;
}
else if (NetworkConfig::get()->isNetworking())
{
m_kart_team_map[index] = race_manager->getKartInfo(index).getSoccerTeam();
team = race_manager->getKartInfo(index).getSoccerTeam();
}
else
{
int rm_id = index -
(race_manager->getNumberOfKarts() - race_manager->getNumPlayers());
assert(rm_id >= 0);
team = race_manager->getKartInfo(rm_id).getSoccerTeam();
m_kart_team_map[index] = team;
}
core::stringw online_name;
if (global_player_id > -1)
{
online_name = race_manager->getKartInfo(global_player_id)
.getPlayerName();
}
// Notice: In blender, please set 1,3,5,7... for blue starting position;
// 2,4,6,8... for red.
if (team == SOCCER_TEAM_BLUE)
{
pos_index = 1 + 2 * cur_blue;
}
else
{
pos_index = 2 + 2 * cur_red;
}
btTransform init_pos = getStartTransform(pos_index - 1);
m_kart_position_map[index] = (unsigned)(pos_index - 1);
std::shared_ptr<RenderInfo> ri = std::make_shared<RenderInfo>();
ri = (team == SOCCER_TEAM_BLUE ? std::make_shared<RenderInfo>(0.66f) :
std::make_shared<RenderInfo>(1.0f));
std::shared_ptr<AbstractKart> new_kart;
if (RewindManager::get()->isEnabled())
{
auto kr = std::make_shared<KartRewinder>(kart_ident, index, position,
init_pos, difficulty, ri);
kr->rewinderAdd();
new_kart = kr;
}
else
{
new_kart = std::make_shared<Kart>(kart_ident, index, position,
init_pos, difficulty, ri);
}
new_kart->init(race_manager->getKartType(index));
Controller *controller = NULL;
switch(kart_type)
{
case RaceManager::KT_PLAYER:
controller = new LocalPlayerController(new_kart.get(), local_player_id,
difficulty);
m_num_players ++;
break;
case RaceManager::KT_NETWORK_PLAYER:
controller = new NetworkPlayerController(new_kart.get());
if (!online_name.empty())
new_kart->setOnScreenText(online_name.c_str());
m_num_players++;
break;
case RaceManager::KT_AI:
controller = loadAIController(new_kart.get());
break;
case RaceManager::KT_GHOST:
break;
case RaceManager::KT_LEADER:
break;
case RaceManager::KT_SPARE_TIRE:
break;
}
new_kart->setController(controller);
return new_kart;
} // createKart
//-----------------------------------------------------------------------------
/** 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_reset_ball_ticks == -1)
{
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))
{
if (NetworkConfig::get()->isNetworking() &&
NetworkConfig::get()->isServer())
{
// Reset the ball 2 seconds in the future to make sure it's
// after all clients time
m_reset_ball_ticks =
World::getWorld()->getTicksSinceStart() +
stk_config->time2Ticks(2.0f);
NetworkString p(PROTOCOL_GAME_EVENTS);
p.setSynchronous(true);
p.addUInt8(GameEventsProtocol::GE_RESET_BALL)
.addTime(m_reset_ball_ticks);
STKHost::get()->sendPacketToAllPeers(&p, true);
}
else if (!NetworkConfig::get()->isNetworking())
{
m_ball_invalid_timer = 0;
resetKartsToSelfGoals();
if (UserConfigParams::m_arena_ai_stats)
getKart(8)->flyUp();
}
}
}
else
m_ball_invalid_timer = 0;
if (m_reset_ball_ticks == World::getWorld()->getTicksSinceStart())
{
assert(NetworkConfig::get()->isNetworking() &&
NetworkConfig::get()->isServer());
resetKartsToSelfGoals();
m_reset_ball_ticks = -1;
}
}
} // updateBallPosition
//-----------------------------------------------------------------------------
int SoccerWorld::getBallNode() const
{
assert(m_ball_track_sector != NULL);
return m_ball_track_sector->getCurrentGraphNode();
} // getBallNode
//-----------------------------------------------------------------------------
SoccerTeam SoccerWorld::getKartTeam(unsigned int kart_id) const
{
std::map<int, SoccerTeam>::const_iterator n =
m_kart_team_map.find(kart_id);
assert(n != m_kart_team_map.end());
return n->second;
} // getKartTeam
//-----------------------------------------------------------------------------
bool SoccerWorld::isCorrectGoal(unsigned int kart_id, bool first_goal) const
{
SoccerTeam team = getKartTeam(kart_id);
if (first_goal)
{
if (team == SOCCER_TEAM_RED)
return true;
}
else if (!first_goal)
{
if (team == SOCCER_TEAM_BLUE)
return true;
}
return false;
} // isCorrectGoal
//-----------------------------------------------------------------------------
void SoccerWorld::updateAIData()
{
if (isRaceOver()) return;
// Fill the kart distance map
m_red_kdm.clear();
m_blue_kdm.clear();
for (unsigned int i = 0; i < m_karts.size(); ++i)
{
if (UserConfigParams::m_arena_ai_stats &&
m_karts[i]->getController()->isPlayerController())
continue;
if (getKartTeam(m_karts[i]->getWorldKartId()) == SOCCER_TEAM_RED)
{
Vec3 rd = m_karts[i]->getXYZ() - getBallPosition();
m_red_kdm.push_back(KartDistanceMap(i, rd.length_2d()));
}
else
{
Vec3 bd = m_karts[i]->getXYZ() - getBallPosition();
m_blue_kdm.push_back(KartDistanceMap(i, bd.length_2d()));
}
}
// Sort the vectors, so first vector will have the min distance
std::sort(m_red_kdm.begin(), m_red_kdm.end());
std::sort(m_blue_kdm.begin(), m_blue_kdm.end());
// Fill Ball and goals data
m_bgd.updateBallAndGoal(getBallPosition(), getBallHeading());
} // updateAIData
//-----------------------------------------------------------------------------
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
//-----------------------------------------------------------------------------
int SoccerWorld::getTeamNum(SoccerTeam team) const
{
int total = 0;
if (m_kart_team_map.empty()) return total;
for (unsigned int i = 0; i < (unsigned)m_karts.size(); ++i)
{
if (team == getKartTeam(m_karts[i]->getWorldKartId())) total++;
}
return total;
} // getTeamNum
//-----------------------------------------------------------------------------
unsigned int SoccerWorld::getRescuePositionIndex(AbstractKart *kart)
{
int last_valid_node =
getTrackSector(kart->getWorldKartId())->getLastValidGraphNode();
if (last_valid_node >= 0)
return last_valid_node;
Log::warn("SoccerWorld", "Missing last valid node for rescuing");
return 0;
} // getRescuePositionIndex
//-----------------------------------------------------------------------------
btTransform SoccerWorld::getRescueTransform(unsigned int rescue_pos) const
{
const Vec3 &xyz = Graph::get()->getQuad(rescue_pos)->getCenter();
const Vec3 &normal = Graph::get()->getQuad(rescue_pos)->getNormal();
btTransform pos;
pos.setOrigin(xyz);
btQuaternion q1 = shortestArcQuat(Vec3(0.0f, 1.0f, 0.0f), normal);
pos.setRotation(q1);
return pos;
} // getRescueTransform
//-----------------------------------------------------------------------------
void SoccerWorld::enterRaceOverState()
{
WorldWithRank::enterRaceOverState();
if (UserConfigParams::m_arena_ai_stats)
{
Log::verbose("Soccer AI profiling", "Total frames elapsed for a team"
" to win with 30 goals: %d", m_frame_count);
// Goal time statistics
std::sort(m_goal_frame.begin(), m_goal_frame.end());
const int mean = std::accumulate(m_goal_frame.begin(),
m_goal_frame.end(), 0) / (int)m_goal_frame.size();
// Prevent overflow if there is a large frame in vector
double squared_sum = 0;
for (const int &i : m_goal_frame)
squared_sum = squared_sum + (double(i - mean) * double(i - mean));
// Use sample st. deviation (n1) as the profiling can't be run forever
const int stdev = int(sqrt(squared_sum / (m_goal_frame.size() - 1)));
int median = 0;
if (m_goal_frame.size() % 2 == 0)
{
median = (m_goal_frame[m_goal_frame.size() / 2 - 1] +
m_goal_frame[m_goal_frame.size() / 2]) / 2;
}
else
{
median = m_goal_frame[m_goal_frame.size() / 2];
}
Log::verbose("Soccer AI profiling", "Frames elapsed for each goal:"
" min: %d max: %d mean: %d median: %d standard deviation: %d",
m_goal_frame.front(), m_goal_frame.back(), mean, median, stdev);
// Goal calculation
int red_own_goal = 0;
int blue_own_goal = 0;
for (unsigned i = 0; i < m_red_scorers.size(); i++)
{
// Notice: if a team has own goal, the score will end up in the
// opposite team
if (!m_red_scorers[i].m_correct_goal)
blue_own_goal++;
}
for (unsigned i = 0; i < m_blue_scorers.size(); i++)
{
if (!m_blue_scorers[i].m_correct_goal)
red_own_goal++;
}
int red_goal = ((int(m_red_scorers.size()) - blue_own_goal) >= 0 ?
(int)m_red_scorers.size() - blue_own_goal : 0);
int blue_goal = ((int(m_blue_scorers.size()) - red_own_goal) >= 0 ?
(int)m_blue_scorers.size() - red_own_goal : 0);
Log::verbose("Soccer AI profiling", "Red goal: %d, Red own goal: %d,"
"Blue goal: %d, Blue own goal: %d", red_goal, red_own_goal,
blue_goal, blue_own_goal);
if (getScore(SOCCER_TEAM_BLUE) >= m_goal_target)
Log::verbose("Soccer AI profiling", "Blue team wins");
else
Log::verbose("Soccer AI profiling", "Red team wins");
delete this;
main_loop->abort();
}
} // enterRaceOverState
//-----------------------------------------------------------------------------
void SoccerWorld::setAITeam()
{
const int total_player = race_manager->getNumPlayers();
const int total_karts = race_manager->getNumberOfKarts();
// No AI
if ((total_karts - total_player) == 0) return;
int red_player = 0;
int blue_player = 0;
for (int i = 0; i < total_player; i++)
{
SoccerTeam team = race_manager->getKartInfo(i).getSoccerTeam();
// Happen in profiling mode
if (team == SOCCER_TEAM_NONE)
{
race_manager->setKartSoccerTeam(i, SOCCER_TEAM_BLUE);
team = SOCCER_TEAM_BLUE;
continue;
}
team == SOCCER_TEAM_BLUE ? blue_player++ : red_player++;
}
int available_ai = total_karts - red_player - blue_player;
while (available_ai > 0)
{
if ((m_red_ai + red_player) > (m_blue_ai + blue_player))
{
m_blue_ai++;
available_ai--;
}
else if ((m_blue_ai + blue_player) > (m_red_ai + red_player))
{
m_red_ai++;
available_ai--;
}
else if ((m_blue_ai + blue_player) == (m_red_ai + red_player))
{
blue_player > red_player ? m_red_ai++ : m_blue_ai++;
available_ai--;
}
}
Log::debug("SoccerWorld","blue AI: %d red AI: %d", m_blue_ai, m_red_ai);
} // setAITeam