Jump to content

Insane Limits: Squad Recruiter


ImportBot

Recommended Posts

Originally Posted by PapaCharlie9*:

 

UPDATE

 

Use this plugin instead:

 

showthread....-1-11-Jan-2015*


 

 

 

 

 

(BF3) Version V0.8/R2: Fully tested on Conquest. Should work for Rush, but not tested. Not designed for TDM, SQDM or Squad Rush, so the limits disable themselves when the server is in those game modes.

 

(BF4) Not tested, compiles.

 

Would you like for players to join squads on your server? These two limits work together to enforce a squad-up policy called the "Insane Limits Draft". If the draft is on and a player is not in a squad and not in the Insane Limits white list:

 

  • On his first death in the round, a chat message will be sent to his team encouraging him to join a squad.
  • On his second death, if he is still not in a squad, the limit will place him in a squad with a forced move.
  • If the move fails or if the player leaves the squad, on his third death, the limit will try to move him to a squad again.
  • If on either death the move succeeds or if two attempts fail, the player is put in a "cool down" pool and no attempt will be made to put him in a squad again until the next round.

This policy helps the vast majority of players who simply forget to join a squad, while respecting the wishes of players who want to choose their own squads or who really don't want to be in a squad if all the available squads have bad players, or whatever. Also, since the move player command often fails, this policy tolerates those failures and doesn't annoy players by trying over and over again. Even so, players may be exempted from the draft altogether by having their name added to the Insane Limits player name white list.

 

The draft can be switched on or off in-game with a command that is limited to admins only with custom lists (see below).

 

There is also a recruiting command that allows players to pull other players on their team into their squad -- saving them from having to harass players in chat to join their squad. In the following policy, "player" is the player issuing the command and "cadet" is the player to recruit into the squad.

 

  • Player must be in a squad and the squad must be public and have room in it.
  • Cadet must be on the same team as player.
  • Cadet must not have been moved this round by Insane Limits Draft.
  • Cadet must not be in a squad, OR, must have the same tags as player.

Assuming all those conditions are met, the move is scheduled for cadet's next death. Two attempts will be made and then the same cool down policy is used as for the draft.

 

The last clause of the policy, having the same tags, allows fellow clan members to pull other clan members into their squad directly, without resorting to chat or waiting for the cadet to leave his current squad so he can join the other. Any player can use this command.

 

Finally, the OnAnyChat limit also advertises the recruiting command. If any player types the word "squad" into chat, a message is sent to that player's squad as follows:

 

Code:

Hey player.Name, type 
!recruit player
to try and move player to your squad.
The commands are:

 

!draft

 

This command reports the current state of the Insane Limits Draft. The draft is either ON or OFF. Any player may use this command.

 

!draft on

 

Turns Insane Limits Draft on. Only members of the admin custom list or with tags on the admin tags custom lists may use this command.

 

!draft off

 

Turns Insane Limits Draft off. Only members of the admin custom list or with tags on the admin tags custom lists may use this command.

 

!recruit cadet

 

Attempt to move the specified cadet to player's squad on cadet's next death. Any player may use this command.

 


 

INSTRUCTIONS

 

Step 1

 

Create a custom list called admins (see note below). Set it Enabled and set the comparison to CaseSensitive. Set its value to a comma separated list of player names.

 

NOTE: If you already have an admin list for another limit, you may use it. Ignore the instructions above for "admins" and make a slight modification to the second_check code of the OnAnyChat limit below. Find the line that contains String admins and change the value of "admins" to whatever the name of your list is. For example, if your custom admin name list is called "admin_list", make the line look like this:

Code:

String admins = "admin_list"; // Define the name of your admins list here
Step 2

 

Create a custom list called admin_tags. Set it Enabled and set the comparison to CaseSensitive. Set its value to a comma separated list of tags. If a player's tag matches any on the list, they are considered an admin.

 

Hint: If you only want to use the tag list and not the name list, just write micovery in the list of admin names.

 

 

Step 3

 

Create a new limit and set it Enabled. Set limit to evaluate OnAnyChat, set the name to Squad Recruiter Commands, set the action to None.

 

Set first_check to this Expression:

 

Code:

( Regex.Match(server.Gamemode, "^(_:Conquest|Rush)", RegexOptions.IgnoreCase).Success && (Regex.Match(player.LastChat, @"^\s*[!](_:recruit|draft)", RegexOptions.IgnoreCase).Success
|| Regex.Match(player.LastChat, @"(_:squad|clan|friend|mate)", RegexOptions.IgnoreCase).Success))
Set second_check to this Code (BF4 Version, requires Insane Limits 0.9.16.0 or later):

 

Code:

/* Version: V9.16/R5
!recruit name
- Attempts to move the soldier matching "name" to the player's squad.

!draft (on|off)
- With arguments, enable/disable the automatic squad draft.
- Without arguments, sends the current state to chat.
*/

String admins = "admins"; // Define the name of your admins list here
String admin_tags = "admin_tags"; // Define the name of your admin tag list here

String prefix = "SQR_";
String kT1Cadets = prefix + "T1_cadets"; // server.RoundData
String kT2Cadets = prefix + "T2_cadets"; // server.RoundData
String kDraft = prefix + "draft"; // plugin.Data
String kCooldown = prefix + "cooldown"; // server.RoundData

String msg = "none";

int level = 2;

try {
    level = Convert.ToInt32(plugin.getPluginVarValue("debug_level"));
} catch (Exception e) {}

Match rMatch = Regex.Match(player.LastChat, @"recruit\s+([^\s]+)", RegexOptions.IgnoreCase);
Match d0Match = Regex.Match(player.LastChat, @"draft\s*$", RegexOptions.IgnoreCase);
Match d1Match = Regex.Match(player.LastChat, @"draft\s+(on|off|1|0|true|false)", RegexOptions.IgnoreCase);

if (level >= 3) plugin.ConsoleWrite("^b[SQR]^n: Orig command by " + player.FullName + ": " + player.LastChat);

/* Somebody mentioned "squad" in their chat */
if (Regex.Match(player.LastChat, @"\s(_:squad|clan|friend|mate)", RegexOptions.IgnoreCase).Success || Regex.Match(player.LastChat, @"^squad\s", RegexOptions.IgnoreCase).Success) {
    if (level >= 3) plugin.ConsoleWrite("^b[SQR]^n: keyword detected.");
    plugin.SendSquadMessage(player.TeamId, player.SquadId, "Hey " + player.Name + ", type '!recruit _' for info on moving friends to your squad");
    return false;
}

Action<String> ChatPlayer = delegate(String who) {
    plugin.ServerCommand("admin.say", msg, "player", who);
    plugin.PRoConChat( "ADMIN to " + who +"> " + msg);
};

/* !recruit command_ */

if (rMatch.Success) {
    // Add to list of cadets that need a squad
    String rName = rMatch.Groups[1].Value;
    PlayerInfoInterface recruit = null;
    // Check if requesting info
    if (rName == "_") {
        if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: " + player.FullName + " requested info");
        msg = "*** Move a friend to your squad with:\n!recruit friends_name";
        ChatPlayer(player.Name);
        msg = "*** Your squad must have room and your tags must match";
        ChatPlayer(player.Name);
        return false;
    }
    // Check if dummy is dumb
    if (rName == "friends_name") {
        if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: dummy is dumb");
        msg = "*** Replace 'friends_name' with the soldier name of your friend";
        ChatPlayer(player.Name);
        return false;
    }
    // Find matching players
    List<PlayerInfoInterface> all = new List<PlayerInfoInterface>();
    all.AddRange(team1.players);
    all.AddRange(team2.players);
    List<PlayerInfoInterface> found = new List<PlayerInfoInterface>();
    List<String> fNames = new List<String>();
    foreach (PlayerInfoInterface p in all) {
        if (Regex.Match(p.Name, rName, RegexOptions.IgnoreCase).Success) {
            found.Add(p);
            fNames.Add(p.Name);
        }
    }
    if (found.Count > 1) {
        msg = "'" + rName + "' matches: " + String.Join(",", fNames.ToArray());
        if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: " + msg);
        msg = "*** Try again: " + msg;
        ChatPlayer(player.Name);
        return false; 
    } else if (found.Count == 1) {
        recruit = found[0];
    }
    // Check if there is a player by that name
    if (recruit == null) {
        if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: no such player: " + rName);
        msg = "*** No such player: " + rName;
        ChatPlayer(player.Name);
        return false;
    }

    // Check if requestor is not in a squad
    if (player.SquadId == 0) {
        if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: player not in a squad: " + player.FullName);
        msg = "*** You are not in a squad: " + player.FullName;
        ChatPlayer(player.Name);
        return false;
    }
    // Check if recruit is on whitelist
    if (plugin.isInPlayerWhitelist(recruit.Name)) {
        if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: on whitelist: " + recruit.FullName);
        msg = "*** Player is exempt: " + recruit.FullName;
        ChatPlayer(player.Name);
        return false;
    }
    
    // Tell player what name we are trying to use
    msg = "*** Closest match to '" + rName + "' is " + recruit.FullName;
    ChatPlayer(player.Name);
    msg = "*** " + player.FullName + " is trying to recruit you to his squad";
    ChatPlayer(recruit.Name);
    

    // Extract tags for further testing
    String ptag = player.Tag;
    if (String.IsNullOrEmpty(ptag)) {
        // Maybe they are using [_-=]XXX[=-_]PlayerName format
        Match tm = Regex.Match(player.Name, @"^[=_\-]_([^=_\-]{2,4})[=_\-]");
        if (tm.Success) {
            ptag = tm.Groups[1].Value;
            if (level >= 4) plugin.ConsoleWrite("^b[SQR]^n extracted [" + ptag + "] from " + player.Name);
        }
    }
    String rtag = recruit.Tag;
    if (String.IsNullOrEmpty(rtag)) {
        // Maybe they are using [_-=]XXX[=-_]PlayerName format
        Match tm = Regex.Match(recruit.Name, @"^[=_\-]_([^=_\-]{2,4})[=_\-]");
        if (tm.Success) {
            rtag = tm.Groups[1].Value;
            if (level >= 4) plugin.ConsoleWrite("^b[SQR]^n extracted [" + rtag + "] from " + recruit.Name);
        }
    }

    // Check if recruit is on the other team (only matching tags may recruit)
    if (recruit.TeamId != player.TeamId) {
        // Check if tags don't match
        if (String.IsNullOrEmpty(ptag) 
          || (!String.IsNullOrEmpty(ptag) 
            && (String.IsNullOrEmpty(rtag) || ptag != rtag)
          )
        ) {
        
            if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: not on same team: " + recruit.FullName);
            msg = "*** Not on same team and tags don't match: " + recruit.FullName;
            ChatPlayer(player.Name);
            return false;
        }
    }
/*
    // Check if recruit is already in a squad (only matching tags may recruit)
    if (recruit.SquadId != 0) {
        // Check if tags don't match
        if (String.IsNullOrEmpty(ptag) 
          || (!String.IsNullOrEmpty(ptag) 
            && (String.IsNullOrEmpty(rtag) || ptag != rtag)
          )
        ) {
            if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: " + recruit.FullName + "is already in a squad and tags don't match");
            msg = "*** Already in a squad and tags don't match: " + recruit.FullName;
            ChatPlayer(player.Name);
            return false;
        }
    }
*/
/*
    // Check if squad id is above 8 as of R20 patch
    if (player.SquadId > 8) {
        if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: " + player.FullName + " is in a custom squad #" + recruit.SquadId);
        msg = "*** You are in custom squad #" + player.SquadId + " and cannot recruit players";
        plugin.SendSquadMessage(player.TeamId, player.SquadId, msg);
        plugin.PRoConChat( "ADMIN > " + msg);
        return false;
    }
*/
    // Check if recruit is in the cooldown pool and has different tags
    if (!server.RoundData.issetObject(kCooldown)) {
        server.RoundData.setObject(kCooldown, new List<String>());
    }
    List<String> coolDown = (List<String>)server.RoundData.getObject(kCooldown);
    if (coolDown.Contains(recruit.Name)) {
        if (!String.IsNullOrEmpty(rtag) && !String.IsNullOrEmpty(ptag) && ptag != rtag) {
            if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: in cooldown pool: " + recruit.FullName);
            msg = "*** Already been moved this round: " + recruit.FullName;
            ChatPlayer(player.Name);
            return false;
        } else {
            // Recruit with matching tag is allowed to be moved again
            coolDown.Remove(recruit.Name);
            server.RoundData.setObject(kCooldown, coolDown);
        }
    }
    // Add to list
    String kCadets = (player.TeamId == 1) _ kT1Cadets : kT2Cadets;
    if (!server.RoundData.issetObject(kCadets)) {
        server.RoundData.setObject(kCadets, new Dictionary<String,int>());
    }
    Dictionary<String,int> teamCadets = (Dictionary<String,int>)server.RoundData.getObject(kCadets);
    int teamSquad = (1000 * player.TeamId) + player.SquadId;
    teamCadets.Add(recruit.Name, teamSquad);
    if (level >= 2) {
        String[] squadName = new String[] { "None",
          "Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel",
          "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa",
          "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey", "Xray",
          "Yankee", "Zulu", "Haggard", "Sweetwater", "Preston", "Redford", "Faith", "Celeste"
        };
        String sqn = (player.SquadId >= 0) _ squadName[player.SquadId] : "_";
        String[] factionNames = new String[3]{"US", "RU", "CN"};
        int faction = server.GetFaction(player.TeamId);
        String teamName = "__";
        if (faction >= 0 && faction <= 2) teamName = factionNames[faction];
        if (level >= 2 ) plugin.ConsoleWrite("^b[SQR]^n: new cadet for squad " + sqn + " on team " + teamName + ": " + recruit.FullName);
    }
    msg = "*** Insane Limits will attempt to move " + recruit.FullName + " to your squad on next death.";
    plugin.SendTeamMessage(player.TeamId, msg);
    plugin.PRoConChat( "ADMIN > " + msg);
    return false;
}

/* !draft command with no arguments */

if (d0Match.Success) {
    // Report enabled/disabled
    if (!plugin.Data.issetBool(kDraft)) {
        plugin.Data.setBool(kDraft, false);
    }
    bool isDraftEnabled = plugin.Data.getBool(kDraft);
    if (isDraftEnabled) {
        msg = "*** Insane Limits squad draft mode is:\n*** ON!";
    } else {
        msg = "*** Insane Limits squad draft mode is:\n*** OFF!";
    }
    plugin.SendGlobalMessage(msg);
    plugin.PRoConChat( "ADMIN > " + msg);
    return false;
}

/* !draft command with arguments */

if (d1Match.Success) {
    String tag = player.Tag;
    if (tag.Length == 0) {
        // Maybe they are using [_-=]XXX[=-_]PlayerName format
        Match tm = Regex.Match(player.Name, @"^[=_\-]_([^=_\-]{2,4})[=_\-]");
        if (tm.Success) {
            tag = tm.Groups[1].Value;
            if (level >= 4) plugin.ConsoleWrite("^b[SQR]^n extracted [" + tag + "] from " + player.Name);
        } else {
            tag = "no tag";
        }
    }
    if (!plugin.isInList(player.Name, admins) && !plugin.isInList(tag, admin_tags)) {
        if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: " + "[" + tag + "]" + player.Name + " not authorized to use !draft command.");
        msg = "*** You are not authorized to change the draft!";
        plugin.SendSquadMessage(player.TeamId, player.SquadId, msg);
        plugin.PRoConChat( "ADMIN > " + msg);
        return false;
    }
    // Enable/Disable
    String state = "OFF!";
    if (!plugin.Data.issetBool(kDraft)) {
        plugin.Data.setBool(kDraft, false);
    } else if (Regex.Match(d1Match.Groups[1].Value, "(on|1|true)", RegexOptions.IgnoreCase).Success) {
        plugin.Data.setBool(kDraft, true);
        state = "ON!";
    } else {
        plugin.Data.setBool(kDraft, false);
    }
    msg = "*** Insane Limits squad draft mode is:\n***" + state;
    plugin.SendGlobalMessage(msg);
    plugin.PRoConChat( "ADMIN > " + msg);
    return false;
}

return false;
Original BF3 Version:

 

 

Code:

/* Version: V0.8/R4
!recruit name
- Attempts to move the soldier matching "name" to the player's squad.

!draft (on|off)
- With arguments, enable/disable the automatic squad draft.
- Without arguments, sends the current state to chat.
*/

String admins = "admins"; // Define the name of your admins list here
String admin_tags = "admin_tags"; // Define the name of your admin tag list here

String prefix = "SQR_";
String kUSCadets = prefix + "US_cadets"; // server.RoundData
String kRUCadets = prefix + "RU_cadets"; // server.RoundData
String kDraft = prefix + "draft"; // plugin.Data
String kCooldown = prefix + "cooldown"; // server.RoundData

String msg = "none";

int level = 2;

try {
    level = Convert.ToInt32(plugin.getPluginVarValue("debug_level"));
} catch (Exception e) {}

Match rMatch = Regex.Match(player.LastChat, @"recruit\s+([^\s]+)", RegexOptions.IgnoreCase);
Match d0Match = Regex.Match(player.LastChat, @"draft\s*$", RegexOptions.IgnoreCase);
Match d1Match = Regex.Match(player.LastChat, @"draft\s+(on|off|1|0|true|false)", RegexOptions.IgnoreCase);

if (level >= 3) plugin.ConsoleWrite("^b[SQR]^n: Orig command by " + player.FullName + ": " + player.LastChat);

/* Somebody mentioned "squad" in their chat */
if (Regex.Match(player.LastChat, @"\s(_:squad|clan|friend|mate)", RegexOptions.IgnoreCase).Success || Regex.Match(player.LastChat, @"^squad\s", RegexOptions.IgnoreCase).Success) {
    if (level >= 3) plugin.ConsoleWrite("^b[SQR]^n: keyword detected.");
    plugin.SendSquadMessage(player.TeamId, player.SquadId, "Hey " + player.Name + ", type '!recruit _' for info on moving friends to your squad");
    return false;
}

Action<String> ChatPlayer = delegate(String who) {
    plugin.ServerCommand("admin.say", msg, "player", who);
    plugin.PRoConChat( "ADMIN to " + who +"> " + msg);
};

/* !recruit command_ */

if (rMatch.Success) {
    // Add to list of cadets that need a squad
    String rName = rMatch.Groups[1].Value;
    PlayerInfoInterface recruit = plugin.GetPlayer(rName, true);
    // Check if requesting info
    if (rName == "_") {
        if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: " + player.FullName + " requested info");
        msg = "*** Move a friend to your squad with:\n!recruit friends_name";
        ChatPlayer(player.Name);
        msg = "*** Your squad must have room and your tags must match";
        ChatPlayer(player.Name);
        return false;
    }
    // Check if dummy is dumb
    if (rName == "friends_name") {
        if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: dummy is dumb");
        msg = "*** Replace 'friends_name' with the soldier name of your friend";
        ChatPlayer(player.Name);
        return false;
    }
    // Check if there is a player by that name
    if (recruit == null) {
        if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: no such player: " + rName);
        msg = "*** No such player: " + rName;
        ChatPlayer(player.Name);
        return false;
    }
    
    // Tell player what name we are trying to use
    msg = "*** Closest match to '" + rName + "' is " + recruit.FullName;
    ChatPlayer(player.Name);
    msg = "*** " + player.FullName + " is trying to recruit you to his squad";
    ChatPlayer(recruit.Name);
    
    // Check if requestor is not in a squad
    if (player.SquadId == 0) {
        if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: player not in a squad: " + player.FullName);
        msg = "*** You are not in a squad: " + player.FullName;
        ChatPlayer(player.Name);
        return false;
    }
    // Check if recruit is on whitelist
    if (plugin.isInPlayerWhitelist(recruit.Name)) {
        if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: on whitelist: " + recruit.FullName);
        msg = "*** Player is exempt: " + recruit.FullName;
        ChatPlayer(player.Name);
        return false;
    }

    // Extract tags for further testing
    String ptag = player.Tag;
    if (String.IsNullOrEmpty(ptag)) {
        // Maybe they are using [_-=]XXX[=-_]PlayerName format
        Match tm = Regex.Match(player.Name, @"^[=_\-]_([^=_\-]{2,4})[=_\-]");
        if (tm.Success) {
            ptag = tm.Groups[1].Value;
            if (level >= 4) plugin.ConsoleWrite("^b[SQR]^n extracted [" + ptag + "] from " + player.Name);
        }
    }
    String rtag = recruit.Tag;
    if (String.IsNullOrEmpty(rtag)) {
        // Maybe they are using [_-=]XXX[=-_]PlayerName format
        Match tm = Regex.Match(recruit.Name, @"^[=_\-]_([^=_\-]{2,4})[=_\-]");
        if (tm.Success) {
            rtag = tm.Groups[1].Value;
            if (level >= 4) plugin.ConsoleWrite("^b[SQR]^n extracted [" + rtag + "] from " + recruit.Name);
        }
    }

    // Check if recruit is on the other team (only matching tags may recruit)
    if (recruit.TeamId != player.TeamId) {
        // Check if tags don't match
        if (String.IsNullOrEmpty(ptag) 
          || (!String.IsNullOrEmpty(ptag) 
            && (String.IsNullOrEmpty(rtag) || ptag != rtag)
          )
        ) {
        
            if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: not on same team: " + recruit.FullName);
            msg = "*** Not on same team and tags don't match: " + recruit.FullName;
            ChatPlayer(player.Name);
            return false;
        }
    }
    // Check if recruit is already in a squad (only matching tags may recruit)
    if (recruit.SquadId != 0) {
        // Check if tags don't match
        if (String.IsNullOrEmpty(ptag) 
          || (!String.IsNullOrEmpty(ptag) 
            && (String.IsNullOrEmpty(rtag) || ptag != rtag)
          )
        ) {
            if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: " + recruit.FullName + "is already in a squad and tags don't match");
            msg = "*** Already in a squad and tags don't match: " + recruit.FullName;
            ChatPlayer(player.Name);
            return false;
        }
    }
/*
    // Check if squad id is above 8 as of R20 patch
    if (player.SquadId > 8) {
        if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: " + player.FullName + " is in a custom squad #" + recruit.SquadId);
        msg = "*** You are in custom squad #" + player.SquadId + " and cannot recruit players";
        plugin.SendSquadMessage(player.TeamId, player.SquadId, msg);
        plugin.PRoConChat( "ADMIN > " + msg);
        return false;
    }
*/
    // Check if recruit is in the cooldown pool and has different tags
    if (!server.RoundData.issetObject(kCooldown)) {
        server.RoundData.setObject(kCooldown, new List<String>());
    }
    List<String> coolDown = (List<String>)server.RoundData.getObject(kCooldown);
    if (coolDown.Contains(recruit.Name)) {
        if (!String.IsNullOrEmpty(rtag) && !String.IsNullOrEmpty(ptag) && ptag != rtag) {
            if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: in cooldown pool: " + recruit.FullName);
            msg = "*** Already been moved this round: " + recruit.FullName;
            ChatPlayer(player.Name);
            return false;
        } else {
            // Recruit with matching tag is allowed to be moved again
            coolDown.Remove(recruit.Name);
            server.RoundData.setObject(kCooldown, coolDown);
        }
    }
    // Add to list
    String kCadets = (player.TeamId == 1) _ kUSCadets : kRUCadets;
    if (!server.RoundData.issetObject(kCadets)) {
        server.RoundData.setObject(kCadets, new Dictionary<String,int>());
    }
    Dictionary<String,int> teamCadets = (Dictionary<String,int>)server.RoundData.getObject(kCadets);
    teamCadets.Add(recruit.Name, player.SquadId);
    if (level >= 2) {
        String[] squadName = new String[9]{"No Squad", "Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel"};
        String sqn = (player.SquadId > 8) _ ("Squad-" + player.SquadId) : squadName[player.SquadId];
        String teamName = (player.TeamId == 1) _ "US" : "RU";
        if (level >= 2 ) plugin.ConsoleWrite("^b[SQR]^n: new cadet for squad " + sqn + " on team " + teamName + ": " + recruit.FullName);
    }
    msg = "*** Insane Limits will attempt to move " + recruit.FullName + " to your squad on next death.";
    plugin.SendTeamMessage(player.TeamId, msg);
    plugin.PRoConChat( "ADMIN > " + msg);
    return false;
}

/* !draft command with no arguments */

if (d0Match.Success) {
    // Report enabled/disabled
    if (!plugin.Data.issetBool(kDraft)) {
        plugin.Data.setBool(kDraft, false);
    }
    bool isDraftEnabled = plugin.Data.getBool(kDraft);
    if (isDraftEnabled) {
        msg = "*** Insane Limits squad draft mode is:\n*** ON!";
    } else {
        msg = "*** Insane Limits squad draft mode is:\n*** OFF!";
    }
    plugin.SendGlobalMessage(msg);
    plugin.PRoConChat( "ADMIN > " + msg);
    return false;
}

/* !draft command with arguments */

if (d1Match.Success) {
    String tag = player.Tag;
    if (tag.Length == 0) {
        // Maybe they are using [_-=]XXX[=-_]PlayerName format
        Match tm = Regex.Match(player.Name, @"^[=_\-]_([^=_\-]{2,4})[=_\-]");
        if (tm.Success) {
            tag = tm.Groups[1].Value;
            if (level >= 4) plugin.ConsoleWrite("^b[SQR]^n extracted [" + tag + "] from " + player.Name);
        } else {
            tag = "no tag";
        }
    }
    if (!plugin.isInList(player.Name, admins) && !plugin.isInList(tag, admin_tags)) {
        if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: " + "[" + tag + "]" + player.Name + " not authorized to use !draft command.");
        msg = "*** You are not authorized to change the draft!";
        plugin.SendSquadMessage(player.TeamId, player.SquadId, msg);
        plugin.PRoConChat( "ADMIN > " + msg);
        return false;
    }
    // Enable/Disable
    String state = "OFF!";
    if (!plugin.Data.issetBool(kDraft)) {
        plugin.Data.setBool(kDraft, false);
    } else if (Regex.Match(d1Match.Groups[1].Value, "(on|1|true)", RegexOptions.IgnoreCase).Success) {
        plugin.Data.setBool(kDraft, true);
        state = "ON!";
    } else {
        plugin.Data.setBool(kDraft, false);
    }
    msg = "*** Insane Limits squad draft mode is:\n***" + state;
    plugin.SendGlobalMessage(msg);
    plugin.PRoConChat( "ADMIN > " + msg);
    return false;
}

return false;

 

 

 

 

Step 4

 

Create a new limit and set it Enabled. Set limit to evaluate OnDeath, set the name to Squad Recruiter, set the action to None.

 

Set first_check to this Code:

 

Code:

/* Version: V9.16/R5 */

if ( Regex.Match(server.Gamemode, "^(_:Conquest|Rush)", RegexOptions.IgnoreCase).Success ) {
    bool initialDraftMode = true; // Change this to false to start with mode OFF
    
    int level = 2;

    try {
        level = Convert.ToInt32(plugin.getPluginVarValue("debug_level"));
    } catch (Exception e) {}
    
    // Check if victim is in the whitelist
    if (plugin.isInPlayerWhitelist(victim.Name)) return false;

    String prefix = "SQR_";
    String kUSCadets = prefix + "T1_cadets"; // server.RoundData
    String kRUCadets = prefix + "T2_cadets"; // server.RoundData
    String kCooldown = prefix + "cooldown"; // server.RoundData
    String kDraft = prefix + "draft"; // plugin.Data

    if (!plugin.Data.issetBool(kDraft)) {
        plugin.Data.setBool(kDraft, initialDraftMode);
    }
    bool isDraftEnabled = plugin.Data.getBool(kDraft);

    String kCadets = (victim.TeamId == 1) _ kUSCadets : kRUCadets;

    if (!server.RoundData.issetObject(kCadets)) {
        server.RoundData.setObject(kCadets, new Dictionary<String, int>());
    }
    Dictionary<String, int> cadets = (Dictionary<String, int>)server.RoundData.getObject(kCadets);
    bool isCadet = cadets.ContainsKey(victim.Name);
    
    if (!isDraftEnabled && !isCadet) return false; // Nothing to do

    if (!server.RoundData.issetObject(kCooldown)) {
        server.RoundData.setObject(kCooldown, new List<String>());
    }
    List<String> coolDown = (List<String>)server.RoundData.getObject(kCooldown);
    bool isCooldown = coolDown.Contains(victim.Name);

    // Check if victim is not a cadet and is in a squad
    if (!isCadet && victim.SquadId != 0) return false;
    
    // Check if victim is in the cooldown pool (draft attempted this round)
    if (!isCadet && isCooldown) {
        if (level >= 3) plugin.ConsoleWrite("^b[SQR]^n: move already tried: " + victim.FullName);
        return false;
    }
    
    // Otherwise, count this activation and proceed
    return true;
}
return false;
Set second_check to this Code (BF4 Version, requires Insane Limits 0.9.16.0 or later):

 

Code:

/* Version: V9.16/R5 */

/* Find a squad to place this dead lone wolf in */

String prefix = "SQR_";
String kT1Cadets = prefix + "T1_cadets"; // server.RoundData
String kT2Cadets = prefix + "T2_cadets"; // server.RoundData
String kDraft = prefix + "draft"; // plugin.Data
String kCooldown = prefix + "cooldown"; // server.RoundData

int level = 2;

try {
    level = Convert.ToInt32(plugin.getPluginVarValue("debug_level"));
} catch (Exception e) {}

/* If draft mode is on or victim on cadet list, attempt move */

String msg = "none";

bool isDraftEnabled = plugin.Data.getBool(kDraft);

if (isDraftEnabled && victim.SquadId == 0 && limit.Activations(victim.Name) == 1) {
    msg = "*** " + victim.FullName + ": don't be a draft dodger, join a squad!";
    plugin.SendTeamMessage(victim.TeamId, msg);
    plugin.PRoConChat("ADMIN > " + msg);
    if (level >= 3) plugin.ConsoleWrite("^b[SQR]^n: announced to " + victim.FullName);
    return false; // First death just gets a message
}

String kCadets = (victim.TeamId == 1) _ kT1Cadets : kT2Cadets;

Dictionary<String,int> cadets = (Dictionary<String,int>)server.RoundData.getObject(kCadets);
bool isCadet = cadets.ContainsKey(victim.Name);

List<String> coolDown = (List<String>)server.RoundData.getObject(kCooldown);
bool isCooldown = coolDown.Contains(victim.Name);

if (!isDraftEnabled && !isCadet) return false; // Nothing to do

/* We are clear to attempt to place the victim in a squad! */

// Find a squad, most full to least

int[] squads = new int[33]; // R20/Alpha - Hotel - 32 + the "no squad" (0)

TeamInfoInterface team = (victim.TeamId == 1) _ team1 : team2;
String[] factionNames = new String[3]{"US", "RU", "CN"};
String teamName = "__";

foreach (PlayerInfoInterface p in team.players) {
    int ii = p.SquadId;
    squads[p.SquadId] += 1;
}

int targetTeam = victim.TeamId;
int targetSquad = 0;

if (isCadet) {
    int teamSquad = cadets[victim.Name];
    targetTeam = (teamSquad / 1000);
    targetSquad = (teamSquad % 1000);
    if (squads[targetSquad] == 5) {
        if (level >= 2) plugin.ConsoleWarn("^b[SQR]^n: squad " + targetSquad.ToString() + " is full: " + victim.FullName);
        msg = "*** Can't move " + victim.FullName + "to your squad, it's full!";
        plugin.SendSquadMessage(targetTeam, targetSquad, msg);
        plugin.PRoConChat("ADMIN > " + msg);
        return false; // No squad available
    }
} else {
    for (int n = 3; n >= 0; --n) {
        for (int s = 1; s <= 32; s++) {
            if (squads[s] == n) {
                targetSquad = s;
                break;
            }
        }
        if (targetSquad != 0) break;
    }
}

int faction = server.GetFaction(targetTeam);
if (faction >= 0 && faction <= 2) teamName = factionNames[faction];

if (targetSquad == 0) {
    if (level >= 2) plugin.ConsoleWarn("^b[SQR]^n: no squad found for: " + victim.FullName + " on " + teamName);
    return false; // No squad available
}

/* 
Attempt the move - this may fail for multiple reasons. 
- Squad may be locked.
- Someone else may move ahead of this command.
- etc.
We'll give it two tries and then give up by putting player in the cooldown pool
*/

String[] squadName = new String[] { "None",
      "Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel",
      "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa",
      "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey", "Xray",
      "Yankee", "Zulu", "Haggard", "Sweetwater", "Preston", "Redford", "Faith", "Celeste"
    };
String sqn = (targetSquad >= 0) _ squadName[targetSquad] : "_";

if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: ^b^4MOVE^0^n " + victim.FullName + " to " + sqn + " on " + teamName);
msg = "*** Recruiting " + victim.FullName + " to " + sqn + " on " + teamName;
plugin.SendTeamMessage(targetTeam, msg);
if (victim.TeamId != targetTeam) plugin.SendPlayerMessage(victim.Name, msg);
plugin.PRoConChat("ADMIN > " + msg);
plugin.PRoConEvent(msg, "Insane Limits");
plugin.ServerCommand("admin.movePlayer", victim.Name, targetTeam.ToString(), targetSquad.ToString(), "false");

/* Clean-up */

// Remove cadet from cadet list
if (isCadet) {
    cadets.Remove(victim.Name);
}

// Add to cooldown pool if second try
if (!isCooldown && limit.Activations(victim.Name) > 1) {
    if (level >= 3) plugin.ConsoleWrite("^b[SQR]^n: cool down " + victim.FullName);
    coolDown.Add(victim.Name);
}
return false;
Original BF3 Version:

 

 

Code:

/* Version: V0.8/R4 */

/* Find a squad to place this dead lone wolf in */

String prefix = "SQR_";
String kUSCadets = prefix + "US_cadets"; // server.RoundData
String kRUCadets = prefix + "RU_cadets"; // server.RoundData
String kDraft = prefix + "draft"; // plugin.Data
String kCooldown = prefix + "cooldown"; // server.RoundData

int level = 2;

try {
    level = Convert.ToInt32(plugin.getPluginVarValue("debug_level"));
} catch (Exception e) {}

/* If draft mode is on or victim on cadet list, attempt move */

String msg = "none";

bool isDraftEnabled = plugin.Data.getBool(kDraft);

if (isDraftEnabled && victim.SquadId == 0 && limit.Activations(victim.Name) == 1) {
    msg = "*** " + victim.FullName + ": don't be a draft dodger, join a squad!";
    plugin.SendTeamMessage(victim.TeamId, msg);
    plugin.PRoConChat("ADMIN > " + msg);
    if (level >= 3) plugin.ConsoleWrite("^b[SQR]^n: announced to " + victim.FullName);
    return false; // First death just gets a message
}

String kCadets = (victim.TeamId == 1) _ kUSCadets : kRUCadets;

Dictionary<String,int> cadets = (Dictionary<String,int>)server.RoundData.getObject(kCadets);
bool isCadet = cadets.ContainsKey(victim.Name);

List<String> coolDown = (List<String>)server.RoundData.getObject(kCooldown);
bool isCooldown = coolDown.Contains(victim.Name);

if (!isDraftEnabled && !isCadet) return false; // Nothing to do

/* We are clear to attempt to place the victim in a squad! */

// Find a squad, most full to least

int[] squads = new int[33]; // R20/Alpha - Hotel - 32 + the "no squad" (0)

TeamInfoInterface team = (victim.TeamId == 1) _ team1 : team2;
String teamName = (victim.TeamId == 1) _ "US" : "RU";

foreach (PlayerInfoInterface p in team.players) {
    int ii = p.SquadId;
    squads[p.SquadId] += 1;
}

int targetSquad = 0;

if (isCadet) {
    targetSquad = cadets[victim.Name];
    if (squads[targetSquad] == 4) {
        if (level >= 2) plugin.ConsoleWarn("^b[SQR]^n: squad " + targetSquad.ToString() + " on " + teamName + " is full: " + victim.FullName);
        msg = "*** Can't move " + victim.FullName + "to your squad, it's full!";
        plugin.SendSquadMessage(victim.TeamId, targetSquad, msg);
        plugin.PRoConChat("ADMIN > " + msg);
        return false; // No squad available
    }
} else {
    for (int n = 3; n >= 0; --n) {
        for (int s = 1; s <= 32; s++) {
            if (squads[s] == n) {
                targetSquad = s;
                break;
            }
        }
        if (targetSquad != 0) break;
    }
}

if (targetSquad == 0) {
    if (level >= 2) plugin.ConsoleWarn("^b[SQR]^n: no squad found for: " + victim.FullName + " on " + teamName);
    return false; // No squad available
}

/* 
Attempt the move - this may fail for multiple reasons. 
- Squad may be locked.
- Someone else may move ahead of this command.
- etc.
We'll give it two tries and then give up by putting player in the cooldown pool
*/

String[] squadName = new String[9]{"No Squad", "Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel"};
String sqn = (targetSquad > 8) _ ("Squad-" + targetSquad) : squadName[targetSquad];

if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: ^b^4MOVE^0^n " + victim.FullName + " to " + sqn + " on " + teamName);
msg = "*** Insane Limits attempting to place " + victim.FullName + " in squad " + sqn + " on team " + teamName;
plugin.SendTeamMessage(victim.TeamId, msg);
plugin.PRoConChat("ADMIN > " + msg);
plugin.PRoConEvent(msg, "Insane Limits");
plugin.ServerCommand("admin.movePlayer", victim.Name, victim.TeamId.ToString(), targetSquad.ToString(), "false");

/* Clean-up */

// Remove cadet from cadet list
if (isCadet) {
    cadets.Remove(victim.Name);
}

// Add to cooldown pool if second try
if (!isCooldown && limit.Activations(victim.Name) > 1) {
    if (level >= 3) plugin.ConsoleWrite("^b[SQR]^n: cool down " + victim.FullName);
    coolDown.Add(victim.Name);
}
return true;

 

 

 

NOTES

 

  • The Insane Limits Draft starts ON when the limit is Enabled. To start it as OFF, go to the OnDeath limit and look in the first_check code for a line containing initialDraftMode. Change the value from true to false.
  • The commands are case insensitive, so you may type !Draft or !DRAFT or !Recruit, etc.
  • The cadet name in the !recruit command uses the best match function, so abbreviations and misspelling are tolerated. However, this does mean that sometimes the limit may attempt to move the wrong player into a squad.
  • Restarting PRoCon or disabling/re-enabling the Insane Limits plugin will cause all the pending moves and the cool down pool to be forgotten. It will be as if the round just started.

 

 

 

At debug level 2 or higher, the plugin console log is updated every time a player is moved by Squad Recruiter. The full name of the player, the destination squad and destination team are included in the update. For example:

 

[20:42:52] [insane Limits] [sQR]: MOVE [uN]XxiLikEtAcoSxX to Alpha on RU

 

"SQR" is an acronym for "SQuad Recruiter".

 

Also at debug level 2, any command that is rejected because of failure to meet required policy conditions, such as trying to use the !draft on command when not an admin or trying to use the !recruit command when not in a squad, will result in a chat message to the player and an update to the console log, for example:

 

[18:15:31] [insane Limits] [sQR]: LtGeneralChesty is already in a squad and tags don't match

 

At debug level 3 or higher, additional debugging messages are logged.

 

Updates

 

Squad Recruiter Commands - OnAnyChat

 

R5 - updated to BF4

R4 - better fix for R20, added '!recruit _' for info, !recruit now works across teams for players with the same tags

R3 - quick patch for R20

R2 - improved tag comparison logic

R1 - original version

 

Squad Recruiter - OnDeath

 

R5 - updated to BF4

R4 - better fix for R20, added '!recruit _' for info, !recruit now works across teams for players with the same tags

R3 - quick patch for R20

R2 - added return false at the end of first_check

R1 - original version

* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by micovery*:

 

Wow this looks great. I think we need to come up with a way to package multi-limit extensions like this one ... where user does not have to do any copy+paste ... they just load the pacakge file, and everything is loaded up and ready to go.

 

Here are some ideas I have.

 

1. Dump all limits *.cs, and zip them up (With each *.cs, you need accompanying *.conf for the settings)

2. Save the limits *.conf file, and have the user load them up. Plugin would take care of assigning a unique limit id.

 

I like approach #1 better, because it will give you the possibility to make the LimitEvaluator.cs class with whatever you want ... You could have private class variables, etc.

 

EDIT:

 

I'll add a new command, "!make package 1,2,3". This will create a pacakge out of limits #1, #2, and #3. There would also be accompanying command "!load pacakge foo.zip" .. which will load up the package. I think this will make the multi-limit setup (like this one) a lot easier.

* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by PapaCharlie9*:

 

Yeah, I've been wanting something like that.

 

How about sticking ALL of the limit config stuff into properties in the class and dump them as initialization values? Then the loader can get the config info through reflection by instantiating the class. Also makes it handy for the limit code to self reflect independently from the id number.

 

What will you do about the limit id being built into the class name? You don't want the loader to clobber an existing limit with the same id.

 

If you have time, how about a !validate command also, that loads the limit in a sandbox/temporary object and compares it to the same-named limits already loaded to make sure they match? Otherwise, to test that I saved everything in the package correctly, I'll have to delete all the relevant limits or use a different connection context to load the package into.

* Restored post. It could be that the author is no longer active.
Link to comment
  • 2 weeks later...

Originally Posted by PapaCharlie9*:

 

Can't compile the Squad recruiter. The SQRC is ok.

More information, please. Are you using Insane Limits 0.0.0.8 patch 3? This limit requires 0.8. Did you copy&paste both the first_check and the second_check correctly as Code? It's a common error to do one or the other as Expression.

 

What exactly is the error you get, with line/column number?

* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by elfiero*:

 

Both set to code, using patch 2 atm since it seems stable. Error message is... Code:

[11:34:30 03] [Insane Limits] Thread(settings): Compiling Limit #15 - Squad Recruiter - OnDeath
[11:34:30 06] [Insane Limits] Thread(settings): ERROR: 1 error compiling Code
And I pasted this to first_check:

/* Version: V0.8/R1 */ if ( Regex.Match(server.Gamemode, "^(_:Conquest|Rush)", RegexOptions.IgnoreCase).Success ) { bool initialDraftMode = true; // Change this to false to start with mode OFF int level = 2; try { level = Convert.ToInt32(plugin.getPluginVarValue("debug_le vel")); } catch (Exception e) {} // Check if victim is in the whitelist if (plugin.isInPlayerWhitelist(victim.Name)) return false; String prefix = "SQR_"; String kUSCadets = prefix + "US_cadets"; // server.RoundData String kRUCadets = prefix + "RU_cadets"; // server.RoundData String kCooldown = prefix + "cooldown"; // server.RoundData String kDraft = prefix + "draft"; // plugin.Data if (!plugin.Data.issetBool(kDraft)) { plugin.Data.setBool(kDraft, initialDraftMode); } bool isDraftEnabled = plugin.Data.getBool(kDraft); String kCadets = (victim.TeamId == 1) ? kUSCadets : kRUCadets; if (!server.RoundData.issetObject(kCadets)) { server.RoundData.setObject(kCadets, new Dictionary()); } Dictionary cadets = (Dictionary)server.RoundData.getObject(kCadets); bool isCadet = cadets.ContainsKey(victim.Name); if (!isDraftEnabled && !isCadet) return false; // Nothing to do if (!server.RoundData.issetObject(kCooldown)) { server.RoundData.setObject(kCooldown, new List()); } List coolDown = (List)server.RoundData.getObject(kCooldown ); bool isCooldown = coolDown.Contains(victim.Name); // Check if victim is not a cadet and is in a squad if (!isCadet && victim.SquadId != 0) return false; // Check if victim is in the cooldown pool (draft attempted this round) if (!isCadet && isCooldown) { if (level >= 3) plugin.ConsoleWrite("^b[sQR]^n: move already tried: " + victim.FullName); return false; } // Otherwise, count this activation and proceed return true; }

and this for second_check:

/* Version: V0.8/R1 */ /* Find a squad to place this dead lone wolf in */ String prefix = "SQR_"; String kUSCadets = prefix + "US_cadets"; // server.RoundData String kRUCadets = prefix + "RU_cadets"; // server.RoundData String kDraft = prefix + "draft"; // plugin.Data String kCooldown = prefix + "cooldown"; // server.RoundData int level = 2; try { level = Convert.ToInt32(plugin.getPluginVarValue("debug_le vel")); } catch (Exception e) {} /* If draft mode is on or victim on cadet list, attempt move */ String msg = "none"; bool isDraftEnabled = plugin.Data.getBool(kDraft); if (isDraftEnabled && victim.SquadId == 0 && limit.Activations(victim.Name) == 1) { msg = "*** Insane Limits Squad Draft is ON!\n"+victim.FullName+": don't be a draft dodger, join a squad!"; plugin.SendTeamMessage(victim.TeamId, msg); plugin.PRoConChat("ADMIN > " + msg); if (level >= 3) plugin.ConsoleWrite("^b[sQR]^n: announced to " + victim.FullName); return false; // First death just gets a message } String kCadets = (victim.TeamId == 1) ? kUSCadets : kRUCadets; Dictionary cadets = (Dictionary)server.RoundData.getObject (kCadets); bool isCadet = cadets.ContainsKey(victim.Name); List coolDown = (List)server.RoundData.getObject(kCooldown ); bool isCooldown = coolDown.Contains(victim.Name); if (!isDraftEnabled && !isCadet) return false; // Nothing to do /* We are clear to attempt to place the victim in a squad! */ // Find a squad, most full to least int[] squads = new int[9]; // Alpha - Hotel + the "no squad" (0) TeamInfoInterface team = (victim.TeamId == 1) ? team1 : team2; String teamName = (victim.TeamId == 1) ? "US" : "RU"; foreach (PlayerInfoInterface p in team.players) { squads[p.SquadId] += 1; } int targetSquad = 0; if (isCadet) { targetSquad = cadets[victim.Name]; if (squads[targetSquad] == 4) { if (level >= 2) plugin.ConsoleWarn("^b[sQR]^n: squad " + targetSquad.ToString() + " on " + teamName + " is full: " + victim.FullName); msg = "*** Can't move " + victim.FullName + "to your squad, it's full!"; plugin.SendSquadMessage(victim.TeamId, targetSquad, msg); plugin.PRoConChat("ADMIN > " + msg); return false; // No squad available } } else { for (int n = 3; n >= 0; --n) { for (int s = 1; s = 2) plugin.ConsoleWarn("^b[sQR]^n: no squad found for: " + victim.FullName + " on " + teamName); return false; // No squad available } /* Attempt the move - this may fail for multiple reasons. - Squad may be locked. - Someone else may move ahead of this command. - etc. We'll give it two tries and then give up by putting player in the cooldown pool */ String[] squadName = new String[9]{"No Squad", "Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel"}; if (level >= 2) plugin.ConsoleWrite("^b[sQR]^n: ^b^4MOVE^0^n " + victim.FullName + " to " + squadName[targetSquad] + " on " + teamName); msg = "*** Insane Limits attempting to place " + victim.FullName + " in squad " + squadName[targetSquad] + " on team " + teamName; plugin.SendTeamMessage(victim.TeamId, msg); plugin.PRoConChat("ADMIN > " + msg); plugin.PRoConEvent(msg, "Insane Limits"); plugin.ServerCommand("admin.movePlayer", victim.Name, victim.TeamId.ToString(), targetSquad.ToString(), "false"); /* Clean-up */ // Remove cadet from cadet list if (isCadet) { cadets.Remove(victim.Name); } // Add to cooldown pool if second try if (!isCooldown && limit.Activations(victim.Name) > 2) { if (level >= 3) plugin.ConsoleWrite("^b[sQR]^n: cool down " + victim.FullName); coolDown.Add(victim.Name); } return true;

* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by PapaCharlie9*:

 

Both set to code, using patch 2 atm since it seems stable. Error message is... Code:

[11:34:30 03] [Insane Limits] Thread(settings): Compiling Limit #15 - Squad Recruiter - OnDeath
[11:34:30 06] [Insane Limits] Thread(settings): ERROR: 1 error compiling Code
No line/column number? There should be a line after the ERROR line that looks something like this:

 

Code:

[10:01:57 06] [Insane Limits] ERROR: (CS1010, line: 83, column: 13): (some message here)
I can't really help without that info.

 

I updated the Squad Recruiter to R2. Try pasting it again. I just tried both first_check and second_check and they worked fine for me.

* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by PapaCharlie9*:

 

doesnt this just work like the squad enforcer plugin ? i dont know

Yes, it does, but it adds some additional features, like:

 

!recruit player

 

Any player can try and recruit another player into their squad, without having to yell at them in chat, "Join my squad". The other player doesn't have to do anything. The next time they die, they are moved to the squad, within the restrictions listed in the OP. It's great for clan players pulling other clan players into their squad.

* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by PapaCharlie9*:

 

I will update it. No error for columns or lines sry... if I remove a slash or anything I get one :biggrin:

 

EDIT: No change, still no error message :sad:

Let's try this. Which limit number are you using? Suppose it is limit #1. Do this in the "console" line of Insane Limits where is says "Type a command here"

 

!dump limit 1

 

(If you are using a different limit number than 1, use that number instead.)

 

Look in your procon folder for LimitEvaluator1.cs (or whatever number your limit is) and post it here as an attachment.

* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by PapaCharlie9*:

 

ok, Done

Okay, I think you have some kind of copy&paste problem, perhaps due to whatever browser you are using to view the forum. What browser and version are you using? Can you try something else, like if you are using Opera, switch to Chrome or Firefox?

 

Here's what your file looks like. All of the line-endings (CR LF) are gone. All of the code is in one line that confuses the compiler:

 

Code:

/* Version: V0.8/R2 */ if ( Regex.Match(server.Gamemode, "^(_:Conquest|Rush)", RegexOptions.IgnoreCase).Success ) { bool initialDraftMode = true; // Change this to false to start with mode OFF int level = 2; try { level = Convert.ToInt32(plugin.getPluginVarValue("debug_level")); } catch (Exception e) {} // Check if victim is in the whitelist if (plugin.isInPlayerWhitelist(victim.Name)) return false; String prefix = "SQR_"; String kUSCadets = prefix + "US_cadets"; // server.RoundData String kRUCadets = prefix + "RU_cadets"; // server.RoundData String kCooldown = prefix + "cooldown"; // server.RoundData String kDraft = prefix + "draft"; // plugin.Data if (!plugin.Data.issetBool(kDraft)) { plugin.Data.setBool(kDraft, initialDraftMode); } bool isDraftEnabled = plugin.Data.getBool(kDraft); String kCadets = (victim.TeamId == 1) _ kUSCadets : kRUCadets; if (!server.RoundData.issetObject(kCadets)) { server.RoundData.setObject(kCadets, new Dictionary<String, int>()); } Dictionary<String, int> cadets = (Dictionary<String, int>)server.RoundData.getObject(kCadets); bool isCadet = cadets.ContainsKey(victim.Name); if (!isDraftEnabled && !isCadet) return false; // Nothing to do if (!server.RoundData.issetObject(kCooldown)) { server.RoundData.setObject(kCooldown, new List<String>()); } List<String> coolDown = (List<String>)server.RoundData.getObject(kCooldown); bool isCooldown = coolDown.Contains(victim.Name); // Check if victim is not a cadet and is in a squad if (!isCadet && victim.SquadId != 0) return false; // Check if victim is in the cooldown pool (draft attempted this round) if (!isCadet && isCooldown) { if (level >= 3) plugin.ConsoleWrite("^b[SQR]^n: move already tried: " + victim.FullName); return false; } // Otherwise, count this activation and proceed return true; } return false;
It's supposed to look like this:

Code:

/* Version: V0.8/R2 */
            
            if ( Regex.Match(server.Gamemode, "^(_:Conquest|Rush)", RegexOptions.IgnoreCase).Success ) {
            	bool initialDraftMode = true; // Change this to false to start with mode OFF
            	
            	int level = 2;
            
            	try {
            		level = Convert.ToInt32(plugin.getPluginVarValue("debug_level"));
            	} catch (Exception e) {}
            	
            	// Check if victim is in the whitelist
            	if (plugin.isInPlayerWhitelist(victim.Name)) return false;
            
            	String prefix = "SQR_";
            	String kUSCadets = prefix + "US_cadets"; // server.RoundData
            	String kRUCadets = prefix + "RU_cadets"; // server.RoundData
            	String kCooldown = prefix + "cooldown"; // server.RoundData
            	String kDraft = prefix + "draft"; // plugin.Data
            
            	if (!plugin.Data.issetBool(kDraft)) {
            		plugin.Data.setBool(kDraft, initialDraftMode);
            	}
            	bool isDraftEnabled = plugin.Data.getBool(kDraft);
            
            	String kCadets = (victim.TeamId == 1) _ kUSCadets : kRUCadets;
            
            	if (!server.RoundData.issetObject(kCadets)) {
            		server.RoundData.setObject(kCadets, new Dictionary<String, int>());
            	}
            	Dictionary<String, int> cadets = (Dictionary<String, int>)server.RoundData.getObject(kCadets);
            	bool isCadet = cadets.ContainsKey(victim.Name);
            	
            	if (!isDraftEnabled && !isCadet) return false; // Nothing to do
            
            	if (!server.RoundData.issetObject(kCooldown)) {
            		server.RoundData.setObject(kCooldown, new List<String>());
            	}
            	List<String> coolDown = (List<String>)server.RoundData.getObject(kCooldown);
            	bool isCooldown = coolDown.Contains(victim.Name);
            
            	// Check if victim is not a cadet and is in a squad
            	if (!isCadet && victim.SquadId != 0) return false;
            	
            	// Check if victim is in the cooldown pool (draft attempted this round)
            	if (!isCadet && isCooldown) {
            		if (level >= 3) plugin.ConsoleWrite("^b[SQR]^n: move already tried: " + victim.FullName);
            		return false;
            	}
            	
            	// Otherwise, count this activation and proceed
            	return true;
            }
            return false;
Here's my file attached. See if you can copy&paste from this file instead of from your browser:

LimitEvaluator15.zip

* Restored post. It could be that the author is no longer active.
Link to comment
  • 2 weeks later...

Originally Posted by PapaCharlie9*:

 

Anyway to make this compatible with Team Deathmatch? I'm only interested in the !recruit bit.

Do squads work in TDM? I thought they don't really have any use. I know this is for Zombie Mode, but can you spawn on someone else if they are in your squad?

 

Anyway, this should work, though I haven't tested it. Find all the first_check code (Steps 3 and 4) that has "^(_:Conquest|Rush)" and change to:

 

Code:

"^(_:Conquest|Rush|Team)"
* Restored post. It could be that the author is no longer active.
Link to comment
  • 5 weeks later...

Originally Posted by PapaCharlie9*:

 

Updated original post & code for R20 patch and for some additional features:

 

* '!recruit _' provide info on how to use the command

* !recruit now works across teams if the players have the same tag

* Restored post. It could be that the author is no longer active.
Link to comment
  • 1 year later...

Originally Posted by miked0602*:

 

Papa, thank you for all your hard work.

Hey Papa,

 

I just wanted to add that I took this script and made it work for BF4.

This is the edited new "second check code" for Squad Recuiter Limit.

My changes reflect the 5 man squads of bf4 and the character length issue on line 118 column 47.

 

Hope everyone will enjoy.

This is tested on Donkey Punch Extravaganza - RAV Hardcore Server

and is working with about a 3 minute delay until squaded up.

 

Code:

/* Version: V0.8/R4 */ BF4 modification by RAV MusTanG

/* Find a squad to place this dead lone wolf in */

String prefix = "SQR_";
String kUSCadets = prefix + "US_cadets"; // server.RoundData
String kRUCadets = prefix + "RU_cadets"; // server.RoundData
String kDraft = prefix + "draft"; // plugin.Data
String kCooldown = prefix + "cooldown"; // server.RoundData

int level = 2;

try {
    level = Convert.ToInt32(plugin.getPluginVarValue("debug_level"));
} catch (Exception e) {}

/* If draft mode is on or victim on cadet list, attempt move */

String msg = "none";

bool isDraftEnabled = plugin.Data.getBool(kDraft);

if (isDraftEnabled && victim.SquadId == 0 && limit.Activations(victim.Name) == 1) {
	msg = "*** victim.FullName+ join a squad!";
	plugin.SendTeamMessage(victim.TeamId, msg);
	plugin.PRoConChat("ADMIN > " + msg);
	if (level >= 3) plugin.ConsoleWrite("^b[SQR]^n: announced to " + victim.FullName);
	return false; // First death just gets a message
}

String kCadets = (victim.TeamId == 1) _ kUSCadets : kRUCadets;

Dictionary<String,int> cadets = (Dictionary<String,int>)server.RoundData.getObject(kCadets);
bool isCadet = cadets.ContainsKey(victim.Name);

List<String> coolDown = (List<String>)server.RoundData.getObject(kCooldown);
bool isCooldown = coolDown.Contains(victim.Name);

if (!isDraftEnabled && !isCadet) return false; // Nothing to do

/* We are clear to attempt to place the victim in a squad! */

// Find a squad, most full to least

int[] squads = new int[33]; // R20/Alpha - Hotel - 32 + the "no squad" (0)

TeamInfoInterface team = (victim.TeamId == 1) _ team1 : team2;
String teamName = (victim.TeamId == 1) _ "US" : "RU";

foreach (PlayerInfoInterface p in team.players) {
	int ii = p.SquadId;
	squads[p.SquadId] += 1;
}

int targetSquad = 0;

if (isCadet) {
	targetSquad = cadets[victim.Name];
	if (squads[targetSquad] == 5) {
		if (level >= 2) plugin.ConsoleWarn("^b[SQR]^n: squad " + targetSquad.ToString() + " on " + teamName + " is full: " + victim.FullName);
		msg = "*** Can't move " + victim.FullName + "to your squad, it's full!";
		plugin.SendSquadMessage(victim.TeamId, targetSquad, msg);
		plugin.PRoConChat("ADMIN > " + msg);
		return false; // No squad available
	}
} else {
	for (int n = 3; n >= 0; --n) {
		for (int s = 1; s <= 32; s++) {
			if (squads[s] == n) {
				targetSquad = s;
				break;
			}
		}
		if (targetSquad != 0) break;
	}
}

if (targetSquad == 0) {
	if (level >= 2) plugin.ConsoleWarn("^b[SQR]^n: no squad found for: " + victim.FullName + " on " + teamName);
	return false; // No squad available
}

/* 
Attempt the move - this may fail for multiple reasons. 
- Squad may be locked.
- Someone else may move ahead of this command.
- etc.
We'll give it two tries and then give up by putting player in the cooldown pool
*/

String[] squadName = new String[9]{"No Squad", "Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel"};
String sqn = (targetSquad > 8) _ ("Squad-" + targetSquad) : squadName[targetSquad];

if (level >= 2) plugin.ConsoleWrite("^b[SQR]^n: ^b^4MOVE^0^n " + victim.FullName + " to " + sqn + " on " + teamName);
msg = "*** Insane Limits attempting to place " + victim.FullName + " in squad " + sqn + " on team " + teamName;
plugin.SendTeamMessage(victim.TeamId, msg);
plugin.PRoConChat("ADMIN > " + msg);
plugin.PRoConEvent(msg, "Insane Limits");
plugin.ServerCommand("admin.movePlayer", victim.Name, victim.TeamId.ToString(), targetSquad.ToString(), "false");

/* Clean-up */

// Remove cadet from cadet list
if (isCadet) {
	cadets.Remove(victim.Name);
}

// Add to cooldown pool if second try
if (!isCooldown && limit.Activations(victim.Name) > 1) {
	if (level >= 3) plugin.ConsoleWrite("^b[SQR]^n: cool down " + victim.FullName);
	coolDown.Add(victim.Name);
}
return true;
* Restored post. It could be that the author is no longer active.
Link to comment
  • 1 month later...

Originally Posted by kaminari*:

 

I get an error when I put the plug-in BF4. It is as follows.

 

Insane Limits] Compiling Limit # 6 - Squad Recruiter - OnDeath

[22:40:49 00] [insane Limits] ERROR: 3 errors compiling Code

[22:40:49 00] [insane Limits] ERROR: (CS1002, line: 96, column: 53):;____?

[22:40:49 01] [insane Limits] ERROR: (CS1002, line: 96, column: 60):;____?

[22:40:49 01] [insane Limits] ERROR: (CS1002, line: 100, column: 20):;____?

 

Thank you.

* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by PapaCharlie9*:

 

I get an error when I put the plug-in BF4. It is as follows.

 

Insane Limits] Compiling Limit # 6 - Squad Recruiter - OnDeath

[22:40:49 00] [insane Limits] ERROR: 3 errors compiling Code

[22:40:49 00] [insane Limits] ERROR: (CS1002, line: 96, column: 53):;____?

[22:40:49 01] [insane Limits] ERROR: (CS1002, line: 96, column: 60):;____?

[22:40:49 01] [insane Limits] ERROR: (CS1002, line: 100, column: 20):;____?

 

Thank you.

There was a mistake in the Step 4 OnDeath second_check Code. Try doing Step 4 over again now, I fixed post #1.
* Restored post. It could be that the author is no longer active.
Link to comment
  • 2 weeks later...

Originally Posted by PapaCharlie9*:

 

Will this work on Domination? :smile:

Yes, but you have to change Step 3 and Step 4.

 

Find all the Regex.Match lines, and change "^(_:Conquest|Rush)" to "^(_:Conquest|Rush|Domination)"

* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by TMiland*:

 

Yes, but you have to change Step 3 and Step 4.

 

Find all the Regex.Match lines, and change "^(_:Conquest|Rush)" to "^(_:Conquest|Rush|Domination)"

That's what i did, and it worked! Just had to check if there was anything else preventing the code to run. :smile:
* Restored post. It could be that the author is no longer active.
Link to comment
  • 1 month later...

Originally Posted by TMiland*:

 

Hey, i added some more squad names, took the names from the "Take squad lead" limit, and i was wondering if it looks okay:

Code:

String[] squadName = new String[]{"None",
      "Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel",
      "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa",
      "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey", "Xray",
      "Yankee", "Zulu", "Haggard", "Sweetwater", "Preston", "Redford", "Faith", "Celeste"};
https://github.com/tmiland/ProconLim...ad%20Recruiter

 

And i was also wondering why the CH team is missing? Is that intentional, or? :smile:

* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by PapaCharlie9*:

 

Hey, i added some more squad names, took the names from the "Take squad lead" limit, and i was wondering if it looks okay:

Code:

String[] squadName = new String[]{"None",
      "Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel",
      "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa",
      "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey", "Xray",
      "Yankee", "Zulu", "Haggard", "Sweetwater", "Preston", "Redford", "Faith", "Celeste"};
https://github.com/tmiland/ProconLim...ad%20Recruiter

 

And i was also wondering why the CH team is missing? Is that intentional, or? :smile:

That limit was written for BF3. I don't think it's been updated to BF4 yet.

 

The squad names are fine.

* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by TMiland*:

 

That limit was written for BF3. I don't think it's been updated to BF4 yet.

 

The squad names are fine.

Oh, i didn't realize that. BUT it is working just fine, even though the CH team is missing. :smile:
* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by PapaCharlie9*:

 

Oh, i didn't realize that. BUT it is working just fine, even though the CH team is missing. :smile:

There are more things wrong than just CN team name. Unless the map has team 1 as US and team 2 as RU, the team names are wrong. The squad names are incomplete (as you noticed). The maximum squad size is wrong. The target team is sometimes wrong (bug).

 

In any case, I updated it to BF4. Use the new version in post #1.

* Restored post. It could be that the author is no longer active.
Link to comment

Archived

This topic is now archived and is closed to further replies.



  • Our picks

    • Game Server Hosting:

      We're happy to announce that EZRCON will branch out into the game server provider scene. This is a big step for us so please having patience if something doesn't go right in this area. Now, what makes us different compared to other providers? Well, we're going with the idea of having a scaleable server hosting and providing more control in how you set up your server. For example, in Minecraft, you have the ability to control how many CPU cores you wish your server to have access to, how much RAM you want to use, how much disk space you want to use. This type of control can't be offered in a single service package so you're able to configure a custom package the way you want it.

      You can see all the available games here. Currently, we have the following games available.

      Valheim (From $1.50 USD)


      Rust (From $3.20 USD)


      Minecraft (Basic) (From $4.00 USD)


      Call of Duty 4X (From $7.00 USD)


      OpenTTD (From $4.00 USD)


      Squad (From $9.00 USD)


      Insurgency: Sandstorm (From $6.40 USD)


      Changes to US-East:

      Starting in January 2022, we will be moving to a different provider that has better support, better infrastructure, and better connectivity. We've noticed that the connection/routes to this location are not ideal and it's been hard getting support to correct this. Our contract for our two servers ends in March/April respectively. If you currently have servers in this location you will be migrated over to the new provider. We'll have more details when the time comes closer to January. The new location for this change will be based out of Atlanta, GA. If you have any questions/concerns please open a ticket and we'll do our best to answer them.
      • 5 replies
    • Hello All,

      I wanted to give an update to how EZRCON is doing. As of today we have 56 active customers using the services offered. I'm glad its doing so well and it hasn't been 1 year yet. To those that have services with EZRCON, I hope the service is doing well and if not please let us know so that we can improve it where possible. We've done quite a few changes behind the scenes to improve the performance hopefully. 

      We'll be launching a new location for hosting procon layers in either Los Angeles, USA or Chicago, IL. Still being decided on where the placement should be but these two locations are not set in stone yet. We would like to get feedback on where we should have a new location for hosting the Procon Layers, which you can do by replying to this topic. A poll will be created where people can vote on which location they would like to see.

      We're also looking for some suggestions on what else you would like to see for hosting provider options. So please let us know your thoughts on this matter.
      • 4 replies
    • Added ability to disable the new API check for player country info


      Updated GeoIP database file


      Removed usage sending stats


      Added EZRCON ad banner



      If you are upgrading then you may need to add these two lines to your existing installation in the file procon.cfg. To enable these options just change False to True.

      procon.private.options.UseGeoIpFileOnly False
      procon.private.options.BlockRssFeedNews False



       
      • 2 replies
    • I wanted I let you know that I am starting to build out the foundation for the hosting services that I talked about here. The pricing model I was originally going for wasn't going to be suitable for how I want to build it. So instead I decided to offer each service as it's own product instead of a package deal. In the future, hopefully, I will be able to do this and offer discounts to those that choose it.

      Here is how the pricing is laid out for each service as well as information about each. This is as of 7/12/2020.

      Single MySQL database (up to 30 GB) is $10 USD per month.



      If you go over the 30 GB usage for the database then each additional gigabyte is charged at $0.10 USD each billing cycle. If you're under 30GB you don't need to worry about this.


      Databases are replicated across 3 zones (regions) for redundancy. One (1) on the east coast of the USA, One (1) in Frankfurt, and One (1) in Singapore. Depending on the demand, this would grow to more regions.


      Databases will also be backed up daily and retained for 7 days.




      Procon Layer will be $2 USD per month.


      Each layer will only allow one (1) game server connection. The reason behind this is for performance.


      Each layer will also come with all available plugins installed by default. This is to help facilitate faster deployments and get you up and running quickly.


      Each layer will automatically restart if Procon crashes. 


      Each layer will also automatically restart daily at midnight to make sure it stays in tip-top shape.


      Custom plugins can be installed by submitting a support ticket.




      Battlefield Admin Control Panel (BFACP) will be $5 USD per month


      As I am still working on building version 3 of the software, I will be installing the last version I did. Once I complete version 3 it will automatically be upgraded for you.





      All these services will be managed by me so you don't have to worry about the technical side of things to get up and going.

      If you would like to see how much it would cost for the services, I made a calculator that you can use. It can be found here https://ezrcon.com/calculator.html

       
      • 11 replies
    • I have pushed out a new minor release which updates the geodata pull (flags in the playerlisting). This should be way more accurate now. As always, please let me know if any problems show up.

       
      • 9 replies
×
×
  • Create New...

Important Information

Please review our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.