void Battle::FixColours() { if ( !IsFounderMe() )return; std::vector<wxColour> &palette = GetFixColoursPalette( m_teams_sizes.size() + 1 ); std::vector<int> palette_use( palette.size(), 0 ); wxColour my_col = GetMe().BattleStatus().colour; // Never changes color of founder (me) :-) int my_diff = 0; int my_col_i = GetClosestFixColour( my_col, palette_use,my_diff ); palette_use[my_col_i]++; std::set<int> parsed_teams; for ( user_map_t::size_type i = 0; i < GetNumUsers(); i++ ) { User &user=GetUser(i); if ( &user == &GetMe() ) continue; // skip founder ( yourself ) UserBattleStatus& status = user.BattleStatus(); if ( status.spectator ) continue; if ( parsed_teams.find( status.team ) != parsed_teams.end() ) continue; // skip duplicates parsed_teams.insert( status.team ); wxColour &user_col=status.colour; int user_col_i=GetClosestFixColour(user_col,palette_use, 60); palette_use[user_col_i]++; for ( user_map_t::size_type j = 0; j < GetNumUsers(); j++ ) { User &usr=GetUser(j); if ( usr.BattleStatus().team == status.team ) { ForceColour( usr, palette[user_col_i]); } } } }
UserPosition IBattle::GetFreePosition() { UserPosition ret; LSL::UnitsyncMap map = LoadMap(); for ( int i = 0; i < int(map.info.positions.size()); i++ ) { bool taken = false; for ( unsigned int bi = 0; bi < GetNumUsers(); bi++ ) { User& user = GetUser( bi ); UserBattleStatus& status = user.BattleStatus(); if ( status.spectator ) continue; if ( ( map.info.positions[i].x == status.pos.x ) && ( map.info.positions[i].y == status.pos.y ) ) { taken = true; break; } } if ( !taken ) { ret.x = LSL::Util::Clamp(map.info.positions[i].x, 0, map.info.width); ret.y = LSL::Util::Clamp(map.info.positions[i].y, 0, map.info.height); return ret; } } ret.x = map.info.width / 2; ret.y = map.info.height / 2; return ret; }
void IBattle::OnSelfLeftBattle() { GetMe().BattleStatus().spectator = false; // always reset back yourself to player when rejoining if ( m_timer ) m_timer->Stop(); delete m_timer; m_timer = 0; m_is_self_in = false; for( size_t j = 0; j < GetNumUsers(); ++j ) { User& u = GetUser( j ); if ( u.GetBattleStatus().IsBot() ) { OnUserRemoved( u ); ui().OnUserLeftBattle( *this, u, true ); j--; } } ClearStartRects(); m_teams_sizes.clear(); m_ally_sizes.clear(); m_players_ready = 0; m_players_sync = 0; m_players_ok = 0; usync().UnSetCurrentMod(); //left battle }
int IBattle::GetPlayerNum( const User& user ) const { for (user_map_t::size_type i = 0; i < GetNumUsers(); i++) { if ( &GetUser(i) == &user ) return i; } ASSERT_EXCEPTION(false, _T("The player is not in this game.") ); return -1; }
void Battle::RingNotSyncedAndNotReadyPlayers() { for (user_map_t::size_type i = 0; i < GetNumUsers(); i++) { User& u = GetUser(i); UserBattleStatus& bs = u.BattleStatus(); if ( bs.IsBot() ) continue; if ( ( !bs.sync || !bs.ready ) && !bs.spectator ) m_serv.Ring( u.GetNick() ); } }
void Battle::ForceUnsyncedAndUnreadyToSpectate() { size_t numusers = GetNumUsers(); for ( size_t i = 0; i < numusers; ++i ) { User &user = GetUser(i); UserBattleStatus& bs = user.BattleStatus(); if ( bs.IsBot() ) continue; if ( !bs.spectator && ( !bs.sync || !bs.ready ) ) ForceSpectator( user, true ); } }
void Battle::Leave() { m_serv.LeaveBattle( m_opts.battleid ); m_is_self_in = false; for( size_t j = 0; j < GetNumUsers(); ++j ) { User& u = GetUser( j ); if ( u.GetBattleStatus().IsBot() ) OnUserRemoved( u ); } susynclib().UnSetCurrentMod( ); }
void IBattle::OnUserBattleStatusUpdated( User &user, UserBattleStatus status ) { UserBattleStatus previousstatus = user.BattleStatus(); user.UpdateBattleStatus( status ); unsigned int oldspeccount = m_opts.spectators; m_opts.spectators = 0; m_players_sync = 0; m_players_ready = 0; m_players_ok = 0; m_teams_sizes.clear(); m_ally_sizes.clear(); for ( unsigned int i = 0; i < GetNumUsers(); i++ ) { User& loopuser = GetUser( i ); UserBattleStatus& loopstatus = loopuser.BattleStatus(); if ( loopstatus.spectator ) m_opts.spectators++; if ( !loopstatus.IsBot() ) { if ( !loopstatus.spectator ) { if ( loopstatus.ready && loopstatus.spectator ) m_players_ready++; if ( loopstatus.sync ) m_players_sync++; if ( loopstatus.ready && loopstatus.sync ) m_players_ok++; PlayerJoinedTeam( loopstatus.team ); PlayerJoinedAlly( loopstatus.ally ); } } } if ( oldspeccount != m_opts.spectators ) { if ( IsFounderMe() ) SendHostInfo( HI_Spectators ); } if ( !status.IsBot() ) { if ( ( status.ready && status.sync ) || status.spectator ) { std::map<wxString, time_t>::iterator itor = m_ready_up_map.find( user.GetNick() ); if ( itor != m_ready_up_map.end() ) { m_ready_up_map.erase( itor ); } } if ( ( !status.ready || !status.sync ) && !status.spectator ) { std::map<wxString, time_t>::iterator itor = m_ready_up_map.find( user.GetNick() ); if ( itor == m_ready_up_map.end() ) { m_ready_up_map[user.GetNick()] = time(0); } } } }
void SinglePlayerBattle::RemoveUnfittingBots() { wxArrayString old_ais = usync().GetAIList( m_previous_local_mod_name ); wxArrayString new_ais = usync().GetAIList( m_local_mod.name ); for ( size_t i = 0; i < old_ais.GetCount(); ++i) { if ( new_ais.Index(old_ais[i]) == wxNOT_FOUND ) { for( size_t j = 0; j < GetNumUsers(); ++j ) { User& u = GetUser( j ); if ( u.GetBattleStatus().airawname == old_ais[i] ) KickPlayer( u ); } } } }
void SinglePlayerBattle::RemoveUnfittingBots() { const auto old_ais = LSL::usync().GetAIList(STD_STRING(m_previous_local_mod_name)); const auto new_ais = LSL::usync().GetAIList(m_local_mod.name); LSL::StringVector diff(old_ais.size()); LSL::StringVector::iterator end = std::set_difference(old_ais.begin(), old_ais.end(), new_ais.begin(), new_ais.end(),diff.begin()); for(auto it = diff.begin(); it != end; ++it) { for( size_t j = 0; j < GetNumUsers(); ++j ) { User& u = GetUser( j ); if (u.GetBattleStatus().airawname == TowxString(*it)) KickPlayer( u ); } } }
void Battle::SetInGame( bool value ) { time_t now = time(0); if ( m_ingame && !value ) { for ( int i = 0; i < long(GetNumUsers()); i++ ) { User& user = GetUser( i ); UserBattleStatus& status = user.BattleStatus(); if ( status.IsBot() || status.spectator ) continue; if ( status.ready && status.sync ) continue; m_ready_up_map[user.GetNick()] = now; } } IBattle::SetInGame( value ); }
int IBattle::GetFreeAlly( bool excludeme ) const { int lowest = 0; bool changed = true; while ( changed ) { changed = false; for ( unsigned int i = 0; i < GetNumUsers(); i++ ) { User& user = GetUser( i ); if ( ( &user == &GetMe() ) && excludeme ) continue; if ( user.BattleStatus().spectator ) continue; if ( user.BattleStatus().ally == lowest ) { lowest++; changed = true; } } } return lowest; }
int IBattle::GetFreeTeam( bool excludeme ) const { int lowest = 0; bool changed = true; while ( changed ) { changed = false; for ( user_map_t::size_type i = 0; i < GetNumUsers(); i++ ) { User& user = GetUser( i ); if ( ( &user == &GetMe() ) && excludeme ) continue; if ( user.BattleStatus().spectator ) continue; if ( user.BattleStatus().team == lowest ) { lowest++; changed = true; } } } return lowest; }
void UserManager::ResetMaster( CFrobDoor *frobDoor ) { int numUsers = GetNumUsers(); if( numUsers > 1 ) { // grayman #3755 - the first pass is to establish order based on proximity to the door idVec3 doorCenter = frobDoor->GetClosedBox().GetCenter(); // grayman #3643 - use center of door, not origin //idVec3 doorOrigin = frobDoor->GetPhysics()->GetOrigin(); idActor *closestUser = NULL; // the user closest to the door int masterIndex = 0; // index of closest user float minDistance = 100000; // minimum distance of all users for( int i = 0 ; i < numUsers ; i++ ) { idActor *user = frobDoor->GetUserManager().GetUserAtIndex( i ); if( user != NULL ) { float distance = ( user->GetPhysics()->GetOrigin() - doorCenter ).LengthFast(); if( distance < minDistance ) { masterIndex = i; closestUser = user; minDistance = distance; } } } if( masterIndex > 0 ) { // only rearrange the queue if someone other than the current master is closer RemoveUser( closestUser ); // remove AI from current spot InsertUserAtIndex( closestUser, 0 ); // and put him at the top } // grayman #3755 - the second pass is to establish order based on who's running. idActor *runner = NULL; // the user running to the door masterIndex = 0; // index of running user for( int i = 0 ; i < numUsers ; i++ ) { idActor *user = frobDoor->GetUserManager().GetUserAtIndex( i ); if( user != NULL ) { if( static_cast<idAI *>( user )->AI_RUN ) { masterIndex = i; runner = user; break; } } } if( masterIndex > 0 ) { // only rearrange the queue if someone other than the current master is running RemoveUser( runner ); // remove AI from current spot InsertUserAtIndex( runner, 0 ); // and put him at the top } } }
wxColour IBattle::GetFreeColour( User * ) const { typedef std::vector<wxColour> ColorVec; ColorVec current_used_colors; for ( user_map_t::size_type i = 0; i < GetNumUsers(); ++i ) { UserBattleStatus& bs = GetUser( i ).BattleStatus(); current_used_colors.push_back( bs.colour ); } int inc = 1; while ( true ) { ColorVec fixcolourspalette = GetFixColoursPalette( m_teams_sizes.size() + inc++ ); ColorVec::iterator fixcolourspalette_new_end = std::unique( fixcolourspalette.begin(), fixcolourspalette.end(), AreColoursSimilarProxy( 20 ) ); fixcolourspalette_new_end = std::remove_if( fixcolourspalette.begin(), fixcolourspalette.end(), DismissColor( current_used_colors ) ); if ( fixcolourspalette_new_end != fixcolourspalette.begin() ) return (*fixcolourspalette.begin()); } }
void Battle::OnTimer( wxTimerEvent& ) { if ( !IsFounderMe() ) return; if ( m_ingame ) return; int autospect_trigger_time = sett().GetBattleLastAutoSpectTime(); if ( autospect_trigger_time == 0 ) return; time_t now = time(0); for ( unsigned int i = 0; i < GetNumUsers(); i++) { User& usr = GetUser( i ); UserBattleStatus& status = usr.BattleStatus(); if ( status.IsBot() || status.spectator ) continue; if ( status.sync && status.ready ) continue; if ( &usr == &GetMe() ) continue; std::map<wxString, time_t>::iterator itor = m_ready_up_map.find( usr.GetNick() ); if ( itor != m_ready_up_map.end() ) { if ( ( now - itor->second ) > autospect_trigger_time ) { ForceSpectator( usr, true ); } } } }
idActor *UserManager::GetUserAtIndex( const int index ) { // grayman #2345 if( GetNumUsers() > index ) { return m_users[index].GetEntity(); } return NULL; }
unsigned int IBattle::GetNumPlayers() const { return GetNumUsers() - GetNumBots(); }
void Battle::FixTeamIDs( BalanceType balance_type, bool support_clans, bool strong_clans, int numcontrolteams ) { wxLogMessage(_T("Autobalancing teams, type=%d, clans=%d, strong_clans=%d, numcontrolteams=%d"),balance_type, support_clans, strong_clans, numcontrolteams); //size_t i; //int num_alliances; std::vector<ControlTeam> control_teams; if ( numcontrolteams == 0 || numcontrolteams == -1 ) numcontrolteams = GetNumUsers() - GetSpectators(); // 0 or -1 -> use num players, will use comshare only if no available team slots IBattle::StartType position_type = (IBattle::StartType)s2l( CustomBattleOptions().getSingleValue( _T("startpostype"), OptionsWrapper::EngineOption ) ); if ( ( position_type == ST_Fixed ) || ( position_type == ST_Random ) ) // if fixed start pos type or random, use max teams = start pos count { try { int mapposcount = LoadMap().info.positions.size(); numcontrolteams = std::min( numcontrolteams, mapposcount ); } catch( assert_exception ) {} } if ( numcontrolteams >= (int)( GetNumUsers() - GetSpectators() ) ) // autobalance behaves weird when trying to put one player per team and i CBA to fix it, so i'll reuse the old code :P { // apparently tasserver doesnt like when i fix/force ids of everyone. std::set<int> allteams; size_t numusers = GetNumUsers(); for( size_t i = 0; i < numusers; ++i ) { User &user = GetUser(i); if( !user.BattleStatus().spectator ) allteams.insert( user.BattleStatus().team ); } std::set<int> teams; int t = 0; for( size_t i = 0; i < GetNumUsers(); ++i ) { User &user = GetUser(i); if( !user.BattleStatus().spectator ) { if( teams.count( user.BattleStatus().team ) ) { while( allteams.count(t) || teams.count( t ) ) t++; ForceTeam( GetUser(i), t ); teams.insert( t ); } else { teams.insert( user.BattleStatus().team ); } } } return; } for ( int i = 0; i < numcontrolteams; i++ ) control_teams.push_back( ControlTeam( i ) ); wxLogMessage(_T("number of teams: %u"), control_teams.size() ); std::vector<User*> players_sorted; players_sorted.reserve( GetNumUsers() ); int player_team_counter = 0; for ( size_t i = 0; i < GetNumUsers(); ++i ) // don't count spectators { if ( !GetUser(i).BattleStatus().spectator ) { players_sorted.push_back( &GetUser(i) ); // -- server fail? it doesnt work right. //ForceTeam(GetUser(i),player_team_counter); player_team_counter++; } } shuffle( players_sorted ); std::map<wxString, ControlTeam> clan_teams; if ( support_clans ) { for ( size_t i = 0; i < players_sorted.size(); ++i ) { wxString clan = players_sorted[i]->GetClan(); if ( !clan.empty() ) { clan_teams[clan].AddPlayer( players_sorted[i] ); } } }; if ( balance_type != balance_random ) std::sort( players_sorted.begin(), players_sorted.end(), PlayerRankCompareFunction ); if ( support_clans ) { std::map<wxString, ControlTeam>::iterator clan_it = clan_teams.begin(); while ( clan_it != clan_teams.end() ) { ControlTeam &clan = (*clan_it).second; // if clan is too small (only 1 clan member in battle) or too big, dont count it as clan if ( ( clan.players.size() < 2 ) || ( !strong_clans && ( clan.players.size() > ( ( players_sorted.size() + control_teams.size() -1 ) / control_teams.size() ) ) ) ) { wxLogMessage(_T("removing clan %s"),(*clan_it).first.c_str()); std::map<wxString, ControlTeam>::iterator next = clan_it; ++next; clan_teams.erase( clan_it ); clan_it = next; continue; } wxLogMessage( _T("Inserting clan %s"), (*clan_it).first.c_str() ); std::sort( control_teams.begin(), control_teams.end() ); float lowestrank = control_teams[0].ranksum; int rnd_k = 1; // number of alliances with rank equal to lowestrank while ( size_t( rnd_k ) < control_teams.size() ) { if ( fabs( control_teams[rnd_k].ranksum - lowestrank ) > 0.01 ) break; rnd_k++; } wxLogMessage(_T("number of lowestrank teams with same rank=%d"), rnd_k ); control_teams[my_random( rnd_k )].AddTeam( clan ); ++clan_it; } } for (size_t i = 0; i < players_sorted.size(); ++i ) { // skip clanners, those have been added already. if ( clan_teams.count( players_sorted[i]->GetClan() ) > 0 ) { wxLogMessage( _T("clanner already added, nick=%s"),players_sorted[i]->GetNick().c_str() ); continue; } // find teams with lowest ranksum // insert current user into random one out of them // since performance doesnt matter here, i simply sort teams, // then find how many teams in beginning have lowest ranksum // note that balance player ranks range from 1 to 1.1 now // i.e. them are quasi equal // so we're essentially adding to teams with smallest number of players, // the one with smallest ranksum. std::sort( control_teams.begin(), control_teams.end() ); float lowestrank = control_teams[0].ranksum; int rnd_k = 1; // number of alliances with rank equal to lowestrank while ( size_t( rnd_k ) < control_teams.size() ) { if ( fabs ( control_teams[rnd_k].ranksum - lowestrank ) > 0.01 ) break; rnd_k++; } wxLogMessage( _T("number of lowestrank teams with same rank=%d"), rnd_k ); control_teams[my_random( rnd_k )].AddPlayer( players_sorted[i] ); } for ( size_t i=0; i < control_teams.size(); ++i ) { for ( size_t j = 0; j < control_teams[i].players.size(); ++j ) { ASSERT_LOGIC( control_teams[i].players[j], _T("fail in Autobalance teams, NULL player") ); wxString msg = wxString::Format( _T("setting player %s to team and ally %d"), control_teams[i].players[j]->GetNick().c_str(), i ); wxLogMessage( _T("%s"), msg.c_str() ); ForceTeam( *control_teams[i].players[j], control_teams[i].teamnum ); ForceAlly( *control_teams[i].players[j], control_teams[i].teamnum ); } } }
void Battle::Autobalance( BalanceType balance_type, bool support_clans, bool strong_clans, int numallyteams ) { wxLogMessage(_T("Autobalancing alliances, type=%d, clans=%d, strong_clans=%d, numallyteams=%d"),balance_type, support_clans,strong_clans, numallyteams); //size_t i; //int num_alliances; std::vector<Alliance>alliances; if ( numallyteams == 0 || numallyteams == -1 ) // 0 or 1 -> use num start rects { int ally = 0; for ( unsigned int i = 0; i < GetNumRects(); ++i ) { BattleStartRect sr = GetStartRect(i); if ( sr.IsOk() ) { ally=i; alliances.push_back( Alliance( ally ) ); ally++; } } // make at least two alliances while ( alliances.size() < 2 ) { alliances.push_back( Alliance( ally ) ); ally++; } } else { for ( int i = 0; i < numallyteams; i++ ) alliances.push_back( Alliance( i ) ); } //for(i=0;i<alliances.size();++i)alliances[i].allynum=i; wxLogMessage( _T("number of alliances: %u"), alliances.size() ); std::vector<User*> players_sorted; players_sorted.reserve( GetNumUsers() ); for ( size_t i = 0; i < GetNumUsers(); ++i ) { User& usr = GetUser( i ); if ( !usr.BattleStatus().spectator ) { players_sorted.push_back( &usr ); } } // remove players in the same team so only one remains std::map< int, User*> dedupe_teams; for ( std::vector<User*>::iterator it = players_sorted.begin(); it != players_sorted.end(); it++ ) { dedupe_teams[(*it)->BattleStatus().team] = *it; } players_sorted.clear(); players_sorted.reserve( dedupe_teams.size() ); for ( std::map<int, User*>::iterator it = dedupe_teams.begin(); it != dedupe_teams.end(); it++ ) { players_sorted.push_back( it->second ); } shuffle( players_sorted ); std::map<wxString, Alliance> clan_alliances; if ( support_clans ) { for ( size_t i=0; i < players_sorted.size(); ++i ) { wxString clan = players_sorted[i]->GetClan(); if ( !clan.empty() ) { clan_alliances[clan].AddPlayer( players_sorted[i] ); } } }; if ( balance_type != balance_random ) std::sort( players_sorted.begin(), players_sorted.end(), PlayerRankCompareFunction ); if ( support_clans ) { std::map<wxString, Alliance>::iterator clan_it = clan_alliances.begin(); while ( clan_it != clan_alliances.end() ) { Alliance &clan = (*clan_it).second; // if clan is too small (only 1 clan member in battle) or too big, dont count it as clan if ( ( clan.players.size() < 2 ) || ( !strong_clans && ( clan.players.size() > ( ( players_sorted.size() + alliances.size() -1 ) / alliances.size() ) ) ) ) { wxLogMessage( _T("removing clan %s"), (*clan_it).first.c_str() ); std::map<wxString, Alliance>::iterator next = clan_it; ++next; clan_alliances.erase( clan_it ); clan_it = next; continue; } wxLogMessage( _T("Inserting clan %s"), (*clan_it).first.c_str() ); std::sort( alliances.begin(), alliances.end() ); float lowestrank = alliances[0].ranksum; int rnd_k = 1;// number of alliances with rank equal to lowestrank while ( size_t( rnd_k ) < alliances.size() ) { if ( fabs( alliances[rnd_k].ranksum - lowestrank ) > 0.01 ) break; rnd_k++; } wxLogMessage( _T("number of lowestrank alliances with same rank=%d"), rnd_k ); alliances[my_random( rnd_k )].AddAlliance( clan ); ++clan_it; } } for ( size_t i = 0; i < players_sorted.size(); ++i ) { // skip clanners, those have been added already. if ( clan_alliances.count( players_sorted[i]->GetClan() ) > 0 ) { wxLogMessage( _T("clanner already added, nick=%s"), players_sorted[i]->GetNick().c_str() ); continue; } // find alliances with lowest ranksum // insert current user into random one out of them // since performance doesnt matter here, i simply sort alliances, // then find how many alliances in beginning have lowest ranksum // note that balance player ranks range from 1 to 1.1 now // i.e. them are quasi equal // so we're essentially adding to alliance with smallest number of players, // the one with smallest ranksum. std::sort( alliances.begin(), alliances.end() ); float lowestrank = alliances[0].ranksum; int rnd_k = 1;// number of alliances with rank equal to lowestrank while ( size_t( rnd_k ) < alliances.size() ) { if ( fabs( alliances[rnd_k].ranksum - lowestrank ) > 0.01 ) break; rnd_k++; } wxLogMessage( _T("number of lowestrank alliances with same rank=%d"), rnd_k ); alliances[my_random( rnd_k )].AddPlayer( players_sorted[i] ); } UserList::user_map_t::size_type totalplayers = GetNumUsers(); for ( size_t i = 0; i < alliances.size(); ++i ) { for ( size_t j = 0; j < alliances[i].players.size(); ++j ) { ASSERT_LOGIC( alliances[i].players[j], _T("fail in Autobalance, NULL player") ); int balanceteam = alliances[i].players[j]->BattleStatus().team; wxLogMessage( _T("setting team %d to alliance %d"), balanceteam, i ); for ( size_t h = 0; h < totalplayers; h++ ) // change ally num of all players in the team { User& usr = GetUser( h ); if ( usr.BattleStatus().team == balanceteam ) ForceAlly( usr, alliances[i].allynum ); } } } }
idActor *UserManager::GetMasterUser() { if( GetNumUsers() > 0 ) { return m_users[0].GetEntity(); } return NULL; }