PRoConEvents.MULTIbalancer.CheckTeamSwitch C# (CSharp) Method

CheckTeamSwitch() private method

private CheckTeamSwitch ( String name, int toTeam ) : bool
name String
toTeam int
return bool
        private bool CheckTeamSwitch(String name, int toTeam)
        {
            if (fPluginState != PluginState.Active || fGameState != GameState.Playing) return false;

            // Get model
            PlayerModel player = GetPlayer(name);
            if (player == null) return false;
            bool bogusMove = false;
            int lastMoveTo = 0;
            int lastMoveFrom = player.LastMoveFrom;

            // Same team?
            if (toTeam == player.Team) {
            /*
            This could happen with the following sequence of actions:
            + Player died and was moved from 1 to 2 for balance immediately, spawn messages set
            + While still dead, player switches himself back to 1 before respawning
            + All of this happens before a listPlayers refresh, so the model still thinks he is in team 1
            We have to detect that the switch is not to the intended team and fix everything up.
            */
            if (player.LastMoveTo != 0 && player.LastMoveTo != toTeam) {
            DebugUnswitch("Player team switch: ^b" + name + "^n trying to switch to " + GetTeamName(toTeam) + " during a plugin move to " + GetTeamName(player.LastMoveTo));
            bogusMove = true;
            lastMoveTo = player.LastMoveTo;
            player.LastMoveTo = 0;
            DebugUnswitch("Ovewriting previous chat message for ^b" + name + "^n: " + player.SpawnChatMessage);
            player.SpawnChatMessage = String.Empty;
            player.SpawnYellMessage = String.Empty;
            } else {
            DebugUnswitch("Player team switch: ^b" + name + "^n, player model already updated to " + GetTeamName(toTeam) + " team");
            return true;
            }
            } else {
            DebugUnswitch("Player team switch: ^b" + name + "^n from " + GetTeamName(player.Team) + " team to " + GetTeamName(toTeam) + " team");
            }

            // Allow special cases
            if (player.Role != ROLE_PLAYER) {
            DebugUnswitch("ALLOWED: not a player role (Role = " + player.Role + ")");
            SetSpawnMessages(name, String.Empty, String.Empty, false);
            CheckAbortMove(name);
            return true;
            } else if (player.Team == 0) {
            DebugUnswitch("ALLOWED: switching from team 0 (Neutral)");
            SetSpawnMessages(name, String.Empty, String.Empty, false);
            CheckAbortMove(name);
            return true;
            }

            // Check if move already in progress for this player and abort it
            bool sendAbortMessage = false;
            lock (fMoveStash) {
            if (fMoveStash.Count > 0) {
             // list only ever has one item
            if (fMoveStash[0].Name == name) {
                fMoveStash.Clear();
            }
            }
            }
            if (sendAbortMessage) {
            DebugUnswitch("ABORTED (by move stash): abort previous move by ^b" + name);
            sendAbortMessage = false;
            }

            // Whitelisted?
            if (OnWhitelist) {
            if (CheckWhitelist(player, WL_SWITCH)) {
            DebugUnswitch("ALLOWED: On whitelist: ^b" + name);
            SetSpawnMessages(name, String.Empty, String.Empty, false);
            CheckAbortMove(name);
            return true;
            }
            }

            // Low population adjustments?
            PerModeSettings perMode = GetPerModeSettings();
            if (perMode.EnableLowPopulationAdjustments && GetPopulation(perMode, true) == Population.Low) {
            DebugUnswitch("ALLOWED: Enable Low Population Adjustments is True and population is Low: ^b" + name);
            SetSpawnMessages(name, String.Empty, String.Empty, false);
            CheckAbortMove(name);
            return true;
            }

            // Check forbidden cases
            bool isSQDM = IsSQDM();
            bool isDispersal = IsInDispersalList(player, false);
            bool isRank = IsRankDispersal(player);
            bool isClanDispersal = IsClanDispersal(player, false);
            bool forbidden = (((isDispersal || isRank || isClanDispersal) && Forbid(perMode, ForbidSwitchingAfterDispersal)) || (player.MovesByMBRound > 0 && !isSQDM && Forbid(perMode, ForbidSwitchingAfterAutobalance)));

            // Unlimited time?
            if (!forbidden && UnlimitedTeamSwitchingDuringFirstMinutesOfRound > 0 && GetTimeInRoundMinutes() <= UnlimitedTeamSwitchingDuringFirstMinutesOfRound) {
            DebugUnswitch("ALLOWED: Time in round " + GetTimeInRoundMinutes().ToString("F0") + " <= " + UnlimitedTeamSwitchingDuringFirstMinutesOfRound.ToString("F0") + ": ^b" + name);
            SetSpawnMessages(name, String.Empty, String.Empty, false);
            CheckAbortMove(name);
            return true;
            }

            // Minutes after joining?
            if (!forbidden && MinutesAfterJoining > 0 && GetPlayerJoinedTimeSpan(player).TotalMinutes <= MinutesAfterJoining) {
            DebugUnswitch("ALLOWED: Time since joining " + GetPlayerJoinedTimeSpan(player).TotalMinutes.ToString("F0") + " <= " + MinutesAfterJoining.ToString("F0") + ": ^b" + name);
            SetSpawnMessages(name, String.Empty, String.Empty, false);
            CheckAbortMove(name);
            return true;
            }

            // Helps?
            int diff = 0;
            int biggestTeam = 0;
            int smallestTeam = 0;
            int winningTeam = 0;
            int losingTeam = 0;
            int[] ascendingSize = null;
            int[] descendingTickets = null;
            int fromTeam = player.Team;
            MoveInfo move = null;
            bool toLosing = false;
            bool toSmallest = false;

            /*
            A player that was previously moved by the plugin is forbidden from moving to any
            other team by their own initiative for the rest of the round, unless this is
            SQDM mode. In SQDM, if a player is moved from A to B and then later decides
            to move to C, the losing team, that is allowed. Even in SQDM, though, no player
            is allowed to move to the winning team.

            All dispersal players are forbidden from moving themselves.
            */

            AnalyzeTeams(out diff, out ascendingSize, out descendingTickets, out biggestTeam, out smallestTeam, out winningTeam, out losingTeam);

            int iFrom = 0;
            int iTo = 0;

            if (isSQDM) {
            // Moving to any team with fewer tickets is encouraged
            for (int i = 0; i < descendingTickets.Length; ++i) {
            if (fromTeam == descendingTickets[i]) iFrom = i;
            if (toTeam == descendingTickets[i]) iTo = i;
            }
            toLosing = (iTo > iFrom);
            } else {
            toLosing = (toTeam == losingTeam);
            }

            // Trying to switch to losing team?
            if (!forbidden && toLosing && toTeam != biggestTeam) {
            move = new MoveInfo(player.Name, player.Tag, fromTeam, GetTeamName(fromTeam), toTeam, GetTeamName(toTeam), YellDurationSeconds);
            move.Format(this, ChatDetectedGoodTeamSwitch, false, true);
            move.Format(this, YellDetectedGoodTeamSwitch, true, true);
            DebugUnswitch("ALLOWED: Team switch to losing team ^b: " + name);
            SetSpawnMessages(name, move.ChatBefore, move.YellBefore, false);
            CheckAbortMove(name);
            return true;
            }

            if (isSQDM) {
            // Moving to any team with fewer players is encouraged
            for (int i = 0; i < ascendingSize.Length; ++i) {
            if (fromTeam == ascendingSize[i]) iFrom = i;
            if (toTeam == ascendingSize[i]) iTo = i;
            }
            toSmallest = (iTo < iFrom);
            } else {
            toSmallest = (toTeam == smallestTeam);
            }

            // Trying to switch to smallest team?
            if (!forbidden && toSmallest && toTeam != winningTeam) {
            move = new MoveInfo(player.Name, player.Tag, fromTeam, GetTeamName(fromTeam), toTeam, GetTeamName(toTeam), YellDurationSeconds);
            move.Format(this, ChatDetectedGoodTeamSwitch, false, true);
            move.Format(this, YellDetectedGoodTeamSwitch, true, true);
            DebugUnswitch("ALLOWED: Team switch to smallest team ^b: " + name);
            SetSpawnMessages(name, move.ChatBefore, move.YellBefore, false);
            CheckAbortMove(name);
            return true;
            }

            // Adjust for SQDM
            if (isSQDM && fServerInfo != null) {
            if (GetPopulation(perMode, true) == Population.Low) {
            // Allow team switch to any team except biggest and winning
            if (!forbidden && toTeam != biggestTeam && toTeam != winningTeam) {
                DebugUnswitch("ALLOWED: SQDM Low population and not switching to biggest or winning team: ^b" + name);
                SetSpawnMessages(name, String.Empty, String.Empty, false);
                CheckAbortMove(name);
                return true;
            }
            }
            }

            // Allow if ticket/point difference is less than allowed margin
            double win = 0;
            double lose = 0;
            double margin = 100;
            if (IsCTF() || IsCarrierAssault() || IsObliteration()) {
            win = GetTeamPoints(winningTeam);
            if (win == 0) win = 1;
            lose = GetTeamPoints(losingTeam);
            if (lose == 0) lose = 1;
            margin = ((win > lose) ? win/lose : lose/win);
            // margin is 110%
            if (!forbidden && (margin * 100) <= 110) {
            DebugUnswitch("ALLOWED: move by ^b" + name + "^n because score margin is only " + (margin*100).ToString("F0") + "%");
            SetSpawnMessages(name, String.Empty, String.Empty, false);
            CheckAbortMove(name);
            return true;
            }
            } else {
            win = fTickets[winningTeam];
            if (win == 0) win = 1;
            lose = fTickets[losingTeam];
            if (lose == 0) lose = 1;
            margin = ((win > lose) ? win/lose : lose/win);
            // margin is 105%
            if (!forbidden && (margin * 100) <= 105) {
            DebugUnswitch("ALLOWED: move by ^b" + name + "^n because margin is only " + (margin*100).ToString("F0") + "%");
            SetSpawnMessages(name, String.Empty, String.Empty, false);
            CheckAbortMove(name);
            return true;
            }
            }

            // Otherwise, do not allow the team switch
            int origTeam = player.Team;
            String origName = GetTeamName(player.Team);

            if (lastMoveFrom != 0 && toTeam != lastMoveFrom) {
            DebugUnswitch("Setting toTeam from " + GetTeamName(toTeam) + " to original LastMoveFrom = " + GetTeamName(lastMoveFrom));
            toTeam = lastMoveFrom;
            }

            if (bogusMove) {
            origTeam = lastMoveTo;
            origName = GetTeamName(lastMoveTo);
            }

            // select forbidden message from: moved by autobalance, moved to unstack, dispersal, ...
            String badChat = ChatDetectedBadTeamSwitch;
            String badYell = YellDetectedBadTeamSwitch;

            ForbidBecause why = ForbidBecause.None;

            if (player.MovesByMBRound > 0 && !isSQDM) {
            why = ForbidBecause.MovedByBalancer;
            if (!Forbid(perMode, ForbidSwitchingAfterAutobalance)) {
            DebugUnswitch("ALLOWED: move by ^b" + name + "^n because ^bForbid Switch After Autobalance^n is False");
            SetSpawnMessages(name, String.Empty, String.Empty, false);
            CheckAbortMove(name);
            return true;
            }
            } else if (toTeam == winningTeam) {
            why = ForbidBecause.ToWinning;
            if (!Forbid(perMode, ForbidSwitchingToWinningTeam)) {
            DebugUnswitch("ALLOWED: move by ^b" + name + "^n because ^bForbid Switch To Winning Team^n is False");
            SetSpawnMessages(name, String.Empty, String.Empty, false);
            CheckAbortMove(name);
            return true;
            }
            } else if (toTeam == biggestTeam) {
            why = ForbidBecause.ToBiggest;
            if (!Forbid(perMode, ForbidSwitchingToBiggestTeam)) {
            DebugUnswitch("ALLOWED: move by ^b" + name + "^n because ^bForbid Switch To Biggest Team^n is False");
            SetSpawnMessages(name, String.Empty, String.Empty, false);
            CheckAbortMove(name);
            return true;
            }
            } else if (isDispersal) {
            why = ForbidBecause.DisperseByList;
            if (!Forbid(perMode, ForbidSwitchingAfterDispersal)) {
            DebugUnswitch("ALLOWED: move by ^b" + name + "^n because ^bForbid Switch After Dispersal^n is False");
            SetSpawnMessages(name, String.Empty, String.Empty, false);
            CheckAbortMove(name);
            return true;
            }
            } else if (isRank) {
            why = ForbidBecause.DisperseByRank;
            if (!Forbid(perMode, ForbidSwitchingAfterDispersal)) {
            DebugUnswitch("ALLOWED: move by ^b" + name + "^n because ^bForbid Switch After Dispersal^n is False");
            SetSpawnMessages(name, String.Empty, String.Empty, false);
            CheckAbortMove(name);
            return true;
            }
            } else if (isClanDispersal) {
            why = ForbidBecause.DisperseByClan;
            if (!Forbid(perMode, ForbidSwitchingAfterDispersal)) {
            DebugUnswitch("ALLOWED: move by ^b" + name + "^n because ^bForbid Switch After Dispersal^n is False");
            SetSpawnMessages(name, String.Empty, String.Empty, false);
            CheckAbortMove(name);
            return true;
            }
            }

            // Check switch to same team?
            if (toTeam == origTeam) {
            ConsoleDebug("CheckTeamSwitch: ^b" + name + "^n, can't forbid unswitch to same team " + GetTeamName(toTeam) + "?");
            SetSpawnMessages(name, String.Empty, String.Empty, false);
            CheckAbortMove(name);
            return true;
            }

            /*
            Too soon to move again.

            This can happen when another plugin, particularly another instance of MB, is moving players.
            Players get into a ping-poing unswitch loop. Adding a time check will prevent this.
            */
            double esm = DateTime.Now.Subtract(player.MovedTimestamp).TotalSeconds;
            if (esm < 15) {
            DebugUnswitch("IGNORED: switch by ^b" + name + "^n, too soon (" + esm.ToString("F1") + " secs ago) since last move, maybe another plugin is switching this player?");
            SetSpawnMessages(name, String.Empty, String.Empty, false);
            CheckAbortMove(name);
            return true;
            }

            // Tried to switch toTeam from origTeam, so moving from toTeam back to origTeam
            move = new MoveInfo(name, player.Tag, toTeam, GetTeamName(toTeam), origTeam, origName, YellDurationSeconds);
            move.For = MoveType.Unswitch;
            move.Because = why;
            move.Format(this, badChat, false, true);
            move.Format(this, badYell, true, true);
            move.Format(this, ChatAfterUnswitching, false, false);
            move.Format(this, YellAfterUnswitching, true, false);
            player.LastMoveFrom = 0;

            if (DebugLevel >= 8) DebugUnswitch(move.ToString());

            if (isSQDM || !EnableImmediateUnswitch) {
            // Delay action until after the player spawns
            DebugUnswitch("FORBIDDEN: delaying unswitch action until spawn of ^b" + name + "^n from " + move.SourceName + " back to " + move.DestinationName);

            if (player.DelayedMove != null) {
            CheckAbortMove(name);
            }
            player.DelayedMove = move;

            if (!String.IsNullOrEmpty(player.SpawnChatMessage)) {
            DebugUnswitch("IGNORED: previously delayed spawn message for ^b" + name + "^n: " + player.SpawnChatMessage);
            SetSpawnMessages(name, String.Empty, String.Empty, false);
            }
            } else {
            // Do the move immediately
            DebugUnswitch("FORBIDDEN: immediately unswitch ^b" + name + "^n from " + move.SourceName + " back to " + move.DestinationName);
            String log = "^4^bUNSWITCHING^n^0 ^b" + player.FullName + "^n from " + move.SourceName + " back to " + move.DestinationName;
            log = (EnableLoggingOnlyMode) ? "^9(SIMULATING)^0 " + log : log;
            DebugWrite(log, 3);
            StartMoveImmediate(move, true);
            }

            return false;
        }
MULTIbalancer