r3819 - in trunk/data: . qcsrc/client qcsrc/common qcsrc/server

DONOTREPLY at icculus.org DONOTREPLY at icculus.org
Sun Jul 13 13:42:30 EDT 2008


Author: blub0
Date: 2008-07-13 13:42:30 -0400 (Sun, 13 Jul 2008)
New Revision: 3819

Added:
   trunk/data/qcsrc/client/mapvoting.qc
Modified:
   trunk/data/Makefile
   trunk/data/defaultNexuiz.cfg
   trunk/data/qcsrc/client/Main.qc
   trunk/data/qcsrc/client/View.qc
   trunk/data/qcsrc/client/csqc_builtins.qc
   trunk/data/qcsrc/client/main.qh
   trunk/data/qcsrc/client/miscfunctions.qc
   trunk/data/qcsrc/client/progs.src
   trunk/data/qcsrc/client/sbar.qc
   trunk/data/qcsrc/client/sortlist.qc
   trunk/data/qcsrc/client/teamplay.qc
   trunk/data/qcsrc/common/constants.qh
   trunk/data/qcsrc/server/cl_client.qc
   trunk/data/qcsrc/server/clientcommands.qc
   trunk/data/qcsrc/server/g_world.qc
Log:
map previews when voting, g_maplist_textonly for old style
new sorting in the scoreboard
-Fparms in Makefile for future FTE version check


Modified: trunk/data/Makefile
===================================================================
--- trunk/data/Makefile	2008-07-13 16:39:55 UTC (rev 3818)
+++ trunk/data/Makefile	2008-07-13 17:42:30 UTC (rev 3819)
@@ -3,6 +3,18 @@
 PK3NAME ?= `date +../data%Y%m%d.pk3`
 ZIP ?= 7za a -tzip -mx=9
 
+# -Fparm: define PARM0, RETURN, etc. for use in asm{}
+# This will make it possible to make a non-FTE asm{} block
+# checking if the client's engine supports -TFTE.
+# At some point CSQC should use -TFTE too, and then, for at least
+# some time, it would be useful to have a well-formatted error message
+# followed by localcmd("disconnect") if the client doesn't support -TFTE
+# instead of letting him guess what the huge QCVM error message means...
+QCFLAGS_CSQC ?= -Fparm
+
+# to be enabled when possible
+# QCFLAGS_SVQC ?= -TFTE
+
 all: qc
 
 .PHONY: qc
@@ -19,10 +31,10 @@
 	rm -f progs.dat menu.dat csprogs.dat
 
 csprogs.dat: qcsrc/client/*.* qcsrc/common/*.*
-	( cd qcsrc/client; $(FTEQCC) )
+	( cd qcsrc/client; $(FTEQCC) $(QCFLAGS_CSQC) )
 
 progs.dat: qcsrc/server/*.* qcsrc/common/*.*
-	( cd qcsrc/server; $(FTEQCC) )
+	( cd qcsrc/server; $(FTEQCC) $(QCFLAGS_SVQC) )
 
 menu.dat: qcsrc/menu/*.* qcsrc/menu/*/*.* qcsrc/common/*.*
 	( cd qcsrc/menu; $(FTEQCC) )

Modified: trunk/data/defaultNexuiz.cfg
===================================================================
--- trunk/data/defaultNexuiz.cfg	2008-07-13 16:39:55 UTC (rev 3818)
+++ trunk/data/defaultNexuiz.cfg	2008-07-13 17:42:30 UTC (rev 3819)
@@ -811,6 +811,7 @@
 seta g_maplist_votable_suggestions_override_mostrecent 0
 seta g_maplist_votable_nodetail 0 // nodetail only shows total count instead of all vote counts per map, so votes don't influence others that much
 seta g_maplist_votable_abstain 0 // when 1, you can abstain from your vote
+seta g_maplist_textonly 0 // use old style centerprint
 alias suggestmap "cmd suggestmap $1"
 
 set g_chat_flood_spl 0                   // seconds between lines to not count as flooding
@@ -1053,5 +1054,3 @@
 alias sbar_font "loadfont user1 $*; sbar_columns_set"
 sbar_font gfx/vera-sans
 
-// supporting stringwidth
-seta csqc_flags 1

Modified: trunk/data/qcsrc/client/Main.qc
===================================================================
--- trunk/data/qcsrc/client/Main.qc	2008-07-13 16:39:55 UTC (rev 3818)
+++ trunk/data/qcsrc/client/Main.qc	2008-07-13 17:42:30 UTC (rev 3819)
@@ -15,11 +15,34 @@
 };
 
 // let's make this a general data buffer...
-float databuf;
 float using_gps;
 
+float __engine_check;
+
+void testpassedyay() {
+	asm {
+		RETURN 0;
+	}
+}
+
 void CSQC_Init(void)
 {
+#if 0
+	asm {
+		STORE_F "DP_SV_WRITEPICTURE" PARM0;
+		CALL1 checkextension;
+		STORE_F RETURN __engine_check;
+		IF __engine_check 6;
+		STORE_F "^3Your engine build is outdated\n^3This Server uses a newer QC VM. Please update!\n" PARM0;
+		CALL1 print;
+		STORE_F "\ndisconnect\n" PARM0;
+		CALL1 localcmd;
+		DONE;
+	}
+#else
+	__engine_check = true;
+#endif
+	
 	float i;
 	CSQC_CheckEngine();
 	drawfont = 0;
@@ -47,8 +70,20 @@
 		sbar_title[i] = strzone("(null)");
 
 	postinit = false;
+
+	Sbar_Init();
 }
 
+// CSQC_Shutdown : Called every time the CSQC code is shutdown (changing maps, quitting, etc)
+void CSQC_Shutdown(void)
+{
+	asm {
+		IF __engine_check 2;
+		RETURN 0;
+	}
+	buf_del(databuf);
+}
+
 void PostInit(void)
 {
 	float i;
@@ -67,11 +102,6 @@
 
 	postinit = true;
 }
-// CSQC_Shutdown : Called every time the CSQC code is shutdown (changing maps, quitting, etc)
-void CSQC_Shutdown(void)
-{
-	buf_del(databuf);
-}
 
 // CSQC_ConsoleCommand : Used to parse commands in the console that have been registered with the "registercmd" function
 // Return value should be 1 if CSQC handled the command, otherwise return 0 to have the engine handle it.
@@ -104,6 +134,21 @@
 	
 	return false;
 }
+
+float GameCommand(string msg)
+{
+	float argc;
+	argc = tokenize(msg);
+	string cmd;
+	cmd = argv(0);
+	if(cmd == "mv_download") {
+		Cmd_MapVote_MapDownload(argc);
+		return true;
+	}
+	
+	return false;
+}
+
 // CSQC_InputEvent : Used to perform actions based on any key pressed, key released and mouse on the client.
 // Return value should be 1 if CSQC handled the input, otherwise return 0 to have the input passed to the engine.
 // All keys are in ascii.
@@ -219,7 +264,6 @@
 	local float file;
 	local vector mi_min, mi_max;
 
-	draw_enginesbar = true;
 	gametype = cvar("gametype");
 	if(gametype == GAME_ONSLAUGHT) {
 		if(!strcasecmp(substring(mapname, 0, 5), "maps/"))
@@ -248,7 +292,7 @@
 				}
 			}
 		} else {
-			print(strcat("^1Error: ^7Mapinfo file '", mapinfo, "' missing! Minimap will be screwed.\n"));
+			print(strcat("Map has no .info file (", mapinfo, ").\n"));
 		}
 		fclose(file);
 
@@ -268,7 +312,6 @@
 	} else if(gametype == GAME_KEYHUNT) {
 		precache_pic("gfx/sb_key_carrying");
 		precache_pic("gfx/sb_key_carrying_outline");
-		draw_enginesbar = false;
 	}
 }
 // CSQC_Parse_StuffCmd : Provides the stuffcmd string in the first parameter that the server provided.  To execute standard behavior, simply execute localcmd with the string.
@@ -293,7 +336,8 @@
 }
 
 void CSQC_CheckRevision();
-void ReadInit()
+
+void Net_ReadInit()
 {
 	csqc_revision = ReadShort();
 	maxclients = ReadByte();
@@ -301,7 +345,7 @@
 	CSQC_CheckRevision();
 }
 
-void ReadPings()
+void Net_ReadPings()
 {
 	float plnum, ping;
 	for(plnum = ReadByte(); plnum != 0; plnum = ReadByte())
@@ -311,7 +355,7 @@
 	}
 }
 
-void ReadCaptures()
+void Net_ReadCaptures()
 {
 	float plnum, caps, mode;
 	mode = ReadByte();
@@ -324,7 +368,7 @@
 	}
 }
 
-void ReadDatabuf(float ofs)
+void Net_ReadDatabuf(float ofs)
 {
 	float plnum, data;
 	for(plnum = ReadByte(); plnum != 0; plnum = ReadByte())
@@ -334,6 +378,26 @@
 	}
 }
 
+void Net_ReadPicture()
+{
+	string img;
+	if(csqc_flags & CSQC_FLAG_READPICTURE)
+	{
+		img = ReadPicture();
+		print(strcat("Got Picture: ", img, "\n"));
+	} else {
+		img = ReadString();
+		print(strcat("^3Warning: ^7Couldn't download ", img, ". This is probably because your engine build is outdated.\n"));
+		float psize, i;
+		psize = ReadShort();
+		// Can I be sure that ReadShort is 2 bytes and ReadLong is 4 bytes?
+		// Because then this could be optimized to first get all 4-byte-groups,
+		// then the remaining 2, then the remaining 1
+		for(i = 0; i < psize; ++i)
+			ReadByte();
+	}
+}
+
 // CSQC_Parse_TempEntity : Handles all temporary entity network data in the CSQC layer.
 // You must ALWAYS first acquire the temporary ID, which is sent as a byte.
 // Return value should be 1 if CSQC handled the temporary entity, otherwise return 0 to have the engine process the event.
@@ -344,29 +408,39 @@
 	// Acquire TE ID
 	local float nTEID;
 		nTEID = ReadByte();
-		
+
+		// NOTE: Could just do return instead of break...
 	switch(nTEID)
 	{
 		case TE_CSQC_INIT:
-			ReadInit();
+			Net_ReadInit();
 			bHandled = true;
 			break;
 		case TE_CSQC_PING:
-			ReadPings();
+			Net_ReadPings();
 			bHandled = true;
 			break;
 		case TE_CSQC_CAPTURES:
-			ReadCaptures();
+			Net_ReadCaptures();
 			bHandled = true;
 			break;
 		case TE_CSQC_RETURNS:
-			ReadDatabuf(DATABUF_RETURNS);
+			Net_ReadDatabuf(DATABUF_RETURNS);
 			bHandled = true;
 			break;
 		case TE_CSQC_DEATHS:
-			ReadDatabuf(DATABUF_DEATHS);
+			Net_ReadDatabuf(DATABUF_DEATHS);
 			bHandled = true;
 			break;
+		case TE_CSQC_PICTURE:
+			Net_ReadPicture();
+			bHandled = true;
+			break;
+		case TE_CSQC_MAPVOTE:
+			Net_Mapvote();
+			bHandled = true;
+			break;
+		
 		default:
 			// No special logic for this temporary entity; return 0 so the engine can handle it
 			bHandled = false;
@@ -382,7 +456,8 @@
 // COMMIT-TODO: Update if necessare, before committing
 float csqc_svn_map[CSQC_REVISION] =
 {
-	3795,
+	3812, // 3795,
+	3815
 };
 
 // COMMIT-TODO: Update if necessare, before committing

Modified: trunk/data/qcsrc/client/View.qc
===================================================================
--- trunk/data/qcsrc/client/View.qc	2008-07-13 16:39:55 UTC (rev 3818)
+++ trunk/data/qcsrc/client/View.qc	2008-07-13 17:42:30 UTC (rev 3819)
@@ -1,15 +1,5 @@
 //include "main.qh"
 
-float sbar_alpha_fg;
-float last_weapon;
-float activeweapon;
-float weapontime;
-float sbar_hudselector;
-float teamplay;
-float myteam;
-
-float numteams; // NOTE: This is changed in Sbar_SortFrags, so use it only AFTER that
-
 void CSQC_common_hud(void);
 
 void CSQC_kh_hud(void);

Modified: trunk/data/qcsrc/client/csqc_builtins.qc
===================================================================
--- trunk/data/qcsrc/client/csqc_builtins.qc	2008-07-13 16:39:55 UTC (rev 3818)
+++ trunk/data/qcsrc/client/csqc_builtins.qc	2008-07-13 17:42:30 UTC (rev 3819)
@@ -98,7 +98,6 @@
 void	drawresetcliparea(void) = #325;
 float	drawcolorcodedstring(vector position, string text, vector scale, float alpha, float flag) = #326;
 float	stringwidth_engine(string text, float handleColors) = #327;
-float	stringwidth(string text, float handleColors);
 float	drawsubpic(vector position, vector size, string pic, vector srcPosition, vector srcSize, vector rgb, float alpha, float flag) = #328;
 
 
@@ -260,4 +259,6 @@
 float(string str, string sub, float startoffs)		strstrofs = #221;
 //float(string str, string sub) 	    		strstrofs = #221;
 entity(float num)   	   				edict_num = #459;
+string(void)						ReadPicture = #501;
+string(string filename)					whichpack = #503;
 float(entity ent)					num_for_edict = #512;

Modified: trunk/data/qcsrc/client/main.qh
===================================================================
--- trunk/data/qcsrc/client/main.qh	2008-07-13 16:39:55 UTC (rev 3818)
+++ trunk/data/qcsrc/client/main.qh	2008-07-13 17:42:30 UTC (rev 3819)
@@ -11,11 +11,10 @@
 void() menu_show_error;
 void() menu_sub_null;
 
-void() menu_show;
 float menu_visible;
+var void() menu_show;
+var float(float bInputType, float nPrimary, float nSecondary) menu_action;
 
-float(float bInputType, float nPrimary, float nSecondary) menu_action;
-
 // --------------------------------------------------------------------------
 // CTF
 
@@ -86,4 +85,4 @@
 float sbar_font;
 
 float csqc_flags;
-#define CSQC_FLAG_STRINGWIDTH 1
+#define CSQC_FLAG_READPICTURE 1

Added: trunk/data/qcsrc/client/mapvoting.qc
===================================================================
--- trunk/data/qcsrc/client/mapvoting.qc	                        (rev 0)
+++ trunk/data/qcsrc/client/mapvoting.qc	2008-07-13 17:42:30 UTC (rev 3819)
@@ -0,0 +1,276 @@
+float mv_num_maps;
+
+string mv_maps[MAPVOTE_COUNT];
+string mv_pics[MAPVOTE_COUNT];
+string mv_pk3[MAPVOTE_COUNT];
+float mv_preview[MAPVOTE_COUNT];
+float mv_votes[MAPVOTE_COUNT];
+entity mv_pk3list;
+float mv_abstain;
+float mv_ownvote;
+float mv_detail;
+
+void MapVote_DrawMapItem(vector pos, float isize, string map, string pic, float count, float id)
+{
+	vector img_size;
+	//img_size_x = img_size_y = isize;
+	img_size_y = isize;
+	img_size_x = isize / 0.75; // 4:3 x can be stretched easily, height is defined in isize
+
+	drawpic(pos, pic, img_size, '1 1 1', 1, DRAWFLAG_NORMAL);
+
+	// half size for the impulse number
+	img_size_x = img_size_y = isize*0.5;
+	pos_y += img_size_y*0.5;
+
+	if(id == mv_ownvote)
+		drawstring(pos - '40 0', strcat(ftos(id+1), ")"), img_size, '1 1 0', 1, DRAWFLAG_NORMAL);
+	else
+		drawstring(pos - '40 0', strcat(ftos(id+1), ")"), img_size, '1 1 1', 1, DRAWFLAG_NORMAL);
+
+	// half again for the mapname
+	img_size = img_size * 0.5; // *= broken???
+	pos_y += img_size_y*0.5;
+	pos_x += isize/0.75 + 10;
+
+	if(mv_detail)
+		drawstring(pos, strcat(ftos(count), " : ", map), img_size, '1 1 1', 1, DRAWFLAG_NORMAL);
+	else
+		drawstring(pos, map, img_size, '1 1 1', 1, DRAWFLAG_NORMAL);
+}
+
+void MapVote_DrawMapNotAvailable(vector pos, float isize, string map, float count, float id)
+{
+	vector img_size, a, b;
+	img_size_y = isize;
+	img_size_x = isize / 0.75;
+
+	a_x = img_size_x; // for the lines
+	b_y = img_size_y;
+	drawfill(pos, img_size, '.5 .5 .5', .7, DRAWFLAG_NORMAL);
+	drawline(2, pos, pos + a, '1 1 1', 1, DRAWFLAG_NORMAL);
+	drawline(2, pos, pos + b, '1 1 1', 1, DRAWFLAG_NORMAL);
+	drawline(2, pos + img_size, pos + a, '1 1 1', 1, DRAWFLAG_NORMAL);
+	drawline(2, pos + img_size, pos + b, '1 1 1', 1, DRAWFLAG_NORMAL);
+
+	img_size_x = img_size_y = isize*0.5;
+	pos_y += img_size_y*0.5;
+
+	if(id == mv_ownvote)
+		drawstring(pos - '40 0', strcat(ftos(id+1), ")"), img_size, '1 1 0', 1, DRAWFLAG_NORMAL);
+	else
+		drawstring(pos - '40 0', strcat(ftos(id+1), ")"), img_size, '1 1 1', 1, DRAWFLAG_NORMAL);
+	
+	img_size = img_size * 0.5; // *= broken???
+	pos_y += img_size_y*0.5;
+	pos_x += isize/0.75 + 10;
+
+	if(mv_detail)
+		drawstring(pos, strcat(ftos(count), " : ", map), img_size, '1 1 1', 1, DRAWFLAG_NORMAL);
+	else
+		drawstring(pos, map, img_size, '1 1 1', 1, DRAWFLAG_NORMAL);
+}
+
+void MapVote_DrawAbstain(vector pos, float isize, float count, float id)
+{
+	vector img_size;
+	img_size_y = isize;
+	img_size_x = isize / 0.75;
+
+	img_size_x = img_size_y = isize*0.5;
+	pos_y += img_size_y*0.5;
+
+	if(id == mv_ownvote)
+		drawstring(pos - '40 0', strcat(ftos(id+1), ")"), img_size, '1 1 0', 1, DRAWFLAG_NORMAL);
+	else
+		drawstring(pos - '40 0', strcat(ftos(id+1), ")"), img_size, '1 1 1', 1, DRAWFLAG_NORMAL);
+	
+	img_size = img_size * 0.5; // *= broken???
+	pos_y += img_size_y*0.5;
+	pos_x += isize/0.75 + 10;
+
+	if(mv_detail)
+		drawstring(pos, strcat(ftos(count), " : Don't care"), img_size, '1 1 1', 1, DRAWFLAG_NORMAL);
+	else
+		drawstring(pos, "Don't care", img_size, '1 1 1', 1, DRAWFLAG_NORMAL);
+}
+
+void MapVote_Draw()
+{
+	string map;
+	float i, tmp;
+	vector pos;
+	vector center;
+	float isize;
+	
+	center_x = (vid_conwidth - 1)/2;
+	xmin = vid_conwidth*0.2;
+	xmax = vid_conwidth - xmin;
+	ymin = 24;
+	i = cvar("con_chatpos"); //*cvar("con_chatsize");
+	if(i < 0)
+		ymax = vid_conheight + (i - cvar("con_chat")) * cvar("con_chatsize");
+	if(i >= 0 || ymax < (vid_conheight*0.5))
+		ymax = vid_conheight - ymin;
+
+	drawfont = sbar_font;
+	sbar_fontsize = stov(cvar_string("sbar_fontsize"));
+	if(sbar_fontsize_x == 0)
+		sbar_fontsize = '8 8 0';
+	if(sbar_fontsize_y == 0)
+		sbar_fontsize_y = sbar_fontsize_x;
+
+	pos_y = ymin;
+	pos_z = 0;
+	pos_x = center_x - stringwidth("Vote for a map", false) * 0.5 * 24;
+	drawstring(pos, "Vote for a map", '24 24', '1 1 1', 1, DRAWFLAG_NORMAL);
+
+	pos_x = xmin;
+	pos_y += 32;
+	isize = (ymax - ymin - mv_num_maps*10) / mv_num_maps;
+	isize = min(isize, 64);
+	for(i = 0; i < (mv_num_maps - mv_abstain); ++i) // - mv_abstain safe? make sure it's 0 or 1
+	{
+		tmp = mv_votes[i]; // FTEQCC bug: too many array accesses in the function call screw it up
+		if(tmp < 0)
+			continue;
+		map = mv_maps[i];
+		if(mv_preview[i])
+			MapVote_DrawMapItem(pos, isize, map, mv_pics[i], tmp, i);
+		else
+			MapVote_DrawMapNotAvailable(pos, isize, map, tmp, i);
+		pos_y += isize + 10;
+	}
+	if(mv_abstain && i < mv_num_maps) {
+		tmp = mv_votes[i];
+		MapVote_DrawAbstain(pos, isize, tmp, i);
+	}
+}
+
+void Cmd_MapVote_MapDownload(float argc)
+{
+	float id;
+	entity pak;
+
+	if(argc != 2 || !mv_pk3list)
+	{
+		print("mv_mapdownload: ^3You're not supposed to use this command on your own!\n");
+		return;
+	}
+	
+	id = stof(argv(1));
+	for(pak = mv_pk3list; pak; pak = pak.chain)
+		if(pak.sv_entnum == id)
+			break;
+	
+	if(!pak || pak.sv_entnum != id) {
+		print("^1Error:^7 Couldn't find pak index.\n");
+		return;
+	}
+
+	//print(strcat("^3Adding: ", ftos(id), " - ", pak.message, " - "));
+	
+	if(PreviewExists(pak.message))
+	{
+		mv_preview[id] = true;
+		//print("^2Found...\n");
+		return;
+	} else if(csqc_flags & CSQC_FLAG_READPICTURE) {
+		print("Requesting preview...\n");
+		localcmd(strcat("\ncmd mv_getpic ", ftos(id), "\n"));
+	} else {
+		print("^3Missing map preview - Update to a newer build to be able to see them.\n");
+	}
+}
+
+void MapVote_CheckPK3(string pic, string pk3, float id)
+{
+	entity pak;
+	pak = spawn();
+	pak.netname = pk3;
+	pak.message = pic;
+	pak.sv_entnum = id;
+	
+	pak.chain = mv_pk3list;
+	mv_pk3list = pak;
+	
+	localcmd(strcat("\ncurl --pak ", pk3, "; wait; cl_cmd mv_download ", ftos(id), "\n"));
+}
+
+void MapVote_CheckPic(string pic, string pk3, float id)
+{
+	if(PreviewExists(pic))
+	{
+		//print(strcat("^2Exists... ", pic, "\n"));
+		mv_preview[id] = true;
+		return;
+	}
+	MapVote_CheckPK3(pic, pk3, id);
+}
+
+void MapVote_Init()
+{
+	float i;
+	string map, pk3;
+	
+	mv_num_maps = min(MAPVOTE_COUNT, ReadByte());
+	mv_abstain = ReadByte();
+	if(mv_abstain)
+		mv_abstain = 1; // must be 1
+	mv_detail = ReadByte();
+	mv_ownvote = -1;
+	
+	// Assume mv_pk3list is NULL, there should only be 1 mapvote per round
+	mv_pk3list = NULL; // I'm still paranoid!
+	
+	for(i = 0; i < mv_num_maps; ++i)
+	{
+		mv_votes[i] = 0;
+		map = strzone(ReadString());
+		pk3 = strzone(ReadString());
+		mv_maps[i] = map;
+		mv_pk3[i] = pk3;
+		map = strzone(strcat("maps/", map));
+		mv_pics[i] = map;
+
+		mv_preview[i] = false;
+
+		//print(strcat("RECV: ", map, " in ", pk3, "\n"));
+		MapVote_CheckPic(map, pk3, i);
+	}
+}
+
+void MapVote_Update()
+{
+	float i, e;
+	for(i = 0; i < mv_num_maps; ++i)
+	{
+		e = ReadByte();
+		mv_votes[i] = ReadByte();
+		if(!e)
+			mv_votes[i] = -1;
+	}
+}
+
+void Net_Mapvote()
+{
+	float type;
+	type = ReadByte();
+	switch(type)
+	{
+	case MAPVOTE_NET_INIT:
+		MapVote_Init();
+		break;
+	case MAPVOTE_NET_UPDATE:
+		MapVote_Update();
+		break;
+	case MAPVOTE_NET_OWNVOTE:
+		mv_ownvote = ReadByte()-1;
+		break;
+	case MAPVOTE_NET_PIC:
+		type = ReadByte();
+		mv_preview[type] = true;
+		//print(strcat("MAPVOTE_NET_PIC: ^2", ftos(type), "\n"));
+		break;
+	}
+}

Modified: trunk/data/qcsrc/client/miscfunctions.qc
===================================================================
--- trunk/data/qcsrc/client/miscfunctions.qc	2008-07-13 16:39:55 UTC (rev 3818)
+++ trunk/data/qcsrc/client/miscfunctions.qc	2008-07-13 17:42:30 UTC (rev 3819)
@@ -1,3 +1,6 @@
+float databuf;
+var float(string text, float handleColors) stringwidth;
+
 float stringwidth_oldfont(string text, float handleColors)
 {
 	float i, len, ch, width;
@@ -23,14 +26,18 @@
 
 void CSQC_CheckEngine()
 {
-	float i, tmp;
+	/*
 	registercvar("csqc_flags", "0");
 	csqc_flags = cvar("csqc_flags");
+	*/
 
-	if(csqc_flags & CSQC_FLAG_STRINGWIDTH)
+	csqc_flags = 0;
+	
+	if(checkextension("DP_SV_WRITEPICTURE"))
 	{
 		stringwidth = stringwidth_engine;
 		sbar_font = FONT_USER+1;
+		csqc_flags |= CSQC_FLAG_READPICTURE;
 	} else {
 		stringwidth = stringwidth_oldfont;
 		sbar_font = FONT_DEFAULT;
@@ -39,7 +46,42 @@
 
 vector Sbar_GetFontsize()
 {
-	if(csqc_flags & CSQC_FLAG_STRINGWIDTH)
+	if(csqc_flags & CSQC_FLAG_READPICTURE)
 		return stov(cvar_string("sbar_fontsize"));
 	return '8 8' ;
 }
+
+float PreviewExists(string name)
+{
+	float f;
+	string file;
+	file = strcat(name, ".tga");
+	f = fopen(file, FILE_READ);
+	if(f >= 0)
+	{
+		fclose(f);
+		return true;
+	}
+	file = strcat(name, ".png");
+	f = fopen(file, FILE_READ);
+	if(f >= 0)
+	{
+		fclose(f);
+		return true;
+	}
+	file = strcat(name, ".jpg");
+	f = fopen(file, FILE_READ);
+	if(f >= 0)
+	{
+		fclose(f);
+		return true;
+	}
+	file = strcat(name, ".pcx");
+	f = fopen(file, FILE_READ);
+	if(f >= 0)
+	{
+		fclose(f);
+		return true;
+	}
+	return false;
+}

Modified: trunk/data/qcsrc/client/progs.src
===================================================================
--- trunk/data/qcsrc/client/progs.src	2008-07-13 16:39:55 UTC (rev 3818)
+++ trunk/data/qcsrc/client/progs.src	2008-07-13 17:42:30 UTC (rev 3819)
@@ -14,6 +14,9 @@
 ons.qc
 ctf.qc
 
+sbar.qc
+mapvoting.qc
+
 Main.qc
 View.qc
-sbar.qc
+

Modified: trunk/data/qcsrc/client/sbar.qc
===================================================================
--- trunk/data/qcsrc/client/sbar.qc	2008-07-13 16:39:55 UTC (rev 3818)
+++ trunk/data/qcsrc/client/sbar.qc	2008-07-13 17:42:30 UTC (rev 3819)
@@ -1,8 +1,14 @@
 
+float last_weapon;
+float activeweapon;
+float weapontime;
+
 float sb_lines; // still don't know what to do with that NOTE: check dp's sbar.c to see what that should be
 
 vector sbar;
 vector sbar_fontsize;
+float sbar_alpha_fg;
+float sbar_hudselector;
 
 entity sortedPlayers;
 entity sortedTeams;
@@ -12,13 +18,22 @@
 .float sb_player;
 .float sb_caps;
 
+entity team1, team2, team3, team4, teamspec;
+
+void CSQC_kh_hud();
+void CSQC_ctf_hud();
+void MapVote_Draw();
 void Sbar_FinaleOverlay()
 {
-	vector pos;
+	/*vector pos;
 	pos_x = (vid_conwidth - 1)/2;
 	pos_y = 16;
-	pos_z = 0;
-	drawpic(pos, "gfx/finale", '0 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
+	pos_z = 0;*/
+	
+	//drawpic(pos, "gfx/finale", '0 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
+	
+	//drawstring(pos, "END", sbar_fontsize, '1 1 1', 1, DRAWFLAG_NORMAL);
+	MapVote_Draw();
 }
 
 void Sbar_DrawWeapon(float nr, float fade, float active)
@@ -151,94 +166,269 @@
 	return (l.sb_player > r.sb_player);
 }
 
+void Sbar_Init()
+{
+	entity tm;
+	sortedPlayers = Sort_New(Sbar_PlayerCmp);
+	sortedTeams = Sort_New(Sbar_TeamCmp);
+	team1 = Sort_Next(sortedTeams);
+	team1.sb_team = COLOR_TEAM1;
+	team2 = Sort_Next(sortedTeams);
+	team2.sb_team = COLOR_TEAM2;
+	team3 = Sort_Next(sortedTeams);
+	team3.sb_team = COLOR_TEAM3;
+	team4 = Sort_Next(sortedTeams);
+	team4.sb_team = COLOR_TEAM4;
+	teamspec = Sort_Next(sortedTeams);
+	teamspec.sb_team = COLOR_SPECTATOR;
+}
+
+void Sbar_UpdatePosFrags(entity player)
+{
+	other = player.sort_prev;
+	while(other != sortedPlayers && player.sb_frags > other.sb_frags)
+	{
+		SORT_SWAP(other, player);
+		other = player.sort_prev;
+	}
+
+	other = player.sort_next;
+	while(other && player.sb_frags < other.sb_frags)
+	{
+		SORT_SWAP(player, other);
+		other = player.sort_next;
+	}
+}
+void Sbar_UpdatePosFragsCTF(entity player)
+{
+	other = player.sort_prev;
+	while(other != sortedPlayers && player.sb_frags > other.sb_frags && player.sb_caps == other.sb_caps)
+	{
+		SORT_SWAP(other, player);
+		other = player.sort_prev;
+	}
+
+	other = player.sort_next;
+	while(other && player.sb_frags < other.sb_frags && player.sb_caps == other.sb_caps)
+	{
+		SORT_SWAP(player, other);
+		other = player.sort_next;
+	}
+}
+void Sbar_UpdatePosCaps(entity player)
+{
+	other = player.sort_prev;
+	while(other != sortedPlayers && player.sb_caps > other.sb_caps)
+	{
+		SORT_SWAP(other, player);
+		other = player.sort_prev;
+	}
+	other = player.sort_next;
+	while(other && player.sb_caps < other.sb_caps)
+	{
+		SORT_SWAP(player, other);
+		other = player.sort_next;
+	}
+	// let it update the frags now too, so if we have more frags then the next guy with the same caps
+	// we beat his ass :)
+	player.sb_frags -= 1;
+}
+
+void Sbar_UpdatePosTeam(entity player)
+{
+	player.sb_caps -= 1; // team change needs a full update
+	other = player.sort_prev;
+	while(other != sortedPlayers && player.sb_team > other.sb_team)
+	{
+		SORT_SWAP(other, player);
+		other = player.sort_prev;
+	}
+
+	other = player.sort_next;
+	while(other && player.sb_team < other.sb_team)
+	{
+		SORT_SWAP(player, other);
+		other = player.sort_next;
+	}
+}
+
+void Sbar_UpdatePlayer(entity player)
+{
+	float i;
+
+	if(player.sb_frags == -666)
+		i = COLOR_SPECTATOR;
+	else
+		i = GetPlayerColor(player.sb_player);
+	
+	if(player.sb_team != i)
+	{
+		player.sb_team = i;
+		Sbar_UpdatePosTeam(player);
+	}
+	
+	if(gametype == GAME_CTF)
+	{
+		i = stof(bufstr_get(databuf, DATABUF_CAPTURES + player.sb_player));
+		if(player.sb_caps != i)
+		{
+			player.sb_caps = i;
+			Sbar_UpdatePosCaps(player);
+		}
+		i = stof(getplayerkey(player.sb_player, "frags"));
+		if(player.sb_frags != i)
+		{
+			player.sb_frags = i;
+			Sbar_UpdatePosFragsCTF(player);
+		}
+	} else {
+		i = stof(getplayerkey(player.sb_player, "frags"));
+		if(player.sb_frags != i)
+		{
+			player.sb_frags = i;
+			Sbar_UpdatePosFrags(player);
+		}
+	}
+}
+
+void Sbar_UpdateTeamPosCaps(entity tm)
+{
+	other = tm.sort_prev;
+	while(other != sortedTeams && tm.sb_caps > other.sb_caps)
+	{
+		SORT_SWAP(other, tm);
+		other = tm.sort_prev;
+	}
+
+	other = tm.sort_next;
+	while(other && tm.sb_caps < tm.sb_caps)
+	{
+		SORT_SWAP(tm, other);
+		other = tm.sort_next;
+	}
+}
+void Sbar_UpdateTeamPosFrags(entity tm)
+{
+	other = tm.sort_prev;
+	while(other != sortedTeams && tm.sb_caps == other.sb_caps && tm.sb_frags > other.sb_frags)
+	{
+		SORT_SWAP(other, tm);
+		other = tm.sort_prev;
+	}
+
+	other = tm.sort_next;
+	while(other && tm.sb_caps == tm.sb_caps && tm.sb_frags < other.sb_frags)
+	{
+		SORT_SWAP(tm, other);
+		other = tm.sort_next;
+	}
+}
+
 void Sbar_SortFrags()
 {
 	float i;
 	entity tmp;
-	entity t1, t2, t3, t4, ts;
+	float t1f, t2f, t3f, t4f;
 	
-	Sort_Remove(sortedPlayers);
-	sortedPlayers = Sort_New(Sbar_PlayerCmp);
+	Sort_Reset(sortedPlayers);
 
 	numteams = 0;
 	if(teamplay)
 	{
-		Sort_Remove(sortedTeams);
-	
-		t1 = spawn();
-		t2 = spawn();
-		t3 = spawn();
-		t4 = spawn();
-		ts = spawn();
-	
-		t1.sb_team = COLOR_TEAM1;
-		t2.sb_team = COLOR_TEAM2;
-		t3.sb_team = COLOR_TEAM3;
-		t4.sb_team = COLOR_TEAM4;
-		ts.sb_team = COLOR_SPECTATOR;
+		Sort_Reset(sortedTeams);
+		tmp = Sort_Next(sortedTeams);
 
-		t1.sb_player = t2.sb_player = t3.sb_player = t4.sb_player = ts.sb_player = 0;
-		t1.sb_frags = t2.sb_frags = t3.sb_frags = t4.sb_frags = 0;
-		t1.sb_caps = caps_team1;
-		t2.sb_caps = caps_team2;
-		sortedTeams = Sort_New(Sbar_TeamCmp);
+		team1.sb_player = 0;
+		team2.sb_player = 0;
+		team3.sb_player = 0;
+		team4.sb_player = 0;
+		teamspec.sb_player = 0;
 		
+		t1f = team1.sb_frags;
+		t2f = team2.sb_frags;
+		t3f = team3.sb_frags;
+		t4f = team4.sb_frags;
+
+		team1.sb_frags = 0;
+		team2.sb_frags = 0;
+		team3.sb_frags = 0;
+		team4.sb_frags = 0;
+		
 		for(i = 0; i < maxclients; ++i)
 		{
 			if(strlen(getplayerkey(i, "name")) <= 0)
 				continue;
-		
-			tmp = spawn();
+
+			Sort_Reset(sortedPlayers);
+
+			tmp = NULL;
+			while(Sort_HasNext(sortedPlayers))
+			{
+				tmp = Sort_Next(sortedPlayers);
+				if(tmp.sb_player == i)
+					break;
+			}
+			if(!tmp || tmp.sb_player != i)
+				tmp = Sort_Next(sortedPlayers);
+			
 			tmp.sb_player = i;
-			tmp.sb_frags = stof(getplayerkey(i, "frags"));
-			tmp.sb_caps = stof(bufstr_get(databuf, DATABUF_CAPTURES + tmp.sb_player));
+			tmp.frame = time;
+			Sbar_UpdatePlayer(tmp);
 			
-			if(tmp.sb_frags == -666)
-				tmp.sb_team = COLOR_SPECTATOR;
-			else
-				tmp.sb_team = GetPlayerColor(i);
-
 			switch(tmp.sb_team)
 			{
-			case COLOR_TEAM1: t1.sb_frags += tmp.sb_frags; t1.sb_player++; break;
-			case COLOR_TEAM2: t2.sb_frags += tmp.sb_frags; t2.sb_player++; break;
-			case COLOR_TEAM3: t3.sb_frags += tmp.sb_frags; t3.sb_player++; break;
-			case COLOR_TEAM4: t4.sb_frags += tmp.sb_frags; t4.sb_player++; break;
-			case COLOR_SPECTATOR: ts.sb_frags += tmp.sb_frags; ts.sb_player++; break;
+			case COLOR_TEAM1: team1.sb_frags += tmp.sb_frags; team1.sb_player++; break;
+			case COLOR_TEAM2: team2.sb_frags += tmp.sb_frags; team2.sb_player++; break;
+			case COLOR_TEAM3: team3.sb_frags += tmp.sb_frags; team3.sb_player++; break;
+			case COLOR_TEAM4: team4.sb_frags += tmp.sb_frags; team4.sb_player++; break;
+			case COLOR_SPECTATOR: teamspec.sb_frags += tmp.sb_frags; teamspec.sb_player++; break;
 			}
 
 			if(i == player_localentnum-1)
 				myteam = tmp.sb_team;
+		}
+		if(team1.sb_player) ++numteams;
+		if(team2.sb_player) ++numteams;
+		if(team3.sb_player) ++numteams;
+		if(team4.sb_player) ++numteams;
 
-			Sort_Add(sortedPlayers, tmp);
+		if(team1.sb_caps != caps_team1)
+		{
+			team1.sb_caps = caps_team1;
+			Sbar_UpdateTeamPosCaps(team1);
 		}
-		if(t1.sb_player) ++numteams;
-		if(t2.sb_player) ++numteams;
-		if(t3.sb_player) ++numteams;
-		if(t4.sb_player) ++numteams;
-
-		Sort_Add(sortedTeams, t1);
-		Sort_Add(sortedTeams, t2);
-		Sort_Add(sortedTeams, t3);
-		Sort_Add(sortedTeams, t4);
-		Sort_Add(sortedTeams, ts);
-
+		if(team2.sb_caps != caps_team2)
+		{
+			team2.sb_caps = caps_team2;
+			Sbar_UpdateTeamPosCaps(team2);
+		}
+		if(team1.sb_frags != t1f) Sbar_UpdateTeamPosFrags(team1);
+		if(team2.sb_frags != t2f) Sbar_UpdateTeamPosFrags(team2);
+		if(team3.sb_frags != t3f) Sbar_UpdateTeamPosFrags(team3);
+		if(team4.sb_frags != t4f) Sbar_UpdateTeamPosFrags(team4);
 	} else {
 		for(i = 0; i < maxclients; ++i)
 		{
 			if(strlen(getplayerkey(i, "name")) <= 0)
 				continue;
 		
-			tmp = spawn();
+			Sort_Reset(sortedPlayers);
+			tmp = NULL;
+			while(Sort_HasNext(sortedPlayers))
+			{
+				tmp = Sort_Next(sortedPlayers);
+				if(tmp.sb_player == i)
+					break;
+			}
+			if(!tmp || tmp.sb_player != i)
+				tmp = Sort_Next(sortedPlayers);
+			
 			tmp.sb_player = i;
-			tmp.sb_frags = stof(getplayerkey(i, "frags"));
-			if(tmp.sb_frags == -666)
-				tmp.sb_team = COLOR_SPECTATOR;
-			else
-				tmp.sb_team = COLOR_TEAM1;
-			Sort_Add(sortedPlayers, tmp);
+			tmp.frame = time;
+			Sbar_UpdatePlayer(tmp);
 		}
 	}
+	Sort_RemoveOld(sortedPlayers);
 }
 
 void Cmd_Sbar_Help(float argc)
@@ -367,7 +557,8 @@
 		return str;
 	}
 }
-
+#define SBAR_DEFAULT_MASK 0
+#define SBAR_MASK_SPECTATORS 1
 float Sbar_IsFieldMasked(float field, float mask)
 {
 	if(mask&1) // spectator
@@ -425,7 +616,7 @@
 void Sbar_PrintScoreboardItem(vector pos, entity pl, float is_self, float mask)
 {
 	vector tmp;
-	string str, tempstr;
+	string str;
 	float i, field, len;
 
 	// Layout:
@@ -517,7 +708,6 @@
 	entity pl, tm;
 	float specs, i;
 	float center_x;
-	string str;
 
 	sbar_fontsize = Sbar_GetFontsize();
 	if(sbar_fontsize_x == 0)
@@ -557,7 +747,7 @@
 	
 	for(i = 0; i < sbar_num_fields; ++i)
 	{
-		if(Sbar_IsFieldMasked(sbar_field[i]))
+		if(Sbar_IsFieldMasked(sbar_field[i], SBAR_DEFAULT_MASK))
 			continue;
 		if(sbar_field[i] == SBF_SEPARATOR)
 			break;
@@ -571,7 +761,7 @@
 		tmp_y = tmp_z = 0;
 		for(i = sbar_num_fields-1; i > 0; --i)
 		{
-			if(Sbar_IsFieldMasked(sbar_field[i]))
+			if(Sbar_IsFieldMasked(sbar_field[i], SBAR_DEFAULT_MASK))
 				continue;
 			if(sbar_field[i] == SBF_SEPARATOR)
 				break;
@@ -639,7 +829,7 @@
 			{
 				if(pl.sb_team != tm.sb_team)
 					continue;
-				Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), 0);
+				Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), SBAR_DEFAULT_MASK);
 				pos_y += 1.25 * sbar_fontsize_y;
 				tmp_y -= 1.25 * sbar_fontsize_y;
 			}
@@ -654,7 +844,7 @@
 			if(pl.sb_team != COLOR_SPECTATOR)
 				continue;
 			//drawcolorcodedstring(pos, getplayerkey(pl.sb_player, "name"), '8 8 0', 1, 0);
-			Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), 1);
+			Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), SBAR_MASK_SPECTATORS);
 			pos += '0 1.25 0' * sbar_fontsize_y;
 			++specs;
 		}
@@ -667,11 +857,10 @@
 		{
 			if(pl.sb_team == COLOR_SPECTATOR)
 				continue;
-			Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), 0);
+			Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), SBAR_DEFAULT_MASK);
 			pos_y += 1.25 * sbar_fontsize_y;
 			tmp_y -= 1.25 * sbar_fontsize_y;
 		}
-		pos_y += tmp_y + 1.5 * sbar_fontsize_y;
 
 		// rgb := tempvector :)
 		rgb = pos + '0 1.5 0' * sbar_fontsize_y;
@@ -682,7 +871,7 @@
 			if(pl.sb_team != COLOR_SPECTATOR)
 				continue;
 			//drawcolorcodedstring(pos, getplayerkey(pl.sb_player, "name"), '8 8 0', 1, 0);
-			Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), 1);
+			Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), SBAR_MASK_SPECTATORS);
 			pos += '0 1.25 0' * sbar_fontsize_y;
 			++specs;
 		}

Modified: trunk/data/qcsrc/client/sortlist.qc
===================================================================
--- trunk/data/qcsrc/client/sortlist.qc	2008-07-13 16:39:55 UTC (rev 3818)
+++ trunk/data/qcsrc/client/sortlist.qc	2008-07-13 17:42:30 UTC (rev 3819)
@@ -1,5 +1,3 @@
-
-
 .float(entity,entity) sort_cmp;
 .entity sort_next, sort_prev;
 
@@ -9,6 +7,7 @@
 	sort = spawn();
 	sort.sort_cmp = cmp;
 	sort.sort_next = NULL;
+	sort.chain = sort;
 	return sort;
 }
 
@@ -29,8 +28,10 @@
 	entity next, parent;
 	parent = sort;
 	next = sort.sort_next;
-	while(next && sort.sort_cmp(next, ent))
+	while(next)
 	{
+		if(!sort.sort_cmp(next, ent))
+			break;
 		parent = next;
 		next = next.sort_next;
 	}
@@ -41,6 +42,45 @@
 		next.sort_prev = ent;
 }
 
+void Sort_Reset(entity sort)
+{
+	sort.chain = sort;
+}
+
+float Sort_HasNext(entity sort)
+{
+	return (sort.chain.sort_next != NULL);
+}
+
+entity Sort_Next(entity sort)
+{
+	entity next;
+	next = sort.chain.sort_next;
+	if(!next) {
+		next = spawn();
+		sort.chain.sort_next = next;
+		next.sort_prev = sort.chain;
+		next.sort_next = NULL;
+	}
+	sort.chain = next;
+	return next;
+}
+
+void Sort_Finish(entity sort)
+{
+	entity next;
+	next = sort.chain;
+	if(!next)
+		return;
+
+	while(next.sort_next)
+	{
+		sort = next.sort_next;
+		next.sort_next = sort.sort_next;
+		remove(sort);
+	}
+}
+
 entity Sort_Get(entity sort, float i)
 {
 	for(; sort.sort_next && i > 0; --i)
@@ -48,16 +88,31 @@
 	return sort;
 }
 
-void Sort_DoSort(entity sort)
+#define SORT_SWAP(a,b) \
+	b.sort_prev = a.sort_prev;			\
+	a.sort_next = b.sort_next;			\
+	if(b.sort_next) b.sort_next.sort_prev = a;			\
+	a.sort_prev.sort_next = b;			\
+	a.sort_prev = b;				\
+	b.sort_next = a
+
+void Sort_Erase(entity ent)
 {
-	entity newsort, next, tmp;
-	newsort = Sort_New(sort.sort_cmp);
-	next = sort.sort_next;
-	while(next)
+	ent.sort_prev.sort_next = ent.sort_next;
+	if(ent.sort_next)
+		ent.sort_next.sort_prev = ent.sort_prev;
+	remove(ent);
+}
+
+void Sort_RemoveOld(entity sort)
+{
+	entity tmp;
+	for(tmp = sort.sort_next; tmp; tmp = tmp.sort_next)
 	{
-		tmp = next.sort_next;
-		Sort_Add(newsort, next);
-		next = tmp;
+		if(tmp.frame < time)
+		{
+			tmp = tmp.sort_prev;
+			Sort_Erase(tmp.sort_next);
+		}
 	}
-	sort.sort_next = newsort.sort_next;
 }

Modified: trunk/data/qcsrc/client/teamplay.qc
===================================================================
--- trunk/data/qcsrc/client/teamplay.qc	2008-07-13 16:39:55 UTC (rev 3818)
+++ trunk/data/qcsrc/client/teamplay.qc	2008-07-13 17:42:30 UTC (rev 3819)
@@ -1,3 +1,6 @@
+float numteams; // NOTE: This is changed in Sbar_SortFrags, so use it only AFTER that
+float teamplay;
+float myteam;
 
 float TeamByColor(float color)
 {

Modified: trunk/data/qcsrc/common/constants.qh
===================================================================
--- trunk/data/qcsrc/common/constants.qh	2008-07-13 16:39:55 UTC (rev 3818)
+++ trunk/data/qcsrc/common/constants.qh	2008-07-13 17:42:30 UTC (rev 3819)
@@ -1,5 +1,7 @@
 // COMMIT-TODO: Update if necessary before committing
-#define CSQC_REVISION 1
+// Revision 1: additional statistics sent (flag caps, returns, deaths)
+// Revision 2: Mapvote preview pictures
+#define CSQC_REVISION 2
 
 // probably put these in common/
 // so server/ and client/ can be synced better
@@ -163,18 +165,24 @@
 const float ENTCS_MSG_ONS_GPS = 1;
 const float ENTCS_MSG_ONS_REMOVE = 2;
 
-const float TE_CSQC_START = 100;
 const float TE_CSQC_INIT = 100;
 const float TE_CSQC_PING = 101;
 const float TE_CSQC_CAPTURES = 102;
 const float TE_CSQC_RETURNS = 103;
 const float TE_CSQC_DEATHS = 104;
+const float TE_CSQC_PICTURE = 105;
+const float TE_CSQC_MAPVOTE = 106;
 
-const float TE_CSQC_END = 105;
-
 const float STAT_KH_KEYS = 32;
 const float STAT_CTF_STATE = 33;
 const float CTF_STATE_ATTACK = 1;
 const float CTF_STATE_DEFEND = 2;
 const float CTF_STATE_COMMANDER = 3;
 
+// moved that here so the client knows the max.
+// # of maps, I'll use arrays for them :P
+#define MAPVOTE_COUNT 10
+const float MAPVOTE_NET_INIT = 0;
+const float MAPVOTE_NET_UPDATE = 1;
+const float MAPVOTE_NET_PIC = 2;
+const float MAPVOTE_NET_OWNVOTE = 3;

Modified: trunk/data/qcsrc/server/cl_client.qc
===================================================================
--- trunk/data/qcsrc/server/cl_client.qc	2008-07-13 16:39:55 UTC (rev 3818)
+++ trunk/data/qcsrc/server/cl_client.qc	2008-07-13 17:42:30 UTC (rev 3819)
@@ -1147,6 +1147,11 @@
 		ctf_UpdateCaptures(MSG_ONE);
 		ctf_UpdateReturns(MSG_ONE);
 		net_UpdateDeaths(MSG_ONE);
+		if(mapvote_initialized && !cvar("g_maplist_textonly"))
+		{
+			MapVote_SendData(MSG_ONE);
+			MapVote_UpdateData(MSG_ONE);
+		}
 	}
 }
 

Modified: trunk/data/qcsrc/server/clientcommands.qc
===================================================================
--- trunk/data/qcsrc/server/clientcommands.qc	2008-07-13 16:39:55 UTC (rev 3818)
+++ trunk/data/qcsrc/server/clientcommands.qc	2008-07-13 17:42:30 UTC (rev 3819)
@@ -172,6 +172,8 @@
 
 	if(GameCommand_Vote(s, self)) {
 		return;
+	} else if(GameCommand_MapVote(argv(0))) {
+		return;
 	} else if(argv(0) == "autoswitch") {
 		// be backwards compatible with older clients (enabled)
 		self.autoswitch = ("0" != argv(1));

Modified: trunk/data/qcsrc/server/g_world.qc
===================================================================
--- trunk/data/qcsrc/server/g_world.qc	2008-07-13 16:39:55 UTC (rev 3818)
+++ trunk/data/qcsrc/server/g_world.qc	2008-07-13 17:42:30 UTC (rev 3819)
@@ -1921,7 +1921,6 @@
 float mapvote_timeout;
 string mapvote_message;
 
-#define MAPVOTE_COUNT 10
 float mapvote_count;
 float mapvote_count_real;
 string mapvote_maps[MAPVOTE_COUNT];
@@ -2006,6 +2005,7 @@
 	mapvote_count += 1;
 }
 
+void MapVote_SendData(float target);
 void MapVote_Init()
 {
 	float i;
@@ -2053,7 +2053,87 @@
 	if(mapvote_count_real < 3 || mapvote_keeptwotime <= time)
 		mapvote_keeptwotime = 0;
 	mapvote_message = "Choose a map and press its key!";
+
+	if(!cvar("g_maplist_textonly"))
+		MapVote_SendData(MSG_BROADCAST);
 }
+
+void MapVote_SendPicture(float id)
+{
+	msg_entity = self;
+	WriteByte(MSG_ONE, SVC_TEMPENTITY);
+	WriteByte(MSG_ONE, TE_CSQC_PICTURE);
+	WritePicture(MSG_ONE, strcat("maps/", mapvote_maps[id]), 1024);
+	WriteByte(MSG_ONE, SVC_TEMPENTITY);
+	WriteByte(MSG_ONE, TE_CSQC_MAPVOTE);
+	WriteByte(MSG_ONE, MAPVOTE_NET_PIC);
+	WriteByte(MSG_ONE, id);
+}
+
+float GameCommand_MapVote(string cmd)
+{
+	if(!intermission_running)
+		return FALSE;
+	if(!cvar("g_maplist_textonly"))
+	{
+		if(cmd == "mv_getpic")
+		{
+			MapVote_SendPicture(stof(argv(1)));
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+void MapVote_SendData(float targ)
+{
+	string mapfile;
+	float i, o, c;
+	WriteByte(targ, SVC_TEMPENTITY);
+	WriteByte(targ, TE_CSQC_MAPVOTE);
+	WriteByte(targ, MAPVOTE_NET_INIT);
+	WriteByte(targ, mapvote_count);
+	WriteByte(targ, mapvote_abstain);
+	WriteByte(targ, mapvote_detail);
+	for(i = 0; i < mapvote_count; ++i)
+	{
+		WriteString(targ, mapvote_maps[i]);
+		mapfile = strcat("maps/", mapvote_maps[i], ".bsp");
+		localcmd(strcat("\ncurl --pak ", whichpack(mapfile), "\n"));
+		mapfile = whichpack(mapfile);
+		for(o = strstr(mapfile, "/", 0)+1; o > 0; o = strstr(mapfile, "/", 0)+1)
+			mapfile = substring(mapfile, o, 999);
+		for(o = strstr(mapfile, "\\", 0)+1; o > 0; o = strstr(mapfile, "\\", 0)+1)
+			mapfile = substring(mapfile, o, 999);
+		WriteString(targ, mapfile);
+	}
+}
+
+void MapVote_UpdateData(float targ)
+{
+	float i;
+	WriteByte(targ, SVC_TEMPENTITY);
+	WriteByte(targ, TE_CSQC_MAPVOTE);
+	WriteByte(targ, MAPVOTE_NET_UPDATE);
+	for(i = 0; i < mapvote_count; ++i)
+	{
+		if(strlen(mapvote_maps[i]) <= 0)
+			WriteByte(targ, 0);
+		else
+			WriteByte(targ, 1);
+		WriteByte(targ, mapvote_votes[i]);
+	}
+}
+void MapVote_TellVote(float targ, float vote)
+{
+	float i;
+	WriteByte(targ, SVC_TEMPENTITY);
+	WriteByte(targ, TE_CSQC_MAPVOTE);
+	WriteByte(targ, MAPVOTE_NET_OWNVOTE);
+	WriteByte(targ, vote);
+}
+
 float MapVote_Finished(float mappos)
 {
 	string result;
@@ -2111,6 +2191,13 @@
 			mapvote_votes[i] = mapvote_votes[i] + 1;
 		}
 	}
+
+	if(!cvar("g_maplist_textonly"))
+	{
+		MapVote_UpdateData(MSG_BROADCAST);
+		FOR_EACH_REALCLIENT(msg_entity)
+			MapVote_TellVote(MSG_ONE, msg_entity.mapvote);
+	}
 }
 
 float MapVote_CheckRules_2()
@@ -2231,51 +2318,54 @@
 
 	MapVote_CheckRules_1(); // just count
 
-	FOR_EACH_REALCLIENT(other)
+	if(cvar("g_maplist_textonly"))
 	{
-		// 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);
-		if(mapvote_abstain)
-			msgstr = substring(msgstr, 1, strlen(msgstr) - 1);
-		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 = strpad(mapvote_maxlen, tmp);
-				tmp = strcat(ftos(mod(i + 1, 10)), ": ", tmp);
- 				if(mapvote_detail)
- 				{
- 					tmp = strcat(tmp, " ^2(", ftos(mapvote_votes[i]), " vote");
- 					if(mapvote_votes[i] != 1)
- 						tmp = strcat(tmp, "s");
- 					tmp = strcat(tmp, ")");
- 					tmp = strpad(mapvote_maxlen + 15, tmp);
- 				}
-				if(mapvote_abstain)
-					if(i == mapvote_count - 1)
-						msgstr = strcat(msgstr, "\n");
-				if(other.mapvote == i + 1)
-					msgstr = strcat(msgstr, "^3> ", tmp, "\n");
+		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);
+			if(mapvote_abstain)
+				msgstr = substring(msgstr, 1, strlen(msgstr) - 1);
+			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
-					msgstr = strcat(msgstr, "^7  ", tmp, "\n");
-			}
+				{
+					tmp = mapvote_maps[i];
+					tmp = strpad(mapvote_maxlen, tmp);
+					tmp = strcat(ftos(mod(i + 1, 10)), ": ", tmp);
+					if(mapvote_detail)
+					{
+						tmp = strcat(tmp, " ^2(", ftos(mapvote_votes[i]), " vote");
+						if(mapvote_votes[i] != 1)
+							tmp = strcat(tmp, "s");
+						tmp = strcat(tmp, ")");
+						tmp = strpad(mapvote_maxlen + 15, tmp);
+					}
+					if(mapvote_abstain)
+						if(i == mapvote_count - 1)
+							msgstr = strcat(msgstr, "\n");
+					if(other.mapvote == i + 1)
+						msgstr = strcat(msgstr, "^3> ", tmp, "\n");
+					else
+						msgstr = strcat(msgstr, "^7  ", tmp, "\n");
+				}
 
- 		msgstr = strcat(msgstr, "\n\n^2", ftos(totalvotes), " vote");
- 		if(totalvotes != 1)
- 			msgstr = strcat(msgstr, "s");
- 		msgstr = strcat(msgstr, " cast");
-		i = ceil(mapvote_timeout - time);
- 		msgstr = strcat(msgstr, "\n", ftos(i), " second");
-		if(i != 1)
-			msgstr = strcat(msgstr, "s");
-		msgstr = strcat(msgstr, " left");
+			msgstr = strcat(msgstr, "\n\n^2", ftos(totalvotes), " vote");
+			if(totalvotes != 1)
+				msgstr = strcat(msgstr, "s");
+			msgstr = strcat(msgstr, " cast");
+			i = ceil(mapvote_timeout - time);
+			msgstr = strcat(msgstr, "\n", ftos(i), " second");
+			if(i != 1)
+				msgstr = strcat(msgstr, "s");
+			msgstr = strcat(msgstr, " left");
 
-		centerprint_atprio(other, CENTERPRIO_MAPVOTE, msgstr);
+			centerprint_atprio(other, CENTERPRIO_MAPVOTE, msgstr);
+		}
 	}
 }
 void MapVote_Start()




More information about the nexuiz-commits mailing list