Esempio n. 1
0
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]);
					}
				}
    }
}
Esempio n. 2
0
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;
}
Esempio n. 3
0
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
}
Esempio n. 4
0
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;
}
Esempio n. 5
0
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() );
    }
}
Esempio n. 6
0
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 );
    }
}
Esempio n. 7
0
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( );
}
Esempio n. 8
0
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 );
        }
    }
}
Esempio n. 11
0
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 );
}
Esempio n. 12
0
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;
}
Esempio n. 13
0
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
		}
	}
}
Esempio n. 15
0
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());
	}
}
Esempio n. 16
0
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;
}
Esempio n. 18
0
unsigned int IBattle::GetNumPlayers() const
{
	return GetNumUsers() - GetNumBots();
}
Esempio n. 19
0
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 );
        }
    }
}
Esempio n. 20
0
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;
}