private int ToTeamByDispersal(String name, int fromTeam, List<PlayerModel>[] teamListsById)
{
int targetTeam = 0;
bool allEqual = false;
int grandTotal = 0;
if (teamListsById == null) return 0;
/*
Select a team that would disperse this player evenly with similar players,
regardless of balance or stacking. Dispersal list takes priority over
other dispersal types.
*/
PlayerModel player = GetPlayer(name);
if (player == null) return 0;
PerModeSettings perMode = GetPerModeSettings();
if (perMode.isDefault) return 0;
bool isSQDM = IsSQDM();
bool mostMoves = true;
bool isDispersalByRank = IsRankDispersal(player);
bool isDispersalByList = IsInDispersalList(player, false);
/* DCE */
bool isDispersalByClanPop = false;
if (!isDispersalByList) isDispersalByClanPop = IsClanDispersal(player, false);
/* By Dispersal List */
if (isDispersalByList) {
int[] usualSuspects = new int[5]{0,0,0,0,0};
if (player.DispersalGroup >= 1 && player.DispersalGroup <= 4) {
// Disperse by group
if (!isSQDM && player.DispersalGroup > 2) {
if (DebugLevel >= 7) ConsoleDebug("ToTeamByDispersal ignoring Group " + player.DispersalGroup + " for ^b" + player.FullName + "^n, not SQDM");
// fall thru
} else {
if (perMode.EnableStrictDispersal) return fGroupAssignments[player.DispersalGroup];
// Otherwise, don't allow server to become wildly unbalanced
targetTeam = fGroupAssignments[player.DispersalGroup];
int nTarget = GetTeam(targetTeam).Count;
int nFrom = GetTeam(fromTeam).Count;
// Always ok if target team is smaller than current team
if (nTarget < nFrom) return targetTeam;
// Might be okay if target team is within MaxDiff
if ((nTarget - nFrom) <= MaxDiff()) return targetTeam;
// Target team too big, don't move this player
if (DebugLevel >= 7) ConsoleDebug("ToTeamByDispersal lenient mode, target team " + GetTeamName(targetTeam) + " has too many players " + nTarget + "/" + nFrom + ", skipping dispersal by group of ^b" + player.FullName);
targetTeam = 0;
goto clan;
}
}
// Otherwise normal list dispersal
mostMoves = true;
for (int teamId = 1; teamId < teamListsById.Length; ++teamId) {
foreach (PlayerModel p in teamListsById[teamId]) {
if (p.Name == player.Name) continue; // don't count this player
if (IsInDispersalList(p, true)) {
usualSuspects[teamId] = usualSuspects[teamId] + 1;
grandTotal = grandTotal + 1;
// Make sure this player hasn't been moved more than any other dispersal player
if (GetMovesThisRound(p) >= GetMovesThisRound(player)) {
mostMoves = false;
}
}
}
}
if (mostMoves && GetMovesThisRound(player) > 0) {
ConsoleDebug("^9ToTeamByDispersal List: ^b" + player.Name + "^n moved more than other dispersals (" + GetMovesThisRound(player) + " times), skipping!");
targetTeam = -1;
goto clan;
}
String an = usualSuspects[1] + "/" + usualSuspects[2];
if (isSQDM) an = an + "/" + usualSuspects[3] + "/" + usualSuspects[4];
DebugWrite("^9(DEBUG) ToTeamByDispersal: analysis of ^b" + player.FullName + "^n dispersal by list: " + an, 5);
// Pick smallest one
targetTeam = 0;
allEqual = true;
int minSuspects = 70;
for (int i = 1; i < usualSuspects.Length; ++i) {
if (!isSQDM && i > 2) continue;
if (allEqual && usualSuspects[i] == minSuspects) {
allEqual = true;
} else if (usualSuspects[i] < minSuspects) {
minSuspects = usualSuspects[i];
targetTeam = i;
if (i != 1) allEqual = false;
} else {
if (i != 1) allEqual = false;
}
}
if (grandTotal > 1 && !allEqual && targetTeam != 0 && targetTeam != fromTeam) {
if (perMode.EnableStrictDispersal) return targetTeam;
// Otherwise, don't allow server to become wildly unbalanced
int nTarget = GetTeam(targetTeam).Count;
int nFrom = GetTeam(fromTeam).Count;
// Always ok if target team is smaller than current team
if (nTarget < nFrom) return targetTeam;
// Might be okay if target team is within MaxDiff
if ((nTarget - nFrom) <= MaxDiff()) return targetTeam;
// Target team too big, don't move this player
if (DebugLevel >= 7) ConsoleDebug("ToTeamByDispersal lenient mode, target team " + GetTeamName(targetTeam) + " has too many players " + nTarget + "/" + nFrom + ", skipping dispersal by list of ^b" + player.FullName);
targetTeam = 0;
goto clan;
}
if (allEqual) DebugWrite("^9(DEBUG) ToTeamByDispersal: all equal list, skipping", 5);
// otherwise fall through and try clan
}
clan:
if (isDispersalByClanPop) {
String tag = ExtractTag(player);
int[] pops = new int[5]{0,0,0,0,0};
grandTotal = 0;
mostMoves = false;
int n = GetClanPopulation(player, 1);
pops[1] = n;
grandTotal = grandTotal + n;
n = GetClanPopulation(player, 2);
pops[2] = n;
grandTotal = grandTotal + n;
if (isSQDM) {
n = GetClanPopulation(player, 3);
pops[3] = n;
grandTotal = grandTotal + n;
n = GetClanPopulation(player, 4);
pops[4] = n;
grandTotal = grandTotal + n;
}
if (grandTotal >= perMode.DisperseEvenlyByClanPlayers) {
if (GetMovesThisRound(player) > 0 && player.Team >= 1 && player.Team < teamListsById.Length) {
mostMoves = true;
foreach (PlayerModel p in teamListsById[player.Team]) {
if (p.Name == player.Name) continue; // don't count this player
// Make sure this player hasn't been moved more than any other dispersal player
if (GetMovesThisRound(p) >= GetMovesThisRound(player)) {
mostMoves = false;
break;
}
}
}
if (mostMoves) {
ConsoleDebug("^9ToTeamByDispersal Clan: ^b" + player.FullName + "^n moved more than other dispersals (" + GetMovesThisRound(player) + " times), skipping!");
targetTeam = -1;
goto rank;
}
String a = pops[1] + "/" + pops[2];
if (isSQDM) a = a + "/" + pops[3] + "/" + pops[4];
DebugWrite("^9(DEBUG) ToTeamByDispersal: analysis of ^b" + player.FullName + "^n dispersal of clan population >= " + perMode.DisperseEvenlyByClanPlayers + ": " + grandTotal + " = " + a, 5);
// Pick largest and smallest
targetTeam = 0;
int bigTeam = 0;
allEqual = true;
int minPop = 40;
int maxPop = 0;
for (int i = 1; i < pops.Length; ++i) {
if (!isSQDM && i > 2) continue;
if (allEqual && pops[i] == minPop) {
allEqual = true;
} else if (pops[i] < minPop) {
minPop = pops[i];
targetTeam = i;
if (i != 1) allEqual = false;
} else {
if (i != 1) allEqual = false;
}
if (pops[i] > maxPop) {
maxPop = pops[i];
bigTeam = i;
}
}
if (allEqual) {
DebugWrite("^9(DEBUG) ToTeamByDispersal: all equal by clan population, skipping", 5);
targetTeam = 0; // don't disperse
goto rank;
} else if (Math.Abs(maxPop - minPop) < 2 || targetTeam == bigTeam) {
DebugWrite("^9(DEBUG) ToTeamByDispersal: [" + tag + "] clan populations " + maxPop + "/" + minPop + " balanced or targetTeam same as bigTeam", 5);
targetTeam = 0;
goto rank;
} else {
return targetTeam;
}
}
// fall through
}
/* By Rank? */
rank:
if (isDispersalByRank) {
int[] rankers = new int[5]{0,0,0,0,0};
grandTotal = 0;
mostMoves = true;
for (int i = 1; i < teamListsById.Length; ++i) {
foreach (PlayerModel p in teamListsById[i]) {
if (p.Name == player.Name) continue; // don't count this player
if (p.Rank >= perMode.DisperseEvenlyByRank) {
rankers[i] = rankers[i] + 1;
grandTotal = grandTotal + 1;
// Make sure this player hasn't been moved more than any other dispersal player
if (GetMovesThisRound(p) >= GetMovesThisRound(player)) {
mostMoves = false;
}
}
}
}
if (mostMoves && GetMovesThisRound(player) > 0) {
ConsoleDebug("^9ToTeamByDispersal Rank: ^b" + player.Name + "^n moved more than other dispersals (" + GetMovesThisRound(player) + " times), skipping!");
return -1;
}
String a = rankers[1] + "/" + rankers[2];
if (isSQDM) a = a + "/" + rankers[3] + "/" + rankers[4];
DebugWrite("^9(DEBUG) ToTeamByDispersal: analysis of ^b" + name + "^n dispersal of rank >= " + perMode.DisperseEvenlyByRank + ": " + a, 5);
// Pick smallest one
targetTeam = 0;
allEqual = true;
int minRanks = 70;
for (int i = 1; i < rankers.Length; ++i) {
if (!isSQDM && i > 2) continue;
if (allEqual && rankers[i] == minRanks) {
allEqual = true;
} else if (rankers[i] < minRanks) {
minRanks = rankers[i];
targetTeam = i;
if (i != 1) allEqual = false;
} else {
if (i != 1) allEqual = false;
}
}
if (allEqual || grandTotal < 2) {
DebugWrite("^9(DEBUG) ToTeamByDispersal: all equal by rank, skipping", 5);
return 0; // don't disperse
}
// fall through
}
return targetTeam; // ok if 0 or same as fromTeam, caller checks
}