r2266 - branches/nexuiz-2.0/data/qcsrc/server

DONOTREPLY at icculus.org DONOTREPLY at icculus.org
Wed Mar 28 08:32:54 EDT 2007


Author: div0
Date: 2007-03-28 08:32:53 -0400 (Wed, 28 Mar 2007)
New Revision: 2266

Modified:
   branches/nexuiz-2.0/data/qcsrc/server/cl_client.qc
   branches/nexuiz-2.0/data/qcsrc/server/cl_player.qc
   branches/nexuiz-2.0/data/qcsrc/server/clientcommands.qc
   branches/nexuiz-2.0/data/qcsrc/server/ctf.qc
   branches/nexuiz-2.0/data/qcsrc/server/defs.qh
   branches/nexuiz-2.0/data/qcsrc/server/domination.qc
   branches/nexuiz-2.0/data/qcsrc/server/g_world.qc
   branches/nexuiz-2.0/data/qcsrc/server/havocbot_roles.qc
   branches/nexuiz-2.0/data/qcsrc/server/miscfunctions.qc
   branches/nexuiz-2.0/data/qcsrc/server/teamplay.qc
Log:
map voting; simple majority; vote nagging; FOR_EACH_PLAYER


Modified: branches/nexuiz-2.0/data/qcsrc/server/cl_client.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/cl_client.qc	2007-03-28 10:14:16 UTC (rev 2265)
+++ branches/nexuiz-2.0/data/qcsrc/server/cl_client.qc	2007-03-28 12:32:53 UTC (rev 2266)
@@ -653,6 +653,25 @@
 	Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
 }
 
+void FixClientCvars(entity e)
+{
+	// send prediction settings to the client
+	stuffcmd(e, "\nin_bindmap 0 0\n");
+	stuffcmd(e, strcat("cl_gravity ", ftos(cvar("sv_gravity")), "\n"));
+	stuffcmd(e, strcat("cl_movement_accelerate ", ftos(cvar("sv_accelerate")), "\n"));
+	stuffcmd(e, strcat("cl_movement_friction ", ftos(cvar("sv_friction")), "\n"));
+	stuffcmd(e, strcat("cl_movement_maxspeed ", ftos(cvar("sv_maxspeed")), "\n"));
+	stuffcmd(e, strcat("cl_movement_airaccelerate ", ftos(cvar("sv_airaccelerate")), "\n"));
+	stuffcmd(e, strcat("cl_movement_maxairspeed ", ftos(cvar("sv_maxairspeed")), "\n"));
+	stuffcmd(e, strcat("cl_movement_stopspeed ", ftos(cvar("sv_stopspeed")), "\n"));
+	stuffcmd(e, strcat("cl_movement_jumpvelocity ", ftos(cvar("g_balance_jumpheight")), "\n"));
+	stuffcmd(e, strcat("cl_movement_stepheight ", ftos(cvar("sv_stepheight")), "\n"));
+	stuffcmd(e, strcat("set cl_movement_friction_on_land ", ftos(cvar("sv_friction_on_land")), "\n"));
+	stuffcmd(e, strcat("set cl_movement_airaccel_qw ", ftos(cvar("sv_airaccel_qw")), "\n"));
+	stuffcmd(e, strcat("set cl_movement_airaccel_sideways_friction ", ftos(cvar("sv_airaccel_sideways_friction")), "\n"));
+	stuffcmd(e, strcat("cl_movement_edgefriction 1\n"));
+}
+
 /*
 =============
 ClientConnect
@@ -722,19 +741,8 @@
 	// TODO: is this being used for anything else than cd tracks?
 	// Remember: SVC_CDTRACK exists. Maybe it should be used.
 	
-	// send prediction settings to the client
-	stuffcmd(self, strcat("cl_movement_accelerate ", ftos(cvar("sv_accelerate")), "\n"));
-	stuffcmd(self, strcat("cl_movement_friction ", ftos(cvar("sv_friction")), "\n"));
-	stuffcmd(self, strcat("cl_movement_maxspeed ", ftos(cvar("sv_maxspeed")), "\n"));
-	stuffcmd(self, strcat("cl_movement_airaccelerate ", ftos(cvar("sv_airaccelerate")), "\n"));
-	stuffcmd(self, strcat("cl_movement_maxairspeed ", ftos(cvar("sv_maxairspeed")), "\n"));
-	stuffcmd(self, strcat("cl_movement_stopspeed ", ftos(cvar("sv_stopspeed")), "\n"));
-	stuffcmd(self, strcat("cl_movement_jumpvelocity ", ftos(cvar("g_balance_jumpheight")), "\n"));
-	stuffcmd(self, strcat("cl_movement_stepheight ", ftos(cvar("sv_stepheight")), "\n"));
-	stuffcmd(self, strcat("set cl_movement_friction_on_land ", ftos(cvar("sv_friction_on_land")), "\n"));
-	stuffcmd(self, strcat("set cl_movement_airaccel_qw ", ftos(cvar("sv_airaccel_qw")), "\n"));
-	stuffcmd(self, strcat("set cl_movement_airaccel_sideways_friction ", ftos(cvar("sv_airaccel_sideways_friction")), "\n"));
-	stuffcmd(self, strcat("cl_movement_edgefriction 0\n"));
+	FixClientCvars(self);
+
 	// Wazat's grappling hook
 	SetGrappleHookBindings();
 
@@ -1335,6 +1343,7 @@
 =============
 */
 void() ctf_setstatus;
+.float vote_nagtime;
 void PlayerPreThink (void)
 {
 	// version nagging
@@ -1351,6 +1360,14 @@
 				self.version_nagtime = 0;
 			}
 
+	// vote nagging
+	if(self.cvar_scr_centertime)
+		if(time > self.vote_nagtime)
+		{
+			VoteNag();
+			self.vote_nagtime = time + self.cvar_scr_centertime * 0.6;
+		}
+
 	if(self.classname == "player") {
 		local vector m1, m2;
 
@@ -1545,10 +1562,12 @@
 
 		if (self.flags & FL_JUMPRELEASED) {
 			if (self.button2 && self.version == cvar("gameversion")) {
+				self.welcomemessage_time = 0;
 				self.flags = self.flags - FL_JUMPRELEASED;
 				LeaveSpectatorMode();
 				return;
 			} else if(self.button0 && self.version == cvar("gameversion")) {
+				self.welcomemessage_time = 0;
 				self.flags = self.flags - FL_JUMPRELEASED;
 				if(SpectateNext() == 1) {
 					self.classname = "spectator";
@@ -1563,10 +1582,12 @@
 	} else if(self.classname == "spectator") {
 		if (self.flags & FL_JUMPRELEASED) {
 			if (self.button2 && self.version == cvar("gameversion")) {
+				self.welcomemessage_time = 0;
 				self.flags = self.flags - FL_JUMPRELEASED;
 				LeaveSpectatorMode();
 				return;
 			} else if(self.button0) {
+				self.welcomemessage_time = 0;
 				self.flags = self.flags - FL_JUMPRELEASED;
 				if(SpectateNext() == 1) {
 					self.classname = "spectator";
@@ -1575,6 +1596,7 @@
 					PutClientInServer();
 				}
 			} else if (self.button3) {
+				self.welcomemessage_time = 0;
 				self.flags = self.flags - FL_JUMPRELEASED;
 				self.classname = "observer";
 				PutClientInServer();

Modified: branches/nexuiz-2.0/data/qcsrc/server/cl_player.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/cl_player.qc	2007-03-28 10:14:16 UTC (rev 2265)
+++ branches/nexuiz-2.0/data/qcsrc/server/cl_player.qc	2007-03-28 12:32:53 UTC (rev 2266)
@@ -455,7 +455,7 @@
 		// 2. if we don't have a cursor trace, find the player which is least
 		//    mis-aimed at
 		entity p;
-		for(p = find(world, classname, "player"); p; p = find(p, classname, "player"))
+		FOR_EACH_PLAYER(p)
 		{
 			float c;
 			c = UpdateSelectedPlayer_canSee(p, selected_score, 100); // 100 = 2.5 meters

Modified: branches/nexuiz-2.0/data/qcsrc/server/clientcommands.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/clientcommands.qc	2007-03-28 10:14:16 UTC (rev 2265)
+++ branches/nexuiz-2.0/data/qcsrc/server/clientcommands.qc	2007-03-28 12:32:53 UTC (rev 2266)
@@ -1,6 +1,7 @@
 void ReadyCount();
 float ValidateMap(string vote);
 void(entity e) DropFlag;
+string MapVote_Suggest(string m);
 
 void Say(entity source, float teamsay, string msgin)
 {
@@ -17,6 +18,9 @@
 	if(!teams_matter)
 		teamsay = FALSE;
 
+	if(intermission_running)
+		teamsay = FALSE;
+
 	if(source.classname != "player") // observers can't
 		teamsay = FALSE;
 
@@ -36,44 +40,22 @@
 	else
 		msgstr = strzone(strcat("\{1}^3", source.netname, "^7: ", msgin, "\n"));
 
-	head = find(world, classname, "player");
-	while(head)
+	if(teamsay)
 	{
-		if(clienttype(head) == CLIENTTYPE_REAL)
-			if(!teamsay || (head.team == source.team))
-			{
-				sprint(head, msgstr);
-				if(teamsay)
-					centerprint(head, cmsgstr);
-				//stuffcmd(head, "play2 misc/talk.wav\n");
-			}
-		head = find(head, classname, "player");
-	}
-
-	if(!teamsay)
-	{
-		head = find(world, classname, "observer");
-		while(head)
+		FOR_EACH_REALPLAYER(head)
 		{
-			if(clienttype(head) == CLIENTTYPE_REAL)
+			if(head.team == source.team)
 			{
 				sprint(head, msgstr);
-				//stuffcmd(head, "play2 misc/talk.wav\n");
+				centerprint(head, cmsgstr);
 			}
-			head = find(head, classname, "observer");
 		}
-		head = find(world, classname, "spectator");
-		while(head)
-		{
-			if(clienttype(head) == CLIENTTYPE_REAL)
-			{
-				sprint(head, msgstr);
-				//stuffcmd(head, "play2 misc/talk.wav\n");
-			}
-			head = find(head, classname, "spectator");
-		}
-		ServerConsoleEcho(substring(msgstr, 1, strlen(msgstr) - 2), TRUE);
 	}
+	else
+	{
+		bprint(msgstr);
+		//ServerConsoleEcho(substring(msgstr, 1, strlen(msgstr) - 2), TRUE);
+	}
 
 	strunzone(msgstr);
 }
@@ -330,6 +312,8 @@
 			sprint(self, strcat(col, argv(i), " "));
 		}
 		sprint(self, "\n");
+	} else if(argv(0) == "teamstatus") {
+		PrintScoreboard(self);
 	} else if(argv(0) == "say") {
 		Say(self, FALSE, substring(s, 4, strlen(s) - 4));
 		//clientcommand(self, formatmessage(s));
@@ -342,6 +326,8 @@
 			sprint(self, "ERROR: unsupported info command\n");
 		else
 			wordwrap_sprint(cmd, 1111);
+	} else if(argv(0) == "suggestmap") {
+		sprint(self, strcat(MapVote_Suggest(argv(1)), "\n"));
 	} else {
 		cmd = argv(0);
 		/* checks not needed any more since DP has separated clientcommands and regular commands
@@ -453,26 +439,12 @@
 }
 
 void VoteReset() {
-	local string searchclass;
-	searchclass = "player";
+	local entity player;
 
-	while (TRUE)
+	FOR_EACH_CLIENT(player)
 	{
-		local entity player;
-		player = find(player, classname, searchclass);
-		while(player)
-		{
-			player.vote_vote = 0;
-			player = find(player, classname, searchclass);
-		}
-
-		if("player" == searchclass) {
-			searchclass = "observer";
-		} else if("observer" == searchclass) {
-			searchclass = "spectator";
-		} else {
-			break;
-		}
+		player.vote_vote = 0;
+		centerprint_expire(player, CENTERPRIO_VOTE);
 	}
 
 	votecalled = FALSE;
@@ -511,6 +483,12 @@
 	VoteReset();
 }
 
+void VoteNag() {
+	if(votecalled)
+		if(self.vote_vote == 0)
+			centerprint_atprio(self, CENTERPRIO_VOTE, strcat("^7^3", votecaller.netname, "^2 called a vote for:\n^1", votecalledvote, "\n\n^2You have not voted yet!\n^2HINT: By default, F1 is yes and F2 is no."));
+}
+
 void VoteCount() {
 	local float playercount;
 	playercount = 0;
@@ -518,34 +496,16 @@
 	yescount = 0;
 	local float nocount;
 	nocount = 0;
-	local string searchclass;
-	searchclass = "player";
+	local entity player;
 
-	while (TRUE)
+	FOR_EACH_REALCLIENT(player)
 	{
-		local entity player;
-		player = find(player, classname, searchclass);
-
-		while(player)
-		{
-			if(clienttype(player) != CLIENTTYPE_BOT) {
-				if(player.vote_vote < 0) {
-					nocount++;
-				} else if(player.vote_vote > 0) {
-					yescount++;
-				}
-				playercount++;
-			}
-			player = find(player, classname, searchclass);
+		if(player.vote_vote < 0) {
+			nocount++;
+		} else if(player.vote_vote > 0) {
+			yescount++;
 		}
-
-		if("player" == searchclass) {
-			searchclass = "observer";
-		} else if("observer" == searchclass) {
-			searchclass = "specator";
-		} else {
-			break;
-		}
+		playercount++;
 	}
 
 	if((playercount == 1) && votecalledmaster) {
@@ -563,7 +523,15 @@
 	} else if((playercount / 2) < nocount) { // vote rejected
 		VoteReject();
 	} else if(time > votefinished) { // vote timedout
-		VoteTimeout();
+		if(cvar("sv_vote_simple_majority"))
+			if(yescount > nocount)
+				VoteAccept();
+			else if(nocount > yescount)
+				VoteReject();
+			else
+				VoteTimeout();
+		else
+			VoteTimeout();
 	} // else still running
 }
 
@@ -573,16 +541,11 @@
 	local entity e;
 	local float r, p;
 
-	e = find(world, classname, "player");
-
-	while(e)
+	FOR_EACH_REALPLAYER(e)
 	{
-		if(clienttype(e) == CLIENTTYPE_REAL)
-		{
-			p += 1;
-			if(e.ready) r += 1;
-		}
-		e = find(e, classname, "player");
+		p += 1;
+		if(e.ready)
+			r += 1;
 	}
 
 	if(p && r == p)

Modified: branches/nexuiz-2.0/data/qcsrc/server/ctf.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/ctf.qc	2007-03-28 10:14:16 UTC (rev 2265)
+++ branches/nexuiz-2.0/data/qcsrc/server/ctf.qc	2007-03-28 12:32:53 UTC (rev 2266)
@@ -213,15 +213,13 @@
 		LogCTF("capture", other.flagcarried.team, other);
 		// give credit to the individual player
 		UpdateFrags(other, cvar("g_ctf_flagscore_capture"));
+
 		// give credit to all players of the team (rewards large teams)
 		// NOTE: this defaults to 0
-		head = find(head, classname, "player");
-		while (head)
-		{
+		FOR_EACH_PLAYER(head)
 			if (head.team == self.team)
 				UpdateFrags(head, cvar("g_ctf_flagscore_capture_team"));
-			head = find(head, classname, "player");
-		}
+
 		sound (self, CHAN_AUTO, self.noise2, 1, ATTN_NONE);
 		RegenFlag (other.flagcarried);
 		other.flagcarried = world;
@@ -246,11 +244,9 @@
 		LogCTF("steal", self.team, other);
 		sound (self, CHAN_AUTO, self.noise, 1, ATTN_NONE);
 
-		player = find(world, classname, "player");
-		while(player) {
-			if(player.team == self.team) centerprint(player, "The enemy got your flag! Retrieve it!");
-			player = find(player, classname, "player");
-		}
+		FOR_EACH_PLAYER(player)
+			if(player.team == self.team)
+				centerprint(player, "The enemy got your flag! Retrieve it!");
 
 		self.movetype = MOVETYPE_NONE;
 		setorigin(self, FLAG_CARRY_POS);
@@ -287,11 +283,10 @@
 			LogCTF("pickup", self.team, other);
 			sound (self, CHAN_AUTO, self.noise, 1, ATTN_NONE);
 
-			player = find(world, classname, "player");
-			while(player) {
-				if(player.team == self.team) centerprint(player, "The enemy got your flag! Retrieve it!");
-				player = find(player, classname, "player");
-			}
+			FOR_EACH_PLAYER(player)
+				if(player.team == self.team)
+					centerprint(player, "The enemy got your flag! Retrieve it!");
+
 			self.movetype = MOVETYPE_NONE;	// flag must have MOVETYPE_NONE here, otherwise it will drop through the floor...
 			setorigin(self, FLAG_CARRY_POS);
 			setattachment(self, other, "");

Modified: branches/nexuiz-2.0/data/qcsrc/server/defs.qh
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/defs.qh	2007-03-28 10:14:16 UTC (rev 2265)
+++ branches/nexuiz-2.0/data/qcsrc/server/defs.qh	2007-03-28 12:32:53 UTC (rev 2266)
@@ -301,6 +301,9 @@
 .float selected_player_display_needs_update; // are regular updates necessary? (health)
 .float selected_player_display_timeout; // when the selection will time out
 
+void FixIntermissionClient(entity e);
+void FixClientCvars(entity e);
+
 void centerprint_atprio(entity e, float prio, string s);
 void centerprint_expire(entity e, float prio);
 void centerprint(entity e, string s);

Modified: branches/nexuiz-2.0/data/qcsrc/server/domination.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/domination.qc	2007-03-28 10:14:16 UTC (rev 2265)
+++ branches/nexuiz-2.0/data/qcsrc/server/domination.qc	2007-03-28 12:32:53 UTC (rev 2266)
@@ -125,26 +125,18 @@
 	{
 		teamfragamt = cvar("g_domination_point_teamamt");
 
-		head = find(head, classname, "player");
-		while (head)
-		{
+		FOR_EACH_PLAYER(head)
 			if (head.team == self.goalentity.team)
 				UpdateFrags(head, teamfragamt);
-			head = find(head, classname, "player");
-		}
 	}
 
 	// if the player left the game, changed teams or became spectator, we have to find another player on the same team to give credit to
 	if (!self.enemy.flags || self.enemy.team != self.goalentity.team || self.enemy.killcount == -666) // flags is zero on removed clients
 	{
 		other = self.enemy;
-		head = find(head, classname, "player");
-		while (head)
-		{
+		FOR_EACH_PLAYER(head)
 			if (head.team == self.goalentity.team)
 				self.enemy = head;
-			head = find(head, classname, "player");
-		}
 		if(self.enemy == other) // search returned no matching player, reset dom point
 		{
 			dom_controlpoint_setup();

Modified: branches/nexuiz-2.0/data/qcsrc/server/g_world.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/g_world.qc	2007-03-28 10:14:16 UTC (rev 2265)
+++ branches/nexuiz-2.0/data/qcsrc/server/g_world.qc	2007-03-28 12:32:53 UTC (rev 2266)
@@ -3,6 +3,7 @@
 string GetMapname();
 void GotoNextMap();
 void HandleMaplistShuffleCommands();
+float() DoNextMapOverride;
 
 void SetDefaultAlpha()
 {
@@ -61,7 +62,8 @@
 		if(argv(0) != GetMapname())
 		{
 			cvar_set("nextmap", argv(0));
-			GotoNextMap();
+			if(!DoNextMapOverride())
+				GotoNextMap();
 		}
 	}
 }
@@ -340,6 +342,9 @@
 
 	registercvar("_g_maplist_shufflenow", "0");
 	registercvar("_g_maplist_have_shuffled", "0");
+	registercvar("_g_maplist_add", "");
+	registercvar("_g_maplist_remove", "");
+	registercvar("_g_maplist_putfirst", "");
 
 	player_count = 0;
 	lms_lowest_lives = 0;
@@ -449,9 +454,12 @@
 	return "dm";
 }
 
+string getmapname_stored;
 string GetMapname()
 {
-	return strcat(GetGametype(), "_", mapname);
+	if(getmapname_stored == "")
+		getmapname_stored = strzone(strcat(GetGametype(), "_", mapname));
+	return getmapname_stored;
 }
 
 float Map_Count, Map_Current;
@@ -511,6 +519,7 @@
 
 string Map_Filename(float position)
 {
+	// FIXME unused
 	return strcat("maps/", argv(position), ".mapcfg");
 }
 
@@ -537,12 +546,39 @@
 	return 0;
 }
 
-void(float position) Map_Goto =
+void(string nextmapname) Map_Goto_SetStr =
 {
+	if(getmapname_stored != "")
+		strunzone(getmapname_stored);
+	if(nextmapname == "")
+		getmapname_stored = "";
+	else
+		getmapname_stored = strzone(nextmapname);
+}
+
+void(float position) Map_Goto_SetFloat =
+{
 	cvar_set("g_maplist_index", ftos(position));
-	localcmd(strcat("exec \"", Map_Filename(position) ,"\"\n"));
+	Map_Goto_SetStr(argv(position));
 }
 
+void() GameResetCfg =
+{
+	// if an exit cfg is defined by exiting map, exec it.
+	string exit_cfg;
+	exit_cfg = cvar_string("exit_cfg");
+	if(exit_cfg != "")
+		localcmd(strcat("exec \"", exit_cfg, "\"\n"));
+
+	localcmd("exec game_reset.cfg\n");
+};
+
+void() Map_Goto =
+{
+	GameResetCfg();
+	localcmd(strcat("exec \"maps/", getmapname_stored ,".mapcfg\"\n"));
+}
+
 // return codes of map selectors:
 //   -1 = temporary failure (that is, try some method that is guaranteed to succeed)
 //   -2 = permanent failure
@@ -645,31 +681,51 @@
 	// isn't chosen in the first pass that should have been
 }
 
-void() GotoNextMap =
+string() GetNextMap =
 {
-	//local string nextmap;
-	//local float n, nummaps;
-	//local string s;
-	string exit_cfg;
-	if (alreadychangedlevel)
-		return;
-	alreadychangedlevel = TRUE;
+	float nextMap;
 
+	Maplist_Init();
+	nextMap = -1;
+
+	if(nextMap == -1)
+		if(cvar("g_maplist_shuffle") > 0)
+			nextMap = MaplistMethod_Shuffle(cvar("g_maplist_shuffle") + 1);
+
+	if(nextMap == -1)
+		if(cvar("g_maplist_selectrandom"))
+			nextMap = MaplistMethod_Random();
+
+	if(nextMap == -1)
+		nextMap = MaplistMethod_Iterate();
+
+	if(nextMap == -1)
+		nextMap = MaplistMethod_Repeat();
+
+	if(nextMap >= 0)
+	{
+		Map_Goto_SetFloat(nextMap);
+		return getmapname_stored;
+	}
+
+	return "";
+};
+
+float() DoNextMapOverride =
+{
 	if(cvar("g_campaign"))
 	{
 		CampaignPostIntermission();
-		return;
+		return TRUE;
 	}
-
 	if(cvar("quit_when_empty"))
 	{
 		if(player_count <= currentbots)
 		{
 			localcmd("quit\n");
-			return;
+			return TRUE;
 		}
 	}
-
 	if (cvar("samelevel")) // if samelevel is set, stay on same level
 	{
 		// this does not work because it tries to exec maps/nexdm01.mapcfg (which doesn't exist, it should be trying maps/dm_nexdm01.mapcfg for example)
@@ -677,72 +733,55 @@
 		// so instead just restart the current map using the restart command (DOES NOT WORK PROPERLY WITH exit_cfg STUFF)
 		localcmd("restart\n");
 		//changelevel (mapname);
-		return;
+		return TRUE;
 	}
-
-	// if an exit cfg is defined by exiting map, exec it.
-	exit_cfg = cvar_string("exit_cfg");
-	if(exit_cfg != "")
-		localcmd(strcat("exec \"", exit_cfg, "\"\n"));
-
-	localcmd("exec game_reset.cfg\n");
-
-
-	if (cvar("lastlevel"))
+	if(cvar_string("nextmap") != "")
+		if(TryFile(strcat("maps/", cvar_string("nextmap"), ".mapcfg")))
+		{
+			Map_Goto_SetStr(cvar_string("nextmap"));
+			Map_Goto();
+			return TRUE;
+		}
+	if(cvar("lastlevel"))
 	{
+		GameResetCfg();
 		localcmd(strcat("set lastlevel 0\n"));
 		localcmd(strcat("togglemenu\n"));
+		return TRUE;
 	}
-	else
+	return FALSE;
+};
+
+void() GotoNextMap =
+{
+	//local string nextmap;
+	//local float n, nummaps;
+	//local string s;
+	if (alreadychangedlevel)
+		return;
+	alreadychangedlevel = TRUE;
+
 	{
-		float nextMap;
+		string nextMap;
 		float allowReset;
 
-		// cvar "nextmap" always gets priority
-		if(cvar_string("nextmap") != "")
-		if(TryFile(strcat("maps/", cvar_string("nextmap"), ".mapcfg")))
-		{
-			localcmd(strcat("exec \"maps/", cvar_string("nextmap"), ".mapcfg\"\n"));
-			return;
-		}
-
 		for(allowReset = 1; allowReset >= 0; --allowReset)
 		{
-			Maplist_Init();
-			nextMap = -1;
+			nextMap = GetNextMap();
+			if(nextMap != "")
+				break;
 
-			if(nextMap == -1)
-				if(cvar("g_maplist_shuffle") > 0)
-					nextMap = MaplistMethod_Shuffle(cvar("g_maplist_shuffle") + 1);
-
-			if(nextMap == -1)
-				if(cvar("g_maplist_selectrandom"))
-					nextMap = MaplistMethod_Random();
-
-			if(nextMap == -1)
-				nextMap = MaplistMethod_Iterate();
-
-			if(nextMap == -1)
-				nextMap = MaplistMethod_Repeat();
-
-			if(nextMap >= 0)
+			if(allowReset)
 			{
-				Map_Goto(nextMap);
-				break;
+				bprint( "Maplist contains no single playable map!  Resetting it to default map list.\n" );
+				cvar_set("g_maplist", cvar_string("g_maplist_defaultlist"));
 			}
-			else // PERMANENT FAILURE
+			else
 			{
-				if(allowReset)
-				{
-					bprint( "Maplist contains no single playable map!  Resetting it to default map list.\n" );
-					cvar_set("g_maplist", cvar_string("g_maplist_defaultlist"));
-				}
-				else
-				{
-					error("Everything is broken - not even the default map list works. Please report this to the developers.");
-				}
+				error("Everything is broken - not even the default map list works. Please report this to the developers.");
 			}
 		}
+		Map_Goto();
 	}
 };
 
@@ -755,8 +794,12 @@
 ============
 */
 .float autoscreenshot;
+void() MapVote_Think;
+float mapvote_initialized;
 void() IntermissionThink =
 {
+	FixIntermissionClient(self);
+
 	if(cvar("sv_autoscreenshot"))
 	if(self.autoscreenshot)
 	if(time > self.autoscreenshot)
@@ -770,10 +813,12 @@
 	if (time < intermission_exittime)
 		return;
 
-	if (time < intermission_exittime + 10 && !self.button0 && !self.button2 && !self.button3 && !self.button6 && !self.buttonuse)
-		return;
+	if(!mapvote_initialized)
+		if (time < intermission_exittime + 10 && !self.button0 && !self.button2 && !self.button3 && !self.button6 && !self.buttonuse)
+			return;
 
-	GotoNextMap ();
+	if(intermission_exittime >= 0)
+		MapVote_Think();
 };
 
 /*
@@ -859,8 +904,7 @@
 		fputs(file, strcat(s, "\n"));
 	}
 
-	other = findchainflags(flags, FL_CLIENT);
-	while (other)
+	FOR_EACH_CLIENT(other)
 	{
 		if ((clienttype(other) == CLIENTTYPE_REAL) || (clienttype(other) == CLIENTTYPE_BOT && cvar("sv_logscores_bots")))
 		{
@@ -876,7 +920,6 @@
 			else if(cvar("sv_logscores_console"))
 				ServerConsoleEcho(strcat(s, other.netname), TRUE);
 		}
-		other = other.chain;
 	}
 
 	if(cvar("sv_eventlog") && gameover)
@@ -890,7 +933,35 @@
 	}
 }
 
+void FixIntermissionClient(entity e)
+{
+	if(!e.autoscreenshot) // initial call
+	{
+		e.angles = e.v_angle;
+		e.angles_x = -e.angles_x;
+		e.autoscreenshot = time + 0.8;	// used for autoscreenshot
+		e.health = -2342;
+		// first intermission phase; voting phase has positive health (used to decide whether to send SVC_FINALE or not)
+		e.solid = SOLID_NOT;
+		e.movetype = MOVETYPE_NONE;
+		e.takedamage = DAMAGE_NO;
+		if(e.weaponentity)
+			e.weaponentity.effects = EF_NODRAW;
+		stuffcmd(e, "\nscr_printspeed 1000000\n");
+		if(clienttype(e) == CLIENTTYPE_REAL)
+		{
+			msg_entity = e;
+			WriteByte(MSG_ONE, SVC_INTERMISSION);
+		}
+	}
 
+	//e.velocity = '0 0 0';
+	//e.fixangle = TRUE;
+
+	// TODO halt weapon animation
+}
+
+
 /*
 go to the next level for deathmatch
 only called if a time or frag limit has expired
@@ -928,7 +999,7 @@
 	GameLogClose();
 
 	maxTotalFrags = 0;
-	for(other = world; (other = findflags(other, flags, FL_CLIENT)); )
+	FOR_EACH_CLIENT(other)
 	{
 		if(maxTotalFrags < other.totalfrags)
 			maxTotalFrags = other.totalfrags;
@@ -936,15 +1007,9 @@
 			minTotalFrags = other.totalfrags;
 	}
 
-	for(other = world; (other = findflags(other, flags, FL_CLIENT)); )
+	FOR_EACH_CLIENT(other)
 	{
-		//other.nextthink = time + 0.5;
-		other.takedamage = DAMAGE_NO;
-		other.solid = SOLID_NOT;
-		other.movetype = MOVETYPE_NONE;
-		other.angles = other.v_angle;
-		other.angles_x = other.angles_x * -1;
-		other.autoscreenshot = time + 0.8;	// used for autoscreenshot
+		FixIntermissionClient(other);
 
 		self = other;
 
@@ -962,7 +1027,7 @@
 	if(cvar("g_campaign"))
 		CampaignPreIntermission();
 
-	WriteByte (MSG_ALL, SVC_INTERMISSION);
+	// WriteByte (MSG_ALL, SVC_INTERMISSION);
 };
 
 /*
@@ -1021,37 +1086,25 @@
 void(.float field, float value) SetWinners =
 {
 	entity head;
-	head = findchain(classname, "player");
-	while (head)
-	{
+	FOR_EACH_PLAYER(head)
 		head.winning = (head.field == value);
-		head = head.chain;
-	}
 }
 
 // set the .winning flag for those players with a given field value
 void(.float field, float value) AddWinners =
 {
 	entity head;
-	head = findchain(classname, "player");
-	while (head)
-	{
+	FOR_EACH_PLAYER(head)
 		if(head.field == value)
 			head.winning = 1;
-		head = head.chain;
-	}
 }
 
 // clear the .winning flags
 void(void) ClearWinners =
 {
 	entity head;
-	head = findchain(classname, "player");
-	while (head)
-	{
+	FOR_EACH_PLAYER(head)
 		head.winning = 0;
-		head = head.chain;
-	}
 }
 
 float() LMS_NewPlayerLives =
@@ -1137,9 +1190,8 @@
 	// check if the top two players have equal score.
 
 	checkrules_leaderfrags = 0;
-	head = findchain(classname, "player");
 	checkrules_equality = FALSE;
-	while (head)
+	FOR_EACH_PLAYER(head)
 	{
 		if(head.frags > checkrules_leaderfrags)
 		{
@@ -1148,7 +1200,6 @@
 		}
 		else if(head.frags > 0 && head.frags == checkrules_leaderfrags)
 			checkrules_equality = TRUE;
-		head = head.chain;
 	}
 
 	SetWinners(frags, checkrules_leaderfrags);
@@ -1173,9 +1224,8 @@
 
 	checkrules_oldleaderfrags = checkrules_leaderfrags;
 	checkrules_leaderfrags = 0;
-	head = findchain(classname, "player");
 	checkrules_equality = FALSE;
-	while (head)
+	FOR_EACH_PLAYER(head)
 	{
 		if(head.frags > checkrules_leaderfrags)
 		{
@@ -1184,7 +1234,6 @@
 		}
 		else if(head.frags > 0 && head.frags == checkrules_leaderfrags)
 			checkrules_equality = TRUE;
-		head = head.chain;
 	}
 
 	if(checkrules_leaderfrags > 0)
@@ -1258,8 +1307,7 @@
 
 	team1_score = team2_score = team3_score = team4_score = 0;
 
-	head = findchain(classname, "player");
-	while (head)
+	FOR_EACH_PLAYER(head)
 	{
 		if(head.team == COLOR_TEAM1)
 			team1_score += head.frags;
@@ -1269,7 +1317,6 @@
 			team3_score += head.frags;
 		else if(head.team == COLOR_TEAM4)
 			team4_score += head.frags;
-		head = head.chain;
 	}
 
 	return WinningConditionBase_Teamplay(fraglimit);
@@ -1284,8 +1331,7 @@
 
 	team1_score = team2_score = team3_score = team4_score = 0;
 
-	head = findchain(classname, "player");
-	while (head)
+	FOR_EACH_PLAYER(head)
 	{
 		if(head.team == COLOR_TEAM1)
 		{
@@ -1307,14 +1353,21 @@
 			if(head.frags > team4_score)
 				team4_score = head.frags;
 		}
-		head = head.chain;
 	}
 
 	return WinningConditionBase_Teamplay(fraglimit);
 }
 
-void PrintScoreboardFor(string name, string colorcode, float whichteam)
+void print_to(entity e, string s)
 {
+	if(e)
+		sprint(e, strcat(s, "\n"));
+	else
+		ServerConsoleEcho(s, TRUE);
+}
+
+void PrintScoreboardFor(entity e, string name, string colorcode, float whichteam)
+{
 	entity head;
 	float v;
 	float teamvalue;
@@ -1322,15 +1375,14 @@
 	string s;
 	float found;
 	found = FALSE;
-	head = find(world, classname, "player");
 	teamvalue = 0;
-	while(head)
+	FOR_EACH_PLAYER(head)
 	{
 		if(!whichteam || head.team == whichteam)
 		{
 			if(name != "")
 				if(!found)
-					ServerConsoleEcho(strcat(" ", colorcode, name, ":"), FALSE);
+					print_to(e, strcat(" ", colorcode, name, ":"));
 			found = TRUE;
 			fragtotal = fragtotal + head.frags;
 			s = ftos(head.frags);
@@ -1343,33 +1395,35 @@
 			v = PlayerValue(head);
 			teamvalue += v;
 			s = strcat(s, " / ", ftos(v));
-			ServerConsoleEcho(strcat("  ", colorcode, head.netname, colorcode, " (", s, ")"), TRUE);
+			print_to(e, strcat("  ", colorcode, head.netname, colorcode, " (", s, ")"));
 		}
-		head = find(head, classname, "player");
 	}
 	if(whichteam && found)
 	{
 		s = ftos(fragtotal);
 		s = strcat(s, " / ", ftos(teamvalue));
-		ServerConsoleEcho(strcat(colorcode, "  (total: ", s, ")"), FALSE);
+		print_to(e, strcat(colorcode, "  (total: ", s, ")"));
 	}
 }
 
-void PrintScoreboard()
+void PrintScoreboard(entity e)
 {
-	ServerConsoleEcho("Scoreboard:", FALSE);
+	print_to(e, strcat("Time:      ", ftos(time / 60)));
+	print_to(e, strcat("Timelimit: ", ftos(cvar("timelimit"))));
+	print_to(e, strcat("Fraglimit: ", ftos(cvar("fraglimit"))));
+	print_to(e, "Scoreboard:");
 	if(teams_matter)
 	{
-		PrintScoreboardFor("Red", "^1", COLOR_TEAM1);
-		PrintScoreboardFor("Blue", "^4", COLOR_TEAM2);
-		PrintScoreboardFor("Pink", "^6", COLOR_TEAM3);
-		PrintScoreboardFor("Yellow", "^3", COLOR_TEAM4);
+		PrintScoreboardFor(e, "Red", "^1", COLOR_TEAM1);
+		PrintScoreboardFor(e, "Blue", "^4", COLOR_TEAM2);
+		PrintScoreboardFor(e, "Pink", "^6", COLOR_TEAM3);
+		PrintScoreboardFor(e, "Yellow", "^3", COLOR_TEAM4);
 	}
 	else
 	{
-		PrintScoreboardFor("", "^7", 0);
+		PrintScoreboardFor(e, "", "^7", 0);
 	}
-	ServerConsoleEcho(".", FALSE);
+	print_to(e, ".");
 }
 
 void RemoveFromMaplist(string m)
@@ -1441,6 +1495,22 @@
 		ServerConsoleEcho("Map already in list.", FALSE);
 }
 
+void MakeFirstInMaplist(string m)
+{
+	if(!TryFile(strcat("maps/", m, ".mapcfg")))
+	{
+		ServerConsoleEcho("Map not found.", FALSE);
+		return;
+	}
+
+	m = strzone(m);
+	RemoveFromMaplist(m);
+	cvar_set("g_maplist", strcat("'", m, "'", cvar_string("g_maplist")));
+	strunzone(m);
+
+	ServerConsoleEcho("Map added as first one.", FALSE);
+}
+
 void ShuffleMaplist()
 {
 	string result;
@@ -1488,6 +1558,11 @@
 		RemoveFromMaplist(cvar_string("_g_maplist_remove"));
 		cvar_set("_g_maplist_remove", "");
 	}
+	if(cvar_string("_g_maplist_putfirst") != "")
+	{
+		MakeFirstInMaplist(cvar_string("_g_maplist_putfirst"));
+		cvar_set("_g_maplist_putfirst", "");
+	}
 	if(cvar("_g_maplist_shufflenow") || (cvar("g_maplist_shuffle") && !cvar("_g_maplist_have_shuffled")))
 	{
 		ShuffleMaplist();
@@ -1520,7 +1595,8 @@
 	if (intermission_running)
 		if (time >= intermission_exittime + 60)
 		{
-			GotoNextMap();
+			if(!DoNextMapOverride())
+				GotoNextMap();
 			return;
 		}
 
@@ -1532,7 +1608,7 @@
 	if(cvar("_scoreboard"))
 	{
 		cvar_set("_scoreboard", "0");
-		PrintScoreboard();
+		PrintScoreboard(world);
 	}
 
 	HandleMaplistShuffleCommands();
@@ -1540,8 +1616,20 @@
 	timelimit = cvar("timelimit") * 60;
 	fraglimit = cvar("fraglimit");
 
-	if (timelimit && time >= timelimit)
-		InitiateOvertime();
+	if(checkrules_overtimeend)
+	{
+		if(!checkrules_overtimewarning)
+		{
+			checkrules_overtimewarning = TRUE;
+			//sound(world, CHAN_AUTO, "announcer/robotic/1minuteremains.ogg", 1, ATTN_NONE);
+			bcenterprint("^3Now playing ^1OVERTIME^3!\n\n^3Keep fragging until we have a ^1winner^3!");
+		}
+	}
+	else
+	{
+		if (timelimit && time >= timelimit)
+			InitiateOvertime();
+	}
 
 	if (checkrules_overtimeend && time >= checkrules_overtimeend)
 	{
@@ -1549,13 +1637,6 @@
 		return;
 	}
 
-	if(!checkrules_overtimewarning && checkrules_overtimeend)
-	{
-		checkrules_overtimewarning = TRUE;
-		//sound(world, CHAN_AUTO, "announcer/robotic/1minuteremains.ogg", 1, ATTN_NONE);
-		bcenterprint("^3Now playing ^1OVERTIME^3!\n\n^3Keep fragging until we have a ^1winner^3!");
-	}
-
 	if (!checkrules_oneminutewarning && timelimit > 0 && time > timelimit - 60)
 	{
 		checkrules_oneminutewarning = TRUE;
@@ -1596,9 +1677,351 @@
 		ClearWinners();
 
 	if(checkrules_overtimeend)
-		if(status != WINNING_NEVER)
+		if(status != WINNING_NEVER || time >= checkrules_overtimeend)
 			status = WINNING_YES;
 
 	if(status == WINNING_YES)
 		NextLevel();
 };
+
+float randsel_value;
+float randsel_priority;
+float randsel_count;
+void RandSel_Init()
+{
+	randsel_value = -1;
+	randsel_priority = -1;
+	randsel_count = -1;
+}
+void RandSel_Add(float priority, float value)
+{
+	if(priority > randsel_priority)
+	{
+		randsel_priority = priority;
+		randsel_value = value;
+		randsel_count = 1;
+	}
+	else if(priority == randsel_priority)
+	{
+		randsel_count += 1;
+		if(ceil(random() * randsel_count) == 1)
+			randsel_value = value;
+	}
+}
+
+float mapvote_nextthink;
+float mapvote_initialized;
+float mapvote_keeptwotime;
+float mapvote_timeout;
+string mapvote_message;
+
+#define MAPVOTE_COUNT 10
+float mapvote_count;
+string mapvote_maps[MAPVOTE_COUNT];
+float mapvote_maps_suggested[MAPVOTE_COUNT];
+string mapvote_suggestions[MAPVOTE_COUNT];
+float mapvote_suggestion_ptr;
+string mapvote_fillstr;
+float mapvote_maxlen;
+float mapvote_voters;
+float mapvote_votes[MAPVOTE_COUNT];
+.float mapvote;
+
+void MapVote_ClearAllVotes()
+{
+	FOR_EACH_CLIENT(other)
+		other.mapvote = 0;
+}
+
+string MapVote_Suggest(string m)
+{
+	float i;
+	if(m == "")
+		return "That's not how to use this command.";
+	if(!cvar("g_maplist_votable_suggestions"))
+		return "Suggestions are not accepted on this server.";
+	if(mapvote_initialized)
+		return "Can't suggest - voting is already in progress!";
+	if(!TryFile(strcat("maps/", m, ".mapcfg")))
+		return "The map you suggested is not available on this server.";
+	for(i = 0; i < mapvote_suggestion_ptr; ++i)
+		if(mapvote_suggestions[i] == m)
+			return "This map was already suggested.";
+	if(mapvote_suggestion_ptr >= MAPVOTE_COUNT)
+	{
+		i = ceil(random() * mapvote_suggestion_ptr) - 1;
+	}
+	else
+	{
+		i = mapvote_suggestion_ptr;
+		mapvote_suggestion_ptr += 1;
+	}
+	if(mapvote_suggestions[i] != "")
+		strunzone(mapvote_suggestions[i]);
+	mapvote_suggestions[i] = strzone(m);
+	GameLogEcho(strcat(":vote:suggested:", m, ":", ftos(self.playerid), ":", self.netname), TRUE);
+	return "Suggestion accepted.";
+}
+
+void MapVote_AddVotable(string nextMap, float isSuggestion)
+{
+	float j;
+	if(nextMap == "")
+		return;
+	for(j = 0; j < mapvote_count; ++j)
+		if(mapvote_maps[j] == nextMap)
+			return;
+	if(strlen(nextMap) > mapvote_maxlen)
+		mapvote_maxlen = strlen(nextMap);
+	mapvote_maps[mapvote_count] = strzone(nextMap);
+	mapvote_maps_suggested[mapvote_count] = isSuggestion;
+	mapvote_count += 1;
+}
+
+void MapVote_Init()
+{
+	float i;
+	float nmax, smax;
+
+	MapVote_ClearAllVotes();
+
+	nmax = min(MAPVOTE_COUNT, cvar("g_maplist_votable"));
+	smax = min(nmax, cvar("g_maplist_votable_suggestions"));
+	mapvote_count = 0;
+
+	for(i = 0; i < 100 && mapvote_count < smax; ++i)
+		MapVote_AddVotable(mapvote_suggestions[ceil(random() * mapvote_suggestion_ptr) - 1], TRUE);
+
+	for(i = 0; i < 100 && mapvote_count < nmax; ++i)
+		MapVote_AddVotable(GetNextMap(), FALSE);
+
+	if(mapvote_count == 0)
+	{
+		bprint( "Maplist contains no single playable map!  Resetting it to default map list.\n" );
+		cvar_set("g_maplist", cvar_string("g_maplist_defaultlist"));
+		for(i = 0; i < 100 && mapvote_count < nmax; ++i)
+			MapVote_AddVotable(GetNextMap(), FALSE);
+	}
+
+	//dprint("mapvote count is ", ftos(mapvote_count), "\n");
+
+	mapvote_fillstr = " ";
+	while(strlen(mapvote_fillstr) < mapvote_maxlen + 16)
+		mapvote_fillstr = strcat(mapvote_fillstr, mapvote_fillstr);
+	mapvote_fillstr = strzone(mapvote_fillstr);
+
+	mapvote_keeptwotime = time + cvar("g_maplist_votable_keeptwotime");
+	mapvote_timeout = time + cvar("g_maplist_votable_timeout");
+	if(mapvote_count < 3 || mapvote_keeptwotime <= time)
+		mapvote_keeptwotime = 0;
+	mapvote_message = "Choose a map and press its key!";
+}
+float MapVote_Finished(float mappos)
+{
+	string result;
+	float i;
+
+	result = strcat(":vote:finished:", mapvote_maps[mappos]);
+	result = strcat(result, ":", ftos(mapvote_votes[mappos]), "::");
+	for(i = 0; i < mapvote_count; ++i)
+		if(i != mappos)
+			if(mapvote_maps[i] != "")
+			{
+				result = strcat(result, ":", mapvote_maps[i]);
+				result = strcat(result, ":", ftos(mapvote_votes[i]));
+			}
+	GameLogEcho(result, FALSE);
+	if(mapvote_maps_suggested[mappos])
+		GameLogEcho(strcat(":vote:suggestion_accepted:", mapvote_maps[mappos]), FALSE);
+
+	FOR_EACH_REALCLIENT(other)
+		FixClientCvars(other);
+
+	Map_Goto_SetStr(mapvote_maps[mappos]);
+	Map_Goto();
+	return TRUE;
+}
+void MapVote_CheckRules_1()
+{
+	float i;
+
+	for(i = 0; i < mapvote_count; ++i) if(mapvote_maps[i] != "")
+	{
+		//dprint("Map ", ftos(i), ": "); dprint(mapvote_maps[i], "\n");
+		mapvote_votes[i] = 0;
+	}
+
+	mapvote_voters = 0;
+	FOR_EACH_REALCLIENT(other)
+	{
+		++mapvote_voters;
+		if(other.mapvote)
+		{
+			i = other.mapvote - 1;
+			//dprint("Player ", other.netname, " vote = ", ftos(other.mapvote - 1), "\n");
+			mapvote_votes[i] = mapvote_votes[i] + 1;
+		}
+	}
+}
+
+float MapVote_CheckRules_2()
+{
+	float i;
+	float firstPlace, secondPlace;
+	float firstPlaceVotes, secondPlaceVotes;
+	string result;
+
+	RandSel_Init();
+	for(i = 0; i < mapvote_count; ++i) if(mapvote_maps[i] != "")
+		RandSel_Add(mapvote_votes[i], i);
+	firstPlace = randsel_value;
+	firstPlaceVotes = randsel_priority;
+	//dprint("First place: ", ftos(firstPlace), "\n");
+	//dprint("First place votes: ", ftos(firstPlaceVotes), "\n");
+
+	RandSel_Init();
+	for(i = 0; i < mapvote_count; ++i) if(mapvote_maps[i] != "")
+		if(i != firstPlace)
+			RandSel_Add(mapvote_votes[i], i);
+	secondPlace = randsel_value;
+	secondPlaceVotes = randsel_priority;
+	//dprint("Second place: ", ftos(secondPlace), "\n");
+	//dprint("Second place votes: ", ftos(secondPlaceVotes), "\n");
+
+	if(firstPlace == -1)
+		error("No first place in map vote... WTF?");
+
+	if(secondPlace == -1 || time > mapvote_timeout || (mapvote_voters - firstPlaceVotes) < firstPlaceVotes)
+		return MapVote_Finished(firstPlace);
+
+	if(mapvote_keeptwotime)
+		if(time > mapvote_keeptwotime || (mapvote_voters - firstPlaceVotes - secondPlaceVotes) < secondPlaceVotes)
+		{
+			mapvote_message = "Now decide between the TOP TWO!";
+			mapvote_keeptwotime = 0;
+			result = strcat(":vote:keeptwo:", mapvote_maps[firstPlace]);
+			result = strcat(result, ":", ftos(firstPlaceVotes));
+			result = strcat(result, ":", mapvote_maps[secondPlace]);
+			result = strcat(result, ":", ftos(secondPlaceVotes), "::");
+			for(i = 0; i < mapvote_count; ++i)
+				if(i != firstPlace)
+					if(i != secondPlace)
+						if(mapvote_maps[i] != "")
+						{
+							result = strcat(result, ":", mapvote_maps[i]);
+							result = strcat(result, ":", ftos(mapvote_votes[i]));
+							strunzone(mapvote_maps[i]);
+							mapvote_maps[i] = "";
+						}
+			GameLogEcho(result, FALSE);
+		}
+
+	return FALSE;
+}
+void MapVote_Tick()
+{
+	string msgstr;
+	string tmp;
+	float i;
+	float keeptwo;
+
+	keeptwo = mapvote_keeptwotime;
+	MapVote_CheckRules_1(); // count
+	if(MapVote_CheckRules_2()) // decide
+		return;
+
+	FOR_EACH_REALCLIENT(other)
+	{
+		// hide scoreboard again
+		if(other.health != 2342)
+		{
+			other.health = 2342;
+			other.impulse = 0;
+			if(clienttype(other) == CLIENTTYPE_REAL)
+			{
+				stuffcmd(other, "\nin_bind 7 1 \"impulse 1\"; in_bind 7 2 \"impulse 2\"; in_bind 7 3 \"impulse 3\"; in_bind 7 4 \"impulse 4\"; in_bind 7 5 \"impulse 5\"; in_bind 7 6 \"impulse 6\"; in_bind 7 7 \"impulse 7\"; in_bind 7 8 \"impulse 8\"; in_bind 7 9 \"impulse 9\"; in_bind 7 0 \"impulse 10\"; in_bind 7 KP_1 \"impulse 1\"; in_bind 7 KP_2 \"impulse 2\"; in_bind 7 KP_3 \"impulse 3\"; in_bind 7 KP_4 \"impulse 4\"; in_bind 7 KP_5 \"impulse 5\"; in_bind 7 KP_6 \"impulse 6\"; in_bind 7 KP_7 \"impulse 7\"; in_bind 7 KP_8 \"impulse 8\"; in_bind 7 KP_9 \"impulse 9\"; in_bind 7 KP_0 \"impulse 10\"; in_bindmap 7 0\n");
+				msg_entity = other;
+				WriteByte(MSG_ONE, SVC_FINALE);
+				WriteString(MSG_ONE, "");
+			}
+		}
+
+		// notify about keep-two
+		if(keeptwo != 0 && mapvote_keeptwotime == 0)
+			stuffcmd(other, "\nplay2 misc/invshot.wav\n");
+
+		// clear possibly invalid votes
+		if(mapvote_maps[other.mapvote - 1] == "")
+			other.mapvote = 0;
+		// use impulses as new vote
+		if(other.impulse >= 1 && other.impulse <= mapvote_count)
+			if(mapvote_maps[other.impulse - 1] != "")
+				other.mapvote = other.impulse;
+		other.impulse = 0;
+	}
+
+	MapVote_CheckRules_1(); // just count
+
+	FOR_EACH_REALCLIENT(other)
+	{
+		// display voting screen
+		msgstr = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
+		msgstr = substring(msgstr, 0, strlen(msgstr) - mapvote_count);
+		msgstr = strcat(msgstr, mapvote_message);
+		msgstr = strcat(msgstr, "\n\n");
+		for(i = 0; i < mapvote_count; ++i)
+			if(mapvote_maps[i] == "")
+				msgstr = strcat(msgstr, "\n");
+			else
+			{
+				tmp = mapvote_maps[i];
+				tmp = strcat(tmp, substring(mapvote_fillstr, 0, mapvote_maxlen  - strlen(tmp)));
+				tmp = strcat(ftos(math_mod(i + 1, 10)), ": ", tmp);
+				tmp = strcat(tmp, " ^2(", ftos(mapvote_votes[i]), " vote");
+				if(mapvote_votes[i] != 1)
+					tmp = strcat(tmp, "s");
+				tmp = strcat(tmp, ")");
+				tmp = strcat(tmp, substring(mapvote_fillstr, 0, mapvote_maxlen + 15 - strlen(tmp)));
+				if(other.mapvote == i + 1)
+					msgstr = strcat(msgstr, "^3> ", tmp, "\n");
+				else
+					msgstr = strcat(msgstr, "^7  ", tmp, "\n");
+			}
+		i = ceil(mapvote_timeout - time);
+		msgstr = strcat(msgstr, "\n\n", ftos(i), " second");
+		if(i != 1)
+			msgstr = strcat(msgstr, "s");
+		msgstr = strcat(msgstr, " left");
+
+		centerprint_atprio(other, CENTERPRIO_MAPVOTE, msgstr);
+	}
+}
+void MapVote_Think()
+{
+	if(alreadychangedlevel)
+		return;
+	
+	if(time < mapvote_nextthink)
+		return;
+	//dprint("tick\n");
+
+	mapvote_nextthink = time + 0.5;
+
+	if(!mapvote_initialized)
+	{
+		mapvote_initialized = TRUE;
+		if(DoNextMapOverride())
+		{
+			alreadychangedlevel = TRUE;
+			return;
+		}
+		if(!cvar("g_maplist_votable"))
+		{
+			GotoNextMap();
+			return;
+		}
+		MapVote_Init();
+	}
+
+	MapVote_Tick();
+};

Modified: branches/nexuiz-2.0/data/qcsrc/server/havocbot_roles.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/havocbot_roles.qc	2007-03-28 10:14:16 UTC (rev 2265)
+++ branches/nexuiz-2.0/data/qcsrc/server/havocbot_roles.qc	2007-03-28 12:32:53 UTC (rev 2266)
@@ -73,8 +73,7 @@
 	//dprint(ftos(self.team)); dprint(" -> noteam is "); dprint(ftos(noteam));
 	//dprint("\n");
 
-	head = findchain(classname, "player");
-	while (head)
+	FOR_EACH_PLAYER(head)
 	{
 		if (self != head)
 		if (head.health > 0)
@@ -91,7 +90,6 @@
 				navigation_routerating(head, t * ratingscale);
 			}
 		}
-		head = head.chain;
 	}
 };
 

Modified: branches/nexuiz-2.0/data/qcsrc/server/miscfunctions.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/miscfunctions.qc	2007-03-28 10:14:16 UTC (rev 2265)
+++ branches/nexuiz-2.0/data/qcsrc/server/miscfunctions.qc	2007-03-28 12:32:53 UTC (rev 2266)
@@ -1,3 +1,9 @@
+#define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
+#define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
+string STR_PLAYER = "player";
+#define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
+#define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
+
 float logfile_open;
 float logfile;
 
@@ -3,12 +9,9 @@
 void(string s) bcenterprint
 {
+	// TODO replace by MSG_ALL (would show it to spectators too, though)?
 	entity head;
-	head = find(world, classname, "player");
-	while(head)
-	{
+	FOR_EACH_PLAYER(head)
 		if(clienttype(head) == CLIENTTYPE_REAL)
 			centerprint(head, s);
-		head = find(head, classname, "player");
-	}
 }
 
@@ -413,21 +416,15 @@
 	p.frags = p.frags - f;
 
 	nTeam = 0;
-	head = find(world, classname, "player");
-	while(head)
-	{
+	FOR_EACH_PLAYER(head)
 		if(head != p)
 			if(head.team == targetteam)
 				nTeam = nTeam + 1;
-		head = find(head, classname, "player");
-	}
 
 	if(nTeam == 0)
 		return;
 
-	head = find(world, classname, "player");
-	while(head)
-	{
+	FOR_EACH_PLAYER(head)
 		if(head != p)
 			if(head.team == targetteam)
 			{
@@ -436,8 +433,6 @@
 				f = f - d;
 				nTeam = nTeam - 1;
 			}
-		head = find(head, classname, "player");
-	}
 
 	if(nTeam != 0)
 		error("nPlayers in team changed!");
@@ -512,11 +507,16 @@
 }
 
 #define CENTERPRIO_POINT 1
+#define CENTERPRIO_VOTE 4
 #define CENTERPRIO_NORMAL 5
+#define CENTERPRIO_MAPVOTE 9
 .float centerprint_priority;
 .float centerprint_expires;
 void centerprint_atprio(entity e, float prio, string s)
 {
+	if(intermission_running)
+		if(prio < CENTERPRIO_MAPVOTE)
+			return;
 	if(time > e.centerprint_expires)
 		e.centerprint_priority = 0;
 	if(prio >= e.centerprint_priority)
@@ -538,3 +538,5 @@
 {
 	centerprint_atprio(e, CENTERPRIO_NORMAL, s);
 }
+
+void VoteNag();

Modified: branches/nexuiz-2.0/data/qcsrc/server/teamplay.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/teamplay.qc	2007-03-28 10:14:16 UTC (rev 2265)
+++ branches/nexuiz-2.0/data/qcsrc/server/teamplay.qc	2007-03-28 12:32:53 UTC (rev 2266)
@@ -565,8 +565,7 @@
 	// FIXME: also find and memorize the lowest-scoring bot on each team (in case players must be shuffled around)
 	// also remember the lowest-scoring player
 
-	head = find(world, classname, "player");
-	while(head)
+	FOR_EACH_PLAYER(head)
 	{
 		if(head != ignore)// && head.netname != "")
 		{
@@ -604,7 +603,6 @@
 				}
 			}
 		}
-		head = find(head, classname, "player");
 	}
 }
 
@@ -995,8 +993,7 @@
 	lowest_player_score = 999999999;
 
 	// find the lowest-scoring player & bot of that team
-	head = find(world, classname, "player");
-	while(head)
+	FOR_EACH_PLAYER(head)
 	{
 		if(head.team == steam)
 		{
@@ -1017,7 +1014,6 @@
 				}
 			}
 		}
-		head = find(head, classname, "player");
 	}
 
 	// prefers to move a bot...




More information about the nexuiz-commits mailing list