r3774 - in trunk/data: gfx qcsrc/client qcsrc/server

DONOTREPLY at icculus.org DONOTREPLY at icculus.org
Fri Jul 4 10:56:24 EDT 2008


Author: blub0
Date: 2008-07-04 10:56:23 -0400 (Fri, 04 Jul 2008)
New Revision: 3774

Added:
   trunk/data/gfx/sb_key_carrying_outline.tga
   trunk/data/qcsrc/client/sbar.qc
   trunk/data/qcsrc/client/sortlist.qc
   trunk/data/qcsrc/client/teamplay.qc
Modified:
   trunk/data/gfx/sb_key_carrying.tga
   trunk/data/qcsrc/client/Main.qc
   trunk/data/qcsrc/client/View.qc
   trunk/data/qcsrc/client/csqc_constants.qc
   trunk/data/qcsrc/client/ctf.qc
   trunk/data/qcsrc/client/main.qh
   trunk/data/qcsrc/client/ons.qc
   trunk/data/qcsrc/client/progs.src
   trunk/data/qcsrc/server/keyhunt.qc
   trunk/data/qcsrc/server/keyhunt.qh
Log:
(experimental) CSQC Hud - can be turned off with "sbar_usecsqc 0"
Added victim's KeyHunt HUD
There may be some discrepancies between the new and the old hud.
After all, it's not a real "copy".


Modified: trunk/data/gfx/sb_key_carrying.tga
===================================================================
(Binary files differ)

Added: trunk/data/gfx/sb_key_carrying_outline.tga
===================================================================
(Binary files differ)


Property changes on: trunk/data/gfx/sb_key_carrying_outline.tga
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Modified: trunk/data/qcsrc/client/Main.qc
===================================================================
--- trunk/data/qcsrc/client/Main.qc	2008-07-03 13:25:33 UTC (rev 3773)
+++ trunk/data/qcsrc/client/Main.qc	2008-07-04 14:56:23 UTC (rev 3774)
@@ -15,8 +15,23 @@
 {
 };
 
+float last_pingrequest;
+float parse_ping;
+float ping_line;
+entity pingList;
+float pingbuf;
+
 void CSQC_Init(void)
-{	
+{
+	float i;
+	last_pingrequest = 0;
+	parse_ping = 0;
+	ping_line = 0;
+
+	pingList = spawn();
+	pingList.sort_next = NULL;
+	pingList.sort_prev = NULL;
+	
 	menu_visible = FALSE;
 	menu_show = menu_show_error;
 	menu_action = menu_sub_null;
@@ -26,13 +41,30 @@
 	registercmd("ons_map");
 	//registercmd("menu_action");
 
+	registercvar("sbar_usecsqc", "1");
+
 	gametype = 0;
 
 	gps_start = world;
+
+	pingbuf = buf_create();
+	for(i = 0; i < maxclients; ++i)
+		bufstr_add(pingbuf, "-", false);
 }
 // CSQC_Shutdown : Called every time the CSQC code is shutdown (changing maps, quitting, etc)
 void CSQC_Shutdown(void)
 {
+	entity next;
+	
+	buf_del(pingbuf);
+
+	while(pingList.sort_next)
+	{
+		next = pingList.sort_next;
+		pingList.sort_next = next.sort_next;
+		remove(next);
+	}
+	remove(pingList);
 }
 
 // CSQC_ConsoleCommand : Used to parse commands in the console that have been registered with the "registercmd" function
@@ -87,38 +119,6 @@
 void(float bIsNewEntity) CSQC_Ent_Update =
 {
 	self.enttype = ReadByte();
-
-	/*if (self.enttype == ENT_CLIENT)
-	{
-		setmodelindex(self, ReadShort());
-		self.origin_x = ReadCoord();
-		self.origin_y = ReadCoord();
-		self.origin_z = ReadCoord();
-		self.angles_x = ReadCoord();
-		self.angles_y = ReadCoord();
-		self.angles_z = ReadCoord();
-		self.velocity_x = ReadCoord();
-		self.velocity_y = ReadCoord();
-		self.velocity_z = ReadCoord();
-		pmove_org = self.origin;
-		pmove_vel = self.velocity;
-		self.avelocity_x = ReadCoord();
-		self.avelocity_y = ReadCoord();
-		self.avelocity_z = ReadCoord();
-		self.movetype = ReadShort();
-		self.frame = ReadShort();
-		self.flags = ReadShort();
-		self.colormap = ReadShort();
-		setorigin(self, self.origin); // relink
-		if (bIsNewEntity)
-		{
-			setsize(self, '0 0 0', '0 0 0');
-			self.drawmask = MASK_NORMAL;
-			//self.think = Client_Think;
-			//self.nextthink = time;
-		}
-		self.ctf_state = ReadByte();
-		}*/
 	if(self.enttype == ENT_CLIENT_ENTCS)
 	{
 		self.sv_entnum = ReadByte();
@@ -165,7 +165,8 @@
 	local float len;
 	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/"))
@@ -211,6 +212,10 @@
 		precache_pic("gfx/ons-cp-blue.tga");
 		precache_pic("gfx/ons-frame.tga");
 		precache_pic("gfx/ons-frame-team.tga");
+	} 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.
@@ -223,10 +228,75 @@
 		Gamemode_Init();
 	}
 }
+
+void PingRequest()
+{
+	float i;
+	entity next;
+	
+	last_pingrequest = time + 5;
+
+	parse_ping = 0;
+	ping_line = 0;
+	for(i = 0; i < maxclients; ++i)
+	{
+		if(strlen(getplayerkey(i, "name")))
+			++parse_ping;
+	}
+
+	while(pingList.sort_next)
+	{
+		next = pingList.sort_next;
+		pingList.sort_next = next.sort_next;
+		remove(next);
+	}
+	pingList.sort_prev = NULL;
+		
+	localcmd("\nping\n");
+}
+
 // CSQC_Parse_Print : Provides the print string in the first parameter that the server provided.  To execute standard behavior, simply execute print with the string.
 void CSQC_Parse_Print(string strMessage)
 {
-	print(strMessage);
+	if(!parse_ping)
+		print(strMessage);
+	else if(strMessage != "Client ping times:\n")
+	{
+		float ping, i, p;
+		entity tmp;
+
+		// pingList.sort_prev = last element
+
+		ping = stof(strMessage);
+
+		/*strMessage = substring(strMessage, 5, 999);
+		print(strcat("Ping for # ", ftos(ping_line), " of ", ftos(parse_ping), " = ", ftos(ping), "   player: "));
+		print(strMessage);
+		print("   searching... ");*/
+		for(i = 0; i < maxclients; ++i)
+		{
+			if(strlen(getplayerkey(i, "name"))) {
+				++p;
+				if(p > ping_line)
+					break;
+			}
+		}
+		if(i >= maxclients)
+			i = maxclients-1;
+		
+		bufstr_set(pingbuf, i, ftos(ping));
+		/*print(getplayerkey(i, "name"));
+		  print("\n");*/
+
+		/*tmp = spawn();
+		tmp.sort_next = tmp.sort_prev = NULL;
+		pingList.sort_prev.sort_next = tmp;
+		pingList.sort_prev = tmp;*/
+
+		++ping_line;
+		if(ping_line >= parse_ping)
+			parse_ping = false;
+	}
 }
 // CSQC_Parse_CenterPrint : Provides the centerprint string in the first parameter that the server provided.  To execute standard behavior, simply execute cprint with the string.
 void CSQC_Parse_CenterPrint(string strMessage)

Modified: trunk/data/qcsrc/client/View.qc
===================================================================
--- trunk/data/qcsrc/client/View.qc	2008-07-03 13:25:33 UTC (rev 3773)
+++ trunk/data/qcsrc/client/View.qc	2008-07-04 14:56:23 UTC (rev 3774)
@@ -1,17 +1,48 @@
 //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);
+void CSQC_ctf_hud(void);
 void CSQC_UpdateView(void)
 {
+	sbar_alpha_fg = cvar("sbar_alpha_fg" );
+	sbar_hudselector = cvar("sbar_hudselector");
+	activeweapon = getstati(STAT_ACTIVEWEAPON);
+	teamplay = cvar("teamplay");
+
+	if(last_weapon != activeweapon) {
+		weapontime = time;
+		last_weapon = activeweapon;
+	}
+
+	if(last_pingrequest < time) {
+		PingRequest();
+	}
+
 	// ALWAYS Clear Current Scene First
 	R_ClearScene();
 
 	// Assign Standard Viewflags
 	// Draw the World (and sky)
 	R_SetView(VF_DRAWWORLD, 1);
+	
 	// Draw the Crosshair
 	R_SetView(VF_DRAWCROSSHAIR, 1);
+	
 	// Draw the Engine Status Bar (the default Quake HUD)
-	R_SetView(VF_DRAWENGINESBAR, 1);
+	draw_enginesbar = !cvar("sbar_usecsqc");
+	R_SetView(VF_DRAWENGINESBAR, draw_enginesbar);
 
 	// Set the console size vars
 	vid_conwidth = cvar("vid_conwidth");
@@ -24,25 +55,8 @@
 	mousepos = mousepos*0.5 + getmousepos();
 	*/
 
-	// Update the camera
-	//UpdateCamera();
-	/*self.origin_z += getstati(STAT_VIEWHEIGHT);
-	R_SetView(VF_ORIGIN, self.origin);
-	self.origin_z -= getstati(STAT_VIEWHEIGHT);
-	R_SetView(VF_ANGLES, input_angles);*/
-	
-	// Setup Entities to be Rendered (include all base types; normal, engine and viewmodels)
 	R_AddEntities(MASK_NORMAL | MASK_ENGINE | MASK_ENGINEVIEWMODELS);
 
-	/* Sample for 3D polygon rendering, do it before R_RenderScene!
-	   R_BeginPolygon("gfx/ctf_ic_atk.tga", 0);
-	   R_PolygonVertex('-5000 -5000 5200', '0 0', '1 1 1', 1);
-	   R_PolygonVertex(' 5000 -5000 5200', '1 0', '1 1 1', 1);
-	   R_PolygonVertex(' 5000  5000 -5200', '1 1', '1 1 1', 1);
-	   R_PolygonVertex('-5000  5000 -5200', '0 1', '1 1 1', 1);
-	   R_EndPolygon();
-	*/
-	
 	// Render the Scene
 	R_RenderScene();
 
@@ -61,11 +75,114 @@
 	{
 		ctf_view();
 	} else */
+	if(!draw_enginesbar)
+	{
+		CSQC_common_hud();
+	}
+	
 	if(gametype == GAME_ONSLAUGHT)
 	{
 		//drawstring('0 0', minimapname, '8 8 0', '1 1 1', 1, 0);
-		drawsetcliparea(0,0,800,600);
+		//drawsetcliparea(0,0,800,600);
 		ons_view();
-		drawresetcliparea();
+		//drawresetcliparea();
 	}
 }
+
+void Sbar_Draw();
+void CSQC_common_hud(void)
+{
+	// Sbar_SortFrags(); done in Sbar_Draw
+	Sbar_Draw();
+}
+
+// KeyHunt HUD by victim
+void CSQC_kh_hud(void)
+{
+	// HUD 0 has the weapons on the right hand side - temporarily shown when needed
+	// HUD 1 has the weapons on the bottom - permanently
+
+	// use the following two binds to check the icons move correctly
+	// bind g "toggle sbar_flagstatus_right; echo Menu right $sbar_flagstatus_right"  // move the icons
+	// bind h "toggle sbar_hudselector; echo HUD $sbar_hudselector"  // change the HUD
+
+	float kh_keys, kh_keys_status, kh_teams_set;
+	float kh_margin_x, kh_margin_y;
+	string kh_carrying, kh_outline;
+	vector red_pos, blue_pos, yellow_pos, pink_pos, kh_size;
+	vector red, blue, yellow, pink;
+	
+	kh_keys = cvar("kh_keys");  // set in keyhunt.qc
+	kh_keys_status = cvar("kh_keys_status");  // set in keyhunt.qc
+	kh_teams_set = cvar("_teams_available");  // set in keyhunt.qc
+
+//	kh_margin_x = 10;
+	kh_margin_y = 8;
+	kh_margin_x = (cvar("sbar_flagstatus_right") * sbar_hudselector * (vid_conwidth - 66)) + 10;
+	// sbar_flagstatus_right 0/1; sbar_hudselector 0/1; screen width - key width + margin
+
+	red_pos_x = blue_pos_x = yellow_pos_x = pink_pos_x = kh_margin_x;
+
+	red_pos_y = kh_margin_y + 120*3;
+	blue_pos_y = kh_margin_y + 120*2;
+	yellow_pos_y = kh_margin_y + 120;
+	pink_pos_y = kh_margin_y + 0;
+
+	red = '1 0 0';
+	blue = '0 0 1';
+	yellow = '1 1 0';
+	pink = '1 0 1';
+
+	kh_size = '45 116';
+
+	kh_carrying = "gfx/sb_key_carrying";
+	kh_outline = "gfx/sb_key_carrying_outline";
+
+	// drawpic(vector position, string pic, vector size, vector rgb, float alpha, float flag)
+	// vector position = '0 0';  // 'x y' 0 0 (the origin) is in the top left. X 0 - 799, Y 0 - 599
+
+	// vector size = '0 0';  // 'x y' changes the x & y dimensions. '0 0' gives the default pic size
+	// vector rgb = '0 0 0';  // 'r g b' range 0 - 1
+
+	if (kh_keys_status & 1)  // red
+		drawpic (red_pos, kh_carrying, kh_size, red, 0.2, 0);  // show 20% alpha key
+	else
+		drawpic (red_pos, kh_outline, kh_size, red, 0.4, 0);  // show key outline
+
+	if (kh_keys & 1)  //red, added by victim
+		drawpic (red_pos, kh_carrying, kh_size, red, 1.0, 0);  // show solid key
+
+
+	if (kh_keys_status & 2)  // blue
+		drawpic (blue_pos, kh_carrying, kh_size, blue, 0.2, 0);
+	else
+		drawpic (blue_pos, kh_outline, kh_size, blue, 0.4, 0);
+
+	if (kh_keys & 2)
+		drawpic (blue_pos, kh_carrying, kh_size, blue, 1.0, 0);
+
+
+	if (kh_teams_set & 4)  // yellow and pink
+	{
+		if (kh_keys_status & 4)  // yellow
+			drawpic (yellow_pos, kh_carrying, kh_size, yellow, 0.2, 0);
+		else
+			drawpic (yellow_pos, kh_outline, kh_size, yellow, 0.4, 0);
+
+		if (kh_keys & 4)  //red, added by victim
+			drawpic (yellow_pos, kh_carrying, kh_size, yellow, 1.0, 0);
+	}
+
+
+	if (kh_teams_set & 8)  // pink only
+	{
+		if (kh_keys_status & 8)  // pink
+			drawpic (pink_pos, kh_carrying, kh_size, pink, 0.2, 0);
+		else
+			drawpic (pink_pos, kh_outline, kh_size, pink, 0.4, 0);
+
+		if (kh_keys & 8)  //red, added by victim
+			drawpic (pink_pos, kh_carrying, kh_size, pink, 1.0, 0);
+	}
+
+}

Modified: trunk/data/qcsrc/client/csqc_constants.qc
===================================================================
--- trunk/data/qcsrc/client/csqc_constants.qc	2008-07-03 13:25:33 UTC (rev 3773)
+++ trunk/data/qcsrc/client/csqc_constants.qc	2008-07-04 14:56:23 UTC (rev 3774)
@@ -59,6 +59,8 @@
 const float		STAT_ITEMS						= 15;
 const float		STAT_VIEWHEIGHT					= 16;
 
+const float		STAT_TIMELIMIT					= 236;
+
 // Sound Constants
 const float		CHAN_AUTO						= 0;
 const float		CHAN_WEAPON						= 1;
@@ -159,3 +161,28 @@
 const float BUTTON_14 = 65536;
 const float BUTTON_15 = 131072;
 const float BUTTON_16 = 262144;
+
+
+const float NEX_IT_UZI              = 1;
+const float NEX_IT_SHOTGUN          = 2;
+const float NEX_IT_GRENADE_LAUNCHER = 4;
+const float NEX_IT_ELECTRO          = 8;
+const float NEX_IT_CRYLINK          = 16;
+const float NEX_IT_NEX              = 32;
+const float NEX_IT_HAGAR            = 64;
+const float NEX_IT_ROCKET_LAUNCHER  = 128;
+const float NEX_IT_SHELLS           = 256;
+const float NEX_IT_BULLETS          = 512;
+const float NEX_IT_ROCKETS          = 1024;
+const float NEX_IT_CELLS            = 2048;
+const float NEX_IT_LASER            = 4094;
+const float NEX_IT_STRENGTH         = 8192;
+const float NEX_IT_INVINCIBLE       = 16384;
+const float NEX_IT_SPEED            = 32768;
+const float NEX_IT_SLOWMO           = 65536;
+
+const float DRAWFLAG_NORMAL = 0;
+const float DRAWFLAG_ADDITIVE = 1;
+const float DRAWFLAG_MODULATE = 2;
+const float DRAWFLAG_2XMODULATE = 3;
+const float DRAWFLAG_NUMFLAGS = 4;

Modified: trunk/data/qcsrc/client/ctf.qc
===================================================================
--- trunk/data/qcsrc/client/ctf.qc	2008-07-03 13:25:33 UTC (rev 3773)
+++ trunk/data/qcsrc/client/ctf.qc	2008-07-04 14:56:23 UTC (rev 3774)
@@ -33,15 +33,10 @@
 	if(getstati(STAT_CTF_STATE) == CTF_STATE_COMMANDER) {
 		drawstring(ps, "\x1D\x1E\x1E\x1E\x1E Order Menu \x1E\x1E\x1E\x1E\x1F", '8 8 0', '1 1 0', 1, 0); ps += po;
 		drawstring(ps, strcat("Order: ", ctf_temp_1), '8 8 0', '1 1 0', 1, 0); ps += po;
-		//drawstring(ps, strcat("N1 = ", ftos(player_localnum), " - N2 = ", ftos(player_localentnum)), '8 8 0', '1 1 0', 1, 0); ps += po;
 		drawcolorcodedstring(ps, "1) ^3previous page", '8 8 0', 1, 0); ps += po;
 		drawcolorcodedstring(ps, "2) ^3next page", '8 8 0', 1, 0); ps += po;
-		//drawcolorcodedstring(ps, strcat("Clients to go: ", ftos(maxclients)), '8 8 0', 1, 0); ps += po;
 		for((n = 2), (p = i = 0); i < maxclients && n > 0; ++i) {
 			frags = getplayerkey(i, "frags");
-			/*print(strcat(ftos(i), "/", ftos(p), "/", ftos(n), "/", getplayerkey(i, "viewentity"), "\n"));
-			print(strcat(" - name: ", getplayerkey(i, "name"), " - frags: ", frags, "\n"));
-			print(strcat(" - color: ", getplayerkey(i, "topcolor"), " - ", color, "\n"));*/
 			if(!frags || (i+1) == player_localentnum)
 				continue;
 			if(frags == "-666" || getplayerkey(i, "topcolor") != color)
@@ -56,7 +51,6 @@
 				drawcolorcodedstring(ps, strcat(ftos(n), ") ", getplayerkey(i, "name"), " : ", ftos(getstatf(STAT_CTF_STATE))), '8 8 0', 1, 0); ps += po;
 			}
 		}
-		//drawstring(ps, strcat("LocalEntNum = ", ftos(player_localnum)), '8 8 0', '1 1 0', 1, 0); ps += po;
 		drawstring(ps, "ESC) Exit Menu", '8 8 0', '1 1 0', 1, 0); ps += po;
 	} else {
 		menu_close();

Modified: trunk/data/qcsrc/client/main.qh
===================================================================
--- trunk/data/qcsrc/client/main.qh	2008-07-03 13:25:33 UTC (rev 3773)
+++ trunk/data/qcsrc/client/main.qh	2008-07-04 14:56:23 UTC (rev 3774)
@@ -20,13 +20,6 @@
 // Onslaught
 
 // Map coordinate base calculations need these
-/*vector mi_redicon;
-vector mi_blueicon;
-vector mi_redbase;
-vector mi_bluebase;
-vector mi_scale;*/
-//vector mi_min;
-//vector mi_max;
 vector mi_center;
 vector mi_scale;
 // Minimap
@@ -39,6 +32,10 @@
 float gametype;
 entity gps_start;
 
+float draw_enginesbar;
+//float sorted_players;
+//float sorted_teams;
+
 // Defs
 .float ctf_state;
 .float health;
@@ -47,5 +44,8 @@
 const float COLOR_TEAM_RED = 64;
 const float COLOR_TEAM_BLUE = 208;
 
-const float COLOR_TEAM1 = 64;
-const float COLOR_TEAM2 = 208;
+const float COLOR_TEAM1       = 4;  // red
+const float COLOR_TEAM2       = 13; // blue
+const float COLOR_TEAM3       = 12; // yellow
+const float COLOR_TEAM4       = 9; // pink
+const float COLOR_SPECTATOR = 1337;

Modified: trunk/data/qcsrc/client/ons.qc
===================================================================
--- trunk/data/qcsrc/client/ons.qc	2008-07-03 13:25:33 UTC (rev 3773)
+++ trunk/data/qcsrc/client/ons.qc	2008-07-04 14:56:23 UTC (rev 3774)
@@ -3,7 +3,7 @@
 	ons_showmap = !ons_showmap;
 };
 
-vector(vector coord) mapCoords =
+vector(vector coord) mapcoords =
 {
 	local vector ret;
 	ret = coord; // put that up to ret's definition and it's '0 0 0' ... stupid fteqcc
@@ -37,19 +37,12 @@
 {
 	if(ons_showmap) {
 		local float color;
-		local vector coord, dir, rgb;
+		local vector coord, rgb;
 
-		color = stof(getplayerkey(player_localentnum-1, "topcolor"));
+		color = stof(getplayerkey(player_localentnum-1, "colors")) & 15;
 
-		/*dir = pmove_org - mi_redbase;
-
-		dir_x *= mi_scale_x;
-		dir_y *= -mi_scale_y;
+		coord = mapcoords(pmove_org);
 			
-		coord = '272 50' + mi_redicon + dir*mi_scale_z;
-		  coord_z = 0;*/
-		coord = mapCoords(pmove_org);
-			
 		drawpic('272 50', minimapname, '256 256', '1 1 1', 1, 0);
 		drawpic('257 35', "gfx/ons-frame.tga", '286 286', '1 1 1', 1, 0);
 		if(color == COLOR_TEAM_RED)
@@ -68,7 +61,7 @@
 		for(tm = gps_start; tm != world; tm = tm.chain)
 		{
 			//print(strcat("GPS: ", ftos(tm.sv_entnum), " - ", vtos(tm.origin), "\n"));
-			drawplayer(mapCoords(tm.origin), tm.angles, rgb);
+			drawplayer(mapcoords(tm.origin), tm.angles, rgb);
 		}
 	}
 };

Modified: trunk/data/qcsrc/client/progs.src
===================================================================
--- trunk/data/qcsrc/client/progs.src	2008-07-03 13:25:33 UTC (rev 3773)
+++ trunk/data/qcsrc/client/progs.src	2008-07-04 14:56:23 UTC (rev 3774)
@@ -6,8 +6,13 @@
 csqc_builtins.qc
 
 main.qh
+
+sortlist.qc
+teamplay.qc
+
 ons.qc
 ctf.qc
 
 Main.qc
 View.qc
+sbar.qc

Added: trunk/data/qcsrc/client/sbar.qc
===================================================================
--- trunk/data/qcsrc/client/sbar.qc	                        (rev 0)
+++ trunk/data/qcsrc/client/sbar.qc	2008-07-04 14:56:23 UTC (rev 3774)
@@ -0,0 +1,832 @@
+
+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;
+
+entity sortedPlayers;
+entity sortedTeams;
+
+.float sb_frags;
+.float sb_team;
+.float sb_player;
+
+void Sbar_FinaleOverlay()
+{
+}
+
+void Sbar_DrawWeapon(float nr, float fade, float active)
+{
+	vector pos, vsize, color;
+	float value;
+	
+	value = (active) ? 1 : 0.6;
+	color_x = color_y = color_z = value;
+	
+	if(sbar_hudselector == 1)
+	{
+		// width = 300, height = 100
+		const float w_width = 32, w_height = 12, w_space = 2, font_size = 8;
+		
+		pos_x = (vid_conwidth - w_width * 9) * 0.5 + w_width * nr;
+		pos_y = (vid_conheight - w_height);
+		pos_z = 0;
+		vsize_x = w_width;
+		vsize_y = w_height;
+		vsize_z = 0;
+		drawpic(pos, strcat("gfx/inv_weapon", ftos(nr)), vsize, color, value * fade * sbar_alpha_fg, 0);
+		pos_x += w_space;
+		pos_y += w_space;
+		vsize_x = font_size;
+		vsize_y = font_size;
+		vsize_z = 0;
+		drawstring(pos, ftos(nr+1), vsize, '1 1 0', sbar_alpha_fg, 0);
+
+	}
+	else
+	{
+		// width = 300, height = 100
+		const float w2_width = 300, w2_height = 100, w2_space = 10;
+		const float w2_scale = 0.4;
+
+		pos_x = vid_conwidth - (w2_width + w2_space) * w2_scale;
+		pos_y = (w2_height + w2_space) * w2_scale * nr + w2_space;
+		pos_z = 0;
+		vsize_x = w2_width * w2_scale;
+		vsize_y = w2_height * w2_scale;
+		vsize_z = 0;
+		
+		drawpic(pos, strcat("gfx/inv_weapon", ftos(nr)), vsize, color, value * fade * sbar_alpha_fg, 0);
+	}
+}
+void Sbar_DrawXNum (vector pos, float num, float digits, float lettersize, vector rgb, float a, float dflags)
+{
+	float l, i;
+	string str, tmp;
+	float minus;
+	vector vsize;
+
+	vsize_x = vsize_y = lettersize;
+	vsize_z = 0;
+
+	if(num < 0)
+	{
+		minus = true;
+		num = -num;
+		pos_x -= lettersize;
+	} else
+		minus = false;
+	
+	if(digits < 0)
+	{
+		tmp = ftos(num);
+		digits = -digits;
+		str = strcat(substring("0000000000", 0, digits - strlen(tmp)), tmp);
+	} else
+		str = ftos(num);
+	
+	l = strlen(str);
+
+	if(l > digits)
+	{
+		str = substring(str, l-digits, 999);
+		l = strlen(str);
+	} else if(l < digits)
+		pos_x += (digits-l) * lettersize;
+
+	if(minus)
+	{
+		drawpic(sbar + pos, "gfx/num_minus", vsize, rgb, a * sbar_alpha_fg, dflags);
+		pos_x += lettersize;
+	}
+
+	for(i = 0; i < l; ++i)
+	{
+		drawpic(sbar + pos, strcat("gfx/num_", substring(str, i, 1)), vsize, rgb, a * sbar_alpha_fg, dflags);
+		pos_x += lettersize;
+	}
+}
+
+float Sbar_PlayerCmp(entity l, entity r)
+{
+	if(teamplay)
+	{
+		if(l.sb_team < r.sb_team)
+			return true;
+		else if(l.sb_team > r.sb_team)
+			return false;
+	}
+	if(l.sb_frags > r.sb_frags)
+		return true;
+	else if(l.sb_frags < r.sb_frags)
+		return false;
+	return (l.sb_player > r.sb_player);
+}
+float Sbar_TeamCmp(entity l, entity r)
+{
+	if(l.sb_frags > r.sb_frags)
+		return true;
+	else if(l.sb_frags < r.sb_frags)
+		return false;
+	return (l.sb_player > r.sb_player);
+}
+
+void Sbar_SortFrags()
+{
+	float i;
+	entity tmp;
+	entity t1, t2, t3, t4, ts;
+	
+	Sort_Remove(sortedPlayers);
+	sortedPlayers = Sort_New(Sbar_PlayerCmp);
+
+	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;
+
+		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;
+		sortedTeams = Sort_New(Sbar_TeamCmp);
+		
+		for(i = 0; i < maxclients; ++i)
+		{
+			if(strlen(getplayerkey(i, "name")) <= 0)
+				continue;
+		
+			tmp = spawn();
+			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 = 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;
+			}
+
+			if(i == player_localentnum-1)
+				myteam = tmp.sb_team;
+
+			Sort_Add(sortedPlayers, tmp);
+		}
+		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);
+
+	} else {
+		for(i = 0; i < maxclients; ++i)
+		{
+			if(strlen(getplayerkey(i, "name")) <= 0)
+				continue;
+		
+			tmp = spawn();
+			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);
+		}
+	}
+}
+
+void Sbar_PrintScoreboardItem(vector pos, entity pl, float is_self, float mask)
+{
+	vector tmp;
+	string str;
+	
+	tmp_y = tmp_z = 0;
+	pos_x += 56;
+
+	str = bufstr_get(pingbuf, pl.sb_player);
+	tmp_x = 4*8 - strlen(str) * 8 - 56;
+	drawstring(pos + tmp, str, '8 8 0', '0.8 0.8 0.8', 0.8, 0);
+
+	if(!(mask & 1))
+	{
+		str = ftos(pl.sb_frags);
+		tmp_x = 4*8 - strlen(str) * 8;
+		drawstring(pos + tmp, str, '8 8 0', '1 1 1', 1, 0);
+	}
+
+	if(is_self)
+		drawstring(pos + '40 0 0', "\x0D", '8 8 0', '1 1 1', 1, 0);
+	str = getplayerkey(pl.sb_player, "name");
+	tmp_x = 5*8 - strlen(str) * 8 + 56;
+	drawcolorcodedstring(pos + '48 0 0', str, '8 8 0', 1, 0);
+}
+void Sbar_PrintScoreboardTeamItem(vector pos, entity tm, vector rgb, string name)
+{
+	vector tmp;
+	string str;
+	
+	tmp_y = tmp_z = 0;
+	pos_x += 56;
+
+	str = ftos(tm.sb_frags);
+	tmp_x = 4*8 - strlen(str) * 8;
+	drawstring(pos + tmp, str, '8 8 0', rgb, 1, 0);
+
+	drawstring(pos + '48 0 0', name, '8 8 0', rgb, 1, 0);
+}
+
+void Sbar_DrawScoreboard()
+{
+	// Assume: frags are already sorted
+	float xmin, xmax, ymin, ymax;
+	vector pos, teammin, teammax, rgb;
+	entity pl, tm;
+	float specs;
+	specs = false;
+
+	xmin = vid_conwidth / 4;
+	xmax = vid_conwidth - xmin;
+	ymin = 64;
+	ymax = vid_conheight - 50;
+
+	pos_x = xmin;
+	pos_y = ymin;
+	pos_z = 0;
+
+	teammin = teammax = '0 0 0';
+	teammin_x = xmin - 2;
+	teammax_x = xmax - 2;
+	
+	drawstring(pos, "ping", '8 8 0', '1 1 1', 1, 0);
+	drawstring(pos + '48 0 0', "frags", '8 8 0', '1 1 1', 1, 0);
+	drawstring(pos + '104 0 0', "name", '8 8 0', '1 1 1', 1, 0);
+	pos += '0 16 0';
+	
+	if(teamplay)
+	{
+		//for(t = 0; t < 4; ++t)
+		for(tm = sortedTeams.sort_next; tm; tm = tm.sort_next)
+		{
+			if(!tm.sb_player || tm.sb_team == COLOR_SPECTATOR) // no players in it?
+				continue;
+
+			teammin_y = pos_y - 2;
+			teammax_y = pos_y + 2 + 10 * (tm.sb_player+1);
+			rgb = GetTeamRGB(tm.sb_team);
+			drawfill(teammin, teammax - teammin, rgb, 0.2, DRAWFLAG_NORMAL);
+			Sbar_PrintScoreboardTeamItem(pos, tm, rgb, GetTeamName(tm.sb_team));
+			pos += '0 12 0';
+			//for(i = 0; i < maxclients; ++i)
+			for(pl = sortedPlayers.sort_next; pl; pl = pl.sort_next)
+			{
+				if(pl.sb_team != tm.sb_team)
+					continue;
+				Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), 0);
+				pos += '0 10 0';
+			}
+			
+			pos += '0 12 0';
+		}
+
+		// rgb := tempvector :)
+		rgb = pos + '0 12 0';
+		//pos += '64 24 0';
+		pos_y += 24;
+		//for(i = 0; i < maxclients; ++i)
+		for(pl = sortedPlayers.sort_next; pl; pl = pl.sort_next)
+		{
+			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);
+			pos += '0 10 0';
+			specs = true;
+		}
+			
+		if(specs)
+			drawstring(rgb, "Spectators", '8 8 0', '1 1 1', 1, 0);
+	} else {
+		//for(i = 0; i < maxclients; ++i)
+		for(pl = sortedPlayers.sort_next; pl; pl = pl.sort_next)
+		{
+			if(pl.sb_team != COLOR_TEAM1)
+				continue;
+			//drawstring(pos, ftos(pl.sb_frags), '8 8 0', '1 1 1', 1, 0);
+			//drawcolorcodedstring(pos + '64 0 0', getplayerkey(pl.sb_player, "name"), '8 8 0', 1, 0);
+			Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), 0);
+			pos += '0 12 0';
+		}
+		rgb = pos + '0 12 0';
+		//pos += '64 24 0';
+		pos_y += 24;
+		for(pl = sortedPlayers.sort_next; pl; pl = pl.sort_next)
+		{
+			if(pl.sb_team != COLOR_SPECTATOR)
+				continue;
+			specs = true;
+			//drawcolorcodedstring(pos, getplayerkey(pl.sb_player, "name"), '8 8 0', 1, 0);
+			Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), 1);
+			pos += '0 12 0';
+		}
+		if(specs)
+			drawstring(rgb, "Spectators", '8 8 0', '1 1 1', 1, 0);
+	}
+}
+
+
+void Sbar_Score(float margin)
+{
+	float timelimit, timeleft, minutes, seconds, distribution, myplace;
+	vector sbar_save, place;
+	entity tm, pl, me;
+	sbar_save = sbar;
+
+	sbar_y = vid_conheight - (32+12);
+	sbar_x -= margin;
+	
+	place = '-48 -12 0';
+	if(teamplay)
+	{
+		// Layout:
+		//
+		//   team1 team3 team4
+		//
+		//         TEAM2
+		//for(i = 0; i < 4; ++i)
+		for(tm = sortedTeams.sort_next; tm; tm = tm.sort_next)
+		{
+			if(tm.sb_team == COLOR_SPECTATOR || !tm.sb_player) // no players? don't display
+				continue;
+			// -32*4 = -128
+			if(tm.sb_team == myteam)
+				Sbar_DrawXNum('-128 0', tm.sb_frags, 4, 32, GetTeamRGB(tm.sb_team), 1, DRAWFLAG_NORMAL);
+			else
+			{
+				Sbar_DrawXNum(place, tm.sb_frags, 4, 12, GetTeamRGB(tm.sb_team), 1, DRAWFLAG_NORMAL);
+				place_x -= 4*12;
+			}
+		}
+	} else {
+		// me vector := [team/connected frags id]
+		myplace = 0;
+		for(me = sortedPlayers.sort_next; me; me = me.sort_next)
+		{
+			if(me.sb_team != COLOR_SPECTATOR)
+				++myplace;
+			if(me.sb_player == player_localentnum - 1)
+				break;
+		}
+		pl = sortedPlayers.sort_next;
+		if(pl == me)
+			pl = pl.sort_next;
+		
+		if(pl && myplace != 1)
+		{
+			distribution = me.sb_frags - pl.sb_frags;
+		} else if(pl) {
+			distribution = me.sb_frags - pl.sb_frags;
+		} else
+			distribution = 0;
+		
+		if(myplace == 1)
+			Sbar_DrawXNum('-36 -12', myplace, 3, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
+		else if(myplace == 2)
+			Sbar_DrawXNum('-36 -12', myplace, 3, 12, '1 1 0', 1, DRAWFLAG_NORMAL);
+		else
+			Sbar_DrawXNum('-36 -12', myplace, 3, 12, '1 0 0', 1, DRAWFLAG_NORMAL);
+
+		if(distribution >= 0)
+		{
+			Sbar_DrawXNum('-84 -12', distribution, 4, 12, ' 1 1 1', 1, DRAWFLAG_NORMAL);
+			Sbar_DrawXNum('-128 0', me.sb_frags, 4, 32, '1 1 1', 1, DRAWFLAG_NORMAL);
+		} else if(distribution >= -5)
+		{
+			Sbar_DrawXNum('-84 -12', distribution, 4, 12, ' 1 1 0', 1, DRAWFLAG_NORMAL);
+			Sbar_DrawXNum('-128 0', me.sb_frags, 4, 32, '1 1 0', 1, DRAWFLAG_NORMAL);
+		} else {
+			Sbar_DrawXNum('-84 -12', distribution, 4, 12, ' 1 0 0', 1, DRAWFLAG_NORMAL);
+			Sbar_DrawXNum('-128 0', me.sb_frags, 4, 32, '1 0 0', 1, DRAWFLAG_NORMAL);
+		}
+	}
+	timelimit = getstatf(STAT_TIMELIMIT);
+	if(timelimit)
+	{
+		timeleft = max(0, timelimit * 60 - time);
+		minutes = floor(timeleft / 60);
+		seconds = floor(timeleft - minutes*60);
+		if(minutes >= 5)
+		{
+			Sbar_DrawXNum('-72 32', minutes, 3, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
+			drawpic(sbar + '-36 32', "gfx/num_colon", '12 12', '1 1 1', sbar_alpha_fg, 0);
+			Sbar_DrawXNum('-24 32', seconds, -2, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
+		} else if(minutes >= 1)
+		{
+			Sbar_DrawXNum('-72 32', minutes, 3, 12, '1 1 0', 1, DRAWFLAG_NORMAL);
+			drawpic(sbar + '-36 32', "gfx/num_colon", '12 12', '1 1 0', sbar_alpha_fg, 0);
+			Sbar_DrawXNum('-24 32', seconds, -2, 12, '1 1 0', 1, DRAWFLAG_NORMAL);
+		} else {
+			Sbar_DrawXNum('-24 32', seconds, -2, 12, '1 0 0', 1, DRAWFLAG_NORMAL);
+		}
+	} else {
+		minutes = floor(time / 60);
+		seconds = floor(time - minutes*60);
+		Sbar_DrawXNum('-72 32', minutes, 3, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
+		drawpic(sbar + '-36 32', "gfx/num_colon", '12 12', '1 1 1', sbar_alpha_fg, 0);
+		Sbar_DrawXNum('-24 32', seconds, -2, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
+	}
+	sbar = sbar_save;
+}
+
+void Sbar_MiniscoreItem(vector pos, entity pl, float is_self)
+{
+	float x;
+	pos_x += 72;
+	
+	if(teamplay)
+		drawfill(pos + '0 1 0', '40 6 0', GetTeamRGB(pl.sb_team)*0.5, 1, DRAWFLAG_NORMAL);
+	else
+		drawfill(pos + '0 1 0', '40 6 0', '0.5 0.5 0.5', 0.5, DRAWFLAG_NORMAL);
+	x = pos_x;
+	pos_x += 5*8;
+	pos_x -= strlen(ftos(pl.sb_frags))*8;
+	drawstring(pos, ftos(pl.sb_frags), '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL);
+	pos_x = x;
+	if(is_self)
+	{
+		pos_x += 48;
+		drawstring(pos, "\x0D", '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL);
+		pos_x += 8;
+	} else
+		pos_x += 56;
+	drawcolorcodedstring(pos, getplayerkey(pl.sb_player, "name"), '8 8 0', 1, 0);
+}
+
+void Sbar_MiniscoreTeamItem(vector pos, float color, float frags, float is_self)
+{
+	float x;
+	pos_x += 72;
+	
+	if(teamplay)
+		drawfill(pos + '0 1 0', '40 6 0', GetTeamRGB(color)*0.5, 1, DRAWFLAG_NORMAL);
+	else
+		drawfill(pos + '0 1 0', '40 6 0', '0.5 0.5 0.5', 0.5, DRAWFLAG_NORMAL);
+	x = pos_x;
+	pos_x += 5*8;
+	pos_x -= strlen(ftos(frags))*8;
+	drawstring(pos, ftos(frags), '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL);
+	pos_x = x;
+	if(is_self)
+	{
+		pos_x += 48;
+		drawstring(pos, "\x0D", '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL);
+		pos_x += 8;
+	} else
+		pos_x += 56;
+	drawstring(pos, GetTeamName(color), '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL);
+}
+
+void Sbar_MiniDeathmatchOverlay(vector pos)
+{
+	float i, numlines, up, down;
+	entity me, tm, pl;
+	float miniscoreboard_size;
+	miniscoreboard_size = cvar("sbar_miniscoreboard_size");
+	
+	if(miniscoreboard_size == 0)
+		return;
+	pos_y = vid_conheight - 8;
+	
+	if(miniscoreboard_size < 0)
+		numlines = (vid_conheight - sbar_y + 7) / 8;
+	else
+		numlines = miniscoreboard_size;
+
+	// give up if there isn't enough room
+	if(pos_x >= vid_conwidth || pos_y >= vid_conheight || numlines < 1)
+		return;
+
+	// me vector := [team/connected frags id]
+	for(me = sortedPlayers.sort_next; me; me = me.sort_next)
+	{
+		if(me.sb_player == player_localentnum - 1)
+			break;
+	}
+
+	if(teamplay)
+		numlines -= numteams;
+
+	// figure out how many players above and below we can show
+	up = floor(numlines/2);
+	down = up;
+	if((up + down) > numlines)
+		down = numlines - up;
+
+	// render bottom-up
+	for(pl = me.sort_next; pl && down > 0; pl = pl.sort_next)
+	{
+		if(pl.sb_team == COLOR_SPECTATOR)
+			continue;
+		Sbar_MiniscoreItem(pos, pl, false);
+		pos_y -= 9;
+		--down;
+	}
+	Sbar_MiniscoreItem(pos, me, true);
+	pos_y -= 9;
+	up += down; // if there weren't enough lines below... add them
+	for(pl = me.sort_prev; pl != sortedPlayers && up > 0; pl = pl.sort_prev)
+	{
+		if(pl.sb_team == COLOR_SPECTATOR)
+			continue;
+		Sbar_MiniscoreItem(pos, pl, false);
+		pos_y -= 9;
+		--up;
+	}
+
+	if(teamplay)
+	{
+		for(tm = sortedTeams.sort_next; tm.sort_next; tm = tm.sort_next);
+		for(; tm != sortedTeams; tm = tm.sort_prev)
+		{
+			if(!tm.sb_player || tm.sb_team == COLOR_SPECTATOR) // no players?
+				continue;
+			Sbar_MiniscoreTeamItem(pos, tm.sb_team, tm.sb_frags, (tm.sb_team == me.sb_team));
+			pos_y -= 9;
+		}
+	}
+}
+
+void Sbar_Draw (void)
+{
+	float i;
+	float x, fade;
+	float stat_items;
+
+	Sbar_SortFrags();
+
+	sb_lines = 24;
+	
+	if (sb_showscores)
+		Sbar_DrawScoreboard();
+	else if (intermission == 1)
+	{
+		Sbar_DrawScoreboard();
+		return;
+	}
+	else if (intermission == 2)
+		Sbar_FinaleOverlay();
+	else
+	{
+		if (sb_showscores || (getstati(STAT_HEALTH) <= 0 && cvar("cl_deathscoreboard")))
+		{
+			sbar_x = (vid_conwidth - 640.0)*0.5;
+			sbar_y = vid_conheight - 47;
+			//Sbar_DrawAlphaPic (sbar_x, sbar_y, sb_scorebar, sbar_alpha_bg.value);
+			drawpic('0 0', "gfx/scorebar", '0 0 0', '1 1 1', cvar("sbar_alpha_bg"), 0);
+			Sbar_DrawScoreboard ();
+		}
+		else
+		{
+			if (sb_lines && sbar_hudselector == 1)
+			{
+				stat_items = getstati(STAT_ITEMS);
+
+				sbar_x = (vid_conwidth - 320.0)*0.5;
+				sbar_y = vid_conheight - 24.0 - 16.0;
+				sbar_z = 0;
+			
+				fade = 3.2 - 2 * (time - weapontime);
+				fade = bound(0.7, fade, 1);
+
+				x = 1.0;
+				for(i = 0; i < 8; ++i)
+				{
+					if(stat_items & x)
+					{
+						Sbar_DrawWeapon(i+1, fade, (i + 2 == activeweapon));
+					}
+					x *= 2;
+				}
+				x *= 2*2*2*2;
+				if(stat_items & x)
+				{
+					Sbar_DrawWeapon(0, fade, (activeweapon == 1));
+				}
+
+				// armor
+				x = getstati(STAT_ARMOR);
+				if (x > 0)
+				{
+					// "gfx/sb_armor"
+					//Sbar_DrawStretchPic (72, 0, sb_armor[0], sbar_alpha_fg.value, 24, 24);
+					drawpic(sbar + '72 0', "gfx/sb_armor", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
+					if(x > 200)
+						Sbar_DrawXNum('0 0', x, 3, 24, '0 1 0', 1, 0);
+					else if(x > 100)
+						Sbar_DrawXNum('0 0', x, 3, 24, '0.2 1 0', 1, 0);
+					else if(x > 50)
+						Sbar_DrawXNum('0 0', x, 3, 24, '0.6 0.7 0.8', 1, 0);
+					else if(x > 25)
+						Sbar_DrawXNum('0 0', x, 3, 24, '1 1 0.2', 1, 0);
+					else
+						Sbar_DrawXNum('0 0', x, 3, 24, '0.7 0 0', 1, 0);
+				}
+
+				// health
+				x = getstati(STAT_HEALTH);
+				if (x != 0)
+				{
+					// "gfx/sb_health"
+					//Sbar_DrawStretchPic (184, 0, sb_health, sbar_alpha_fg.value, 24, 24);
+					drawpic(sbar + '184 0', "gfx/sb_health", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
+					if(x > 200)
+						Sbar_DrawXNum('112 0', x, 3, 24, '0 1 0', 1, 0);
+					else if(x > 100)
+						Sbar_DrawXNum('112 0', x, 3, 24, '0.2 1 0', 1, 0);
+					else if(x > 50)
+						Sbar_DrawXNum('112 0', x, 3, 24, '0.6 0.7 0.8', 1, 0);
+					else if(x > 25)
+						Sbar_DrawXNum('112 0', x, 3, 24, '1 1 0.2', 1, 0);
+					else
+						Sbar_DrawXNum('112 0', x, 3, 24, '0.7 0 0', 1, 0);
+				}
+
+				// ammo
+				x = getstati(STAT_AMMO);
+				if ((stat_items & (NEX_IT_SHELLS | NEX_IT_BULLETS | NEX_IT_ROCKETS | NEX_IT_CELLS)) || x != 0)
+				{
+					if (stat_items & NEX_IT_SHELLS)
+						drawpic(sbar + '296 0', "gfx/sb_shells", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
+					else if (stat_items & NEX_IT_BULLETS)
+						drawpic(sbar + '296 0', "gfx/sb_bullets", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
+					else if (stat_items & NEX_IT_ROCKETS)
+						drawpic(sbar + '296 0', "gfx/sb_rocket", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
+					else if (stat_items & NEX_IT_CELLS)
+						drawpic(sbar + '296 0', "gfx/sb_cells", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
+					if(x > 10)
+						Sbar_DrawXNum('224 0', x, 3, 24, '0.6 0.7 0.8', 1, 0);
+					else
+						Sbar_DrawXNum('224 0', x, 3, 24, '0.7 0 0', 1, 0);
+				}
+
+				if (sbar_x + 320 + 160 <= vid_conwidth)
+					Sbar_MiniDeathmatchOverlay(sbar + '320 0');
+				if (sbar_x > 0)
+					Sbar_Score(16);
+				// The margin can be at most 8 to support 640x480 console size:
+				//   320 + 2 * (144 + 16) = 640
+			}
+			else if (sb_lines)
+			{
+			
+				stat_items = getstati(STAT_ITEMS);
+			
+				sbar_x = (vid_conwidth - 640.0)*0.5;
+				sbar_y = vid_conheight - 47;
+				sbar_z = 0;
+
+				fade = 3 - 2 * (time - weapontime);
+
+				x = 1.0;
+				for(i = 0; i < 8; ++i)
+				{
+					if(stat_items & x)
+					{
+						Sbar_DrawWeapon(i+1, fade, (i + 2 == activeweapon));
+					}
+					x *= 2;
+				}
+				x *= 2*2*2*2;
+				if(stat_items & x)
+				{
+					Sbar_DrawWeapon(0, fade, (activeweapon == 1));
+				}
+
+				if (sb_lines > 24)
+					drawpic(sbar, "gfx/sbar", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
+				else
+					drawpic(sbar, "gfx/sbar_minimal", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
+
+				// armor
+				// (340-3*24) = 268
+				Sbar_DrawXNum('268 12', getstati(STAT_ARMOR), 3, 24, '0.6 0.7 0.8', 1, 0);
+
+				// health
+				// (154-3*24) = 82
+				x = getstati(STAT_HEALTH);
+				if(x > 100)
+					Sbar_DrawXNum('82 12', x, 3, 24, '1 1 1', 1, 0);
+				else if(x <= 25 && time - floor(time) > 0.5)
+					Sbar_DrawXNum('82 12', x, 3, 24, '0.7 0 0', 1, 0);
+				else
+					Sbar_DrawXNum('81 12', x, 3, 24, '0.6 0.7 0.8', 1, 0);
+
+				// AK dont draw ammo for the laser
+				x = getstati(STAT_AMMO);
+				if(activeweapon != 12)
+				{
+					// (519-3*24) = 447
+					if (stat_items & NEX_IT_SHELLS)
+						drawpic(sbar + '519 0', "gfx/sb_shells", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
+					else if (stat_items & NEX_IT_BULLETS)
+						drawpic(sbar + '519 0', "gfx/sb_bullets", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
+					else if (stat_items & NEX_IT_ROCKETS)
+						drawpic(sbar + '519 0', "gfx/sb_rocket", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
+					else if (stat_items & NEX_IT_CELLS)
+						drawpic(sbar + '519 0', "gfx/sb_cells", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
+					if(x > 10)
+						Sbar_DrawXNum('447 12', x, 3, 24, '0.6 0.7 0.8', 1, 0);
+					else
+						Sbar_DrawXNum('447 12', x, 3, 24, '0.7 0 0', 1, 0);
+				}
+
+				if (sb_lines > 24)
+					drawpic(sbar, "gfx/sbar_overlay", '0 0 0', '1 1 1', 1, DRAWFLAG_MODULATE);
+
+				if (sbar_x + 600 + 160 <= vid_conwidth)
+					Sbar_MiniDeathmatchOverlay (sbar + '600 0');
+
+				if (sbar_x > 0)
+					Sbar_Score(-16);
+				// Because:
+				//   Mini scoreboard uses 12*4 per other team, that is, 144
+				//   pixels when there are four teams...
+				//   Nexuiz by default sets vid_conwidth to 800... makes
+				//   sbar_x == 80...
+				//   so we need to shift it by 64 pixels to the right to fit
+				//   BUT: then it overlaps with the image that gets drawn
+				//   for viewsize 100! Therefore, just account for 3 teams,
+				//   that is, 96 pixels mini scoreboard size, needing 16 pixels
+				//   to the right!
+			}
+		
+		
+			if(gametype == GAME_KEYHUNT)
+			{
+				CSQC_kh_hud();
+			} else if(gametype == GAME_CTF)
+			{
+				CSQC_ctf_hud();
+			}
+		}
+	}
+}
+
+void CSQC_ctf_hud(void)
+{
+	// cvar("sbar_flagstatus_right") move the flag icons right
+	// cvar("sbar_flagstatus_pos") pixel position of the nexuiz flagstatus icons
+	float redflag, blueflag;
+	float stat_items;
+	vector pos;
+	
+	stat_items = getstati(STAT_ITEMS);
+	redflag = (stat_items/32768) & 3;
+	blueflag = (stat_items/131072) & 3;
+
+	pos_x = (cvar("sbar_flagstatus_right")) ? vid_conwidth - 10 - sbar_x - 64 : 10 - sbar_x;
+	pos_z = 0;
+
+	if(sbar_hudselector == 1)
+		pos_y = (vid_conheight - sbar_y) - cvar("sbar_flagstatus_pos") - 64;
+	else
+		pos_y = -117;
+
+	pos += sbar;
+
+	switch(redflag)
+	{
+	case 1: drawpic(pos, "gfx/sb_flag_red_taken", '0 0 0', '1 1 1', 1, DRAWFLAG_NORMAL); break;
+	case 2: drawpic(pos, "gfx/sb_flag_red_lost", '0 0 0', '1 1 1', 1, DRAWFLAG_NORMAL); break;
+	case 3: drawpic(pos, "gfx/sb_flag_red_carrying", '0 0 0', '1 1 1', 1, DRAWFLAG_NORMAL); break;
+	}
+
+	pos_y -= 64;
+	
+	switch(blueflag)
+	{
+	case 1: drawpic(pos, "gfx/sb_flag_blue_taken", '0 0 0', '1 1 1', 1, 0); break;
+	case 2: drawpic(pos, "gfx/sb_flag_blue_lost", '0 0 0', '1 1 1', 1, 0); break;
+	case 3: drawpic(pos, "gfx/sb_flag_blue_carrying", '0 0 0', '1 1 1', 1, 0); break;
+	}
+}

Added: trunk/data/qcsrc/client/sortlist.qc
===================================================================
--- trunk/data/qcsrc/client/sortlist.qc	                        (rev 0)
+++ trunk/data/qcsrc/client/sortlist.qc	2008-07-04 14:56:23 UTC (rev 3774)
@@ -0,0 +1,63 @@
+
+
+.float(entity,entity) sort_cmp;
+.entity sort_next, sort_prev;
+
+entity Sort_New(float(entity,entity) cmp)
+{
+	entity sort;
+	sort = spawn();
+	sort.sort_cmp = cmp;
+	sort.sort_next = NULL;
+	return sort;
+}
+
+void Sort_Remove(entity sort)
+{
+	entity next;
+	while(sort.sort_next)
+	{
+		next = sort.sort_next;
+		remove(sort);
+		sort = next;
+	}
+	remove(sort);
+}
+
+void Sort_Add(entity sort, entity ent)
+{
+	entity next, parent;
+	parent = sort;
+	next = sort.sort_next;
+	while(next && sort.sort_cmp(next, ent))
+	{
+		parent = next;
+		next = next.sort_next;
+	}
+	ent.sort_next = next;
+	ent.sort_prev = parent;
+	parent.sort_next = ent;
+	if(next)
+		next.sort_prev = ent;
+}
+
+entity Sort_Get(entity sort, float i)
+{
+	for(; sort.sort_next && i > 0; --i)
+		sort = sort.sort_next;
+	return sort;
+}
+
+void Sort_DoSort(entity sort)
+{
+	entity newsort, next, tmp;
+	newsort = Sort_New(sort.sort_cmp);
+	next = sort.sort_next;
+	while(next)
+	{
+		tmp = next.sort_next;
+		Sort_Add(newsort, next);
+		next = tmp;
+	}
+	sort.sort_next = newsort.sort_next;
+}

Added: trunk/data/qcsrc/client/teamplay.qc
===================================================================
--- trunk/data/qcsrc/client/teamplay.qc	                        (rev 0)
+++ trunk/data/qcsrc/client/teamplay.qc	2008-07-04 14:56:23 UTC (rev 3774)
@@ -0,0 +1,41 @@
+
+float TeamByColor(float color)
+{
+	switch(color)
+	{
+	case COLOR_TEAM1: return 0;
+	case COLOR_TEAM2: return 1;
+	case COLOR_TEAM3: return 2;
+	case COLOR_TEAM4: return 3;
+	default: return 0;
+	}
+}
+
+float GetPlayerColor(float i)
+{
+	return stof(getplayerkey(i, "colors")) & 15;
+}
+
+vector GetTeamRGB(float color)
+{
+	switch(color)
+	{
+	default: return '1 1 1';
+	case COLOR_TEAM1: return '1 0 0'; // red
+	case COLOR_TEAM2: return '0 0 1'; // blue
+	case COLOR_TEAM3: return '1 1 0'; // yellow
+	case COLOR_TEAM4: return '1 0 1'; // pink
+	}
+}
+
+string GetTeamName(float color)
+{
+	switch(color)
+	{
+	default: return "Spectators";
+	case COLOR_TEAM1: return "Red Team";
+	case COLOR_TEAM2: return "Blue Team";
+	case COLOR_TEAM3: return "Yellow Team";
+	case COLOR_TEAM4: return "Pink Team";
+	}
+}

Modified: trunk/data/qcsrc/server/keyhunt.qc
===================================================================
--- trunk/data/qcsrc/server/keyhunt.qc	2008-07-03 13:25:33 UTC (rev 3773)
+++ trunk/data/qcsrc/server/keyhunt.qc	2008-07-04 14:56:23 UTC (rev 3774)
@@ -23,6 +23,15 @@
 
 string kh_Controller_Waitmsg;
 
+.float siren_time;  //  time delay the siren
+.float stuff_time;  //  time delay to stuffcmd a cvar
+
+float test[17] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+//test[0] = status of dropped keys, test[1 - 16] = player #
+//replace 17 with cvar("maxplayers") or similar !!!!!!!!!
+//for(i = 0; i < maxplayers; ++i)
+//	test[i] = "0";
+
 float kh_Team_ByID(float t)
 {
 	if(t == 0) return COLOR_TEAM1;
@@ -47,11 +56,12 @@
 string kh_sound_destroy = "sound/kh/destroy.wav";
 string kh_sound_drop = "sound/kh/drop.wav";
 string kh_sound_collect = "sound/kh/collect.wav";
+string kh_sound_alarm = "sound/kh/alarm.wav";  // the new siren/alarm
 
 float kh_sprite_dropped, kh_sprite_finish, kh_sprite_red, kh_sprite_blue, kh_sprite_pink, kh_sprite_yellow, kh_sprite_friend;
 float kh_key_dropped, kh_key_carried;
 
-float kh_GetCarrierSprite(float t, float e)
+float kh_GetCarrierSprite(float t, float e)  // runs all the time
 {
 	if(t == e)           return kh_sprite_friend;
 	if(t == COLOR_TEAM1) return kh_sprite_red;
@@ -61,7 +71,7 @@
 	return 0;
 }
 
-void kh_Controller_SetThink(float t, string msg, kh_Think_t func)
+void kh_Controller_SetThink(float t, string msg, kh_Think_t func)  // runs occasionaly
 {
 	kh_Controller_Thinkfunc = func;
 	kh_controller.cnt = t;
@@ -75,7 +85,7 @@
 		kh_controller.nextthink = time; // force
 }
 
-void kh_Controller_Think()
+void kh_Controller_Think()  // called a lot
 {
 	entity e;
 	if(intermission_running)
@@ -108,7 +118,7 @@
 
 // frags f: take from cvar * f
 // frags 0: no frags
-void kh_Scores_Event(entity player, entity key, string what, float frags_player, float frags_owner)
+void kh_Scores_Event(entity player, entity key, string what, float frags_player, float frags_owner)  // update the score when a key is captured
 {
 	string s;
 	if(intermission_running)
@@ -117,7 +127,7 @@
 		UpdateFrags(player, frags_player);
 	if(key && key.owner && frags_owner)
 		UpdateFrags(key.owner, frags_owner);
-	if(!cvar("sv_eventlog"))
+	if(!cvar("sv_eventlog"))  //output extra info to the console or text file
 		return;
 	s = strcat(":keyhunt:", what, ":", ftos(player.playerid));
 	s = strcat(s, ":", ftos(frags_player));
@@ -131,7 +141,7 @@
 	GameLogEcho(s, FALSE);
 }
 
-vector kh_AttachedOrigin(entity e)
+vector kh_AttachedOrigin(entity e)  // runs when a team captures the flag, it can run 2 or 3 times.
 {
 	if(e.tag_entity)
 	{
@@ -142,7 +152,7 @@
 		return e.origin;
 }
 
-void kh_Key_Attach(entity key)
+void kh_Key_Attach(entity key)  // runs when a player picks up a key and several times when a key is assigned to a player at the start of a round
 {
 #ifdef KH_PLAYER_USE_ATTACHMENT
 	entity first;
@@ -172,7 +182,7 @@
 	}
 #else
 	setattachment(key, key.owner, "");
-	setorigin(key, '0 0 1' * KH_KEY_ZSHIFT); // fixing x, y in think
+	setorigin(key, '0 0 1' * KH_KEY_ZSHIFT);  // fixing x, y in think
 	key.angles_y -= key.owner.angles_y;
 #endif
 	key.flags = 0;
@@ -184,8 +194,24 @@
 	key.modelindex = kh_key_carried;
 }
 
-void kh_Key_Detach(entity key)
+void kh_Key_Detach(entity key) // runs every time a key is dropped or lost. Runs several times times when all the keys are captured
 {
+	float i;
+	i = test[key.owner.playerid];
+		if(key.netname == "^1red key")
+			i -= 1;
+		if(key.netname == "^4blue key")
+			i -= 2;
+		if(key.netname == "^3yellow key")
+			i -= 4;
+		if(key.netname == "^6pink key")
+			i -= 8;
+	test[key.owner.playerid] = i;
+
+	kh_show_temp();
+
+	stuffcmd(key.owner, strcat("set kh_keys ", ftos(test[key.owner.playerid]), "\n"));  // send to current player
+
 #ifdef KH_PLAYER_USE_ATTACHMENT
 	entity first;
 	first = key.owner.kh_next;
@@ -224,7 +250,7 @@
 	key.kh_previous_owner = key.owner;
 }
 
-void kh_Key_AssignTo(entity key, entity player)
+void kh_Key_AssignTo(entity key, entity player)  // runs every time a key is picked up or assigned. Runs prior to kh_key_attach
 {
 	if(key.owner == player)
 		return;
@@ -258,6 +284,22 @@
 		if(key.kh_next)
 			key.kh_next.kh_prev = key;
 
+		float i;
+		i = test[key.owner.playerid];
+			if(key.netname == "^1red key")
+				i += 1;
+			if(key.netname == "^4blue key")
+				i += 2;
+			if(key.netname == "^3yellow key")
+				i += 4;
+			if(key.netname == "^6pink key")
+				i += 8;
+		test[key.owner.playerid] = i;
+
+		kh_show_temp();
+
+		stuffcmd(player, strcat("set kh_keys ", ftos(test[key.owner.playerid]), "\n"));  // send to current player
+
 		kh_Key_Attach(key);
 
 		if(key.kh_next == world)
@@ -283,7 +325,7 @@
 			self.team = attacker.team;
 }
 
-void kh_Key_Spawn(entity initial_owner, float angle)
+void kh_Key_Spawn(entity initial_owner, float angle)  // runs every time a new flag is created, ie after all the keys have been collected
 {
 	entity key;
 	key = spawn();
@@ -326,7 +368,7 @@
 	key.kh_worldkeynext = kh_worldkeylist;
 	kh_worldkeylist = key;
 
-	sprint(initial_owner, strcat("You got the ^2", key.netname, "\n"));
+	centerprint(initial_owner, strcat("You are starting with the ", key.netname, "\n"));  // message to player at start of round
 
 	WaypointSprite_Spawn("", 0, 0, key, '0 0 1' * KH_KEY_WP_ZSHIFT, world, key.team, key, waypointsprite_attachedforcarrier, FALSE);
 	key.waypointsprite_attachedforcarrier.waypointsprite_for_player = kh_Key_waypointsprite_for_player;
@@ -334,7 +376,7 @@
 	kh_Key_AssignTo(key, initial_owner);
 }
 
-void kh_Key_Remove(entity key)
+void kh_Key_Remove(entity key)  // runs after when all the keys have been collected or when a key has been dropped for more than X seconds
 {
 	entity o;
 	o = key.owner;
@@ -365,7 +407,7 @@
 }
 
 // -1 when no team completely owns all keys yet
-float kh_Key_AllOwnedByWhichTeam()
+float kh_Key_AllOwnedByWhichTeam()  // constantly called. check to see if all the keys are owned by the same team
 {
 	entity key;
 	float teem;
@@ -383,14 +425,19 @@
 	return teem;
 }
 
-void kh_Key_Collect(entity key, entity player)
+void kh_Key_Collect(entity key, entity player)  //a player picks up a dropped key
 {
 	sound(player, CHAN_AUTO, kh_sound_collect, 1, ATTN_NORM);
 
 	if(key.kh_dropperteam != player.team)
 		kh_Scores_Event(player, key, "collect", cvar("g_balance_keyhunt_score_collect"), 0);
 	key.kh_dropperteam = 0;
-	bprint(player.netname, "^7 collected the ", key.netname, "\n");
+	bprint(player.netname, "^7 picked up the ", key.netname, "\n");
+
+	kh_show_temp();
+
+	stuffcmd(player, strcat("set kh_keys ", ftos(test[key.owner.playerid]), "\n"));  // send to current player
+
 	kh_Key_AssignTo(key, player);
 
 	if(kh_Key_AllOwnedByWhichTeam() != -1)
@@ -400,7 +447,7 @@
 	}
 }
 
-void kh_Key_DropAll(entity player, float suicide)
+void kh_Key_DropAll(entity player, float suicide) // runs whenever a player dies
 {
 	entity key;
 	entity mypusher;
@@ -413,7 +460,7 @@
 		while((key = player.kh_next))
 		{
 			kh_Scores_Event(player, key, "losekey", 0, 0);
-			bprint(player.netname, "^7 lost the ", key.netname, "\n");
+			bprint(player.netname, "^7 died and lost the ", key.netname, "\n");
 			kh_Key_AssignTo(key, world);
 			makevectors('-1 0 0' * (45 + 45 * random()) + '0 360 0' * random());
 			key.velocity = W_CalculateProjectileVelocity(player.velocity, cvar("g_balance_keyhunt_dropvelocity") * v_forward);
@@ -426,7 +473,7 @@
 	}
 }
 
-void kh_Key_Touch()
+void kh_Key_Touch()  // runs many, many times when a key has been dropped and can be picked up
 {
 	if(intermission_running)
 		return;
@@ -439,17 +486,26 @@
 		return;
 	if(other == self.enemy)
 		if(time < self.kh_droptime + cvar("g_balance_keyhunt_delay_collect"))
-			return; // you just dropped it!
+			return;  // you just dropped it!
 	kh_Key_Collect(self, other);
 }
 
-void kh_Key_Think()
+void kh_Key_Think()  // runs all the time
 {
 	entity head;
+	entity player;  // needed by FOR_EACH_PLAYER
 
 	if(intermission_running)
 		return;
 
+	if(time > self.stuff_time)
+	{
+		FOR_EACH_PLAYER(player)
+			stuffcmd(player, strcat("set kh_keys_status ", ftos(test[0]), "\n"));  // send key status to all players
+
+		self.stuff_time = time + 1;  // repeat in 1 second
+	}
+
 #ifdef KH_KEY_ATTACHMENT_DEBUG
 	if(self.kh_prev == self.owner)
 	{
@@ -472,7 +528,7 @@
 		if(time >= self.owner.kh_droptime + cvar("g_balance_keyhunt_delay_drop"))
 		{
 			self.owner.kh_droptime = time;
-			self.kh_droptime = time; // prevent collecting this one for some time
+			self.kh_droptime = time;  // prevent collecting this one for some time
 			self.enemy = self.owner;
 			self.pusher = world;
 			kh_Scores_Event(self.owner, self, "dropkey", 0, 0);
@@ -494,6 +550,12 @@
 	if(self.owner)
 	if(kh_Key_AllOwnedByWhichTeam() != -1)
 	{
+		if(self.siren_time < time)
+		{
+			sound(world, CHAN_AUTO, kh_sound_alarm, 1, ATTN_NORM);  // play a simple alarm
+			self.siren_time = time + 2.5;  // repeat every 2.5 seconds
+		}
+
 		entity key;
 		vector p;
 		p = self.owner.origin;
@@ -522,7 +584,7 @@
 	self.nextthink = time + 0.05;
 }
 
-void kh_WinnerTeam(float teem)
+void kh_WinnerTeam(float teem)  // runs when a team wins
 {
 	// all key carriers get some points
 	vector firstorigin, lastorigin, midpoint;
@@ -569,13 +631,13 @@
 		te_lightning2(world, lastorigin, firstorigin);
 	}
 	midpoint = midpoint * (1 / kh_teams);
-	te_customflash(midpoint, 1000, 1, TeamColor(teem) * 0.5 + '0.5 0.5 0.5'); // make the color >=0.5 in each component
+	te_customflash(midpoint, 1000, 1, TeamColor(teem) * 0.5 + '0.5 0.5 0.5');  // make the color >=0.5 in each component
 
 	sound(world, CHAN_AUTO, kh_sound_capture, 1, ATTN_NONE);
 	kh_FinishRound();
 }
 
-void kh_LoserTeam(float teem, entity lostkey)
+void kh_LoserTeam(float teem, entity lostkey)  // runs when a player pushes a flag carrier off the map
 {
 	entity player, key, attacker;
 	float players;
@@ -655,7 +717,7 @@
 	kh_FinishRound();
 }
 
-void kh_FinishRound()
+void kh_FinishRound()  // runs when a team captures the keys
 {
 	// prepare next round
 	kh_interferemsg_time = 0;
@@ -666,7 +728,7 @@
 	kh_Controller_SetThink(cvar("g_balance_keyhunt_delay_round"), "Round starts in ", kh_StartRound);
 }
 
-string kh_CheckEnoughPlayers()
+string kh_CheckEnoughPlayers()  // checks enough player are present, runs after every completed round
 {
 	float i, players, teem;
 	entity player;
@@ -693,7 +755,7 @@
 	return result;
 }
 
-void kh_WaitForPlayers()
+void kh_WaitForPlayers()  // delay start of the round until enough players are present
 {
 	string teams_missing;
 	teams_missing = kh_CheckEnoughPlayers();
@@ -703,7 +765,7 @@
 		kh_Controller_SetThink(1, strcat("Waiting for players to join...\n\nNeed active players for: ", teams_missing), kh_WaitForPlayers);
 }
 
-void kh_StartRound()
+void kh_StartRound()  // runs at the start of each round
 {
 	string teams_missing;
 	float i, players, teem;
@@ -743,12 +805,12 @@
 	kh_Controller_SetThink(cvar("g_balance_keyhunt_delay_tracking"), "Scanning frequency range...", kh_EnableTrackingDevice);
 }
 
-void kh_setstatus()
+void kh_setstatus()  // runs all the time
 {
 	if(kh_teams)
 	{
 		float kh_KEY;
-		kh_KEY = (IT_RED_FLAG_TAKEN | IT_RED_FLAG_LOST | IT_BLUE_FLAG_TAKEN | IT_BLUE_FLAG_LOST); // the one impossible combination
+		kh_KEY = (IT_RED_FLAG_TAKEN | IT_RED_FLAG_LOST | IT_BLUE_FLAG_TAKEN | IT_BLUE_FLAG_LOST);  // the one impossible combination
 		if(self.kh_next)
 			self.items = self.items | kh_KEY;
 		else
@@ -756,7 +818,7 @@
 	}
 }
 
-void kh_EnableTrackingDevice()
+void kh_EnableTrackingDevice()  // runs after each round
 {
 	entity player;
 
@@ -767,7 +829,7 @@
 	kh_tracking_enabled = TRUE;
 }
 
-float kh_Key_waypointsprite_for_player(entity e)
+float kh_Key_waypointsprite_for_player(entity e) // ??
 {
 	if(!kh_tracking_enabled)
 		return 0;
@@ -775,10 +837,10 @@
 		return kh_sprite_dropped;
 	if(!self.owner.owner)
 		return kh_sprite_dropped;
-	return 0; // draw only when key is not owned
+	return 0;  // draw only when key is not owned
 }
 
-float kh_KeyCarrier_waypointsprite_for_player(entity e)
+float kh_KeyCarrier_waypointsprite_for_player(entity e)  // runs all the time
 {
 	if(e.classname != "player" || self.team != e.team)
 		if(!kh_tracking_enabled)
@@ -800,7 +862,7 @@
 	return kh_GetCarrierSprite(self.team, e.team);
 }
 
-float kh_HandleFrags(entity attacker, entity targ, float f)
+float kh_HandleFrags(entity attacker, entity targ, float f)  // adds to the player score
 {
 	if(attacker == targ)
 		return f;
@@ -825,12 +887,13 @@
 	return f;
 }
 
-void kh_init()
+void kh_init()  // sets up th KH environment
 {
 	precache_sound(kh_sound_capture);
 	precache_sound(kh_sound_destroy);
 	precache_sound(kh_sound_drop);
 	precache_sound(kh_sound_collect);
+	precache_sound(kh_sound_alarm);  // the new siren
 
 	precache_model("models/sprites/key-dropped.sp2");
 	precache_model("models/sprites/keycarrier-finish.sp2");
@@ -892,3 +955,35 @@
 	remove(kh_controller);
 	kh_controller = world;
 }
+
+void kh_show_temp()
+{
+	entity player;
+	float i, j;
+
+	switch(kh_teams)
+	{
+		case 2:
+			j = 3;
+			break;
+		case 3:
+			j = 7;
+			break;
+		case 4:
+			j = 15;
+			break;
+		case 5:
+			j = 31;
+			break;
+	}
+
+	j = 0;  // reset/blank j
+	for(i=1; i<17; ++i)  // replace 17 with cvar("maxplayers"); !!!!!!!!!
+		j += test[i];
+
+	test[0] = j;
+
+	FOR_EACH_PLAYER(player)
+		stuffcmd(player, strcat("set kh_keys_status ", ftos(test[0]), "\n"));  // send key status to all players
+
+}
\ No newline at end of file

Modified: trunk/data/qcsrc/server/keyhunt.qh
===================================================================
--- trunk/data/qcsrc/server/keyhunt.qh	2008-07-03 13:25:33 UTC (rev 3773)
+++ trunk/data/qcsrc/server/keyhunt.qh	2008-07-04 14:56:23 UTC (rev 3774)
@@ -25,6 +25,8 @@
 float kh_HandleFrags(entity attacker, entity targ, float f);
 float kh_Key_AllOwnedByWhichTeam();
 
+void kh_show_temp();  // added by victim
+
 #define STR_ITEM_KH_KEY "item_kh_key"
 typedef void(void) kh_Think_t;
 var kh_Think_t kh_Controller_Thinkfunc;




More information about the nexuiz-commits mailing list