Jump to content

Insane Limits V0.9/R6: Vote to nuke camping/base raping team, or !surrender (CQ/Rush)


ImportBot

Recommended Posts

Originally Posted by PapaCharlie9*:

 

VERSION: 0.8p3/R5. Compiled but not tested. If you try this out, post your feedback in this thread.

 

See post #10 below for the !surrender version.

 

Do you want to enforce a "no camping of deployments" rule on your Conquest or Rush server, but can't babysit 24 hours a day? How about giving your players a vote if their deployment gets camped and they get can't get out?

 

This limit implements a voting system for Conquest and Rush maps when there are a minimum number of players on the server (default 16) and there are enough tickets left in the round (default 10%, e.g., on a 300 ticket map, both teams have more than 30 tickets left) and that the gap between winning and losing team tickets is large enough (default 50 tickets). Players on the losing team may vote to kill everyone on the camping team by typing:

 

!votecamp

 

or

 

!vote camp

 

One vote per player per round. If more than a set percentage of the losing team votes (default is 50%), the winning/camping team is nuked: each and every player is killed. This gives the camped team a chance to break out. Since the kills are admin kills, they do not count against the kill count stats for the camping team. All that happens is that they have to respawn and get back in position. The winning team can't vote, any votes they make are ignored. Winning/losing is determined by ticket count.

 

Each team only gets to vote once per round -- if a team succeeds at completing a vote, subsequent votes are ignored for the rest of the round. If the losing team gets trapped in their deployment more than once, they deserve to be pwned. The vote only lasts for a certain number of minutes (defined by the timeout variable in the code, 5 minutes by default). If insufficient votes are cast before the timeout expires, the vote is cancelled. The same team may try to start a vote again if they wish.

 

 

See Post #10* below for a variation of this limit that allows the losing team to vote to end the current round.

 

See Post #101* below for a variation of this limit that adds a !cancelvote command for admins.

 

See Post #169* below for a variation of this limit that adds a second admin.kill (second nuke) when a nuked player tries to spawn. Effectively the same as a double nuke for one successful vote.

 

See Post #210* below for a variation of this limit that makes the command be !votenuke and that adds a series of countdown messages before the team is nuked.

 

See Post #261* below for an additional limit that sends chat and yell messages every 60 seconds (you can change) about the status of the vote.

 

 

 


 

RECOMMENDED: Inform your players about the vote system by adding scrolling/spambot chat, adding to your on-demand !rules command, or something similar.

 

Set the limit to evaluate OnAnyChat. Set the Action to None.

 

Set first_check to this Expression:

 

Only use one of the following choices, BF3 or BF4.

 

BF3

 

Code:

(Regex.Match(server.Gamemode, @"(Conquest|Rush)").Success)
BF4

 

Code:

(Regex.Match(server.Gamemode, @"(Conquest|Rush|Domination)").Success)
This second_check works for either BF3 or BF4:

 

Set second_check to this Code:

 

Code:

/* VERSION 0.9.15/R6 */
double percent = 50; // CUSTOMIZE: of losing team that has to vote
double timeout = 5.0; // CUSTOMIZE: number of minutes before vote times out
int minPlayers = 16; // CUSTOMIZE: minimum players to enable vote
double minTicketPercent = 10; // CUSTOMIZE: minimum ticket percentage remaining in the round
double minTicketGap = 50; // CUSTOMIZE: minimum ticket gap between winning and losing teams

String kCamp = "votecamp";
String kOncePrefix = "votecamp_once_";
String kVoteTime = "votecamp_time";

int level = 2;

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

String msg = "empty";

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


/* Parse the command */

Match campMatch = Regex.Match(player.LastChat, @"^\s*!vote\s*camp", RegexOptions.IgnoreCase);

/* Bail out if not a proper vote */

if (!campMatch.Success) return false;

/* Bail out if round about to end */

if (server.RemainTicketsPercent(1) < minTicketPercent || server.RemainTicketsPercent(2) < minTicketPercent) {
    msg = "Round too close to ending to hold a vote!";
    ChatPlayer(player.Name);
    return false;
}

/* Bail out if ticket ratio isn't large enough */

double t1 = server.RemainTickets(1);
double t2 = server.RemainTickets(2);
if (Math.Abs(t1 - t2) < minTicketGap) {
    msg = "Ticket counts too close to hold a vote!";
    ChatPlayer(player.Name);
    return false;
}

/* Bail out if voter is not on the losing team */

int losing = (t1 < t2) _ 1 : 2;

if (player.TeamId != losing) {
    msg = "You are not on the losing team!";
    ChatPlayer(player.Name);
    return false;
}

/* Bail out if this team already completed a vote camp this round */

String key = kOncePrefix + losing;
if (server.RoundData.issetBool(key)) {
    msg = "Your team already completed a vote camp this round!";
    ChatPlayer(player.Name);
    return false;
}

/* Bail out if not enough players to enable vote */

if (server.PlayerCount < minPlayers) {
    msg = "Not enough players to hold a vote!";
    ChatPlayer(player.Name);
    return false;
}

/* Count the vote in the voter's dictionary */
/* Votes are kept with the voter */
/* If the voter leaves, his votes are not counted */

if (!player.RoundData.issetBool(kCamp)) player.RoundData.setBool(kCamp, true);

if (level >= 2) plugin.ConsoleWrite("^b[VoteCamp]^n " + player.FullName + " voted to stop camping");

msg = "You voted to stop the other team from camping your deployment";
ChatPlayer(player.Name);

/* Tally the votes */

int votes = 0;
List<PlayerInfoInterface> losers = (losing == 1) _ team1.players : team2.players;

/* Bail out if too much time has past */

if (!server.RoundData.issetObject(kVoteTime)) {
    server.RoundData.setObject(kVoteTime, DateTime.Now);
    if (level >= 2) plugin.ConsoleWrite("^b[VoteCamp]^n vote timer started");
}
DateTime started = (DateTime)server.RoundData.getObject(kVoteTime);
TimeSpan since = DateTime.Now.Subtract(started);

if (since.TotalMinutes > timeout) {
    msg = "The voting time has expired, the vote is cancelled!";
    plugin.SendGlobalMessage(msg);
    plugin.SendGlobalYell(msg, 10);
    if (level >= 2) plugin.ConsoleWrite("^b[VoteCamp]^n vote timeout expired");
    foreach (PlayerInfoInterface can in losers) {
        // Erase the vote
        if (can.RoundData.issetBool(kCamp)) can.RoundData.unsetBool(kCamp);
    }
    server.RoundData.unsetObject(kVoteTime);

    /* Losing team only gets to try this vote once per round */
    server.RoundData.setBool(key, true);

    return false;
}

/* Otherwise tally */

foreach(PlayerInfoInterface p in losers) {
    if (p.RoundData.issetBool(kCamp)) votes++;
}
if (level >= 3) plugin.ConsoleWrite("^b[VoteCamp]^n loser votes = " + votes + " of " + losers.Count);

int needed = Convert.ToInt32(Math.Ceiling((double) losers.Count * (percent/100.0)));
int remain = needed - votes;

if (level >= 3) plugin.ConsoleWrite("^b[VoteCamp]^n needed = " + needed);

String campers = (losing == 1) _ "RU" : "US"; // BF3
String voters = (losing == 1) _ "US" : "RU"; // BF3

if (server.GameVersion == "BF4") {
    String[] factionNames = new String[]{"US", "RU", "CN"};
    int f1 = (team1.Faction == -1 || team1.Faction > 2) _ 0 : team1.Faction;
    int f2 = (team2.Faction == -1 || team2.Faction > 2) _ 1 : team2.Faction;
    campers = (losing == 1) _ factionNames[f2] : factionNames[f1];
    voters = (losing == 1) _ factionNames[f1] : factionNames[f2];
}

if (remain > 0) {
    msg = remain + " " + voters + " votes needed to punish " + campers + " team! " + Convert.ToInt32(Math.Ceiling(timeout - since.TotalMinutes)) + " mins left to vote!";
    plugin.SendGlobalMessage(msg);
    plugin.SendGlobalYell(msg, 8);
    if (level >= 2) plugin.ConsoleWrite("^b[VoteCamp]^n " + msg);
    return false;
}

/* Punish the campers */

msg = "Vote succeeded: " + campers + " team is being nuked for camping your deployment! Break out now!";
plugin.SendTeamMessage(losing, msg);
plugin.SendGlobalMessage(msg, 15);
if (level >= 2) plugin.ConsoleWrite("^b[VoteCamp]^n " + msg);

List<PlayerInfoInterface> bad = (losing == 1) _ team2.players : team1.players;
foreach (PlayerInfoInterface nuke in bad) {
    plugin.KillPlayer(nuke.Name, 1);
    if (level >= 3) plugin.ConsoleWrite("^b[VoteCamp]^n killing " + nuke.FullName);
}

/* Losing team only gets to do this once */

server.RoundData.setBool(key, true);

return false;
Keywords: vote, nuke, kill, team, camping, base rape, anti-camping, anti-base raping

 

R6 - added BF4 faction names

R5 - added level 3 debugging

R4 - changed determination of losing team to be based on tickets instead of points

R3 - added minTicketPercent and minTicketGap variables so that vote is enabled only if there are enough tickets left in the round and enough of a gap between the winning and losing teams, respectively

R2 - added minPlayers variable so that vote is enabled only when a minimum number of players are on the server

R1 - original version

* Restored post. It could be that the author is no longer active.
Link to comment
  • Replies 306
  • Created
  • Last Reply

Originally Posted by PapaCharlie9*:

 

HINT: Since the vote is check only when a !votecamp command is sent, players on the losing team should pay attention to players quitting from the server. If players quit from their side, the voting goal reduces proportionally, but it is checked only after a command. So players on the losing team who are still on the server should type !votecamp, even if they have already voted, to force the limit to recheck the voting goal.

 

For example, if the losing side starts with 32 players, 17 votes are needed to trigger the nuke of the winning team. Let's say 15 players have voted. Two more votes are needed. However, 4 players who haven't voted on the losing team leave the server. That cuts the vote goal down to 15, 50% of 28 is 14 + 1. The vote will succeed and the punishment will happen, but only if some player on the losing team types !votecamp again.

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

Originally Posted by Learning-Curve*:

 

@papa

LOL this is just cute enough to try. Interesting idea but also starts with the premiss we are dealing with fair and mature adults.

Potential problems:

1- Nuking team when base rape is not occuring. (may just want to advance and capture a flag {eg. metro usa side losing and wants to capture B} by nuking RU team they can capture and advance to ticket booth)

 

A- Is there a way for IL to know if all flags are owned or lost?

 

2- Nuking team when too few players to justify nuking. ( I cant see a server starting or just having 8-10 players (4vs4 - 5vs5) being raped unless they are not interested in advancing )

 

A- There could be a minimum player count before nuke vote is enabled or actionable. (atleast the option for server owners to adjust. {eg. min player count 20, ie.10vs10 }

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

Originally Posted by PapaCharlie9*:

 

@papa

LOL this is just cute enough to try. Interesting idea but also starts with the premiss we are dealing with fair and mature adults.

Potential problems:

1- Nuking team when base rape is not occuring. (may just want to advance and capture a flag {eg. metro usa side losing and wants to capture B} by nuking RU team they can capture and advance to ticket booth)

 

A- Is there a way for IL to know if all flags are owned or lost?

 

2- Nuking team when too few players to justify nuking. ( I cant see a server starting or just having 8-10 players (4vs4 - 5vs5) being raped unless they are not interested in advancing )

 

A- There could be a minimum player count before nuke vote is enabled or actionable. (atleast the option for server owners to adjust. {eg. min player count 20, ie.10vs10 }

Good feedback. I added the minimum player count, excellent suggestion.

 

No plugin knows how many flags are capped, it's not in the protocol. Yes, this can be abused, but note that it can only be abused once per team per round, so it's self-limiting and balanced. If one team nukes the other as an abuse, the other team can retaliate in kind if they end up losing in points.

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

Originally Posted by LumpyNutZ*:

 

Papa, heres my req.: Is it possible to edit this so it will launch voting for ending the round. Like that it would be public command like !endvote or something.

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

Originally Posted by dyn*:

 

Excellent idea! While we usually do not nuke, this is one aspect that can really kill a server.

 

I haven't tested it yet, but do have a couple of suggestions:

 

Do NOT nuke if under XX tickets are left. (keep from nuking with only a few tickets left or round is about to end - conquest).

ONLY nuke if XX ticket difference (I really don't want to allow nuking if there is only a 10 ticket difference).

 

Keep up the good work. I always look forward to seeing your posts / improvements.

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

Originally Posted by LumpyNutZ*:

 

May I ask where to download, I want to try this plug-in

You have to download and install this plugin for porocn www.phogue.net/forumvb/showth...%28beta-BF3%29* (read description and info).

Then you have to insert code provided in this thread into that plugin.

First it could look lilbit hard, but its not, once u get a hang of it.

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

Originally Posted by PapaCharlie9*:

 

May I ask where to download, I want to try this plug-in

Watch the video at the link Lumpy provided, it shows how to add a limit.
* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by PapaCharlie9*:

 

Papa, heres my req.: Is it possible to edit this so it will launch voting for ending the round. Like that it would be public command like !endvote or something.

Give this a try. Let me know if there are any compilation errors. Follow the steps in post #1, but use this code for second_check instead of the code in post #1.

 

Changes:

 

* !surrender or !votenext instead of !votecamp, as in vote to go to the next round -- losing team would use !surrender, winning team would use !votenext, though in truth, either team could use either command, they both do the same thing

 

* ends the round with the leading team getting the win

 

* either team may vote, not limited to the losing team

 

* a vote from the winning team counts only if there is a matching vote on the losing team, that is, the winning team can't unilaterally vote to end the round, they can only support the losing team's surrender

 

Set the limit to evaluate OnAnyChat. Set the Action to None.

 

Set first_check to this Expression:

 

Code:

(Regex.Match(server.Gamemode, @"(Conquest|Rush)").Success)
Set second_check to this Code:

 

Code:

/* VERSION 0.9.15/R8 - surrender */
double percent = 50; // CUSTOMIZE: of losing team that has to vote
double timeout = 5.0; // CUSTOMIZE: number of minutes before vote times out
int minPlayers = 16; // CUSTOMIZE: minimum players to enable vote
double minTicketPercent = 10; // CUSTOMIZE: minimum ticket percentage remaining in the round
double minTicketGap = 50; // CUSTOMIZE: minimum ticket gap between winning and losing teams

String kNext = "votenext";
String kVoteTime = "votenext_time";
String kNeeded = "votenext_needed";

int level = 2;

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

String msg = "empty";

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


/* Parse the command */

Match nextMatch = Regex.Match(player.LastChat, @"^\s*[@!](_:surrender|vote\s*next)", RegexOptions.IgnoreCase);

/* Bail out if not a proper vote */

if (!nextMatch.Success) return false;

/* Bail out if round about to end */

if (server.RemainTicketsPercent(1) < minTicketPercent || server.RemainTicketsPercent(2) < minTicketPercent) {
	msg = "Round too close to ending to hold a vote!";
	ChatPlayer(player.Name);
	return false;
}

/* Bail out if ticket ratio isn't large enough */

double t1 = server.RemainTickets(1);
double t2 = server.RemainTickets(2);
if (Math.Abs(t1 - t2) < minTicketGap) {
	msg = "Ticket counts too close to hold a vote!";
	ChatPlayer(player.Name);
	return false;
}

/* Determine losing team by tickets */

int losing = (t1 < t2) _ 1 : 2;

/* Bail out if not enough players to enable vote */

if (server.PlayerCount < minPlayers) {
	msg = "Not enough players to hold a vote!";
	ChatPlayer(player.Name);
	return false;
}

/* Count the vote in the voter's dictionary */
/* Votes are kept with the voter */
/* If the voter leaves, his votes are not counted */

if (!player.RoundData.issetBool(kNext)) {
	player.RoundData.setBool(kNext, true);
} else {
	msg = "You already voted!";
	ChatPlayer(player.Name);
	return false;
}

if (level >= 2) plugin.ConsoleWrite("^b[VoteNext]^n " + player.FullName + " voted to end the round");

msg = "You voted to end this round and start the next round!";
ChatPlayer(player.Name);

/* Tally the votes */

int votes = 0;
List<PlayerInfoInterface> losers = (losing == 1) _ team1.players : team2.players;
List<PlayerInfoInterface> winners = (losing == 1) _ team2.players : team1.players;

/* Bail out if too much time has past */

bool firstTime = false;

if (!server.RoundData.issetObject(kVoteTime)) {
	server.RoundData.setObject(kVoteTime, DateTime.Now);
	if (level >= 2) plugin.ConsoleWrite("^b[VoteNext]^n vote timer started");
	firstTime = true;
}
DateTime started = (DateTime)server.RoundData.getObject(kVoteTime);
TimeSpan since = DateTime.Now.Subtract(started);

if (since.TotalMinutes > timeout) {
	msg = "The voting time has expired, the vote is cancelled!";
	plugin.SendGlobalMessage(msg);
	plugin.SendGlobalYell(msg, 10);
	if (level >= 2) plugin.ConsoleWrite("^b[VoteNext]^n vote timeout expired");
	foreach (PlayerInfoInterface can in losers) {
		// Erase the vote
		if (can.RoundData.issetBool(kNext)) can.RoundData.unsetBool(kNext);
	}
	foreach (PlayerInfoInterface can in winners) {
		// Erase the vote
		if (can.RoundData.issetBool(kNext)) can.RoundData.unsetBool(kNext);
	}
	server.RoundData.unsetObject(kVoteTime);

	return false;
}

/* Otherwise tally */

foreach(PlayerInfoInterface p in losers) {
    if (p.RoundData.issetBool(kNext)) votes++;
}
if (level >= 3) plugin.ConsoleWrite("^b[VoteNext]^n loser votes = " + votes + " of " + losers.Count);

/* Votes on the winning side are counted as long as they are less than the votes on the losing side */

int losingVotes = votes;
int winningVotes = 0;
foreach(PlayerInfoInterface p in winners) {
    if (p.RoundData.issetBool(kNext)) {
        winningVotes++;
        if (winningVotes > losingVotes) break;
        ++votes;
    }
}

if (level >= 3) plugin.ConsoleWrite("^b[VoteNext]^n winner votes = " + winningVotes + " of " + winners.Count);

int needed = Convert.ToInt32(Math.Ceiling((double) losers.Count * (percent/100.0)));
if (server.RoundData.issetInt(kNeeded)) needed = Math.Min(needed, server.RoundData.getInt(kNeeded));
server.RoundData.setInt(kNeeded, needed);

int remain = needed - votes;

if (level >= 3) plugin.ConsoleWrite("^b[VoteNext]^n needed votes = " + needed);

String voters = (losing == 1) _ "US" : "RU";
String otherVoters = (losing == 1) _ "RU" : "US";

if (server.GameVersion == "BF4") {
    String[] factionNames = new String[]{"US", "RU", "CN"};
    int f1 = (team1.Faction < 0 || team1.Faction > 2) _ 0 : team1.Faction;
    int f2 = (team2.Faction < 0 || team2.Faction > 2) _ 1 : team2.Faction;
    voters = (losing == 1) _ factionNames[f1] : factionNames[f2];
    otherVoters = (losing == 1) _ factionNames[f2] : factionNames[f1];
}

if (remain > 0) {
	if (firstTime) {
		msg = remain + " " + voters + " !surrender or " + otherVoters + " !votenext votes needed to end round with " + Convert.ToInt32(Math.Ceiling(timeout - since.TotalMinutes)) + " minutes left!";
	} else {
		msg = remain + " !surrender/!votenext needed to end round with " + Convert.ToInt32(Math.Ceiling(timeout - since.TotalMinutes)) + " mins left!";
	}
	plugin.SendGlobalMessage(msg);
	plugin.SendGlobalYell(msg, 8);
	if (level >= 2) plugin.ConsoleWrite("^b[VoteNext]^n " + msg);
	return false;
}

/* End the round */

String wteam = (losing == 1) _ "RU" : "US"; // BF3

if (server.GameVersion == "BF4") {
    String[] factionNames = new String[]{"US", "RU", "CN"};
    int f1 = (team1.Faction < 0 || team1.Faction > 2) _ 0 : team1.Faction;
    int f2 = (team2.Faction < 0 || team2.Faction > 2) _ 1 : team2.Faction;
    wteam = (losing == 1) _ factionNames[f2] : factionNames[f1];
}

msg = "Vote succeeded: round ends now, " + wteam + " team wins!";
plugin.SendGlobalMessage(msg);
plugin.SendGlobalYell(msg, 10);
if (level >= 2) plugin.ConsoleWrite("^b[VoteNext]^n " + msg);

String wid = (losing == 1) _ "2" : "1";

ThreadStart roundEnder = delegate {
    Thread.Sleep(10*1000);
    plugin.ServerCommand("mapList.endRound", wid);
};

Thread enderThread = new Thread(roundEnder);
enderThread.Start();
Thread.Sleep(10);

return false;
REVISIONS

 

R8: BF4 faction names

R7: enabled either @ or ! for surrender/votenext command

R6: Made needed be the minimum of calculated and last, improved the first message, stopped players from voting multiple times

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

Originally Posted by PapaCharlie9*:

 

Excellent idea! While we usually do not nuke, this is one aspect that can really kill a server.

 

I haven't tested it yet, but do have a couple of suggestions:

 

Do NOT nuke if under XX tickets are left. (keep from nuking with only a few tickets left or round is about to end - conquest).

ONLY nuke if XX ticket difference (I really don't want to allow nuking if there is only a 10 ticket difference).

 

Keep up the good work. I always look forward to seeing your posts / improvements.

Great suggestions. I've added both constraints in R3. Change the minTicketPercent value if you don't like the default of 10% and change the minTicketGap value if you don't like the gap of 50 tickets.

 

Caution, I usually compile any changes I make to fix any syntax errors, but I didn't have time to do it for R3, so please let me know if you have any problems.

 

EDIT: I compiled the changes and got no errors, whew!

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

Originally Posted by LumpyNutZ*:

 

Give this a try. Let me know if there are any compilation errors.

 

Changes:

 

* !votenext instead of !votecamp, as in vote to go to the next round

* ends the round with the leading team getting the win

 

Only the second_check needs to change (based on latest R3 code, so it has minTicketPercent and minTicketGap variables available):

Code:

/* VERSION 0.8/R3 - votenext */
double percent = 50; // CUSTOMIZE: of losing team that has to vote
double timeout = 5.0; // CUSTOMIZE: number of minutes before vote times out
int minPlayers = 16; // CUSTOMIZE: minimum players to enable vote
double minTicketPercent = 10; // CUSTOMIZE: minimum ticket percentage remaining in the round
double minTicketGap = 50; // CUSTOMIZE: minimum ticket gap between winning and losing teams

String kNext = "votenext";
String kOncePrefix = "votenext_once_";
String kVoteTime = "votenext_time";

int level = 2;

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

String msg = "empty";

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


/* Parse the command */

Match nextMatch = Regex.Match(player.LastChat, @"^\s*!vote\s*next", RegexOptions.IgnoreCase);

/* Bail out if not a proper vote */

if (!nextMatch.Success) return false;

/* Bail out if round about to end */

if (server.RemainTicketsPercent(1) < minTicketPercent || server.RemainTicketsPercent(2) < minTicketPercent) {
	msg = "Round too close to ending to hold a vote!";
	ChatPlayer(player.Name);
	return false;
}

/* Bail out if ticket ratio isn't large enough */

double t1 = server.RemainTickets(1);
double t2 = server.RemainTickets(2);
if (Math.Abs(t1 - t2) < minTicketGap) {
	msg = "Ticket counts too close to hold a vote!";
	ChatPlayer(player.Name);
	return false;
}

/* Bail out if voter is not on the losing team */

int losing = (team1.ScoreRound < team2.ScoreRound) _ 1 : 2;

if (player.TeamId != losing) {
	msg = "You are not on the losing team!";
	ChatPlayer(player.Name);
	return false;
}

/* Bail out if this team already completed a vote next this round */

String key = kOncePrefix + losing;
if (server.RoundData.issetBool(key)) {
	msg = "Your team already completed a vote next this round!";
	ChatPlayer(player.Name);
	return false;
}

/* Bail out if not enough players to enable vote */

if (server.PlayerCount < minPlayers) {
	msg = "Not enough players to hold a vote!";
	ChatPlayer(player.Name);
	return false;
}

/* Count the vote in the voter's dictionary */
/* Votes are kept with the voter */
/* If the voter leaves, his votes are not counted */

if (!player.RoundData.issetBool(kNext)) player.RoundData.setBool(kNext, true);

if (level >= 2) plugin.ConsoleWrite("^b[VoteNext]^n " + player.FullName + " voted to end the round");

msg = "You voted to end this round and start the next round!";
ChatPlayer(player.Name);

/* Tally the votes */

int votes = 0;
List<PlayerInfoInterface> losers = (losing == 1) _ team1.players : team2.players;

/* Bail out if too much time has past */

if (!server.RoundData.issetObject(kVoteTime)) {
	server.RoundData.setObject(kVoteTime, DateTime.Now);
	if (level >= 2) plugin.ConsoleWrite("^b[VoteNext]^n vote timer started");
}
DateTime started = (DateTime)server.RoundData.getObject(kVoteTime);
TimeSpan since = DateTime.Now.Subtract(started);

if (since.TotalMinutes > timeout) {
	msg = "The voting time has expired, the vote is cancelled!";
	plugin.SendGlobalMessage(msg);
	plugin.ServerCommand("admin.yell", msg);
	if (level >= 2) plugin.ConsoleWrite("^b[VoteNext]^n vote timeout expired");
	foreach (PlayerInfoInterface can in losers) {
		// Erase the vote
		if (can.RoundData.issetBool(kNext)) can.RoundData.unsetBool(kNext);
	}
	server.RoundData.unsetObject(kVoteTime);

	/* Losing team only gets to try this vote once per round */
	server.RoundData.setBool(key, true);

	return false;
}

/* Otherwise tally */

foreach(PlayerInfoInterface p in losers) {
    if (p.RoundData.issetBool(kNext)) votes++;
}

int needed = Convert.ToInt32(Math.Ceiling((double) losers.Count * (percent/100.0)));
int remain = needed - votes;

String voters = (losing == 1) _ "US" : "RU";
if (remain > 0) {
	msg = remain + " " + voters + " votes needed to end this round with " + Convert.ToInt32(Math.Ceiling(timeout - since.TotalMinutes)) + " mins left to vote!";
	plugin.SendGlobalMessage(msg);
	plugin.ServerCommand("admin.yell", msg, "8");
	if (level >= 2) plugin.ConsoleWrite("^b[VoteNext]^n " + msg);
	return false;
}

/* End the round */

String winners = (losing == 1) _ "RU" : "US";

msg = "Vote succeeded: round will end with " + winners + " declared the winner!";
plugin.SendGlobalMessage(msg);
if (level >= 2) plugin.ConsoleWrite("^b[VoteNext]^n " + msg);

String wteam = (losing == 1) _ "2" : "1";
plugin.ServerCommand("mapList.endRound", wteam);

return false;
So, i tried to do like u said but getting this:

 

Code:

[10:51:22 16] [Insane Limits] Thread(settings): ERROR: 20 errors compiling Expression
[10:51:22 16] [Insane Limits] Thread(settings): ERROR: (CS0103, line: 37, column: 46):  The name 'losers' does not exist in the current context
[10:51:22 16] [Insane Limits] Thread(settings): ERROR: (CS0103, line: 38, column: 43):  The name 'kNext' does not exist in the current context
[10:51:22 16] [Insane Limits] Thread(settings): ERROR: (CS0103, line: 38, column: 51):  The name 'votes' does not exist in the current context
[10:51:22 16] [Insane Limits] Thread(settings): ERROR: (CS0103, line: 41, column: 64):  The name 'losers' does not exist in the current context
[10:51:22 16] [Insane Limits] Thread(settings): ERROR: (CS0103, line: 41, column: 80):  The name 'percent' does not exist in the current context
[10:51:22 16] [Insane Limits] Thread(settings): ERROR: (CS0103, line: 42, column: 35):  The name 'votes' does not exist in the current context
[10:51:22 16] [Insane Limits] Thread(settings): ERROR: (CS0103, line: 44, column: 30):  The name 'losing' does not exist in the current context
[10:51:22 16] [Insane Limits] Thread(settings): ERROR: (CS0103, line: 46, column: 14):  The name 'msg' does not exist in the current context
[10:51:22 16] [Insane Limits] Thread(settings): ERROR: (CS0103, line: 46, column: 115):  The name 'timeout' does not exist in the current context
[10:51:22 16] [Insane Limits] Thread(settings): ERROR: (CS0103, line: 46, column: 125):  The name 'since' does not exist in the current context
[10:51:22 16] [Insane Limits] Thread(settings): ERROR: (CS0103, line: 47, column: 39):  The name 'msg' does not exist in the current context
[10:51:22 16] [Insane Limits] Thread(settings): ERROR: (CS0103, line: 48, column: 49):  The name 'msg' does not exist in the current context
[10:51:22 16] [Insane Limits] Thread(settings): ERROR: (CS0103, line: 49, column: 18):  The name 'level' does not exist in the current context
[10:51:22 16] [Insane Limits] Thread(settings): ERROR: (CS0103, line: 49, column: 70):  The name 'msg' does not exist in the current context
[10:51:22 16] [Insane Limits] Thread(settings): ERROR: (CS0103, line: 55, column: 31):  The name 'losing' does not exist in the current context
[10:51:22 16] [Insane Limits] Thread(settings): ERROR: (CS0103, line: 57, column: 13):  The name 'msg' does not exist in the current context
[10:51:22 16] [Insane Limits] Thread(settings): ERROR: (CS0103, line: 58, column: 38):  The name 'msg' does not exist in the current context
[10:51:22 16] [Insane Limits] Thread(settings): ERROR: (CS0103, line: 59, column: 17):  The name 'level' does not exist in the current context
[10:51:22 16] [Insane Limits] Thread(settings): ERROR: (CS0103, line: 59, column: 69):  The name 'msg' does not exist in the current context
[10:51:22 16] [Insane Limits] Thread(settings): ERROR: (CS0103, line: 61, column: 29):  The name 'losing' does not exist in the current context
Im pretty sure im doing something wrong there or...?
* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by PapaCharlie9*:

 

Code:

ERROR: 20 errors compiling [b]Expression[/b]
Im pretty sure im doing something wrong there or...?
Set second_check to Code, not Expression. Follow the steps in post #1, but replace the second_check Code with the code in post #10.
* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by Hutchew*:

 

LOL, great idea, and implemented in our servers. Only thing wrong is teams are backwards; winning team has the ability to vote round end, losing team does not. Created some pandemonium there for a bit. Still lol'ing about that one..........

 

Any simple way to change the command to !surrender instead of !votenext? I think it's more suiting to the situation.

 

You rock Papa. Thanks for all your time & troubles.

 

Hutchew

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

Originally Posted by Learning-Curve*:

 

@Hutchew - LOL I like your "white flag" !votesurrender idea. Certainly plays correctly into the situation you are voting on.

 

Can the !vote"xxx" event be edited to read anything? !votewhiteflag !votewesuck etc... or to have it inlcude a global yell message that might embarass the side who decided to give up and plead for a second chance?

 

@papa - While there are clean fair minded players that may be on both teams - I would like to allow those on winning side the ability to SUPPORT the nuke event with only a !vote yes or abstain. There have been many occasions some on winning side are pleading their own team mates to back off to give them a chance - I hope we can find a way to give them a role in support of nuke action.

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

Originally Posted by LumpyNutZ*:

 

Set second_check to Code, not Expression. Follow the steps in post #1, but replace the second_check Code with the code in post #10.

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

Originally Posted by funnysergio*:

 

Hello i tried the code from post #10 but i got these :

 

Code:

[13:03:21 92] [Insane Limits] ERROR: 3 errors compiling Code
[13:03:21 92] [Insane Limits] ERROR: (CS1002, line: 22, column: 71):  ; attendu
[13:03:21 92] [Insane Limits] ERROR: (CS0168, line: 51, column: 15):  La variable 'e' est déclarée, mais jamais utilisée
[13:03:21 92] [Insane Limits] ERROR: (CS0162, line: 187, column: 13):  Impossible d'atteindre le code détecté
* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by PapaCharlie9*:

 

LOL, great idea, and implemented in our servers. Only thing wrong is teams are backwards; winning team has the ability to vote round end, losing team does not. Created some pandemonium there for a bit. Still lol'ing about that one..........

Really? I double-checked the code and I'm sure I got the teams right. It's this code:

 

Code:

/* Bail out if voter is not on the losing team */

int losing = (team1.ScoreRound < team2.ScoreRound) _ 1 : 2;

if (player.TeamId != losing) {
	msg = "You are not on the losing team!";
	ChatPlayer(player.Name);
	return false;
}
What that says is, if team1 (US) score is lower than team2's (RU) score, the losing team is team 1. Otherwise, the losing team is team 2. Then, if the player voting is not on the same team as the losing team, they can't vote.

 

It's possible, in the case of close scores, for the winning and losing teams to switch back and forth. Is that what happened?

 

Any simple way to change the command to !surrender instead of !votenext? I think it's more suiting to the situation.

Yes it is simple and I went ahead and made the change, good suggestion! Pick up a new copy from post #10.
* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by PapaCharlie9*:

 

Can the !vote"xxx" event be edited to read anything? !votewhiteflag !votewesuck etc... or to have it inlcude a global yell message that might embarass the side who decided to give up and plead for a second chance?

Yes. The command is defined in this line of code:

 

Code:

Match nextMatch = Regex.Match(player.LastChat, @"^\s*!surrender", RegexOptions.IgnoreCase);
You can change "!surrender" to whatever you want. If it has spaces, don't use spaces, use \s*, to allow for any number of spaces, including none. So for example, if you wanted the command to be "!give up", you would use:

 

Code:

Match nextMatch = Regex.Match(player.LastChat, @"^\s*!give\s*up", RegexOptions.IgnoreCase);
You should also inspect the chat message strings, any line like msg = "...";, and adjust as needed.

 

@papa - While there are clean fair minded players that may be on both teams - I would like to allow those on winning side the ability to SUPPORT the nuke event with only a !vote yes or abstain. There have been many occasions some on winning side are pleading their own team mates to back off to give them a chance - I hope we can find a way to give them a role in support of nuke action.

It's possible to allow the winning side to vote also.

 

See Post #10 above, it's been changed to allow either team to vote.

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

Originally Posted by PapaCharlie9*:

 

Hello i tried the code from post #10 but i got these :

 

Code:

[13:03:21 92] [Insane Limits] ERROR: 3 errors compiling Code
[13:03:21 92] [Insane Limits] ERROR: (CS1002, line: 22, column: 71):  ; attendu
[13:03:21 92] [Insane Limits] ERROR: (CS0168, line: 51, column: 15):  La variable 'e' est déclarée, mais jamais utilisée
[13:03:21 92] [Insane Limits] ERROR: (CS0162, line: 187, column: 13):  Impossible d'atteindre le code détecté
Follow the directions in post #1, first_check Expression, but for the second_check Code, use the code from post #10 instead of from #1.
* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by Hutchew*:

 

Really? I double-checked the code and I'm sure I got the teams right. It's this code:

 

Code:

/* Bail out if voter is not on the losing team */

int losing = (team1.ScoreRound < team2.ScoreRound) _ 1 : 2;

if (player.TeamId != losing) {
	msg = "You are not on the losing team!";
	ChatPlayer(player.Name);
	return false;
}
What that says is, if team1 (US) score is lower than team2's (RU) score, the losing team is team 1. Otherwise, the losing team is team 2. Then, if the player voting is not on the same team as the losing team, they can't vote.

 

It's possible, in the case of close scores, for the winning and losing teams to switch back and forth. Is that what happened?

 

 

Yes it is simple and I went ahead and made the change, good suggestion! Pick up a new copy from post #10.

Yep, positive it's backwards. Tried it again with the !surrender code and it allowed the winning team to vote, but not the losing team. At the time, the US team had @200 tickets less than the RU team. Otherwise, it looks like all will work once that issue is resolved.

 

Thanks for changing it to !surrender. That's just too cool.

 

Hutchew

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

Originally Posted by PapaCharlie9*:

 

Yep, positive it's backwards. Tried it again with the !surrender code and it allowed the winning team to vote, but not the losing team. At the time, the US team had @200 tickets less than the RU team. Otherwise, it looks like all will work once that issue is resolved.

 

Thanks for changing it to !surrender. That's just too cool.

 

Hutchew

Wait, now I'm getting confused. By losing team, I mean the one with the lower score, not less tickets. Are you basing your "reversed" observation on tickets or point score?

 

Did you install the latest version, R3? I had an earlier version with the test reversed, which would indeed have the teams backwards, but I fixed that in later versions. You must have, since you mentioned !surrender.

 

If you are basing it on point score, it's possible Insane Limits is backwards. I'm just using the "team1" score for "1" and the "team2" score for "2". If those are backwards inside of Insane Limits, that would explain it, but I'd be very surprised if that were true.

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

Originally Posted by Hutchew*:

 

Wait, now I'm getting confused. By losing team, I mean the one with the lower score, not less tickets. Are you basing your "reversed" observation on tickets or point score?

 

Did you install the latest version, R3? I had an earlier version with the test reversed, which would indeed have the teams backwards, but I fixed that in later versions. You must have, since you mentioned !surrender.

 

If you are basing it on point score, it's possible Insane Limits is backwards. I'm just using the "team1" score for "1" and the "team2" score for "2". If those are backwards inside of Insane Limits, that would explain it, but I'd be very surprised if that were true.

Papa, Latest version from post #10 installed. That may very well be the case, as I was basing it off tickets, not score. I didn't think to look at that. Usually though, the team that is being base-raped is bleeding tickets and are at least 100 or more behind the other team, so I would think it would be better to base it off tickets instead of score.

 

Update- Yes it does work correctly off score instead of tickets. Any way to base off tickets only?

 

Thanks Papacharlie.

 

Hutchew

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

Originally Posted by PapaCharlie9*:

 

Papa, Latest version from post #10 installed. That may very well be the case, as I was basing it off tickets, not score. I didn't think to look at that. Usually though, the team that is being base-raped is bleeding tickets and are at least 100 or more behind the other team, so I would think it would be better to base it off tickets instead of score.

 

Update- Yes it does work correctly off score instead of tickets. Any way to base off tickets only?

 

Thanks Papacharlie.

 

Hutchew

Whew, what a relief! I was going crazy trying to figure out why my code wasn't working. It's like you were seeing the sun rise in the west or that 1+1 equals 3.

 

Should the restriction be based on tickets or score? Or should any team be allowed to vote? I'm thinking for the !surrender version, it might be best to just let either team vote and remove the check altogether. Although maybe I'll change it to accept either !surrender or !votenext. For the !votecamp, maybe tickets would be best.

 

EDIT: Done and done. I modified the !surrender/!votenext version (post #10) to properly allow winners to vote as well as losers. The modification originally suggested in post #20 was wrong, it's been fixed.

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

Originally Posted by Hutchew*:

 

Whew, what a relief! I was going crazy trying to figure out why my code wasn't working. It's like you were seeing the sun rise in the west or that 1+1 equals 3.

 

Should the restriction be based on tickets or score? Or should any team be allowed to vote? I'm thinking for the !surrender version, it might be best to just let either team vote and remove the check altogether. Although maybe I'll change it to accept either !surrender or !votenext. For the !votecamp, maybe tickets would be best.

 

EDIT: Done and done. I modified the !surrender/!votenext version (post #10) to properly allow winners to vote as well as losers. The modification originally suggested in post #20 was wrong, it's been fixed.

I would like to see !surrender based on tickets, with 10% remaining and 100 tickets difference, as well as allowing winning team to vote as well, if you would, please.

 

Thanks again, Papa.

 

Hutchew

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

Originally Posted by HexaCanon*:

 

i think there is an issue with vote counter/timer

Code:

[15:41:56 79] [Insane Limits] [VoteNext] [TROL]TheTrollerHD voted to end the round
[15:41:56 79] [Insane Limits] [VoteNext] vote timer started
[15:41:56 79] [Insane Limits] Thread(say): sending 1 queued message ...
[15:41:56 79] [Insane Limits] [VoteNext] Vote succeeded: round ends now, RU team wins!
[15:41:56 81] [Insane Limits] Original command: !surrender
yes it is broken, it is not counting votes, if someone votes the round ends ... i disabled till there is a fix.
* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by PapaCharlie9*:

 

i think there is an issue with vote counter/timer

Code:

[15:41:56 79] [Insane Limits] [VoteNext] [TROL]TheTrollerHD voted to end the round
[15:41:56 79] [Insane Limits] [VoteNext] vote timer started
[15:41:56 79] [Insane Limits] Thread(say): sending 1 queued message ...
[15:41:56 79] [Insane Limits] [VoteNext] Vote succeeded: round ends now, RU team wins!
[15:41:56 81] [Insane Limits] Original command: !surrender
yes it is broken, it is not counting votes, if someone votes the round ends ... i disabled till there is a fix.
Good catch. The vote counting was broken. I have fixed it. The update and fix are in post #10. Thanks!
* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by PapaCharlie9*:

 

I would like to see !surrender based on tickets, with 10% remaining and 100 tickets difference, as well as allowing winning team to vote as well, if you would, please.

 

Thanks again, Papa.

 

Hutchew

Already done. It's already changed to be based on tickets and the winning team is allowed to vote, up to the number of losing team votes. The 10% remaining is the default and is already set (see minTicketPercent in the code). For 100 tickets difference, find minTicketGap in the code and change 50 (the default) to 100. In general, anything that says CUSTOMIZE in the code is something you can change to customize the behavior of the limit.
* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by PapaCharlie9*:

 

NOTE: due to the R24 queue problem and "ghost players", the calculation of needed votes might be understated. That is, if you see 60 players total, 30 on each team with a 50% required vote, the needed votes should be 15, but it might be a lower number, like 14 or 12, due to some of the 60 players being ghost players. PRoCon/Insane Limits doesn't know about those players, so it may think the total number of players is lower than it actually is.

 

Nothing I can do about that, we just have to wait for this to get fixed in BF3.

* 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.