Advertisement
Hadlock

insanebalancerbf3procon-v-alpha.cs

Nov 3rd, 2011
129
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 137.54 KB | None | 0 0
  1. /*
  2.  * Copyright 2010 Miguel Mendoza - miguel@micovery.com
  3.  *
  4.  * Insane Balancer is free software: you can redistribute it and/or modify it under the terms of the
  5.  * GNU General Public License as published by the Free Software Foundation, either version 3 of the License,
  6.  * or (at your option) any later version. Insane Balancer is distributed in the hope that it will be useful,
  7.  * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  8.  * See the GNU General Public License for more details. You should have received a copy of the
  9.  * GNU General Public License along with Insane Balancer. If not, see http://www.gnu.org/licenses/.
  10.  *
  11.  */
  12.  
  13. using System;
  14. using System.IO;
  15. using System.Text;
  16. using System.Reflection;
  17. using System.Collections.Generic;
  18. using System.Collections;
  19. using System.Net;
  20. using System.Threading;
  21.  
  22. //using System.Drawing;
  23. //using System.Drawing.Drawing2D;
  24.  
  25.  
  26. using System.Data;
  27. using System.Text.RegularExpressions;
  28.  
  29. using PRoCon.Core;
  30. using PRoCon.Core.Plugin;
  31. using PRoCon.Core.Plugin.Commands;
  32. using PRoCon.Core.Players;
  33. using PRoCon.Core.Players.Items;
  34. using PRoCon.Core.Battlemap;
  35. using PRoCon.Core.Maps;
  36.  
  37. namespace PRoConEvents
  38. {
  39.  
  40.     public class InsaneBalancer :  PRoConPluginAPI, IPRoConPluginInterface
  41.     {
  42.  
  43.         int check_state_phase = 0;
  44.         bool virtual_mode = false;
  45.         int win_teamId, lose_teamId;
  46.         public bool run_balancer = false;
  47.         public bool level_started = false;
  48.  
  49.      
  50.  
  51.  
  52.         private class PlayerSquad
  53.         {
  54.  
  55.             public List<PlayerProfile> members;
  56.             int squadId = 0;
  57.             int teamId = 0;
  58.  
  59.             public PlayerSquad(int tid, int sid)
  60.             {
  61.                 members = new List<PlayerProfile>();
  62.                 teamId = tid;
  63.                 squadId = sid;
  64.             }
  65.  
  66.             public PlayerSquad(PlayerSquad squad)
  67.             {
  68.                 squadId = squad.squadId;
  69.                 teamId = squad.squadId;
  70.                 members = squad.members;
  71.             }
  72.  
  73.             private bool playerBelongs(PlayerProfile player)
  74.             {
  75.                 return getTeamId() == player.getTeamId() && getSquadId() == player.getSquadId();
  76.             }
  77.  
  78.             public bool hasPlayer(PlayerProfile player)
  79.             {
  80.                 return members.Contains(player);
  81.             }
  82.  
  83.             public int getFreeSlots()
  84.             {
  85.                 if (getSquadId() == 0)
  86.                     return 32;
  87.  
  88.                 return 4 - getCount();
  89.             }
  90.  
  91.             public bool addPlayer(PlayerProfile player)
  92.             {
  93.                 if (!playerBelongs(player) || hasPlayer(player))
  94.                     return false;
  95.  
  96.  
  97.                 members.Add(player);
  98.                 return true;
  99.             }
  100.  
  101.             public virtual int getTeamId()
  102.             {
  103.                 return teamId;
  104.             }
  105.  
  106.             public virtual int getSquadId()
  107.             {
  108.                 return squadId;
  109.             }
  110.  
  111.  
  112.             public bool dropPlayer(PlayerProfile player)
  113.             {
  114.                 if (!members.Contains(player))
  115.                     return false;
  116.  
  117.                 members.Remove(player);
  118.                 return true;
  119.             }
  120.  
  121.             public virtual PlayerProfile getRandomPlayer()
  122.             {
  123.                 if (getCount() == 0)
  124.                     return null;
  125.  
  126.                 /* 0 - player 0 has been chosen by fair dice roll */
  127.                 return members[0];
  128.             }
  129.  
  130.             public PlayerProfile removeRandomPlayer()
  131.             {
  132.                 PlayerProfile player = getRandomPlayer();
  133.                 if (player == null)
  134.                     return null;
  135.  
  136.                 dropPlayer(player);
  137.                 return player;
  138.             }
  139.  
  140.             public int getCount()
  141.             {
  142.                 return members.Count;
  143.             }
  144.  
  145.             public List<PlayerProfile> getMembers()
  146.             {
  147.                 return members;
  148.             }
  149.  
  150.  
  151.  
  152.  
  153.  
  154.  
  155.             public double getRoundSpm()
  156.             {
  157.                 int count = getCount();
  158.                 if (count == 0)
  159.                     return 0;
  160.  
  161.                 double spm = 0;
  162.                 foreach (PlayerProfile player in getMembers())
  163.                 {
  164.                     spm += player.getRoundSpm();
  165.                 }
  166.  
  167.                 return (spm / count);
  168.             }
  169.  
  170.             public double getRoundKpm()
  171.             {
  172.                 int count = getCount();
  173.                 if (count == 0)
  174.                     return 0;
  175.  
  176.                 double kpm = 0;
  177.                 foreach (PlayerProfile player in getMembers())
  178.                 {
  179.                     kpm += player.getRoundKpm();
  180.                 }
  181.  
  182.                 return (kpm / count);
  183.             }
  184.  
  185.  
  186.             public double getRoundKills()
  187.             {
  188.                 int count = getCount();
  189.                 if (count == 0)
  190.                     return 0;
  191.  
  192.                 double kills = 0;
  193.                 foreach (PlayerProfile player in getMembers())
  194.                 {
  195.                     kills += player.getRoundKills();
  196.                 }
  197.  
  198.                 return (kills / count);
  199.             }
  200.  
  201.             public double getRoundDeaths()
  202.             {
  203.                 int count = getCount();
  204.                 if (count == 0)
  205.                     return 0;
  206.  
  207.                 double deaths = 0;
  208.                 foreach (PlayerProfile player in getMembers())
  209.                 {
  210.                     deaths += player.getRoundDeaths();
  211.                 }
  212.  
  213.                 return (deaths / count);
  214.             }
  215.  
  216.             public double getRoundScore()
  217.             {
  218.                 int count = getCount();
  219.                 if (count == 0)
  220.                     return 0;
  221.  
  222.                 double score = 0;
  223.                 foreach (PlayerProfile player in getMembers())
  224.                 {
  225.                     score += player.getRoundScore();
  226.                 }
  227.  
  228.                 return (score / count);
  229.             }
  230.  
  231.  
  232.             public double getRoundKdr()
  233.             {
  234.                 return (getRoundKills() + 1) / (getRoundDeaths() + 1);
  235.             }
  236.  
  237.  
  238.  
  239.  
  240.  
  241.  
  242.  
  243.  
  244.  
  245.  
  246.             public override String ToString()
  247.             {
  248.                 return "Team(" + getTeamId() + ").Squad(" + SQN(getSquadId()) + "): " + getMembersListStr();
  249.             }
  250.  
  251.             public string getMembersListStr()
  252.             {
  253.                 List<string> names = new List<string>();
  254.                 foreach (PlayerProfile player in members)
  255.                     names.Add(player.name);
  256.  
  257.                 return String.Join(", ", names.ToArray());
  258.             }
  259.  
  260.  
  261.             public string getClanTag()
  262.             {
  263.                 if (getCount() == 0)
  264.                     return "";
  265.  
  266.                 return getRandomPlayer().getClanTag();
  267.             }
  268.  
  269.             public string getMajorityClanTag()
  270.             {
  271.  
  272.                 if (getCount() == 0)
  273.                     return "";
  274.  
  275.                 Dictionary<string, int> tagCount = new Dictionary<string, int>();
  276.                 string tag = "";
  277.  
  278.                 /* count how many times each tag repeats */
  279.                 foreach (PlayerProfile player in getMembers())
  280.                 {
  281.                     tag = player.getClanTag();
  282.                     if (tag == null || tag.Length == 0)
  283.                         continue;
  284.  
  285.                     if (!tagCount.ContainsKey(tag))
  286.                         tagCount[tag] = 0;
  287.  
  288.                     tagCount[tag]++;
  289.                 }
  290.  
  291.                 if (tagCount.Count == 0)
  292.                     return "";
  293.  
  294.                 /* sort by ascending tag count */
  295.                 List<KeyValuePair<string, int>> list = new List<KeyValuePair<string, int>>(tagCount);
  296.                 list.Sort(delegate(KeyValuePair<string, int> left, KeyValuePair<string, int> right) { return left.Value.CompareTo(right.Value)*(-1); });
  297.  
  298.                 return list[0].Key;
  299.             }
  300.  
  301.             public void setSquadId(int sid)
  302.             {
  303.                 squadId = sid;
  304.             }
  305.  
  306.             public void setTeamId(int tid)
  307.             {
  308.                 teamId = tid;
  309.             }
  310.         }
  311.  
  312.         private class PlayerMessage
  313.         {
  314.             public MessageType type;
  315.             public string text;
  316.             public int time;
  317.  
  318.             public PlayerMessage(string tx)
  319.             {
  320.                 text = tx;
  321.                 type = MessageType.say;
  322.             }
  323.         }
  324.  
  325.         public enum PluginState { stop, wait, check, warn, balance };
  326.  
  327.         //variables to keep track of the start time for each state
  328.         DateTime startStopTime;
  329.         DateTime startWaitTime;
  330.         DateTime startCheckTime;
  331.         DateTime startWarnTime;
  332.         DateTime startBalanceTime;
  333.         DateTime startRoundTime;
  334.         DateTime utc; //universal time;
  335.  
  336.         PluginState pluginState;
  337.         CServerInfo serverInfo = null;
  338.  
  339.         bool plugin_enabled = false;
  340.  
  341.         public Dictionary<string, bool> booleanVariables;
  342.         public Dictionary<string, int> integerVariables;
  343.         public Dictionary<string, float> floatVariables;
  344.         public Dictionary<string, string> stringListVariables;
  345.         public Dictionary<string, string> stringVariables;
  346.         public List<string> hiddenVariables;
  347.  
  348.  
  349.         public delegate bool integerVariableValidator(string var, int value);
  350.         public delegate bool booleanVariableValidator(string var, bool value);
  351.         private delegate int player_sort_method(PlayerProfile left, PlayerProfile right);
  352.         private delegate int squad_sort_method(PlayerSquad left, PlayerSquad right);
  353.         public delegate bool stringVariableValidator(string var, string value);
  354.         public Dictionary<string, integerVariableValidator> intergerVarValidators;
  355.         public Dictionary<string, booleanVariableValidator> booleanVarValidators;
  356.         public Dictionary<string, stringVariableValidator> stringVarValidators;
  357.  
  358.  
  359.         private Dictionary<string, PlayerProfile> players;
  360.  
  361.  
  362.         public enum PlayerState { dead, alive, left, kicked, limbo };
  363.         public enum MessageType { say, invalid };
  364.  
  365.        
  366.         private struct PlayerStats
  367.         {
  368.             public double rank;
  369.             public double kills;
  370.             public double deaths;
  371.             public double score;
  372.             public double skill;
  373.  
  374.             public void reset()
  375.             {
  376.                 rank = 0;
  377.                 kills = 0;
  378.                 deaths = 0;
  379.                 score = 0;
  380.                 skill = 0;
  381.             }
  382.  
  383.             public double kdr()
  384.             {
  385.                 return (kills + 1) / (deaths + 1);
  386.             }
  387.         }
  388.  
  389.         private class PlayerProfile
  390.         {
  391.             private InsaneBalancer plugin;
  392.             public string name;
  393.             public PlayerStats stats;
  394.             public PlayerStats round_stats;
  395.             public PlayerState state;
  396.             public CPlayerInfo info;
  397.             public CPunkbusterInfo pbinfo;
  398.             public Queue<PlayerMessage> qmsg;          //queued messages
  399.            
  400.             int savedTeamId = -1;
  401.             int savedSquadId = -1;
  402.  
  403.             int targetTeamId = -1;
  404.             int targetSquadId = -1;
  405.  
  406.  
  407.             public void leaveGame()
  408.             {
  409.                 state = PlayerState.left;
  410.             }
  411.  
  412.             public void spawned()
  413.             {
  414.                 state = PlayerState.alive;
  415.             }
  416.  
  417.             public bool isInGame()
  418.             {
  419.                 return (info.TeamID != null && info.TeamID >= 0);
  420.             }
  421.  
  422.             public PlayerProfile(PlayerProfile player)
  423.             {
  424.                 /* shallow copy */
  425.                 updateInfo(player.info);
  426.                 pbinfo = player.pbinfo;
  427.                 name = player.name;
  428.                 plugin = player.plugin;
  429.                 stats = player.stats;
  430.                 state = player.state;
  431.                 qmsg = player.qmsg;
  432.             }
  433.  
  434.             public PlayerProfile(InsaneBalancer plg, CPlayerInfo inf)
  435.             {
  436.                 plg.DebugWrite("^1ENTER: PlayerProfile(InsaneBalancer, CPlayerInfo)", 7);
  437.                
  438.                 pbinfo = new CPunkbusterInfo();
  439.                 name = info.SoldierName;
  440.                 plugin = plg;
  441.  
  442.                 updateInfo(inf);
  443.                 resetStats();
  444.                 plg.DebugWrite("^1EXIT: PlayerProfile(InsaneBalancer, CPlayerInfo)", 7);
  445.             }
  446.  
  447.             public PlayerProfile(InsaneBalancer plg, CPunkbusterInfo inf)
  448.             {
  449.                 plg.DebugWrite("^1ENTER: PlayerProfile(InsaneBalancer, CPunkbusterInfo)", 7);
  450.                 info = new CPlayerInfo();
  451.                 pbinfo = inf;
  452.                 name = pbinfo.SoldierName;
  453.                 plugin = plg;
  454.                 resetStats();
  455.                 plg.DebugWrite("^1EXIT: PlayerProfile(InsaneBalancer, CPunkbusterInfo)", 7);
  456.             }
  457.  
  458.             public PlayerProfile(InsaneBalancer plg, string nm)
  459.             {
  460.                 plg.DebugWrite("^1ENTER: PlayerProfile(InsaneBalancer, string)", 7);
  461.                 name = nm;
  462.                 info = new CPlayerInfo();
  463.                 pbinfo = new CPunkbusterInfo();
  464.                 plugin = plg;
  465.                 resetStats();
  466.                 plg.DebugWrite("^1EXIT: PlayerProfile(InsaneBalancer, string)", 7);
  467.  
  468.             }
  469.  
  470.             public void updateInfo(CPlayerInfo inf)
  471.             {
  472.                 plugin.DebugWrite("^1ENTER: updateInfo", 7);
  473.                 /* don't update the information while round is begining */
  474.                 if (plugin.run_balancer)
  475.                     return;
  476.  
  477.                 info = inf;
  478.  
  479.                 round_stats.kills = info.Kills;
  480.                 round_stats.deaths = info.Deaths;
  481.                 round_stats.score = info.Score;
  482.                 plugin.DebugWrite("^1EXIT: updateInfo", 7);
  483.  
  484.             }
  485.  
  486.            
  487.  
  488.             public void resetStats()
  489.             {
  490.                 plugin.DebugWrite("^1ENTER: resetStats for ^b"+name, 7);
  491.                 //queued messages
  492.                 qmsg = new Queue<PlayerMessage>();
  493.  
  494.                 //other
  495.                 state = PlayerState.limbo;
  496.                 round_stats.reset();
  497.                
  498.                 savedSquadId = -1;
  499.                 savedTeamId = -1;
  500.                 targetSquadId = -1;
  501.                 targetTeamId = -1;
  502.                 plugin.DebugWrite("^1EXIT: resetStats for ^b" + name, 7);
  503.             }
  504.  
  505.  
  506.             /* Player Messages */
  507.  
  508.             public void dequeueMessages()
  509.             {
  510.                 while (this.qmsg.Count > 0)
  511.                 {
  512.                     PlayerMessage msg = this.qmsg.Dequeue();
  513.                     if (msg.type.Equals(MessageType.say))
  514.                     {
  515.                         this.plugin.SendPlayerMessage(name, msg.text);
  516.                     }
  517.                 }
  518.             }
  519.  
  520.  
  521.             public void enqueueMessage(PlayerMessage msg)
  522.             {
  523.                 this.qmsg.Enqueue(msg);
  524.             }
  525.  
  526.  
  527.  
  528.             /* Round Level Statistics */
  529.             public double getRoundKpm()
  530.             {
  531.                 double minutes = plugin.getRoundMinutes();
  532.                 double kills = getRoundKills();
  533.  
  534.                 return kills / minutes;
  535.             }
  536.  
  537.             public double getRoundSpm()
  538.             {
  539.                 double minutes = plugin.getRoundMinutes();
  540.                 double score = getRoundScore();
  541.                 return score / minutes;
  542.             }
  543.  
  544.             public double getRoundKills()
  545.             {
  546.                 return round_stats.kills;
  547.             }
  548.  
  549.             public double getRoundScore()
  550.             {
  551.                 return round_stats.score;
  552.             }
  553.  
  554.             public double getRoundDeaths()
  555.             {
  556.                 return round_stats.deaths;
  557.             }
  558.            
  559.             public double getRoundKdr()
  560.             {
  561.                 return (getRoundKills() + 1) / (getRoundDeaths() + 1);
  562.        
  563.             }
  564.  
  565.             public override string ToString()
  566.             {
  567.                 return this.name;
  568.             }
  569.  
  570.  
  571.             /* Player State and Information */
  572.             public bool isAlive()
  573.             {
  574.                 return state.Equals(PlayerState.alive);
  575.             }
  576.  
  577.             public bool isDead()
  578.             {
  579.                 return state.Equals(PlayerState.dead);
  580.             }
  581.  
  582.             public bool wasKicked()
  583.             {
  584.                 return state.Equals(PlayerState.kicked);
  585.             }
  586.  
  587.             public string getClanTag()
  588.             {
  589.  
  590.                 if (info == null || info.ClanTag == null || info.ClanTag.Length == 0)
  591.                     return "";
  592.  
  593.                 return info.ClanTag.ToLower();
  594.                  
  595.             }
  596.  
  597.             public bool isInClan()
  598.             {
  599.                 return getClanTag().Length > 0;
  600.             }
  601.  
  602.             public virtual void setSquadId(int sid)
  603.             {
  604.                 info.SquadID = sid;
  605.             }
  606.  
  607.             public virtual void setTeamId(int tid)
  608.             {
  609.                 info.TeamID = tid;
  610.             }
  611.  
  612.             public virtual int getSquadId()
  613.             {
  614.                 return info.SquadID;
  615.             }
  616.  
  617.             public virtual int getTeamId()
  618.             {
  619.                 return info.TeamID;
  620.             }
  621.  
  622.             public void saveTeamSquad()
  623.             {
  624.  
  625.                 if (savedTeamId == -1)
  626.                     savedTeamId = getTeamId();
  627.  
  628.                 if (savedSquadId == -1)
  629.                     savedSquadId = getSquadId();
  630.             }
  631.  
  632.             public void saveTargetTeamSquad()
  633.             {
  634.  
  635.                 if (targetTeamId == -1)
  636.                     targetTeamId = getTeamId();
  637.  
  638.                 if (targetSquadId == -1)
  639.                     targetSquadId = getSquadId();
  640.             }
  641.  
  642.             public int getSavedSquadId()
  643.             {
  644.                 return savedSquadId;
  645.             }
  646.  
  647.             public int getSavedTeamId()
  648.             {
  649.                 return savedTeamId;
  650.             }
  651.  
  652.             public int getTargetSquadId()
  653.             {
  654.                 return targetSquadId;
  655.             }
  656.  
  657.             public int getTargetTeamId()
  658.             {
  659.                 return targetTeamId;
  660.             }
  661.  
  662.             public void resetTeamSquad()
  663.             {
  664.                 if (savedTeamId > 0)
  665.                     setTeamId(savedTeamId);
  666.  
  667.                 if (savedSquadId > 0)
  668.                     setSquadId(savedSquadId);
  669.             }
  670.  
  671.  
  672.             public string getRoundStatistics()
  673.             {
  674.                 return String.Format("score({0}), kills({1}), deaths({2}) kdr({3}), spm({4}), kpm({5})", getRoundScore(), getRoundKills(), getRoundDeaths(), Math.Round(getRoundKdr(),2) , Math.Round(getRoundSpm(),2), Math.Round(getRoundKpm(),2));
  675.             }
  676.  
  677.         }
  678.  
  679.  
  680.         public InsaneBalancer()
  681.         {
  682.             utc = DateTime.Now;
  683.             startRoundTime = utc;
  684.  
  685.             this.players = new Dictionary<string, PlayerProfile>();
  686.  
  687.  
  688.             this.booleanVariables = new Dictionary<string, bool>();
  689.             this.booleanVariables.Add("auto_start", true);
  690.             this.booleanVariables.Add("keep_squads", true);
  691.             this.booleanVariables.Add("keep_clans", false);
  692.             this.booleanVariables.Add("warn_say", false);
  693.             this.booleanVariables.Add("balance_round", true);
  694.             this.booleanVariables.Add("balance_live", true);
  695.  
  696.  
  697.             this.booleanVariables.Add("quiet_mode", false);
  698.             this.booleanVariables.Add("advanced_mode", false);
  699.  
  700.             this.integerVariables = new Dictionary<string, int>();
  701.             this.integerVariables.Add("balance_threshold", 1);
  702.             this.integerVariables.Add("debug_level", 3);
  703.             this.integerVariables.Add("live_interval_time", 15);
  704.             this.integerVariables.Add("round_interval", 1);
  705.             this.integerVariables.Add("warn_msg_interval_time", 15);
  706.             this.integerVariables.Add("warn_msg_total_time", 15);
  707.             this.integerVariables.Add("warn_msg_countdown_time", 3);
  708.             this.integerVariables.Add("warn_msg_display_time", 5);
  709.  
  710.  
  711.             this.intergerVarValidators = new Dictionary<string, integerVariableValidator>();
  712.             this.intergerVarValidators.Add("warn_msg_interval_time", integerValidator);
  713.             this.intergerVarValidators.Add("warn_msg_total_time", integerValidator);
  714.             this.intergerVarValidators.Add("warn_msg_display_time", integerValidator);
  715.             this.intergerVarValidators.Add("warn_msg_countdown_time", integerValidator);
  716.             this.intergerVarValidators.Add("balance_threshold", integerValidator);
  717.             this.intergerVarValidators.Add("round_interval", integerValidator);
  718.             this.intergerVarValidators.Add("live_interval_time", integerValidator);
  719.             this.intergerVarValidators.Add("debug_level", integerValidator);
  720.  
  721.             this.booleanVarValidators = new Dictionary<string, booleanVariableValidator>();
  722.             this.booleanVarValidators.Add("keep_squads", booleanValidator);
  723.             this.booleanVarValidators.Add("keep_clans", booleanValidator);
  724.  
  725.  
  726.  
  727.             this.stringVarValidators = new Dictionary<string, stringVariableValidator>();
  728.             this.stringVarValidators.Add("round_sort", stringValidator);
  729.             this.stringVarValidators.Add("live_sort", stringValidator);
  730.  
  731.  
  732.             this.floatVariables = new Dictionary<string, float>();
  733.             this.stringListVariables = new Dictionary<string, string>();
  734.             this.stringListVariables.Add("admin_list", @"micovery, admin2, admin3");
  735.  
  736.             this.stringVariables = new Dictionary<string, string>();
  737.  
  738.             this.stringVariables.Add("round_sort", "spm_desc_round");
  739.             this.stringVariables.Add("live_sort", "spm_desc_round");
  740.  
  741.             this.hiddenVariables = new List<string>();
  742.             this.hiddenVariables.Add("warn_msg_total_time");
  743.             this.hiddenVariables.Add("warn_msg_countdown_time");
  744.             this.hiddenVariables.Add("warn_msg_interval_time");
  745.             this.hiddenVariables.Add("warn_msg_display_time");
  746.             this.hiddenVariables.Add("quiet_mode");
  747.             this.hiddenVariables.Add("debug_level");
  748.             this.hiddenVariables.Add("auto_start");
  749.  
  750.         }
  751.  
  752.  
  753.         public void loadSettings()
  754.         {
  755.  
  756.         }
  757.  
  758.  
  759.         private player_sort_method getPlayerSort(string phase)
  760.         {
  761.             string sort_method = getStringVarValue(phase);
  762.  
  763.  
  764.             if (sort_method.CompareTo("kdr_asc_round") == 0)
  765.                 return player_kdr_asc_round_cmp;
  766.             else if (sort_method.CompareTo("kdr_desc_round") == 0)
  767.                 return player_kdr_desc_round_cmp;
  768.             else if (sort_method.CompareTo("score_asc_round") == 0)
  769.                 return player_score_asc_round_cmp;
  770.             else if (sort_method.CompareTo("score_desc_round") == 0)
  771.                 return player_score_desc_round_cmp;
  772.             else if (sort_method.CompareTo("spm_asc_round") == 0)
  773.                 return player_spm_asc_round_cmp;
  774.             else if (sort_method.CompareTo("spm_desc_round") == 0)
  775.                 return player_spm_desc_round_cmp;
  776.             else if (sort_method.CompareTo("kpm_asc_round") == 0)
  777.                 return player_kpm_asc_round_cmp;
  778.             else if (sort_method.CompareTo("kpm_desc_round") == 0)
  779.                 return player_kpm_desc_round_cmp;
  780.  
  781.             ConsoleWrite("cannot find player sort method for ^b" + sort_method + "^0 during ^b" + phase + "^n, using default sort");
  782.             return player_spm_asc_round_cmp;
  783.         }
  784.  
  785.  
  786.         private squad_sort_method getSquadSort(string phase)
  787.         {
  788.             string sort_method = getStringVarValue(phase);
  789.            
  790.             if (sort_method.CompareTo("kdr_asc_round") == 0)
  791.                 return squad_kdr_asc_round_cmp;
  792.             else if (sort_method.CompareTo("kdr_desc_round") == 0)
  793.                 return squad_kdr_desc_round_cmp;
  794.             else if (sort_method.CompareTo("score_asc_round") == 0)
  795.                 return squad_score_asc_round_cmp;
  796.             else if (sort_method.CompareTo("score_desc_round") == 0)
  797.                 return squad_score_desc_round_cmp;
  798.             else if (sort_method.CompareTo("spm_asc_round") == 0)
  799.                 return squad_spm_asc_round_cmp;
  800.             else if (sort_method.CompareTo("spm_desc_round") == 0)
  801.                 return squad_spm_desc_round_cmp;
  802.             else if (sort_method.CompareTo("kpm_asc_round") == 0)
  803.                 return squad_kpm_asc_round_cmp;
  804.             else if (sort_method.CompareTo("kpm_desc_round") == 0)
  805.                 return squad_kpm_desc_round_cmp;
  806.  
  807.             ConsoleWrite("cannot find squad sort method for ^b" + sort_method + "^0 during ^b" + phase + "^n, using default sort");
  808.             return squad_kpm_desc_round_cmp;
  809.         }
  810.  
  811.  
  812.         /* squad comparison methods */
  813.  
  814.         private int squad_count_asc_cmp(PlayerSquad left, PlayerSquad right)
  815.         {
  816.             int lval = left.getCount();
  817.             int rval = right.getCount();
  818.  
  819.             return lval.CompareTo(rval);
  820.         }
  821.  
  822.         private int squad_count_desc_cmp(PlayerSquad left, PlayerSquad right)
  823.         {
  824.             return squad_count_asc_cmp(left, right) * (-1);
  825.         }
  826.  
  827.  
  828.         private int squad_kdr_asc_round_cmp(PlayerSquad left, PlayerSquad right)
  829.         {
  830.             double lval = left.getRoundKdr();
  831.             double rval = right.getRoundKdr();
  832.             return lval.CompareTo(rval);
  833.         }
  834.  
  835.         private int squad_kdr_desc_round_cmp(PlayerSquad left, PlayerSquad right)
  836.         {
  837.             return squad_kdr_asc_round_cmp(left, right) * (-1);
  838.         }
  839.  
  840.         private int squad_spm_asc_round_cmp(PlayerSquad left, PlayerSquad right)
  841.         {
  842.             double lval = left.getRoundSpm();
  843.             double rval = right.getRoundSpm();
  844.             return lval.CompareTo(rval);
  845.         }
  846.  
  847.         private int squad_spm_desc_round_cmp(PlayerSquad left, PlayerSquad right)
  848.         {
  849.             return squad_spm_asc_round_cmp(left, right) * (-1);
  850.         }
  851.  
  852.  
  853.         private int squad_score_asc_round_cmp(PlayerSquad left, PlayerSquad right)
  854.         {
  855.             double lval = left.getRoundScore();
  856.             double rval = right.getRoundScore();
  857.             return lval.CompareTo(rval);
  858.         }
  859.  
  860.         private int squad_score_desc_round_cmp(PlayerSquad left, PlayerSquad right)
  861.         {
  862.             return squad_score_asc_round_cmp(left, right) * (-1);
  863.         }
  864.  
  865.         private int squad_kpm_asc_round_cmp(PlayerSquad left, PlayerSquad right)
  866.         {
  867.             double lval = left.getRoundKpm();
  868.             double rval = right.getRoundKpm();
  869.             return lval.CompareTo(rval);
  870.         }
  871.  
  872.         private int squad_kpm_desc_round_cmp(PlayerSquad left, PlayerSquad right)
  873.         {
  874.             return squad_kpm_asc_round_cmp(left, right) * (-1);
  875.         }
  876.  
  877.  
  878.  
  879.  
  880.         /* player comparison methods */
  881.  
  882.  
  883.         private int player_kdr_asc_round_cmp(PlayerProfile left, PlayerProfile right)
  884.         {
  885.             double lval = left.getRoundKdr();
  886.             double rval = right.getRoundKdr();
  887.  
  888.             return lval.CompareTo(rval);
  889.         }
  890.  
  891.         private int player_kdr_desc_round_cmp(PlayerProfile left, PlayerProfile right)
  892.         {
  893.             return player_kdr_asc_round_cmp(left, right) * (-1);
  894.         }
  895.  
  896.         private int player_spm_asc_round_cmp(PlayerProfile left, PlayerProfile right)
  897.         {
  898.             double lval = left.getRoundSpm();
  899.             double rval = right.getRoundSpm();
  900.             return lval.CompareTo(rval);
  901.         }
  902.  
  903.         private int player_spm_desc_round_cmp(PlayerProfile left, PlayerProfile right)
  904.         {
  905.             return player_spm_asc_round_cmp(left, right) * (-1);
  906.         }
  907.  
  908.  
  909.         private int player_score_asc_round_cmp(PlayerProfile left, PlayerProfile right)
  910.         {
  911.             double lval = left.getRoundScore();
  912.             double rval = right.getRoundScore();
  913.             return lval.CompareTo(rval);
  914.         }
  915.  
  916.         private int player_score_desc_round_cmp(PlayerProfile left, PlayerProfile right)
  917.         {
  918.             return player_score_asc_round_cmp(left, right) * (-1);
  919.         }
  920.  
  921.         private int player_kpm_asc_round_cmp(PlayerProfile left, PlayerProfile right)
  922.         {
  923.             double lval = left.getRoundKpm();
  924.             double rval = right.getRoundKpm();
  925.             return lval.CompareTo(rval);
  926.         }
  927.  
  928.         private int player_kpm_desc_round_cmp(PlayerProfile left, PlayerProfile right)
  929.         {
  930.             return player_kpm_asc_round_cmp(left, right) * (-1);
  931.         }
  932.  
  933.  
  934.  
  935.  
  936.        
  937.        
  938.        
  939.        
  940.        
  941.        
  942.        
  943.        
  944.        
  945.        
  946.         public void unloadSettings()
  947.         {
  948.             this.players.Clear();
  949.         }
  950.  
  951.  
  952.  
  953.         public string GetPluginName()
  954.         {
  955.             return "Insane Balancer";
  956.         }
  957.  
  958.         public string GetPluginVersion()
  959.         {
  960.             return "0.0.0.2";
  961.         }
  962.  
  963.         public string GetPluginAuthor()
  964.         {
  965.             return "micovery";
  966.         }
  967.  
  968.         public string GetPluginWebsite()
  969.         {
  970.             return "www.insanegamersasylum.com";
  971.         }
  972.  
  973.  
  974.         public string GetPluginDescription()
  975.         {
  976.             return @"
  977.        <h2>Description</h2>
  978.        <p> This is the draft impelementation for a flexible team balancer, which can balance teams by skill, rank, score, kdr and other rules.
  979.            All of it, while doing best effort to maintain squads together, and clans on the same team.
  980.        </p>
  981.  
  982.        <h2>Sort Methods</h2>
  983.        <p> A sort method is a rule used for sorting a list of players or squads. The following balancing methods are supported:
  984.        </p>
  985.        <ul>
  986.            
  987.            <li><b>kpm_asc_round</b> , <b>kpm_desc_round</b> <br />
  988.             Sorting based on the soldier round kills per minute
  989.            </li>
  990.            <li><b>spm_asc_round</b> , <b>spm_desc_round</b> <br />
  991.             Sorting based on the soldier round score per minute
  992.            </li>
  993.            <li><b>kdr_asc_round</b> , <b>kdr_desc_round</b> <br />
  994.             Sorting based on the soldier round kill to death ratio  
  995.            </li>
  996.          
  997.             <li><b>score_asc_round</b> , <b>score_desc_round</b> <br />
  998.             Sorting based on the soldier round score  
  999.            </li>
  1000.  
  1001.        </ul>
  1002.  
  1003.            All the data for sorting rules ending in <b>_round</b> is obtained from the previous or current round statistics.
  1004.      
  1005.        <h2>Live Balancing Logic</h2>
  1006.                          
  1007.        <blockquote>
  1008.        Insane Balancer tries to be as un-intrusive as posible while balancing a game that is in progress.
  1009.        If the teams become un-balanced while the game is in progess it will create two pools of players and sort them.
  1010.        (players chosen from the bigger team) One pool for players who are not in any squad, and another pool for squads.
  1011.        First it will chose the player at the top of the no-squad pool and move it to the other team until teams are balanced.
  1012.        If the no-squad pool becomes empty (and teams are still unbalanced) then squad at the top of the squad pool is moved
  1013.        to the other team if the number of players needed is greater than or equal to the size of the squad. If the number of players
  1014.        needed is less than the size of the top squad, a random random player is chosen from the squad and moved to the opposite team
  1015.        until teams are balanced. (players that were on the same squad are kept together)
  1016.        </blockquote>
  1017.  
  1018.        
  1019.        <h2>Round Re-balancing Logic</h2>
  1020.                          
  1021.        <blockquote>
  1022.        If end of round balancing is enabled, Insane Balancer will completely re-sort teams, even if they are already balanced.
  1023.        The logic for the re-sort is as follows. Create two pools of players and sort them (choosing players from all teams).
  1024.        One pool for players who are not in squads, and another for players who are in squads. Then, move all all players and squads
  1025.        to the neutral team, in order to end up with two empty non-neutral teams. Then, pick the squad at the top of the squad pool,
  1026.        and move it to the losing team. Then pick the next squad on top of the squad pool, and move it to the team with the least players, and so on.
  1027.        Once the squad pool is empty, pick the player on top of the no-squad pool, and move it to the team with least players, and so on.
  1028.        If teams are still unbalanced after the no-squad pool is empty, then the live balancing logic is applied.        
  1029.        </blockquote>
  1030.  
  1031.        
  1032.        <h2>Balanced Team Determination </h2>
  1033.        
  1034.        <blockquote>
  1035.        Teams are determined to be balanced if the difference in number of players between teams is less than or equal to the <b>balance_threshold</b>.
  1036.        The <b>balance_threshold</b> has to be a number greater than 0. If the total number of players in the server is less than or equal to the
  1037.        <b>balance_threshold</b>, then the user set threshold is ignored, and a value of 1 is used instead. Technically, no team should ever be bigger
  1038.        than the other by more than the value of <b>balance_threshold</b>.
  1039.        </blockquote>
  1040.  
  1041.        <h2>Keeping Squads Together </h2>
  1042.  
  1043.        <blockquote>
  1044.        Insane Balancer is coded to keep squads together by default. However, you can set <b>keep_squads</b> to false and both round-balancing and
  1045.        live-balancing logics are changed a bit. What happens, is that all squads in the squad pool are broken, and players put into the no-squad pool,
  1046.        before balancing starts.<br />
  1047.        <br />
  1048.        Note that for live-balancing, players are not actually moved out of the squad. (it would kill all players if you do that).
  1049.        They are just treated as if they were not in a squad. Also, If <b>keep_squads</b> is enabled, clan-squads will not be broken.
  1050.        </blockquote>
  1051.  
  1052.        <h2> Keeping Clans On Same Team </h2>  
  1053.  
  1054.        <blockquote>                                                                        
  1055.        During end-of round re-balancing, if <b>keep_clans</b> is enabled, players with the same clan tag are be removed from their current squad,
  1056.        and put into exclusive squads. These special clan squads are given priority over non-clan squads, so that clan-squads end up in the same team.
  1057.        Note that when <b>keep_clans</b> is enabled, teams may end up unbalanced in number, so the live-balancing logic may still need to be applied.<br />
  1058.        <br />
  1059.        During live-balancing, if <b>keep_clans</b> is enabled, players with clan tags are given priority, as long as there is at least two members of
  1060.        the same clan in the server. When picking players to move to the other team, if a player has a clan tag, the player will be automatically skipped,
  1061.        if the majority of the clan is in the same team (otherwise the player is moved to the other team to join his clan buddies).
  1062.        If at the end of live-balancing phase, teams are still unbalanced, then <b>keep_clans</b> is disabled temporarily, and the live-balancer logic is applied again.
  1063.        </blockquote>        
  1064.  
  1065.        <h2>Whitelist</h2>
  1066.  
  1067.        <blockquote>
  1068.        Whitelist is a list of players which have even higher priority than clan members. During live balancing, if a player is in the whitelist, he is skipped.
  1069.        If at the end of the live balancing, the teams are still unbalanced, then the whitelist is ignored, and the live balancing logic is re-applied.
  1070.        </blockquote>
  1071.  
  1072.        <h2>Settings</h2>
  1073.        <ol>
  1074.          <li><blockquote><b>balance_threshold</b><br />
  1075.                <i>(integer > 0)</i> -  maximum difference in team sizes before teams are considered unbalanced <br />
  1076.                Technically, no team will ever be bigger by more than the <b>balance_threshold</b>
  1077.                </blockquote>
  1078.          </li>
  1079.          <li><blockquote><b>live_interval_time</b><br />
  1080.                <i>(integer > 0)</i> - interval number of seconds at which team balance is checked during game  <br />
  1081.                </blockquote>
  1082.          </li>
  1083.          <li><blockquote><b>round_interval</b><br />
  1084.                <i>(integer > 0)</i> - interval number number of rounds at which the round balancer is applied  <br />
  1085.                For example, if map Atacama has 6 rounds, and the value of <b>round_interval</b> is 2, then the round
  1086.                balancer is run at the end of rounds 2, 4, and 6.
  1087.                </blockquote>
  1088.          </li>
  1089.          <li><blockquote><strong>keep_squads</strong><br />
  1090.                <i>true</i> - squads are preseved when balancing <br />
  1091.                <i>false</i> - squads are intentionally broken up before balancing starts
  1092.                </blockquote>
  1093.                This setting only applies tot he round-end balancer.
  1094.          </li>
  1095.          <li><blockquote><strong>keep_clans</strong><br />
  1096.                <i>true</i> - players with same clan tags are kept on the same team <br />
  1097.                <i>false</i> - clan tags are ignored during balancing
  1098.                </blockquote>
  1099.          </li>
  1100.          <li><blockquote><strong>warn_say</strong><br />
  1101.                <i>true</i> - send auto-balancer warning in chat <br />
  1102.                <i>false</i> - do not say the auto-balancer warning in chat
  1103.                </blockquote>
  1104.          </li>
  1105.          <li><blockquote><strong>balance_round</strong><br />
  1106.                <i>true</i> - enables the end of round balancer<br />
  1107.                <i>false</i> - disabled the end of round balancer
  1108.                </blockquote>
  1109.          </li>
  1110.          <li><blockquote><strong>balance_live</strong><br />
  1111.                <i>true</i> - enables the live balancer<br />
  1112.                <i>false</i> - disables the live balancer
  1113.                </blockquote>
  1114.          </li>
  1115.          <li><blockquote><strong>admin_list</strong><br />
  1116.                <i>(string)</i> - list of players who are allow to execute admin commands      
  1117.                </blockquote>
  1118.           </li>        
  1119.           <li><blockquote><strong>round_sort</strong><br />
  1120.                <i>(string)</i> - method used for sorting players and squads during end of round balancing      
  1121.                </blockquote>
  1122.           </li>
  1123.           <li><blockquote><strong>live_sort</strong><br />
  1124.                <i>(string)</i> - method used for sorting players and squads during live balancing      
  1125.                </blockquote>
  1126.           </li>
  1127.           <li><blockquote><strong>advanced_mode</strong><br />
  1128.                <i>true</i> - enables the advanced settings mode which displays extra undocumented plugin settings<br />
  1129.                <i>false</i> - disables the advanced settings mode
  1130.                </blockquote>
  1131.           </li>
  1132.        </ol>
  1133.  
  1134.       <h2> Advanced Settings</h2>
  1135.       There are settings that are shown by enabling <b>advanced_mode</b>
  1136.        <ol>
  1137.          <li><blockquote><b>warn_msg_interval_time</b><br />
  1138.                <i>(integer > 0)</i> -  this is the interval time in seconds at which the balance warning will be sent <br />
  1139.                The value must be less than or equal to the <b>warn_msg_total_time</b>
  1140.                </blockquote>
  1141.          </li>
  1142.          <li><blockquote><b>warn_msg_total_time</b><br />
  1143.                <i>(integer > 0)</i> -  this is the total amount of time in seconds, that a warning message will be sent  <br />
  1144.                Basically this is a grace period for players to fix themselves the teams before the live balancer kicks in.
  1145.                </blockquote>
  1146.          </li>
  1147.          <li><blockquote><b>warn_msg_display_time</b><br />
  1148.                <i>(integer >= 0)</i> -  this is the amount of time in seconds for how long the balancing warning is displayed  <br />
  1149.                The value must be less than or equal to the <b>warn_msg_total_time</b>
  1150.                </blockquote>
  1151.          </li>  
  1152.          <li><blockquote><b>warn_msg_countdown_time</b><br />
  1153.                <i>(integer >= 0)</i> - this is the amount of time in seconds for the countdown at the end of the warning period.
  1154.                The value must be less than or equal to the <b>warn_msg_total_time</b>
  1155.                </blockquote>
  1156.          </li>
  1157.          <li><blockquote><strong>auto_start</strong><br />
  1158.                <i>true</i> - enables auto start for live balancing checks<br />
  1159.                <i>false</i> - disables the live balancer auto start, you will need to issue the <i>!start check</i> command
  1160.                </blockquote>
  1161.           </li>
  1162.           <li><blockquote><b>warn_msg_countdown_time</b><br />
  1163.                <i>(integer >= 0 && <= 6)</i> - this variable is for debug mode. When the value is zero, no debug information is printed <br />
  1164.                on the plugin console.  For medium debugging information you can set it to 3. The bigger the value, the more debugging details are printed. <br />
  1165.               </blockquote>
  1166.          </li>    
  1167.        </ol>
  1168.        <h2>Public In-Game Commands</h2>
  1169.        <p>
  1170.            In-game commands are messages typed into the game chat box, which have special meaning to the plugin.
  1171.            Commands must start with one of the following characters: !,@, or /. This plugin interprets the following commands:
  1172.        </p>
  1173.        <ul>
  1174.           <li><blockquote><strong>!move</strong><br />
  1175.               This command can be used by regular players to move themselves to the opposite team as long as teams are balanced.
  1176.               </blockquote>
  1177.           </li>
  1178.        </ul>
  1179.       <h2> Admin In-Game Commands</h2>
  1180.        <p>
  1181.            These are the commands that only soldiers in the ""admin_list"" are allowed to execute. Reply messages generated by admin commands
  1182.            are sent only to the admin who executed the command.
  1183.        </p>
  1184.        <ul>
  1185.           <li><blockquote><strong>!start check</strong><br />
  1186.               This command puts the live balancer in started state, so that it periodically (every <b>live_interval_time</b> seconds) checks the teams for balance. <br />
  1187.               When this command is run <b>balance_live</b>is implicitly set to true.
  1188.               </blockquote>
  1189.           </li>
  1190.           <li><blockquote><strong>!stop check</strong><br />
  1191.                This command puts the live balancer in stopped state.
  1192.                When this command is run <b>balance_live</b> is implicitly set to false.
  1193.               </blockquote>
  1194.           </li>
  1195.           <li><blockquote><strong>!show round stats [player-name]</strong><br />
  1196.                This command is used for showing the player statistics for the current round.
  1197.                The name of the player is optional. If you do not provide a player name, it will print statistics for all players.
  1198.               </blockquote>
  1199.           </li>
  1200.           <li><blockquote><strong>!balance live</strong><br />
  1201.               This command forces the live balancing logic to be applied whithout any warning period or countdown.
  1202.               </blockquote>
  1203.           </li>
  1204.          <li><blockquote><strong>!balance round</strong><br />
  1205.               This command forces the round balancing logic to be applied whithout any warning period or countdown.
  1206.               </blockquote>
  1207.           </li>
  1208.           <li><blockquote>
  1209.                <strong>1. !set {variable} {to|=} {value}</strong><br />
  1210.                <strong>2. !set {variable} {value}</strong><br />      
  1211.                <strong>3. !set {variable}</strong><br />  
  1212.                This command is used for setting the value of this plugin's variables.<br />
  1213.                For the 2nd invocation syntax you cannot use ""="" or ""to"" as the variable value. <br />
  1214.                For the 3rd invocation syntax the value is assumed to be ""true"".
  1215.               </blockquote>
  1216.           </li>
  1217.           <li><blockquote>
  1218.                <strong>!get {variable} </strong><br />
  1219.                This command prints the value of the specified variable.
  1220.               </blockquote>
  1221.           </li>
  1222.         </ul>
  1223.        ";
  1224.         }
  1225.  
  1226.         public void OnPluginLoaded(string strHostName, string strPort, string strPRoConVersion)
  1227.         {
  1228.             ConsoleWrite("plugin loaded");
  1229.             this.RegisterEvents("OnPlayerJoin",
  1230.                                 "OnPlayerLeft",
  1231.                                 "OnGlobalChat",
  1232.                                 "OnTeamChat",
  1233.                                 "OnSquadChat",
  1234.                                 "OnLevelStarted",
  1235.                                 "OnPunkbusterplayerStatsCmd",
  1236.                                 "OnServerInfo",
  1237.                                 "OnPlayerTeamChange",
  1238.                                 "OnPlayerSquadChange",
  1239.                                 "OnplayersStatsCmd",
  1240.                                 "OnRoundOver");
  1241.         }
  1242.  
  1243.         public void OnPluginEnable()
  1244.         {
  1245.             ConsoleWrite("^b^2Enabled!^0");
  1246.  
  1247.             plugin_enabled = true;
  1248.  
  1249.             unloadSettings();
  1250.             loadSettings();
  1251.  
  1252.             addPluginCallTask("InsaneBalancer", "ticks", 0, 1, -1);
  1253.             initializeBalancer();
  1254.         }
  1255.  
  1256.  
  1257.         public void addPluginCallTask(string task, string method, int delay, int interval, int repeat)
  1258.         {
  1259.             this.ExecuteCommand("procon.protected.tasks.add", task, delay.ToString(), interval.ToString(), repeat.ToString(), "procon.protected.plugins.call", "InsaneBalancer", method);
  1260.         }
  1261.  
  1262.         public void removeTask(string task)
  1263.         {
  1264.             this.ExecuteCommand("procon.protected.tasks.remove", task);
  1265.         }
  1266.  
  1267.         public int getElapsedTime(DateTime now, PluginState state)
  1268.         {
  1269.             DateTime startTime = getStartTime(state);
  1270.             int elapsed = (int)now.Subtract(startTime).TotalSeconds;
  1271.             return elapsed;
  1272.         }
  1273.  
  1274.         public DateTime getStartTime(PluginState state)
  1275.         {
  1276.             if (state.Equals(PluginState.wait))
  1277.                 return startWaitTime;
  1278.             else if (state.Equals(PluginState.warn))
  1279.                 return startWarnTime;
  1280.             else if (state.Equals(PluginState.check))
  1281.                 return startCheckTime;
  1282.             else if (state.Equals(PluginState.balance))
  1283.                 return startBalanceTime;
  1284.             else if (state.Equals(PluginState.stop))
  1285.                 return startStopTime;
  1286.             else
  1287.                 ConsoleWrite("^1cannot find start time for ^b" + state.ToString() + "^n^0");
  1288.  
  1289.             return utc;
  1290.         }
  1291.  
  1292.         public void setStartTime(PluginState state, DateTime now)
  1293.         {
  1294.             if (state.Equals(PluginState.wait))
  1295.                 startWaitTime = now;
  1296.             else if (state.Equals(PluginState.warn))
  1297.                 startWarnTime = now;
  1298.             else if (state.Equals(PluginState.check))
  1299.                 startCheckTime = now;
  1300.             else if (state.Equals(PluginState.balance))
  1301.                 startBalanceTime = now;
  1302.             else if (state.Equals(PluginState.stop))
  1303.                 startStopTime = now;
  1304.             else
  1305.                 ConsoleWrite("^1cannot set start time for ^b" + state.ToString() + "^n^0");
  1306.         }
  1307.  
  1308.  
  1309.         public int getMaxTime(PluginState state)
  1310.         {
  1311.             if (state.Equals(PluginState.wait))
  1312.                 return getIntegerVarValue("live_interval_time");
  1313.             else if (state.Equals(PluginState.warn))
  1314.                 return getIntegerVarValue("warn_msg_total_time");
  1315.             /*else
  1316.                 DebugWrite("^1Getting max time for ^b" + state.ToString() + "^n state is not valid", 6);
  1317.             */
  1318.  
  1319.             return getElapsedTime(utc, PluginState.check);
  1320.         }
  1321.  
  1322.         public int getRemainingTime(DateTime now, PluginState state)
  1323.         {
  1324.  
  1325.             int max_time = getMaxTime(state);
  1326.             int elapsed = getElapsedTime(now, state);
  1327.  
  1328.             int remain = max_time - elapsed;
  1329.             return remain;
  1330.         }
  1331.  
  1332.  
  1333.         public void ExecCommand(params string[] args)
  1334.         {
  1335.             List<string> list = new List<string>();
  1336.             list.Add("procon.protected.send");
  1337.             list.AddRange(args);
  1338.             this.ExecuteCommand(list.ToArray());
  1339.         }
  1340.  
  1341.  
  1342.  
  1343.         public void getPlayerList()
  1344.         {
  1345.             ExecCommand("admin.listPlayers", "all");
  1346.             ExecCommand("punkBuster.pb_sv_command", "pb_sv_plist");
  1347.         }
  1348.  
  1349.         public void getServerInfo()
  1350.         {
  1351.             ExecCommand("serverInfo");
  1352.         }
  1353.  
  1354.  
  1355.         public void ticks()
  1356.         {
  1357.             utc = utc.AddSeconds(1);
  1358.             timer(utc);
  1359.  
  1360.         }
  1361.  
  1362.         public bool isPluginState(PluginState state)
  1363.         {
  1364.             return pluginState.Equals(state);
  1365.         }
  1366.  
  1367.         public bool isPluginWaiting()
  1368.         {
  1369.             return isPluginState(PluginState.wait);
  1370.         }
  1371.  
  1372.         public bool isPluginBalancing()
  1373.         {
  1374.             return isPluginState(PluginState.balance);
  1375.         }
  1376.  
  1377.         public bool isPluginWarning()
  1378.         {
  1379.             return isPluginState(PluginState.warn);
  1380.         }
  1381.  
  1382.         public bool isPluginStopped()
  1383.         {
  1384.             return isPluginState(PluginState.stop);
  1385.         }
  1386.  
  1387.         public bool isPluginChecking()
  1388.         {
  1389.             return isPluginState(PluginState.check);
  1390.         }
  1391.  
  1392.         public void startCheckState(DateTime now)
  1393.         {
  1394.             DebugWrite("^1ENTER: startCheckState", 10);
  1395.             if (check_state_phase == 0)
  1396.             {
  1397.                 pluginState = PluginState.check;
  1398.                 setStartTime(pluginState, now.AddSeconds(1));
  1399.                 DebugWrite("^b" + pluginState + "^n state started " + getStartTime(pluginState).ToString() + "^n^0", 1);
  1400.  
  1401.                 DebugWrite("^b" + PluginState.check.ToString() + "^n state ^bphase-" + check_state_phase + "^n started " + getStartTime(pluginState).ToString() + "^0", 2);
  1402.                 DebugWrite("Requesting player list", 2);
  1403.  
  1404.                 check_state_phase = 1;
  1405.                 getPlayerList();
  1406.  
  1407.                 DebugWrite("^1EXIT: startCheckState", 10);
  1408.                 return;
  1409.             }
  1410.             else if (check_state_phase == 1)
  1411.             {
  1412.  
  1413.                 DebugWrite("^b" + PluginState.check.ToString() + "^n state ^bphase-" + check_state_phase + "^n started " + now.ToString() + "^0", 2);
  1414.  
  1415.  
  1416.  
  1417.                 if (teamsUnbalanced())
  1418.                 {
  1419.                     DebugWrite("Teams are unbalanced, going to ^b" + PluginState.warn.ToString() + "^n state", 2);
  1420.                     startWarnState(now);
  1421.                 }
  1422.                 else
  1423.                 {
  1424.                     DebugWrite("Teams are balanced, going to ^b" + PluginState.wait.ToString() + "^n state", 2);
  1425.                     restartWaitState(now);
  1426.                 }
  1427.  
  1428.                 check_state_phase = 0;
  1429.                 DebugWrite("^1EXIT: startCheckState", 10);
  1430.                 return;
  1431.             }
  1432.         }
  1433.  
  1434.         public void timer(DateTime now)
  1435.         {
  1436.  
  1437.             if (!getBooleanVarValue("balance_live"))
  1438.                 return;
  1439.  
  1440.             int remain_time = getRemainingTime(now, pluginState);
  1441.             int elapsed_time = getElapsedTime(now, pluginState);
  1442.  
  1443.  
  1444.             if (isPluginChecking() || isPluginStopped() || isPluginBalancing())
  1445.                 DebugWrite(pluginState.ToString() + "(" + elapsed_time + ")", 4);
  1446.             else
  1447.                 DebugWrite(pluginState.ToString() + "(" + remain_time + ")", 4);
  1448.  
  1449.  
  1450.  
  1451.             if (isPluginStopped())
  1452.             {
  1453.                 if (getBooleanVarValue("auto_start"))
  1454.                 {
  1455.                     DebugWrite("^bauto_start^n is enabled, going to ^b" + PluginState.wait.ToString() + "^n state^0", 2);
  1456.                     startWaitSate(now);
  1457.                 }
  1458.             }
  1459.             else if (isPluginWaiting())
  1460.             {
  1461.                 if (remain_time <= 0)
  1462.                     startCheckState(now);
  1463.             }
  1464.             else if (isPluginWarning())
  1465.             {
  1466.                 int countdown_time = getIntegerVarValue("warn_msg_countdown_time");
  1467.                 int display_time = getIntegerVarValue("warn_msg_display_time");
  1468.                 int interval_time = getIntegerVarValue("warn_msg_interval_time");
  1469.  
  1470.                 if (teamsBalanced())
  1471.                 {
  1472.                     DebugWrite("Teams are balanced, halting ^b" + PluginState.warn.ToString() + "^n state, and restarting ^b" + PluginState.wait.ToString() + "^n state^0", 4);
  1473.                     restartWaitState(now);
  1474.                     return;
  1475.                 }
  1476.                 else if (remain_time <= 0)
  1477.                 {
  1478.                     balanceLive(now);
  1479.                     restartWaitState(now);
  1480.                     return;
  1481.                 }
  1482.                 else if (remain_time >= 1 && remain_time <= countdown_time)
  1483.                 {
  1484.                     warnCountdown();
  1485.                     return;
  1486.                 }
  1487.                 else if (isTimeLeft(remain_time, display_time, interval_time, countdown_time))
  1488.                 {
  1489.                     warnAnnounce(display_time);
  1490.                     return;
  1491.                 }
  1492.             }
  1493.         }
  1494.  
  1495.  
  1496.         private bool teamsUnbalanced()
  1497.         {
  1498.             return !teamsBalanced();
  1499.         }
  1500.  
  1501.         private bool teamsBalanced()
  1502.         {
  1503.             DebugWrite("^1ENTER: teamsBalanced", 10);
  1504.             //return false;
  1505.             /* initialize hash with player count for 16 teams*/
  1506.             Dictionary<int, int> player_count = getPlayerCount();
  1507.             int total = player_count[1] + player_count[2];
  1508.  
  1509.             int difference = Math.Abs(player_count[1] - player_count[2]);
  1510.             int balance_threshold = getIntegerVarValue("balance_threshold");
  1511.  
  1512.             /* assumer the minimum threshold if user total players is less than user set threshold */
  1513.             int threshold = (total <= balance_threshold) ? 1 : balance_threshold;
  1514.  
  1515.             DebugWrite("^1EXIT: teamsBalanced", 10);
  1516.  
  1517.             if (difference <= balance_threshold)
  1518.                 return true;
  1519.  
  1520.             return false;
  1521.         }
  1522.  
  1523.         private int sumSquadPlayers(List<PlayerSquad> squads)
  1524.         {
  1525.             int sum = 0;
  1526.             foreach (PlayerSquad squad in squads)
  1527.                 sum += squad.getCount();
  1528.             return sum;
  1529.         }
  1530.  
  1531.         private void listPlayers()
  1532.         {
  1533.             List<PlayerProfile> players_list = getPlayersProfile("");
  1534.             int count = 1;
  1535.  
  1536.             DebugWrite("== Listing Players == ", 3);
  1537.             foreach (PlayerProfile player in players_list)
  1538.             {
  1539.                 DebugWrite("    "+count+". ^b"+player.name+"^n STeam("+player.getSavedTeamId()+").SSquad("+SQN(player.getSavedSquadId())+") ... Team("+player.getTeamId()+").Squad("+SQN(player.getSquadId())+")", 3);
  1540.                 count++;
  1541.             }
  1542.         }
  1543.  
  1544.         private void balanceRound(int winTeamId)
  1545.         {
  1546.             if (!getBooleanVarValue("balance_round"))
  1547.             {
  1548.                 ConsoleWrite("Round balancer disbaled, not running");
  1549.                 return;
  1550.             }
  1551.  
  1552.             virtual_mode = true;
  1553.             DateTime now = utc;
  1554.             pluginState = PluginState.balance;
  1555.             setStartTime(pluginState, now.AddSeconds(1));
  1556.             DebugWrite("^b" + pluginState + "_clans^n state started " + getStartTime(pluginState).ToString() + "^n^0", 1);
  1557.  
  1558.  
  1559.             int neutralTeamId = 0;
  1560.             int loseTeamId = getOpposingTeamId(winTeamId);
  1561.  
  1562.             DebugWrite("Building no-squad pool from ^bTeam(" + winTeamId + ")^n and ^bTeam(" + loseTeamId + ")^n^0", 3);
  1563.             List<PlayerProfile> win_nosquad_pool = getNoSquadPlayers(winTeamId);
  1564.             List<PlayerProfile> lose_nosquad_pool = getNoSquadPlayers(loseTeamId);
  1565.             List<PlayerProfile> nosquad_pool = new List<PlayerProfile>();
  1566.             nosquad_pool.AddRange(win_nosquad_pool);
  1567.             nosquad_pool.AddRange(lose_nosquad_pool);
  1568.  
  1569.             DebugWrite("No-squad pool has ^b" + nosquad_pool.Count + "^n player/s^0", 2);
  1570.             DebugWrite("Building squad pool from ^bTeam(" + winTeamId + ")^n and ^bTeam(" + loseTeamId + ")^n^0", 3);
  1571.  
  1572.             List<PlayerSquad> win_squad_pool = getNonEmptySquads(winTeamId);
  1573.             List<PlayerSquad> lose_squad_pool = getNonEmptySquads(loseTeamId);
  1574.             List<PlayerSquad> squad_pool = new List<PlayerSquad>();
  1575.             squad_pool.AddRange(win_squad_pool);
  1576.             squad_pool.AddRange(lose_squad_pool);
  1577.  
  1578.             DebugWrite("Squad pool has ^b" + squad_pool.Count + "^n squads^0", 3);
  1579.  
  1580.             if (!getBooleanVarValue("keep_squads"))
  1581.             {
  1582.                 foreach (PlayerSquad squad in squad_pool)
  1583.                 {
  1584.                     DebugWrite("Breaking up ^bTeam(" + squad.getTeamId() + ").Squad(" + SQN(squad.getSquadId()) + ")^n^0", 3);
  1585.                     while (squad.getCount() > 0)
  1586.                     {
  1587.                         PlayerProfile player = squad.getRandomPlayer();
  1588.                         squad.dropPlayer(player);
  1589.                         movePlayer(player, player.getTeamId(), 0, true);
  1590.                         nosquad_pool.Add(player);
  1591.  
  1592.                     }
  1593.                 }
  1594.             }
  1595.  
  1596.             DebugWrite("Moving no-squad pool to neutral ^bTeam(" + neutralTeamId + ")^n^0", 3);
  1597.             foreach (PlayerProfile player in nosquad_pool)
  1598.                 movePlayer(player, neutralTeamId, 0, true);
  1599.  
  1600.             DebugWrite("Moving squal pool to neutral ^bTeam(" + neutralTeamId + ")^n^0", 3);
  1601.             foreach (PlayerSquad squad in squad_pool)
  1602.                 moveSquad(squad, neutralTeamId);
  1603.  
  1604.  
  1605.             /* re-build the pools */
  1606.  
  1607.             DebugWrite("Rebuilding no-squad pool from ^bTeam(" + winTeamId + ")^n and ^bTeam(" + loseTeamId + ")^n^0", 3);
  1608.             nosquad_pool = getNoSquadPlayers(neutralTeamId);
  1609.             DebugWrite("No-squad pool has ^b" + nosquad_pool.Count + "^n player/s^0", 3);
  1610.  
  1611.             DebugWrite("Rebuilding squad pool from ^bTeam(" + winTeamId + ")^n and ^bTeam(" + loseTeamId + ")^n^0", 3);
  1612.             squad_pool = getNonEmptySquads(neutralTeamId);
  1613.             DebugWrite("Squad pool has ^b" + squad_pool.Count + "^n squads^0", 2);
  1614.  
  1615.  
  1616.             if (getBooleanVarValue("keep_clans"))
  1617.             {
  1618.                 DebugWrite("Keeping clans in same team", 3);
  1619.  
  1620.                 /* collect statistics about clans */
  1621.                 DebugWrite("Collecting clan statistics", 3);
  1622.                 Dictionary<string, int> clan_stats = new Dictionary<string, int>();
  1623.                 getClanStats(nosquad_pool, clan_stats);
  1624.                 foreach (PlayerSquad squad in squad_pool)
  1625.                     getClanStats(squad.getMembers(), clan_stats);
  1626.  
  1627.                
  1628.                 List<string> clanlist = new List<string>(clan_stats.Keys);
  1629.                 DebugWrite("^b" + clanlist.Count + "^n clans in server: [^b" + String.Join("^n], [^b", clanlist.ToArray()) + "^n]", 3);
  1630.                
  1631.                
  1632.                 int count = 1;
  1633.                 foreach (KeyValuePair<string, int> pair in clan_stats)
  1634.                 {
  1635.                     DebugWrite("    "+count+". clan [^b" + pair.Key + "^n] has ^b" + pair.Value + "^n member/s", 3);
  1636.                     count++;
  1637.                 }
  1638.                
  1639.                  
  1640.                 /* for clans with more than two players in game, create a new squad for them, and remove them from their current squads */
  1641.                 Dictionary<string, List<PlayerSquad>> clan_squads = new Dictionary<string, List<PlayerSquad>>();
  1642.                 DebugWrite("Creating clan squads from ^bno-squad^n pool", 3);
  1643.                 getClanSquads(nosquad_pool, clan_squads, clan_stats);
  1644.                 DebugWrite("Creating clan squads from ^bsquad-spool^n pool", 3);
  1645.                 foreach (PlayerSquad squad in squad_pool)
  1646.                     getClanSquads(squad.getMembers(), clan_squads, clan_stats);
  1647.  
  1648.                 /* remove the empty squads */
  1649.                 DebugWrite("Removing empty squads", 3);
  1650.                 squad_pool.RemoveAll(delegate(PlayerSquad squad) { return squad.getCount() == 0; });
  1651.  
  1652.  
  1653.                 /* add clan squads to the squad pool */
  1654.                 DebugWrite("Adding clan squads to the squad pool", 3);
  1655.                 foreach (KeyValuePair<string, List<PlayerSquad>> pair in clan_squads)
  1656.                     foreach (PlayerSquad squad in pair.Value)
  1657.                         squad_pool.Add(squad);
  1658.             }
  1659.  
  1660.             /* sort the no-squad pool */
  1661.             DebugWrite("Sorting the no-squad pool by ^b" + getStringVarValue("round_sort") + "^n^0", 3);
  1662.             nosquad_pool.Sort(new Comparison<PlayerProfile>(getPlayerSort("round_sort")));
  1663.            
  1664.             for (int i = 0; i < nosquad_pool.Count; i++)
  1665.             {
  1666.                 DebugWrite("      " + i + ". " + nosquad_pool[i].name + "(" + getSortFieldValueStr(nosquad_pool[i], "round_sort") + ")", 3);
  1667.             }
  1668.            
  1669.  
  1670.             /* sort the squad pool */
  1671.             DebugWrite("Sorting the squad pool by ^b" + getStringVarValue("round_sort") + "^n^0", 3);
  1672.             squad_pool.Sort(new Comparison<PlayerSquad>(getSquadSort("round_sort")));
  1673.            
  1674.             for (int i = 0; i < squad_pool.Count; i++)
  1675.             {
  1676.                 DebugWrite("      " + i + ". " + squad_pool[i].ToString() + "(" + getSortFieldValueStr(squad_pool[i], "round_sort") + ")", 3);
  1677.             }
  1678.            
  1679.             int[] teamCount = new int[3];
  1680.             teamCount[neutralTeamId] = sumSquadPlayers(squad_pool) + nosquad_pool.Count;
  1681.             teamCount[winTeamId] = 0;
  1682.             teamCount[loseTeamId] = 0;
  1683.  
  1684.             Dictionary<string, int> clanTeam = new Dictionary<string, int>();
  1685.  
  1686.             /* assume the smaller team */
  1687.             int smallTeamId = loseTeamId;
  1688.  
  1689.             DebugWrite("Moving ^b" + squad_pool.Count + "^n squads from neutral ^bTeam(" + neutralTeamId + ")^n into ^bTeam(" + winTeamId + ")^n and ^bTeam(" + loseTeamId + ")^n^0", 3);
  1690.  
  1691.             while (squad_pool.Count > 0)
  1692.             {
  1693.                 /* get the top squad */
  1694.                 PlayerSquad squad = squad_pool[0];
  1695.                 squad_pool.RemoveAt(0);
  1696.  
  1697.                 if (getBooleanVarValue("keep_clans") && squad.getSquadId() > 8)
  1698.                 {
  1699.                     string tag = squad.getClanTag();
  1700.                     if (clanTeam.ContainsKey(tag))
  1701.                         smallTeamId = clanTeam[tag];
  1702.                     else
  1703.                         clanTeam.Add(tag, smallTeamId);
  1704.                 }
  1705.  
  1706.                 int squad_sz = squad.getCount();
  1707.  
  1708.                 /* move top squad to the smaller team */
  1709.                 DebugWrite("Moving entire " + squad.ToString() + " to ^bTeam(" + smallTeamId + ")^n^0", 3);
  1710.                 moveSquad(squad, smallTeamId);
  1711.  
  1712.                 /* update the team counts */
  1713.                 teamCount[smallTeamId] += squad_sz;
  1714.                 teamCount[neutralTeamId] -= squad_sz;
  1715.  
  1716.                 /* determine the smaller team */
  1717.                 smallTeamId = getSmallTeamId(winTeamId, teamCount[winTeamId], loseTeamId, teamCount[loseTeamId]);
  1718.             }
  1719.  
  1720.             DebugWrite("^bTeam(" + winTeamId + ")^n has now ^b" + teamCount[winTeamId] + "^n player/s", 3);
  1721.             DebugWrite("^bTeam(" + loseTeamId + ")^n has now ^b" + teamCount[loseTeamId] + "^n player/s", 3);
  1722.  
  1723.             DebugWrite("Moving ^b" + nosquad_pool.Count + "^n player/s from neutral ^bTeam(" + neutralTeamId + ")^n into ^bTeam(" + winTeamId + ")^n and ^bTeam(" + loseTeamId + ")^n^0", 3);
  1724.            
  1725.             while (nosquad_pool.Count > 0)
  1726.             {
  1727.                 /* get the top player */
  1728.                 PlayerProfile player = nosquad_pool[0];
  1729.                 nosquad_pool.RemoveAt(0);
  1730.  
  1731.                 /* move the top player to the smaller team */
  1732.                 DebugWrite("Moving ^b" + player.ToString() + "^n to ^bTeam(^n" + smallTeamId + ")^n^0", 3);
  1733.                 movePlayer(player, smallTeamId, 0, true);
  1734.  
  1735.                 /* update the team counts */
  1736.                 teamCount[neutralTeamId] -= 1;
  1737.                 teamCount[smallTeamId] += 1;
  1738.  
  1739.                 /* determine the smaller team */
  1740.                 smallTeamId = getSmallTeamId(winTeamId, teamCount[winTeamId], loseTeamId, teamCount[loseTeamId]);
  1741.             }
  1742.  
  1743.  
  1744.             DebugWrite("^bTeam(" + winTeamId + ")^n has now ^b" + teamCount[winTeamId] + "^n player/s", 3);
  1745.             DebugWrite("^bTeam(" + loseTeamId + ")^n has now ^b" + teamCount[loseTeamId] + "^n player/s", 3);
  1746.  
  1747.             if (teamsUnbalanced())
  1748.             {
  1749.                 DebugWrite("Teams are still unbalanced, applying the live balancing logic", 3);
  1750.                 balanceLive(utc);
  1751.             }
  1752.             else
  1753.                 DebugWrite("Teams should now be balanced!", 3);
  1754.            
  1755.             virtual_mode = false;
  1756.  
  1757.  
  1758.             fixTeamSquads(winTeamId);
  1759.         }
  1760.  
  1761.         private void balanceLive(DateTime now)
  1762.         {
  1763.            
  1764.             balanceTeams(now);
  1765.            
  1766.             if (balanceTeams(now) > 0 && getBooleanVarValue("keep_clans"))
  1767.             {
  1768.                 DebugWrite("Re-running live balancer, with ^bkeep_clans^n disabled", 3);
  1769.                 setBooleanVarValue("keep_clans", false);
  1770.                 balanceTeams(now);
  1771.                 setBooleanVarValue("keep_clans", true);
  1772.             }
  1773.         }
  1774.  
  1775.        
  1776.  
  1777.         private void fixTeamSquads(int winTeamId)
  1778.         {
  1779.             List<PlayerProfile> players_list = getPlayersProfile("");
  1780.  
  1781.             /* remove the players that are not moving at all */
  1782.             players_list.RemoveAll(delegate(PlayerProfile p) { return p.getTargetTeamId() == p.getSavedTeamId() && p.getTargetSquadId() == p.getSavedSquadId(); });
  1783.  
  1784.             /* save everyone's target team and squad */
  1785.             players_list.ForEach(delegate(PlayerProfile p) { p.saveTargetTeamSquad(); });
  1786.  
  1787.             /* divide every one into teams  */
  1788.             List<PlayerProfile>[] steams = new List<PlayerProfile>[3];
  1789.            
  1790.             int loseTeamId = getOpposingTeamId(winTeamId);
  1791.             int neutralTeamId = 0;
  1792.             steams[neutralTeamId] = new List<PlayerProfile>();
  1793.             steams[winTeamId] = new List<PlayerProfile>();
  1794.             steams[loseTeamId] = new List<PlayerProfile>();
  1795.  
  1796.             foreach (PlayerProfile p in players_list)
  1797.                 steams[p.getSavedTeamId()].Add(p);
  1798.  
  1799.  
  1800.             /* put some players into the neutral team */
  1801.             int difference = Math.Abs(steams[winTeamId].Count - steams[loseTeamId].Count);
  1802.             difference = (difference == 0) ? 1 : difference;
  1803.  
  1804.             List<PlayerProfile> winNeutral = removePlayers(steams[winTeamId], difference);
  1805.             List<PlayerProfile> loseNeutral = removePlayers(steams[loseTeamId], difference);
  1806.             List<PlayerProfile> neutral = new List<PlayerProfile>();
  1807.             neutral.AddRange(winNeutral);
  1808.             neutral.AddRange(loseNeutral);
  1809.  
  1810.  
  1811.             DebugWrite("Moving ^b" + neutral.Count + "^n offset player/s  to neutral team", 3);
  1812.             neutral.ForEach(delegate(PlayerProfile p) { movePlayer(p, 0, 0, true); });
  1813.  
  1814.        
  1815.             DebugWrite("Moving ^b" + (steams[winTeamId].Count + steams[loseTeamId].Count) + "^n  player/s to no-squad", 3);
  1816.             for(int i = 1; i <= 2; i++)
  1817.                 steams[i].ForEach(delegate(PlayerProfile p) { if (p.getSavedSquadId() != 0) movePlayer(p, p.getSavedTeamId(), 0, true); });
  1818.  
  1819.  
  1820.             /* round robin start swapping players */
  1821.             DebugWrite("Swapping ^b" + (steams[winTeamId].Count + steams[loseTeamId].Count) + "^n player/s to target teams", 3);      
  1822.             while (steams[winTeamId].Count > 0 && steams[loseTeamId].Count > 0)
  1823.             {
  1824.                 int bigTeamId = getBigTeamId(winTeamId, steams[winTeamId].Count , loseTeamId, steams[loseTeamId].Count);
  1825.  
  1826.                 PlayerProfile p = steams[bigTeamId][0];
  1827.                 steams[bigTeamId].Remove(p);
  1828.                 movePlayer(p, p.getTargetTeamId(), p.getTargetSquadId(), true);
  1829.             }
  1830.  
  1831.             /* put back the players who are in the offset */
  1832.             DebugWrite("Moving ^b" + neutral.Count + "^n neutral player/s to target teams", 3);
  1833.             neutral.ForEach(delegate(PlayerProfile p) { movePlayer(p, p.getTargetTeamId(), p.getTargetSquadId(), true); });
  1834.    
  1835.         }
  1836.  
  1837.         private void getClanSquads(List<PlayerProfile> members, Dictionary<string, List<PlayerSquad>> clan_squads, Dictionary<string, int> clan_stats)
  1838.         {
  1839.             int neutralTeamId = 0;
  1840.             int noSquadId = 0;
  1841.             int max_squad_size = 4;
  1842.  
  1843.             members.RemoveAll(delegate(PlayerProfile player)
  1844.             {
  1845.                 /* if player is not in a clan, ignore it*/
  1846.                 if (!player.isInClan())
  1847.                     return false;
  1848.  
  1849.                 string tag = player.getClanTag();
  1850.  
  1851.                 /* if less than two players for the clan, ignore it */
  1852.                
  1853.                 if (clan_stats[tag] < 2)
  1854.                     return false;
  1855.                
  1856.  
  1857.                 if (!clan_squads.ContainsKey(tag))
  1858.                     clan_squads[tag] = new List<PlayerSquad>();
  1859.  
  1860.                 List<PlayerSquad> squads = clan_squads[tag];
  1861.  
  1862.                 /* if there is no squads, of they are all full, add a new at the end */
  1863.                 if (squads.Count == 0 || squads[squads.Count - 1].getCount() == max_squad_size)
  1864.                     squads.Add(new PlayerSquad(neutralTeamId, getNextFreeClanSquadId(clan_squads)));
  1865.  
  1866.                 /* add player to the last squad in the clan */
  1867.                 movePlayer(player, neutralTeamId, squads[squads.Count - 1].getSquadId(), true);
  1868.                 squads[squads.Count - 1].addPlayer(player);
  1869.  
  1870.                 return true;
  1871.             });
  1872.         }
  1873.  
  1874.         private int getNextFreeClanSquadId(Dictionary<string, List<PlayerSquad>> clan_squads)
  1875.         {
  1876.             int count = 0;
  1877.             foreach (KeyValuePair<string, List<PlayerSquad>> pair in clan_squads)
  1878.                 foreach (PlayerSquad squad in pair.Value)
  1879.                     count++;
  1880.  
  1881.             return 9 + count;
  1882.         }
  1883.  
  1884.         private void getClanStats(List<PlayerProfile> members, Dictionary<string, int> stats)
  1885.         {
  1886.             foreach (PlayerProfile player in members)
  1887.             {
  1888.                 if (!player.isInClan())
  1889.                     continue;
  1890.  
  1891.                 string tag = player.getClanTag();
  1892.  
  1893.                 if (!stats.ContainsKey(tag))
  1894.                     stats[tag] = 0;
  1895.  
  1896.                 stats[tag]++;
  1897.             }
  1898.         }
  1899.  
  1900.         private void getClanStatsByTeam(List<PlayerProfile> members,  Dictionary<string, int>[] stats)
  1901.         {
  1902.             for(int i = 0; i < members.Count; i++)
  1903.             {
  1904.                 PlayerProfile player = members[i];
  1905.                 if (!player.isInClan())
  1906.                     continue;
  1907.  
  1908.                 string tag = player.getClanTag();
  1909.                 int teamId = player.getTeamId();
  1910.  
  1911.                 if (!stats[teamId].ContainsKey(tag))
  1912.                 {
  1913.                     stats[teamId].Add(tag, 0);
  1914.                     stats[getOpposingTeamId(teamId)].Add(tag, 0);
  1915.                 }
  1916.  
  1917.                 stats[teamId][tag]++;
  1918.             }
  1919.         }
  1920.  
  1921.  
  1922.         private int getSmallTeamId(int team1Id, int team1Count, int team2Id, int team2Count)
  1923.         {
  1924.             return (team1Count < team2Count) ? team1Id : team2Id;
  1925.         }
  1926.  
  1927.         private int getBigTeamId(int team1Id, int team1Count, int team2Id, int team2Count)
  1928.         {
  1929.             return (team1Count > team2Count) ? team1Id : team2Id;
  1930.         }
  1931.  
  1932.         private int getOpposingTeamId(int teamId)
  1933.         {
  1934.             return (teamId == 0) ? teamId : (teamId == 1) ? 2 : 1;
  1935.         }
  1936.  
  1937.  
  1938.         private int balanceTeams(DateTime now)
  1939.         {
  1940.             pluginState = PluginState.balance;
  1941.             setStartTime(pluginState, now.AddSeconds(1));
  1942.             DebugWrite("^b" + pluginState + "_live^n state started " + getStartTime(pluginState).ToString() + "^n^0", 1);
  1943.  
  1944.             bool keep_clans = getBooleanVarValue("keep_clans");
  1945.             Dictionary<int, int> player_count = getPlayerCount();
  1946.  
  1947.             if (player_count[1] == player_count[2])
  1948.                 return 0;
  1949.  
  1950.             int neutral_team = 0;
  1951.             int bigger_team = (player_count[1] > player_count[2]) ? 1 : 2;
  1952.             int smaller_team = (player_count[1] > player_count[2]) ? 2 : 1;
  1953.             int total = player_count[1] + player_count[2];
  1954.             int difference = Math.Abs(player_count[1] - player_count[2]);
  1955.             int needed = difference / 2;
  1956.  
  1957.          
  1958.             DebugWrite("Total of ^b" + total + "^n player/s in server^0", 3);
  1959.             for (int i = 1; i < 3; i++)
  1960.             {
  1961.                 DebugWrite("^bTeam(" + i + ")^n has ^b" + player_count[i] + "^n player/s^0", 3);
  1962.             }
  1963.  
  1964.             DebugWrite("Teams differ by ^b" + difference + "^n player/s,  ^b" + needed + "^n player/s are needed on ^bTeam(" + smaller_team + ")^n^0", 3);
  1965.  
  1966.             DebugWrite("Building no-squad pool from ^bTeam(" + bigger_team + ")^n^0", 3);
  1967.             List<PlayerProfile> nosquad_pool = getNoSquadPlayers(bigger_team);
  1968.             DebugWrite("No-squad pool has ^b" + nosquad_pool.Count + "^n player/s^0", 3);
  1969.  
  1970.             DebugWrite("Building squad pool from ^bTeam(" + bigger_team + ")^n^0", 3);
  1971.             List<PlayerSquad> squad_pool = getNonEmptySquads(bigger_team);
  1972.             DebugWrite("Squad pool has ^b" + squad_pool.Count + "^n squads^0", 3);
  1973.  
  1974.  
  1975.             Dictionary<string, int>[] clan_stats = new Dictionary<string, int>[3];
  1976.             clan_stats[smaller_team] = new Dictionary<string, int>();
  1977.             clan_stats[bigger_team] = new Dictionary<string, int>();
  1978.             clan_stats[neutral_team] = new Dictionary<string, int>();
  1979.  
  1980.             if (keep_clans)
  1981.             {
  1982.                 DebugWrite("Keeping clans in same team", 3);
  1983.  
  1984.                 List<PlayerProfile> players_list = getPlayersProfile("");
  1985.                 /* collect statistics about clans */
  1986.                 DebugWrite("Collecting clan statistics", 3);
  1987.                 getClanStatsByTeam(players_list, clan_stats);
  1988.  
  1989.                
  1990.                 List<string> clanlist = new List<string>();
  1991.                 clanlist.AddRange(clan_stats[1].Keys);
  1992.                
  1993.                 DebugWrite("^b" + clanlist.Count + "^n clans in server: [^b" + String.Join("^n], [^b", clanlist.ToArray()) + "^n]", 3);
  1994.             }
  1995.  
  1996.             /* sort the no-squad pool */
  1997.             DebugWrite("Sorting the no-squad pool by ^b" + getStringVarValue("live_sort") + "^n^0", 3);
  1998.             nosquad_pool.Sort(new Comparison<PlayerProfile>(getPlayerSort("live_sort")));
  1999.            
  2000.             for (int i = 0; i < nosquad_pool.Count; i++)
  2001.             {
  2002.                 DebugWrite("      " + i + ". " + nosquad_pool[i].name + "(" + getSortFieldValueStr(nosquad_pool[i], "live_sort") + ")", 3);
  2003.             }
  2004.  
  2005.  
  2006.             DebugWrite("Moving ^b" + needed + "^n players from sorted no-squad pool to ^bTeam(" + smaller_team + ")^n^0", 3);
  2007.             while (needed > 0 && nosquad_pool.Count > 0)
  2008.             {
  2009.                 PlayerProfile player = nosquad_pool[0];
  2010.                 nosquad_pool.RemoveAt(0);
  2011.                 string tag = player.getClanTag();
  2012.  
  2013.                 /* if keeping clans together, and there are more than two players in the clan in the sever */
  2014.                 if (keep_clans)
  2015.                 {
  2016.                     if (shouldSkipClanPlayer(player, smaller_team, bigger_team, clan_stats))
  2017.                         continue;
  2018.                 }
  2019.  
  2020.                 DebugWrite("Moving ^b" + player.ToString() + "^n to ^bTeam(^n" + smaller_team + ")^n^0", 3);
  2021.                 movePlayer(player, smaller_team, 0, true);
  2022.                 needed--;
  2023.             }
  2024.  
  2025.             /* if teams are balanced, we are done */
  2026.             if (needed == 0)
  2027.             {
  2028.                 DebugWrite("Teams should now be balanced!", 3);
  2029.                 return needed;
  2030.             }
  2031.  
  2032.             /* teams are not balanced, proceed on squad balancing */
  2033.  
  2034.             DebugWrite("Teams are still unbalanced, " + needed + " more player/s needed", 3);
  2035.  
  2036.             /* sort the squad pool */
  2037.             DebugWrite("Sorting the squad pool by ^b" + getStringVarValue("live_sort") + "^n^0", 3);
  2038.             squad_pool.Sort(new Comparison<PlayerSquad>(getSquadSort("live_sort")));
  2039.            
  2040.             for (int i = 0; i < squad_pool.Count; i++)
  2041.             {
  2042.                 DebugWrite("      " + i + ". " + squad_pool[i].ToString() + "(" + getSortFieldValueStr(squad_pool[i], "live_sort") + ")", 3);
  2043.             }
  2044.  
  2045.             DebugWrite("Moving squads from sorted squad pool to ^bTeam(" + smaller_team + ")^n^0", 3);
  2046.             while (needed > 0 && squad_pool.Count > 0)
  2047.             {
  2048.                 PlayerSquad squad = squad_pool[0];
  2049.                 squad_pool.RemoveAt(0);
  2050.  
  2051.              
  2052.                 int squad_sz = squad.getCount();
  2053.                 string squad_uid = squad.ToString();
  2054.                 string smaller_team_uid = "^bTeam(" + smaller_team + ")^n";
  2055.  
  2056.                 DebugWrite("^b" + needed + "^n players are needed on " + smaller_team_uid + "^0", 3);
  2057.                 DebugWrite(squad_uid + " has ^b" + squad_sz + "^n player/s^0", 2);
  2058.  
  2059.                 if (needed >= squad_sz)
  2060.                 {
  2061.                     if (keep_clans)
  2062.                     {
  2063.                         if (shouldSkipClanSquad(squad, smaller_team, bigger_team, clan_stats))
  2064.                             continue;
  2065.                     }
  2066.  
  2067.                     /* we can move the entrie squad */
  2068.                     DebugWrite("Moving entire " + squad_uid + " to " + smaller_team_uid + "^0", 3);
  2069.                     moveSquad(squad, smaller_team);
  2070.                     needed -= squad_sz;
  2071.                 }
  2072.                 else
  2073.                 {
  2074.                     /* we have to break up a squad */
  2075.                     PlayerSquad temp_squad = new PlayerSquad(squad.getTeamId(), squad.getSquadId());
  2076.  
  2077.                     DebugWrite("Breaking up " + squad_uid + " to get ^b" + needed + "^n player/s^0", 3);
  2078.  
  2079.  
  2080.                     /* get as many players as needed */
  2081.                     while (needed > 0 && squad.getCount() > 0)
  2082.                     {
  2083.                         PlayerProfile player = squad.getRandomPlayer();
  2084.                         squad.dropPlayer(player);
  2085.                         if (shouldSkipClanPlayer(player, smaller_team, bigger_team, clan_stats))
  2086.                             continue;
  2087.                         temp_squad.addPlayer(player);
  2088.                         DebugWrite("Player " + player.name + " selected to move to " + smaller_team_uid + "^0", 3);
  2089.                         needed--;
  2090.                     }
  2091.  
  2092.                     /* move the temporary squad */
  2093.                     moveSquad(temp_squad, smaller_team);
  2094.                 }
  2095.             }
  2096.  
  2097.            
  2098.             if (needed == 0)
  2099.                 DebugWrite("Teams should now be balanced!", 3);
  2100.             else
  2101.                 DebugWrite("Teams are still ubalanced!", 3);
  2102.            
  2103.             return needed;
  2104.         }
  2105.  
  2106.         private bool shouldSkipClanSquad(PlayerSquad squad, int smaller_team, int bigger_team, Dictionary<string, int>[] clan_stats)
  2107.         {
  2108.             int squad_sz = squad.getCount();
  2109.             string tag = squad.getMajorityClanTag();
  2110.  
  2111.             if (tag.Length > 0 && (clan_stats[bigger_team][tag] + clan_stats[smaller_team][tag]) > 1)
  2112.             {
  2113.                 if (clan_stats[bigger_team][tag] >= clan_stats[smaller_team][tag])
  2114.                 {
  2115.                     DebugWrite("Skipping clan-squad " + squad.ToString() + " because majority of clan is in same team", 3);
  2116.                     return true;
  2117.                 }
  2118.  
  2119.                 /* update clan stats */
  2120.                 clan_stats[bigger_team][tag] -= squad_sz;
  2121.                 clan_stats[smaller_team][tag] += squad_sz;
  2122.             }
  2123.  
  2124.             return false;
  2125.         }
  2126.  
  2127.         private bool shouldSkipClanPlayer(PlayerProfile player, int smaller_team, int bigger_team, Dictionary<string, int>[] clan_stats)
  2128.         {
  2129.             string tag = player.getClanTag();
  2130.             if (player.isInClan() && (clan_stats[bigger_team][tag] + clan_stats[smaller_team][tag]) > 1)
  2131.             {
  2132.                 /* if the majority of the players in the clan are in this team, skip this player */
  2133.                 if (clan_stats[bigger_team][tag] >= clan_stats[smaller_team][tag])
  2134.                 {
  2135.                     DebugWrite("Skipping clan-player ^b" + player.name + "^n because majority of clan is in his team", 3);
  2136.                     return true;
  2137.                 }
  2138.  
  2139.                 /* update the clan stats */
  2140.                 clan_stats[bigger_team][tag]--;
  2141.                 clan_stats[smaller_team][tag]++;
  2142.             }
  2143.             return false;
  2144.         }
  2145.  
  2146.         private string getSortFieldValueStr(PlayerProfile player, string phase)
  2147.         {
  2148.             string sort_method = getStringVarValue(phase);
  2149.  
  2150.             if (sort_method.CompareTo("kdr_asc_round") == 0 || sort_method.CompareTo("kdr_desc_round") == 0)
  2151.                 return "kdr_round: " + Math.Round(player.getRoundKdr(), 2);
  2152.             else if (sort_method.CompareTo("score_asc_round") == 0 || sort_method.CompareTo("score_desc_round") == 0)
  2153.                 return "score_round: " + Math.Round(player.getRoundScore(), 2);
  2154.             else if (sort_method.CompareTo("spm_asc_round") == 0 || sort_method.CompareTo("spm_desc_round") == 0)
  2155.                 return "spm_round: " + Math.Round(player.getRoundSpm(), 2);
  2156.             else if (sort_method.CompareTo("kpm_asc_round") == 0 || sort_method.CompareTo("kpm_desc_round") == 0)
  2157.                 return "kpm_round: " + Math.Round(player.getRoundKpm(), 2);
  2158.  
  2159.              
  2160.             ConsoleWrite("^1cannot find player sort method for ^b" + sort_method + "^0");
  2161.             return "";
  2162.         }
  2163.  
  2164.         private string getSortFieldValueStr(PlayerSquad squad, string phase)
  2165.         {
  2166.             string sort_method = getStringVarValue(phase);
  2167.            
  2168.             if (sort_method.CompareTo("kdr_asc_round") == 0 || sort_method.CompareTo("kdr_desc_round") == 0)
  2169.                 return "kdr_round: " + Math.Round(squad.getRoundKdr(), 2);
  2170.             else if (sort_method.CompareTo("score_asc_round") == 0 || sort_method.CompareTo("score_desc_round") == 0)
  2171.                 return "score_round: " + Math.Round(squad.getRoundScore(), 2);
  2172.             else if (sort_method.CompareTo("spm_asc_round") == 0 || sort_method.CompareTo("spm_desc_round") == 0)
  2173.                 return "spm_round: " + Math.Round(squad.getRoundSpm(), 2);
  2174.             else if (sort_method.CompareTo("kpm_asc_round") == 0 || sort_method.CompareTo("kpm_desc_round") == 0)
  2175.                 return "kpm_round: " + Math.Round(squad.getRoundKpm(), 2);
  2176.  
  2177.             ConsoleWrite("^1cannot find squad sort method for ^b" + sort_method + "^0");
  2178.             return "";
  2179.         }
  2180.  
  2181.         private void movePlayer(PlayerProfile player, int teamId, int squadId)
  2182.         {
  2183.             movePlayer(player, teamId, squadId, false);
  2184.         }
  2185.  
  2186.         private void movePlayer(PlayerProfile player, int teamId, int squadId, bool force)
  2187.         {
  2188.             if (player == null)
  2189.                 return;
  2190.  
  2191.             if (!force && player.getTeamId() == teamId && player.getSquadId() == squadId)
  2192.             {
  2193.                 ConsoleWrite("^1not moving ^b" + player.name + "^n to same Team(" + teamId + ").Squad(" + SQN(squadId) + ")");
  2194.                 return;
  2195.             }
  2196.  
  2197.            
  2198.             /* firt move player to the no-squad, to guarantee a spot */
  2199.             if (squadId != 0 && player.getSquadId() != 0)
  2200.             {
  2201.                 if (!virtual_mode)
  2202.                     ExecCommand("admin.movePlayer", player.name, teamId.ToString(), "0", "true");
  2203.                 else
  2204.                 {
  2205.                     player.saveTeamSquad();
  2206.                 }
  2207.             }
  2208.  
  2209.             DebugWrite("moving ^b" + player.name + "^n to Team(" + teamId + ").Squad(" + SQN(squadId) + ")", 4);
  2210.  
  2211.  
  2212.             /* in virtual mode, don't actually do the move */
  2213.             if (!virtual_mode)
  2214.                 ExecCommand("admin.movePlayer", player.name, teamId.ToString(), squadId.ToString(), "true");
  2215.             /* instead save the original squad, and team id */
  2216.             else
  2217.                 player.saveTeamSquad();
  2218.  
  2219.             player.setTeamId(teamId);
  2220.             player.setSquadId(squadId);
  2221.         }
  2222.  
  2223.  
  2224.         /* best effort to move an entire squad into another team withouth breaking up */
  2225.  
  2226.         private void moveSquad(PlayerSquad squad, int teamId)
  2227.         {
  2228.             if (squad == null)
  2229.                 return;
  2230.  
  2231.             /* first move all players to the opposite team without squad (to guarantee a spot)*/
  2232.             int squadId = 0;
  2233.  
  2234.  
  2235.             List<PlayerProfile> squad_players = squad.getMembers();
  2236.  
  2237.             /* find a squad on teamId with enough space */
  2238.             List<PlayerSquad> squads = getAllSquads(teamId);
  2239.  
  2240.             /* sort the squads in increasing order of player count */
  2241.  
  2242.             squads.Sort(new Comparison<PlayerSquad>(squad_count_asc_cmp));
  2243.  
  2244.             for (int i = 0; i < squads.Count; i++)
  2245.             {
  2246.                 PlayerSquad sorted_squad = squads[i];
  2247.                 if (sorted_squad.getSquadId() > 8)
  2248.                     continue;
  2249.  
  2250.                 while (sorted_squad.getFreeSlots() > 0 && squad_players.Count > 0)
  2251.                 {
  2252.                     PlayerProfile squad_player = squad_players[0];
  2253.                     squad_players.RemoveAt(0);
  2254.                     movePlayer(squad_player, teamId, sorted_squad.getSquadId());
  2255.                 }
  2256.             }
  2257.  
  2258.             foreach (PlayerProfile squad_player in squad_players)
  2259.                 ConsoleWrite("^1could not find squad on ^bTeam(" + teamId + ")^n for ^b" + squad_player.name + "^n^0");
  2260.  
  2261.         }
  2262.  
  2263.         private List<PlayerProfile> removePlayers(List<PlayerProfile> player_list, int max_size)
  2264.         {
  2265.             if (players == null || players.Count == 0)
  2266.             {
  2267.                 ConsoleWrite("^1cannot make a squad without any players");
  2268.                 return null;
  2269.             }
  2270.  
  2271.             List<PlayerProfile> removed = new List<PlayerProfile>();
  2272.             while (player_list.Count > 0 && removed.Count <= max_size)
  2273.             {
  2274.                 removed.Add(player_list[0]);
  2275.                 player_list.RemoveAt(0);
  2276.             }
  2277.  
  2278.             return removed;
  2279.         }
  2280.  
  2281.      
  2282.         public Dictionary<int, int> getPlayerCount()
  2283.         {
  2284.             DebugWrite("^1ENTER: getPlayerCount", 11);
  2285.             /* initialize hash with player count for 16 teams*/
  2286.             Dictionary<int, int> player_count = new Dictionary<int, int>();
  2287.             for (int i = 0; i < 16; i++)
  2288.                 player_count[i] = 0;
  2289.  
  2290.             List<PlayerProfile> player_list = getPlayersProfile("");
  2291.  
  2292.             foreach (PlayerProfile player in player_list)
  2293.                 player_count[player.getTeamId()]++;
  2294.  
  2295.             DebugWrite("^1EXIT: getPlayerCount", 11);
  2296.             return player_count;
  2297.         }
  2298.  
  2299.  
  2300.  
  2301.         private List<PlayerProfile> getNoSquadPlayers(int teamId)
  2302.         {
  2303.             Dictionary<int, PlayerSquad> squads = getSquads(teamId);
  2304.             /* return the members of the no-squad */
  2305.             return squads[0].getMembers();
  2306.         }
  2307.  
  2308.  
  2309.         private List<PlayerSquad> getAllSquads(int teamId)
  2310.         {
  2311.             Dictionary<int, PlayerSquad> squads = getSquads(teamId);
  2312.  
  2313.             /* remove the no-squad */
  2314.             squads.Remove(0);
  2315.  
  2316.             List<PlayerSquad> list = new List<PlayerSquad>();
  2317.             foreach (KeyValuePair<int, PlayerSquad> pair in squads)
  2318.                 list.Add(pair.Value);
  2319.  
  2320.             return list;
  2321.         }
  2322.  
  2323.  
  2324.  
  2325.         private List<PlayerSquad> getNonEmptySquads(int teamId)
  2326.         {
  2327.             Dictionary<int, PlayerSquad> squads = getSquads(teamId);
  2328.  
  2329.             /* remove the no-squad */
  2330.             squads.Remove(0);
  2331.  
  2332.             /* get only the non-empty squads */
  2333.             List<PlayerSquad> list = new List<PlayerSquad>();
  2334.             foreach (KeyValuePair<int, PlayerSquad> pair in squads)
  2335.                 if (pair.Value.getCount() > 0)
  2336.                     list.Add(pair.Value);
  2337.  
  2338.             return list;
  2339.         }
  2340.  
  2341.  
  2342.         private Dictionary<int, PlayerSquad> getSquads(int teamId)
  2343.         {
  2344.  
  2345.             List<PlayerProfile> player_list = getPlayersProfile("");
  2346.  
  2347.             Dictionary<int, PlayerSquad> squads = new Dictionary<int, PlayerSquad>();
  2348.             for (int i = 0; i <= 32; i++)
  2349.                 squads[i] = new PlayerSquad(teamId, i);
  2350.  
  2351.             foreach (PlayerProfile player in player_list)
  2352.             {
  2353.                 if (player.getTeamId() == teamId)
  2354.                     squads[player.getSquadId()].addPlayer(player);
  2355.             }
  2356.  
  2357.             return squads;
  2358.         }
  2359.  
  2360.         private bool isTimeLeft(int remain_time, int msg_display_time, int msg_interval_time, int countdown_time)
  2361.         {
  2362.             return (remain_time % msg_interval_time) == 0 && (remain_time - msg_display_time) >= countdown_time;
  2363.         }
  2364.        
  2365.         public bool isServerEmpty()
  2366.         {
  2367.             return players.Count == 0;
  2368.         }
  2369.  
  2370.         public void forwardTicks(int count)
  2371.         {
  2372.             utc = utc.AddSeconds(count);
  2373.         }
  2374.  
  2375.  
  2376.         public void ConsoleWrite(string msg)
  2377.         {
  2378.             string prefix = "[^b" + GetPluginName() + "^n] ";
  2379.             this.ExecuteCommand("procon.protected.pluginconsole.write", prefix + msg);
  2380.         }
  2381.  
  2382.         public void DebugWrite(string msg, int level)
  2383.         {
  2384.             if (getIntegerVarValue("debug_level") >= level)
  2385.                 ConsoleWrite(msg);
  2386.         }
  2387.  
  2388.         public void OnPluginDisable()
  2389.         {
  2390.             ConsoleWrite("^b^1Disabled =(^0");
  2391.  
  2392.             plugin_enabled = false;
  2393.  
  2394.             unloadSettings();
  2395.             removeTask("InsaneBalancer");
  2396.             players.Clear();
  2397.         }
  2398.  
  2399.         public List<CPluginVariable> GetDisplayPluginVariables()
  2400.         {
  2401.             List<CPluginVariable> lstReturn = new List<CPluginVariable>();
  2402.  
  2403.             lstReturn.Add(new CPluginVariable("Settings|Refresh", typeof(string), "| edit this field to refresh settings |"));
  2404.  
  2405.             List<string> vars = getPluginVars(true);
  2406.             foreach (string var in vars)
  2407.             {
  2408.                 if (var.Equals("live_sort") || var.Equals("round_sort"))
  2409.                 {
  2410.  
  2411.  
  2412.                     lstReturn.Add(new CPluginVariable("Settings|" + var, "enum."+var+"("+String.Join("|",getAllowedSorts().ToArray())+")", getPluginVarValue(var)));
  2413.                     continue;
  2414.                 }
  2415.  
  2416.                 lstReturn.Add(new CPluginVariable("Settings|" + var, typeof(string), getPluginVarValue(var)));
  2417.             }
  2418.  
  2419.             return lstReturn;
  2420.         }
  2421.  
  2422.         //Lists all of the plugin variables.
  2423.         public List<CPluginVariable> GetPluginVariables()
  2424.         {
  2425.             List<CPluginVariable> lstReturn = new List<CPluginVariable>();
  2426.  
  2427.             List<string> vars = getPluginVars();
  2428.             foreach (string var in vars)
  2429.                 lstReturn.Add(new CPluginVariable(var, typeof(string), getPluginVarValue(var)));
  2430.  
  2431.             return lstReturn;
  2432.         }
  2433.  
  2434.  
  2435.         public void SetPluginVariable(string strVariable, string strValue)
  2436.         {
  2437.             //ConsoleWrite("setting " + strVariable + " to " + strValue);
  2438.             if (strVariable.ToLower().Contains("refresh"))
  2439.                 return;
  2440.             setPluginVarValue(strVariable, strValue);
  2441.         }
  2442.  
  2443.  
  2444.         public void OnPlayerJoin(string strSoldierName)
  2445.         {
  2446.  
  2447.             //if (!this.players.ContainsKey(strSoldierName))
  2448.             //    this.players.Add(strSoldierName, new PlayerProfile(this, strSoldierName));
  2449.         }
  2450.  
  2451.  
  2452.         public void OnPlayerLeft(string strSoldierName)
  2453.         {
  2454.             PlayerProfile player = getPlayerProfile(strSoldierName);
  2455.             if (player != null)
  2456.             {
  2457.                 player.leaveGame();
  2458.                 this.players.Remove(player.name);
  2459.             }
  2460.         }
  2461.  
  2462.         public void OnGlobalChat(string strSpeaker, string strMessage)
  2463.         {
  2464.             if (isInGameCommand(strMessage))
  2465.                 inGameCommand(strSpeaker, strMessage);
  2466.         }
  2467.  
  2468.  
  2469.         public void OnTeamChat(string strSpeaker, string strMessage, int iTeamID)
  2470.         {
  2471.             if (isInGameCommand(strMessage))
  2472.                 inGameCommand(strSpeaker, strMessage);
  2473.         }
  2474.  
  2475.  
  2476.         public void OnSquadChat(string strSpeaker, string strMessage, int iTeamID, int iSquadID)
  2477.         {
  2478.             if (isInGameCommand(strMessage))
  2479.                 inGameCommand(strSpeaker, strMessage);
  2480.         }
  2481.  
  2482.  
  2483.         private bool isInGameCommand(string str)
  2484.         {
  2485.             if (Regex.Match(str, @"^\s*[@/!]").Success)
  2486.                 return true;
  2487.  
  2488.             return false;
  2489.         }
  2490.  
  2491.         public void OnLevelStarted()
  2492.         {
  2493.             DebugWrite("Level starting!", 3);
  2494.             level_started = true;
  2495.         }
  2496.  
  2497.  
  2498.         public void OnPunkbusterplayerStatsCmd(CPunkbusterInfo cpbiPlayer)
  2499.         {
  2500.  
  2501.             if (cpbiPlayer != null)
  2502.             {
  2503.                 if (this.players.ContainsKey(cpbiPlayer.SoldierName))
  2504.                     this.players[cpbiPlayer.SoldierName].pbinfo = cpbiPlayer;
  2505.                 else
  2506.                     this.players.Add(cpbiPlayer.SoldierName, new PlayerProfile(this, cpbiPlayer));
  2507.             }
  2508.         }
  2509.  
  2510.  
  2511.  
  2512.         public void OnServerInfo(CServerInfo csiServerInfo)
  2513.         {
  2514.             this.serverInfo = csiServerInfo;
  2515.         }
  2516.  
  2517.         public void OnPlayerTeamChange(string strSoldierName, int iTeamID, int iSquadID)
  2518.         {
  2519.             PlayerProfile player = getPlayerProfile(strSoldierName);
  2520.             if (player == null)
  2521.                 return;
  2522.  
  2523.             player.setSquadId(iSquadID);
  2524.             player.setTeamId(iTeamID);
  2525.  
  2526.         }
  2527.  
  2528.         public void OnPlayerSquadChange(string strSoldierName, int iTeamID, int iSquadID)
  2529.         {
  2530.             PlayerProfile player = getPlayerProfile(strSoldierName);
  2531.             if (player == null)
  2532.                 return;
  2533.  
  2534.             player.setSquadId(iSquadID);
  2535.             player.setTeamId(iTeamID);
  2536.         }
  2537.  
  2538.         public void OnplayersStatsCmd(List<CPlayerInfo> lstPlayers, CPlayerSubset cpsSubset)
  2539.         {
  2540.  
  2541.         /*    if (cpsSubset.Subset == CPlayerSubset.PlayerSubsetType.All)
  2542.             {
  2543.                 foreach (CPlayerInfo cpiPlayer in lstPlayers)
  2544.                 {
  2545.                     if (this.players.ContainsKey(cpiPlayer.SoldierName))
  2546.                         this.players[cpiPlayer.SoldierName].updateInfo(cpiPlayer);
  2547.                     else
  2548.                         this.players.Add(cpiPlayer.SoldierName, new PlayerProfile(this, cpiPlayer));
  2549.                 }
  2550.             }
  2551.          */
  2552.         }
  2553.  
  2554.  
  2555.         public bool checkRoundBalance()
  2556.         {
  2557.             int round_interval = this.getIntegerVarValue("round_interval");
  2558.             int round_total = serverInfo.TotalRounds;
  2559.             int round_current = serverInfo.CurrentRound+1;
  2560.             string map = serverInfo.Map.ToLower();
  2561.  
  2562.             if (round_interval > round_total)
  2563.             {
  2564.                 ConsoleWrite("^1^bround_interval(" + round_interval + ")^n is greater than total ^brounds(" + round_total + ")^n for ^bmap(" + map + ")^n^0");
  2565.                 ConsoleWrite("setting ^bround_interval^n to ^b" + round_total + "^n internally for ^bmap(" + map + ")^n^0");
  2566.                 round_interval = round_total;
  2567.             }
  2568.  
  2569.  
  2570.             ConsoleWrite("End of round detected");
  2571.             ConsoleWrite("Current round is ^b" + round_current + "^n/^b" + round_total + "^n,");
  2572.             ConsoleWrite("Round balance interval is ^b" + round_interval + "^n^0");
  2573.  
  2574.             if (round_current % round_interval == 0)
  2575.                 return true;
  2576.  
  2577.             return false;
  2578.         }
  2579.  
  2580.         public static string SQN(int squadNo)
  2581.         {
  2582.  
  2583.             switch (squadNo)
  2584.             {
  2585.                 case 0:
  2586.                     return "Neutral";
  2587.                 case 1:
  2588.                     return "Alpha";
  2589.                 case 2:
  2590.                     return "Bravo";
  2591.                 case 3:
  2592.                     return "Charlie";
  2593.                 case 4:
  2594.                     return "Delta";
  2595.                 case 5:
  2596.                     return "Echo";
  2597.                 case 6:
  2598.                     return "Foxtrot";
  2599.                 case 7:
  2600.                     return "Golf";
  2601.                 case 8:
  2602.                     return "Hotel";
  2603.  
  2604.                 default:
  2605.                     if (squadNo > 8 && squadNo <= 32)
  2606.                         return "Clan-" + squadNo;
  2607.                     else
  2608.                         return "Unknown";
  2609.             }
  2610.         }
  2611.  
  2612.         public double getRoundMinutes()
  2613.         {
  2614.             return utc.Subtract(startRoundTime).TotalMinutes;
  2615.         }
  2616.  
  2617.         public void OnRoundOver(int iWinningTeamID)
  2618.         {
  2619.             run_balancer = checkRoundBalance();
  2620.             win_teamId = iWinningTeamID;
  2621.             lose_teamId = getOpposingTeamId(win_teamId);
  2622.             level_started = true;
  2623.            
  2624.         }
  2625.  
  2626.         public override void OnPlayerSpawned(string soldierName, Inventory spawnedInventory)
  2627.         {
  2628.             /*
  2629.  
  2630.             if (level_started)
  2631.             {
  2632.                 if (run_balancer)
  2633.                 {
  2634.                     run_balancer = false;
  2635.                     int seconds = 10;
  2636.                     SendGlobalMessage("Team scrambler will start in " + seconds + " seconds");
  2637.                     Thread.Sleep(10*1000);
  2638.                     SendGlobalMessage("Scramling teams now.");
  2639.                     //showPlayerRoundStatsCmd("micovery", null);
  2640.                     balanceRound(win_teamId);
  2641.                     restartWaitState(utc);
  2642.                     resetPlayerStats();
  2643.                     //showPlayerRoundStatsCmd("micovery", null);
  2644.  
  2645.                 }
  2646.                 startRoundTime = utc;
  2647.                 level_started = false;
  2648.             }
  2649.  
  2650.             */
  2651.            
  2652.  
  2653.  
  2654.             PlayerProfile player = getPlayerProfile(soldierName);
  2655.             if (player != null)
  2656.                 player.spawned();
  2657.            
  2658.         }
  2659.  
  2660.         private void resetPlayerStats()
  2661.         {
  2662.             DebugWrite("^1ENTER: resetPlayerStats", 7);
  2663.             List<PlayerProfile> players_list = getPlayersProfile("");
  2664.             foreach (PlayerProfile player in players_list)
  2665.             {
  2666.                 player.resetStats();
  2667.             }
  2668.             DebugWrite("^1EXIT: resetPlayerStats", 7);
  2669.         }
  2670.  
  2671.  
  2672.  
  2673.         private void SendPlayerMessage(string soldierName, string message)
  2674.         {
  2675.             if (getBooleanVarValue("quiet_mode") && !isAdmin(soldierName))
  2676.                 return;
  2677.  
  2678.             ExecCommand("admin.say", message, "player", soldierName);
  2679.         }
  2680.  
  2681.  
  2682.         private void SendGlobalMessage(string message)
  2683.         {
  2684.             if (getBooleanVarValue("quiet_mode"))
  2685.                 SendConsoleMessage(message);
  2686.             else
  2687.                 ExecCommand("admin.say", message, "all");
  2688.  
  2689.         }
  2690.  
  2691.         private void SendConsoleMessage(string name, string msg)
  2692.         {
  2693.             List<string> admin_list = getStringListVarValue("admin_list");
  2694.             ConsoleWrite(msg);
  2695.  
  2696.             msg = Regex.Replace(msg, @"\^[0-9a-zA-Z]", "");
  2697.  
  2698.             if (name != null)
  2699.                 SendPlayerMessage(name, msg);
  2700.             else
  2701.                 SendConsoleMessage(msg);
  2702.         }
  2703.  
  2704.         private void SendConsoleMessage(string msg)
  2705.         {
  2706.             List<string> admin_list = getStringListVarValue("admin_list");
  2707.             ConsoleWrite(msg);
  2708.  
  2709.             msg = Regex.Replace(msg, @"\^[0-9a-zA-Z]", "");
  2710.  
  2711.             foreach (string name in admin_list)
  2712.             {
  2713.                 PlayerProfile pp = this.getPlayerProfile(name);
  2714.                 if (pp != null)
  2715.                 {
  2716.                     SendPlayerMessage(pp.name, msg);
  2717.                 }
  2718.             }
  2719.         }
  2720.  
  2721.  
  2722.         private void inGameCommand(string sender, string cmd)
  2723.         {
  2724.  
  2725.            
  2726.             //Player commands
  2727.             Match adminMovePlayerMatch = Regex.Match(cmd, @"\s*[!@/]\s*move\s+([^ ]+)", RegexOptions.IgnoreCase);
  2728.             Match movePlayerMatch = Regex.Match(cmd, @"\s*[!@/]\s*move", RegexOptions.IgnoreCase);
  2729.  
  2730.             Match showPlayerRoundStatsMatch = Regex.Match(cmd, @"\s*[!@/]\s*show\s+round\s+stats\s+([^ ]+)", RegexOptions.IgnoreCase);
  2731.             Match showRoundStatsMatch = Regex.Match(cmd, @"\s*[!@/]\s*show\s+round\s+stats", RegexOptions.IgnoreCase);
  2732.            
  2733.  
  2734.             Match stopBalancerMatch = Regex.Match(cmd, @"\s*[!@/]\s*stop\s+check", RegexOptions.IgnoreCase);
  2735.             Match startBalancerMatch = Regex.Match(cmd, @"\s*[!@/]\s*start\s+check", RegexOptions.IgnoreCase);
  2736.             Match balanceLiveMatch = Regex.Match(cmd, @"\s*[!@/]\s*balance\s+live", RegexOptions.IgnoreCase);
  2737.             Match balanceRoundMatch = Regex.Match(cmd, @"\s*[!@/]\s*balance\s+round", RegexOptions.IgnoreCase);
  2738.  
  2739.  
  2740.  
  2741.             //Setting/Getting variables
  2742.             Match setVarValueMatch = Regex.Match(cmd, @"\s*[!@/]\s*set\s+([^ ]+)\s+(.+)", RegexOptions.IgnoreCase);
  2743.             Match setVarValueEqMatch = Regex.Match(cmd, @"\s*[!@/]\s*set\s+([^ ]+)\s*=\s*(.+)", RegexOptions.IgnoreCase);
  2744.             Match setVarValueToMatch = Regex.Match(cmd, @"\s*[!@/]\s*set\s+([^ ]+)\s+to\s+(.+)", RegexOptions.IgnoreCase);
  2745.             Match setVarTrueMatch = Regex.Match(cmd, @"\s*[!@/]\s*set\s+([^ ]+)", RegexOptions.IgnoreCase);
  2746.             Match getVarValueMatch = Regex.Match(cmd, @"\s*[!@/]\s*get\s+([^ ]+)", RegexOptions.IgnoreCase);
  2747.             Match enableMatch = Regex.Match(cmd, @"\s*[!@/]\s*enable\s+(.+)", RegexOptions.IgnoreCase);
  2748.             Match disableMatch = Regex.Match(cmd, @"\s*[!@/]\s*disable\s+(.+)", RegexOptions.IgnoreCase);
  2749.  
  2750.             //Information
  2751.             Match pluginSettingsMatch = Regex.Match(cmd, @"\s*[!@/]\s*settings", RegexOptions.IgnoreCase);
  2752.  
  2753.  
  2754.             bool senderIsAdmin = isAdmin(sender);
  2755.  
  2756.  
  2757.             DateTime now = utc;
  2758.             if (startBalancerMatch.Success && senderIsAdmin)
  2759.                 startBalancerCmd(sender, now);
  2760.             else if (stopBalancerMatch.Success && senderIsAdmin)
  2761.                 stopBalancerCmd(sender, now);
  2762.             else if (showPlayerRoundStatsMatch.Success && senderIsAdmin)
  2763.                 showPlayerRoundStatsCmd(sender, showPlayerRoundStatsMatch.Groups[1].Value);
  2764.             else if (showRoundStatsMatch.Success && senderIsAdmin)
  2765.                 showPlayerRoundStatsCmd(sender, null);
  2766.             else if (balanceLiveMatch.Success && senderIsAdmin)
  2767.                 balanceLiveCmd(sender, now);
  2768.             else if (balanceRoundMatch.Success && senderIsAdmin)
  2769.                 balanceRoundCmd(sender, now);
  2770.             else if (adminMovePlayerMatch.Success && senderIsAdmin)
  2771.                 movePlayerCmd(sender, adminMovePlayerMatch.Groups[1].Value);
  2772.             else if (movePlayerMatch.Success)
  2773.                 movePlayerCmd(sender);
  2774.             else if (setVarValueEqMatch.Success && senderIsAdmin)
  2775.                 setVariableCmd(sender, setVarValueEqMatch.Groups[1].Value, setVarValueEqMatch.Groups[2].Value);
  2776.             else if (setVarValueToMatch.Success && senderIsAdmin)
  2777.                 setVariableCmd(sender, setVarValueToMatch.Groups[1].Value, setVarValueToMatch.Groups[2].Value);
  2778.             else if (setVarValueMatch.Success && senderIsAdmin)
  2779.                 setVariableCmd(sender, setVarValueMatch.Groups[1].Value, setVarValueMatch.Groups[2].Value);
  2780.             else if (setVarTrueMatch.Success && senderIsAdmin)
  2781.                 setVariableCmd(sender, setVarTrueMatch.Groups[1].Value, "1");
  2782.             else if (getVarValueMatch.Success && senderIsAdmin)
  2783.                 getVariableCmd(sender, getVarValueMatch.Groups[1].Value);
  2784.             else if (enableMatch.Success && senderIsAdmin)
  2785.                 enableVarGroupCmd(sender, enableMatch.Groups[1].Value);
  2786.             else if (disableMatch.Success && senderIsAdmin)
  2787.                 disableVarGroupCmd(sender, disableMatch.Groups[1].Value);
  2788.             else if (pluginSettingsMatch.Success && senderIsAdmin)
  2789.                 pluginSettingsCmd(sender);
  2790.         }
  2791.  
  2792.  
  2793.  
  2794.  
  2795.  
  2796.         private string state2strWHILE(PluginState state)
  2797.         {
  2798.             if (state.Equals(PluginState.balance))
  2799.             {
  2800.                 return "balancing";
  2801.             }
  2802.             else if (state.Equals(PluginState.check))
  2803.             {
  2804.                 return "checking";
  2805.             }
  2806.             else if (state.Equals(PluginState.warn))
  2807.             {
  2808.                 return "warning";
  2809.             }
  2810.             else if (state.Equals(PluginState.stop))
  2811.             {
  2812.                 return "stopped";
  2813.             }
  2814.             else if (state.Equals(PluginState.wait))
  2815.             {
  2816.                 return "waiting";
  2817.             }
  2818.  
  2819.             return "unknown state";
  2820.         }
  2821.  
  2822.  
  2823.  
  2824.         private void initializeBalancer()
  2825.         {
  2826.             getServerInfo();
  2827.             startStopState(utc);
  2828.         }
  2829.  
  2830.  
  2831.         private void movePlayerCmd(string sender)
  2832.         {
  2833.             if (teamsUnbalanced())
  2834.             {
  2835.                 SendConsoleMessage(sender, "Teams are un-balanced, cannot move to other team");
  2836.                 return;
  2837.             }
  2838.  
  2839.             movePlayerCmd(sender, null);
  2840.         }
  2841.  
  2842.         private void movePlayerCmd(string sender, string player)
  2843.         {
  2844.             /* player is moving himself */
  2845.             if (player == null)
  2846.                 player = sender;
  2847.  
  2848.             SendConsoleMessage(sender, "moving player " + player);
  2849.  
  2850.             PlayerProfile profile = getPlayerProfile(player);
  2851.  
  2852.             if (profile == null)
  2853.             {
  2854.                 SendConsoleMessage(sender, "cannot find profile for " + player);
  2855.                 return;
  2856.             }
  2857.  
  2858.             if (profile.getTeamId() == 0)
  2859.             {
  2860.                 SendConsoleMessage(sender, "cannot move " + player + " from neutral team");
  2861.                 return;
  2862.             }
  2863.  
  2864.             int opposite = (profile.getTeamId() == 1) ? 2 : 1;
  2865.             movePlayer(profile, opposite, 0, false);
  2866.         }
  2867.  
  2868.  
  2869.         private void startWaitSate(DateTime now)
  2870.         {
  2871.             startWaitSate(null, now);
  2872.         }
  2873.  
  2874.  
  2875.  
  2876.         private void startWarnState(DateTime now)
  2877.         {
  2878.             startWarnState(null, now);
  2879.         }
  2880.  
  2881.         private void startWarnState(string sender, DateTime now)
  2882.         {
  2883.             if (!isPluginChecking())
  2884.             {
  2885.                 SendConsoleMessage(sender, "plugin is in " + pluginState.ToString() + " state");
  2886.                 return;
  2887.             }
  2888.  
  2889.             pluginState = PluginState.warn;
  2890.             setStartTime(pluginState, now.AddSeconds(1));
  2891.  
  2892.             DebugWrite("^b" + pluginState + "^n state started " + getStartTime(pluginState).ToString(), 1);
  2893.         }
  2894.  
  2895.         private void startWaitSate(string sender, DateTime now)
  2896.         {
  2897.  
  2898.             if (!isPluginStopped())
  2899.             {
  2900.                 SendConsoleMessage(sender, "cannot start while balancer is in " + pluginState.ToString() + " state");
  2901.                 return;
  2902.             }
  2903.  
  2904.             if (sender != null)
  2905.                 setPluginVarValue("auto_start", "true");
  2906.  
  2907.  
  2908.             pluginState = PluginState.wait;
  2909.             setStartTime(pluginState, now.AddSeconds(1));
  2910.  
  2911.             SendConsoleMessage(sender, "^b" + pluginState + "^n state started " + getStartTime(pluginState).ToString());
  2912.         }
  2913.  
  2914.         private void startStopState(DateTime now)
  2915.         {
  2916.             startStopState(null, now);
  2917.         }
  2918.  
  2919.         private void startStopState(string sender, DateTime now)
  2920.         {
  2921.  
  2922.             virtual_mode = false;
  2923.             run_balancer = false;
  2924.             level_started = false;
  2925.             check_state_phase = 0;
  2926.  
  2927.             if (sender != null)
  2928.                 setPluginVarValue("auto_start", "false");
  2929.  
  2930.             pluginState = PluginState.stop;
  2931.             setStartTime(pluginState, now.AddSeconds(1));
  2932.  
  2933.             SendConsoleMessage(sender, "^b" + pluginState + "^n state started " + getStartTime(pluginState).ToString());
  2934.         }
  2935.  
  2936.  
  2937.         public void balanceLiveCmd(string sender, DateTime now)
  2938.         {
  2939.  
  2940.             balanceLive(now);
  2941.             restartWaitState(now);
  2942.         }
  2943.  
  2944.         public void balanceRoundCmd(string sender, DateTime now)
  2945.         {
  2946.  
  2947.             balanceRound(1);
  2948.             restartWaitState(now);
  2949.         }
  2950.  
  2951.  
  2952.  
  2953.  
  2954.  
  2955.         public void showPlayerRoundStatsCmd(string sender, string player_name)
  2956.         {
  2957.             List<PlayerProfile> players_list;
  2958.             if (player_name == null)
  2959.                 players_list = getPlayersProfile("");
  2960.             else
  2961.                 players_list = getPlayersProfile(player_name);
  2962.  
  2963.  
  2964.             if (players_list.Count == 0)
  2965.                 return;
  2966.  
  2967.             SendConsoleMessage(sender, " == Round Statistics ( "+Math.Round(getRoundMinutes(), 2)+" minutes) ==");
  2968.             foreach (PlayerProfile player in players_list)
  2969.             {
  2970.                 SendConsoleMessage(sender, player.name + ": " + player.getRoundStatistics());
  2971.             }
  2972.         }
  2973.  
  2974.         private void startBalancerCmd(string sender, DateTime now)
  2975.         {
  2976.             setBooleanVarValue("balance_live", true);
  2977.             startWaitSate(now);
  2978.         }
  2979.  
  2980.         private void stopBalancerCmd(string sender, DateTime now)
  2981.         {
  2982.             setBooleanVarValue("balance_live", false);
  2983.             startStopState(now);
  2984.         }
  2985.  
  2986.         private void restartWaitState(DateTime now)
  2987.         {
  2988.             pluginState = PluginState.wait;
  2989.             setStartTime(pluginState, now.AddSeconds(1));
  2990.             DebugWrite("^b" + pluginState.ToString() + "^n state re-started " + getStartTime(pluginState).ToString() + "^0", 1);
  2991.         }
  2992.  
  2993.  
  2994.  
  2995.         private void genericSayAnnounce(List<string> messages)
  2996.         {
  2997.             if (messages.Count == 0)
  2998.                 return;
  2999.  
  3000.             int remain_time = getRemainingTime(utc, pluginState);
  3001.  
  3002.             for (int i = 0; i < messages.Count; i++)
  3003.             {
  3004.                 string msg = messages[i];
  3005.                 msg = msg.Replace("%time%", remain_time.ToString());
  3006.                 SendGlobalMessage(msg);
  3007.             }
  3008.  
  3009.         }
  3010.  
  3011.  
  3012.  
  3013.         private void warnAnnounce(int display_time)
  3014.         {
  3015.             if (!isPluginWarning())
  3016.                 return;
  3017.  
  3018.             DebugWrite("sending ^b" + pluginState.ToString() + "^n announcement", 1);
  3019.  
  3020.             List<string> msg = new List<string>();
  3021.             msg.Add("Teams are unbalanced");
  3022.             msg.Add("Autobalancer starts in %time% secs");
  3023.  
  3024.             if (getBooleanVarValue("warn_say"))
  3025.                 genericSayAnnounce(msg);
  3026.         }
  3027.  
  3028.  
  3029.         private void warnCountdown()
  3030.         {
  3031.             if (!isPluginWarning())
  3032.                 return;
  3033.  
  3034.             DebugWrite("sending ^b" + pluginState.ToString() + "^n countdown", 1);
  3035.  
  3036.             int remain_time = getRemainingTime(utc, PluginState.warn);
  3037.             string msg = "Autobalancer starts in " + remain_time.ToString() + "!";
  3038.  
  3039.             if (getBooleanVarValue("warn_say"))
  3040.                 SendGlobalMessage(msg);
  3041.         }
  3042.  
  3043.         private void enableVarGroupCmd(string sender, string group)
  3044.         {
  3045.             if (group.CompareTo("plugin") == 0)
  3046.             {
  3047.                 ConsoleWrite("Disabling plugin");
  3048.                 this.ExecuteCommand("procon.plugin.enable", "InsaneBalancer", "false");
  3049.             }
  3050.             enablePluginVarGroup(sender, group);
  3051.         }
  3052.  
  3053.         private void disableVarGroupCmd(string sender, string group)
  3054.         {
  3055.             if (group.CompareTo("plugin") == 0)
  3056.             {
  3057.                 ConsoleWrite("Enabling plugin");
  3058.                 this.ExecuteCommand("procon.plugin.enable", "InsaneBalancer", "true");
  3059.             }
  3060.  
  3061.             disablePluginVarGroup(sender, group);
  3062.         }
  3063.  
  3064.         private bool setPluginVarGroup(string sender, string group, string val)
  3065.         {
  3066.             if (group == null)
  3067.             {
  3068.                 SendPlayerMessage(sender, "no variables to enable");
  3069.                 return false;
  3070.             }
  3071.  
  3072.  
  3073.             group = group.Replace(";", ",");
  3074.             List<string> vars = new List<string>(Regex.Split(group, @"\s*,\s*", RegexOptions.IgnoreCase));
  3075.             foreach (string var in vars)
  3076.             {
  3077.                 if (setPluginVarValue(sender, var, val))
  3078.                     SendPlayerMessage(sender, var + " set to \"" + val + "\"");
  3079.  
  3080.             }
  3081.             return true;
  3082.         }
  3083.  
  3084.         private bool enablePluginVarGroup(string sender, string group)
  3085.         {
  3086.             //search for all variables matching
  3087.             List<string> vars = getVariableNames(group);
  3088.             if (vars.Count == 0)
  3089.             {
  3090.                 SendPlayerMessage(sender, "no variables match \"" + group + "\"");
  3091.                 return false;
  3092.             }
  3093.  
  3094.             return setPluginVarGroup(sender, String.Join(",", vars.ToArray()), "true");
  3095.         }
  3096.  
  3097.         private List<string> getVariableNames(string group)
  3098.         {
  3099.             List<string> names = new List<string>();
  3100.             List<string> list = new List<string>(Regex.Split(group, @"\s*,\s*"));
  3101.             List<string> vars = getPluginVars();
  3102.             foreach (string search in list)
  3103.             {
  3104.                 foreach (string var in vars)
  3105.                 {
  3106.                     if (var.Contains(search))
  3107.                         if (!names.Contains(var))
  3108.                             names.Add(var);
  3109.                 }
  3110.             }
  3111.  
  3112.             return names;
  3113.         }
  3114.  
  3115.         private bool disablePluginVarGroup(string sender, string group)
  3116.         {
  3117.             //search for all variables matching
  3118.             List<string> vars = getVariableNames(group);
  3119.             if (vars.Count == 0)
  3120.             {
  3121.                 SendConsoleMessage(sender, "no variables match \"" + group + "\"");
  3122.                 return false;
  3123.             }
  3124.             return setPluginVarGroup(sender, String.Join(",", vars.ToArray()), "false");
  3125.         }
  3126.  
  3127.         private void getVariableCmd(string sender, string var)
  3128.         {
  3129.             string val = getPluginVarValue(sender, var);
  3130.  
  3131.             SendPlayerMessage(sender, var + " = " + val);
  3132.         }
  3133.  
  3134.  
  3135.  
  3136.  
  3137.         private void setVariableCmd(string sender, string var, string val)
  3138.         {
  3139.            
  3140.             if (setPluginVarValue(sender, var, val))
  3141.                 SendPlayerMessage(sender, var + " set to \"" + val + "\"");
  3142.         }
  3143.  
  3144.         private void pluginSettingsCmd(string sender)
  3145.         {
  3146.             SendConsoleMessage(" == Insane Balancer Settings == ");
  3147.             foreach (string var in getPluginVars())
  3148.             {
  3149.                 SendConsoleMessage(sender, var + " = " + getPluginVarValue(sender, var));
  3150.             }
  3151.         }
  3152.  
  3153.  
  3154.         public bool stringValidator(string var, string value)
  3155.         {
  3156.             if (var.CompareTo("round_sort") == 0)
  3157.             {
  3158.                 if (!strAssertSort(value))
  3159.                     return false;
  3160.             }
  3161.             if (var.CompareTo("live_sort") == 0)
  3162.             {
  3163.                 if (!strAssertSort(value))
  3164.                     return false;
  3165.             }
  3166.             return true;
  3167.         }
  3168.  
  3169.  
  3170.         public List<String> getAllowedSorts()
  3171.         {
  3172.             List<string> sort_methods = new List<string>();
  3173.  
  3174.  
  3175.             sort_methods.Add("kdr_asc_round");
  3176.             sort_methods.Add("kdr_desc_round");
  3177.             sort_methods.Add("spm_asc_round");
  3178.             sort_methods.Add("spm_desc_round");
  3179.             sort_methods.Add("kpm_asc_round");
  3180.             sort_methods.Add("kpm_desc_round");
  3181.             sort_methods.Add("score_asc_round");
  3182.             sort_methods.Add("score_desc_round");
  3183.  
  3184.             return sort_methods;
  3185.         }
  3186.  
  3187.        
  3188.         public bool strAssertSort(string value)
  3189.         {
  3190.             if (value == null)
  3191.                 return false;
  3192.  
  3193.  
  3194.             List<String> sort_methods = getAllowedSorts();
  3195.  
  3196.             if (!sort_methods.Contains(value))
  3197.             {
  3198.                 SendConsoleMessage("^1^b" + value + "^n is not a valid sort method ^0");
  3199.                 SendConsoleMessage("valid sort methods are: ^b" + String.Join("^0,^b ", sort_methods.ToArray()) + "^0");
  3200.                 return false;
  3201.             }
  3202.             return true;
  3203.         }
  3204.  
  3205.         public bool booleanValidator(string var, bool value)
  3206.         {
  3207.             return true;
  3208.         }
  3209.  
  3210.  
  3211.         bool boolAssertNE(string var, bool value, string cmp)
  3212.         {
  3213.             bool cmp_value = getBooleanVarValue(cmp);
  3214.  
  3215.             if (!(value != cmp_value))
  3216.             {
  3217.                 ConsoleWrite("^1 cannot set ^b" + var + "^n to ^b" + value.ToString() + "^n while ^b" + cmp + "^n is set to ^b" + cmp_value.ToString() + "^n^0");
  3218.                 return false;
  3219.             }
  3220.             return true;
  3221.         }
  3222.  
  3223.         public bool integerValidator(string var, int value)
  3224.         {
  3225.  
  3226.             if (var.CompareTo("warn_msg_interval_time") == 0)
  3227.             {
  3228.                 if (!intAssertGTE(var, value, 0) ||
  3229.                     !intAssertLTE(var, value, "warn_msg_total_time"))
  3230.                     return false;
  3231.             }
  3232.  
  3233.             if (var.CompareTo("warn_msg_countdown_time") == 0)
  3234.             {
  3235.                 if (!intAssertGTE(var, value, 0) ||
  3236.                     !intAssertLTE(var, value, "warn_msg_total_time"))
  3237.                     return false;
  3238.             }
  3239.  
  3240.             if (var.CompareTo("warn_msg_total_time") == 0)
  3241.             {
  3242.                 if (!intAssertGTE(var, value, 0) ||
  3243.                     !intAssertGTE(var, value, "warn_msg_interval_time") ||
  3244.                     !intAssertGTE(var, value, "warn_msg_countdown_time"))
  3245.                     return false;
  3246.             }
  3247.  
  3248.             if (var.CompareTo("warn_msg_display_time") == 0)
  3249.             {
  3250.                 if (!intAssertGTE(var, value, 0) ||
  3251.                     !intAssertLTE(var, value, "warn_msg_total_time"))
  3252.                     return false;
  3253.             }
  3254.  
  3255.             if (var.CompareTo("balance_threshold") == 0)
  3256.             {
  3257.                 if (!intAssertGT(var, value, 0))
  3258.                     return false;
  3259.             }
  3260.  
  3261.             if (var.CompareTo("round_interval") == 0)
  3262.             {
  3263.  
  3264.                 if (!intAssertGT(var, value, 0))
  3265.                     return false;
  3266.  
  3267.                 if (serverInfo == null)
  3268.                     return true;
  3269.  
  3270.                 if (value > serverInfo.TotalRounds)
  3271.                 {
  3272.                     SendConsoleMessage("^1^b" + var + "(" + value + ")^n must be less than or equal than the total number of ^brounds(" + serverInfo.TotalRounds + ")^n per ^bmap(" + serverInfo.Map.ToLower() + ")^n^0");
  3273.                     return false;
  3274.                 }
  3275.             }
  3276.  
  3277.             if (var.CompareTo("live_interval_time") == 0)
  3278.             {
  3279.                 if (!intAssertGT(var, value, 0))
  3280.                     return false;
  3281.             }
  3282.  
  3283.             if (var.CompareTo("debug_level") == 0)
  3284.             {
  3285.                 if (!intAssertGTE(var, value, 0))
  3286.                     return false;
  3287.             }
  3288.  
  3289.             return true;
  3290.         }
  3291.  
  3292.  
  3293.         private bool intAssertLT(string var, int value, int max_value)
  3294.         {
  3295.             if (!(value < max_value))
  3296.             {
  3297.                 SendConsoleMessage("^1^b" + var + "(" + value + ")^n must be less than  ^b" + max_value + "^n^0");
  3298.                 return false;
  3299.             }
  3300.  
  3301.             return true;
  3302.         }
  3303.  
  3304.  
  3305.         private bool intAssertLTE(string var, int value, int max_value)
  3306.         {
  3307.             if (!(value <= max_value))
  3308.             {
  3309.                 SendConsoleMessage("^1^b" + var + "(" + value + ")^n must be less than or equal to ^b" + max_value + "^n^0");
  3310.                 return false;
  3311.             }
  3312.  
  3313.             return true;
  3314.         }
  3315.  
  3316.  
  3317.         private bool intAssertGT(string var, int value, int min_value)
  3318.         {
  3319.             if (!(value > min_value))
  3320.             {
  3321.                 SendConsoleMessage("^1^b" + var + "(" + value + ")^n must be greater than  ^b" + min_value + "^n^0");
  3322.                 return false;
  3323.             }
  3324.  
  3325.             return true;
  3326.         }
  3327.  
  3328.  
  3329.         private bool intAssertGTE(string var, int value, int min_value)
  3330.         {
  3331.             if (!(value >= min_value))
  3332.             {
  3333.                 SendConsoleMessage("^1^b" + var + "(" + value + ")^n must be greater than or equal to ^b" + min_value + "^n^0");
  3334.                 return false;
  3335.             }
  3336.  
  3337.             return true;
  3338.         }
  3339.  
  3340.         private bool intAssertGTE(string var1, int var1_value, string var2)
  3341.         {
  3342.             int var2_value = getIntegerVarValue(var2);
  3343.  
  3344.             if (!(var1_value >= var2_value))
  3345.             {
  3346.  
  3347.                 SendConsoleMessage("^1^b" + var1 + "(" + var1_value + ")^n must be greater than or equal to the value of ^b" + var2 + "(" + var2_value + ")^n");
  3348.  
  3349.                 return false;
  3350.             }
  3351.  
  3352.             return true;
  3353.         }
  3354.  
  3355.  
  3356.         private bool intAssertLTE(string var1, int var1_value, string var2)
  3357.         {
  3358.             int var2_value = getIntegerVarValue(var2);
  3359.  
  3360.  
  3361.             if (!(var1_value <= var2_value))
  3362.             {
  3363.                 SendConsoleMessage("^1^b" + var1 + "(" + var1_value + ")^n must be less than or equal to the value of ^b" + var2 + "(" + var2_value + ")^n");
  3364.                 return false;
  3365.             }
  3366.  
  3367.             return true;
  3368.         }
  3369.  
  3370.  
  3371.         private bool setPluginVarValue(string var, string val)
  3372.         {
  3373.             return setPluginVarValue(null, var, val);
  3374.         }
  3375.  
  3376.         private bool setPluginVarValue(string sender, string var, string val)
  3377.         {
  3378.             if (var == null || val == null)
  3379.                 return false;
  3380.  
  3381.             if (!getPluginVars().Contains(var))
  3382.             {
  3383.                 SendConsoleMessage(sender, "Insane Balancer: unknown variable \"" + var + "\"");
  3384.                 return false;
  3385.             }
  3386.  
  3387.             /* Parse Boolean Values */
  3388.             bool booleanValue = false;
  3389.             bool isBooleanValue = true;
  3390.             if (Regex.Match(val, @"\s*(1|true|yes)\s*", RegexOptions.IgnoreCase).Success)
  3391.                 booleanValue = true;
  3392.             else if (Regex.Match(val, @"\s*(0|false|no)\s*", RegexOptions.IgnoreCase).Success)
  3393.                 booleanValue = false;
  3394.             else
  3395.                 isBooleanValue = false;
  3396.  
  3397.  
  3398.             /* Parse Integer Values */
  3399.             int integerValue = 0;
  3400.             //bool isIntegerValue = int.TryParse(val, out integerValue) && integerValue >= 0;
  3401.             bool isIntegerValue = int.TryParse(val, out integerValue);
  3402.  
  3403.             /* Parse Float Values */
  3404.             float floatValue = 0F;
  3405.             bool isFloatValue = float.TryParse(val, out floatValue) && floatValue >= 0F;
  3406.  
  3407.             /* Parse String List */
  3408.             List<string> stringListValue = new List<string>(Regex.Split(val.Replace(";", ","), @"\s*,\s*"));
  3409.             bool isStringList = true;
  3410.  
  3411.             /* Parse String var */
  3412.             string stringValue = val;
  3413.             bool isStringValue = (val != null);
  3414.  
  3415.  
  3416.             if (isBooleanVar(var))
  3417.             {
  3418.                 if (!isBooleanValue)
  3419.                 {
  3420.                     SendConsoleMessage(sender, "\"" + val + "\" is invalid for " + var);
  3421.                     return false;
  3422.                 }
  3423.                 setBooleanVarValue(var, booleanValue);
  3424.                 return true;
  3425.             }
  3426.             else if (isIntegerVar(var))
  3427.             {
  3428.                 if (!isIntegerValue)
  3429.                 {
  3430.                     SendConsoleMessage(sender, "\"" + val + "\" is invalid for " + var);
  3431.                     return false;
  3432.                 }
  3433.  
  3434.                 setIntegerVarValue(var, integerValue);
  3435.                 return true;
  3436.             }
  3437.             else if (isFloatVar(var))
  3438.             {
  3439.                 if (!isFloatValue)
  3440.                 {
  3441.                     SendConsoleMessage(sender, "\"" + val + "\" is invalid for " + var);
  3442.                     return false;
  3443.                 }
  3444.  
  3445.                 setFloatVarValue(var, floatValue);
  3446.                 return true;
  3447.             }
  3448.             else if (isStringListVar(var))
  3449.             {
  3450.                 if (!isStringList)
  3451.                 {
  3452.                     SendConsoleMessage(sender, "\"" + val + "\"  is invalid for " + var);
  3453.                     return false;
  3454.                 }
  3455.  
  3456.                 setStringListVarValue(var, stringListValue);
  3457.                 return true;
  3458.             }
  3459.             else if (isStringVar(var))
  3460.             {
  3461.                 if (!isStringValue)
  3462.                 {
  3463.                     SendConsoleMessage(sender, "invalid value for " + var);
  3464.                     return false;
  3465.                 }
  3466.  
  3467.                 setStringVarValue(var, stringValue);
  3468.                 return true;
  3469.             }
  3470.             else
  3471.             {
  3472.                 SendConsoleMessage(sender, "Insane Balancer: unknown variable \"" + var + "\"");
  3473.                 return false;
  3474.             }
  3475.  
  3476.         }
  3477.  
  3478.         private bool isIntegerVar(string var)
  3479.         {
  3480.             return this.integerVariables.ContainsKey(var);
  3481.         }
  3482.  
  3483.         private int getIntegerVarValue(string var)
  3484.         {
  3485.             if (!isIntegerVar(var))
  3486.             {
  3487.                 SendConsoleMessage("unknown variable \"" + var + "\"");
  3488.                 return -1;
  3489.             }
  3490.  
  3491.             return this.integerVariables[var];
  3492.         }
  3493.  
  3494.         private bool setIntegerVarValue(string var, int val)
  3495.         {
  3496.             if (!isIntegerVar(var))
  3497.             {
  3498.                 SendConsoleMessage("unknown variable \"" + var + "\"");
  3499.                 return false;
  3500.             }
  3501.  
  3502.             if (hasIntegerValidator(var))
  3503.             {
  3504.                 integerVariableValidator validator = intergerVarValidators[var];
  3505.                 if (validator(var, val) == false)
  3506.                     return false;
  3507.             }
  3508.  
  3509.             this.integerVariables[var] = val;
  3510.             return true;
  3511.         }
  3512.  
  3513.         private bool hasBooleanValidator(string var)
  3514.         {
  3515.             return booleanVarValidators.ContainsKey(var);
  3516.         }
  3517.  
  3518.         private bool hasIntegerValidator(string var)
  3519.         {
  3520.             return intergerVarValidators.ContainsKey(var);
  3521.         }
  3522.  
  3523.         private bool hasStringValidator(string var)
  3524.         {
  3525.             return stringVarValidators.ContainsKey(var);
  3526.         }
  3527.  
  3528.         private bool isStringVar(string var)
  3529.         {
  3530.             return this.stringVariables.ContainsKey(var);
  3531.         }
  3532.  
  3533.  
  3534.         private string getStringVarValue(string var)
  3535.         {
  3536.             if (!isStringVar(var))
  3537.             {
  3538.                 SendConsoleMessage("unknown variable \"" + var + "\"");
  3539.                 return "";
  3540.             }
  3541.  
  3542.             return this.stringVariables[var];
  3543.         }
  3544.  
  3545.         private bool setStringVarValue(string var, string val)
  3546.         {
  3547.             if (!isStringVar(var))
  3548.             {
  3549.                 SendConsoleMessage("unknown variable \"" + var + "\"");
  3550.                 return false;
  3551.             }
  3552.  
  3553.  
  3554.             if (hasStringValidator(var))
  3555.             {
  3556.                 stringVariableValidator validator = stringVarValidators[var];
  3557.                 if (validator(var, val) == false)
  3558.                     return false;
  3559.             }
  3560.  
  3561.  
  3562.             string oldval = this.stringVariables[var];
  3563.             this.stringVariables[var] = val;
  3564.  
  3565.             return true;
  3566.         }
  3567.  
  3568.  
  3569.  
  3570.  
  3571.         private bool isStringListVar(string var)
  3572.         {
  3573.             return this.stringListVariables.ContainsKey(var);
  3574.         }
  3575.  
  3576.         private List<string> getStringListVarValue(string var)
  3577.         {
  3578.             if (!isStringListVar(var))
  3579.             {
  3580.                 SendConsoleMessage("unknown variable \"" + var + "\"");
  3581.                 return new List<string>();
  3582.             }
  3583.  
  3584.             string[] out_list = Regex.Split(this.stringListVariables[var].Replace(";", ","), @"\s*,\s*");
  3585.             return new List<string>(out_list);
  3586.         }
  3587.  
  3588.         private bool setStringListVarValue(string var, List<string> val)
  3589.         {
  3590.             if (!isStringListVar(var))
  3591.             {
  3592.                 SendConsoleMessage("unknown variable \"" + var + "\"");
  3593.                 return false;
  3594.             }
  3595.  
  3596.             List<string> cleanList = new List<string>();
  3597.             foreach (string item in val)
  3598.                 if (Regex.Match(item, @"^\s*$").Success)
  3599.                     continue;
  3600.                 else
  3601.                     cleanList.Add(item);
  3602.  
  3603.             //this.stringListVariables[var] = val;
  3604.             this.stringListVariables[var] = String.Join(",", cleanList.ToArray());
  3605.             return true;
  3606.         }
  3607.  
  3608.  
  3609.         private bool isFloatVar(string var)
  3610.         {
  3611.             return this.floatVariables.ContainsKey(var);
  3612.         }
  3613.  
  3614.         private float getFloatVarValue(string var)
  3615.         {
  3616.             if (!isFloatVar(var))
  3617.             {
  3618.                 SendConsoleMessage("unknown variable \"" + var + "\"");
  3619.                 return -1F;
  3620.             }
  3621.  
  3622.             return this.floatVariables[var];
  3623.         }
  3624.  
  3625.         private bool setFloatVarValue(string var, float val)
  3626.         {
  3627.             if (!isFloatVar(var))
  3628.             {
  3629.                 SendConsoleMessage("unknown variable \"" + var + "\"");
  3630.                 return false;
  3631.             }
  3632.  
  3633.             this.floatVariables[var] = val;
  3634.             return true;
  3635.         }
  3636.  
  3637.  
  3638.         private bool isBooleanVar(string var)
  3639.         {
  3640.             return this.booleanVariables.ContainsKey(var);
  3641.         }
  3642.  
  3643.         private bool getBooleanVarValue(string var)
  3644.         {
  3645.             if (!isBooleanVar(var))
  3646.             {
  3647.                 SendConsoleMessage("unknown variable \"" + var + "\"");
  3648.                 return false;
  3649.             }
  3650.  
  3651.             return this.booleanVariables[var];
  3652.         }
  3653.  
  3654.         private bool setBooleanVarValue(string var, bool val)
  3655.         {
  3656.             if (!isBooleanVar(var))
  3657.             {
  3658.                 SendConsoleMessage("unknown variable \"" + var + "\"");
  3659.                 return false;
  3660.             }
  3661.  
  3662.             if (hasBooleanValidator(var))
  3663.             {
  3664.                 booleanVariableValidator validator = booleanVarValidators[var];
  3665.                 if (validator(var, val) == false)
  3666.                     return false;
  3667.             }
  3668.  
  3669.             this.booleanVariables[var] = val;
  3670.             return true;
  3671.         }
  3672.  
  3673.  
  3674.         private string getPluginVarValue(string var)
  3675.         {
  3676.             return getPluginVarValue(null, var);
  3677.         }
  3678.  
  3679.         private string getPluginVarValue(string sender, string var)
  3680.         {
  3681.             if (!getPluginVars().Contains(var))
  3682.             {
  3683.                 SendConsoleMessage(sender, "Insane Balancer: unknown variable \"" + var + "\"");
  3684.                 return "";
  3685.             }
  3686.  
  3687.             if (isBooleanVar(var))
  3688.             {
  3689.                 return getBooleanVarValue(var).ToString();
  3690.             }
  3691.             else if (isIntegerVar(var))
  3692.             {
  3693.                 return getIntegerVarValue(var).ToString();
  3694.             }
  3695.             else if (isFloatVar(var))
  3696.             {
  3697.                 return getFloatVarValue(var).ToString();
  3698.             }
  3699.             else if (isStringListVar(var))
  3700.             {
  3701.                 string lst = list2string(getStringListVarValue(var), "");
  3702.                 return lst;
  3703.             }
  3704.             else if (isStringVar(var))
  3705.             {
  3706.                 return getStringVarValue(var);
  3707.             }
  3708.             else
  3709.             {
  3710.                 SendConsoleMessage(sender, "Insane Balancer: unknown variable \"" + var + "\"");
  3711.                 return "";
  3712.             }
  3713.         }
  3714.  
  3715.         private List<string> getPluginVars()
  3716.         {
  3717.             return getPluginVars(false);
  3718.         }
  3719.  
  3720.         private List<string> getPluginVars(bool hide)
  3721.         {
  3722.             List<string> vars = new List<string>();
  3723.  
  3724.             vars.AddRange(getIntegerPluginVars());
  3725.             vars.AddRange(getBooleanPluginVars());
  3726.             vars.AddRange(getStringListPluginVars());
  3727.             vars.AddRange(getFloatPluginVars());
  3728.             vars.AddRange(getStringPluginVars());
  3729.  
  3730.             if (hide && !getBooleanVarValue("advanced_mode"))
  3731.             {
  3732.                 foreach (string hidden_var in hiddenVariables)
  3733.                     vars.Remove(hidden_var);
  3734.             }
  3735.  
  3736.             return vars;
  3737.         }
  3738.  
  3739.  
  3740.         private List<string> getStringPluginVars()
  3741.         {
  3742.             return new List<string>(this.stringVariables.Keys);
  3743.         }
  3744.  
  3745.  
  3746.         private List<string> getStringListPluginVars()
  3747.         {
  3748.             return new List<string>(this.stringListVariables.Keys);
  3749.         }
  3750.  
  3751.  
  3752.         private List<string> getIntegerPluginVars()
  3753.         {
  3754.             return new List<string>(this.integerVariables.Keys);
  3755.         }
  3756.  
  3757.         private List<string> getFloatPluginVars()
  3758.         {
  3759.             return new List<string>(this.floatVariables.Keys);
  3760.         }
  3761.  
  3762.         private List<string> getBooleanPluginVars()
  3763.         {
  3764.             return new List<string>(this.booleanVariables.Keys);
  3765.         }
  3766.  
  3767.         public string playerstate2stringED(PlayerState state)
  3768.         {
  3769.             switch (state)
  3770.             {
  3771.                 case PlayerState.alive:
  3772.                     return "is alive";
  3773.                 case PlayerState.dead:
  3774.                     return "is dead";
  3775.                 case PlayerState.kicked:
  3776.                     return "was kicked";
  3777.                 case PlayerState.left:
  3778.                     return "left the game";
  3779.                 case PlayerState.limbo:
  3780.                     return "is in limbo";
  3781.                 default:
  3782.                     return "(%player_state%)";
  3783.             }
  3784.  
  3785.         }
  3786.  
  3787.  
  3788.         public string list2string(List<string> list, string glue)
  3789.         {
  3790.  
  3791.             if (list == null || list.Count == 0)
  3792.                 return "";
  3793.             else if (list.Count == 1)
  3794.                 return list[0];
  3795.  
  3796.             string last = list[list.Count - 1];
  3797.             list.RemoveAt(list.Count - 1);
  3798.  
  3799.             string str = "";
  3800.             foreach (string item in list)
  3801.                 str += item + ", ";
  3802.  
  3803.             return str + glue + last;
  3804.         }
  3805.  
  3806.         public string list2string(List<string> list)
  3807.         {
  3808.             return list2string(list, "and ");
  3809.         }
  3810.  
  3811.  
  3812.  
  3813.  
  3814.         private bool isAdmin(string soldier)
  3815.         {
  3816.             List<string> admin_list = getStringListVarValue("admin_list");
  3817.             return admin_list.Contains(soldier);
  3818.         }
  3819.  
  3820.  
  3821.         private PlayerProfile getPlayerProfile(CPlayerInfo info)
  3822.         {
  3823.             return getPlayerProfile(info.SoldierName);
  3824.         }
  3825.  
  3826.  
  3827.         private List<PlayerProfile> getPlayersProfile(string name)
  3828.         {
  3829.             DebugWrite("^1ENTER: getPlayersProfile", 11);
  3830.             List<PlayerProfile> profiles = new List<PlayerProfile>();
  3831.             foreach (KeyValuePair<string, PlayerProfile> pair in this.players)
  3832.             {
  3833.                 DebugWrite("^1       getPlayersProfile: processing "+pair.Key, 6);
  3834.                 if (pair.Value.info == null || pair.Value.info.TeamID < 0)
  3835.                 {
  3836.                     DebugWrite("^1       getPlayersProfile: skipping " + pair.Key, 6);
  3837.                     continue;
  3838.                 }
  3839.  
  3840.                 if (name.Equals(""))
  3841.                     profiles.Add(pair.Value);
  3842.                 else if (pair.Key.ToLower().Contains(name.ToLower()))
  3843.                     profiles.Add(pair.Value);
  3844.             }
  3845.  
  3846.             DebugWrite("^1EXIT: getPlayersProfile, found ^b"+profiles.Count + "^n players", 11);
  3847.             return profiles;
  3848.         }
  3849.  
  3850.         private PlayerProfile getPlayerProfile(string name)
  3851.         {
  3852.             PlayerProfile pp;
  3853.             this.players.TryGetValue(name, out pp);
  3854.             return pp;
  3855.         }
  3856.  
  3857.  
  3858.  
  3859.  
  3860.         public override void OnPunkbusterPlayerInfo(CPunkbusterInfo cpbiPlayer)
  3861.         {
  3862.  
  3863.             if (cpbiPlayer != null)
  3864.             {
  3865.                 if (this.players.ContainsKey(cpbiPlayer.SoldierName))
  3866.                     this.players[cpbiPlayer.SoldierName].pbinfo = cpbiPlayer;
  3867.                 else
  3868.                     this.players.Add(cpbiPlayer.SoldierName, new PlayerProfile(this, cpbiPlayer));
  3869.             }
  3870.         }
  3871.  
  3872.         public override void OnListPlayers(List<CPlayerInfo> lstPlayers, CPlayerSubset cpsSubset)
  3873.         {
  3874.  
  3875.             DebugWrite("^1Players list received, ^b" + lstPlayers.Count + "^n players found", 6);
  3876.  
  3877.                
  3878.                 foreach (CPlayerInfo cpiPlayer in lstPlayers)
  3879.                 {
  3880.                     DebugWrite("^1    found, ^b" + cpiPlayer.SoldierName + "^n", 6);
  3881.  
  3882.                     if (this.players.ContainsKey(cpiPlayer.SoldierName))
  3883.                     {
  3884.                     //    DebugWrite("^1    updating, ^b" + cpiPlayer.SoldierName + "^n", 6);
  3885.                         this.players[cpiPlayer.SoldierName].updateInfo(cpiPlayer);
  3886.                     }
  3887.                    /* else
  3888.                     {
  3889.                         DebugWrite("^1    adding, ^b" + cpiPlayer.SoldierName + "^n", 6);
  3890.                         this.players.Add(cpiPlayer.SoldierName, new PlayerProfile(this, cpiPlayer));
  3891.                     } */
  3892.                     //DebugWrite("^1    done with, ^b" + cpiPlayer.SoldierName + "^n", 6);
  3893.                 }
  3894.  
  3895.             if (check_state_phase == 1)
  3896.                 startCheckState(utc);
  3897.  
  3898.         }
  3899.  
  3900.  
  3901.     }
  3902. }
  3903.  
  3904.  
  3905.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement