Jump to content

Insane Limits V0.8/R1: Infantry only/vehicle vote (any kind of vote for next round)


ImportBot

Recommended Posts

Originally Posted by PapaCharlie9*:

 

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

 

Regarding this reqest:

Vehicle Vote

 

is it possible to to program Limits or proconrulz to have a vote before the round is over wether to have vehicles next round or not? It will need to be fore the next round only unless the vote was passed on the next round aswell. Plus if its possible to have a slots counter so it the vote can be activated after a chose number of players in the server. If it could work like the map vote plugin that would be great, 1 vote per person, on/off depending on how many are in the server (low numbers will be off), time left of map before voting is displayed, can be on all game types or selected ones on the admins choice (admin will need to set it up).

 

I play HC but this could be handy for softcore admins aswell

These two limits implement in-round voting to change the next round to infantry only. The change only lasts for one round, unless a new vote is held and the following round is also voted to be infantry only. If no such vote is held, the round after the next round will revert to vehicles.

 

NOTE: This is a general framework for holding votes in this round to change the server configuration of the next round. It can be adapted to a variety of purposes, such as voting for the next map, the next mode, number of tickets, etc.

 

To vote for infantry only, players must type one of the following commands into chat:

 

!voteio

 

or

 

!vote io

 

or

 

!voteinfantry

 

or

 

!vote infantry

 

Any prefix of "infantry" is also acceptable, such as "!vote inf".

 

One vote per player per round. If more than a set percentage of total players votes (default is 50%), next round will be set to be infantry only. There is no way to vote against the change. Players that do not want to change to infantry only should just not vote (abstain).

 

The vote only lasts for a certain number of minutes (defined by the timeout variable in the code, 10 minutes by default). If insufficient votes are cast before the timeout expires, the vote is cancelled. If a vote is cancelled, no new votes may be held that round.

 


 

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

 

Step 1

 

Set the limit to evaluate OnAnyChat and name it "Vote IO". Set the Action to None.

 

Set first_check to this Expression:

 

Code:

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

 

Code:

/* VERSION 0.8/R1 - vote io */
double percent = 50; // CUSTOMIZE: percent of players needed to vote
double timeout = 10.0; // CUSTOMIZE: number of minutes before vote times out
int minPlayers = 16; // CUSTOMIZE: minimum players to enable vote
double minTicketPercent = 20; // CUSTOMIZE: minimum ticket percentage remaining in the round

String kVote = "voteinfantry";
String kVoteResult = "voteinfantry_result";
String kVoteTime = "voteinfantry_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*i[on]f_a_n_t_r_y_", RegexOptions.IgnoreCase);

/* Bail out if not a proper vote */

if (!nextMatch.Success) return false;

/* Bail out if vote already succeeded/failed this round */

if (server.RoundData.issetBool(kVoteResult)) {
	bool succeeded = server.RoundData.getBool(kVoteResult);
	if (succeeded) {
		msg = "Vote already suceeded, next round will be infantry only!";
	} else {
		msg = "Vote failed this round, try again next round!";
	}
	ChatPlayer(player.Name);
	return false;
}

/* Bail out if round about to end */

if (!server.RoundData.issetObject(kVoteTime) && (server.RemainTicketsPercent(1) < minTicketPercent || server.RemainTicketsPercent(2) < minTicketPercent || (team3.players.Count > 0 && server.RemainTicketsPercent(3) < minTicketPercent) || (team4.players.Count > 0 && server.RemainTicketsPercent(4) < minTicketPercent))) {
	msg = "Round too close to ending to start a vote!";
	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(kVote)) player.RoundData.setBool(kVote, true);

if (level >= 2) plugin.ConsoleWrite("^b[Vote IO]^n " + player.FullName + " voted for infantry only");

msg = "You voted to make the next round infantry only!";
ChatPlayer(player.Name);

/* Tally the votes */

int votes = 0;
List<PlayerInfoInterface> all = new List<PlayerInfoInterface>();
all.AddRange(team1.players);
all.AddRange(team2.players);
if (team3.players.Count > 0) all.AddRange(team3.players);
if (team4.players.Count > 0) all.AddRange(team4.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[Vote IO]^n vote timer started");
}
DateTime started = (DateTime)server.RoundData.getObject(kVoteTime);
TimeSpan since = DateTime.Now.Subtract(started);

if (since.TotalMinutes > timeout) {
	msg = "Voting time has expired, the vote is cancelled!";
	plugin.SendGlobalMessage(msg);
	plugin.ServerCommand("admin.yell", msg);
	if (level >= 2) plugin.ConsoleWrite("^b[Vote IO]^n vote timeout expired");
	foreach (PlayerInfoInterface can in all) {
		// Erase the vote
		if (can.RoundData.issetBool(kVote)) can.RoundData.unsetBool(kVote);
	}
	server.RoundData.setBool(kVoteResult, false);
	return false;
}

/* Otherwise tally */

foreach(PlayerInfoInterface p in all) {
    if (p.RoundData.issetBool(kVote)) votes++;
}
if (level >= 3) plugin.ConsoleWrite("^b[Vote IO]^n votes = " + votes + " of " + all.Count);

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

if (level >= 3) plugin.ConsoleWrite("^b[Vote IO]^n " + percent.ToString("F0") + "% needed votes = " + needed);

if (remain > 0) {
	msg = remain + " votes needed to change to IO with " + Convert.ToInt32(Math.Ceiling(timeout - since.TotalMinutes)) + " mins left!";
	plugin.SendGlobalMessage(msg);
	plugin.ServerCommand("admin.yell", msg, "8");
	if (level >= 2) plugin.ConsoleWrite("^b[Vote IO]^n " + msg);
	return false;
}

/* Vote succeeded, set up the next round */

server.RoundData.setBool(kVoteResult, false);
plugin.Data.setBool(kVoteResult, true); // Signal the other limit to change to io

/* BF3 friendly map names, including B2K and CloseQ */
Dictionary<String, String> Maps = new Dictionary<String, String>();
Maps.Add("MP_001", "Grand Bazaar");
Maps.Add("MP_003", "Teheran Highway");
Maps.Add("MP_007", "Caspian Border");
Maps.Add("MP_011", "Seine Crossing");
Maps.Add("MP_012", "Operation Firestorm");
Maps.Add("MP_013", "Damavand Peak");
Maps.Add("MP_017", "Noshahr Canals");
Maps.Add("MP_018", "Kharg Island");
Maps.Add("MP_Subway", "Operation Metro");
Maps.Add("XP1_001", "Strike At Karkand");
Maps.Add("XP1_002", "Gulf of Oman");
Maps.Add("XP1_003", "Sharqi Peninsula");
Maps.Add("XP1_004", "Wake Island");
// CloseQ
Maps.Add("XP2_Factory", "Scrapmetal");
Maps.Add("XP2_Office", "Operation 925");
Maps.Add("XP2_Palace", "Donya Fortress");
Maps.Add("XP2_Skybar", "Ziba Tower");

/* BF3 friendly game modes, including B2K and CloseQ */
Dictionary<String, String> Modes = new Dictionary<String, String>();    
Modes.Add("ConquestLarge0", "CQ64");
Modes.Add("ConquestSmall0", "CQ");
Modes.Add("ConquestAssaultLarge0", "CQA64");
Modes.Add("ConquestAssaultSmall0", "CQA");
Modes.Add("ConquestAssaultSmall1", "CQA Small");
Modes.Add("RushLarge0", "Rush");
Modes.Add("SquadRush0", "SQR");
Modes.Add("SquadDeathMatch0", "SQDM");
Modes.Add("TeamDeathMatch0", "TDM");
// CloseQ
Modes.Add("Domination0", "ConDom");
Modes.Add("GunMaster0", "Gun");
Modes.Add("TeamDeathMatchC0", "TDMC");

String mapName = (Maps.ContainsKey(server.NextMapFileName)) _ Maps[server.NextMapFileName] : server.NextMapFileName;
String modeName = (Modes.ContainsKey(server.NextGamemode)) _ Modes[server.NextGamemode] : server.NextGamemode;

msg = "Vote completed! Next round ("+ mapName + "/" + modeName + ") will be infantry only!";
plugin.SendGlobalMessage(msg);
plugin.ServerCommand("admin.yell", msg, "20");
if (level >= 2) plugin.ConsoleWrite("^b[Vote IO]^n " + msg);

return false;
Step 2

 

Set the limit to evaluate OnIntervalServer, with interval set to 30 seconds and name it "Change IO". Set the Action to None.

 

Set first_check to this Code:

 

Code:

/* VERSION 0.8/R1 - vote io */
String kVoteResult = "voteinfantry_result"; // plugin.Data bool
String kState = "voteinfantry_state"; // plugin.Data int
String kNextMap = "voteinfantry_map"; // plugin.Data String
String kNextMode = "voteinfatnry_mode"; // plugin.Data String
String kCurrRound = "voteinfantry_round"; // plugin.Data int

double minTicketPercent = 5.0;

int level = 2;

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

if (!plugin.Data.issetInt(kState)) {
	plugin.Data.setInt(kState, 0);
	if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n initialized to State 0");
}

int state = plugin.Data.getInt(kState);
int nextState = state;

switch (state) {
	case 0: { // Idle
		if (plugin.Data.issetBool(kVoteResult)) {
			if (plugin.Data.getBool(kVoteResult)) {
				nextState = 1;
				if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n successful vote detected!");
				// Remember the next map/mode and current round number
				plugin.Data.setString(kNextMap, server.NextMapFileName);
				plugin.Data.setString(kNextMode, server.NextMapFileName);
				plugin.Data.setInt(kCurrRound, server.CurrentRound);
			}
		plugin.Data.unsetBool(kVoteResult);
		}
		break;
	}
	case 1: { // Detect round change and change config
		bool headStart = (server.RemainTicketsPercent(1) < minTicketPercent || server.RemainTicketsPercent(2) < minTicketPercent || (team3.players.Count > 0 && server.RemainTicketsPercent(3) < minTicketPercent) || (team4.players.Count > 0 && server.RemainTicketsPercent(4) < minTicketPercent));
		bool roundChanged = false;
		if ((plugin.Data.getString(kNextMap) == server.MapFileName && plugin.Data.getString(kNextMode) == server.Gamemode) || plugin.Data.getInt(kCurrRound) != server.CurrentRound) {
			roundChanged = true;
			if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n round change detected!");
			nextState = 2;
			// Remember the next map/mode and current round number
			plugin.Data.setString(kNextMap, server.NextMapFileName);
			plugin.Data.setString(kNextMode, server.NextMapFileName);
			plugin.Data.setInt(kCurrRound, server.CurrentRound);
		}
		if (headStart || roundChanged) {
			// CUSTOMIZE
			plugin.ServerCommand("vars.friendlyFire", "false");
			plugin.ServerCommand("vars.3pCam ", "false");
			plugin.ServerCommand("vars.vehicleSpawnAllowed", "false");
			if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n reconfiguring for IO!");
		}
		break;
	}
	case 2: { // Detect round change and revert to default config
		bool headStartRevert = (server.RemainTicketsPercent(1) < minTicketPercent || server.RemainTicketsPercent(2) < minTicketPercent || (team3.players.Count > 0 && server.RemainTicketsPercent(3) < minTicketPercent) || (team4.players.Count > 0 && server.RemainTicketsPercent(4) < minTicketPercent));
		bool roundRevert = false;
		bool skip = false;
		if (plugin.Data.issetBool(kVoteResult)) {
			if (plugin.Data.getBool(kVoteResult)) {
				nextState = 1;
				if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n another successful vote detected!");
				// Remember the next map/mode and current round number
				plugin.Data.setString(kNextMap, server.NextMapFileName);
				plugin.Data.setString(kNextMode, server.NextMapFileName);
				plugin.Data.setInt(kCurrRound, server.CurrentRound);
				headStartRevert = false;
				skip = true;
			}
			plugin.Data.unsetBool(kVoteResult);
		}
		if (!skip) { 
			if ((plugin.Data.getString(kNextMap) == server.MapFileName && plugin.Data.getString(kNextMode) == server.Gamemode) || plugin.Data.getInt(kCurrRound) != server.CurrentRound) {
				roundRevert = true;
				if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n round revert detected!");
				nextState = 0;
				// Forget
				plugin.Data.unsetString(kNextMap);
				plugin.Data.unsetString(kNextMode);
				plugin.Data.unsetInt(kCurrRound);
				plugin.Data.unsetBool(kVoteResult);		
			}
			if (headStartRevert || roundRevert) {
				// CUSTOMIZE
				// plugin.ServerCommand("vars.friendlyFire", "true");
				plugin.ServerCommand("vars.3pCam ", "true");
				plugin.ServerCommand("vars.vehicleSpawnAllowed", "true");
				if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n reconfiguring for vehicles!");
			}
		}
		break;	
	}
	default: nextState = 0; break;
}

if (state != nextState) {
	if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n changing to State " + nextState);
	plugin.Data.setInt(kState, nextState);
}

return false;
Keywords: vote, infantry only, votemap, vehicles, round

 

 

R1 - original version

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

Originally Posted by Tomgun*:

 

ive been away for a while and just got back, so glad someone looked into this, great work :biggrin:

 

I use the map vote plugin on my server and works well, this should be a nice addition to this and maybe someone will make this into a separate plugin sometime int he future.

 

Again nice work, ill try it out see what happens

 

cheers

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

Originally Posted by TMiland*:

 

Will this work for BF4? :smile:

 

If we run this before round ends: Code:

plugin.ServerCommand("vars.preset", "INFANTRY", "true");
it will be executed on the next round. (It works, just tested)

 

Then next round on Inf Only, do: Code:

plugin.ServerCommand("vars.preset", "NORMAL", "true");
to get it back to Normal.

 

And of course it has to work in conjunction with xvotemap. :smile:

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

Originally Posted by TMiland*:

 

Will this work_:

 

Step 1

 

Code:

/* VERSION 0.8/R1 - vote io */
double percent = 50; // CUSTOMIZE: percent of players needed to vote
double timeout = 10.0; // CUSTOMIZE: number of minutes before vote times out
int minPlayers = 16; // CUSTOMIZE: minimum players to enable vote
double minTicketPercent = 20; // CUSTOMIZE: minimum ticket percentage remaining in the round

String kVote = "voteinfantry";
String kVoteResult = "voteinfantry_result";
String kVoteTime = "voteinfantry_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*i[on]f_a_n_t_r_y_", RegexOptions.IgnoreCase);

/* Bail out if not a proper vote */

if (!nextMatch.Success) return false;

/* Bail out if vote already succeeded/failed this round */

if (server.RoundData.issetBool(kVoteResult)) {
	bool succeeded = server.RoundData.getBool(kVoteResult);
	if (succeeded) {
		msg = "Vote already suceeded, next round will be infantry only!";
	} else {
		msg = "Vote failed this round, try again next round!";
	}
	ChatPlayer(player.Name);
	return false;
}

/* Bail out if round about to end */

if (!server.RoundData.issetObject(kVoteTime) && (server.RemainTicketsPercent(1) < minTicketPercent || server.RemainTicketsPercent(2) < minTicketPercent || (team3.players.Count > 0 && server.RemainTicketsPercent(3) < minTicketPercent) || (team4.players.Count > 0 && server.RemainTicketsPercent(4) < minTicketPercent))) {
	msg = "Round too close to ending to start a vote!";
	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(kVote)) player.RoundData.setBool(kVote, true);

if (level >= 2) plugin.ConsoleWrite("^b[Vote IO]^n " + player.FullName + " voted for infantry only");

msg = "You voted to make the next round infantry only!";
ChatPlayer(player.Name);

/* Tally the votes */

int votes = 0;
List<PlayerInfoInterface> all = new List<PlayerInfoInterface>();
all.AddRange(team1.players);
all.AddRange(team2.players);
if (team3.players.Count > 0) all.AddRange(team3.players);
if (team4.players.Count > 0) all.AddRange(team4.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[Vote IO]^n vote timer started");
}
DateTime started = (DateTime)server.RoundData.getObject(kVoteTime);
TimeSpan since = DateTime.Now.Subtract(started);

if (since.TotalMinutes > timeout) {
	msg = "Voting time has expired, the vote is cancelled!";
	plugin.SendGlobalMessage(msg);
	plugin.ServerCommand("admin.yell", msg);
	if (level >= 2) plugin.ConsoleWrite("^b[Vote IO]^n vote timeout expired");
	foreach (PlayerInfoInterface can in all) {
		// Erase the vote
		if (can.RoundData.issetBool(kVote)) can.RoundData.unsetBool(kVote);
	}
	server.RoundData.setBool(kVoteResult, false);
	return false;
}

/* Otherwise tally */

foreach(PlayerInfoInterface p in all) {
    if (p.RoundData.issetBool(kVote)) votes++;
}
if (level >= 3) plugin.ConsoleWrite("^b[Vote IO]^n votes = " + votes + " of " + all.Count);

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

if (level >= 3) plugin.ConsoleWrite("^b[Vote IO]^n " + percent.ToString("F0") + "% needed votes = " + needed);

if (remain > 0) {
	msg = remain + " votes needed to change to IO with " + Convert.ToInt32(Math.Ceiling(timeout - since.TotalMinutes)) + " mins left!";
	plugin.SendGlobalMessage(msg);
	plugin.ServerCommand("admin.yell", msg, "8");
	if (level >= 2) plugin.ConsoleWrite("^b[Vote IO]^n " + msg);
	return false;
}

/* Vote succeeded, set up the next round */

server.RoundData.setBool(kVoteResult, false);
plugin.Data.setBool(kVoteResult, true); // Signal the other limit to change to io

String mapName = plugin.FriendlyMapName(server.NextMapFileName);
String modeName = plugin.FriendlyModeName(server.NextGamemode);

String mapName = (mapName.ContainsKey(server.NextMapFileName)) _ mapName[server.NextMapFileName] : server.NextMapFileName;
String modeName = (modeName.ContainsKey(server.NextGamemode)) _ modeName[server.NextGamemode] : server.NextGamemode;

msg = "Vote completed! Next round (" + mapName + "/" + modeName + ") will be infantry only!";
plugin.SendGlobalMessage(msg);
plugin.ServerCommand("admin.yell", msg, "15");
if (level >= 2) plugin.ConsoleWrite("^b[Vote IO]^n " + msg);

return false;
Step 2

 

Code:

/* VERSION 0.8/R1 - vote io */
String kVoteResult = "voteinfantry_result"; // plugin.Data bool
String kState = "voteinfantry_state"; // plugin.Data int
String kNextMap = "voteinfantry_map"; // plugin.Data String
String kNextMode = "voteinfatnry_mode"; // plugin.Data String
String kCurrRound = "voteinfantry_round"; // plugin.Data int

double minTicketPercent = 5.0;

int level = 2;
String mapName = plugin.FriendlyMapName(server.NextMapFileName);
String modeName = plugin.FriendlyModeName(server.NextGamemode);
try {
	level = Convert.ToInt32(plugin.getPluginVarValue("debug_level"));
} catch (Exception e) {}

if (!plugin.Data.issetInt(kState)) {
	plugin.Data.setInt(kState, 0);
	if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n initialized to State 0");
}

int state = plugin.Data.getInt(kState);
int nextState = state;

switch (state) {
	case 0: { // Idle
		if (plugin.Data.issetBool(kVoteResult)) {
			if (plugin.Data.getBool(kVoteResult)) {
				nextState = 1;
				if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n successful vote detected!");
				// Remember the next map/mode and current round number
				plugin.Data.setString(kNextMap, mapName);
				plugin.Data.setString(kNextMode, mapName);
				plugin.Data.setInt(kCurrRound, server.CurrentRound);
			}
		plugin.Data.unsetBool(kVoteResult);
		}
		break;
	}
	case 1: { // Detect round change and change config
		bool headStart = (server.RemainTicketsPercent(1) < minTicketPercent || server.RemainTicketsPercent(2) < minTicketPercent || (team3.players.Count > 0 && server.RemainTicketsPercent(3) < minTicketPercent) || (team4.players.Count > 0 && server.RemainTicketsPercent(4) < minTicketPercent));
		bool roundChanged = false;
		if ((plugin.Data.getString(kNextMap) == server.MapFileName && plugin.Data.getString(kNextMode) == server.Gamemode) || plugin.Data.getInt(kCurrRound) != server.CurrentRound) {
			roundChanged = true;
			if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n round change detected!");
			nextState = 2;
			// Remember the next map/mode and current round number
			plugin.Data.setString(kNextMap, mapName);
			plugin.Data.setString(kNextMode, mapName);
			plugin.Data.setInt(kCurrRound, server.CurrentRound);
		}
		if (headStart || roundChanged) {
			// CUSTOMIZE
			plugin.ServerCommand("vars.preset", "INFANTRY", "true");
			if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n setting preset to IO!");
		}
		break;
	}
	case 2: { // Detect round change and revert to default config
		bool headStartRevert = (server.RemainTicketsPercent(1) < minTicketPercent || server.RemainTicketsPercent(2) < minTicketPercent || (team3.players.Count > 0 && server.RemainTicketsPercent(3) < minTicketPercent) || (team4.players.Count > 0 && server.RemainTicketsPercent(4) < minTicketPercent));
		bool roundRevert = false;
		bool skip = false;
		if (plugin.Data.issetBool(kVoteResult)) {
			if (plugin.Data.getBool(kVoteResult)) {
				nextState = 1;
				if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n another successful vote detected!");
				// Remember the next map/mode and current round number
				plugin.Data.setString(kNextMap, mapName);
				plugin.Data.setString(kNextMode, mapName);
				plugin.Data.setInt(kCurrRound, server.CurrentRound);
				headStartRevert = false;
				skip = true;
			}
			plugin.Data.unsetBool(kVoteResult);
		}
		if (!skip) {
			if ((plugin.Data.getString(kNextMap) == server.MapFileName && plugin.Data.getString(kNextMode) == server.Gamemode) || plugin.Data.getInt(kCurrRound) != server.CurrentRound) {
				roundRevert = true;
				if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n round revert detected!");
				nextState = 0;
				// Forget
				plugin.Data.unsetString(kNextMap);
				plugin.Data.unsetString(kNextMode);
				plugin.Data.unsetInt(kCurrRound);
				plugin.Data.unsetBool(kVoteResult);		
			}
			if (headStartRevert || roundRevert) {
				// CUSTOMIZE
				plugin.ServerCommand("vars.preset", "NORMAL", "true");
				if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n setting preset to NORMAL!");
			}
		}
		break;	
	}
	default: nextState = 0; break;
}

if (state != nextState) {
	if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n changing to State " + nextState);
	plugin.Data.setInt(kState, nextState);
}

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

Originally Posted by PapaCharlie9*:

 

Will this work_:

 

Step 1

 

Code:

/* VERSION 0.8/R1 - vote io */
double percent = 50; // CUSTOMIZE: percent of players needed to vote
double timeout = 10.0; // CUSTOMIZE: number of minutes before vote times out
int minPlayers = 16; // CUSTOMIZE: minimum players to enable vote
double minTicketPercent = 20; // CUSTOMIZE: minimum ticket percentage remaining in the round

String kVote = "voteinfantry";
String kVoteResult = "voteinfantry_result";
String kVoteTime = "voteinfantry_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*i[on]f_a_n_t_r_y_", RegexOptions.IgnoreCase);

/* Bail out if not a proper vote */

if (!nextMatch.Success) return false;

/* Bail out if vote already succeeded/failed this round */

if (server.RoundData.issetBool(kVoteResult)) {
	bool succeeded = server.RoundData.getBool(kVoteResult);
	if (succeeded) {
		msg = "Vote already suceeded, next round will be infantry only!";
	} else {
		msg = "Vote failed this round, try again next round!";
	}
	ChatPlayer(player.Name);
	return false;
}

/* Bail out if round about to end */

if (!server.RoundData.issetObject(kVoteTime) && (server.RemainTicketsPercent(1) < minTicketPercent || server.RemainTicketsPercent(2) < minTicketPercent || (team3.players.Count > 0 && server.RemainTicketsPercent(3) < minTicketPercent) || (team4.players.Count > 0 && server.RemainTicketsPercent(4) < minTicketPercent))) {
	msg = "Round too close to ending to start a vote!";
	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(kVote)) player.RoundData.setBool(kVote, true);

if (level >= 2) plugin.ConsoleWrite("^b[Vote IO]^n " + player.FullName + " voted for infantry only");

msg = "You voted to make the next round infantry only!";
ChatPlayer(player.Name);

/* Tally the votes */

int votes = 0;
List<PlayerInfoInterface> all = new List<PlayerInfoInterface>();
all.AddRange(team1.players);
all.AddRange(team2.players);
if (team3.players.Count > 0) all.AddRange(team3.players);
if (team4.players.Count > 0) all.AddRange(team4.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[Vote IO]^n vote timer started");
}
DateTime started = (DateTime)server.RoundData.getObject(kVoteTime);
TimeSpan since = DateTime.Now.Subtract(started);

if (since.TotalMinutes > timeout) {
	msg = "Voting time has expired, the vote is cancelled!";
	plugin.SendGlobalMessage(msg);
	plugin.ServerCommand("admin.yell", msg);
	if (level >= 2) plugin.ConsoleWrite("^b[Vote IO]^n vote timeout expired");
	foreach (PlayerInfoInterface can in all) {
		// Erase the vote
		if (can.RoundData.issetBool(kVote)) can.RoundData.unsetBool(kVote);
	}
	server.RoundData.setBool(kVoteResult, false);
	return false;
}

/* Otherwise tally */

foreach(PlayerInfoInterface p in all) {
    if (p.RoundData.issetBool(kVote)) votes++;
}
if (level >= 3) plugin.ConsoleWrite("^b[Vote IO]^n votes = " + votes + " of " + all.Count);

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

if (level >= 3) plugin.ConsoleWrite("^b[Vote IO]^n " + percent.ToString("F0") + "% needed votes = " + needed);

if (remain > 0) {
	msg = remain + " votes needed to change to IO with " + Convert.ToInt32(Math.Ceiling(timeout - since.TotalMinutes)) + " mins left!";
	plugin.SendGlobalMessage(msg);
	plugin.ServerCommand("admin.yell", msg, "8");
	if (level >= 2) plugin.ConsoleWrite("^b[Vote IO]^n " + msg);
	return false;
}

/* Vote succeeded, set up the next round */

server.RoundData.setBool(kVoteResult, false);
plugin.Data.setBool(kVoteResult, true); // Signal the other limit to change to io

String mapName = plugin.FriendlyMapName(server.NextMapFileName);
String modeName = plugin.FriendlyModeName(server.NextGamemode);

String mapName = (mapName.ContainsKey(server.NextMapFileName)) _ mapName[server.NextMapFileName] : server.NextMapFileName;
String modeName = (modeName.ContainsKey(server.NextGamemode)) _ modeName[server.NextGamemode] : server.NextGamemode;

msg = "Vote completed! Next round (" + mapName + "/" + modeName + ") will be infantry only!";
plugin.SendGlobalMessage(msg);
plugin.ServerCommand("admin.yell", msg, "15");
if (level >= 2) plugin.ConsoleWrite("^b[Vote IO]^n " + msg);

return false;
Step 2

 

Code:

/* VERSION 0.8/R1 - vote io */
String kVoteResult = "voteinfantry_result"; // plugin.Data bool
String kState = "voteinfantry_state"; // plugin.Data int
String kNextMap = "voteinfantry_map"; // plugin.Data String
String kNextMode = "voteinfatnry_mode"; // plugin.Data String
String kCurrRound = "voteinfantry_round"; // plugin.Data int

double minTicketPercent = 5.0;

int level = 2;
String mapName = plugin.FriendlyMapName(server.NextMapFileName);
String modeName = plugin.FriendlyModeName(server.NextGamemode);
try {
	level = Convert.ToInt32(plugin.getPluginVarValue("debug_level"));
} catch (Exception e) {}

if (!plugin.Data.issetInt(kState)) {
	plugin.Data.setInt(kState, 0);
	if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n initialized to State 0");
}

int state = plugin.Data.getInt(kState);
int nextState = state;

switch (state) {
	case 0: { // Idle
		if (plugin.Data.issetBool(kVoteResult)) {
			if (plugin.Data.getBool(kVoteResult)) {
				nextState = 1;
				if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n successful vote detected!");
				// Remember the next map/mode and current round number
				plugin.Data.setString(kNextMap, mapName);
				plugin.Data.setString(kNextMode, mapName);
				plugin.Data.setInt(kCurrRound, server.CurrentRound);
			}
		plugin.Data.unsetBool(kVoteResult);
		}
		break;
	}
	case 1: { // Detect round change and change config
		bool headStart = (server.RemainTicketsPercent(1) < minTicketPercent || server.RemainTicketsPercent(2) < minTicketPercent || (team3.players.Count > 0 && server.RemainTicketsPercent(3) < minTicketPercent) || (team4.players.Count > 0 && server.RemainTicketsPercent(4) < minTicketPercent));
		bool roundChanged = false;
		if ((plugin.Data.getString(kNextMap) == server.MapFileName && plugin.Data.getString(kNextMode) == server.Gamemode) || plugin.Data.getInt(kCurrRound) != server.CurrentRound) {
			roundChanged = true;
			if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n round change detected!");
			nextState = 2;
			// Remember the next map/mode and current round number
			plugin.Data.setString(kNextMap, mapName);
			plugin.Data.setString(kNextMode, mapName);
			plugin.Data.setInt(kCurrRound, server.CurrentRound);
		}
		if (headStart || roundChanged) {
			// CUSTOMIZE
			plugin.ServerCommand("vars.preset", "INFANTRY", "true");
			if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n setting preset to IO!");
		}
		break;
	}
	case 2: { // Detect round change and revert to default config
		bool headStartRevert = (server.RemainTicketsPercent(1) < minTicketPercent || server.RemainTicketsPercent(2) < minTicketPercent || (team3.players.Count > 0 && server.RemainTicketsPercent(3) < minTicketPercent) || (team4.players.Count > 0 && server.RemainTicketsPercent(4) < minTicketPercent));
		bool roundRevert = false;
		bool skip = false;
		if (plugin.Data.issetBool(kVoteResult)) {
			if (plugin.Data.getBool(kVoteResult)) {
				nextState = 1;
				if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n another successful vote detected!");
				// Remember the next map/mode and current round number
				plugin.Data.setString(kNextMap, mapName);
				plugin.Data.setString(kNextMode, mapName);
				plugin.Data.setInt(kCurrRound, server.CurrentRound);
				headStartRevert = false;
				skip = true;
			}
			plugin.Data.unsetBool(kVoteResult);
		}
		if (!skip) {
			if ((plugin.Data.getString(kNextMap) == server.MapFileName && plugin.Data.getString(kNextMode) == server.Gamemode) || plugin.Data.getInt(kCurrRound) != server.CurrentRound) {
				roundRevert = true;
				if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n round revert detected!");
				nextState = 0;
				// Forget
				plugin.Data.unsetString(kNextMap);
				plugin.Data.unsetString(kNextMode);
				plugin.Data.unsetInt(kCurrRound);
				plugin.Data.unsetBool(kVoteResult);		
			}
			if (headStartRevert || roundRevert) {
				// CUSTOMIZE
				plugin.ServerCommand("vars.preset", "NORMAL", "true");
				if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n setting preset to NORMAL!");
			}
		}
		break;	
	}
	default: nextState = 0; break;
}

if (state != nextState) {
	if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n changing to State " + nextState);
	plugin.Data.setInt(kState, nextState);
}

return false;
Looks good to me, though I only skimmed it. The important changes are the preset and friendly map/mode improvement code.

 

Shouldn't you omit the "true" from the preset change? It thought that means override all settings and prevent them from being changed, or something like that?

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

Originally Posted by TMiland*:

 

Looks good to me, though I only skimmed it. The important changes are the preset and friendly map/mode improvement code.

 

Shouldn't you omit the "true" from the preset change? It thought that means override all settings and prevent them from being changed, or something like that?

Okay, got this error on limit 1:

[08:35:04 35] [insane Limits] ERROR: 8 errors compiling Expression

[08:35:04 35] [insane Limits] ERROR: (CS0128, line: 174, column: 20): A local variable named 'mapName' is already defined in this scope

[08:35:04 35] [insane Limits] ERROR: (CS0117, line: 174, column: 39): 'string' does not contain a definition for 'ContainsKey'

[08:35:04 35] [insane Limits] ERROR: (CS1502, line: 174, column: 78): The best overloaded method match for 'string.this[int]' has some invalid arguments

[08:35:04 35] [insane Limits] ERROR: (CS1503, line: 174, column: 86): Argument '1': cannot convert from 'string' to 'int'

[08:35:04 35] [insane Limits] ERROR: (CS0128, line: 175, column: 20): A local variable named 'modeName' is already defined in this scope

[08:35:04 35] [insane Limits] ERROR: (CS0117, line: 175, column: 41): 'string' does not contain a definition for 'ContainsKey'

[08:35:04 35] [insane Limits] ERROR: (CS1502, line: 175, column: 77): The best overloaded method match for 'string.this[int]' has some invalid arguments

[08:35:04 35] [insane Limits] ERROR: (CS1503, line: 175, column: 86): Argument '1': cannot convert from 'string' to 'int'

So i guess this is where i messed up? :tongue:

 

Code:

String mapName = plugin.FriendlyMapName(server.NextMapFileName);
String modeName = plugin.FriendlyModeName(server.NextGamemode);

String mapName = (mapName.ContainsKey(server.NextMapFileName)) _ mapName[server.NextMapFileName] : server.NextMapFileName;
String modeName = (modeName.ContainsKey(server.NextGamemode)) _ modeName[server.NextGamemode] : server.NextGamemode;
Edit:

Changed the limit to add friendly mapnames for BF4: Code:

/* VERSION 0.8/R1 - vote io */
double percent = 50; // CUSTOMIZE: percent of players needed to vote
double timeout = 10.0; // CUSTOMIZE: number of minutes before vote times out
int minPlayers = 16; // CUSTOMIZE: minimum players to enable vote
double minTicketPercent = 20; // CUSTOMIZE: minimum ticket percentage remaining in the round

String kVote = "voteinfantry";
String kVoteResult = "voteinfantry_result";
String kVoteTime = "voteinfantry_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*i[on]f_a_n_t_r_y_", RegexOptions.IgnoreCase);

/* Bail out if not a proper vote */

if (!nextMatch.Success) return false;

/* Bail out if vote already succeeded/failed this round */

if (server.RoundData.issetBool(kVoteResult)) {
	bool succeeded = server.RoundData.getBool(kVoteResult);
	if (succeeded) {
		msg = "Vote already suceeded, next round will be infantry only!";
	} else {
		msg = "Vote failed this round, try again next round!";
	}
	ChatPlayer(player.Name);
	return false;
}

/* Bail out if round about to end */

if (!server.RoundData.issetObject(kVoteTime) && (server.RemainTicketsPercent(1) < minTicketPercent || server.RemainTicketsPercent(2) < minTicketPercent || (team3.players.Count > 0 && server.RemainTicketsPercent(3) < minTicketPercent) || (team4.players.Count > 0 && server.RemainTicketsPercent(4) < minTicketPercent))) {
	msg = "Round too close to ending to start a vote!";
	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(kVote)) player.RoundData.setBool(kVote, true);

if (level >= 2) plugin.ConsoleWrite("^b[Vote IO]^n " + player.FullName + " voted for infantry only");

msg = "You voted to make the next round infantry only!";
ChatPlayer(player.Name);

/* Tally the votes */

int votes = 0;
List<PlayerInfoInterface> all = new List<PlayerInfoInterface>();
all.AddRange(team1.players);
all.AddRange(team2.players);
if (team3.players.Count > 0) all.AddRange(team3.players);
if (team4.players.Count > 0) all.AddRange(team4.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[Vote IO]^n vote timer started");
}
DateTime started = (DateTime)server.RoundData.getObject(kVoteTime);
TimeSpan since = DateTime.Now.Subtract(started);

if (since.TotalMinutes > timeout) {
	msg = "Voting time has expired, the vote is cancelled!";
	plugin.SendGlobalMessage(msg);
	plugin.ServerCommand("admin.yell", msg);
	if (level >= 2) plugin.ConsoleWrite("^b[Vote IO]^n vote timeout expired");
	foreach (PlayerInfoInterface can in all) {
		// Erase the vote
		if (can.RoundData.issetBool(kVote)) can.RoundData.unsetBool(kVote);
	}
	server.RoundData.setBool(kVoteResult, false);
	return false;
}

/* Otherwise tally */

foreach(PlayerInfoInterface p in all) {
    if (p.RoundData.issetBool(kVote)) votes++;
}
if (level >= 3) plugin.ConsoleWrite("^b[Vote IO]^n votes = " + votes + " of " + all.Count);

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

if (level >= 3) plugin.ConsoleWrite("^b[Vote IO]^n " + percent.ToString("F0") + "% needed votes = " + needed);

if (remain > 0) {
	msg = remain + " votes needed to change to IO with " + Convert.ToInt32(Math.Ceiling(timeout - since.TotalMinutes)) + " mins left!";
	plugin.SendGlobalMessage(msg);
	plugin.ServerCommand("admin.yell", msg, "8");
	if (level >= 2) plugin.ConsoleWrite("^b[Vote IO]^n " + msg);
	return false;
}

/* Vote succeeded, set up the next round */

server.RoundData.setBool(kVoteResult, false);
plugin.Data.setBool(kVoteResult, true); // Signal the other limit to change to io

/* BF4 friendly map names, including China Rising, Second Assault, Naval Strike, Dragons Teeth and Final Stand */
Dictionary<String, String> Maps = new Dictionary<String, String>();
Maps.Add("MP_Abandoned", "Zavod 311");
Maps.Add("MP_Damage", "Lancang Dam");
Maps.Add("MP_Flooded", "Flood Zone");
Maps.Add("MP_Journey", "Golmud Railway");
Maps.Add("MP_Naval", "Paracel Storm");
Maps.Add("MP_Prison", "Operation Locker");
Maps.Add("MP_Resort", "Hainan Resort");
Maps.Add("MP_Siege", "Siege of Shanghai");
Maps.Add("MP_TheDish", "Rogue Transmission");
Maps.Add("MP_Tremors", "Dawnbreaker");
// XP1 (China Rising)
Maps.Add("XP1_001", "Silk Road");
Maps.Add("XP1_002", "Altai Range");
Maps.Add("XP1_003", "Guilin Peaks");
Maps.Add("XP1_004", "Dragon Pass");
// XP0 (Second Assault)
Maps.Add("XP0_Caspian", "Caspian Border 2014");
Maps.Add("XP0_Firestorm", "Operation Firestorm 2014");
Maps.Add("XP0_Metro", "Operation Metro 2014");
Maps.Add("XP0_Oman", "Gulf of Oman 2014");
// XP2 (Naval Strike)
Maps.Add("XP2_001", "Lost Islands");
Maps.Add("XP2_002", "Nansha Strike");
Maps.Add("XP2_003", "Wavebreaker");
Maps.Add("XP2_004", "Operation Mortar");
// XP3 (Dragons Teeth)
Maps.Add("XP3_MarketPl", "Pearl Market");
Maps.Add("XP3_Prpganda", "Propaganda");
Maps.Add("XP3_UrbanGdn", "Lumphini Garden");
Maps.Add("XP3_WtrFront", "Sunken Dragon");
// XP4 (Final Stand)
Maps.Add("XP4_Arctic", "Operation Whiteout");
Maps.Add("XP4_SubBase", "Hammerhead");
Maps.Add("XP4_Titan", "Hangar 21");
Maps.Add("XP4_WlkrFtry", "Giants Of Karelia");

/* BF4 friendly game modes */
Dictionary<String, String> Modes = new Dictionary<String, String>();    
Modes.Add("ConquestLarge0", "Conquest Large");
Modes.Add("ConquestSmall0", "Conquest Small");
Modes.Add("RushLarge0", "Rush");
Modes.Add("SquAddeathMatch0", "Squad Deathmatch");
Modes.Add("TeamDeathMatch0", "Team Deathmatch");

String mapName = (Maps.ContainsKey(server.NextMapFileName)) _ Maps[server.NextMapFileName] : server.NextMapFileName;
String modeName = (Modes.ContainsKey(server.NextGamemode)) _ Modes[server.NextGamemode] : server.NextGamemode;

msg = "Vote completed! Next round (" + mapName + "/" + modeName + ") will be infantry only!";
plugin.SendGlobalMessage(msg);
plugin.ServerCommand("admin.yell", msg, "15");
if (level >= 2) plugin.ConsoleWrite("^b[Vote IO]^n " + msg);

return false;
Edit2:

 

Now i only need to notify after the mapvote is finished, do you know how? :smile:

 

Edit3:

I ended up with this clumsy limit:

 

Create a limit to evaluate OnIntervalServer, Interval 120, call it "Vote IO Reminder".

 

Set first_check to this Code: Code:

String ChatMessage = "Type !voteio (!vote io) or !voteinfantry (!vote infantry) to run the next round as INFANTRY ONLY!"; // CHANGE
String YellMessage = "Type !voteio (!vote io) or !voteinfantry (!vote infantry) to run the next round as INFANTRY ONLY!"; // CHANGE
int YellDuration = 15; // CHANGE
int TicketsStart = 500; // CHANGE
int TicketsStop = 150; // CHANGE

if (server.RemainTickets(1) < TicketsStart || server.RemainTickets(2) < TicketsStart) {
	plugin.PRoConChat("ADMIN > " + ChatMessage);
	plugin.SendGlobalMessage(ChatMessage);
	plugin.SendGlobalYell(YellMessage, YellDuration);
} else if (server.RemainTickets(1) < TicketsStop || server.RemainTickets(2) < TicketsStop) {
	return false;
}

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

Originally Posted by PapaCharlie9*:

 

Okay, got this error on limit 1:

 

 

So i guess this is where i messed up? :tongue:

 

Code:

String mapName = plugin.FriendlyMapName(server.NextMapFileName);
String modeName = plugin.FriendlyModeName(server.NextGamemode);

String mapName = (mapName.ContainsKey(server.NextMapFileName)) _ mapName[server.NextMapFileName] : server.NextMapFileName;
String modeName = (modeName.ContainsKey(server.NextGamemode)) _ modeName[server.NextGamemode] : server.NextGamemode;
Edit:

Changed the limit to add friendly mapnames for BF4:

Yes, that was the mess up, but you fixed it in the most painful way possible! All you had to do was DELETE the second set of mapName/modeName lines. The plugin.Friendly... lines were fine.
* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by TMiland*:

 

Yes, that was the mess up, but you fixed it in the most painful way possible! All you had to do was DELETE the second set of mapName/modeName lines. The plugin.Friendly... lines were fine.

Aw crap! Okay, got it sorted out now, thanks for pointing it out. I'm a noob! :ohmy:

 

Here goes all i have so far:

 

Set the limit to evaluate OnAnyChat and name it "Vote IO". Set the Action to None.

 

Set first_check to this Expression:

Code:

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

Code:

/* VERSION 0.8/R1 - vote io */
double percent = 10; // CUSTOMIZE: percent of players needed to vote
double timeout = 20.0; // CUSTOMIZE: number of minutes before vote times out
int minPlayers = 16; // CUSTOMIZE: minimum players to enable vote
double minTicketPercent = 20; // CUSTOMIZE: minimum ticket percentage remaining in the round

String kVote = "voteinfantry";
String kVoteResult = "voteinfantry_result";
String kVoteTime = "voteinfantry_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*i[on]f_a_n_t_r_y_", RegexOptions.IgnoreCase);

/* Bail out if not a proper vote */

if (!nextMatch.Success) return false;

/* Bail out if vote already succeeded/failed this round */

if (server.RoundData.issetBool(kVoteResult)) {
	bool succeeded = server.RoundData.getBool(kVoteResult);
	if (succeeded) {
		msg = "Vote already suceeded, next round will be infantry only!";
	} else {
		msg = "Vote failed this round, try again next round!";
	}
	ChatPlayer(player.Name);
	return false;
}

/* Bail out if round about to end */

if (!server.RoundData.issetObject(kVoteTime) && (server.RemainTicketsPercent(1) < minTicketPercent || server.RemainTicketsPercent(2) < minTicketPercent || (team3.players.Count > 0 && server.RemainTicketsPercent(3) < minTicketPercent) || (team4.players.Count > 0 && server.RemainTicketsPercent(4) < minTicketPercent))) {
	msg = "Round too close to ending to start a vote!";
	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(kVote)) player.RoundData.setBool(kVote, true);

if (level >= 2) plugin.ConsoleWrite("^b[Vote IO]^n " + player.FullName + " voted for infantry only");

msg = "You voted to make the next round infantry only!";
ChatPlayer(player.Name);

/* Tally the votes */

int votes = 0;
List<PlayerInfoInterface> all = new List<PlayerInfoInterface>();
all.AddRange(team1.players);
all.AddRange(team2.players);
if (team3.players.Count > 0) all.AddRange(team3.players);
if (team4.players.Count > 0) all.AddRange(team4.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[Vote IO]^n vote timer started");
}
DateTime started = (DateTime)server.RoundData.getObject(kVoteTime);
TimeSpan since = DateTime.Now.Subtract(started);

if (since.TotalMinutes > timeout) {
	msg = "Voting time has expired, the vote is cancelled!";
	plugin.SendGlobalMessage(msg);
	plugin.ServerCommand("admin.yell", msg);
	if (level >= 2) plugin.ConsoleWrite("^b[Vote IO]^n vote timeout expired");
	foreach (PlayerInfoInterface can in all) {
		// Erase the vote
		if (can.RoundData.issetBool(kVote)) can.RoundData.unsetBool(kVote);
	}
	server.RoundData.setBool(kVoteResult, false);
	return false;
}

/* Otherwise tally */

foreach(PlayerInfoInterface p in all) {
    if (p.RoundData.issetBool(kVote)) votes++;
}
if (level >= 3) plugin.ConsoleWrite("^b[Vote IO]^n votes = " + votes + " of " + all.Count);

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

if (level >= 3) plugin.ConsoleWrite("^b[Vote IO]^n " + percent.ToString("F0") + "% needed votes = " + needed);

if (remain > 0) {
	msg = remain + " votes needed to change to IO with " + Convert.ToInt32(Math.Ceiling(timeout - since.TotalMinutes)) + " mins left!";
	plugin.SendGlobalMessage(msg);
	plugin.ServerCommand("admin.yell", msg, "8");
	if (level >= 2) plugin.ConsoleWrite("^b[Vote IO]^n " + msg);
	return false;
}

/* Vote succeeded, set up the next round */

server.RoundData.setBool(kVoteResult, false);
plugin.Data.setBool(kVoteResult, true); // Signal the other limit to change to io

String mapName = plugin.FriendlyMapName(server.NextMapFileName);
String modeName = plugin.FriendlyModeName(server.NextGamemode);

msg = "Vote completed! Next round (" + mapName + "/" + modeName + ") will be infantry only!";
plugin.SendGlobalMessage(msg);
plugin.ServerCommand("admin.yell", msg, "15");
if (level >= 2) plugin.ConsoleWrite("^b[Vote IO]^n " + msg);

return false;
Set the limit to evaluate OnIntervalServer, with interval set to 30 seconds and name it "Change IO". Set the Action to None.

 

Set first_check to this Code:

Code:

/* VERSION 0.8/R1 - vote io */
String kVoteResult = "voteinfantry_result"; // plugin.Data bool
String kState = "voteinfantry_state"; // plugin.Data int
String kNextMap = "voteinfantry_map"; // plugin.Data String
String kNextMode = "voteinfatnry_mode"; // plugin.Data String
String kCurrRound = "voteinfantry_round"; // plugin.Data int

double minTicketPercent = 5.0;

int level = 2;

String mapName = plugin.FriendlyMapName(server.NextMapFileName);

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

if (!plugin.Data.issetInt(kState)) {
	plugin.Data.setInt(kState, 0);
	if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n initialized to State 0");
}

int state = plugin.Data.getInt(kState);
int nextState = state;

switch (state) {
	case 0: { // Idle
		if (plugin.Data.issetBool(kVoteResult)) {
			if (plugin.Data.getBool(kVoteResult)) {
				nextState = 1;
				if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n successful vote detected!");
				// Remember the next map/mode and current round number
				plugin.Data.setString(kNextMap, mapName);
				plugin.Data.setString(kNextMode, mapName);
				plugin.Data.setInt(kCurrRound, server.CurrentRound);
			}
		plugin.Data.unsetBool(kVoteResult);
		}
		break;
	}
	case 1: { // Detect round change and change config
		bool headStart = (server.RemainTicketsPercent(1) < minTicketPercent || server.RemainTicketsPercent(2) < minTicketPercent || (team3.players.Count > 0 && server.RemainTicketsPercent(3) < minTicketPercent) || (team4.players.Count > 0 && server.RemainTicketsPercent(4) < minTicketPercent));
		bool roundChanged = false;
		if ((plugin.Data.getString(kNextMap) == server.MapFileName && plugin.Data.getString(kNextMode) == server.Gamemode) || plugin.Data.getInt(kCurrRound) != server.CurrentRound) {
			roundChanged = true;
			if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n round change detected!");
			nextState = 2;
			// Remember the next map/mode and current round number
			plugin.Data.setString(kNextMap, mapName);
			plugin.Data.setString(kNextMode, mapName);
			plugin.Data.setInt(kCurrRound, server.CurrentRound);
		}
		if (headStart || roundChanged) {
			// CUSTOMIZE
			plugin.ServerCommand("vars.preset", "NORMAL", "true");
			server.Data.setString("RememberPreset", "NORMAL"); // where 'preset' is the string value of the preset, i.e., "Normal" or "Infantry".
			if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n setting preset to NORMAL!");
		}
		break;
	}
	case 2: { // Detect round change and revert to default config
		bool headStartRevert = (server.RemainTicketsPercent(1) < minTicketPercent || server.RemainTicketsPercent(2) < minTicketPercent || (team3.players.Count > 0 && server.RemainTicketsPercent(3) < minTicketPercent) || (team4.players.Count > 0 && server.RemainTicketsPercent(4) < minTicketPercent));
		bool roundRevert = false;
		bool skip = false;
		if (plugin.Data.issetBool(kVoteResult)) {
			if (plugin.Data.getBool(kVoteResult)) {
				nextState = 1;
				if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n another successful vote detected!");
				// Remember the next map/mode and current round number
				plugin.Data.setString(kNextMap, mapName);
				plugin.Data.setString(kNextMode, mapName);
				plugin.Data.setInt(kCurrRound, server.CurrentRound);
				headStartRevert = false;
				skip = true;
			}
			plugin.Data.unsetBool(kVoteResult);
		}
		if (!skip) {
			if ((plugin.Data.getString(kNextMap) == server.MapFileName && plugin.Data.getString(kNextMode) == server.Gamemode) || plugin.Data.getInt(kCurrRound) != server.CurrentRound) {
				roundRevert = true;
				if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n round revert detected!");
				nextState = 0;
				// Forget
				plugin.Data.unsetString(kNextMap);
				plugin.Data.unsetString(kNextMode);
				plugin.Data.unsetInt(kCurrRound);
				plugin.Data.unsetBool(kVoteResult);		
			}
			if (headStartRevert || roundRevert) {
				// CUSTOMIZE
				plugin.ServerCommand("vars.preset", "INFANTRY", "true");
				server.Data.setString("RememberPreset", "INFANTRY"); // where 'preset' is the string value of the preset, i.e., "Normal" or "Infantry".
				if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n setting preset to IO!");
			}
		}
		break;	
	}
	default: nextState = 0; break;
}

if (state != nextState) {
	if (level >= 3) plugin.ConsoleWrite("^b[Change IO]^n changing to State " + nextState);
	plugin.Data.setInt(kState, nextState);
}

return false;
Create a limit to evaluate OnIntervalServer, Interval 120, call it "Vote IO Reminder".

 

Set first_check to this Code:

Code:

String msg = "Type !voteio to run the next round as INFANTRY ONLY!";
// Times
int yellTime = 10; // seconds
int secondNoticeSpawnCount = 4;

/* CODE */

String key = "WelcomeVoteIO_" + player.Name;

int count = 0;
if (player.Data.issetInt(key)) count = player.Data.getInt(key);

count = count + 1;

if (count == secondNoticeSpawnCount) { // Second notice

    plugin.SendPlayerMessage(player.Name, msg);
	plugin.SendPlayerYell(player.Name, msg, yellTime);
}

player.Data.setInt(key, count);

return false;
Set the limit to evaluate OnRoundOver and name it "Vote IO Preset in ServerName". Set the Action to None.

 

Set first_check to this Code:

Code:

if (!server.Data.issetString("RememberPreset"))
    return false; // skip if not set yet
String preset =server.Data.getString("RememberPreset");
if (preset == "Normal") {
		String ServerName = "YOUR SERVER NAME | Normal";
		plugin.ServerCommand("vars.serverName", ServerName);
		plugin.ConsoleWrite("^b^1SERVER NAME >^0^n " + ServerName);
		plugin.PRoConChat("^b^1SERVER NAME >^0^n " + ServerName);
} else if (preset == "Infantry") {
		String ServerName = "YOUR SERVER NAME | Infantry Only";
		plugin.ServerCommand("vars.serverName", ServerName);
		plugin.ConsoleWrite("^b^1SERVER NAME >^0^n " + ServerName);
		plugin.PRoConChat("^b^1SERVER NAME >^0^n " + ServerName);
}
* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by TMiland*:

 

Okay, i had switched normal and inf only, so now it works if a successful vote is done.

 

Only problem i have now, is that after a successful vote is done, and the inf only map is running, then when voting for a new inf only round, it is set back to normal.

 

How can this be resolved PC9? :smile:

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

Originally Posted by PapaCharlie9*:

 

Okay, i had switched normal and inf only, so now it works if a successful vote is done.

 

Only problem i have now, is that after a successful vote is done, and the inf only map is running, then when voting for a new inf only round, it is set back to normal.

 

How can this be resolved PC9? :smile:

I thought that's what you had wanted -- the round is always Normal, unless a vote is taken to make the next round (only!) be Infantry only. Then it is back to Normal.

 

How do you want it to work?

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

Originally Posted by TMiland*:

 

I thought that's what you had wanted -- the round is always Normal, unless a vote is taken to make the next round (only!) be Infantry only. Then it is back to Normal.

 

How do you want it to work?

Maybe i am hopeless to explain, the issue is that when a vote is done when on the successful inf only round, it goes back to Normal.

 

It should continue on inf only, not normal. :tongue:

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

Originally Posted by Ten24-kik0*:

 

Will this work for BF4? :smile:

 

If we run this before round ends: Code:

plugin.ServerCommand("vars.preset", "INFANTRY", "true");
it will be executed on the next round. (It works, just tested)

 

Then next round on Inf Only, do: Code:

plugin.ServerCommand("vars.preset", "NORMAL", "true");
to get it back to Normal.

 

And of course it has to work in conjunction with xvotemap. :smile:

I'm trying to get our server hardcore + infantry only. I tried using the above codes, but still get vehicles. Anyone have any ideas?
* 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.