r3991 - in branches/nexuiz-2.0: . Docs Docs/server/rcon2irc data data/gfx data/maps data/qcsrc/client data/qcsrc/common data/qcsrc/menu data/qcsrc/menu/nexuiz data/qcsrc/server misc misc/gtkradiant misc/gtkradiant/singlepatches

DONOTREPLY at icculus.org DONOTREPLY at icculus.org
Thu Jul 31 02:23:51 EDT 2008


Author: div0
Date: 2008-07-31 02:23:51 -0400 (Thu, 31 Jul 2008)
New Revision: 3991

Added:
   branches/nexuiz-2.0/data/gfx/vera-sans-big.tga
   branches/nexuiz-2.0/data/gfx/vera-sans-big.width
   branches/nexuiz-2.0/data/qcsrc/server/frags.txt
   branches/nexuiz-2.0/data/qcsrc/server/scores.qc
   branches/nexuiz-2.0/data/qcsrc/server/scores.qh
   branches/nexuiz-2.0/data/qcsrc/server/scores_rules.qc
   branches/nexuiz-2.0/misc/autoshader.sh
   branches/nexuiz-2.0/misc/brightspot.c
   branches/nexuiz-2.0/misc/brightspot.sh
   branches/nexuiz-2.0/misc/crc16.c
   branches/nexuiz-2.0/misc/csqcarchive.sh
   branches/nexuiz-2.0/misc/gtkradiant/singlepatches/both-obj.diff
   branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-UTlmexposure.diff
   branches/nexuiz-2.0/misc/ttf2conchars/
Removed:
   branches/nexuiz-2.0/misc/gtkradiant/singlepatches-radiant15/
   branches/nexuiz-2.0/misc/gtkradiant/singlepatches/both-obj.diff
Modified:
   branches/nexuiz-2.0/.patchsets
   branches/nexuiz-2.0/Docs/eventlog.txt
   branches/nexuiz-2.0/Docs/server/rcon2irc/rcon2irc.pl
   branches/nexuiz-2.0/data/Makefile
   branches/nexuiz-2.0/data/defaultNexuiz.cfg
   branches/nexuiz-2.0/data/gfx/vera-sans.tga
   branches/nexuiz-2.0/data/gfx/vera-sans.width
   branches/nexuiz-2.0/data/maps/silvercity.mapinfo
   branches/nexuiz-2.0/data/maps/starship.mapinfo
   branches/nexuiz-2.0/data/qcsrc/client/Defs.qc
   branches/nexuiz-2.0/data/qcsrc/client/Main.qc
   branches/nexuiz-2.0/data/qcsrc/client/View.qc
   branches/nexuiz-2.0/data/qcsrc/client/csqc_constants.qc
   branches/nexuiz-2.0/data/qcsrc/client/ctf.qc
   branches/nexuiz-2.0/data/qcsrc/client/main.qh
   branches/nexuiz-2.0/data/qcsrc/client/mapvoting.qc
   branches/nexuiz-2.0/data/qcsrc/client/miscfunctions.qc
   branches/nexuiz-2.0/data/qcsrc/client/ons.qc
   branches/nexuiz-2.0/data/qcsrc/client/progs.src
   branches/nexuiz-2.0/data/qcsrc/client/sbar.qc
   branches/nexuiz-2.0/data/qcsrc/client/sortlist.qc
   branches/nexuiz-2.0/data/qcsrc/client/teamplay.qc
   branches/nexuiz-2.0/data/qcsrc/common/constants.qh
   branches/nexuiz-2.0/data/qcsrc/common/mapinfo.qc
   branches/nexuiz-2.0/data/qcsrc/common/util.qc
   branches/nexuiz-2.0/data/qcsrc/common/util.qh
   branches/nexuiz-2.0/data/qcsrc/menu/nexuiz/serverlist.c
   branches/nexuiz-2.0/data/qcsrc/menu/skin.qh
   branches/nexuiz-2.0/data/qcsrc/server/arena.qc
   branches/nexuiz-2.0/data/qcsrc/server/cl_client.qc
   branches/nexuiz-2.0/data/qcsrc/server/cl_player.qc
   branches/nexuiz-2.0/data/qcsrc/server/cl_weapons.qc
   branches/nexuiz-2.0/data/qcsrc/server/clientcommands.qc
   branches/nexuiz-2.0/data/qcsrc/server/ctf.qc
   branches/nexuiz-2.0/data/qcsrc/server/defs.qh
   branches/nexuiz-2.0/data/qcsrc/server/domination.qc
   branches/nexuiz-2.0/data/qcsrc/server/ent_cs.qc
   branches/nexuiz-2.0/data/qcsrc/server/g_damage.qc
   branches/nexuiz-2.0/data/qcsrc/server/g_world.qc
   branches/nexuiz-2.0/data/qcsrc/server/gamecommand.qc
   branches/nexuiz-2.0/data/qcsrc/server/keyhunt.qc
   branches/nexuiz-2.0/data/qcsrc/server/miscfunctions.qc
   branches/nexuiz-2.0/data/qcsrc/server/progs.src
   branches/nexuiz-2.0/data/qcsrc/server/teamplay.qc
   branches/nexuiz-2.0/misc/gtkradiant/gtkradiant-nexuiz-patchset.diff
   branches/nexuiz-2.0/misc/gtkradiant/mergepatches.sh
   branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-UTavgcolorfix.diff
   branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-UTfloodlight.diff
   branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-UTtrianglecheck.diff
   branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-decomptexcoords.diff
   branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-snapplane.diff
Log:
rcon2irc connection error fix
new csqc scoreboard (game mode generic)
Remove ctf support for starship. It's not symmetric.
ctf on silvercity? no thanks.
ctf no longer uses capturelimit
add RestoreGame() hook to fix loaded games (they no longer erase server.db)
enable aggressive fteqcc optimizations (requires a CURRENT fteqcc!)
add a big variant of the font; fix minor issues in the regular one
add tool to make an archive of all csqc files
add cvar cl_readpicture_force
fix map vote time sync issues... ARGH
new scores log format support
GtkRadiant patchset fixed for current svn
script to automatically create shaders for textures with alpha channel, and sky


Modified: branches/nexuiz-2.0/.patchsets
===================================================================
--- branches/nexuiz-2.0/.patchsets	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/.patchsets	2008-07-31 06:23:51 UTC (rev 3991)
@@ -1,2 +1,2 @@
 master = svn://svn.icculus.org/nexuiz/trunk
-revisions_applied = 1-3887
+revisions_applied = 1-3990

Modified: branches/nexuiz-2.0/Docs/eventlog.txt
===================================================================
--- branches/nexuiz-2.0/Docs/eventlog.txt	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/Docs/eventlog.txt	2008-07-31 06:23:51 UTC (rev 3991)
@@ -40,8 +40,11 @@
    :ctf:returned:<flagcolor>
    :dom:taken:<previouscolor>:<ID of player>
    :scores:<gametype>_<mapname>:<map runtime>
-   :player:<frags>:<deaths>:<playtime>:<team>:<ID>:<nickname>
-   :teamscores:<team1color>:<team1score>:<team2color>:<team2score>:...
+   :labels:player:<head1><flags>,<head2><flags>,...
+   :player:see-labels:<score1>,<score2>,...:<playtime>:<team>:<ID>:<nickname>
+   :player:see-labels:<score1>,<score2>,...:<playtime>:spectator:<ID>:<nickname>
+   :labels:teamscores:<head1><flags>,<head2><flags>,...
+   :teamscores:see-labels:<score1>,<score2>,...:<team>
    :end
    :restart
    :gameover
@@ -70,6 +73,13 @@
    10 = Pink Team
    13 = Yellow Team
 
+label flags:
+   !! = primary sorting key
+   <!! = primary sorting key, lower is better
+   ! = secondary sorting key
+   <! = secondary sorting key, lower is better
+   < = lower is better
+
 itemstring:
    <weaponid><flags>
    or

Modified: branches/nexuiz-2.0/Docs/server/rcon2irc/rcon2irc.pl
===================================================================
--- branches/nexuiz-2.0/Docs/server/rcon2irc/rcon2irc.pl	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/Docs/server/rcon2irc/rcon2irc.pl	2008-07-31 06:23:51 UTC (rev 3991)
@@ -693,13 +693,6 @@
 	push @tasks, [time() + $time, $sub];
 }
 
-# Build up an IO::Select object for all our channels.
-my $s = IO::Select->new();
-for my $chan(values %channels)
-{
-	$s->add($_) for $chan->fds();
-}
-
 # On IRC error, delete some data store variables of the connection, and
 # reconnect to the IRC server soon (but only if someone is actually playing)
 sub irc_error()
@@ -1104,7 +1097,7 @@
 		return 0;
 	} ],
 
-	# scores: Nexuiz server -> IRC channel
+	# scores: Nexuiz server -> IRC channel, legacy format
 	[ dp => q{:player:(-?\d+):(\d+):(\d+):(\d+):(\d+):(.*)} => sub {
 		my ($frags, $deaths, $time, $team, $id, $name) = @_;
 		return if not exists $store{scores};
@@ -1113,7 +1106,7 @@
 		return 0;
 	} ],
 
-	# scores: Nexuiz server -> IRC channel (CTF)
+	# scores: Nexuiz server -> IRC channel (CTF), legacy format
 	[ dp => q{:teamscores:(\d+:-?\d*(?::\d+:-?\d*)*)} => sub {
 		my ($teams) = @_;
 		return if not exists $store{scores};
@@ -1121,6 +1114,22 @@
 		return 0;
 	} ],
 
+	# scores: Nexuiz server -> IRC channel, new format
+	[ dp => q{:player:see-labels:(\d+)[-0-9,]*:(\d+):(\d+):(\d+):(.*)} => sub {
+		my ($frags, $time, $team, $id, $name) = @_;
+		return if not exists $store{scores};
+		push @{$store{scores}{players}}, [$frags, $team, $name];
+		return 0;
+	} ],
+
+	# scores: Nexuiz server -> IRC channel (CTF), new format
+	[ dp => q{:teamscores:see-labels:(\d+)[-0-9,]*:(\d+)} => sub {
+		my ($frags, $team) = @_;
+		return if not exists $store{scores};
+		$store{scores}{teams}{$team} = $frags;
+		return 0;
+	} ],
+
 	# scores: Nexuiz server -> IRC channel
 	[ dp => q{:end} => sub {
 		return if not exists $store{scores};
@@ -1316,6 +1325,13 @@
 # Main loop.
 for(;;)
 {
+	# Build up an IO::Select object for all our channels.
+	my $s = IO::Select->new();
+	for my $chan(values %channels)
+	{
+		$s->add($_) for $chan->fds();
+	}
+
 	# wait for something to happen on our sockets, or wait 2 seconds without anything happening there
 	$s->can_read(2);
 	my @errors = $s->has_exception(0);

Modified: branches/nexuiz-2.0/data/Makefile
===================================================================
--- branches/nexuiz-2.0/data/Makefile	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/Makefile	2008-07-31 06:23:51 UTC (rev 3991)
@@ -3,13 +3,14 @@
 PK3NAME ?= `date +../data%Y%m%d.pk3`
 ZIP ?= 7za a -tzip -mx=9
 
+FTEQCCFLAGS ?= -Wall -Wno-mundane -O3 -flo
+FTEQCCFLAGS_PROGS ?= 
+FTEQCCFLAGS_MENU ?= 
+
 # NOTE: use -DUSE_FTE instead of -TFTE here!
 # It will automagically add an engine check with -TID and then change back to -TFTE
-QCFLAGS_CSQC ?= 
+FTEQCCFLAGS_CSPROGS ?= 
 
-# to be enabled when possible
-# QCFLAGS_SVQC ?= -TFTE
-
 all: qc
 
 .PHONY: qc
@@ -26,13 +27,13 @@
 	rm -f progs.dat menu.dat csprogs.dat
 
 csprogs.dat: qcsrc/client/*.* qcsrc/common/*.*
-	( cd qcsrc/client; $(FTEQCC) $(QCFLAGS_CSQC) )
+	cd qcsrc/client && $(FTEQCC) $(FTEQCCFLAGS) $(QCFLAGS_CSPROGS)
 
 progs.dat: qcsrc/server/*.* qcsrc/common/*.*
-	( cd qcsrc/server; $(FTEQCC) $(QCFLAGS_SVQC) )
+	cd qcsrc/server && $(FTEQCC) $(FTEQCCFLAGS) $(QCFLAGS_PROGS)
 
 menu.dat: qcsrc/menu/*.* qcsrc/menu/*/*.* qcsrc/common/*.*
-	( cd qcsrc/menu; $(FTEQCC) )
+	cd qcsrc/menu && $(FTEQCC) $(FTEQCCFLAGS) $(FTEQCCFLAGS_MENU)
 
 gfx/menu/default/skinvalues.txt: qcsrc/menu/skin-customizables.inc
 	$(PERL) qcsrc/menu/skin-customizables.inc > gfx/menu/default/skinvalues.txt

Modified: branches/nexuiz-2.0/data/defaultNexuiz.cfg
===================================================================
--- branches/nexuiz-2.0/data/defaultNexuiz.cfg	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/defaultNexuiz.cfg	2008-07-31 06:23:51 UTC (rev 3991)
@@ -325,7 +325,7 @@
 seta g_runematch_point_limit -1
 seta g_keyhunt_point_limit -1
 
-seta g_ctf_win_mode 2 // 0: captures only, 1: captures, points if equal captures, 2: points
+seta g_ctf_win_mode 2 // 0: captures only, 1: captures, then points, 2: points only
 
 // 50% of the spawns shall be far away from any players
 set g_spawn_furthest 0.5
@@ -445,7 +445,6 @@
 set g_domination_default_teams		2 // default number of teams for maps that aren't domination-specific
 set g_domination_disable_frags		0 // players can't get frags normally, only get points from kills
 set g_domination_point_amt		0 // override: how many points to get per ping
-set g_domination_point_teamamt	0 // override: how many team points to get per ping
 set g_domination_point_rate		0 // override: how often to give those points
 set g_domination_point_capturetime	0.1 // how long it takes to capture a point (given no interference)
 set g_domination_point_glow		0 // domination point glow (warning, slow)
@@ -1055,7 +1054,8 @@
 
 // sbar: font size
 set sbar_fontsize 11
-alias sbar_font "loadfont user1 $*; sbar_columns_set"
+alias sbar_font "loadfont user1 $*; loadfont user2 ${*}-big; sbar_columns_set"
+seta sbar_columns default
 sbar_font gfx/vera-sans
 
 // these entities are not referenced by anything directly, they just represent

Copied: branches/nexuiz-2.0/data/gfx/vera-sans-big.tga (from rev 3990, trunk/data/gfx/vera-sans-big.tga)
===================================================================
(Binary files differ)

Copied: branches/nexuiz-2.0/data/gfx/vera-sans-big.width (from rev 3990, trunk/data/gfx/vera-sans-big.width)
===================================================================
--- branches/nexuiz-2.0/data/gfx/vera-sans-big.width	                        (rev 0)
+++ branches/nexuiz-2.0/data/gfx/vera-sans-big.width	2008-07-31 06:23:51 UTC (rev 3991)
@@ -0,0 +1,17 @@
+extraspacing 0.000000 
+0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 
+0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 
+0.359375 0.453125 0.500000 0.765625 0.656250 0.906250 0.796875 0.328125 0.453125 0.453125 0.500000 0.765625 0.390625 0.406250 0.390625 0.375000 
+0.656250 0.656250 0.656250 0.656250 0.656250 0.656250 0.656250 0.656250 0.656250 0.656250 0.406250 0.406250 0.765625 0.765625 0.765625 0.546875 
+0.906250 0.718750 0.703125 0.687500 0.765625 0.640625 0.640625 0.750000 0.765625 0.375000 0.421875 0.750000 0.593750 0.906250 0.765625 0.781250 
+0.687500 0.781250 0.718750 0.671875 0.640625 0.750000 0.718750 1.000000 0.718750 0.687500 0.671875 0.453125 0.375000 0.453125 0.765625 0.484375 
+0.484375 0.625000 0.671875 0.562500 0.671875 0.640625 0.437500 0.671875 0.656250 0.359375 0.375000 0.640625 0.359375 0.937500 0.656250 0.640625 
+0.671875 0.671875 0.484375 0.562500 0.468750 0.656250 0.609375 0.843750 0.609375 0.609375 0.546875 0.656250 0.375000 0.656250 0.765625 0.765625 
+0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 
+0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 
+0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 
+0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 
+0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 
+0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 
+0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 
+0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 0.765625 

Modified: branches/nexuiz-2.0/data/gfx/vera-sans.tga
===================================================================
(Binary files differ)

Modified: branches/nexuiz-2.0/data/gfx/vera-sans.width
===================================================================
--- branches/nexuiz-2.0/data/gfx/vera-sans.width	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/gfx/vera-sans.width	2008-07-31 06:23:51 UTC (rev 3991)
@@ -1,12 +1,12 @@
 extraspacing 0.000000 
-0.125000 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 
 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 
+0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 
 0.406250 0.484375 0.546875 0.796875 0.671875 0.921875 0.812500 0.375000 0.484375 0.484375 0.546875 0.796875 0.421875 0.453125 0.421875 0.421875 
 0.671875 0.671875 0.671875 0.671875 0.671875 0.671875 0.671875 0.671875 0.671875 0.671875 0.437500 0.437500 0.796875 0.796875 0.796875 0.593750 
 0.921875 0.734375 0.734375 0.703125 0.781250 0.671875 0.671875 0.781250 0.796875 0.421875 0.468750 0.765625 0.640625 0.921875 0.796875 0.796875 
 0.703125 0.796875 0.734375 0.703125 0.671875 0.765625 0.734375 1.000000 0.734375 0.718750 0.703125 0.484375 0.421875 0.484375 0.796875 0.531250 
 0.531250 0.656250 0.703125 0.593750 0.703125 0.671875 0.468750 0.703125 0.687500 0.390625 0.421875 0.671875 0.390625 0.953125 0.687500 0.671875 
-0.703125 0.703125 0.515625 0.593750 0.500000 0.687500 0.640625 0.859375 0.640625 0.640625 0.593750 0.687500 0.421875 0.687500 0.796875 1.000000 
+0.703125 0.703125 0.515625 0.593750 0.500000 0.687500 0.640625 0.859375 0.640625 0.640625 0.593750 0.687500 0.421875 0.687500 0.796875 0.718750 
 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 
 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 
 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 0.718750 

Modified: branches/nexuiz-2.0/data/maps/silvercity.mapinfo
===================================================================
--- branches/nexuiz-2.0/data/maps/silvercity.mapinfo	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/maps/silvercity.mapinfo	2008-07-31 06:23:51 UTC (rev 3991)
@@ -7,7 +7,6 @@
 type dm 30 20
 type tdm 50 20 2
 type dom 200 20
-type ctf 300 20
 type rune 200 20
 type lms 9 20
 type kh 1000 20 3

Modified: branches/nexuiz-2.0/data/maps/starship.mapinfo
===================================================================
--- branches/nexuiz-2.0/data/maps/starship.mapinfo	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/maps/starship.mapinfo	2008-07-31 06:23:51 UTC (rev 3991)
@@ -6,7 +6,6 @@
 has weapons
 type dm 30 20
 type dom 200 20
-type ctf 300 20
 type rune 200 20
 type lms 9 20
 type arena 10 20

Modified: branches/nexuiz-2.0/data/qcsrc/client/Defs.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/Defs.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/client/Defs.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -167,6 +167,10 @@
 // Basic variables
 .float enttype; // entity type sent from server
 .float sv_entnum; // entity number sent from server
+.float team;
+.float team_size;
+
 float vid_conwidth, vid_conheight;
 float caps_team1, caps_team2;
 float configdb;
+string shortmapname;

Modified: branches/nexuiz-2.0/data/qcsrc/client/Main.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/Main.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/client/Main.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -4,7 +4,7 @@
 
 void() menu_show_error =
 {
-	drawstring('0 200', "ERROR - MENU IS VISIBLE BUT NO MENU WAS DEFINED!", '8 8 0', '1 0 0', 1, 0);
+	drawstring('0 200 0', "ERROR - MENU IS VISIBLE BUT NO MENU WAS DEFINED!", '8 8 0', '1 0 0', 1, 0);
 };
 
 // CSQC_Init : Called every time the CSQC code is initialized (essentially at map load)
@@ -56,7 +56,7 @@
 	menu_action = menu_sub_null;
 	using_gps = false;
 	//ctf_temp_1 = "";
-	localcmd("alias order \"cmd order $*\"");
+	// localcmd("alias order \"cmd order $*\""); enable if ctf-command thingy is used
 	//registercmd("ctf_menu");
 	registercmd("ons_map");
 	//registercmd("menu_action");
@@ -64,7 +64,7 @@
 	registercmd("sbar_columns_help");
 
 	registercvar("sbar_usecsqc", "1");
-	registercvar("sbar_columns", "ping name | caps returns frags deaths", CVAR_SAVE);
+	registercvar("sbar_columns", "default", CVAR_SAVE);
 
 	gametype = 0;
 
@@ -76,7 +76,10 @@
 
 	postinit = false;
 
-	Sbar_Init();
+	teams = Sort_Spawn();
+	players = Sort_Spawn();
+	
+	teamspec = AddTeam(COLOR_SPECTATOR); // add specs first
 }
 
 // CSQC_Shutdown : Called every time the CSQC code is shutdown (changing maps, quitting, etc)
@@ -88,6 +91,9 @@
 		return 0;
 #pragma TARGET fte
 #endif
+
+	remove(teams);
+	remove(players);
 	db_close(configdb);
 	buf_del(databuf);
 }
@@ -119,7 +125,8 @@
 {
 	float argc;
 	// Tokenize String
-	argc = tokenize(strMessage);
+	//argc = tokenize(strMessage);
+	argc = tokenizebyseparator(strMessage, " ");
 	
 	// Acquire Command
 	local string strCmd;
@@ -185,7 +192,7 @@
 
 // --------------------------------------------------------------------------
 // BEGIN OPTIONAL CSQC FUNCTIONS
-void ReadONS()
+void Ent_ReadONS()
 {
 	entity gps;
 	using_gps = true;
@@ -207,7 +214,7 @@
 	}
 }
 
-void RemoveONS()
+void Ent_RemoveONS()
 {
 	if(gps_start == self)
 		gps_start = self.chain;
@@ -223,6 +230,72 @@
 	}
 }
 
+void Gamemode_Init();
+void Ent_ReadScoresInfo()
+{
+	float i;
+	gametype = ReadByte();
+	for(i = 0; i < MAX_SCORE; ++i)
+	{
+		scores_label[i] = strzone(ReadString());
+		scores_flags[i] = ReadByte();
+	}
+	for(i = 0; i < MAX_TEAMSCORE; ++i)
+	{
+		teamscores_label[i] = strzone(ReadString());
+		teamscores_flags[i] = ReadByte();
+	}
+	Sbar_InitScores();
+	Gamemode_Init();
+}
+
+void Ent_ReadPlayerScore(float isNew)
+{
+	float i, Team;
+	entity tm;
+
+	// damnit -.- don't want to go change every single .sv_entnum in sbar.qc AGAIN
+	// (no I've never heard of M-x replace-string, sed, or anything like that)
+	self.sv_entnum = ReadByte()-1;
+	Team = GetPlayerColor(self.sv_entnum);
+
+	if(isNew)
+		RegisterPlayer(self);
+
+	if(isNew || Team != self.team)
+	{
+		if(!isNew)
+		{
+			tm = GetTeam(self.team, false);
+			tm.team_size -= 1;
+		}
+		
+		self.team = Team;
+		tm = GetTeam(Team, true);
+		tm.team_size += 1;
+	}
+
+	for(i = 0; i < MAX_SCORE; ++i)
+		self.(scores[i]) = ReadShort();
+
+	Sbar_UpdatePlayerPos(self);
+}
+
+void Ent_ReadTeamScore(float isNew)
+{
+	float i;
+	
+	self.team = ReadByte();
+
+	if(isNew)
+		RegisterTeam(self);
+
+	for(i = 0; i < MAX_TEAMSCORE; ++i)
+		self.(teamscores[i]) = ReadShort();
+
+	Sbar_UpdateTeamPos(self);
+}
+
 // CSQC_Ent_Update : Called every frame that the server has indicated an update to the SSQC / CSQC entity has occured.
 // The only parameter reflects if the entity is "new" to the client, meaning it just came into the client's PVS.
 void(float bIsNewEntity) CSQC_Ent_Update =
@@ -237,13 +310,19 @@
 		{
 			switch(msg)
 			{
-			case ENTCS_MSG_ONS_GPS: ReadONS(); break;
-			case ENTCS_MSG_ONS_REMOVE: RemoveONS(); break;
+			case ENTCS_MSG_ONS_GPS: Ent_ReadONS(); break;
+			case ENTCS_MSG_ONS_REMOVE: Ent_RemoveONS(); break;
 			default:
 				error("unknown ENTCS_MSG type\n");
 			}
 		}
 	}
+	else if(self.enttype == ENT_CLIENT_SCORES_INFO)
+		Ent_ReadScoresInfo();
+	else if(self.enttype == ENT_CLIENT_SCORES)
+		Ent_ReadPlayerScore(bIsNewEntity);
+	else if(self.enttype == ENT_CLIENT_TEAMSCORES)
+		Ent_ReadTeamScore(bIsNewEntity);
 	else
 		error("unknown entity type in CSQC_Ent_Update\n");
 	
@@ -268,6 +347,20 @@
 					ent.chain = self.chain;
 			}
 		}
+	} else if(self.enttype == ENT_CLIENT_SCORES_INFO)
+	{
+		// OH NOES!! WE LOST DA SCORES INFO ENTITY
+		print("The world is going to explode.");
+		// kkthxbai
+	} else if(self.enttype == ENT_CLIENT_SCORES)
+	{
+		entity tm;
+		tm = GetTeam(self.team, false);
+		tm.team_size -= 1;
+		RemovePlayer(self);
+	} else if(self.enttype == ENT_CLIENT_TEAMSCORES)
+	{
+		RemoveTeam(self);
 	}
 	remove(self);
 }
@@ -279,18 +372,18 @@
 	local float file;
 	local vector mi_min, mi_max;
 
-	gametype = cvar("gametype");
+	if(!strcasecmp(substring(mapname, 0, 5), "maps/"))
+		shortmapname = substring(mapname, 5, 999);
+	else
+		shortmapname = mapname;
+	len = strlen(shortmapname);
+	if(!strcasecmp(substring(shortmapname, len-4, 4), ".bsp"))
+		shortmapname = substring(shortmapname, 0, len-4);
+	shortmapname = strzone(shortmapname);
+
 	if(gametype == GAME_ONSLAUGHT) {
-		if(!strcasecmp(substring(mapname, 0, 5), "maps/"))
-			minimapname = substring(mapname, 5, 999);
-		else
-			minimapname = mapname;
-		len = strlen(minimapname);
-		if(!strcasecmp(substring(minimapname, len-4, 4), ".bsp"))
-			minimapname = substring(minimapname, 0, len-4);
-		
 		mapinfo = strcat("maps/", minimapname, ".info");
-		minimapname = strzone(strcat("gfx/", minimapname, "_mini.tga"));
+		minimapname = strzone(strcat("gfx/", shortmapname, "_mini.tga"));
 
 		mi_min = world.mins;
 		mi_max = world.maxs;
@@ -333,11 +426,6 @@
 void CSQC_Parse_StuffCmd(string strMessage)
 {
 	localcmd(strMessage);
-	// watch for gametype changes!
-	if(gametype != cvar("gametype"))
-	{
-		Gamemode_Init();
-	}
 }
 // 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)

Modified: branches/nexuiz-2.0/data/qcsrc/client/View.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/View.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/client/View.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -5,8 +5,12 @@
 void CSQC_kh_hud(void);
 void CSQC_ctf_hud(void);
 void PostInit(void);
+float Sbar_WouldDrawScoreboard ();
 void CSQC_UpdateView(void)
 {
+	// watch for gametype changes here...
+	// in ParseStuffCMD the cmd isn't executed yet :/
+	// might even be better to add the gametype to TE_CSQC_INIT...?
 	if(!postinit)
 		PostInit();
 	
@@ -28,7 +32,7 @@
 	R_SetView(VF_DRAWWORLD, 1);
 	
 	// Draw the Crosshair
-	R_SetView(VF_DRAWCROSSHAIR, 1);
+	R_SetView(VF_DRAWCROSSHAIR, !Sbar_WouldDrawScoreboard());
 	
 	// Draw the Engine Status Bar (the default Quake HUD)
 	draw_enginesbar = !cvar("sbar_usecsqc");
@@ -124,7 +128,7 @@
 	yellow = '1 1 0';
 	pink = '1 0 1';
 
-	kh_size = '0 0';  // don't resize the image
+	kh_size = '0 0 0';  // don't resize the image
 
 	kh_carrying = "gfx/sb_kh_full";
 	kh_outline = "gfx/sb_kh_outline";

Modified: branches/nexuiz-2.0/data/qcsrc/client/csqc_constants.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/csqc_constants.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/client/csqc_constants.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -59,6 +59,7 @@
 const float		STAT_ITEMS						= 15;
 const float		STAT_VIEWHEIGHT					= 16;
 
+const float		STAT_FRAGLIMIT					= 235;
 const float		STAT_TIMELIMIT					= 236;
 
 // Sound Constants

Modified: branches/nexuiz-2.0/data/qcsrc/client/ctf.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/ctf.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/client/ctf.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -25,8 +25,8 @@
 	local vector ps, po;
 	local float i, p, n;
 	local string frags, color;
-	ps = '0 200';
-	po = '0 8';
+	ps = '0 200 0';
+	po = '0 8 0';
 
 	color = getplayerkey(player_localentnum-1, "topcolor");
 	
@@ -146,14 +146,14 @@
 void() ctf_menu_render =
 {
 	local vector ps, po;
-	ps = '0 200';
-	po = '0 8';
+	ps = '0 200 0';
+	po = '0 8 0';
 	
 	if(getstati(STAT_CTF_STATE) == CTF_STATE_COMMANDER) {
 		drawstring(ps, "\x1D\x1E\x1E\x1E\x1E Command Menu \x1E\x1E\x1E\x1E\x1F", '8 8 0', '1 1 0', 1, 0); ps += po;
 		drawstring(ps, "Issue orders:", '8 8 0', '1 1 0', 1, 0); ps += po;
 		drawstring(ps, " 1) Attack ", '8 8 0', '1 1 0', 1, 0);
-		drawstring(ps + '80 0', " \x0F", '8 8 0', '1 1 1', 1, 0); ps += po;
+		drawstring(ps + '80 0 0', " \x0F", '8 8 0', '1 1 1', 1, 0); ps += po;
 		drawstring(ps, " 2) Defend \x0E", '8 8 0', '1 1 0', 1, 0); ps += po;
 		ps += po;
 		drawstring(ps, "3) Resign from command.", '8 8 0', '1 1 0', 1, 0); ps += po;
@@ -204,13 +204,13 @@
 	local float stat;
 	stat = getstati(STAT_CTF_STATE);
 	if(stat == CTF_STATE_ATTACK) {
-		drawpic('0 0', "gfx/ctf_ic_atk.tga", '64 64 0', '1 1 1', 1, 0);
+		drawpic('0 0 0', "gfx/ctf_ic_atk.tga", '64 64 0', '1 1 1', 1, 0);
 	} else if(stat == CTF_STATE_DEFEND) {
-		drawpic('0 0', "gfx/ctf_ic_def.tga", '64 64 0', '1 1 1', 1, 0);
+		drawpic('0 0 0', "gfx/ctf_ic_def.tga", '64 64 0', '1 1 1', 1, 0);
 	} else if(stat == CTF_STATE_COMMANDER) {
-		drawstring('0 0', "You're commander!", '8 8 0', '1 1 1', 1, 0);
+		drawstring('0 0 0', "You're commander!", '8 8 0', '1 1 1', 1, 0);
 	} else if(stat < 0) {
 	} else {
-		drawstring('0 0', "Awaiting orders...", '8 8 0', '1 1 1', 0.5, 0);
+		drawstring('0 0 0', "Awaiting orders...", '8 8 0', '1 1 1', 0.5, 0);
 	}
 };

Modified: branches/nexuiz-2.0/data/qcsrc/client/main.qh
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/main.qh	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/client/main.qh	2008-07-31 06:23:51 UTC (rev 3991)
@@ -67,23 +67,36 @@
 
 #define MAX_SBAR_FIELDS 16
 
-#define SBF_END 0
-#define SBF_PING 1
-#define SBF_NAME 2
-#define SBF_CAPS 3
-#define SBF_RETS 4
-#define SBF_FRAGS 5
-#define SBF_DEATHS 6
-#define SBF_KDRATIO 7
+#define SP_END -1
 
-#define SBF_SEPARATOR 100
+#define SP_PING -2
+#define SP_NAME -3
+#define SP_KDRATIO -4
+#define SP_CLRATIO -5
 
+#define SP_SEPARATOR -100
+
 float sbar_field[MAX_SBAR_FIELDS + 1];
 float sbar_size[MAX_SBAR_FIELDS + 1];
 string sbar_title[MAX_SBAR_FIELDS + 1];
 float sbar_num_fields;
+
 float sbar_font;
+float sbar_bigfont;
 
+string scores_label[MAX_SCORE];
+float scores_flags[MAX_SCORE];
+string teamscores_label[MAX_SCORE];
+float teamscores_flags[MAX_SCORE];
+.float scores[MAX_SCORE];
+.float teamscores[MAX_TEAMSCORE];
+
+#define IS_INCREASING(x) ( (x)&SFL_LOWER_IS_BETTER )
+#define IS_DECREASING(x) ( !((x)&SFL_LOWER_IS_BETTER) )
+
+
+vector sbar_fontsize;
+
 float csqc_flags;
 #define CSQC_FLAG_READPICTURE 1
 

Modified: branches/nexuiz-2.0/data/qcsrc/client/mapvoting.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/mapvoting.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/client/mapvoting.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -11,91 +11,98 @@
 float mv_detail;
 float mv_timeout;
 
-void MapVote_DrawMapItem(vector pos, float isize, string map, string pic, float count, float id)
+string MapVote_FormatMapItem(float id, string map, float count, float maxwidth)
 {
-	vector img_size;
-	vector rgb;
-	//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
+	string pre, post;
+	pre = strcat(ftos(id+1), ". ");
+	if(mv_detail)
+		post = strcat(" (", ftos(count), " votes)");
+	else
+		post = "";
+	maxwidth -= stringwidth(pre, FALSE) + stringwidth(post, FALSE);
+	map = textShortenToWidth(map, maxwidth, FALSE);
+	return strcat(pre, map, post);
+}
 
-	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;
-
+vector MapVote_RGB(float id)
+{
 	if(id == mv_ownvote)
-		rgb = '1 1 0';
+		return '1 1 0';
 	else
-		rgb = '1 1 1';
-	
-	drawstring(pos - '40 0', strcat(ftos(id+1), "."), img_size, rgb, 1, DRAWFLAG_NORMAL);
-
-	pos_x += isize/0.75 + 10;
-
-	if(mv_detail)
-		drawstring(pos, strcat(ftos(count), " : ", map), img_size, rgb, 1, DRAWFLAG_NORMAL);
-	else
-		drawstring(pos, map, img_size, rgb, 1, DRAWFLAG_NORMAL);
+		return '1 1 1';
 }
 
-void MapVote_DrawMapNotAvailable(vector pos, float isize, string map, float count, float id)
+void MapVote_DrawMapItem(vector pos, float isize, float tsize, string map, string pic, float count, float id)
 {
 	vector img_size, a, b;
 	vector rgb;
+	string label;
+	float text_size;
+	
+	isize -= sbar_fontsize_y; // respect the text when calculating the image size
+
+	rgb = MapVote_RGB(id);
+	
 	img_size_y = isize;
-	img_size_x = isize / 0.75;
+	img_size_x = isize / 0.75; // 4:3 x can be stretched easily, height is defined in isize
 
-	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);
+	drawfont = sbar_font;
+	pos_y = pos_y + img_size_y;
+	
+	label = MapVote_FormatMapItem(id, map, count, tsize / sbar_fontsize_x);
 
-	img_size_x = img_size_y = isize*0.5;
-	pos_y += img_size_y*0.5;
-
-	if(id == mv_ownvote)
-		rgb = '1 1 0';
-	else
-		rgb = '1 1 1';
+	text_size = stringwidth(label, false) * sbar_fontsize_x;
 	
-	drawstring(pos - '40 0', strcat(ftos(id+1), "."), img_size, rgb, 1, DRAWFLAG_NORMAL);
+	pos_x -= text_size*0.5;
+	drawstring(pos, label, sbar_fontsize, rgb, 1, DRAWFLAG_NORMAL);
 	
-	pos_x += isize/0.75 + 10;
+	pos_x = pos_x + text_size*0.5 - img_size_x*0.5;
+	pos_y = pos_y - img_size_y;
 
-	if(mv_detail)
-		drawstring(pos, strcat(ftos(count), " : ", map), img_size, rgb, 1, DRAWFLAG_NORMAL);
+	if(pic == "")
+	{
+		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);
+	}
 	else
-		drawstring(pos, map, img_size, rgb, 1, DRAWFLAG_NORMAL);
+	{
+		drawpic(pos, pic, img_size, '1 1 1', 1, DRAWFLAG_NORMAL);
+	}
 }
 
-void MapVote_DrawAbstain(vector pos, float isize, float count, float id)
+void MapVote_DrawAbstain(vector pos, float isize, float tsize, float count, float id)
 {
 	vector img_size;
 	vector rgb;
-	img_size_y = isize;
-	img_size_x = isize / 0.75;
+	float text_size;
+	string label;
+	
+	rgb = MapVote_RGB(id);
 
-	img_size_x = img_size_y = isize*0.5;
-	pos_y += img_size_y*0.5;
+	drawfont = sbar_font;
+	pos_y = pos_y + sbar_fontsize_y;
+	
+	label = MapVote_FormatMapItem(id, "Don't care", count, tsize / sbar_fontsize_x);
 
-	if(id == mv_ownvote)
-		rgb = '1 1 0';
-	else
-		rgb = '1 1 1';
+	text_size = stringwidth(label, false) * sbar_fontsize_x;
 	
-	drawstring(pos - '40 0', strcat(ftos(id+1), "."), img_size, rgb, 1, DRAWFLAG_NORMAL);
-	
-	pos_x += isize/0.75 + 10;
+	pos_x -= text_size*0.5;
+	drawstring(pos, label, sbar_fontsize, rgb, 1, DRAWFLAG_NORMAL);
+}
 
-	if(mv_detail)
-		drawstring(pos, strcat(ftos(count), " : Don't care"), img_size, rgb, 1, DRAWFLAG_NORMAL);
-	else
-		drawstring(pos, "Don't care", img_size, rgb, 1, DRAWFLAG_NORMAL);
+vector MapVote_GridVec(vector gridspec, float i, float m)
+{
+	float r;
+	r = mod(i, m);
+	return
+		'1 0 0' * (gridspec_x * r)
+		+
+		'0 1 0' * (gridspec_y * (i - r) / m);
 }
 
 void MapVote_Draw()
@@ -105,19 +112,21 @@
 	vector pos;
 	float isize;
 	float center;
-	float margin;
+	float columns, rows;
+	float tsize;
+	vector dist;
 	
 	center = (vid_conwidth - 1)/2;
-	xmin = vid_conwidth*0.2;
+	xmin = vid_conwidth*0.05; // 5% border must suffice
 	xmax = vid_conwidth - xmin;
-	ymin = 24;
+	ymin = 20;
 	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;
+	drawfont = sbar_bigfont;
 	sbar_fontsize = stov(cvar_string("sbar_fontsize"));
 	if(sbar_fontsize_x == 0)
 		sbar_fontsize = '8 8 0';
@@ -127,37 +136,59 @@
 	pos_y = ymin;
 	pos_z = 0;
 	pos_x = center - stringwidth("Vote for a map", false) * 0.5 * 24;
-	drawstring(pos, "Vote for a map", '24 24', '1 1 1', 1, DRAWFLAG_NORMAL);
+	drawstring(pos, "Vote for a map", '24 24 0', '1 1 1', 1, DRAWFLAG_NORMAL);
 	pos_y += 26;
 
-	i = ceil(mv_timeout - time);
+	i = ceil(max(0, mv_timeout - time));
 	map = strcat(ftos(i), " seconds left");
 	pos_x = center - stringwidth(map, false) * 0.5 * 16;
-	drawstring(pos, map, '16 16', '0 1 0', 1, DRAWFLAG_NORMAL);
+	drawstring(pos, map, '16 16 0', '0 1 0', 1, DRAWFLAG_NORMAL);
 	pos_y += 22;
 	pos_x = xmin;
+
+	drawfont = sbar_bigfont; // FIXME change this to sbar_font when it gets a SANE size later
 	
-	isize = (ymax - pos_y - mv_num_maps*10) / mv_num_maps;
-	isize = min(isize, 64);
+	// base for multi-column stuff...
+	ymin = pos_y;
+	if(mv_abstain)
+		mv_num_maps -= 1;
+	
+	if(mv_num_maps > 3)
+	{
+		columns = 3;
+	} else {
+		columns = mv_num_maps;
+	}
+	rows = ceil(mv_num_maps / columns);
 
-	for(i = 0; i < (mv_num_maps - mv_abstain); ++i)
+	dist_x = (xmax - xmin) / columns;
+	dist_y = (ymax - pos_y) / rows;
+	tsize = dist_x - 10;
+	isize = min(dist_y - 10, 0.75 * tsize);
+
+	pos_x += (xmax - xmin) / (2 * columns);
+	pos_y += (dist_y - isize) / 2;
+	ymax -= isize;
+	for(i = 0; i < mv_num_maps; ++i)
 	{
 		tmp = mv_votes[i]; // FTEQCC bug: too many array accesses in the function call screw it up
 		if(tmp < 0)
-		{
-			pos_y += isize + 10;
 			continue;
-		}
 		map = mv_maps[i];
 		if(mv_preview[i])
-			MapVote_DrawMapItem(pos, isize, map, mv_pics[i], tmp, i);
+			MapVote_DrawMapItem(pos + MapVote_GridVec(dist, i, columns), isize, tsize, map, mv_pics[i], tmp, i);
 		else
-			MapVote_DrawMapNotAvailable(pos, isize, map, tmp, i);
-		pos_y += isize + 10;
+			MapVote_DrawMapItem(pos + MapVote_GridVec(dist, i, columns), isize, tsize, map, "", tmp, i);
 	}
+
+	if(mv_abstain)
+		++mv_num_maps;
+	
 	if(mv_abstain && i < mv_num_maps) {
 		tmp = mv_votes[i];
-		MapVote_DrawAbstain(pos, isize, tmp, i);
+		pos_y = ymax + isize - sbar_fontsize_y;
+		pos_x = (xmax+xmin)*0.5;
+		MapVote_DrawAbstain(pos, isize, xmax - xmin, tmp, i);
 	}
 }
 
@@ -236,7 +267,7 @@
 	mv_detail = ReadByte();
 
 	mv_ownvote = -1;
-	mv_timeout = time + ReadByte();
+	mv_timeout = ReadCoord();
 
 	if(mv_num_maps <= 8)
 		m = ReadByte();

Modified: branches/nexuiz-2.0/data/qcsrc/client/miscfunctions.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/miscfunctions.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/client/miscfunctions.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -1,6 +1,111 @@
 float databuf;
 var float(string text, float handleColors) stringwidth;
 
+entity players;
+entity teams;
+
+float RegisterPlayer(entity player)
+{
+	entity pl;
+	for(pl = players.sort_next; pl; pl = pl.sort_next)
+		if(pl == player)
+			return false;
+	player.sort_next = players.sort_next;
+	player.sort_prev = players;
+	if(players.sort_next)
+		players.sort_next.sort_prev = player;
+	players.sort_next = player;
+	return true;
+}
+
+void RemovePlayer(entity player)
+{
+	entity pl, parent;
+	parent = players;
+	for(pl = players.sort_next; pl && pl != player; pl = pl.sort_next)
+		parent = pl;
+
+	if(!pl)
+	{
+		print("Trying to remove a player which is not in the playerlist!");
+		return;
+	}
+	parent.sort_next = player.sort_next;
+	if(player.sort_next)
+		player.sort_next.sort_prev = parent;
+}
+
+entity AddTeam(float num)
+{
+	entity tm;
+	tm = spawn();
+	tm.team = num;
+	tm.sort_next = teams.sort_next;
+	tm.sort_prev = teams;
+	if(teams.sort_next)
+		teams.sort_next.sort_prev = tm;
+	teams.sort_next = tm;
+	return tm;
+}
+
+void MoveToLast(entity e)
+{
+	other = e.sort_next;
+	while(other)
+	{
+		SORT_SWAP(other, e);
+		other = e.sort_next;
+	}
+}
+
+// warning: Local "team" defined with name of a global
+// FU FTEQCC, .float team is a ENTVAR shitty piece of crap!!!
+float RegisterTeam(entity Team)
+{
+	entity tm;
+	for(tm = teams.sort_next; tm; tm = tm.sort_next)
+		if(tm == Team)
+			return false;
+	Team.sort_next = teams.sort_next;
+	Team.sort_prev = teams;
+	if(teams.sort_next)
+		teams.sort_next.sort_prev = Team;
+	teams.sort_next = Team;
+	return true;
+}
+
+void RemoveTeam(entity Team)
+{
+	entity tm, parent;
+	parent = teams;
+	for(tm = teams.sort_next; tm && tm != Team; tm = tm.sort_next)
+		parent = tm;
+
+	if(!tm)
+	{
+		print("Trying to remove a team which is not in the teamlist!");
+		return;
+	}
+	parent.sort_next = Team.sort_next;
+	if(Team.sort_next)
+		Team.sort_next.sort_prev = parent;
+}
+
+entity GetTeam(float num, float add)
+{
+	entity tm;
+	for(tm = teams.sort_next; tm; tm = tm.sort_next)
+	{
+		if(tm.team == num)
+			return tm;
+	}
+	
+	if(add)
+		return AddTeam(num);
+	
+	return NULL;
+}
+
 float stringwidth_oldfont(string text, float handleColors)
 {
 	float i, len, ch, width;
@@ -37,10 +142,12 @@
 	{
 		stringwidth = stringwidth_engine;
 		sbar_font = FONT_USER+1;
+		sbar_bigfont = FONT_USER+2;
 		csqc_flags |= CSQC_FLAG_READPICTURE;
 	} else {
 		stringwidth = stringwidth_oldfont;
 		sbar_font = FONT_DEFAULT;
+		sbar_bigfont = FONT_DEFAULT;
 	}
 }
 
@@ -48,13 +155,17 @@
 {
 	if(csqc_flags & CSQC_FLAG_READPICTURE)
 		return stov(cvar_string("sbar_fontsize"));
-	return '8 8' ;
+	return '8 8 0' ;
 }
 
 float PreviewExists(string name)
 {
 	float f;
 	string file;
+
+	if(cvar("cl_readpicture_force"))
+		return false;
+
 	file = strcat(name, ".tga");
 	f = fopen(file, FILE_READ);
 	if(f >= 0)

Modified: branches/nexuiz-2.0/data/qcsrc/client/ons.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/ons.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/client/ons.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -11,7 +11,7 @@
 	ret_x = ret_x * 256.0 / mi_scale_x;
 	ret_y = -ret_y * 256.0 / mi_scale_y;
 	ret_z = 0;
-	ret = ret + '400 178';
+	ret = ret + '400 178 0';
 	return ret;
 };
 
@@ -26,10 +26,10 @@
 	// Ryling on !tex[0] => texture_white
 	// beware of the order, it has to be clockwise!
 	R_BeginPolygon("", 0);
-	R_PolygonVertex(coord+v_forward*2, '0 0', rgb, 1);
-	R_PolygonVertex(coord+v_right*3-v_forward*2, '0 1', rgb, 1);
-	R_PolygonVertex(coord-v_forward, '1 0', rgb, 1);
-	R_PolygonVertex(coord-v_right*3-v_forward*2, '1 1', rgb, 1);
+	R_PolygonVertex(coord+v_forward*2, '0 0 0', rgb, 1);
+	R_PolygonVertex(coord+v_right*3-v_forward*2, '0 1 0', rgb, 1);
+	R_PolygonVertex(coord-v_forward, '1 0 0', rgb, 1);
+	R_PolygonVertex(coord-v_right*3-v_forward*2, '1 1 0', rgb, 1);
 	R_EndPolygon();
 };
 
@@ -44,9 +44,9 @@
 
 		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);
-		drawpic('257 35', "gfx/ons-frame-team.tga", '286 286', rgb, 1, 0);
+		drawpic('272 50 0', minimapname, '256 256 0', '1 1 1', 1, 0);
+		drawpic('257 35 0', "gfx/ons-frame.tga", '286 286 0', '1 1 1', 1, 0);
+		drawpic('257 35 0', "gfx/ons-frame-team.tga", '286 286 0', rgb, 1, 0);
 
 		drawplayer(coord, input_angles, '1 1 1');
 

Modified: branches/nexuiz-2.0/data/qcsrc/client/progs.src
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/progs.src	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/client/progs.src	2008-07-31 06:23:51 UTC (rev 3991)
@@ -11,8 +11,8 @@
 
 main.qh
 
+sortlist.qc
 miscfunctions.qc
-sortlist.qc
 teamplay.qc
 
 ons.qc

Modified: branches/nexuiz-2.0/data/qcsrc/client/sbar.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/sbar.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/client/sbar.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -6,10 +6,9 @@
 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;
 
@@ -17,6 +16,9 @@
 .float sb_team;
 .float sb_player;
 .float sb_caps;
+*/
+float ps_primary, ps_secondary;
+float ts_primary, ts_secondary;
 
 entity team1, team2, team3, team4, teamspec;
 
@@ -30,7 +32,7 @@
 	pos_y = 16;
 	pos_z = 0;*/
 	
-	//drawpic(pos, "gfx/finale", '0 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
+	//drawpic(pos, "gfx/finale", '0 0 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
 	
 	//drawstring(pos, "END", sbar_fontsize, '1 1 1', 1, DRAWFLAG_NORMAL);
 	MapVote_Draw();
@@ -128,447 +130,429 @@
 	}
 }
 
-float Sbar_PlayerCmp(entity l, entity r)
+void Cmd_Sbar_SetFields(float argc);
+void Sbar_InitScores()
 {
-	if(teamplay)
+	float i, f, primary_prio, secondary_prio;
+
+	ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
+	for(i = 0; i < MAX_SCORE; ++i)
 	{
-		if(l.sb_team > r.sb_team)
-			return true;
-		else if(l.sb_team > r.sb_team)
-			return false;
-		if(gametype == GAME_CTF)
-		{
-			if(l.sb_caps > r.sb_caps)
-				return true;
-			else if(l.sb_caps < r.sb_caps)
-				return false;
-		}
+		f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
+		if(f == SFL_SORT_PRIO_PRIMARY)
+			ps_primary = i;
+		if(f == SFL_SORT_PRIO_SECONDARY)
+			ps_secondary = i;
 	}
-	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(gametype == GAME_CTF)
+	if(ps_secondary == -1)
+		ps_secondary = ps_primary;
+	
+	for(i = 0; i < MAX_TEAMSCORE; ++i)
 	{
-		if(l.sb_caps > r.sb_caps)
-			return true;
-		else if(l.sb_caps < r.sb_caps)
-			return false;
+		f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
+		if(f == SFL_SORT_PRIO_PRIMARY)
+			ts_primary = i;
+		if(f == SFL_SORT_PRIO_SECONDARY)
+			ts_secondary = i;
 	}
-	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);
+	if(ts_secondary == -1)
+		ts_secondary = ts_primary;
+
+	Cmd_Sbar_SetFields(0);
 }
 
-void Sbar_Init()
+void Sbar_UpdatePlayerPos(entity pl);
+void Sbar_UpdatePlayerTeams()
 {
-	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;
-}
+	float Team;
+	entity pl, tmp;
+	float num;
 
-void Sbar_UpdatePosFrags(entity player)
-{
-	other = player.sort_prev;
-	while(other != sortedPlayers && player.sb_frags > other.sb_frags)
+	num = 0;
+	for(pl = players.sort_next; pl; pl = pl.sort_next)
 	{
-		SORT_SWAP(other, player);
-		other = player.sort_prev;
-	}
+		num += 1;
+		Team = GetPlayerColor(pl.sv_entnum);
+		if(pl.team != Team)
+		{
+			tmp = GetTeam(pl.team, false);
+			tmp.team_size -= 1;
+			tmp = GetTeam(Team, true);
+			tmp.team_size += 1;
+			
+			pl.team = Team;
 
-	other = player.sort_next;
-	while(other && player.sb_frags < other.sb_frags)
-	{
-		SORT_SWAP(player, other);
-		other = player.sort_next;
+			tmp = pl.sort_prev;
+			Sbar_UpdatePlayerPos(pl);
+			if(tmp)
+				pl = tmp;
+			else
+				pl = players.sort_next;
+		}
 	}
+	//print(strcat("PNUM: ", ftos(num), "\n"));
 }
-void Sbar_UpdatePosFragsCTF(entity player)
+
+float Sbar_ComparePlayerScores(entity left, entity right)
 {
-	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;
-	}
+	float vl, vr;
+	vl = GetPlayerColor(left.sv_entnum);
+	vr = GetPlayerColor(right.sv_entnum);
+	
+	if(vl > vr)
+		return true;
+	if(vl < vr)
+		return false;
 
-	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;
-	}
+	vl = left.scores[ps_primary];
+	vr = right.scores[ps_primary];
+	if(vl > vr)
+		return IS_INCREASING(scores_flags[ps_primary]);
+	if(vl < vr)
+		return IS_DECREASING(scores_flags[ps_primary]);
+	
+	vl = left.scores[ps_secondary];
+	vr = right.scores[ps_secondary];
+	if(vl > vr)
+		return IS_INCREASING(scores_flags[ps_secondary]);
+	if(vl < vr)
+		return IS_DECREASING(scores_flags[ps_secondary]);
+	
+	return false;
 }
-void Sbar_UpdatePosCaps(entity player)
+
+void Sbar_UpdatePlayerPos(entity player)
 {
-	other = player.sort_prev;
-	while(other != sortedPlayers && player.sb_caps > other.sb_caps)
+	for(other = player.sort_next; other && Sbar_ComparePlayerScores(player, other); other = player.sort_next)
 	{
-		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)
+	for(other = player.sort_prev; other != players && Sbar_ComparePlayerScores(other, player); other = player.sort_prev)
 	{
 		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 Sbar_CompareTeamScores(entity left, entity right)
 {
-	float i;
+	float vl, vr;
 
-	if(player.sb_frags == -666)
-		i = COLOR_SPECTATOR;
-	else
-		i = GetPlayerColor(player.sb_player);
+	if(left.team == COLOR_SPECTATOR)
+		return 1;
+	if(right.team == COLOR_SPECTATOR)
+		return 0;
 	
-	if(player.sb_team != i)
-	{
-		player.sb_team = i;
-		Sbar_UpdatePosTeam(player);
-	}
+	vl = left.teamscores[ts_primary];
+	vr = right.teamscores[ts_primary];
+	if(vl > vr)
+		return IS_INCREASING(teamscores_flags[ts_primary]);
+	if(vl < vr)
+		return IS_DECREASING(teamscores_flags[ts_primary]);
 	
-	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);
-		}
-	}
+	vl = left.teamscores[ts_secondary];
+	vr = right.teamscores[ts_secondary];
+	if(vl > vr)
+		return IS_INCREASING(teamscores_flags[ts_secondary]);
+	if(vl < vr)
+		return IS_DECREASING(teamscores_flags[ts_secondary]);
+
+	return false;
 }
 
-void Sbar_UpdateTeamPosCaps(entity tm)
+void Sbar_UpdateTeamPos(entity Team)
 {
-	other = tm.sort_prev;
-	while(other != sortedTeams && tm.sb_caps > other.sb_caps)
+	for(other = Team.sort_next; other && Sbar_CompareTeamScores(Team, other); other = Team.sort_next)
 	{
-		SORT_SWAP(other, tm);
-		other = tm.sort_prev;
+		SORT_SWAP(Team, other);
 	}
-
-	other = tm.sort_next;
-	while(other && tm.sb_caps < tm.sb_caps)
+	for(other = Team.sort_prev; other != teams && Sbar_CompareTeamScores(other, Team); other = Team.sort_prev)
 	{
-		SORT_SWAP(tm, other);
-		other = tm.sort_next;
+		SORT_SWAP(other, Team);
 	}
 }
-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 == other.sb_caps && tm.sb_frags < other.sb_frags)
-	{
-		SORT_SWAP(tm, other);
-		other = tm.sort_next;
-	}
-}
-
-void Sbar_SortFrags()
-{
-	float i;
-	entity tmp;
-	float t1f, t2f, t3f, t4f;
-	
-	Sort_Reset(sortedPlayers);
-
-	numteams = 0;
-	if(teamplay)
-	{
-		Sort_Reset(sortedTeams);
-		tmp = Sort_Next(sortedTeams);
-
-		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;
-
-			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.frame = time;
-			Sbar_UpdatePlayer(tmp);
-			
-			switch(tmp.sb_team)
-			{
-			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;
-
-		if(team1.sb_caps != caps_team1)
-		{
-			team1.sb_caps = caps_team1;
-			Sbar_UpdateTeamPosCaps(team1);
-		}
-		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;
-		
-			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.frame = time;
-			Sbar_UpdatePlayer(tmp);
-		}
-	}
-	Sort_RemoveOld(sortedPlayers);
-}
-
 void Cmd_Sbar_Help(float argc)
 {
 	print("You can modify the scoreboard using the\n");
 	print("^3|---------------------------------------------------------------|\n");
-	print("^2sbar_columns^7 cvar and the ^2sbar_columns_set command.\n");
-	print("^2sbar_columns^7             specifies the default layout and\n");
-	print("^2sbar_columns_set^7         actually changes the layout.\n");
-	print("You can call ^2sbar_columns_set^7 with the new layout\n");
-	print("as parameters, or eithout parameters it will read the cvar.\n\n");
+	print("^1 TO BE DONE\n");
 	print("Usage:\n");
+	print("^2sbar_columns_set default\n");
 	print("^2sbar_columns_set ^7filed1 field2 ...\n");
-	print("Fields which are not relevant to the current gametype\n");
-	print("won't be displayed\n\n");
 	print("The following field names are recognized (case INsensitive):\n");
+	print("You can use a ^3|^7 to start the right-aligned fields.\n");
+	
 	print("^3name^7 or ^3nick^7             Name of a player\n");
-	print("^3caps^7 or ^3captures^7         Number of flags captured\n");
-	print("^3rets^7 or ^3returns^7          Number of flags returned\n");
-	print("^3frags^7 or ^3kills^7           Frags\n");
-	print("^3deaths^7 or ^3dths^7           Number of deaths\n");
+	print("^3ping^7                     Ping time\n\n");
 	print("^3kd^7 or ^3kdr^7 or ^3kdratio^7 or ^3k/d\n");
 	print("                         The kill-death ratio\n");
-	print("^3ping^7                     Ping time\n\n");
-	print("You can use a ^3|^7 to start the right-aligned fields.\n");
-	print("Example: ping name | caps rets frags k/d\n");
-	print("This will put the ping and the name on the left side.\n");
-	print("The captures, returns, frags and kill-death ratio will be\n");
-	print("rendered beginning on the right side.\n");
 
+	print("Before a field you can put a + or - sign, then a comma separated list\n");
+	print("of game types, then a slash, to make the field show up only in these\n");
+	print("or in all but these game types.\n");
+
+	local float i;
+	print("Additional columns:\n");
+	for(i = 0; i < MAX_SCORE; ++i)
+	{
+		if(scores_label[i] != "")
+			print(strcat(scores_label[i], "\n"));
+	}
 }
 
 #define MIN_NAMELEN 24
 #define MAX_NAMELEN 24
 
+string Sbar_DefaultColumnLayout()
+{
+	return "ping name | -ctf,kh/kills -ctf,kh/deaths +lms/lives +lms/rank +kh,ctf/caps +kh/pushes +kh/destroyed -lms/score";
+}
+
 void Cmd_Sbar_SetFields(float argc)
 {
-	float i;
-	string str;
+	float i, j, slash;
+	string str, pattern, subpattern;
 	float digit;
+	float have_name, have_primary, have_secondary, have_separator;
+	float missing;
 
+	// TODO: re enable with gametype dependant cvars?
+	if(argc < 2) // no arguments provided
+		argc = tokenizebyseparator(strcat("x ", cvar_string("sbar_columns")), " ");
+
 	if(argc < 2)
-		argc = tokenize(strcat("x ", cvar_string("sbar_columns")));
+		argc = tokenizebyseparator(strcat("x ", Sbar_DefaultColumnLayout()), " ");
+
+	if(argc == 2)
+	{
+		if(argv(1) == "default")
+			argc = tokenizebyseparator(strcat("x ", Sbar_DefaultColumnLayout()), " ");
+		else if(argv(1) == "all")
+		{
+			string s;
+			s = "ping name |";
+			for(i = 0; i < MAX_SCORE; ++i)
+			{
+				if(i != ps_primary)
+				if(i != ps_secondary)
+				if(scores_label[i] != "")
+					s = strcat(s, " ", scores_label[i]);
+			}
+			if(ps_secondary != ps_primary)
+				s = strcat(s, " ", scores_label[ps_secondary]);
+			s = strcat(s, " ", scores_label[ps_primary]);
+			argc = tokenizebyseparator(strcat("x ", s), " ");
+		}
+	}
+		
 	
 	argc = min(MAX_SBAR_FIELDS, argc);
 	sbar_num_fields = 0;
+
 	drawfont = sbar_font;
 	digit = stringwidth("0123456789", FALSE) / 10;
-	for(i = 0; i < argc-1; ++i)
+
+	subpattern = strcat(",", GametypeNameFromType(gametype), ",");
+
+	argc = min(argc-1, MAX_SBAR_FIELDS-1);
+	for(i = 0; i < argc; ++i)
 	{
 		str = argv(i+1);
-		strunzone(sbar_title[i]);
-		sbar_title[i] = strzone(str);
-		sbar_size[i] = stringwidth(str, FALSE);
+
+		slash = strstrofs(str, "/", 0);
+		if(slash >= 0)
+		{
+			pattern = substring(str, 0, slash);
+			str = substring(str, slash + 1, strlen(str) - (slash + 1));
+
+			if(substring(pattern, 0, 1) == "-")
+			{
+				pattern = substring(pattern, 1, strlen(pattern) - 1);
+				if(strstrofs(strcat(",", pattern, ","), subpattern, 0) >= 0)
+					continue;
+			}
+			else
+			{
+				if(substring(pattern, 0, 1) == "+")
+					pattern = substring(pattern, 1, strlen(pattern) - 1);
+				if(strstrofs(strcat(",", pattern, ","), subpattern, 0) < 0)
+					continue;
+			}
+		}
+
+		strunzone(sbar_title[sbar_num_fields]);
+		sbar_title[sbar_num_fields] = strzone(str);
+		sbar_size[sbar_num_fields] = stringwidth(str, FALSE);
 		str = strtolower(str);
+
 		if(str == "ping") {
-			sbar_field[i] = SBF_PING;
+			sbar_field[sbar_num_fields] = SP_PING;
 		} else if(str == "name" || str == "nick") {
-			sbar_field[i] = SBF_NAME;
-			sbar_size[i] = MIN_NAMELEN; // minimum size? any use?
-		} else if(str == "caps" || str == "captures") {
-			if(sbar_size[i] < 3*digit)
-				sbar_size[i] = 3*digit;
-			sbar_field[i] = SBF_CAPS;
-		} else if(str == "rets" || str == "returns") {
-			if(sbar_size[i] < 3*digit)
-				sbar_size[i] = 3*digit;
-			sbar_field[i] = SBF_RETS;
-		} else if(str == "frags" || str == "kills") {
-			if(sbar_size[i] < 5*digit)
-				sbar_size[i] = 5*digit;
-			sbar_field[i] = SBF_FRAGS;
-		} else if(str == "deaths" || str == "dths") {
-			if(sbar_size[i] < 5*digit)
-				sbar_size[i] = 5*digit;
-			sbar_field[i] = SBF_DEATHS;
-		} else if(str == "kdratio") {
-			sbar_field[i] = SBF_KDRATIO;
-		} else if(str == "kdr" || str == "k/d") {
-			sbar_field[i] = SBF_KDRATIO;
-		} else if(str == "kd") {
-			sbar_field[i] = SBF_KDRATIO;
+			sbar_field[sbar_num_fields] = SP_NAME;
+			sbar_size[sbar_num_fields] = MIN_NAMELEN; // minimum size? any use?
+			have_name = 1;
 		} else if(str == "|") {
-			sbar_field[i] = SBF_SEPARATOR;
+			sbar_field[sbar_num_fields] = SP_SEPARATOR;
+			have_separator = 1;
 		} else {
+			for(j = 0; j < MAX_SCORE; ++j)
+				if(str == strtolower(scores_label[j]))
+					goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
+:notfound
 			print(strcat("^1Error:^7 Unknown score field: '", str, "'\n"));
-			--sbar_num_fields;
+			continue;
+:found
+			sbar_field[sbar_num_fields] = j;
+			if(j == ps_primary)
+				have_primary = 1;
+			if(j == ps_secondary)
+				have_secondary = 1;
 		}
 		++sbar_num_fields;
 	}
-	sbar_field[i] = SBF_END;
+
+	if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
+		have_primary = 1;
+	if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
+		have_secondary = 1;
+	if(ps_primary == ps_secondary)
+		have_secondary = 1;
+	missing = !have_primary + !have_secondary + !have_separator + !have_name;
+
+	if(sbar_num_fields+missing < MAX_SBAR_FIELDS)
+	{
+		if(!have_name)
+		{
+			strunzone(sbar_title[sbar_num_fields]);
+			for(i = sbar_num_fields; i > 0; --i)
+			{
+				sbar_title[i] = sbar_title[i-1];
+				sbar_size[i] = sbar_size[i-1];
+				sbar_field[i] = sbar_field[i-1];
+			}
+			sbar_title[0] = strzone("name");
+			sbar_field[0] = SP_NAME;
+			sbar_size[0] = MIN_NAMELEN; // minimum size? any use?
+			++sbar_num_fields;
+			print("fixed missing field 'name'\n");
+
+			if(!have_separator)
+			{
+				strunzone(sbar_title[sbar_num_fields]);
+				for(i = sbar_num_fields; i > 1; --i)
+				{
+					sbar_title[i] = sbar_title[i-1];
+					sbar_size[i] = sbar_size[i-1];
+					sbar_field[i] = sbar_field[i-1];
+				}
+				sbar_title[1] = strzone("|");
+				sbar_field[1] = SP_SEPARATOR;
+				sbar_size[1] = stringwidth("|", FALSE);
+				++sbar_num_fields;
+				print("fixed missing field '|'\n");
+			}
+		}
+		else if(!have_separator)
+		{
+			strunzone(sbar_title[sbar_num_fields]);
+			sbar_title[sbar_num_fields] = strzone("|");
+			sbar_size[sbar_num_fields] = stringwidth("|", FALSE);
+			sbar_field[sbar_num_fields] = SP_SEPARATOR;
+			++sbar_num_fields;
+			print("fixed missing field '|'\n");
+		}
+
+		if(!have_secondary)
+		{
+			strunzone(sbar_title[sbar_num_fields]);
+			sbar_title[sbar_num_fields] = strzone(scores_label[ps_secondary]);
+			sbar_size[sbar_num_fields] = stringwidth(sbar_title[sbar_num_fields], FALSE);
+			sbar_field[sbar_num_fields] = ps_secondary;
+			++sbar_num_fields;
+			print("fixed missing field '", scores_label[ps_secondary], "'\n");
+		}
+
+		if(!have_primary)
+		{
+			strunzone(sbar_title[sbar_num_fields]);
+			sbar_title[sbar_num_fields] = strzone(scores_label[ps_primary]);
+			sbar_size[sbar_num_fields] = stringwidth(sbar_title[sbar_num_fields], FALSE);
+			sbar_field[sbar_num_fields] = ps_primary;
+			++sbar_num_fields;
+			print("fixed missing field '", scores_label[ps_primary], "'\n");
+		}
+	}
+
+	sbar_field[sbar_num_fields] = SP_END;
 }
 
+// MOVEUP::
 vector sbar_field_rgb;
 string Sbar_GetField(entity pl, float field)
 {
-	float tmp;
+	float tmp, num, denom, f;
 	string str;
 	sbar_field_rgb = '1 1 1';
 	switch(field)
 	{
-	case SBF_PING:
-		str = bufstr_get(databuf, DATABUF_PING + pl.sb_player);
-		tmp = max(0, min(220, stof(str)-80)) / 220;
-		sbar_field_rgb = '1 1 1' - '0 1 1'*tmp;
-		return str;
-	case SBF_NAME: return getplayerkey(pl.sb_player, "name");
-	case SBF_CAPS: return ftos(pl.sb_caps);
-	case SBF_RETS: return bufstr_get(databuf, DATABUF_RETURNS + pl.sb_player);
-	case SBF_FRAGS: return ftos(pl.sb_frags);
-	case SBF_DEATHS: return bufstr_get(databuf, DATABUF_DEATHS + pl.sb_player);
-	case SBF_KDRATIO:
-		tmp = stof(bufstr_get(databuf, DATABUF_DEATHS + pl.sb_player));
-		if(tmp == 0) {
-			sbar_field_rgb = '0 1 0';
-			str = ftos(pl.sb_frags);
-		} else if(pl.sb_frags <= 0) {
-			sbar_field_rgb = '1 0 0';
-			str = ftos(pl.sb_frags / tmp);
-		} else
-			str = ftos(pl.sb_frags / tmp);
+		case SP_PING:
+			str = bufstr_get(databuf, DATABUF_PING + pl.sv_entnum);
+			tmp = max(0, min(220, stof(str)-80)) / 220;
+			sbar_field_rgb = '1 1 1' - '0 1 1'*tmp;
+			return str;
 		
-		tmp = strstrofs(str, ".", 0);
-		if(tmp > 0)
-			str = substring(str, 0, tmp+2);
-		return str;
+		case SP_NAME:
+			return getplayerkey(pl.sv_entnum, "name");
+
+		case SP_KDRATIO:
+			num = pl.(scores[SP_KILLS]);
+			denom = pl.(scores[SP_DEATHS]);
+
+			if(denom == 0) {
+				sbar_field_rgb = '0 1 0';
+				str = ftos(num);
+			} else if(num <= 0) {
+				sbar_field_rgb = '1 0 0';
+				str = ftos(num/denom);
+			} else
+				str = ftos(num/denom);
+		
+			tmp = strstrofs(str, ".", 0);
+			if(tmp > 0)
+				str = substring(str, 0, tmp+2);
+			return str;
+			
+		default:
+			tmp = pl.(scores[field]);
+			f = scores_flags[field];
+			if(field == ps_primary)
+				sbar_field_rgb = '1 1 0';
+			else if(field == ps_secondary)
+				sbar_field_rgb = '0 1 1';
+			else
+				sbar_field_rgb = '1 1 1';
+			if(!tmp)
+				if(f & (SFL_HIDE_ZERO | SFL_RANK))
+					return "";
+			if(f & SFL_RANK)
+			{
+				str = ftos(floor(tmp + 0.5));
+				num = strlen(str);
+				if((num >= 2) && (substring(str, num - 2, 1) == "1"))
+					return strcat(str, "th");
+				else if(substring(str, num - 1, 1) == "1")
+					return strcat(str, "st");
+				else if(substring(str, num - 1, 1) == "2")
+					return strcat(str, "nd");
+				else if(substring(str, num - 1, 1) == "3")
+					return strcat(str, "rd");
+				else
+					return strcat(str, "th");
+			}
+			return ftos(tmp);
 	}
+	//return "error";
 }
-#define SBAR_DEFAULT_MASK 0
-#define SBAR_MASK_SPECTATORS 1
-float Sbar_IsFieldMasked(float field, float mask)
-{
-	if(mask&1) // spectator
-		return (field != SBF_NAME && field != SBF_PING);
-	if(gametype != GAME_CTF)
-	{
-		if(field == SBF_CAPS || field == SBF_RETS)
-			return true;
-	}
-	return false;
-}
 
 // shamelessly stolen from menu QC :P <- as if I would steal YOUR code pfft ;)
 float textLengthUpToWidth(string theText, float maxWidth, float handleColors)
@@ -612,11 +596,14 @@
 }
 
 float xmin, xmax, ymin, ymax, sbwidth, sbheight;
-void Sbar_PrintScoreboardItem(vector pos, entity pl, float is_self, float mask)
+
+void Sbar_PrintScoreboardItem(vector pos, entity pl, float is_self)
 {
 	vector tmp;
 	string str;
 	float i, field, len;
+	float is_spec;
+	is_spec = (GetPlayerColor(pl.sv_entnum) == COLOR_SPECTATOR);
 
 	// Layout:
 	tmp_z = 0;
@@ -624,30 +611,32 @@
 	{
 		tmp_x = sbwidth;
 		tmp_y = sbar_fontsize_y;
-		drawfill(pos - '1 1', tmp + '2 2', '1 1 1', 0.3, DRAWFLAG_NORMAL);
+		drawfill(pos - '1 1 0', tmp + '2 2 0', '1 1 1', 0.3, DRAWFLAG_NORMAL);
 	}	
 	tmp_y = 0;
 	
 	for(i = 0; i < sbar_num_fields; ++i)
 	{
 		field = sbar_field[i];
-		if(field == SBF_SEPARATOR)
+		if(field == SP_SEPARATOR)
 			break;
-		if(Sbar_IsFieldMasked(field, mask))
+
+		if(is_spec && field != SP_NAME && field != SP_PING) {
+			pos_x += sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
 			continue;
-
+		}
 		str = Sbar_GetField(pl, field);
 
-		if(field == SBF_NAME)
+		if(field == SP_NAME)
 		{
 			float realsize;
 			float j;
 			realsize = sbar_size[i];
 			if(i+1 < sbar_num_fields)
-				if(sbar_field[i+1] == SBF_SEPARATOR)
+				if(sbar_field[i+1] == SP_SEPARATOR)
 				{
 					realsize = (xmax - xmin) / sbar_fontsize_x;
-					for(j = 0; j < sbar_num_fields; ++j) if(j != i) if(sbar_field[j] != SBF_SEPARATOR)
+					for(j = 0; j < sbar_num_fields; ++j) if(j != i) if(sbar_field[j] != SP_SEPARATOR)
 						realsize -= sbar_size[j] + 1;
 					realsize += 1;
 				}
@@ -659,7 +648,8 @@
 			sbar_size[i] = len;
 
 		pos_x += sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
-		if(field == SBF_NAME) {
+
+		if(field == SP_NAME) {
 			tmp_x = sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
 			drawcolorcodedstring(pos - tmp, str, sbar_fontsize, 1, DRAWFLAG_NORMAL);
 		} else {
@@ -668,27 +658,30 @@
 		}
 	}
 	
-	if(sbar_field[i] == SBF_SEPARATOR)
+	if(sbar_field[i] == SP_SEPARATOR)
 	{
 		pos_x = xmax;
 		for(i = sbar_num_fields-1; i > 0; --i)
 		{
 			field = sbar_field[i];
-			if(field == SBF_SEPARATOR)
+			if(field == SP_SEPARATOR)
 				break;
-			if(Sbar_IsFieldMasked(field, mask))
+			
+			if(is_spec && field != SP_NAME && field != SP_PING) {
+				pos_x -= sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
 				continue;
+			}
 			
 			str = Sbar_GetField(pl, field);
 
-			if(field == SBF_NAME)
+			if(field == SP_NAME)
 				str = textShortenToWidth(str, sbar_size[i], TRUE);
 			len = stringwidth(str, TRUE);
 
 			if(sbar_size[i] < len)
 				sbar_size[i] = len;
-			
-			if(field == SBF_NAME) {
+
+			if(field == SP_NAME) {
 				tmp_x = sbar_fontsize_x*len; // left or right aligned? let's put it right...
 				drawcolorcodedstring(pos - tmp, str, sbar_fontsize, 1, DRAWFLAG_NORMAL);
 			} else {
@@ -733,38 +726,36 @@
 	pos_z = 0;
 
 	// Heading
-	drawfont = sbar_font;
+	drawfont = sbar_bigfont;
 	pos_x = center_x - stringwidth("Scoreboard", TRUE)*0.5*24;
-	drawstring(pos, "Scoreboard", '24 24', '1 1 1', 1, DRAWFLAG_NORMAL);
+	drawstring(pos, "Scoreboard", '24 24 0', '1 1 1', 1, DRAWFLAG_NORMAL);
 	pos_x = xmin;
 	pos_y += 24 + 4;
 
 	// Titlebar background:
 	tmp_x = sbwidth;
 	tmp_y = sbar_fontsize_y;
-	drawfill(pos - '1 1', tmp + '2 2', '0.5 0.5 0.5', 0.5, DRAWFLAG_NORMAL);
+	drawfill(pos - '1 1 0', tmp + '2 2 0', '0.5 0.5 0.5', 0.5, DRAWFLAG_NORMAL);
 	
+	drawfont = sbar_font;
+
 	for(i = 0; i < sbar_num_fields; ++i)
 	{
-		if(Sbar_IsFieldMasked(sbar_field[i], SBAR_DEFAULT_MASK))
-			continue;
-		if(sbar_field[i] == SBF_SEPARATOR)
+		if(sbar_field[i] == SP_SEPARATOR)
 			break;
 		drawstring(pos, sbar_title[i], sbar_fontsize, '1 1 1', 1, DRAWFLAG_NORMAL);
 		pos_x += sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
 	}
 	
-	if(sbar_field[i] == SBF_SEPARATOR)
+	if(sbar_field[i] == SP_SEPARATOR)
 	{
 		pos_x = xmax + sbar_fontsize_x;
 		tmp_y = tmp_z = 0;
 		for(i = sbar_num_fields-1; i > 0; --i)
 		{
-			if(Sbar_IsFieldMasked(sbar_field[i], SBAR_DEFAULT_MASK))
-				continue;
-			if(sbar_field[i] == SBF_SEPARATOR)
+			if(sbar_field[i] == SP_SEPARATOR)
 				break;
-			
+
 			pos_x -= sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
 			/**
 			 * FTEQCC BUG!
@@ -786,49 +777,41 @@
 	
 	if(teamplay)
 	{
-		for(tm = sortedTeams.sort_next; tm; tm = tm.sort_next)
+		//for(tm = sortedTeams.sort_next; tm; tm = tm.sort_next)
+		for(tm = teams.sort_next; tm; tm = tm.sort_next)
 		{
-			if(!tm.sb_player || tm.sb_team == COLOR_SPECTATOR) // no players in it?
+			if(!tm.team_size || tm.team == COLOR_SPECTATOR)
 				continue;
 
-			rgb = GetTeamRGB(tm.sb_team);
+			rgb = GetTeamRGB(tm.team);
 
-			pos_x = xmin - 4*24;
-			if(gametype == GAME_CTF)
-			{
-				if(tm.sb_team == COLOR_TEAM1)
-					Sbar_DrawXNum(pos, caps_team1, 4, 24, rgb, 1, DRAWFLAG_NORMAL);
-				else if(tm.sb_team == COLOR_TEAM2)
-					Sbar_DrawXNum(pos, caps_team2, 4, 24, rgb, 1, DRAWFLAG_NORMAL);
-				pos_x = xmin - 4*10;
-				Sbar_DrawXNum(pos + '0 24', tm.sb_frags, 4, 10, rgb, 1, DRAWFLAG_NORMAL);
-				pos_x = xmin;
-			} else
-				Sbar_DrawXNum(pos, tm.sb_frags, 4, 24, rgb, 1, DRAWFLAG_NORMAL);
 			pos_x = xmin;
 
-			// abuse specs as playerounter
-			specs = 0;
-			for(pl = sortedPlayers.sort_next; pl; pl = pl.sort_next)
-			{
-				if(pl.sb_team == tm.sb_team)
-					++specs;
-			}
+			Sbar_DrawXNum(
+				pos - '6.5 0 0' * sbar_fontsize_y,
+				tm.(teamscores[ts_primary]),
+				4, sbar_fontsize_y * 1.5, rgb, 1, DRAWFLAG_NORMAL);
 
+			if(ts_primary != ts_secondary)
+			Sbar_DrawXNum(
+				pos - '4.5 0 0' * sbar_fontsize_y + '0 1.5 0' * sbar_fontsize_y,
+				tm.(teamscores[ts_secondary]),
+				4, sbar_fontsize_y * 1, rgb, 1, DRAWFLAG_NORMAL);
+			
+			specs = tm.team_size;
+
 			if(specs < 2)
 				specs = 2;
-			if(gametype == GAME_CTF && specs < 4)
-				specs = 4;
 			
 			tmp_x = sbwidth;
 			tmp_y = 1.25 * sbar_fontsize_y * specs;
-			drawfill(pos - '1 1', tmp + '2 0', rgb, 0.2, DRAWFLAG_NORMAL);
+			drawfill(pos - '1 1 0', tmp + '2 0 0', rgb, 0.2, DRAWFLAG_NORMAL);
 			
-			for(pl = sortedPlayers.sort_next; pl; pl = pl.sort_next)
+			for(pl = players.sort_next; pl; pl = pl.sort_next)
 			{
-				if(pl.sb_team != tm.sb_team)
+				if(pl.team != tm.team)
 					continue;
-				Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), SBAR_DEFAULT_MASK);
+				Sbar_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1));
 				pos_y += 1.25 * sbar_fontsize_y;
 				tmp_y -= 1.25 * sbar_fontsize_y;
 			}
@@ -838,12 +821,12 @@
 		rgb = pos + '0 1.5 0' * sbar_fontsize_y;
 		pos_y += 3 * sbar_fontsize_y;
 		specs = 0;
-		for(pl = sortedPlayers.sort_next; pl; pl = pl.sort_next)
+		for(pl = players.sort_next; pl; pl = pl.sort_next)
 		{
-			if(pl.sb_team != COLOR_SPECTATOR)
+			if(pl.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), SBAR_MASK_SPECTATORS);
+			Sbar_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1));
 			pos += '0 1.25 0' * sbar_fontsize_y;
 			++specs;
 		}
@@ -852,11 +835,11 @@
 			drawstring(rgb, "Spectators", sbar_fontsize, '1 1 1', 1, 0);
 	} else {
 		pos_x = xmin;
-		for(pl = sortedPlayers.sort_next; pl; pl = pl.sort_next)
+		for(pl = players.sort_next; pl; pl = pl.sort_next)
 		{
-			if(pl.sb_team == COLOR_SPECTATOR)
+			if(pl.team == COLOR_SPECTATOR)
 				continue;
-			Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), SBAR_DEFAULT_MASK);
+			Sbar_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1));
 			pos_y += 1.25 * sbar_fontsize_y;
 			tmp_y -= 1.25 * sbar_fontsize_y;
 		}
@@ -865,12 +848,11 @@
 		rgb = pos + '0 1.5 0' * sbar_fontsize_y;
 		pos_y += 3 * sbar_fontsize_y;
 		specs = 0;
-		for(pl = sortedPlayers.sort_next; pl; pl = pl.sort_next)
+		for(pl = players.sort_next; pl; pl = pl.sort_next)
 		{
-			if(pl.sb_team != COLOR_SPECTATOR)
+			if(pl.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), SBAR_MASK_SPECTATORS);
+			Sbar_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1));
 			pos += '0 1.25 0' * sbar_fontsize_y;
 			++specs;
 		}
@@ -878,15 +860,47 @@
 		if(specs)
 			drawstring(rgb, "Spectators", sbar_fontsize, '1 1 1', 1, 0);
 	}
+
+	string str;
+	float tl, fl;
+	str = strcat("playing on ^2", shortmapname, "^7");
+	tl = getstatf(STAT_TIMELIMIT);
+	fl = getstatf(STAT_FRAGLIMIT);
+	if(gametype == GAME_LMS)
+	{
+		if(tl > 0)
+			str = strcat(str, " for up to ^1", ftos(tl), " minutes^7");
+	}
+	else
+	{
+		if(tl > 0)
+			str = strcat(str, " for ^1", ftos(tl), " minutes^7");
+		if(fl > 0)
+		{
+			if(tl > 0)
+				str = strcat(str, " or");
+			str = strcat(str, " until ^3", ftos(fl));
+			if(scores_label[ps_primary] == "score")
+				str = strcat(str, " points^7");
+			else
+				str = strcat(str, " ", scores_label[ps_primary]);
+		}
+	}
+	
+	pos_y += 1.5 * sbar_fontsize_y;
+	drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - sbar_fontsize_x * stringwidth(str, TRUE)), str, sbar_fontsize, 0.8, 0);
+
 	sbar = sbar_save;
 }
 
 void Sbar_Score(float margin)
 {
-	float timelimit, timeleft, minutes, seconds, distribution, myplace;
+	float timelimit, timeleft, minutes, seconds, distribution, myplace, score;
 	vector sbar_save, place;
 	entity tm, pl, me;
 	sbar_save = sbar;
+	
+	myteam = GetPlayerColor(player_localentnum - 1);
 
 	sbar_y = vid_conheight - (32+12);
 	sbar_x -= margin;
@@ -900,59 +914,58 @@
 		//
 		//         TEAM2
 		//for(i = 0; i < 4; ++i)
-		for(tm = sortedTeams.sort_next; tm; tm = tm.sort_next)
+		for(tm = teams.sort_next; tm; tm = tm.sort_next)
 		{
-			if(tm.sb_team == COLOR_SPECTATOR || !tm.sb_player) // no players? don't display
+			if(tm.team == COLOR_SPECTATOR || !tm.team_size) // 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);
+			score = tm.(teamscores[ts_primary]);
+			if(tm.team == myteam)
+				Sbar_DrawXNum('-128 0 0', score, 4, 32, GetTeamRGB(tm.team), 1, DRAWFLAG_NORMAL);
 			else
 			{
-				Sbar_DrawXNum(place, tm.sb_frags, 4, 12, GetTeamRGB(tm.sb_team), 1, DRAWFLAG_NORMAL);
+				Sbar_DrawXNum(place, score, 4, 12, GetTeamRGB(tm.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)
+		for(me = players.sort_next; me; me = me.sort_next)
 		{
-			if(me.sb_team != COLOR_SPECTATOR)
+			if(me.team != COLOR_SPECTATOR)
 				++myplace;
-			if(me.sb_player == player_localentnum - 1)
+			if(me.sv_entnum == player_localentnum - 1)
 				break;
 		}
-		pl = sortedPlayers.sort_next;
+		pl = players;
 		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;
+		if(pl) {
+			distribution = me.(scores[ps_primary]) - pl.(scores[ps_primary]);
 		} else
 			distribution = 0;
 		
 		if(myplace == 1)
-			Sbar_DrawXNum('-36 -12', myplace, 3, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
+			Sbar_DrawXNum('-36 -12 0', 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);
+			Sbar_DrawXNum('-36 -12 0', myplace, 3, 12, '1 1 0', 1, DRAWFLAG_NORMAL);
 		else
-			Sbar_DrawXNum('-36 -12', myplace, 3, 12, '1 0 0', 1, DRAWFLAG_NORMAL);
+			Sbar_DrawXNum('-36 -12 0', myplace, 3, 12, '1 0 0', 1, DRAWFLAG_NORMAL);
 
+		score = me.(scores[ps_primary]);
 		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);
+			Sbar_DrawXNum('-84 -12 0', distribution, 4, 12, ' 1 1 1', 1, DRAWFLAG_NORMAL);
+			Sbar_DrawXNum('-128 0 0', score, 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);
+			Sbar_DrawXNum('-84 -12 0', distribution, 4, 12, ' 1 1 0', 1, DRAWFLAG_NORMAL);
+			Sbar_DrawXNum('-128 0 0', score, 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);
+			Sbar_DrawXNum('-84 -12 0', distribution, 4, 12, ' 1 0 0', 1, DRAWFLAG_NORMAL);
+			Sbar_DrawXNum('-128 0 0', score, 4, 32, '1 0 0', 1, DRAWFLAG_NORMAL);
 		}
 	}
 	timelimit = getstatf(STAT_TIMELIMIT);
@@ -963,40 +976,41 @@
 		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);
+			Sbar_DrawXNum('-72 32 0', minutes, 3, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
+			drawpic(sbar + '-36 32 0', "gfx/num_colon", '12 12 0', '1 1 1', sbar_alpha_fg, 0);
+			Sbar_DrawXNum('-24 32 0', 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);
+			Sbar_DrawXNum('-72 32 0', minutes, 3, 12, '1 1 0', 1, DRAWFLAG_NORMAL);
+			drawpic(sbar + '-36 32 0', "gfx/num_colon", '12 12 0', '1 1 0', sbar_alpha_fg, 0);
+			Sbar_DrawXNum('-24 32 0', seconds, -2, 12, '1 1 0', 1, DRAWFLAG_NORMAL);
 		} else {
-			Sbar_DrawXNum('-24 32', seconds, -2, 12, '1 0 0', 1, DRAWFLAG_NORMAL);
+			Sbar_DrawXNum('-24 32 0', 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_DrawXNum('-72 32 0', minutes, 3, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
+		drawpic(sbar + '-36 32 0', "gfx/num_colon", '12 12 0', '1 1 1', sbar_alpha_fg, 0);
+		Sbar_DrawXNum('-24 32 0', seconds, -2, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
 	}
 	sbar = sbar_save;
 }
 
 void Sbar_MiniscoreItem(vector pos, entity pl, float is_self)
 {
-	float x;
+	float x, score;
 	pos_x += 72;
 	
 	if(teamplay)
-		drawfill(pos + '0 1 0', '40 6 0', GetTeamRGB(pl.sb_team)*0.5, 1, DRAWFLAG_NORMAL);
+		drawfill(pos + '0 1 0', '40 6 0', GetTeamRGB(pl.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 -= stringwidth(ftos(pl.sb_frags), FALSE)*8;
-	drawstring(pos, ftos(pl.sb_frags), '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL);
+	score = pl.(scores[ps_primary]);
+	pos_x -= stringwidth(ftos(score), FALSE)*8;
+	drawstring(pos, ftos(score), '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL);
 	pos_x = x;
 	if(is_self)
 	{
@@ -1005,7 +1019,7 @@
 		pos_x += 8;
 	} else
 		pos_x += 56;
-	drawcolorcodedstring(pos, getplayerkey(pl.sb_player, "name"), '8 8 0', 1, 0);
+	drawcolorcodedstring(pos, getplayerkey(pl.sv_entnum, "name"), '8 8 0', 1, 0);
 }
 
 void Sbar_MiniscoreTeamItem(vector pos, float color, float frags, float is_self)
@@ -1034,7 +1048,7 @@
 
 void Sbar_MiniDeathmatchOverlay(vector pos)
 {
-	float numlines, up, down;
+	float numlines, up, down, score;
 	entity me, tm, pl;
 	float miniscoreboard_size;
 	miniscoreboard_size = cvar("sbar_miniscoreboard_size");
@@ -1053,9 +1067,9 @@
 		return;
 
 	// me vector := [team/connected frags id]
-	for(me = sortedPlayers.sort_next; me; me = me.sort_next)
+	for(me = players.sort_next; me; me = me.sort_next)
 	{
-		if(me.sb_player == player_localentnum - 1)
+		if(me.sv_entnum == player_localentnum - 1)
 			break;
 	}
 
@@ -1071,7 +1085,7 @@
 	// render bottom-up
 	for(pl = me.sort_next; pl && down > 0; pl = pl.sort_next)
 	{
-		if(pl.sb_team == COLOR_SPECTATOR)
+		if(pl.team == COLOR_SPECTATOR)
 			continue;
 		Sbar_MiniscoreItem(pos, pl, false);
 		pos_y -= 9;
@@ -1080,9 +1094,9 @@
 	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)
+	for(pl = me.sort_prev; pl && up > 0; pl = pl.sort_prev)
 	{
-		if(pl.sb_team == COLOR_SPECTATOR)
+		if(pl.team == COLOR_SPECTATOR)
 			continue;
 		Sbar_MiniscoreItem(pos, pl, false);
 		pos_y -= 9;
@@ -1091,24 +1105,37 @@
 
 	if(teamplay)
 	{
-		for(tm = sortedTeams.sort_next; tm.sort_next; tm = tm.sort_next);
-		for(; tm != sortedTeams; tm = tm.sort_prev)
+		for(tm = teams.sort_next; tm.sort_next; tm = tm.sort_next);
+		for(; tm; tm = tm.sort_prev)
 		{
-			if(!tm.sb_player || tm.sb_team == COLOR_SPECTATOR) // no players?
+			if(!tm.team_size || tm.team == COLOR_SPECTATOR)
 				continue;
-			Sbar_MiniscoreTeamItem(pos, tm.sb_team, tm.sb_frags, (tm.sb_team == me.sb_team));
+			score = tm.(teamscores[ts_primary]);
+			Sbar_MiniscoreTeamItem(pos, tm.team, score, (tm.team == me.team));
 			pos_y -= 9;
 		}
 	}
 }
 
+float Sbar_WouldDrawScoreboard ()
+{
+	if (sb_showscores)
+		return 1;
+	else if (intermission == 1)
+		return 1;
+	else if (intermission == 2)
+		return 1;
+	return 0;
+}
+
 void Sbar_Draw (void)
 {
 	float i;
 	float x, fade;
 	float stat_items;
 
-	Sbar_SortFrags();
+	//Sbar_SortFrags();
+	Sbar_UpdatePlayerTeams();
 
 	sb_lines = 24;
 	
@@ -1128,7 +1155,7 @@
 			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);
+			//drawpic('0 0 0', "gfx/scorebar", '0 0 0', '1 1 1', cvar("sbar_alpha_bg"), 0);
 			Sbar_DrawScoreboard ();
 		}
 		else
@@ -1165,17 +1192,17 @@
 				{
 					// "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);
+					drawpic(sbar + '72 0 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);
+						Sbar_DrawXNum('0 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);
+						Sbar_DrawXNum('0 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);
+						Sbar_DrawXNum('0 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);
+						Sbar_DrawXNum('0 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);
+						Sbar_DrawXNum('0 0 0', x, 3, 24, '0.7 0 0', 1, 0);
 				}
 
 				// health
@@ -1184,17 +1211,17 @@
 				{
 					// "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);
+					drawpic(sbar + '184 0 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);
+						Sbar_DrawXNum('112 0 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);
+						Sbar_DrawXNum('112 0 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);
+						Sbar_DrawXNum('112 0 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);
+						Sbar_DrawXNum('112 0 0', x, 3, 24, '1 1 0.2', 1, 0);
 					else
-						Sbar_DrawXNum('112 0', x, 3, 24, '0.7 0 0', 1, 0);
+						Sbar_DrawXNum('112 0 0', x, 3, 24, '0.7 0 0', 1, 0);
 				}
 
 				// ammo
@@ -1202,21 +1229,21 @@
 				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);
+						drawpic(sbar + '296 0 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);
+						drawpic(sbar + '296 0 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);
+						drawpic(sbar + '296 0 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);
+						drawpic(sbar + '296 0 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);
+						Sbar_DrawXNum('224 0 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);
+						Sbar_DrawXNum('224 0 0', x, 3, 24, '0.7 0 0', 1, 0);
 				}
 
 				if (sbar_x + 320 + 160 <= vid_conwidth)
-					Sbar_MiniDeathmatchOverlay(sbar + '320 0');
+					Sbar_MiniDeathmatchOverlay(sbar + '320 0 0');
 				if (sbar_x > 0)
 					Sbar_Score(16);
 				// The margin can be at most 8 to support 640x480 console size:
@@ -1255,17 +1282,17 @@
 
 				// armor
 				// (340-3*24) = 268
-				Sbar_DrawXNum('268 12', getstati(STAT_ARMOR), 3, 24, '0.6 0.7 0.8', 1, 0);
+				Sbar_DrawXNum('268 12 0', 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);
+					Sbar_DrawXNum('82 12 0', 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);
+					Sbar_DrawXNum('82 12 0', 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);
+					Sbar_DrawXNum('81 12 0', x, 3, 24, '0.6 0.7 0.8', 1, 0);
 
 				// AK dont draw ammo for the laser
 				x = getstati(STAT_AMMO);
@@ -1273,24 +1300,24 @@
 				{
 					// (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);
+						drawpic(sbar + '519 0 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);
+						drawpic(sbar + '519 0 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);
+						drawpic(sbar + '519 0 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);
+						drawpic(sbar + '519 0 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);
+						Sbar_DrawXNum('447 12 0', 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);
+						Sbar_DrawXNum('447 12 0', 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');
+					Sbar_MiniDeathmatchOverlay (sbar + '600 0 0');
 
 				if (sbar_x > 0)
 					Sbar_Score(-16);

Modified: branches/nexuiz-2.0/data/qcsrc/client/sortlist.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/sortlist.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/client/sortlist.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -1,6 +1,15 @@
 .float(entity,entity) sort_cmp;
 .entity sort_next, sort_prev;
 
+entity Sort_Spawn()
+{
+	entity sort;
+	sort = spawn();
+	sort.sort_next = NULL;
+	sort.chain = sort;
+	return sort;
+}
+
 entity Sort_New(float(entity,entity) cmp)
 {
 	entity sort;
@@ -88,12 +97,17 @@
 	return sort;
 }
 
-#define SORT_SWAP(a,b) \
-	b.sort_prev = a.sort_prev;			\
-	a.sort_next = b.sort_next;			\
+/**
+ * Swap two neighbours in a sortlist.
+ * @param a FIRST entity
+ * @param b entity after a
+ */
+#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;				\
+	if(a.sort_prev) a.sort_prev.sort_next = b;			\
+	a.sort_prev = b;									\
 	b.sort_next = a
 
 void Sort_Erase(entity ent)

Modified: branches/nexuiz-2.0/data/qcsrc/client/teamplay.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/teamplay.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/client/teamplay.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -16,7 +16,12 @@
 
 float GetPlayerColor(float i)
 {
-	return stof(getplayerkey(i, "colors")) & 15;
+	if(getplayerkey(i, "frags") == "-666")
+		return COLOR_SPECTATOR;
+	else if(!teamplay)
+		return 0;
+	else
+		return stof(getplayerkey(i, "colors")) & 15;
 }
 
 vector GetTeamRGB(float color)

Modified: branches/nexuiz-2.0/data/qcsrc/common/constants.qh
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/common/constants.qh	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/common/constants.qh	2008-07-31 06:23:51 UTC (rev 3991)
@@ -3,7 +3,8 @@
 // Revision 2: Mapvote preview pictures
 // Revision 3: optimized map vote protocol
 // Revision 4: CSQC config var system
-#define CSQC_REVISION 4
+// Revision 5: mapvote time fix
+#define CSQC_REVISION 5
 
 // probably put these in common/
 // so server/ and client/ can be synced better
@@ -26,6 +27,9 @@
 const float ENT_CLIENT = 0;
 const float ENT_CLIENT_DEAD = 1;
 const float ENT_CLIENT_ENTCS = 2;
+const float ENT_CLIENT_SCORES_INFO = 3;
+const float ENT_CLIENT_SCORES = 4;
+const float ENT_CLIENT_TEAMSCORES = 5;
 
 ///////////////////////////
 // key constants
@@ -166,6 +170,7 @@
 const float ENTCS_MSG_END = 0;
 const float ENTCS_MSG_ONS_GPS = 1;
 const float ENTCS_MSG_ONS_REMOVE = 2;
+const float ENTCS_MSG_INIT = 3;
 
 const float TE_CSQC_INIT = 100;
 const float TE_CSQC_PING = 101;
@@ -189,3 +194,43 @@
 const float MAPVOTE_NET_UPDATE = 1;
 const float MAPVOTE_NET_PIC = 2;
 const float MAPVOTE_NET_OWNVOTE = 3;
+
+/**
+ * Lower scores are better (e.g. suicides)
+ */
+#define SFL_LOWER_IS_BETTER     1
+
+/**
+ * Don't show zero values as scores
+ */
+#define SFL_HIDE_ZERO           2
+
+/**
+ * Allow a column to be hidden (do not automatically add it even if it is a sorting key)
+ */
+#define SFL_ALLOW_HIDE         16
+
+/**
+ * Display as a rank (with st, nd, rd, th suffix)
+ */
+#define SFL_RANK               32
+
+/**
+ * Scoring priority (NOTE: PRIMARY is used for fraglimit)
+ */
+#define SFL_SORT_PRIO_SECONDARY 4
+#define SFL_SORT_PRIO_PRIMARY   8
+#define SFL_SORT_PRIO_MASK     12
+
+/**
+ * Score indices
+ */
+#define MAX_SCORE 10
+#define MAX_TEAMSCORE 2
+
+#define ST_SCORE 0
+#define SP_KILLS 0
+#define SP_DEATHS 1
+#define SP_SUICIDES 2
+#define SP_SCORE 3
+// game mode specific indices are not in common/, but in server/scores_rules.qc!

Modified: branches/nexuiz-2.0/data/qcsrc/common/mapinfo.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/common/mapinfo.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/common/mapinfo.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -504,7 +504,8 @@
 	if(pWantedType == MAPINFO_TYPE_CTF)
 	{
 		sa = car(s); if(sa == "") sa = "10";
-		cvar_set("capturelimit", sa);
+		if(cvar("g_ctf_win_mode") != 2)
+			cvar_set("fraglimit", sa);
 		s = cdr(s);
 	}
 }

Modified: branches/nexuiz-2.0/data/qcsrc/common/util.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/common/util.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/common/util.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -409,3 +409,18 @@
 		fputs(fh, strcat(bufstr_get(buf, i), "\n"));
 	fclose(fh);
 }
+
+string GametypeNameFromType(float g)
+{
+	if      (g == GAME_DEATHMATCH) return "dm";
+	else if (g == GAME_TEAM_DEATHMATCH) return "tdm";
+	else if (g == GAME_DOMINATION) return "dom";
+	else if (g == GAME_CTF) return "ctf";
+	else if (g == GAME_RUNEMATCH) return "rune";
+	else if (g == GAME_LMS) return "lms";
+	else if (g == GAME_KEYHUNT) return "kh";
+	else if (g == GAME_ONSLAUGHT) return "ons";
+	else if (g == GAME_ASSAULT) return "as";
+	return "dm";
+}
+

Modified: branches/nexuiz-2.0/data/qcsrc/common/util.qh
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/common/util.qh	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/common/util.qh	2008-07-31 06:23:51 UTC (rev 3991)
@@ -55,3 +55,5 @@
 #ifndef MENUQC
 float mod(float a, float b) { return a - (floor(a / b) * b); }   
 #endif
+
+string GametypeNameFromType(float g);

Modified: branches/nexuiz-2.0/data/qcsrc/menu/nexuiz/serverlist.c
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/menu/nexuiz/serverlist.c	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/menu/nexuiz/serverlist.c	2008-07-31 06:23:51 UTC (rev 3991)
@@ -124,17 +124,17 @@
 	else */
 	{
 		float m;
-		m = SLIST_MASK_AND;
+		m = SLIST_MASK_AND - 1;
 		resethostcachemasks();
 		if(!me.filterShowFull)
-			sethostcachemasknumber(m++, SLIST_FIELD_FREESLOTS, 1, SLIST_TEST_GREATEREQUAL);
+			sethostcachemasknumber(++m, SLIST_FIELD_FREESLOTS, 1, SLIST_TEST_GREATEREQUAL);
 		if(!me.filterShowEmpty)
-			sethostcachemasknumber(m++, SLIST_FIELD_NUMHUMANS, 1, SLIST_TEST_GREATEREQUAL);
-		m = SLIST_MASK_OR;
+			sethostcachemasknumber(++m, SLIST_FIELD_NUMHUMANS, 1, SLIST_TEST_GREATEREQUAL);
+		m = SLIST_MASK_OR - 1;
 		if(me.filterString)
 		{
-			sethostcachemaskstring(m++, SLIST_FIELD_NAME, me.filterString, SLIST_TEST_CONTAINS);
-			sethostcachemaskstring(m++, SLIST_FIELD_MAP, me.filterString, SLIST_TEST_CONTAINS);
+			sethostcachemaskstring(++m, SLIST_FIELD_NAME, me.filterString, SLIST_TEST_CONTAINS);
+			sethostcachemaskstring(++m, SLIST_FIELD_MAP, me.filterString, SLIST_TEST_CONTAINS);
 		}
 		sethostcachesort(me.currentSortField, me.currentSortOrder < 0);
 		resorthostcache();

Modified: branches/nexuiz-2.0/data/qcsrc/menu/skin.qh
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/menu/skin.qh	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/menu/skin.qh	2008-07-31 06:23:51 UTC (rev 3991)
@@ -13,7 +13,7 @@
 #define SKINBEGIN void Skin_ApplySetting(string key, string value) { switch(key) {
 #define SKINVECTOR(name,def) case #name: SKIN##name = stov(value); break
 #define SKINFLOAT(name,def) case #name: SKIN##name = stof(value); break
-#define SKINSTRING(name,def) break
+#define SKINSTRING(name,def)
 #define SKINEND case "": break; case "//": break; default: print("Invalid key in skin file: ", key, "\n"); } }
 #include "skin-customizables.inc"
 #undef SKINEND

Modified: branches/nexuiz-2.0/data/qcsrc/server/arena.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/arena.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/server/arena.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -86,7 +86,6 @@
 		{
 			removedecor(self);
 		}
-		// TODO properly reset Onslaught
 		else if(self.classname == "onslaught_generator")
 		{
 			onslaught_generator_reset();
@@ -128,8 +127,9 @@
 				{
 					//NEW: changed behaviour so that it prevents that previous spectators/observers suddenly spawn as players
 					if (self.classname == "player") {
-						self.frags = (g_lms)?LMS_NewPlayerLives():0;
-						self.deaths = 0;
+						PlayerScore_Clear(self);
+						if(g_lms)
+							PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives());
 						self.killcount = 0;
 						//stop the player from moving so that he stands still once he gets respawned
 						self.velocity = '0 0 0';

Modified: branches/nexuiz-2.0/data/qcsrc/server/cl_client.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/cl_client.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/server/cl_client.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -1,3 +1,6 @@
+// let's abuse an existing field
+#define SPAWNPOINT_SCORE frags
+
 .float wantswelcomemessage;
 .string netname_previous;
 
@@ -77,12 +80,12 @@
 
 	for(spot = firstspot; spot; spot = spot.chain)
 	{
-		spot.frags = Spawn_Score(spot, playerlist, teamcheck);
+		spot.SPAWNPOINT_SCORE = Spawn_Score(spot, playerlist, teamcheck);
 
 		if(cvar("spawn_debugview"))
 		{
 			setmodel(spot, "models/runematch/rune.mdl");
-			if(spot.frags < mindist)
+			if(spot.SPAWNPOINT_SCORE < mindist)
 			{
 				spot.colormod = '1 0 0';
 				spot.scale = 1;
@@ -90,13 +93,13 @@
 			else
 			{
 				spot.colormod = '0 1 0';
-				spot.scale = spot.frags / mindist;
+				spot.scale = spot.SPAWNPOINT_SCORE / mindist;
 			}
 		}
 
-		if(spot.frags >= 0) // spawning allowed here
+		if(spot.SPAWNPOINT_SCORE >= 0) // spawning allowed here
 		{
-			if(spot.frags < mindist)
+			if(spot.SPAWNPOINT_SCORE < mindist)
 			{
 				// too short distance
 				spawn_allgood = FALSE;
@@ -147,7 +150,7 @@
 
 	RandomSelection_Init();
 	for(spot = firstspot; spot; spot = spot.chain)
-		RandomSelection_Add(spot, 0, pow(bound(lower, spot.frags, upper), exponent) * spot.cnt, spot.frags >= lower);
+		RandomSelection_Add(spot, 0, pow(bound(lower, spot.SPAWNPOINT_SCORE, upper), exponent) * spot.cnt, spot.SPAWNPOINT_SCORE >= lower);
 
 	return RandomSelection_chosen_ent;
 }
@@ -202,7 +205,7 @@
 
 	if(cvar("spawn_debugview"))
 	{
-		print("spot mindistance: ", ftos(spot.frags), "\n");
+		print("spot mindistance: ", ftos(spot.SPAWNPOINT_SCORE), "\n");
 
 		entity e;
 		if(teamcheck)
@@ -348,8 +351,6 @@
 putting a client as observer in the server
 =============
 */
-void ctf_UpdateCaptures(float);
-void ctf_UpdateReturns(float);
 void PutObserverInServer (void)
 {
 	entity	spot;
@@ -372,18 +373,21 @@
 		DropFlag(self.flagcarried);
 
 	WaypointSprite_PlayerDead();
-
-	DistributeFragsAmongTeam(self, self.team, 1);
-	if(g_ctf)
+	
+	if(self.killcount != -666)
 	{
-		self.captures = 0;
-		ctf_UpdateCaptures(MSG_BROADCAST);
+		if(g_lms)
+		{
+			if(PlayerScore_Add(self, SP_LMS_RANK, 0) > 0)
+				bprint ("^4", self.netname, "^4 has no more lives left\n");
+			else
+				bprint ("^4", self.netname, "^4 is spectating now\n"); // TODO turn this into a proper forfeit?
+		}
+		else
+			bprint ("^4", self.netname, "^4 is spectating now\n");
 	}
 
-	if(self.frags <= 0 && self.frags > -666 && g_lms && self.killcount != -666)
-		bprint ("^4", self.netname, "^4 has no more lives left\n");
-	else if(self.killcount != -666)
-		bprint ("^4", self.netname, "^4 is spectating now\n");
+	PlayerScore_Clear(self); // clear scores when needed
 
 	self.spectatortime = time;
 	
@@ -402,9 +406,6 @@
 	self.damageforcescale = 0;
 	self.death_time = 0;
 	self.dead_frame = 0;
-	self.deaths = 0;
-	self.captures = 0;
-	self.returns = 0;
 	self.alpha = 0;
 	self.scale = 0;
 	self.fade_time = 0;
@@ -448,6 +449,7 @@
 		if(self.frags != -2)
 		{
 			Spawnqueue_Insert(self);
+			// FIXME what IS this?
 		}
 		else
 		{
@@ -455,10 +457,16 @@
 			Spawnqueue_Remove(self);
 		}
 	}
-	else if(!g_lms)
+	else if(g_lms)
+	{
+		// Only if the player cannot play at all
+		if(PlayerScore_Add(self, SP_LMS_RANK, 0) == 666)
+			self.frags = -666;
+		else
+			self.frags = -667;
+	}
+	else
 		self.frags = -666;
-	
-	net_UpdateDeaths(MSG_BROADCAST);
 }
 
 float RestrictSkin(float s)
@@ -546,8 +554,12 @@
 	}
 
 	// player is dead and becomes observer
-	if(g_lms && self.frags < 1)
-		self.classname = "observer";
+	// FIXME fix LMS scoring for new system
+	if(g_lms)
+	{
+		if(PlayerScore_Add(self, SP_LMS_RANK, 0) > 0)
+			self.classname = "observer";
+	}
 
 	if(g_arena)
 	if(!self.spawned)
@@ -663,19 +675,9 @@
 		self.BUTTON_ATCK = self.BUTTON_JUMP = self.BUTTON_ATCK2 = 0;
 
 		if(self.killcount == -666) {
+			PlayerScore_Clear(self);
 			self.killcount = 0;
-			if(!g_arena)
-			if(!g_lms)
-				self.frags = 0;
-			if(g_ctf)
-			{
-				self.captures = 0;
-				self.returns = 0;
-				ctf_UpdateCaptures(MSG_BROADCAST);
-				ctf_UpdateReturns(MSG_BROADCAST);
-			}
-			self.deaths = 0;
-			net_UpdateDeaths(MSG_BROADCAST);
+			self.frags = 0;
 		}
 
 		self.cnt = WEP_LASER;
@@ -780,15 +782,6 @@
 
 void ClientKill_Now_TeamChange()
 {
-	if(g_ctf)
-	{
-		self.captures = 0;
-		self.returns = 0;
-		ctf_UpdateCaptures(MSG_BROADCAST);
-		ctf_UpdateReturns(MSG_BROADCAST);
-	}
-	self.deaths = 0;
-	net_UpdateDeaths(MSG_BROADCAST);
 	if(self.killindicator_teamchange == -1)
 	{
 		self.team = -1;
@@ -1011,6 +1004,8 @@
 		player_count = 0;
 	}
 
+	PlayerScore_Attach(self);
+
 	bot_clientconnect();
 
 	//if(g_domination)
@@ -1109,14 +1104,8 @@
 
 	stuffcmd(self, strcat("set gametype ", ftos(game), "\n"));
 
-	if(g_lms)
+	if(g_arena)
 	{
-		self.frags = LMS_NewPlayerLives();
-		if(!self.frags)
-			self.frags = -666;
-	}
-	else if(g_arena)
-	{
 		self.classname = "observer";
 		Spawnqueue_Insert(self);
 	}
@@ -1144,15 +1133,21 @@
 		sprint(self, strcat("nexuiz-csqc protocol ", ftos(CSQC_REVISION), "\n"));
 		SendCSQCInfo();
 		msg_entity = self;
-		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);
 		}
 	}
+
+	if(g_lms)
+	{
+		if(PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives()) <= 0)
+		{
+			PlayerScore_Add(self, SP_LMS_RANK, 666);
+			self.frags = -666; // FIXME do we still need this?
+		}
+	}
 }
 
 /*
@@ -1206,13 +1201,6 @@
 	if(self.flagcarried)
 		DropFlag(self.flagcarried);
 
-	DistributeFragsAmongTeam(self, self.team, 1);
-	if(g_ctf)
-	{
-		self.captures = 0;
-		ctf_UpdateCaptures(MSG_BROADCAST);
-	}
-
 	save = self.flags;
 	self.flags = self.flags - (self.flags & FL_CLIENT);
 	bot_relinkplayerlist();
@@ -1234,11 +1222,17 @@
 	}
 	*/
 
+	if(self.flags & FL_CLIENT)
+		PlayerScore_Detach(self);
+	else
+		print("Warning: ClientDisconnect without ClientConnect\n");
+
 	if(self.netname_previous)
 		strunzone(self.netname_previous);
 
 	// free cvars
 	GetCvars(-1);
+	self.playerid = 0;
 }
 
 .float BUTTON_CHAT;

Modified: branches/nexuiz-2.0/data/qcsrc/server/cl_player.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/cl_player.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/server/cl_player.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -351,19 +351,6 @@
 
 void ClientKill_Now_TeamChange();
 
-void net_UpdateDeaths(float msg_target)
-{
-	entity p;
-	WriteByte(msg_target, SVC_TEMPENTITY);
-	WriteByte(msg_target, TE_CSQC_DEATHS);
-	FOR_EACH_PLAYER(p)
-	{
-		WriteByte(msg_target, num_for_edict(p));
-		WriteByte(msg_target, p.deaths);
-	}
-	WriteByte(msg_target, 0);
-}
-
 void PlayerDamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
 {
 	local float take, save, waves, sdelay;
@@ -459,9 +446,6 @@
 		float defer_ClientKill_Now_TeamChange;
 		defer_ClientKill_Now_TeamChange = FALSE;
 
-		self.deaths += 1;
-		net_UpdateDeaths(MSG_BROADCAST);
-
 		// get rid of kill indicator
 		if(self.killindicator)
 		{

Modified: branches/nexuiz-2.0/data/qcsrc/server/cl_weapons.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/cl_weapons.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/server/cl_weapons.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -35,6 +35,7 @@
 	if(weaponid == WEP_HAGAR)             return "Hagar";
 	if(weaponid == WEP_ROCKET_LAUNCHER)   return "Rocket Launcher";
 	if(weaponid == WEP_CRYLINK)           return "Crylink";
+	if(weaponid == WEP_ANTINEX)	      return "Antinex";
 	return "@!#%'n Tuba";
 }
 
@@ -67,6 +68,7 @@
 		case WEP_NEX:              return IT_CELLS;
 		case WEP_HAGAR:            return IT_ROCKETS;
 		case WEP_ROCKET_LAUNCHER:  return IT_ROCKETS;
+		case WEP_ANTINEX:	   return IT_ROCKETS;
 		default:                   return 0;
 	}
 }

Modified: branches/nexuiz-2.0/data/qcsrc/server/clientcommands.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/clientcommands.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/server/clientcommands.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -185,6 +185,8 @@
 		}
 		sprint(self, strcat("^1autoswitch turned ", autoswitchmsg, "\n"));
 	} else if(argv(0) == "clientversion") {
+		if not(self.flags & FL_CLIENT)
+			return;
 		if (argv(1) == "$gameversion") {
 			//versionmsg = "^1client is too old to get versioninfo.\nUPDATE!!! (http://www.nexuiz.com)^8";
 			// either that or someone wants to be funny
@@ -208,6 +210,8 @@
 	} else if(argv(0) == "sentcvar") { // new system
 		GetCvars(1);
 	} else if(argv(0) == "spectate") {
+		if not(self.flags & FL_CLIENT)
+			return;
 		if(g_lms || g_arena)
 			return; // don't allow spectating in lms, unless player runs out of lives
 		if(self.classname == "player" && cvar("sv_spectate") == 1) {
@@ -215,19 +219,20 @@
 				DropFlag(self.flagcarried);
 			kh_Key_DropAll(self, TRUE);
 			WaypointSprite_PlayerDead();
-			DistributeFragsAmongTeam(self, self.team, 1.0);
 			self.classname = "observer";
 			if(blockSpectators)
 				sprint(self, strcat("^7You have to become a player within the next ", ftos(cvar("g_maxplayers_spectator_blocktime")), " seconds, otherwise you will be kicked, because spectators aren't allowed at this time!\n"));
 			PutClientInServer();
 		}
 	} else if(argv(0) == "join") {
+		if not(self.flags & FL_CLIENT)
+			return;
 		if(!g_arena)
 		if (self.classname != "player" && !lockteams)
 		{
 			if(isJoinAllowed()) {
 				self.classname = "player";
-				self.frags = 0;
+				PlayerScore_Clear(self);
 				bprint ("^4", self.netname, "^4 is playing now\n");
 				PutClientInServer();
 			}
@@ -237,6 +242,8 @@
 			}
 		}
 	} else if( argv(0) == "selectteam" ) {
+		if not(self.flags & FL_CLIENT)
+			return;
 		if( !cvar("teamplay") ) {
 			sprint( self, "selecteam can only be used in teamgames\n");
 		} else if(cvar("g_campaign")) {
@@ -257,6 +264,8 @@
 			sprint( self, strcat( "selectteam none/red/blue/yellow/pink/auto - \"", argv(1), "\" not recognised\n" ) );
 		}
 	} else if(argv(0) == "ready") {
+		if not(self.flags & FL_CLIENT)
+			return;
 		if(cvar("sv_ready_restart"))
 		{
 			if(timeoutStatus) {
@@ -299,8 +308,6 @@
 		}
 		sprint(self, "\n");
 #endif
-	} else if(argv(0) == "teamstatus") {
-		PrintScoreboard(self);
 	} else if(argv(0) == "voice") {
 		VoiceMessage(argv(1));
 	} else if(argv(0) == "say") {
@@ -318,6 +325,8 @@
 	} else if(argv(0) == "suggestmap") {
 		sprint(self, strcat(MapVote_Suggest(argv(1)), "\n"));
 	} else if(argv(0) == "calltimeout") {
+		if not(self.flags & FL_CLIENT)
+			return;
 		if(cvar("sv_timeout")) {
 			if(self.classname == "player") {
 				if(votecalled)
@@ -329,6 +338,8 @@
 				sprint(self, "^7Error: only players can call a timeout!\n");
 		}
 	} else if(argv(0) == "resumegame") {
+		if not(self.flags & FL_CLIENT)
+			return;
 		if(cvar("sv_timeout")) {
 			evaluateResumeGame();
 		}
@@ -462,7 +473,10 @@
 
 	//reset map immediately if this cvar is not set
 	if (!cvar("sv_ready_restart_after_countdown"))
+	{
 		reset_map();
+		Score_ClearAll();
+	}
 	
 	if(cvar("sv_eventlog"))
 		GameLogEcho(":restart", FALSE);

Modified: branches/nexuiz-2.0/data/qcsrc/server/ctf.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/ctf.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/server/ctf.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -230,34 +230,6 @@
 	}
 };
 
-void ctf_UpdateCaptures(float msg_target)
-{
-	entity p;
-	WriteByte(msg_target, SVC_TEMPENTITY);
-	WriteByte(msg_target, TE_CSQC_CAPTURES);
-	WriteByte(msg_target, cvar("g_ctf_win_mode"));
-	WriteByte(msg_target, caps_team1);
-	WriteByte(msg_target, caps_team2);
-	FOR_EACH_PLAYER(p)
-	{
-		WriteByte(msg_target, num_for_edict(p));
-		WriteByte(msg_target, p.captures);
-	}
-	WriteByte(msg_target, 0);
-}
-void ctf_UpdateReturns(float msg_target)
-{
-	entity p;
-	WriteByte(msg_target, SVC_TEMPENTITY);
-	WriteByte(msg_target, TE_CSQC_RETURNS);
-	FOR_EACH_PLAYER(p)
-	{
-		WriteByte(msg_target, num_for_edict(p));
-		WriteByte(msg_target, p.returns);
-	}
-	WriteByte(msg_target, 0);
-}
-
 void FlagTouch()
 {
 	if(gameover) return;
@@ -311,16 +283,7 @@
 			bprint(other.netname, "^7 captured the ", other.flagcarried.netname, " in ", s, ", failing to break ", strcat(h0, " record of ", s0, " seconds\n"));
 		}
 
-		other.captures += 1;
-		if(other.team == COLOR_TEAM1)
-			++caps_team1;
-		else if(other.team == COLOR_TEAM2)
-			++caps_team2;
-		else
-			print("Unknown team captured the flag!\n");
-		ctf_UpdateCaptures(MSG_BROADCAST);
-		// FIXME: When counting captures, should the score be updated?
-
+		PlayerTeamScore_Add(other, SP_CTF_CAPS, ST_CTF_CAPS, 1);
 		LogCTF("capture", other.flagcarried.team, other);
 		// give credit to the individual player
 		UpdateFrags(other, cvar("g_ctf_flagscore_capture"));
@@ -360,6 +323,7 @@
 		self.angles = '0 0 0';
 		bprint(other.netname, "^7 got the ", self.netname, "\n");
 		UpdateFrags(other, cvar("g_ctf_flagscore_pickup"));
+		PlayerScore_Add(other, SP_CTF_PICKUPS, 1);
 		LogCTF("steal", self.team, other);
 		sound (self, CHAN_AUTO, self.noise, 1, ATTN_NONE);
 
@@ -382,15 +346,11 @@
 		{
 			// return flag
 			bprint(other.netname, "^7 returned the ", self.netname, "\n");
-			if(cvar("g_ctf_win_mode") == 2)
-			{
-				if (other.team == COLOR_TEAM1 || other.team == COLOR_TEAM2)
-					UpdateFrags(other, cvar("g_ctf_flagscore_return"));
-				else
-					UpdateFrags(other, cvar("g_ctf_flagscore_return_rogue"));
-			}
-			other.returns += 1;
-			ctf_UpdateReturns(MSG_BROADCAST);
+			if (other.team == COLOR_TEAM1 || other.team == COLOR_TEAM2)
+				UpdateFrags(other, cvar("g_ctf_flagscore_return"));
+			else
+				UpdateFrags(other, cvar("g_ctf_flagscore_return_rogue"));
+			PlayerScore_Add(other, SP_CTF_RETURNS, 1);
 			LogCTF("return", self.team, other);
 			sound (self, CHAN_AUTO, self.noise1, 1, ATTN_NONE);
 			ReturnFlag(self);
@@ -707,20 +667,14 @@
 	// if no teams are found, spawn defaults
 	if (find(world, classname, "ctf_team") == world)
 		ctf_spawnteams();
+
+	ScoreRules_ctf();
 };
 
 void ctf_init()
 {
 	local entity e;
 
-	caps_team1 = caps_team2 = 0;
-	registercvar("capturelimit", "8");
-	registercvar("g_ctf_win_mode", "0");
-	//registercvar("g_overtime", "1");
-	registercvar("g_ctf_flagscore_kill", "6");
-	registercvar("g_ctf_flagpenalty_drop", "0");
-	//addstat(STAT_CTF_CAPTURES, AS_INT, captures);
-	//addstat(STAT_CTF_STATE, AS_FLOAT_TRUNCATED, ctf_state);
 	e = spawn();
 	e.think = ctf_delayedinit;
 	e.nextthink = time + 0.1;
@@ -816,7 +770,7 @@
 			if(pl.iscommander) { // don't reassign if alreay there
 				return;
 			}
-			if(plmax == world || plmax.frags < pl.frags)
+			if(plmax == world || plmax.frags < pl.frags) <<<<<<<<<<<<<<<<< BROKEN in new scoring system
 				plmax = pl;
 		}
 	}

Modified: branches/nexuiz-2.0/data/qcsrc/server/defs.qh
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/defs.qh	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/server/defs.qh	2008-07-31 06:23:51 UTC (rev 3991)
@@ -16,6 +16,7 @@
 float g_domination, g_ctf, g_tdm, g_keyhunt, g_onslaught, g_assault, g_arena, g_lms, g_runematch;
 float g_cloaked, g_footsteps, g_grappling_hook, g_instagib, g_laserguided_missile, g_midair, g_minstagib, g_nixnex, g_nixnex_with_laser, g_norecoil, g_rocketarena, g_vampire, g_minstagib_invis_alpha;
 float g_tourney;
+float g_ctf_win_mode;
 float tourneyInMatchStage;
 
 float sv_cheats;
@@ -208,11 +209,11 @@
 float WEP_NEX				= 7; // float	IT_NEX					= 32;
 float WEP_HAGAR				= 8; // float	IT_HAGAR				= 64;
 float WEP_ROCKET_LAUNCHER	= 9; // float	IT_ROCKET_LAUNCHER		= 128;
-
+float WEP_ANTINEX			= 10;
 // For weapon cycling commands
 float WEP_FIRST				= 1;
-float WEP_LAST				= 9;
-float WEP_COUNT             = 10;
+float WEP_LAST				= 10;
+float WEP_COUNT             = 11;
 
 void(entity client, string s) centerprint_builtin = #73;
 .vector dest1, dest2;
@@ -289,7 +290,6 @@
 float timelimit_orig;
 
 .float winning;
-.float deaths;
 .float jointime;
 
 float isJoinAllowed();
@@ -442,6 +442,5 @@
 
 float next_pingtime;
 
-.float captures;
-.float returns;
-float caps_team1, caps_team2;
+.float Version;
+.float(entity to) SendEntity;

Modified: branches/nexuiz-2.0/data/qcsrc/server/domination.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/domination.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/server/domination.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -15,6 +15,10 @@
 Note: The only teams who can use dom control points are identified by spawnfunc_dom_team entities (if none exist these default to red and blue and use only quake models/sounds).
 */
 
+#define DOMPOINTFRAGS frags
+
+.float enemy_playerid;
+
 void() dom_controlpoint_setup;
 
 void LogDom(string mode, float team_before, entity actor)
@@ -58,6 +62,10 @@
 	//bprint("\n");
 
 	bprint("^3", head.netname, "^3", self.message, "\n");
+	if(self.enemy.playerid == self.enemy_playerid)
+		PlayerScore_Add(self.enemy, SP_DOM_TAKES, 1);
+	else
+		self.enemy = world;
 
 	if (head.noise != "")
 		sound(self, CHAN_BODY, head.noise, 1, ATTN_NORM);
@@ -100,8 +108,7 @@
 {
 	local entity head;
 	local float waittime;
-	local float teamfragamt;
-	local float individualfragamt;
+	local float fragamt;
 
 	self.nextthink = time + 0.1;
 
@@ -120,43 +127,27 @@
 		waittime = self.wait;
 	self.delay = time + waittime;
 
-	// give credit to all players of the team (rewards large teams)
+	// give credit to the team
 	// NOTE: this defaults to 0
-	if (self.goalentity.netname)
+	if (self.goalentity.netname != "")
 	{
-		teamfragamt = cvar("g_domination_point_teamamt");
+		fragamt = cvar("g_domination_point_amt");
+		if(!fragamt)
+			fragamt = self.DOMPOINTFRAGS;
+		TeamScore_AddToTeam(self.goalentity.team, ST_SCORE, fragamt);
+		TeamScore_AddToTeam(self.goalentity.team, ST_DOM_TICKS, fragamt);
 
-		FOR_EACH_PLAYER(head)
-			if (head.team == self.goalentity.team)
-				UpdateFrags(head, teamfragamt);
-	}
-
-	// if the player left the game, changed teams or became spectator, we have to find another player on the same team to give credit to
-	if (!self.enemy.flags || self.enemy.team != self.goalentity.team || self.enemy.killcount == -666) // flags is zero on removed clients
-	{
-		other = self.enemy;
-		FOR_EACH_PLAYER(head)
-			if (head.team == self.goalentity.team)
-				self.enemy = head;
-		if(self.enemy == other) // search returned no matching player, reset dom point
+		// give credit to the individual player, if he is still there
+		if (self.enemy.playerid == self.enemy_playerid)
 		{
-			dom_controlpoint_setup();
-			self.enemy = world;
-			self.cnt = 0;
-			self.aiment = world;
+			PlayerScore_Add(self.enemy, SP_SCORE, fragamt);
+			PlayerScore_Add(self.enemy, SP_DOM_TICKS, fragamt);
 		}
+		else
+			self.enemy = world;
 	}
+}
 
-	// give credit to the individual player
-	if (self.enemy)
-	{
-		individualfragamt = cvar("g_domination_point_amt");
-		if(!individualfragamt)
-			individualfragamt = self.frags;
-		UpdateFrags(self.enemy, individualfragamt);
-	}
-};
-
 void dompointtouch()
 {
 	local entity head;
@@ -198,6 +189,7 @@
 	self.skin = head.skin;
 
 	self.enemy = other; // individual player scoring
+	self.enemy_playerid = other.playerid;
 	dompoint_captured();
 };
 
@@ -246,7 +238,8 @@
 	self.model = "";
 	self.modelindex = 0;
 	// this would have to be changed if used in quakeworld
-	self.team = self.cnt + 1;
+	if(self.cnt)
+		self.team = self.cnt + 1; // WHY are these different anyway?
 };
 
 void dom_controlpoint_setup()
@@ -269,8 +262,8 @@
 	if(!self.message)
 		self.message = " has captured a control point";
 
-	if(!self.frags)
-		self.frags = 1;
+	if(!self.DOMPOINTFRAGS)
+		self.DOMPOINTFRAGS = 1;
 	if(!self.wait)
 		self.wait = 5;
 
@@ -591,6 +584,8 @@
 			}
 		}
 	}
+
+	ScoreRules_dom();
 };
 
 void dom_init()

Modified: branches/nexuiz-2.0/data/qcsrc/server/ent_cs.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/ent_cs.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/server/ent_cs.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -13,9 +13,6 @@
 // it did when I added this) But you have to use .Version
 // Capital V
 
-.float(entity to) SendEntity;
-.float Version;
-
 entity entcs_start;
 
 void entcs_init()
@@ -26,7 +23,7 @@
 	entcs_start.chain = world;
 };
 
-entity get_entcs_ent(float num)
+entity get_entcs_ent()
 {
 	entity entcs;
 	entcs = spawn();
@@ -104,7 +101,7 @@
 	print("Attaching ENTCS entity\n");
 
 	num = num_for_edict(self);
-	ent = get_entcs_ent(num);
+	ent = get_entcs_ent();
 
 	ent.classname = "entcs_sender";
 	ent.health = num;

Copied: branches/nexuiz-2.0/data/qcsrc/server/frags.txt (from rev 3990, trunk/data/qcsrc/server/frags.txt)
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/frags.txt	                        (rev 0)
+++ branches/nexuiz-2.0/data/qcsrc/server/frags.txt	2008-07-31 06:23:51 UTC (rev 3991)
@@ -0,0 +1,7 @@
+TODO: remove the .frags field, except for the following values:
+
+0 = regular player
+-2 = ???, used by arena
+-666 = spectator
+
+Anything else shall go through PlayerScore!

Modified: branches/nexuiz-2.0/data/qcsrc/server/g_damage.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/g_damage.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/server/g_damage.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -42,51 +42,67 @@
 
 void UpdateFrags(entity player, float f)
 {
-	player.frags += f;
-	player.totalfrags += f;
+	PlayerTeamScore_AddScore(player, f);
 }
 
 void GiveFrags (entity attacker, entity targ, float f)
 {
+	// TODO route through PlayerScores instead
 	if(gameover) return;
 
+	if(f < 0)
+	{
+		if(targ == attacker)
+		{
+			// suicide
+			PlayerScore_Add(attacker, SP_SUICIDES, 1);
+		}
+		else
+		{
+			// teamkill
+			PlayerScore_Add(attacker, SP_KILLS, -1); // or maybe add a teamkills field?
+		}
+	}
+	else
+	{
+		// regular frag
+		PlayerScore_Add(attacker, SP_KILLS, 1);
+	}
+
+	PlayerScore_Add(targ, SP_DEATHS, 1);
+
 	if(g_arena)
 		if(cvar("g_arena_roundbased"))
 			return;
 
-	if(g_domination)
+	// FIXME fix the mess this is (we have REAL points now!)
+	if(g_runematch)
 	{
-		if(cvar("g_domination_disable_frags"))
-			if(f > 0)
-				return;
+		f = RunematchHandleFrags(attacker, targ, f);
 	}
-	else if(g_runematch)
-	{
-		if(f > 0)
-			f = RunematchHandleFrags(attacker, targ, f);
-	}
 	else if(g_keyhunt)
 	{
 		f = kh_HandleFrags(attacker, targ, f);
 	}
 	else if(g_lms)
 	{
-		// count remaining lives, not frags in lms
-		targ.frags -= 1;
-		// keep track of the worst players lives
-		if(targ.frags < lms_lowest_lives)
-			lms_lowest_lives = targ.frags;
-		// player has no more lives left
-		if (!targ.frags)
+		// remove a life
+		float tl;
+		tl = PlayerScore_Add(targ, SP_LMS_LIVES, -1);
+		if(tl < lms_lowest_lives)
+			lms_lowest_lives = tl;
+		if(tl <= 0)
 		{
 			if(!lms_next_place)
 				lms_next_place = player_count;
-			targ.frags = -lms_next_place;
-			lms_next_place -= 1;
+			PlayerScore_Add(targ, SP_LMS_RANK, lms_next_place); // won't ever spawn again
+			--lms_next_place;
 		}
-		return;
+		f = 0;
 	}
 
+	attacker.totalfrags += f;
+
 	if(f)
 		UpdateFrags(attacker, f);
 }
@@ -321,7 +337,10 @@
 						bprint ("^1",s, "^1 was fragged by ", a, "\n");
 				}
 				if(g_ctf && targ.flagcarried)
+				{
 					GiveFrags(attacker, targ, cvar("g_ctf_flagscore_kill"));
+					PlayerScore_Add(attacker, SP_CTF_FCKILLS, 1);
+				}
 				else
 					GiveFrags(attacker, targ, 1);
 				if (targ.killcount > 2) {
@@ -442,7 +461,7 @@
 				else
 					bprint ("^1",s, "^1 died\n");
 			GiveFrags(targ, targ, -1);
-			if(targ.frags == -5) {
+			if(PlayerScore_Add(targ, SP_SCORE, 0) == -5) {
 				announce(targ, "announcer/male/botlike.ogg");
 			}
 

Modified: branches/nexuiz-2.0/data/qcsrc/server/g_world.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/g_world.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/server/g_world.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -47,14 +47,14 @@
 	else
 		dprint("*** fteqcc test: same-function-twice bug got FINALLY FIXED! HOORAY!\n");
 
-	world.frags = -10;
+	world.cnt = -10;
 	world.enemy = world;
-	world.enemy.frags += 10;
-	if(world.frags > 0.2 || world.frags < -0.2) // don't error out if it's just roundoff errors
+	world.enemy.cnt += 10;
+	if(world.cnt > 0.2 || world.cnt < -0.2) // don't error out if it's just roundoff errors
 		dprint("*** fteqcc test: found += bug\n");
 	else
 		dprint("*** fteqcc test: += bug got FINALLY FIXED! HOORAY!\n");
-	world.frags = 0;
+	world.cnt = 0;
 }
 
 /**
@@ -382,25 +382,7 @@
 
 string GetGametype()
 {
-	if (game == GAME_DEATHMATCH)
-		return "dm";
-	else if (game == GAME_TEAM_DEATHMATCH)
-		return "tdm";
-	else if (game == GAME_DOMINATION)
-		return "dom";
-	else if (game == GAME_CTF)
-		return "ctf";
-	else if (game == GAME_RUNEMATCH)
-		return "rune";
-	else if (game == GAME_LMS)
-		return "lms";
-	else if (game == GAME_KEYHUNT)
-		return "kh";
-	else if (game == GAME_ONSLAUGHT)
-		return "ons";
-	else if (game == GAME_ASSAULT)
-		return "as";
-	return "dm";
+	return GametypeNameFromType(game);
 }
 
 float IsSameGametype(string mapcfgname)
@@ -954,6 +936,7 @@
 	local float to_console;
 	local float to_eventlog;
 	local float to_file;
+	local float i;
 
 	to_console = cvar("sv_logscores_console");
 	to_eventlog = cvar("sv_eventlog");
@@ -992,14 +975,25 @@
 			fputs(file, strcat(s, "\n"));
 	}
 
+	s = strcat(":labels:player:", GetPlayerScoreString(world));
+	if(to_console)
+		ServerConsoleEcho(s, TRUE);
+	if(to_eventlog)
+		GameLogEcho(s, TRUE);
+	if(to_file)
+		fputs(file, strcat(s, "\n"));
+
 	FOR_EACH_CLIENT(other)
 	{
+		// FIXME make this use the new score system
 		if ((clienttype(other) == CLIENTTYPE_REAL) || (clienttype(other) == CLIENTTYPE_BOT && cvar("sv_logscores_bots")))
 		{
-			s = strcat(":player:", ftos(other.frags), ":");
-			s = strcat(s, ftos(other.deaths), ":");
+			s = strcat(":player:see-labels:", GetPlayerScoreString(other), ":");
 			s = strcat(s, ftos(rint(time - other.jointime)), ":");
-			s = strcat(s, ftos(other.team), ":");
+			if(other.classname == "player")
+				s = strcat(s, ftos(other.team), ":");
+			else
+				s = strcat(s, "spectator:");
 
 			if(to_console)
 				ServerConsoleEcho(strcat(s, other.netname), TRUE);
@@ -1010,19 +1004,27 @@
 		}
 	}
 
-	if(g_ctf)
-	if(cvar("g_ctf_win_mode") != 2)
+	if(teamplay)
 	{
-		s = strcat(":teamscores:", ftos(COLOR_TEAM1), ":");
-		s = strcat(s, ftos(caps_team1), ":");
-		s = strcat(s, ftos(COLOR_TEAM2), ":");
-		s = strcat(s, ftos(caps_team2));
+		s = strcat(":labels:teamscores:", GetTeamScoreString(0));
 		if(to_console)
 			ServerConsoleEcho(s, TRUE);
 		if(to_eventlog)
 			GameLogEcho(s, TRUE);
 		if(to_file)
 			fputs(file, strcat(s, "\n"));
+	
+		for(i = 1; i < 16; ++i)
+		{
+			s = strcat(":teamscores:see-labels:", GetTeamScoreString(i));
+			s = strcat(s, ":", ftos(i));
+			if(to_console)
+				ServerConsoleEcho(s, TRUE);
+			if(to_eventlog)
+				GameLogEcho(s, TRUE);
+			if(to_file)
+				fputs(file, strcat(s, "\n"));
+		}
 	}
 
 	if(to_console)
@@ -1220,15 +1222,6 @@
 			head.winning = 1;
 }
 
-// clear frags for all players but the team given (when they ran out of spawnpoints)
-void ClearFragsForEveryoneBut(.float field, float value)
-{
-	entity head;
-	FOR_EACH_PLAYER(head)
-		if(head.field != value)
-			head.frags = max(head.frags, 0);
-}
-
 // clear the .winning flags
 void ClearWinners(void)
 {
@@ -1282,11 +1275,11 @@
 
 	// first player has left the game for dying too much? Nobody else can get in.
 	if(lms_lowest_lives < 1)
-		return FALSE;
+		return 0;
 
 	if(!cvar("g_lms_join_anytime"))
 		if(lms_lowest_lives < fl - cvar("g_lms_last_join"))
-			return FALSE;
+			return 0;
 
 	return bound(1, lms_lowest_lives, fl);
 }
@@ -1345,13 +1338,12 @@
 
 }
 
-
 // LMS winning condition: game terminates if and only if there's at most one
 // one player who's living lives. Top two scores being equal cancels the time
 // limit.
 float WinningCondition_LMS()
 {
-	entity head;
+	entity head, head2;
 	float have_player;
 	float have_players;
 	float l;
@@ -1363,8 +1355,8 @@
 	head = find(world, classname, "player");
 	if(head)
 		have_player = TRUE;
-	head = find(head, classname, "player");
-	if(head)
+	head2 = find(head, classname, "player");
+	if(head2)
 		have_players = TRUE;
 
 	if(have_player)
@@ -1377,15 +1369,20 @@
 		else
 		{
 			// exactly one player?
+
+			ClearWinners();
+			SetWinners(winning, 0); // NOTE: exactly one player is still "player", so this works out
+
 			if(l)
 			{
-				// but no game has taken place yet
+				// game still running (that is, nobody got removed from the game by a frag yet)? then continue
+				return WINNING_NO;
 			}
 			else
 			{
 				// a winner!
-				ClearWinners(); SetWinners(winning, 0); // NOTE: exactly one player is still "player", so this works out
-				dprint("Have a winner, ending game.\n");
+				// and assign him his first place
+				PlayerScore_Add(head, SP_LMS_RANK, 1);
 				return WINNING_YES;
 			}
 		}
@@ -1407,195 +1404,19 @@
 	}
 
 	// When we get here, we have at least two players who are actually LIVING,
-	// or one player who is still waiting for a victim to join the server. Now
-	// check if the top two players have equal score.
+	// now check if the top two players have equal score.
+	WinningConditionHelper();
 
-	checkrules_leaderfrags = 0;
-	checkrules_equality = FALSE;
-	FOR_EACH_PLAYER(head)
-	{
-		if(head.frags > checkrules_leaderfrags)
-		{
-			checkrules_leaderfrags = head.frags;
-			checkrules_equality = FALSE;
-		}
-		else if(head.frags > 0 && head.frags == checkrules_leaderfrags)
-			checkrules_equality = TRUE;
-	}
-
-	SetWinners(frags, checkrules_leaderfrags);
-
-	// The top two players have the same amount of lives? No timelimit then,
-	// enter overtime...
-
-	if(checkrules_equality)
+	ClearWinners();
+	if(WinningConditionHelper_winner)
+		WinningConditionHelper_winner.winning = TRUE;
+	if(WinningConditionHelper_equality)
 		return WINNING_NEVER;
 
 	// Top two have different scores? Way to go for our beloved TIMELIMIT!
 	return WINNING_NO;
 }
 
-// DM winning condition: game terminates if a player reached the fraglimit,
-// unless the first two players have the same score. The latter case also
-// breaks the time limit.
-float WinningCondition_MaxIndividualScore(float fraglimit)
-{
-	float checkrules_oldleaderfrags;
-	entity head;
-
-	checkrules_oldleaderfrags = checkrules_leaderfrags;
-	checkrules_leaderfrags = 0;
-	checkrules_equality = FALSE;
-	FOR_EACH_PLAYER(head)
-	{
-		if(head.frags > checkrules_leaderfrags)
-		{
-			checkrules_leaderfrags = head.frags;
-			checkrules_equality = FALSE;
-		}
-		else if(head.frags > 0 && head.frags == checkrules_leaderfrags)
-			checkrules_equality = TRUE;
-	}
-
-	if(checkrules_leaderfrags > 0)
-		SetWinners(frags, checkrules_leaderfrags);
-	else
-		ClearWinners();
-
-	if (!g_runematch)
-		if (checkrules_leaderfrags != checkrules_oldleaderfrags)
-		{
-			if (checkrules_leaderfrags == fraglimit - 1)
-				sound(world, CHAN_AUTO, "announcer/robotic/1fragleft.wav", 1, ATTN_NONE);
-			else if (checkrules_leaderfrags == fraglimit - 2)
-				sound(world, CHAN_AUTO, "announcer/robotic/2fragsleft.wav", 1, ATTN_NONE);
-			else if (checkrules_leaderfrags == fraglimit - 3)
-				sound(world, CHAN_AUTO, "announcer/robotic/3fragsleft.wav", 1, ATTN_NONE);
-		}
-
-	return GetWinningCode(fraglimit && checkrules_leaderfrags >= fraglimit, checkrules_equality);
-}
-
-float WinningConditionBase_Teamplay(float fraglimit)
-{
-	tdm_old_score = tdm_max_score;
-	tdm_max_score = max4(team1_score, team2_score, team3_score, team4_score);
-
-	checkrules_equality =
-	(
-		(tdm_max_score > 0)
-		&&
-		(
-			  (team1_score == tdm_max_score)
-			+ (team2_score == tdm_max_score)
-			+ (team3_score == tdm_max_score)
-			+ (team4_score == tdm_max_score)
-			>= 2));
-
-	ClearWinners();
-	if(tdm_max_score > 0)
-	{
-		if(team1_score == tdm_max_score)
-			AddWinners(team, COLOR_TEAM1);
-		if(team2_score == tdm_max_score)
-			AddWinners(team, COLOR_TEAM2);
-		if(team3_score == tdm_max_score)
-			AddWinners(team, COLOR_TEAM3);
-		if(team4_score == tdm_max_score)
-			AddWinners(team, COLOR_TEAM4);
-	}
-
-	if(!g_runematch && !g_domination && !g_ctf)
-		if(tdm_max_score != tdm_old_score)
-		{
-			if(tdm_max_score == fraglimit - 1)
-				sound(world, CHAN_AUTO, "announcer/robotic/1fragleft.wav", 1, ATTN_NONE);
-			else if(tdm_max_score == fraglimit - 2)
-				sound(world, CHAN_AUTO, "announcer/robotic/2fragsleft.wav", 1, ATTN_NONE);
-			else if(tdm_max_score == fraglimit - 3)
-				sound(world, CHAN_AUTO, "announcer/robotic/3fragsleft.wav", 1, ATTN_NONE);
-		}
-
-	return GetWinningCode(fraglimit && tdm_max_score >= fraglimit, checkrules_equality);
-}
-
-// TDM winning condition: game terminates if a team's score sum reached the
-// fraglimit, unless the first two teams have the same total score. The latter
-// case also breaks the time limit.
-float WinningCondition_MaxTeamSum(float fraglimit)
-{
-	entity head;
-
-	team1_score = team2_score = team3_score = team4_score = 0;
-
-	FOR_EACH_PLAYER(head)
-	{
-		if(head.team == COLOR_TEAM1)
-			team1_score += head.frags;
-		else if(head.team == COLOR_TEAM2)
-			team2_score += head.frags;
-		else if(head.team == COLOR_TEAM3)
-			team3_score += head.frags;
-		else if(head.team == COLOR_TEAM4)
-			team4_score += head.frags;
-	}
-
-	return WinningConditionBase_Teamplay(fraglimit);
-}
-
-// DOM/CTF winning condition: game terminates if the max of a team's players'
-// score reached the fraglimit, unless the first two teams have the same
-// maximum score. The latter case also breaks the time limit.
-float WinningCondition_MaxTeamMax(float fraglimit)
-{
-	entity head;
-
-	team1_score = team2_score = team3_score = team4_score = 0;
-
-	FOR_EACH_PLAYER(head)
-	{
-		if(head.team == COLOR_TEAM1)
-		{
-			if(head.frags > team1_score)
-				team1_score = head.frags;
-		}
-		else if(head.team == COLOR_TEAM2)
-		{
-			if(head.frags > team2_score)
-				team2_score = head.frags;
-		}
-		else if(head.team == COLOR_TEAM3)
-		{
-			if(head.frags > team3_score)
-				team3_score = head.frags;
-		}
-		else if(head.team == COLOR_TEAM4)
-		{
-			if(head.frags > team4_score)
-				team4_score = head.frags;
-		}
-	}
-
-	return WinningConditionBase_Teamplay(fraglimit);
-}
-
-float WinningCondition_CTF(float capturelimit, float fraglimit)
-{
-	if(cvar("g_ctf_win_mode") == 2)
-		return WinningCondition_MaxTeamSum(fraglimit);
-	
-	team1_score = caps_team1;
-	team2_score = caps_team2;
-	team3_score = team4_score = 0;
-
-	if(team1_score == team2_score && cvar("g_ctf_win_mode"))
-	{
-		return WinningCondition_MaxTeamSum(0);
-	}
-
-	return WinningConditionBase_Teamplay(capturelimit);
-}
-
 void print_to(entity e, string s)
 {
 	if(e)
@@ -1604,66 +1425,6 @@
 		ServerConsoleEcho(s, TRUE);
 }
 
-void PrintScoreboardFor(entity e, string name, string colorcode, float whichteam)
-{
-	entity head;
-	float v;
-	float teamvalue;
-	float fragtotal;
-	string s;
-	float found;
-	found = FALSE;
-	teamvalue = 0;
-	FOR_EACH_PLAYER(head)
-	{
-		if(!whichteam || head.team == whichteam)
-		{
-			if(name != "")
-				if(!found)
-					print_to(e, strcat(" ", colorcode, name, ":"));
-			found = TRUE;
-			fragtotal = fragtotal + head.frags;
-			s = ftos(head.frags);
-			s = strcat(s, "/", ftos(head.deaths));
-			s = strcat(s, " @ ", ftos(head.ping));
-			if(clienttype(head) == CLIENTTYPE_BOT)
-				s = strcat(s, "botms");
-			else
-				s = strcat(s, "ms");
-			v = PlayerValue(head);
-			teamvalue += v;
-			s = strcat(s, " / ", ftos(v));
-			print_to(e, strcat("  ", colorcode, head.netname, colorcode, " (", s, ")"));
-		}
-	}
-	if(whichteam && found)
-	{
-		s = ftos(fragtotal);
-		s = strcat(s, " / ", ftos(teamvalue));
-		print_to(e, strcat(colorcode, "  (total: ", s, ")"));
-	}
-}
-
-void PrintScoreboard(entity e)
-{
-	print_to(e, strcat("Time:      ", ftos(time / 60)));
-	print_to(e, strcat("Timelimit: ", ftos(cvar("timelimit"))));
-	print_to(e, strcat("Fraglimit: ", ftos(cvar("fraglimit"))));
-	print_to(e, "Scoreboard:");
-	if(teams_matter)
-	{
-		PrintScoreboardFor(e, "Red", "^1", COLOR_TEAM1);
-		PrintScoreboardFor(e, "Blue", "^4", COLOR_TEAM2);
-		PrintScoreboardFor(e, "Yellow", "^3", COLOR_TEAM3);
-		PrintScoreboardFor(e, "Pink", "^6", COLOR_TEAM4);
-	}
-	else
-	{
-		PrintScoreboardFor(e, "", "^7", 0);
-	}
-	print_to(e, ".");
-}
-
 void ShuffleMaplist()
 {
 	string result;
@@ -1714,6 +1475,23 @@
 	cvar_set("g_maplist", result);
 }
 
+float WinningCondition_Scores(float limit)
+{
+	// TODO make everything use THIS winning condition (except LMS)
+	WinningConditionHelper();
+	
+	ClearWinners();
+	if(WinningConditionHelper_winner)
+		WinningConditionHelper_winner.winning = 1;
+	if(WinningConditionHelper_winnerteam >= 0)
+		SetWinners(team, WinningConditionHelper_winnerteam);
+
+	if(WinningConditionHelper_topscore == 0)
+		WinningConditionHelper_equality = 0;
+	
+	return GetWinningCode(limit && (WinningConditionHelper_topscore >= limit), WinningConditionHelper_equality);
+}
+
 float WinningCondition_RanOutOfSpawns()
 {
 	entity head;
@@ -1758,26 +1536,21 @@
 	}
 	else if(team1_score + team2_score + team3_score + team4_score == 1)
 	{
-		if(team1_score)
+		float t, i;
+		if(team1_score) t = COLOR_TEAM1;
+		if(team2_score) t = COLOR_TEAM2;
+		if(team3_score) t = COLOR_TEAM3;
+		if(team4_score) t = COLOR_TEAM4;
+		CheckAllowedTeams(world);
+		for(i = 0; i < MAX_TEAMSCORE; ++i)
 		{
-			AddWinners(team, COLOR_TEAM1);
-			ClearFragsForEveryoneBut(team, COLOR_TEAM1);
+			if(t != COLOR_TEAM1) if(c1 >= 0) TeamScore_AddToTeam(COLOR_TEAM1, i, -1000);
+			if(t != COLOR_TEAM2) if(c2 >= 0) TeamScore_AddToTeam(COLOR_TEAM2, i, -1000);
+			if(t != COLOR_TEAM3) if(c3 >= 0) TeamScore_AddToTeam(COLOR_TEAM3, i, -1000);
+			if(t != COLOR_TEAM4) if(c4 >= 0) TeamScore_AddToTeam(COLOR_TEAM4, i, -1000);
 		}
-		if(team2_score)
-		{
-			AddWinners(team, COLOR_TEAM2);
-			ClearFragsForEveryoneBut(team, COLOR_TEAM2);
-		}
-		if(team3_score)
-		{
-			AddWinners(team, COLOR_TEAM3);
-			ClearFragsForEveryoneBut(team, COLOR_TEAM3);
-		}
-		if(team4_score)
-		{
-			AddWinners(team, COLOR_TEAM4);
-			ClearFragsForEveryoneBut(team, COLOR_TEAM4);
-		}
+
+		AddWinners(team, t);
 		return WINNING_YES;
 	}
 	else
@@ -1796,7 +1569,6 @@
 	local float status;
 	local float timelimit;
 	local float fraglimit;
-	local float capturelimit;
 
 	VoteThink();
 	MapVote_Think();
@@ -1825,7 +1597,6 @@
 
 	timelimit = cvar("timelimit") * 60;
 	fraglimit = cvar("fraglimit");
-	capturelimit = cvar("capturelimit");
 
 	if(checkrules_overtimeend)
 	{
@@ -1861,7 +1632,7 @@
 	}
 	else if(g_assault)
 	{
-		status = WinningCondition_Assault();
+		status = WinningCondition_Assault(); // TODO remove this?
 	}
 	else if(g_lms)
 	{
@@ -1869,28 +1640,11 @@
 	}
 	else if (g_onslaught)
 	{
-		status = WinningCondition_Onslaught();
+		status = WinningCondition_Onslaught(); // TODO remove this?
 	}
-	else if(g_ctf)
-	{
-		status = WinningCondition_CTF(capturelimit, fraglimit);
-	}
 	else
 	{
-		if(teams_matter)
-		{
-			if(g_tdm || g_runematch || g_ctf || g_domination || g_keyhunt)
-				status = WinningCondition_MaxTeamSum(fraglimit);
-			//else if()
-			//	status = WinningCondition_MaxTeamMax(fraglimit);
-			else
-			{
-				dprint("div0: How can this happen?\n");
-				status = WinningCondition_MaxTeamMax(fraglimit);
-			}
-		}
-		else
-			status = WinningCondition_MaxIndividualScore(fraglimit);
+		status = WinningCondition_Scores(fraglimit);
 	}
 
 	if(status == WINNING_STARTOVERTIME)
@@ -2059,7 +1813,7 @@
 	mapvote_screenshot_dir = strzone(mapvote_screenshot_dir);
 
 	if(!cvar("g_maplist_textonly"))
-		MapVote_SendData(MSG_BROADCAST);
+		MapVote_SendData(MSG_ALL);
 }
 
 void MapVote_SendPicture(float id)
@@ -2069,7 +1823,7 @@
 	WriteByte(MSG_ONE, TE_CSQC_MAPVOTE);
 	WriteByte(MSG_ONE, MAPVOTE_NET_PIC);
 	WriteByte(MSG_ONE, id);
-	WritePicture(MSG_ONE, strcat(mapvote_screenshot_dir, "/", mapvote_maps[id]), 1024);
+	WritePicture(MSG_ONE, strcat(mapvote_screenshot_dir, "/", mapvote_maps[id]), 3072);
 }
 
 float GameCommand_MapVote(string cmd)
@@ -2114,7 +1868,7 @@
   	WriteByte(targ, mapvote_count);
   	WriteByte(targ, mapvote_abstain);
   	WriteByte(targ, mapvote_detail);
-	WriteByte(targ, mapvote_timeout - time);
+	WriteCoord(targ, mapvote_timeout);
  	if(mapvote_count <= 8)
  		WriteByte(targ, MapVote_GetMapMask());
  	else
@@ -2534,6 +2288,25 @@
 	return TRUE;
 }
 
+void RestoreGame()
+{
+	// Loaded from a save game
+	// some things then break, so let's work around them...
+
+	// Progs DB (capture records)
+	if(sv_cheats)
+		ServerProgsDB = db_create();
+	else
+		ServerProgsDB = db_load("server.db");
+
+	// Mapinfo
+#ifdef MAPINFO
+	MapInfo_Shutdown();
+	MapInfo_Enumerate();
+	MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), 1);
+#endif
+}
+
 void SV_Shutdown()
 {
 	if(world_initialized)

Modified: branches/nexuiz-2.0/data/qcsrc/server/gamecommand.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/gamecommand.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/server/gamecommand.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -48,12 +48,6 @@
 	if(GameCommand_Generic(command))
 		return;
 
-	if(argv(0) == "teamstatus")
-	{
-		PrintScoreboard(world);
-		return;
-	}
-
 	if(argv(0) == "printstats")
 	{
 		DumpStats(FALSE);
@@ -208,6 +202,10 @@
 			print("Client not found\n");
 		return;
 	}
+	if (argv(0) == "sdp")
+	{
+		Score_DebugPrint();
+	}
 
 	print("Invalid command. For a list of supported commands, try sv_cmd help.\n");
 }

Modified: branches/nexuiz-2.0/data/qcsrc/server/keyhunt.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/keyhunt.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/server/keyhunt.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -55,6 +55,7 @@
 .float kh_droptime;
 .float kh_dropperteam;
 .entity kh_previous_owner;
+.float kh_previous_owner_playerid;
 
 string kh_sound_capture = "sound/kh/capture.wav";
 string kh_sound_destroy = "sound/kh/destroy.wav";
@@ -253,6 +254,7 @@
 	// let key.team stay
 	key.modelindex = kh_key_dropped;
 	key.kh_previous_owner = key.owner;
+	key.kh_previous_owner_playerid = key.owner.playerid;
 }
 
 void kh_Key_AssignTo(entity key, entity player)  // runs every time a key is picked up or assigned. Runs prior to kh_key_attach
@@ -436,7 +438,10 @@
 	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);
+		PlayerScore_Add(player, SP_KH_PICKUPS, 1);
+	}
 	key.kh_dropperteam = 0;
 	bprint(player.netname, "^7 picked up the ", key.netname, "\n");
 
@@ -462,6 +467,7 @@
 		while((key = player.kh_next))
 		{
 			kh_Scores_Event(player, key, "losekey", 0, 0);
+			PlayerScore_Add(player, SP_KH_LOSSES, 1);
 			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());
@@ -599,7 +605,12 @@
 	// twice the score for 3 team games, three times the score for 4 team games!
 	// note: for a win by destroying the key, this should NOT be applied
 	FOR_EACH_KH_KEY(key)
-		kh_Scores_Event(key.owner, key, "capture", DistributeEvenly_Get(1), 0);
+	{
+		float f;
+		f = DistributeEvenly_Get(1);
+		kh_Scores_Event(key.owner, key, "capture", f, 0);
+		PlayerTeamScore_Add(key.owner, SP_KH_CAPS, ST_KH_CAPS, 1);
+	}
 
 	first = TRUE;
 	FOR_EACH_KH_KEY(key)
@@ -645,6 +656,7 @@
 	entity player, key, attacker;
 	float players;
 	float keys;
+	float f;
 
 	attacker = world;
 	if(lostkey.pusher)
@@ -660,6 +672,7 @@
 			kh_Scores_Event(lostkey.kh_previous_owner, world, "pushed", 0, -cvar("g_balance_keyhunt_score_push"));
 			// don't actually GIVE him the -nn points, just log
 		kh_Scores_Event(attacker, world, "push", cvar("g_balance_keyhunt_score_push"), 0);
+		PlayerScore_Add(attacker, SP_KH_PUSHES, 1);
 		centerprint(attacker, "Your push is the best!\n\n\n");
 		bprint("The ", ColoredTeamName(teem), "^7 could not take care of the ", lostkey.netname, "^7 when ", attacker.netname, "^7 came\n");
 	}
@@ -680,11 +693,17 @@
 			kh_Scores_Event(lostkey.kh_previous_owner, world, "destroyed", 0, -cvar("g_balance_keyhunt_score_destroyed"));
 			// don't actually GIVE him the -nn points, just log
 
+		if(lostkey.kh_previous_owner.playerid == lostkey.kh_previous_owner_playerid)
+			PlayerScore_Add(lostkey.kh_previous_owner, SP_KH_DESTROYS, 1);
+
 		DistributeEvenly_Init(cvar("g_balance_keyhunt_score_destroyed"), keys * of + players);
 
 		FOR_EACH_KH_KEY(key)
 			if(key.owner && key.team != teem)
-				kh_Scores_Event(key.owner, world, "destroyed_holdingkey", DistributeEvenly_Get(of), 0);
+			{
+				f = DistributeEvenly_Get(of);
+				kh_Scores_Event(key.owner, world, "destroyed_holdingkey", f, 0);
+			}
 
 		fragsleft = DistributeEvenly_Get(players);
 
@@ -707,7 +726,10 @@
 
 			FOR_EACH_PLAYER(player)
 				if(player.team == thisteam)
-					kh_Scores_Event(player, world, "destroyed", DistributeEvenly_Get(1), 0);
+				{
+					f = DistributeEvenly_Get(1);
+					kh_Scores_Event(player, world, "destroyed", f, 0);
+				}
 
 			--j;
 		}
@@ -884,6 +906,8 @@
 		else
 		{
 			kh_Scores_Event(attacker, targ.kh_next, "carrierfrag", cvar("g_balance_keyhunt_score_carrierfrag")-1, 0);
+			PlayerScore_Add(attacker, SP_KH_KCKILLS, 1);
+			// the frag gets added later
 		}
 	}
 
@@ -951,6 +975,8 @@
 	setmodel(kh_controller, "");
 
 	addstat(STAT_KH_KEYS, AS_INT, kh_state);
+
+	ScoreRules_kh(kh_teams);
 }
 
 void kh_finalize()

Modified: branches/nexuiz-2.0/data/qcsrc/server/miscfunctions.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/miscfunctions.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/server/miscfunctions.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -109,11 +109,20 @@
 	}
 }
 
+string STR_PLAYER = "player";
+
+#if 0
 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
-string STR_PLAYER = "player";
 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
+#else
+#define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
+#define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
+#define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
+#define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
+#define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
+#endif
 
 // change that to actually calling strcat when running on an engine without
 // unlimited tempstrings:
@@ -524,44 +533,6 @@
 	cvar_set("developer", ftos(dev));
 }
 
-void DistributeFragsAmongTeam(entity p, float targetteam, float factor)
-{
-	float nTeam;
-	entity head;
-	float f;
-
-	if(!teams_matter)
-		return;
-
-	//if(p.frags < 0)
-	//{
-	//	p.frags = 0; // do not harm the new team!
-	//	return; // won't distribute negative scores
-	//}
-
-	if(p.frags == -666)
-		return;
-
-	f = ceil(factor * p.frags);
-	p.frags = p.frags - f;
-
-	nTeam = 0;
-	FOR_EACH_PLAYER(head)
-		if(head != p)
-			if(head.team == targetteam)
-				nTeam = nTeam + 1;
-
-	if(nTeam == 0)
-		return;
-
-	DistributeEvenly_Init(f, nTeam);
-
-	FOR_EACH_PLAYER(head)
-		if(head != p)
-			if(head.team == targetteam)
-				head.frags = head.frags + DistributeEvenly_Get(1);
-}
-
 string Team_ColorCode(float teamid)
 {
 	if(teamid == COLOR_TEAM1)
@@ -783,6 +754,7 @@
 	sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
 	sv_pogostick = cvar("sv_pogostick");
 	sv_doublejump = cvar("sv_doublejump");
+	g_ctf_win_mode = cvar("g_ctf_win_mode");
 
 	g_pickup_shells                    = cvar("g_pickup_shells");
 	g_pickup_shells_max                = cvar("g_pickup_shells_max");

Modified: branches/nexuiz-2.0/data/qcsrc/server/progs.src
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/progs.src	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/server/progs.src	2008-07-31 06:23:51 UTC (rev 3991)
@@ -18,6 +18,8 @@
 
 ../common/mapinfo.qh
 
+scores.qh
+
 ipban.qh
 
 keyhunt.qh
@@ -26,6 +28,8 @@
 
 vote.qh
 
+scores_rules.qc
+
 miscfunctions.qc
 
 waypointsprites.qc
@@ -120,4 +124,6 @@
 
 
 //// tZork Turrets ////
-tturrets/include/turret_tturrets.qh
\ No newline at end of file
+tturrets/include/turret_tturrets.qh
+
+scores.qc

Copied: branches/nexuiz-2.0/data/qcsrc/server/scores.qc (from rev 3990, trunk/data/qcsrc/server/scores.qc)
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/scores.qc	                        (rev 0)
+++ branches/nexuiz-2.0/data/qcsrc/server/scores.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -0,0 +1,479 @@
+.float scores[MAX_SCORE];
+.float teamscores[MAX_TEAMSCORE];
+
+.entity scorekeeper;
+entity teamscorekeepers[16];
+string scores_label[MAX_SCORE];
+float scores_flags[MAX_SCORE];
+string teamscores_label[MAX_TEAMSCORE];
+float teamscores_flags[MAX_TEAMSCORE];
+float teamscores_entities_count;
+var .float scores_primary;
+var .float teamscores_primary;
+
+void Net_LinkEntity(entity e)
+{
+	e.model = "net_entity";
+	e.modelindex = 1;
+	e.effects = EF_NODEPTHTEST | EF_LOWPRECISION;
+}
+
+vector ScoreField_Compare(entity t1, entity t2, .float field, float fieldflags, vector previous) // returns: cmp value, best prio
+{
+	if(!(fieldflags & SFL_SORT_PRIO_MASK)) // column does not sort
+		return previous;
+	if(fieldflags & SFL_SORT_PRIO_MASK < previous_y)
+		return previous;
+	if(t1.field == t2.field)
+		return previous;
+
+	previous_y = fieldflags & SFL_SORT_PRIO_MASK;
+
+	if(fieldflags & SFL_LOWER_IS_BETTER)
+		previous_x = (t2.field - t1.field);
+	else
+		previous_x = (t1.field - t2.field);
+
+	return previous;
+}
+
+/*
+ * teamscore entities
+ */
+
+float TeamScore_SendEntity(entity to)
+{
+	float i;
+
+	WriteByte(MSG_ENTITY, ENT_CLIENT_TEAMSCORES);
+	WriteByte(MSG_ENTITY, self.team - 1);
+	for(i = 0; i < MAX_TEAMSCORE; ++i)
+		WriteShort(MSG_ENTITY, self.teamscores[i]);
+
+	return TRUE;
+}
+
+void TeamScore_Spawn(float t, string name)
+{
+	entity ts;
+	ts = spawn();
+	ts.classname = "csqc_score_team";
+	ts.SendEntity = TeamScore_SendEntity;
+	ts.netname = name; // not used yet, FIXME
+	ts.Version = 1; // immediately send, so csqc knows about the team
+	ts.team = t;
+	Net_LinkEntity(ts);
+	teamscorekeepers[t - 1] = ts;
+	++teamscores_entities_count;
+}
+
+float TeamScore_AddToTeam(float t, float scorefield, float score)
+{
+	entity s;
+	if(!scores_initialized) return 0; // FIXME remove this when everything uses this system
+	if(t <= 0 || t >= 16)
+		error("Adding score to invalid team!");
+	s = teamscorekeepers[t - 1];
+	if(!s)
+		error("Adding score to unknown team!");
+	if(score)
+		s.Version += 1;
+	return (s.(teamscores[scorefield]) += score);
+}
+
+float TeamScore_Add(entity player, float scorefield, float score)
+{
+	return TeamScore_AddToTeam(player.team, scorefield, score);
+}
+
+float TeamScore_Compare(entity t1, entity t2)
+{
+	if(!t1 || !t2) return (!t2) - !t1;
+
+	vector result;
+	float i;
+	for(i = 0; i < MAX_TEAMSCORE; ++i)
+	{
+		var .float f;
+		f = teamscores[i];
+		result = ScoreField_Compare(t1, t2, f, teamscores_flags[i], result);
+	}
+	return result_x;
+}
+
+/*
+ * the scoreinfo entity
+ */
+
+void ScoreInfo_SetLabel_PlayerScore(float i, string label, float scoreflags)
+{
+	scores_label[i] = label;
+	scores_flags[i] = scoreflags;
+	if(scoreflags & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_PRIMARY)
+		scores_primary = scores[i];
+}
+
+void ScoreInfo_SetLabel_TeamScore(float i, string label, float scoreflags)
+{
+	teamscores_label[i] = label;
+	teamscores_flags[i] = scoreflags;
+	if(scoreflags & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_PRIMARY)
+		teamscores_primary = teamscores[i];
+}
+
+float ScoreInfo_SendEntity(entity to)
+{
+	WriteByte(MSG_ENTITY, ENT_CLIENT_SCORES_INFO);
+	float i;
+	WriteByte(MSG_ENTITY, game);
+	for(i = 0; i < MAX_SCORE; ++i)
+	{
+		WriteString(MSG_ENTITY, scores_label[i]);
+		WriteByte(MSG_ENTITY, scores_flags[i]);
+	}
+	for(i = 0; i < MAX_TEAMSCORE; ++i)
+	{
+		WriteString(MSG_ENTITY, teamscores_label[i]);
+		WriteByte(MSG_ENTITY, teamscores_flags[i]);
+	}
+	return TRUE;
+}
+
+void ScoreInfo_Init(float teams)
+{
+	scores_initialized = 1;
+	if(teams >= 1)
+		TeamScore_Spawn(COLOR_TEAM1, "Red");
+	if(teams >= 2)
+		TeamScore_Spawn(COLOR_TEAM2, "Blue");
+	if(teams >= 3)
+		TeamScore_Spawn(COLOR_TEAM3, "Yellow");
+	if(teams >= 4)
+		TeamScore_Spawn(COLOR_TEAM4, "Pink");
+	entity si;
+	si = spawn();
+	Net_LinkEntity(si);
+	si.classname = "csqc_score_info";
+	si.SendEntity = ScoreInfo_SendEntity;
+	si.Version = 1;
+}
+
+/*
+ * per-player score entities
+ */
+
+float PlayerScore_SendEntity()
+{
+	float i;
+
+	WriteByte(MSG_ENTITY, ENT_CLIENT_SCORES);
+	WriteByte(MSG_ENTITY, num_for_edict(self.owner));
+	for(i = 0; i < MAX_SCORE; ++i)
+		WriteShort(MSG_ENTITY, self.scores[i]);
+
+	return TRUE;
+}
+
+void PlayerScore_Clear(entity player)
+{
+	entity sk;
+	float i;
+
+	if(teamscores_entities_count)
+		return;
+	if(g_lms)
+		return;
+	if(g_arena)
+		return;
+	//print("clear clear clear... HAHA\n");
+
+	sk = player.scorekeeper;
+	for(i = 0; i < MAX_SCORE; ++i)
+		sk.(scores[i]) = 0;
+	sk.Version += 1;
+}
+
+void Score_ClearAll()
+{
+	entity p, sk;
+	float i;
+	FOR_EACH_CLIENTSLOT(p)
+	{
+		sk = p.scorekeeper;
+		if(!sk)
+			continue;
+		for(i = 0; i < MAX_SCORE; ++i)
+			sk.(scores[i]) = 0;
+		sk.Version += 1;
+	}
+	for(i = 0; i < 16; ++i)
+	{
+		sk = teamscorekeepers[i];
+		if(!sk)
+			continue;
+		for(i = 0; i < MAX_SCORE; ++i)
+			sk.(teamscores[i]) = 0;
+		sk.Version += 1;
+	}
+}
+
+void PlayerScore_Attach(entity player)
+{
+	entity sk;
+	if(player.scorekeeper)
+		error("player already has a scorekeeper");
+	sk = spawn();
+	sk.owner = player;
+	sk.Version = 1;
+	sk.SendEntity = PlayerScore_SendEntity;
+	Net_LinkEntity(sk);
+	player.scorekeeper = sk;
+}
+
+void PlayerScore_Detach(entity player)
+{
+	if(!player.scorekeeper)
+		error("player has no scorekeeper");
+	remove(player.scorekeeper);
+	player.scorekeeper = world;
+}
+
+float PlayerScore_Add(entity player, float scorefield, float score)
+{
+	entity s;
+	if(!scores_initialized) return 0; // FIXME remove this when everything uses this system
+	s = player.scorekeeper;
+	if(!s)
+		error("Adding score to unknown player!");
+	if(score)
+		s.Version += 1;
+	return (s.(scores[scorefield]) += score);
+}
+
+void PlayerTeamScore_Add(entity player, float pscorefield, float tscorefield, float score)
+{
+	PlayerScore_Add(player, pscorefield, score);
+	if(teamscores_entities_count) // only for teamplay
+		TeamScore_Add(player, tscorefield, score);
+}
+
+float PlayerScore_Compare(entity t1, entity t2)
+{
+	if(!t1 || !t2) return (!t2) - !t1;
+
+	vector result;
+	float i;
+	for(i = 0; i < MAX_SCORE; ++i)
+	{
+		var .float f;
+		f = scores[i];
+		result = ScoreField_Compare(t1, t2, f, scores_flags[i], result);
+	}
+	return result_x;
+}
+
+void WinningConditionHelper()
+{
+	float c;
+	if(teamscores_entities_count)
+	{
+		float t;
+		WinningConditionHelper_equality = 1;
+		WinningConditionHelper_winnerteam = 0;
+		for(t = 1; t < 16; ++t)
+		{
+			entity sk1, sk2;
+			sk1 = teamscorekeepers[WinningConditionHelper_winnerteam];
+			sk2 = teamscorekeepers[t];
+			c = TeamScore_Compare(sk1, sk2);
+			if(c == 0)
+				WinningConditionHelper_equality = 1;
+			else if(c < 0)
+			{
+				WinningConditionHelper_equality = 0;
+				WinningConditionHelper_winnerteam = t;
+			}
+		}
+
+		WinningConditionHelper_topscore = teamscorekeepers[WinningConditionHelper_winnerteam].teamscores_primary;
+
+		WinningConditionHelper_winner = world;
+		if(WinningConditionHelper_equality)
+			WinningConditionHelper_winnerteam = -1;
+		else
+			++WinningConditionHelper_winnerteam; // map to Nexuiz team numbers (as opposed to colors)
+	}
+	else
+	{
+		entity p;
+		WinningConditionHelper_equality = 1;
+		WinningConditionHelper_winner = world;
+		FOR_EACH_PLAYER(p)
+		{
+			c = PlayerScore_Compare(WinningConditionHelper_winner.scorekeeper, p.scorekeeper);
+			if(c == 0)
+				WinningConditionHelper_equality = 1;
+			else if(c < 0)
+			{
+				WinningConditionHelper_equality = 0;
+				WinningConditionHelper_winner = p;
+			}
+		}
+
+		WinningConditionHelper_topscore = WinningConditionHelper_winner.scorekeeper.scores_primary;
+
+		if(WinningConditionHelper_equality)
+			WinningConditionHelper_winner = world;
+		WinningConditionHelper_winnerteam = -1;
+	}
+}
+
+void Score_DebugPrint()
+{
+	entity p, sk;
+	float i, t;
+
+	print("netname");
+	for(i = 0; i < MAX_SCORE; ++i)
+		print(":", scores_label[i]);
+	print("\n");
+	FOR_EACH_PLAYER(p)
+	{
+		sk = p.scorekeeper;
+		print(p.netname);
+		for(i = 0; i < MAX_SCORE; ++i)
+			print(":", ftos(sk.(scores[i])));
+		print("\n");
+	}
+
+	print("teamname");
+	for(i = 0; i < MAX_TEAMSCORE; ++i)
+		print(":", teamscores_label[i]);
+	print("\n");
+	for(t = 0; t < 16; ++t)
+	{
+		sk = teamscorekeepers[t];
+		if(sk)
+		{
+			print(ftos(t));
+			for(i = 0; i < MAX_TEAMSCORE; ++i)
+				print(":", ftos(sk.(teamscores[i])));
+			print("\n");
+		}
+	}
+}
+
+string GetScoreLogLabel(string label, float fl)
+{
+	if(fl & SFL_LOWER_IS_BETTER)
+		label = strcat(label, "<");
+	if(fl & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_PRIMARY)
+		label = strcat(label, "!!");
+	else if(fl & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_SECONDARY)
+		label = strcat(label, "!");
+	return label;
+}
+
+string GetPlayerScoreString(entity pl)
+{
+	string out;
+	entity sk;
+	float i, f;
+	string l;
+
+	out = "";
+	if(!pl)
+	{
+		// label
+		for(i = 0; i < MAX_SCORE; ++i)
+			if(scores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_PRIMARY)
+			{
+				f = scores_flags[i];
+				l = scores_label[i];
+				out = strcat(out, GetScoreLogLabel(l, f), ",");
+			}
+		for(i = 0; i < MAX_SCORE; ++i)
+			if(scores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_SECONDARY)
+			{
+				f = scores_flags[i];
+				l = scores_label[i];
+				out = strcat(out, GetScoreLogLabel(l, f), ",");
+			}
+		for(i = 0; i < MAX_SCORE; ++i)
+			if(scores_flags[i] & SFL_SORT_PRIO_MASK != SFL_SORT_PRIO_PRIMARY)
+			if(scores_flags[i] & SFL_SORT_PRIO_MASK != SFL_SORT_PRIO_SECONDARY)
+			{
+				f = scores_flags[i];
+				l = scores_label[i];
+				out = strcat(out, GetScoreLogLabel(l, f), ",");
+			}
+		out = substring(out, 0, strlen(out) - 1);
+	}
+	else if((sk = pl.scorekeeper))
+	{
+		for(i = 0; i < MAX_SCORE; ++i)
+			if(scores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_PRIMARY)
+				out = strcat(out, ftos(sk.(scores[i])), ",");
+		for(i = 0; i < MAX_SCORE; ++i)
+			if(scores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_SECONDARY)
+				out = strcat(out, ftos(sk.(scores[i])), ",");
+		for(i = 0; i < MAX_SCORE; ++i)
+			if(scores_flags[i] & SFL_SORT_PRIO_MASK != SFL_SORT_PRIO_PRIMARY)
+			if(scores_flags[i] & SFL_SORT_PRIO_MASK != SFL_SORT_PRIO_SECONDARY)
+				out = strcat(out, ftos(sk.(scores[i])), ",");
+		out = substring(out, 0, strlen(out) - 1);
+	}
+	return out;
+}
+
+string GetTeamScoreString(float tm)
+{
+	string out;
+	entity sk;
+	float i, f;
+	string l;
+
+	out = "";
+	if(tm == 0)
+	{
+		// label
+		for(i = 0; i < MAX_SCORE; ++i)
+			if(teamscores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_PRIMARY)
+			{
+				f = teamscores_flags[i];
+				l = teamscores_label[i];
+				out = strcat(out, GetScoreLogLabel(l, f), ",");
+			}
+		for(i = 0; i < MAX_SCORE; ++i)
+			if(teamscores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_SECONDARY)
+			{
+				f = teamscores_flags[i];
+				l = teamscores_label[i];
+				out = strcat(out, GetScoreLogLabel(l, f), ",");
+			}
+		for(i = 0; i < MAX_SCORE; ++i)
+			if(teamscores_flags[i] & SFL_SORT_PRIO_MASK != SFL_SORT_PRIO_PRIMARY)
+			if(teamscores_flags[i] & SFL_SORT_PRIO_MASK != SFL_SORT_PRIO_SECONDARY)
+			{
+				f = teamscores_flags[i];
+				l = teamscores_label[i];
+				out = strcat(out, GetScoreLogLabel(l, f), ",");
+			}
+		out = substring(out, 0, strlen(out) - 1);
+	}
+	else if((sk = teamscorekeepers[tm - 1]))
+	{
+		for(i = 0; i < MAX_TEAMSCORE; ++i)
+			if(teamscores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_PRIMARY)
+				out = strcat(out, ftos(sk.(teamscores[i])), ",");
+		for(i = 0; i < MAX_TEAMSCORE; ++i)
+			if(teamscores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_SECONDARY)
+				out = strcat(out, ftos(sk.(teamscores[i])), ",");
+		for(i = 0; i < MAX_TEAMSCORE; ++i)
+			if(teamscores_flags[i] & SFL_SORT_PRIO_MASK != SFL_SORT_PRIO_PRIMARY)
+			if(teamscores_flags[i] & SFL_SORT_PRIO_MASK != SFL_SORT_PRIO_SECONDARY)
+				out = strcat(out, ftos(sk.(teamscores[i])), ",");
+		out = substring(out, 0, strlen(out) - 1);
+	}
+	return out;
+}

Copied: branches/nexuiz-2.0/data/qcsrc/server/scores.qh (from rev 3990, trunk/data/qcsrc/server/scores.qh)
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/scores.qh	                        (rev 0)
+++ branches/nexuiz-2.0/data/qcsrc/server/scores.qh	2008-07-31 06:23:51 UTC (rev 3991)
@@ -0,0 +1,99 @@
+float scores_initialized; // 1 when scores labels/rules have been set
+
+/**
+ * Attaches a PlayerScore entity to a player. Use that in ClientConnect.
+ * Remember to detach it in ClientDisconnect!
+ */
+void PlayerScore_Attach(entity player);
+
+/**
+ * Detaches a PlayerScore entity from the player. Use that in ClientDisconnect.
+ */
+void PlayerScore_Detach(entity player);
+
+/**
+ * Adds a score to the player's scores.
+ * NEVER call this if PlayerScore_Attach has not been called yet!
+ * Means: FIXME make players unable to join the game when not called ClientConnect yet.
+ * Returns the new score.
+ */
+float PlayerScore_Add(entity player, float scorefield, float score);
+
+/**
+ * Initialize the score of this player if needed.
+ * Does nothing in teamplay.
+ * Use that when a spectator becomes a player.
+ */
+void PlayerScore_Clear(entity player);
+
+/**
+ * Adds a score to the player's team's scores.
+ * NEVER call this if team has not been set yet!
+ * Returns the new score.
+ */
+float TeamScore_Add(entity player, float scorefield, float score);
+
+/**
+ * Adds a score to the given team.
+ * NEVER call this if team has not been set yet!
+ * Returns the new score.
+ */
+float TeamScore_AddToTeam(float t, float scorefield, float score);
+
+/**
+ * Adds a score to both the player and the team.
+ */
+void PlayerTeamScore_Add(entity player, float pscorefield, float tscorefield, float score);
+
+/**
+ * Adds to the generic score fields for both the player and the team.
+ */
+#define PlayerTeamScore_AddScore(p,s) PlayerTeamScore_Add(p, SP_SCORE, ST_SCORE, s)
+
+/**
+ * Initialize the scores info for the given number of teams.
+ * Immediately set all labels afterwards.
+ */
+void ScoreInfo_Init(float teams);
+
+/**
+ * Set the label of a team score item, as well as the scoring flags.
+ */
+void ScoreInfo_SetLabel_TeamScore(float i, string label, float scoreflags);
+
+/**
+ * Set the label of a player score item, as well as the scoring flags.
+ */
+void ScoreInfo_SetLabel_PlayerScore(float i, string label, float scoreflags);
+
+/**
+ * Clear ALL scores (for ready-restart).
+ */
+void Score_ClearAll();
+
+/**
+ * Prints the scores (ugly!) to the console.
+ */
+void Score_DebugPrint();
+
+/**
+ * Sets the following results for the current scores entities.
+ */
+void WinningConditionHelper();
+float WinningConditionHelper_topscore;   ///< highest score
+float WinningConditionHelper_equality;   ///< 1 if and only if the top two have equal scores
+float WinningConditionHelper_winnerteam; ///< the color of the winning team, or -1 if none
+entity WinningConditionHelper_winner;    ///< the winning player, or world if none
+
+/**
+ * Returns score strings for eventlog etc.
+ * When called with world, or 0, as argument, they return the labels in the
+ * same order.
+ * The strings are comma separated; labels that end with !! designate primary,
+ * labels that end with ! designate high priority.
+ * Labels get an appended < if the scores are better if smaller (e.g. deaths).
+ * High priorities always come first.
+ * Example label string: score!!,kills,deaths<,suicides<
+ */
+string GetPlayerScoreString(entity pl);
+string GetTeamScoreString(float tm);

Copied: branches/nexuiz-2.0/data/qcsrc/server/scores_rules.qc (from rev 3990, trunk/data/qcsrc/server/scores_rules.qc)
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/scores_rules.qc	                        (rev 0)
+++ branches/nexuiz-2.0/data/qcsrc/server/scores_rules.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -0,0 +1,111 @@
+float c1, c2, c3, c4;
+void CheckAllowedTeams (entity for_whom);
+
+// NOTE: SP_ constants may not be >= MAX_SCORE; ST_constants may not be >= MAX_TEAMSCORE
+// scores that should be in all modes:
+void ScoreRules_basics(float teams, float sprio)
+{
+	ScoreInfo_Init(teams);
+	if(sprio)
+		ScoreInfo_SetLabel_TeamScore  (ST_SCORE,        "score",     sprio);
+	ScoreInfo_SetLabel_PlayerScore(SP_KILLS,        "kills",     0);
+	ScoreInfo_SetLabel_PlayerScore(SP_DEATHS,       "deaths",    SFL_LOWER_IS_BETTER);
+	ScoreInfo_SetLabel_PlayerScore(SP_SUICIDES,     "suicides",  SFL_LOWER_IS_BETTER);
+	if(sprio)
+		ScoreInfo_SetLabel_PlayerScore(SP_SCORE,        "score",     sprio);
+}
+void ScoreRules_generic()
+{
+	CheckAllowedTeams(world);
+	if(teamplay)
+	{
+		CheckAllowedTeams(world);
+		ScoreRules_basics(((c4>=0) ? 4 : (c3>=0) ? 3 : 2), SFL_SORT_PRIO_PRIMARY);
+	}
+	else
+		ScoreRules_basics(0, SFL_SORT_PRIO_PRIMARY);
+}
+
+// g_ctf
+#define ST_CTF_CAPS 1
+#define SP_CTF_CAPS 4
+#define SP_CTF_PICKUPS 5
+#define SP_CTF_FCKILLS 6
+#define SP_CTF_RETURNS 7
+void ScoreRules_ctf()
+{
+	float sp_score, sp_caps;
+	sp_score = sp_caps = 0;
+	switch(cvar("g_ctf_win_mode"))
+	{
+		case 0: // caps only
+			sp_caps = SFL_SORT_PRIO_PRIMARY;
+			break;
+		case 1: // caps, then score
+			sp_caps = SFL_SORT_PRIO_PRIMARY;
+			sp_score = SFL_SORT_PRIO_SECONDARY;
+			break;
+		case 2: // score only
+		default:
+			sp_score = SFL_SORT_PRIO_PRIMARY;
+			sp_caps = SFL_SORT_PRIO_SECONDARY; // looks better ;)
+			break;
+	}
+
+	CheckAllowedTeams(world);
+	ScoreRules_basics(2 + (c3>=0), sp_score); // NOTE this assumes that the rogue team is team 3
+	ScoreInfo_SetLabel_TeamScore  (ST_CTF_CAPS,     "caps",      sp_caps);
+	ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPS,     "caps",      sp_caps);
+	ScoreInfo_SetLabel_PlayerScore(SP_CTF_PICKUPS,  "pickups",   0);
+	ScoreInfo_SetLabel_PlayerScore(SP_CTF_FCKILLS,  "fckills",   0);
+	ScoreInfo_SetLabel_PlayerScore(SP_CTF_RETURNS,  "returns",   0);
+}
+
+// g_domination
+#define ST_DOM_TICKS 1
+#define SP_DOM_TICKS 4
+#define SP_DOM_TAKES 5
+void ScoreRules_dom()
+{
+	float sp_domticks, sp_score;
+	sp_score = sp_domticks = 0;
+	if(cvar("g_domination_disable_frags"))
+		sp_domticks = SFL_SORT_PRIO_PRIMARY;
+	else
+		sp_score = SFL_SORT_PRIO_PRIMARY;
+	CheckAllowedTeams(world);
+	ScoreRules_basics(((c4>=0) ? 4 : (c3>=0) ? 3 : 2), sp_score);
+	ScoreInfo_SetLabel_TeamScore  (ST_DOM_TICKS,    "ticks",     sp_domticks);
+	ScoreInfo_SetLabel_PlayerScore(SP_DOM_TICKS,    "ticks",     sp_domticks);
+	ScoreInfo_SetLabel_PlayerScore(SP_DOM_TAKES,    "takes",     0);
+}
+
+// LMS stuff
+#define SP_LMS_LIVES 4
+#define SP_LMS_RANK 5
+void ScoreRules_lms()
+{
+	ScoreRules_basics(0, 0);
+	ScoreInfo_SetLabel_PlayerScore(SP_LMS_LIVES,    "lives",     SFL_SORT_PRIO_SECONDARY);
+	ScoreInfo_SetLabel_PlayerScore(SP_LMS_RANK,     "rank",      SFL_LOWER_IS_BETTER | SFL_RANK | SFL_SORT_PRIO_PRIMARY | SFL_ALLOW_HIDE);
+}
+
+// Key hunt stuff
+#define ST_KH_CAPS 1
+#define SP_KH_CAPS 4
+#define SP_KH_PUSHES 5
+#define SP_KH_DESTROYS 6
+#define SP_KH_PICKUPS 7
+#define SP_KH_KCKILLS 8
+#define SP_KH_LOSSES 9
+void ScoreRules_kh(float teams)
+{
+	ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY);
+	ScoreInfo_SetLabel_TeamScore  (ST_KH_CAPS,      "caps",      SFL_SORT_PRIO_SECONDARY);
+	ScoreInfo_SetLabel_PlayerScore(SP_KH_CAPS,      "caps",      SFL_SORT_PRIO_SECONDARY);
+	ScoreInfo_SetLabel_PlayerScore(SP_KH_PUSHES,    "pushes",    0);
+	ScoreInfo_SetLabel_PlayerScore(SP_KH_DESTROYS,  "destroyed", SFL_LOWER_IS_BETTER);
+	ScoreInfo_SetLabel_PlayerScore(SP_KH_PICKUPS,   "pickups",   0);
+	ScoreInfo_SetLabel_PlayerScore(SP_KH_KCKILLS,   "kckills",   0);
+	ScoreInfo_SetLabel_PlayerScore(SP_KH_LOSSES,    "losses",    SFL_LOWER_IS_BETTER);
+}

Modified: branches/nexuiz-2.0/data/qcsrc/server/teamplay.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/teamplay.qc	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/data/qcsrc/server/teamplay.qc	2008-07-31 06:23:51 UTC (rev 3991)
@@ -25,15 +25,7 @@
 
 void TeamchangeFrags(entity e)
 {
-	if(g_ctf)
-	{
-		e.captures = 0;
-	}
-	if(e.classname == "player")
-	{
-		// reduce frags during a team change
-		DistributeFragsAmongTeam(e, (e.clientcolors & 0x0F) + 1, 1 - cvar("g_changeteam_fragtransfer") / 100);
-	}
+	PlayerScore_Clear(e);
 }
 
 vector TeamColor(float teem)
@@ -108,6 +100,14 @@
 	cvar_set("teamplay", "0");
 }
 
+void default_delayedinit()
+{
+	remove(self);
+
+	if(!scores_initialized)
+		ScoreRules_generic();
+}
+
 void ActivateTeamplay()
 {
 	float teamplay_default;
@@ -225,7 +225,9 @@
 			fraglimit_override = -1;
 		gamemode_name = "Last Man Standing";
 		teams_matter = 0;
-		lms_lowest_lives = 999;
+		lms_lowest_lives = 9999;
+
+		ScoreRules_lms();
 	}
 	else if(game == GAME_ARENA || cvar("g_arena"))
 	{
@@ -303,6 +305,10 @@
 	cache_lastmutatormsg = strzone("");
 	cache_lastmotd = strzone("");
 
+	if(g_ctf)
+		if(g_ctf_win_mode != 2)
+			fraglimit_override = capturelimit_override;
+
 	// enforce the server's universal frag/time limits
 	if(!cvar("g_campaign"))
 	{
@@ -310,8 +316,6 @@
 			cvar_set("fraglimit", ftos(fraglimit_override));
 		if(timelimit_override >= 0)
 			cvar_set("timelimit", ftos(timelimit_override));
-		if(capturelimit_override >= 0)
-			cvar_set("capturelimit", ftos(capturelimit_override));
 	}
 
 	if (game == GAME_DOMINATION)//cvar("g_domination"))
@@ -326,6 +330,11 @@
 		kh_init();
 	else if (game == GAME_ONSLAUGHT)
 		entcs_init();
+
+	entity e;
+	e = spawn();
+	e.nextthink = time + 0.3; // MUST be after all other delayed inits!
+	e.think = default_delayedinit;
 }
 
 string GetClientVersionMessage(float v) {
@@ -349,6 +358,7 @@
 void PrintWelcomeMessage(entity pl)
 {
 	string s, mutator, modifications, padding;
+	float p;
 
 	/*if(self.welcomemessage_time > time)
 		return;
@@ -370,25 +380,34 @@
 	{
 		if(self.classname == "observer")
 		{
-			if(g_lms && self.frags <= 0 && self.frags > -666)
-				return centerprint_atprio(self, CENTERPRIO_SPAM, strcat(NEWLINES, "^1You have no more lives left\nwait for next round\n\n\n^7press attack to spectate other players"));
-			else if(g_lms && self.frags == -666)
-				return centerprint_atprio(self, CENTERPRIO_SPAM, strcat(NEWLINES, "^1Match has already begun\nwait for next round\n\n\n^7press attack to spectate other players"));
+			if(g_lms)
+			{
+				p = PlayerScore_Add(self, SP_LMS_RANK, 0);
+				if(p >= 666)
+					return centerprint_atprio(self, CENTERPRIO_SPAM, strcat(NEWLINES, "^1Match has already begun\nwait for next round\n\n\n^7press attack to spectate other players"));
+				else if(p > 0)
+					return centerprint_atprio(self, CENTERPRIO_SPAM, strcat(NEWLINES, "^1You have no more lives left\nwait for next round\n\n\n^7press attack to spectate other players"));
+			}
 		}
 		else if(self.classname == "spectator")
 		{
-			if ((g_lms && self.frags < 1) || g_arena)
-				return centerprint_atprio(self, CENTERPRIO_SPAM, strcat(NEWLINES, "spectating ", self.enemy.netname, "\n\n\n^7press attack for next player\npress attack2 for free fly mode"));
-			else {
-				local string specString;
-				specString = strcat(NEWLINES, "spectating ", self.enemy.netname, "\n\n\n^7press jump to play\n^7press attack for next player\npress attack2 for free fly mode");
-				
-				if(time < restart_countdown) //also show the countdown when being a spectator
-					specString = strcat(specString, "\n\n^1Game starts in ", ftos(restartAnnouncer.cnt + 1), " seconds^7");
-				else if (timeoutStatus != 0)
-					specString = strcat(specString, "\n\n", getTimeoutText(1));
-				return centerprint_atprio(self, CENTERPRIO_SPAM, specString);
+			if(g_lms)
+			{
+				p = PlayerScore_Add(self, SP_LMS_RANK, 0);
+				if(p)
+					return centerprint_atprio(self, CENTERPRIO_SPAM, strcat(NEWLINES, "spectating ", self.enemy.netname, "\n\n\n^7press attack for next player\npress attack2 for free fly mode"));
 			}
+			if (g_arena)
+				return centerprint_atprio(self, CENTERPRIO_SPAM, strcat(NEWLINES, "spectating ", self.enemy.netname, "\n\n\n^7press attack for next player\npress attack2 for free fly mode"));
+
+			local string specString;
+			specString = strcat(NEWLINES, "spectating ", self.enemy.netname, "\n\n\n^7press jump to play\n^7press attack for next player\npress attack2 for free fly mode");
+			
+			if(time < restart_countdown) //also show the countdown when being a spectator
+				specString = strcat(specString, "\n\n^1Game starts in ", ftos(restartAnnouncer.cnt + 1), " seconds^7");
+			else if (timeoutStatus != 0)
+				specString = strcat(specString, "\n\n", getTimeoutText(1));
+			return centerprint_atprio(self, CENTERPRIO_SPAM, specString);
 		}
 	}
 
@@ -479,18 +498,7 @@
 	if (cache_motd != "") {
 		s = strcat(s, "\n\n^8MOTD: ^7", cache_motd);
 	}
-
 	s = strcat(s, "\n");
-	if(cvar("fraglimit"))
-	{
-		padding = "";
-		if(cvar("timelimit"))
-			padding = "        ";
-			//        " minutes"
-		s = strcat(s, "\n^8frag limit: ^7", cvar_string("fraglimit"), padding);
-	}
-	if(cvar("timelimit"))
-		s = strcat(s, "\n^8time limit: ^7", cvar_string("timelimit"), " minutes");
 
 	centerprint(pl, s);
 	//sprint(pl, s);
@@ -638,22 +646,25 @@
 		head = find(head, classname, teament_name);
 	}
 
-	if(cvar("bot_vs_human") > 0)
+	if(for_whom)
 	{
-		// bots are all blue
-		if(clienttype(for_whom) == CLIENTTYPE_BOT)
-			c1 = c3 = c4 = -1;
-		else
-			c2 = -1;
+		if(cvar("bot_vs_human") > 0)
+		{
+			// bots are all blue
+			if(clienttype(for_whom) == CLIENTTYPE_BOT)
+				c1 = c3 = c4 = -1;
+			else
+				c2 = -1;
+		}
+		else if(cvar("bot_vs_human") < 0)
+		{
+			// bots are all red
+			if(clienttype(for_whom) == CLIENTTYPE_BOT)
+				c2 = c3 = c4 = -1;
+			else
+				c1 = -1;
+		}
 	}
-	else if(cvar("bot_vs_human") < 0)
-	{
-		// bots are all red
-		if(clienttype(for_whom) == CLIENTTYPE_BOT)
-			c2 = c3 = c4 = -1;
-		else
-			c1 = -1;
-	}
 }
 
 float PlayerValue(entity p)
@@ -1108,18 +1119,18 @@
 		{
 			if(head.isbot)
 			{
-				if(head.frags < lowest_bot_score)
+				if(head.totalfrags < lowest_bot_score)
 				{
 					lowest_bot = head;
-					lowest_bot_score = head.frags;
+					lowest_bot_score = head.totalfrags;
 				}
 			}
 			else
 			{
-				if(head.frags < lowest_player_score)
+				if(head.totalfrags < lowest_player_score)
 				{
 					lowest_player = head;
-					lowest_player_score = head.frags;
+					lowest_player_score = head.totalfrags;
 				}
 			}
 		}
@@ -1304,28 +1315,6 @@
 
 
 
-/*void UpdateTeamScore(entity e, float first)
-{
-	clientno = e.FIXME;
-	if(first)
-	{
-		WriteByte (MSG_ALL, SVC_UPDATENAME);
-		WriteByte (MSG_ALL, clientno);
-		WriteString (MSG_ALL, e.netname);
-
-		WriteByte (MSG_ALL, SVC_UPDATECOLORS);
-		WriteByte (MSG_ALL, clientno);
-		WriteByte (MSG_ALL, e.b_shirt * 16 + who.b_pants);
-	}
-
-	WriteByte (MSG_ALL, SVC_UPDATEFRAGS);
-	WriteByte (MSG_ALL, clientno);
-	WriteShort (MSG_ALL, e.frags + 10000);
-};
-
-*/
-
-
 // code from here on is just to support maps that don't have team entities
 void tdm_spawnteam (string teamname, float teamcolor)
 {

Copied: branches/nexuiz-2.0/misc/autoshader.sh (from rev 3990, trunk/misc/autoshader.sh)
===================================================================
--- branches/nexuiz-2.0/misc/autoshader.sh	                        (rev 0)
+++ branches/nexuiz-2.0/misc/autoshader.sh	2008-07-31 06:23:51 UTC (rev 3991)
@@ -0,0 +1,195 @@
+#!/bin/sh
+
+if ! [ -d "textures" ] && ! [ -d "env" ]; then
+	echo "Sorry, must be run from a directory with a textures subfolder. Giving up."
+	exit 1
+fi
+
+case "$0" in
+	*/*)
+		mydir=${0%/*}
+		;;
+	*)
+		mydir=.
+		;;
+esac
+
+makeshader()
+{
+	s=`texnormalize "$1"`
+	dir=${s#textures/}
+	dir=${dir%%/*}
+	echo scripts/$dir.shader
+	mkdir -p scripts
+	cat <<EOF >>"scripts/$dir.shader"
+$s
+{
+	qer_editorimage $1
+	qer_trans 0.5
+	// maybe: surfaceparm nonsolid
+	surfaceparm trans
+	surfaceparm alphashadow
+	surfaceparm nomarks
+	cull disable
+	{
+		map $s
+		blendfunc blend
+		// or: alphafunc GE128
+	}
+	{
+		map $lightmap
+		blendfunc filter
+		rgbGen identity
+	}
+}
+EOF
+}
+
+makeskyshader()
+{
+	coords=`sh "$mydir/brightspot.sh" "$1"`
+	s=`texnormalize "$1"`
+	case "$coords" in
+		*\ *)
+			;;
+		*)
+			coords="-42 -42"
+			echo >&2 "NOTE: brightspot tool did not work"
+			;;
+	esac
+	s=${s%_up}
+	s=${s#env/}
+	dir=${s%%/*}
+	echo >&2 "appending to scripts/$dir.shader"
+	echo scripts/$dir.shader
+	mkdir -p scripts
+	cat <<EOF >>"scripts/$dir.shader"
+textures/$s
+{
+	qer_editorimage $1
+	surfaceparm noimpact
+	surfaceparm nolightmap
+	surfaceparm sky
+	surfaceparm nomarks
+	q3map_sunExt .5 .5 .7 $coords 2 16 // red green blue intensity degrees elevation deviance samples
+	q3map_surfacelight 150 // intensity
+	skyparms env/$s - -
+}
+EOF
+}
+
+texnormalize()
+{
+	echo "$1" | sed 's/\.[Jj][Pp][Gg]$\|\.[Tt][Gg][Aa]$\|\.[Pp][Nn][Gg]$//;'
+}
+
+allshadernames() # prints all shader names or texture names
+{
+	cat scripts/*.shader 2>/dev/null | tr '\r' '\n' | {
+		mode=root
+		while IFS= read -r LINE; do
+			LINE=`echo "$LINE" | sed 's,//.*,,; s/\s\+/ /g; s/^ //; s/ $//; s/"//g;'`
+			[ -n "$LINE" ] || continue
+			set -- $LINE
+			case "$mode:$1" in
+				root:'{')
+					mode=shader
+					;;
+				root:*)
+					texnormalize "$1"
+					;;
+
+				shader:'{')
+					mode=stage
+					;;
+				shader:'}')
+					mode=root
+					;;
+				shader:skyparms)
+					echo "`texnormalize "$1"`_up"
+					;;
+
+				stage:'}')
+					mode=shader
+					;;
+				stage:map)
+					texnormalize "$2"
+					;;
+				stage:clampmap)
+					texnormalize "$2"
+					;;
+				stage:animmap)
+					shift
+					shift
+					for X in "$@"; do
+						texnormalize "$X"
+					done
+					;;
+			esac
+		done
+	}
+}
+
+allshaders=`allshadernames`
+lf="
+"
+
+has_shader()
+{
+	sh=`texnormalize "$1"`
+	case "$lf$allshaders$lf" in
+		*"$lf$sh$lf"*)
+			return 0
+			;;
+	esac
+	return 1
+}
+
+has_alpha()
+{
+	convert "$1" -depth 8 RGBA:- | xxd -c 4 -g 1 | grep -v " ff  " >/dev/null
+}
+
+autoshaders()
+{
+	{
+		[ -d "textures" ] && find textures -type f \( -iname \*.tga -o -iname \*.png \) -print | while IFS= read -r TEX; do
+			case `texnormalize "$TEX"` in
+				*_norm|*_shirt|*_pants|*_glow|*_gloss|*_bump)
+					# ignore these (they are used implicitly)
+					continue
+					;;
+			esac
+			if has_shader "$TEX"; then
+				echo>&2 "    $TEX has an associated shader, ignoring."
+			else
+				if has_alpha "$TEX"; then
+					echo>&2 "*** $TEX has alpha but no shader, creating default alpha shader."
+					makeshader "$TEX"
+				else
+					echo>&2 "    $TEX has no shader and no alpha, fine."
+				fi
+			fi
+		done
+		[ -d "env" ] && find env -type f \( -iname \*_up.tga -o -iname \*_up.png -o -iname \*_up.jpg \) -print | while IFS= read -r TEX; do
+			if has_shader "$TEX"; then
+				echo>&2 "    $TEX has an associated shader, ignoring."
+			else
+				echo>&2 "*** $TEX is sky but has no shader, creating default sky shader."
+				makeskyshader "$TEX"
+			fi
+		done
+	} | sort -u
+}
+
+aashaders=`autoshaders`
+
+if [ -n "$aashaders" ]; then
+	cat <<EOF
+The following shader files have been automatically created or appended to:
+
+$aashaders
+
+Please edit them to your needs, and possibly rename them.
+EOF
+fi

Copied: branches/nexuiz-2.0/misc/brightspot.c (from rev 3990, trunk/misc/brightspot.c)
===================================================================
--- branches/nexuiz-2.0/misc/brightspot.c	                        (rev 0)
+++ branches/nexuiz-2.0/misc/brightspot.c	2008-07-31 06:23:51 UTC (rev 3991)
@@ -0,0 +1,185 @@
+#include <stdio.h>
+#include <math.h>
+
+// USAGE: see brightspot.sh (and in the future brightspot.bat)
+// It should output the right parameters for the sun direction in q3map2's format.
+// But probably is broken.
+
+#define false 0
+#define true 1
+
+int flip[6*3] =
+{
+	false, false,  true, // "rt"
+	 true,  true,  true, // "lf"
+	false,  true, false, // "bk"
+	 true, false, false, // "ft"
+	false, false,  true, // "up"
+	false, false,  true  // "dn"
+};
+
+static const double skyboxtexcoord2f[6*4*2] =
+{
+    // skyside[0]
+    0, 1,
+    1, 1,
+    1, 0,
+    0, 0,
+    // skyside[1]
+    1, 0,
+    0, 0,
+    0, 1,
+    1, 1,
+    // skyside[2]
+    1, 1,
+    1, 0,
+    0, 0,
+    0, 1,
+    // skyside[3]
+    0, 0,
+    0, 1,
+    1, 1,
+    1, 0,
+    // skyside[4]
+    0, 1,
+    1, 1,
+    1, 0,
+    0, 0,
+    // skyside[5]
+    0, 1,
+    1, 1,
+    1, 0,
+    0, 0
+};
+
+
+static const double skyboxvertex3f[6*4*3] =
+{
+        // skyside[0]
+         16, -16,  16,
+         16, -16, -16,
+         16,  16, -16,
+         16,  16,  16,
+        // skyside[1]
+        -16,  16,  16,
+        -16,  16, -16,
+        -16, -16, -16,
+        -16, -16,  16,
+        // skyside[2]
+         16,  16,  16,
+         16,  16, -16,
+        -16,  16, -16,
+        -16,  16,  16,
+        // skyside[3]
+        -16, -16,  16,
+        -16, -16, -16,
+         16, -16, -16,
+         16, -16,  16,
+        // skyside[4]
+        -16, -16,  16,
+         16, -16,  16,
+         16,  16,  16,
+        -16,  16,  16,
+        // skyside[5]
+         16, -16, -16,
+        -16, -16, -16,
+        -16,  16, -16,
+         16,  16, -16
+};
+
+void Unmap2f(double x, double y, const double *corners, double *u, double *v)
+{
+	// x - corners[0] == *u * (corners[2] - corners[0]) + *v * (corners[4] - corners[2]);
+	// y - corners[1] == *u * (corners[3] - corners[1]) + *v * (corners[5] - corners[3]);
+	
+	double xc0 = x - corners[0];
+	double yc1 = y - corners[1];
+	double c20 = corners[2] - corners[0];
+	double c31 = corners[3] - corners[1];
+	double c42 = corners[4] - corners[2];
+	double c53 = corners[5] - corners[3];
+
+	// xc0 == *u * c20 + *v * c42;
+	// yc1 == *u * c31 + *v * c53;
+
+	double det = c20 * c53 - c31 * c42;
+	double du = xc0 * c53 - yc1 * c42;
+	double dv = c20 * yc1 - c31 * xc0;
+
+	*u = du / det;
+	*v = dv / det;
+}
+
+void Map3f(double u, double v, const double *corners, double *x, double *y, double *z)
+{
+	*x = corners[0] + u * (corners[3] - corners[0]) + v * (corners[6] - corners[3]);
+	*y = corners[1] + u * (corners[4] - corners[1]) + v * (corners[7] - corners[4]);
+	*z = corners[2] + u * (corners[5] - corners[2]) + v * (corners[8] - corners[5]);
+}
+
+void MapCoord(int pic, int y, int x, double vec[3])
+{
+	int h;
+	int flipx = flip[3*pic+0];
+	int flipy = flip[3*pic+1];
+	int flipdiag = flip[3*pic+2];
+	double u, v;
+
+	if(flipx)
+		x = 511 - x;
+
+	if(flipy)
+		y = 511 - y;
+
+	if(flipdiag)
+	{
+		h = x; x = y; y = h;
+	}
+
+	Unmap2f((x + 0.5) / 512.0, (y + 0.5) / 512.0, skyboxtexcoord2f + 4*2*pic, &u, &v);
+	Map3f(u, v, skyboxvertex3f + 6*2*pic, &vec[0], &vec[1], &vec[2]);
+}
+
+int main(int argc, char **argv)
+{
+	FILE *f;
+	int i, j, k;
+	unsigned char picture[6][512][512];
+	double brightvec[3];
+	double pitch, yaw;
+
+	if(argc != 2)
+	{
+		fprintf(stderr, "Usage: %s imagefile.gray\n", *argv);
+		return 1;
+	}
+
+	f = fopen(argv[1], "rb");
+	if(!f)
+	{
+		perror("fopen");
+		return 1;
+	}
+	fread(&picture, sizeof(picture), 1, f);
+	fclose(f);
+
+	brightvec[0] = brightvec[1] = brightvec[2] = 0;
+	for(i = 0; i < 6; ++i)
+		for(j = 0; j < 512; ++j)
+			for(k = 0; k < 512; ++k)
+			{
+				double vec[3], f;
+				MapCoord(i, j, k, vec);
+				f = pow(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2], -1.5); // I know what I am doing.
+				f *= exp(10 * (picture[i][j][k] - 255));
+				brightvec[0] += f * vec[0];
+				brightvec[1] += f * vec[1];
+				brightvec[2] += f * vec[2];
+			}
+	
+	pitch = atan2(brightvec[2], sqrt(brightvec[0]*brightvec[0] + brightvec[1]*brightvec[1]));
+	yaw = atan2(brightvec[1], brightvec[0]);
+
+	printf("%f %f\n", yaw * 180 / M_PI, pitch * 180 / M_PI);
+	return 0;
+}

Copied: branches/nexuiz-2.0/misc/brightspot.sh (from rev 3990, trunk/misc/brightspot.sh)
===================================================================
--- branches/nexuiz-2.0/misc/brightspot.sh	                        (rev 0)
+++ branches/nexuiz-2.0/misc/brightspot.sh	2008-07-31 06:23:51 UTC (rev 3991)
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+if [ -z "$1" ]; then
+	echo>&2 "Usage: $0 foo_rt.jpg"
+	exit 1
+fi
+
+brightspot=
+if which brightspot-bin >/dev/null; then
+	brightspot=brightspot-bin
+else
+	case "$0" in
+		*/*)
+			mydir=${0%/*}
+			;;
+		*)
+			mydir=.
+			;;
+	esac
+	brightspot="$mydir/brightspot-bin"
+	[ "$brightspot" -nt "$mydir/brightspot.c" ] || gcc -lm -O3 -Wall -Wextra "$mydir/brightspot.c" -o "$brightspot" || exit 1
+fi
+
+i=$1
+ext=${i##*.}
+name=${i%.*}
+name=${name%_[rlbfud][tfktpn]}
+
+{
+	convert "$name"_rt."$ext" -depth 8 -geometry 512x512 GRAY:-
+	convert "$name"_lf."$ext" -depth 8 -geometry 512x512 GRAY:-
+	convert "$name"_bk."$ext" -depth 8 -geometry 512x512 GRAY:-
+	convert "$name"_ft."$ext" -depth 8 -geometry 512x512 GRAY:-
+	convert "$name"_up."$ext" -depth 8 -geometry 512x512 GRAY:-
+	convert "$name"_dn."$ext" -depth 8 -geometry 512x512 GRAY:-
+} | "$brightspot" /dev/stdin

Copied: branches/nexuiz-2.0/misc/crc16.c (from rev 3990, trunk/misc/crc16.c)
===================================================================
--- branches/nexuiz-2.0/misc/crc16.c	                        (rev 0)
+++ branches/nexuiz-2.0/misc/crc16.c	2008-07-31 06:23:51 UTC (rev 3991)
@@ -0,0 +1,97 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+// this CRC matches DP
+// output is:
+//   filesize.crc16
+// usage:
+//   mv csprogs.dat csprogs.dat.`crc16 < csprogs.dat`
+// see script csqcarchive.sh
+
+/*
+============================================================================
+
+					CRC FUNCTIONS
+
+============================================================================
+*/
+
+// this is a 16 bit, non-reflected CRC using the polynomial 0x1021
+// and the initial and final xor values shown below...  in other words, the
+// CCITT standard CRC used by XMODEM
+
+#define CRC_INIT_VALUE	0xffff
+#define CRC_XOR_VALUE	0x0000
+
+static unsigned short crctable[256] =
+{
+	0x0000,	0x1021,	0x2042,	0x3063,	0x4084,	0x50a5,	0x60c6,	0x70e7,
+	0x8108,	0x9129,	0xa14a,	0xb16b,	0xc18c,	0xd1ad,	0xe1ce,	0xf1ef,
+	0x1231,	0x0210,	0x3273,	0x2252,	0x52b5,	0x4294,	0x72f7,	0x62d6,
+	0x9339,	0x8318,	0xb37b,	0xa35a,	0xd3bd,	0xc39c,	0xf3ff,	0xe3de,
+	0x2462,	0x3443,	0x0420,	0x1401,	0x64e6,	0x74c7,	0x44a4,	0x5485,
+	0xa56a,	0xb54b,	0x8528,	0x9509,	0xe5ee,	0xf5cf,	0xc5ac,	0xd58d,
+	0x3653,	0x2672,	0x1611,	0x0630,	0x76d7,	0x66f6,	0x5695,	0x46b4,
+	0xb75b,	0xa77a,	0x9719,	0x8738,	0xf7df,	0xe7fe,	0xd79d,	0xc7bc,
+	0x48c4,	0x58e5,	0x6886,	0x78a7,	0x0840,	0x1861,	0x2802,	0x3823,
+	0xc9cc,	0xd9ed,	0xe98e,	0xf9af,	0x8948,	0x9969,	0xa90a,	0xb92b,
+	0x5af5,	0x4ad4,	0x7ab7,	0x6a96,	0x1a71,	0x0a50,	0x3a33,	0x2a12,
+	0xdbfd,	0xcbdc,	0xfbbf,	0xeb9e,	0x9b79,	0x8b58,	0xbb3b,	0xab1a,
+	0x6ca6,	0x7c87,	0x4ce4,	0x5cc5,	0x2c22,	0x3c03,	0x0c60,	0x1c41,
+	0xedae,	0xfd8f,	0xcdec,	0xddcd,	0xad2a,	0xbd0b,	0x8d68,	0x9d49,
+	0x7e97,	0x6eb6,	0x5ed5,	0x4ef4,	0x3e13,	0x2e32,	0x1e51,	0x0e70,
+	0xff9f,	0xefbe,	0xdfdd,	0xcffc,	0xbf1b,	0xaf3a,	0x9f59,	0x8f78,
+	0x9188,	0x81a9,	0xb1ca,	0xa1eb,	0xd10c,	0xc12d,	0xf14e,	0xe16f,
+	0x1080,	0x00a1,	0x30c2,	0x20e3,	0x5004,	0x4025,	0x7046,	0x6067,
+	0x83b9,	0x9398,	0xa3fb,	0xb3da,	0xc33d,	0xd31c,	0xe37f,	0xf35e,
+	0x02b1,	0x1290,	0x22f3,	0x32d2,	0x4235,	0x5214,	0x6277,	0x7256,
+	0xb5ea,	0xa5cb,	0x95a8,	0x8589,	0xf56e,	0xe54f,	0xd52c,	0xc50d,
+	0x34e2,	0x24c3,	0x14a0,	0x0481,	0x7466,	0x6447,	0x5424,	0x4405,
+	0xa7db,	0xb7fa,	0x8799,	0x97b8,	0xe75f,	0xf77e,	0xc71d,	0xd73c,
+	0x26d3,	0x36f2,	0x0691,	0x16b0,	0x6657,	0x7676,	0x4615,	0x5634,
+	0xd94c,	0xc96d,	0xf90e,	0xe92f,	0x99c8,	0x89e9,	0xb98a,	0xa9ab,
+	0x5844,	0x4865,	0x7806,	0x6827,	0x18c0,	0x08e1,	0x3882,	0x28a3,
+	0xcb7d,	0xdb5c,	0xeb3f,	0xfb1e,	0x8bf9,	0x9bd8,	0xabbb,	0xbb9a,
+	0x4a75,	0x5a54,	0x6a37,	0x7a16,	0x0af1,	0x1ad0,	0x2ab3,	0x3a92,
+	0xfd2e,	0xed0f,	0xdd6c,	0xcd4d,	0xbdaa,	0xad8b,	0x9de8,	0x8dc9,
+	0x7c26,	0x6c07,	0x5c64,	0x4c45,	0x3ca2,	0x2c83,	0x1ce0,	0x0cc1,
+	0xef1f,	0xff3e,	0xcf5d,	0xdf7c,	0xaf9b,	0xbfba,	0x8fd9,	0x9ff8,
+	0x6e17,	0x7e36,	0x4e55,	0x5e74,	0x2e93,	0x3eb2,	0x0ed1,	0x1ef0
+};
+
+unsigned short CRC_Block(const unsigned char *data, size_t size)
+{
+	unsigned short crc = CRC_INIT_VALUE;
+	while (size--)
+		crc = (crc << 8) ^ crctable[(crc >> 8) ^ (*data++)];
+	return crc ^ CRC_XOR_VALUE;
+}
+
+int main()
+{
+	unsigned char *buf;
+	size_t bufsize = 0;
+	size_t newbufsize;
+	size_t nread;
+
+	for(;;)
+	{
+		newbufsize = 2 * bufsize + 1;
+		buf = realloc(buf, newbufsize);
+		nread = fread(buf + bufsize, 1, newbufsize - bufsize, stdin);
+		if(nread != newbufsize - bufsize)
+		{
+			// output checksum
+			unsigned short crc = CRC_Block(buf, bufsize + nread);
+			printf("%ju.%u\n", (uintmax_t) (bufsize + nread), crc);
+
+			// exit
+			break;
+		}
+		bufsize = newbufsize;
+	}
+
+	return 0;
+}

Copied: branches/nexuiz-2.0/misc/csqcarchive.sh (from rev 3990, trunk/misc/csqcarchive.sh)
===================================================================
--- branches/nexuiz-2.0/misc/csqcarchive.sh	                        (rev 0)
+++ branches/nexuiz-2.0/misc/csqcarchive.sh	2008-07-31 06:23:51 UTC (rev 3991)
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+set -e
+
+make crc16
+crc16=`pwd`/crc16
+out=`pwd`/csqcarchive.zip
+
+t=`mktemp -dt csqcarchive.XXXXXX`
+cd "$t"
+
+revs()
+{
+	{
+		svn log svn://svn.icculus.org/nexuiz/$1/data/qcsrc/common
+		echo
+		svn log svn://svn.icculus.org/nexuiz/$1/data/qcsrc/client
+	} | {
+		while IFS= read -r LINE; do
+			if [ "$LINE" = "------------------------------------------------------------------------" ]; then
+				read -r REV REST
+				case "$REV" in
+					r*)
+						echo ${REV#r}
+						;;
+				esac
+			fi
+		done
+	} | sort -n
+}
+
+rm -f "$out"
+for repo in branches/nexuiz-2.0 trunk; do
+	for rev in `revs $repo`; do
+		if [ "$rev" -lt 3789 ]; then
+			continue
+		fi
+		svn checkout -r"$rev" svn://svn.icculus.org/nexuiz/$repo/data/qcsrc
+		rm -f Makefile csprogs.dat
+		wget -OMakefile "http://svn.icculus.org/*checkout*/nexuiz/$repo/data/Makefile?revision=$rev" || continue
+		make csprogs.dat || continue
+		nm="csprogs.dat.`$crc16 < csprogs.dat`"
+		mv csprogs.dat "$nm"
+		zip -9r "$out" "$nm"
+	done
+done

Modified: branches/nexuiz-2.0/misc/gtkradiant/gtkradiant-nexuiz-patchset.diff
===================================================================
--- branches/nexuiz-2.0/misc/gtkradiant/gtkradiant-nexuiz-patchset.diff	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/misc/gtkradiant/gtkradiant-nexuiz-patchset.diff	2008-07-31 06:23:51 UTC (rev 3991)
@@ -113,9 +113,9 @@
  	}
 Index: tools/quake3/q3map2/convert_map.c
 ===================================================================
---- tools/quake3/q3map2/convert_map.c	(revision 290)
+--- tools/quake3/q3map2/convert_map.c	(revision 191)
 +++ tools/quake3/q3map2/convert_map.c	(working copy)
-@@ -45,6 +45,105 @@
+@@ -46,6 +46,105 @@
  #define	SNAP_FLOAT_TO_INT	4
  #define	SNAP_INT_TO_FLOAT	(1.0 / SNAP_FLOAT_TO_INT)
  
@@ -221,7 +221,7 @@
  static void ConvertBrush( FILE *f, int num, bspBrush_t *brush, vec3_t origin )
  {
  	int				i, j;
-@@ -53,12 +152,17 @@
+@@ -54,12 +153,17 @@
  	bspShader_t		*shader;
  	char			*texture;
  	bspPlane_t		*plane;
@@ -239,7 +239,7 @@
  	
  	/* clear out build brush */
  	for( i = 0; i < buildBrush->numsides; i++ )
-@@ -108,9 +212,88 @@
+@@ -109,9 +213,88 @@
  		/* get build side */
  		buildSide = &buildBrush->sides[ i ];
  		
@@ -328,7 +328,7 @@
  		
  		/* get texture name */
  		if( !Q_strncasecmp( buildSide->shaderInfo->shader, "textures/", 9 ) )
-@@ -129,14 +312,21 @@
+@@ -130,14 +313,21 @@
  		
  		/* print brush side */
  		/* ( 640 24 -224 ) ( 448 24 -224 ) ( 448 -232 -224 ) common/caulk 0 48 0 0.500000 0.500000 0 0 0 */
@@ -354,11 +354,11 @@
  
 Index: tools/quake3/q3map2/main.c
 ===================================================================
---- tools/quake3/q3map2/main.c	(revision 290)
+--- tools/quake3/q3map2/main.c	(revision 191)
 +++ tools/quake3/q3map2/main.c	(working copy)
-@@ -276,6 +276,18 @@
- 			else
- 				Sys_Printf( "Unknown conversion format \"%s\". Defaulting to ASE.\n", argv[ i ] );
+@@ -541,6 +541,18 @@
+ 					Sys_Printf( "Unknown conversion format \"%s\". Defaulting to ASE.\n", argv[ i ] );
+ 			}
   		}
 +		else if( !strcmp( argv[ i ],  "-ne" ) )
 + 		{
@@ -377,9 +377,9 @@
  	/* clean up map name */
 Index: tools/quake3/q3map2/model.c
 ===================================================================
---- tools/quake3/q3map2/model.c	(revision 290)
+--- tools/quake3/q3map2/model.c	(revision 193)
 +++ tools/quake3/q3map2/model.c	(working copy)
-@@ -221,6 +221,8 @@
+@@ -222,6 +222,8 @@
  	byte				*color;
  	picoIndex_t			*indexes;
  	remap_t				*rm, *glob;
@@ -388,7 +388,7 @@
  	
  	
  	/* get model */
-@@ -399,9 +401,8 @@
+@@ -398,9 +400,8 @@
  		/* ydnar: giant hack land: generate clipping brushes for model triangles */
  		if( si->clipModel || (spawnFlags & 2) )	/* 2nd bit */
  		{
@@ -601,27 +601,32 @@
  		}
 Index: tools/quake3/q3map2/map.c
 ===================================================================
---- tools/quake3/q3map2/map.c	(revision 290)
+--- tools/quake3/q3map2/map.c	(revision 193)
 +++ tools/quake3/q3map2/map.c	(working copy)
-@@ -183,9 +183,15 @@
+@@ -184,7 +184,7 @@
  snaps a plane to normal/distance epsilons
  */
  
 -void SnapPlane( vec3_t normal, vec_t *dist )
 +void SnapPlane( vec3_t normal, vec_t *dist, vec3_t center )
  {
--	SnapNormal( normal );
+ // SnapPlane disabled by LordHavoc because it often messes up collision
+ // brushes made from triangles of embedded models, and it has little effect
+@@ -193,7 +193,13 @@
+   SnapPlane reenabled by namespace because of multiple reports of
+   q3map2-crashes which were triggered by this patch.
+ */
 +	// div0: ensure the point "center" stays on the plane (actually, this
 +	// rotates the plane around the point center).
 +	// if center lies on the plane, it is guaranteed to stay on the plane by
 +	// this fix.
 +	vec_t centerDist = DotProduct(normal, center);
-+  	SnapNormal( normal );
+ 	SnapNormal( normal );
 +	*dist += (DotProduct(normal, center) - centerDist);
  
  	if( fabs( *dist - Q_rint( *dist ) ) < distanceEpsilon )
  		*dist = Q_rint( *dist );
-@@ -199,7 +205,7 @@
+@@ -207,7 +213,7 @@
  must be within an epsilon distance of the plane
  */
  
@@ -630,7 +635,7 @@
  
  #ifdef USE_HASHING
  
-@@ -207,10 +213,14 @@
+@@ -215,10 +221,14 @@
  	int		i, j, hash, h;
  	plane_t	*p;
  	vec_t	d;
@@ -647,7 +652,7 @@
  	hash = (PLANE_HASHES - 1) & (int) fabs( dist );
  	
  	/* search the border bins as well */
-@@ -251,7 +261,13 @@
+@@ -259,7 +269,13 @@
  	plane_t	*p;
  	
  
@@ -664,9 +669,9 @@
  		if( PlaneEqual( p, normal, dist ) )
 Index: tools/quake3/q3map2/shaders.c
 ===================================================================
---- tools/quake3/q3map2/shaders.c	(revision 290)
+--- tools/quake3/q3map2/shaders.c	(revision 191)
 +++ tools/quake3/q3map2/shaders.c	(working copy)
-@@ -747,8 +747,14 @@
+@@ -793,8 +793,14 @@
  	}
  	
  	if( VectorLength( si->color ) <= 0.0f )
@@ -684,22 +689,21 @@
  
 Index: tools/quake3/q3map2/light_ydnar.c
 ===================================================================
---- tools/quake3/q3map2/light_ydnar.c	(revision 290)
+--- tools/quake3/q3map2/light_ydnar.c	(revision 191)
 +++ tools/quake3/q3map2/light_ydnar.c	(working copy)
-@@ -1449,6 +1449,8 @@
- 	vec3_t				color, averageColor, averageDir, total, temp, temp2;
+@@ -1767,6 +1864,8 @@
  	float				tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
  	trace_t				trace;
+ 	float				stackLightLuxels[ STACK_LL_SIZE ];
 +	vec3_t				flood;
 +	float				*floodlight;
  	
  	
  	/* bail if this number exceeds the number of raw lightmaps */
-@@ -1871,6 +1873,78 @@
- 	/* free light list */
+@@ -2223,6 +2332,78 @@
  	FreeTraceLights( &trace );
  	
-+  	/*	-----------------------------------------------------------------
+ 	/*	-----------------------------------------------------------------
 +		floodlight pass
 +		----------------------------------------------------------------- */
 +
@@ -771,10 +775,11 @@
 +		}
 +	}
 +	
- 	/* -----------------------------------------------------------------
- 	   filter pass
- 	   ----------------------------------------------------------------- */
-@@ -3123,7 +3197,320 @@
++	/*	-----------------------------------------------------------------
+ 		dirt pass
+ 		----------------------------------------------------------------- */
+ 	
+@@ -3587,7 +3768,320 @@
  	CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
  }
  
@@ -1097,9 +1102,9 @@
 +
 Index: tools/quake3/q3map2/light.c
 ===================================================================
---- tools/quake3/q3map2/light.c	(revision 290)
+--- tools/quake3/q3map2/light.c	(revision 191)
 +++ tools/quake3/q3map2/light.c	(working copy)
-@@ -1363,6 +1363,56 @@
+@@ -1378,6 +1378,56 @@
  			break;
  	}
  	
@@ -1156,9 +1161,9 @@
  	/* normalize to get primary light direction */
  	VectorNormalize( gp->dir, gp->dir );
  	
-@@ -1544,6 +1594,12 @@
- 	qboolean	minVertex, minGrid;
- 	const char	*value;
+@@ -1661,6 +1711,12 @@
+ 		RunThreadsOnIndividual( numRawLightmaps, qtrue, DirtyRawLightmap );
+ 	}
  	
 +	/* floodlight them up */
 +	if( floodlighty )
@@ -1167,19 +1172,19 @@
 +		RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
 +	}
  
- 	/* ydnar: smooth normals */
- 	if( shade )
-@@ -1675,6 +1731,7 @@
+ 	/* ydnar: set up light envelopes */
+ 	SetupEnvelopes( qfalse, fast );
+@@ -1703,6 +1759,7 @@
  		/* flag bouncing */
  		bouncing = qtrue;
  		VectorClear( ambientColor );
-+		floodlighty = qfalse;
++		floodlighty = false;
  		
  		/* generate diffuse lights */
  		RadFreeLights();
-@@ -2114,6 +2171,21 @@
- 			loMem = qtrue;
- 			Sys_Printf( "Enabling low-memory (potentially slower) lighting mode\n" );
+@@ -2191,6 +2256,21 @@
+ 			cpmaHack = qtrue;
+ 			Sys_Printf( "Enabling Challenge Pro Mode Asstacular Vertex Lighting Mode (tm)\n" );
  		}
 +		else if( !strcmp( argv[ i ], "-floodlight" ) )
 +		{
@@ -1197,21 +1202,21 @@
 +			Sys_Printf( "Low Quality FloodLighting enabled\n" );
 +		}
  		
- 		else
- 			Sys_Printf( "WARNING: Unknown option \"%s\"\n", argv[ i ] );
-@@ -2156,6 +2228,7 @@
- 	
+ 		/* r7: dirtmapping */
+ 		else if( !strcmp( argv[ i ], "-dirty" ) )
+@@ -2279,6 +2359,7 @@
  	/* ydnar: set up optimization */
  	SetupBrushes();
+ 	SetupDirt();
 +	SetupFloodLight();
  	SetupSurfaceLightmaps();
  	
  	/* initialize the surface facet tracing */
 Index: tools/quake3/q3map2/lightmaps_ydnar.c
 ===================================================================
---- tools/quake3/q3map2/lightmaps_ydnar.c	(revision 290)
+--- tools/quake3/q3map2/lightmaps_ydnar.c	(revision 191)
 +++ tools/quake3/q3map2/lightmaps_ydnar.c	(working copy)
-@@ -413,6 +413,12 @@
+@@ -414,6 +414,12 @@
  		lm->superNormals = safe_malloc( size );
  	memset( lm->superNormals, 0, size );
  	
@@ -1226,25 +1231,25 @@
  	if( lm->superClusters == NULL )
 Index: tools/quake3/q3map2/q3map2.h
 ===================================================================
---- tools/quake3/q3map2/q3map2.h	(revision 290)
+--- tools/quake3/q3map2/q3map2.h	(revision 191)
 +++ tools/quake3/q3map2/q3map2.h	(working copy)
-@@ -265,6 +265,7 @@
- #define SUPER_NORMAL_SIZE		3
+@@ -267,6 +267,7 @@
+ #define SUPER_NORMAL_SIZE		4
  #define SUPER_DELUXEL_SIZE		3
  #define BSP_DELUXEL_SIZE		3
 +#define SUPER_FLOODLIGHT_SIZE	1
  
  #define VERTEX_LUXEL( s, v )	(vertexLuxels[ s ] + ((v) * VERTEX_LUXEL_SIZE))
  #define RAD_VERTEX_LUXEL( s, v )(radVertexLuxels[ s ] + ((v) * VERTEX_LUXEL_SIZE))
-@@ -273,6 +274,7 @@
- #define SUPER_LUXEL( s, x, y )	(lm->superLuxels[ s ] + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
+@@ -279,6 +280,7 @@
  #define SUPER_ORIGIN( x, y )	(lm->superOrigins + ((((y) * lm->sw) + (x)) * SUPER_ORIGIN_SIZE))
  #define SUPER_NORMAL( x, y )	(lm->superNormals + ((((y) * lm->sw) + (x)) * SUPER_NORMAL_SIZE))
+ #define SUPER_DIRT( x, y )		(lm->superNormals + ((((y) * lm->sw) + (x)) * SUPER_NORMAL_SIZE) + 3)	/* stash dirtyness in normal[ 3 ] */
 +#define SUPER_FLOODLIGHT( x, y )	(lm->superFloodLight + ((((y) * lm->sw) + (x)) * SUPER_FLOODLIGHT_SIZE) )	
- #define SUPER_CLUSTER( x, y )	(lm->superClusters + (((y) * lm->sw) + (x)))
- #define SUPER_DELUXEL( x, y )	(lm->superDeluxels + ((((y) * lm->sw) + (x)) * SUPER_DELUXEL_SIZE))
- #define BSP_DELUXEL( x, y )		(lm->bspDeluxels + ((((y) * lm->w) + (x)) * BSP_DELUXEL_SIZE))
-@@ -1364,6 +1366,7 @@
+ 
+ 
+ 
+@@ -1392,6 +1395,7 @@
  	
  	float					*superDeluxels;	/* average light direction */
  	float					*bspDeluxels;
@@ -1252,66 +1257,280 @@
  }
  rawLightmap_t;
  
-@@ -1670,6 +1673,9 @@
- void						SmoothNormals( void );
+@@ -1704,6 +1708,10 @@
+ float						DirtForSample( trace_t *trace );
+ void						DirtyRawLightmap( int num );
  
- void						MapRawLightmap( int num );
 +void						SetupFloodLight();
 +float						FloodLightForSample( trace_t *trace );
 +void						FloodLightRawLightmap( int num );
++
  void						IlluminateRawLightmap( int num );
  void						IlluminateVertexes( int num );
  
-@@ -2037,6 +2043,12 @@
- Q_EXTERN qboolean			sunOnly;
- Q_EXTERN int				approximateTolerance Q_ASSIGN( 0 );
- Q_EXTERN qboolean			noCollapse;
+@@ -2098,6 +2106,13 @@
+ Q_EXTERN float				dirtScale Q_ASSIGN( 1.0f );
+ Q_EXTERN float				dirtGain Q_ASSIGN( 1.0f );
+ 
 +Q_EXTERN qboolean			debugnormals Q_ASSIGN( qfalse );
 +Q_EXTERN qboolean			floodlighty Q_ASSIGN( qfalse );
 +Q_EXTERN qboolean			floodlight_lowquality Q_ASSIGN( qfalse );
 +Q_EXTERN vec3_t				floodlightRGB;
 +Q_EXTERN float				floodlightIntensity Q_ASSIGN( 512 );
 +Q_EXTERN float				floodlightDistance Q_ASSIGN( 1024 );
- Q_EXTERN qboolean			debug;
- Q_EXTERN qboolean			debugSurfaces;
- Q_EXTERN qboolean			debugUnused;
++
+ Q_EXTERN qboolean			dump Q_ASSIGN( qfalse );
+ Q_EXTERN qboolean			debug Q_ASSIGN( qfalse );
+ Q_EXTERN qboolean			debugUnused Q_ASSIGN( qfalse );
+Index: tools/quake3/q3map2/game_ja.h
+===================================================================
+--- tools/quake3/q3map2/game_ja.h	(revision 191)
++++ tools/quake3/q3map2/game_ja.h	(working copy)
+@@ -67,6 +67,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"RBSP",				/* bsp file prefix */
+ 	1,					/* bsp file version */
+Index: tools/quake3/q3map2/game_tremulous.h
+===================================================================
+--- tools/quake3/q3map2/game_tremulous.h	(revision 191)
++++ tools/quake3/q3map2/game_tremulous.h	(working copy)
+@@ -70,6 +70,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	46,					/* bsp file version */
+Index: tools/quake3/q3map2/game_wolfet.h
+===================================================================
+--- tools/quake3/q3map2/game_wolfet.h	(revision 191)
++++ tools/quake3/q3map2/game_wolfet.h	(working copy)
+@@ -66,6 +66,7 @@
+ 	qtrue,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	47,					/* bsp file version */
+Index: tools/quake3/q3map2/game_wolf.h
+===================================================================
+--- tools/quake3/q3map2/game_wolf.h	(revision 191)
++++ tools/quake3/q3map2/game_wolf.h	(working copy)
+@@ -129,6 +129,7 @@
+ 	qtrue,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	47,					/* bsp file version */
+Index: tools/quake3/q3map2/game_sof2.h
+===================================================================
+--- tools/quake3/q3map2/game_sof2.h	(revision 191)
++++ tools/quake3/q3map2/game_sof2.h	(working copy)
+@@ -139,6 +139,7 @@
+ 	qfalse,					/* wolf lighting model? */
+ 	128,					/* lightmap width/height */
+ 	1.0f,					/* lightmap gamma */
++	1.0f,					/* lightmap exposure */
+ 	1.0f,					/* lightmap compensate */
+ 	"RBSP",					/* bsp file prefix */
+ 	1,						/* bsp file version */
+Index: tools/quake3/q3map2/game_etut.h
+===================================================================
+--- tools/quake3/q3map2/game_etut.h	(revision 191)
++++ tools/quake3/q3map2/game_etut.h	(working copy)
+@@ -148,6 +148,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	2.2f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	47,					/* bsp file version */
+Index: tools/quake3/q3map2/game_jk2.h
+===================================================================
+--- tools/quake3/q3map2/game_jk2.h	(revision 191)
++++ tools/quake3/q3map2/game_jk2.h	(working copy)
+@@ -64,6 +64,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"RBSP",				/* bsp file prefix */
+ 	1,					/* bsp file version */
+Index: tools/quake3/q3map2/game_qfusion.h
+===================================================================
+--- tools/quake3/q3map2/game_qfusion.h	(revision 191)
++++ tools/quake3/q3map2/game_qfusion.h	(working copy)
+@@ -115,6 +115,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	512,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"FBSP",				/* bsp file prefix */
+ 	1,					/* bsp file version */
+Index: tools/quake3/q3map2/game_tenebrae.h
+===================================================================
+--- tools/quake3/q3map2/game_tenebrae.h	(revision 191)
++++ tools/quake3/q3map2/game_tenebrae.h	(working copy)
+@@ -112,6 +112,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	512,				/* lightmap width/height */
+ 	2.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	46,					/* bsp file version */
+Index: tools/quake3/q3map2/game_quake3.h
+===================================================================
+--- tools/quake3/q3map2/game_quake3.h	(revision 191)
++++ tools/quake3/q3map2/game_quake3.h	(working copy)
+@@ -112,6 +112,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	46,					/* bsp file version */
+Index: tools/quake3/q3map2/game_ef.h
+===================================================================
+--- tools/quake3/q3map2/game_ef.h	(revision 191)
++++ tools/quake3/q3map2/game_ef.h	(working copy)
+@@ -113,6 +113,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	46,					/* bsp file version */
+Index: tools/quake3/q3map2/light_ydnar.c
+===================================================================
+--- tools/quake3/q3map2/light_ydnar.c	(revision 191)
++++ tools/quake3/q3map2/light_ydnar.c	(working copy)
+@@ -49,6 +49,7 @@
+ 	int		i;
+ 	float	max, gamma;
+ 	vec3_t	sample;
++	float 	inv, dif;
+ 	
+ 	
+ 	/* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
+@@ -72,16 +73,51 @@
+ 		/* gamma */
+ 		sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
+ 	}
++
++	if (lightmapExposure == 1)
++	{
++		/* clamp with color normalization */
++		max = sample[ 0 ];
++		if( sample[ 1 ] > max )
++			max = sample[ 1 ];
++		if( sample[ 2 ] > max )
++			max = sample[ 2 ];
++		if( max > 255.0f )
++			VectorScale( sample, (255.0f / max), sample );
++	}
++	else
++	{
++		if (lightmapExposure==0)
++		{
++			lightmapExposure=1.0f;
++		}
++		inv=1.f/lightmapExposure;
++		//Exposure
++    	
++		max = sample[ 0 ];
++		if( sample[ 1 ] > max )
++			max = sample[ 1 ];
++		if( sample[ 2 ] > max )
++			max = sample[ 2 ];  
++      
++		dif = (1-  exp(-max * inv) )  *  255;
++
++		if (max >0) 
++		{
++			dif = dif / max;
++		}
++		else
++		{
++			dif = 0;
++		}
++
++		for (i=0;i<3;i++)
++		{
++			sample[i]*=dif;
++		}
++	}
++
+ 	
+-	/* clamp with color normalization */
+-	max = sample[ 0 ];
+-	if( sample[ 1 ] > max )
+-		max = sample[ 1 ];
+-	if( sample[ 2 ] > max )
+-		max = sample[ 2 ];
+-	if( max > 255.0f )
+-		VectorScale( sample, (255.0f / max), sample );
+-	
+ 	/* compensate for ingame overbrighting/bitshifting */
+ 	VectorScale( sample, (1.0f / lightmapCompensate), sample );
+ 	
+Index: tools/quake3/q3map2/light.c
+===================================================================
+--- tools/quake3/q3map2/light.c (revision 191)
++++ tools/quake3/q3map2/light.c (working copy)
+@@ -1836,6 +1893,14 @@
+ 			i++;
+ 		}
+ 		
++		else if( !strcmp( argv[ i ], "-exposure" ) )
++		{
++			f = atof( argv[ i + 1 ] );
++			lightmapExposure = f;
++			Sys_Printf( "Lighting exposure set to %f\n", lightmapExposure );
++			i++;
++		}
++		
+ 		else if( !strcmp( argv[ i ], "-compensate" ) )
+ 		{
+ 			f = atof( argv[ i + 1 ] );
 Index: tools/quake3/q3map2/q3map2.h
 ===================================================================
---- tools/quake3/q3map2/q3map2.h	(revision 290)
+--- tools/quake3/q3map2/q3map2.h	(revision 191)
 +++ tools/quake3/q3map2/q3map2.h	(working copy)
-@@ -1274,6 +1274,7 @@
- 	vec3_t				color;			/* starts out at full color, may be reduced if transparent surfaces are crossed */
- 	
- 	/* output */
-+	vec3_t				hit;
- 	int					compileFlags;	/* for determining surface compile flags traced through */
- 	qboolean			passSolid;
- 	qboolean			opaque;
-Index: tools/quake3/q3map2/light_trace.c
-===================================================================
---- tools/quake3/q3map2/light_trace.c	(revision 290)
-+++ tools/quake3/q3map2/light_trace.c	(working copy)
-@@ -1596,6 +1596,7 @@
- 	/* bogus node number means solid, end tracing unless testing all */
- 	if( nodeNum < 0 )
- 	{
-+		VectorCopy( origin, trace->hit );
- 		trace->passSolid = qtrue;
- 		return qtrue;
- 	}
-@@ -1606,6 +1607,7 @@
- 	/* solid? */
- 	if( node->type == TRACE_LEAF_SOLID )
- 	{
-+		VectorCopy( origin, trace->hit );
- 		trace->passSolid = qtrue;
- 		return qtrue;
- 	}
+@@ -543,6 +545,7 @@
+ 	qboolean			wolfLight;						/* when true, lights work like wolf q3map  */
+ 	int					lightmapSize;					/* bsp lightmap width/height */
+ 	float				lightmapGamma;					/* default lightmap gamma */
++	float				lightmapExposure;				/* default lightmap exposure */
+ 	float				lightmapCompensate;				/* default lightmap compensate value */
+ 	char				*bspIdent;						/* 4-letter bsp file prefix */
+ 	int					bspVersion;						/* bsp version to use */
+@@ -2117,6 +2132,7 @@
+ 
+ /* ydnar: lightmap gamma/compensation */
+ Q_EXTERN float				lightmapGamma Q_ASSIGN( 1.0f );
++Q_EXTERN float				lightmapExposure Q_ASSIGN( 1.0f );
+ Q_EXTERN float				lightmapCompensate Q_ASSIGN( 1.0f );
+ 
+ /* ydnar: for runtime tweaking of falloff tolerance */
 Index: tools/quake3/q3map2/light_ydnar.c
 ===================================================================
---- tools/quake3/q3map2/light_ydnar.c	(revision 290)
+--- tools/quake3/q3map2/light_ydnar.c	(revision 191)
 +++ tools/quake3/q3map2/light_ydnar.c	(working copy)
-@@ -372,7 +372,7 @@
+@@ -384,7 +420,7 @@
  #define NUDGE			0.5f
  #define BOGUS_NUDGE		-99999.0f
  
@@ -1320,7 +1539,7 @@
  {
  	int				i, x, y, numClusters, *clusters, pointCluster, *cluster;
  	float			*luxel, *origin, *normal, d, lightmapSampleOffset;
-@@ -380,6 +380,12 @@
+@@ -392,6 +428,12 @@
  	vec3_t			pNormal;
  	vec3_t			vecs[ 3 ];
  	vec3_t			nudged;
@@ -1333,7 +1552,7 @@
  	float			*nudge;
  	static float	nudges[][ 2 ] =
  					{
-@@ -473,6 +479,51 @@
+@@ -485,6 +527,51 @@
  	/* non axial lightmap projection (explicit xyz) */
  	else
  		VectorCopy( dv->xyz, origin );
@@ -1385,7 +1604,7 @@
  	
  	/* planar surfaces have precalculated lightmap vectors for nudging */
  	if( lm->plane != NULL )
-@@ -504,8 +555,13 @@
+@@ -516,8 +603,13 @@
  	else
  		origin[ lm->axisNum ] += lightmapSampleOffset;
  	
@@ -1400,7 +1619,7 @@
  	
  	/* another retarded hack, storing nudge count in luxel[ 1 ] */
  	luxel[ 1 ] = 0.0f;	
-@@ -521,14 +577,14 @@
+@@ -533,14 +625,14 @@
  			for( i = 0; i < 3; i++ )
  			{
  				/* set nudged point*/
@@ -1418,7 +1637,7 @@
  			luxel[ 1 ] += 1.0f;
  		}
  	}
-@@ -538,8 +594,8 @@
+@@ -550,8 +642,8 @@
  	{
  		VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
  		pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
@@ -1429,7 +1648,7 @@
  		luxel[ 1 ] += 1.0f;
  	}
  	
-@@ -585,7 +641,7 @@
+@@ -597,7 +689,7 @@
  than the distance between two luxels (thanks jc :)
  */
  
@@ -1438,7 +1657,7 @@
  {
  	bspDrawVert_t	mid, *dv2[ 3 ];
  	int				max;
-@@ -633,7 +689,7 @@
+@@ -645,7 +737,7 @@
  	
  	/* split the longest edge and map it */
  	LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
@@ -1447,7 +1666,7 @@
  	
  	/* push the point up a little bit to account for fp creep (fixme: revisit this) */
  	//%	VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
-@@ -641,12 +697,12 @@
+@@ -653,12 +745,12 @@
  	/* recurse to first triangle */
  	VectorCopy( dv, dv2 );
  	dv2[ max ] = &mid;
@@ -1462,7 +1681,7 @@
  }
  
  
-@@ -662,6 +718,7 @@
+@@ -674,6 +766,7 @@
  	int				i;
  	vec4_t			plane;
  	vec3_t			*stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
@@ -1470,7 +1689,7 @@
  	
  	
  	/* get plane if possible */
-@@ -687,16 +744,20 @@
+@@ -699,16 +792,20 @@
  		ttv = NULL;
  	}
  	
@@ -1495,7 +1714,7 @@
  		return qtrue;
  	}
  	
-@@ -718,7 +779,7 @@
+@@ -730,7 +827,7 @@
  			dv2[ 2 ] = dv[ (i + 1) % 3 ];
  			
  			/* map the degenerate triangle */
@@ -1504,7 +1723,7 @@
  		}
  	}
  	
-@@ -780,8 +841,8 @@
+@@ -792,8 +889,8 @@
  	LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );
  	
  	/* map the vertexes */
@@ -1515,7 +1734,7 @@
  	
  	/* 0 and 2 */
  	if( max == 0 )
-@@ -866,10 +927,10 @@
+@@ -878,10 +975,10 @@
  	}
  	
  	/* map the vertexes */
@@ -1530,7 +1749,7 @@
  	
  	/* subdivide the quad */
  	MapQuad_r( lm, info, dv, plane, stv, ttv );
-@@ -1161,7 +1222,7 @@
+@@ -1173,7 +1270,7 @@
  					continue;
  				
  				/* map the fake vert */
@@ -1539,7 +1758,7 @@
  			}
  		}
  	}
-@@ -1636,22 +1697,32 @@
+@@ -1963,22 +2062,32 @@
  					deluxel = SUPER_DELUXEL( x, y );
  					origin = SUPER_ORIGIN( x, y );
  					normal = SUPER_NORMAL( x, y );
@@ -1589,16 +1808,16 @@
  					if( deluxemap )
 Index: tools/quake3/q3map2/q3map2.h
 ===================================================================
---- tools/quake3/q3map2/q3map2.h	(revision 290)
+--- tools/quake3/q3map2/q3map2.h	(revision 303)
 +++ tools/quake3/q3map2/q3map2.h	(working copy)
-@@ -34,8 +34,8 @@
+@@ -35,8 +35,8 @@
  
  
  /* version */
--#define Q3MAP_VERSION	"2.5.11"
--#define Q3MAP_MOTD		"A well-oiled toaster oven"
-+#define Q3MAP_VERSION	"2.5.11-div0-obj-decomptexcoords-snapplane-UTavgcolorfix-UTfloodlight-UTtrianglecheck"
-+#define Q3MAP_MOTD		"Blackhole Box gives the light back"
+-#define Q3MAP_VERSION	"2.5.17"
+-#define Q3MAP_MOTD		"Last one turns the lights off"
++#define Q3MAP_VERSION	"2.5.17-div0-obj-decomptexcoords-snapplane-UTavgcolorfix-UTfloodlight-UTlmexposure-UTtrianglecheck"
++#define Q3MAP_MOTD		"Light some candles, put them on a wooden table, take a photo, and paste it on the lightmaps!"
  
  
  

Modified: branches/nexuiz-2.0/misc/gtkradiant/mergepatches.sh
===================================================================
--- branches/nexuiz-2.0/misc/gtkradiant/mergepatches.sh	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/misc/gtkradiant/mergepatches.sh	2008-07-31 06:23:51 UTC (rev 3991)
@@ -37,16 +37,16 @@
 cat <<EOF
 Index: tools/quake3/q3map2/q3map2.h
 ===================================================================
---- tools/quake3/q3map2/q3map2.h	(revision 290)
+--- tools/quake3/q3map2/q3map2.h	(revision 303)
 +++ tools/quake3/q3map2/q3map2.h	(working copy)
-@@ -34,8 +34,8 @@
+@@ -35,8 +35,8 @@
  
  
  /* version */
--#define Q3MAP_VERSION	"2.5.11"
--#define Q3MAP_MOTD		"A well-oiled toaster oven"
-+#define Q3MAP_VERSION	"2.5.11-div0$pq"
-+#define Q3MAP_MOTD		"Blackhole Box gives the light back"
+-#define Q3MAP_VERSION	"2.5.17"
+-#define Q3MAP_MOTD		"Last one turns the lights off"
++#define Q3MAP_VERSION	"2.5.17-div0$pq"
++#define Q3MAP_MOTD		"Light some candles, put them on a wooden table, take a photo, and paste it on the lightmaps!"
  
  
  

Deleted: branches/nexuiz-2.0/misc/gtkradiant/singlepatches/both-obj.diff
===================================================================
--- branches/nexuiz-2.0/misc/gtkradiant/singlepatches/both-obj.diff	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/misc/gtkradiant/singlepatches/both-obj.diff	2008-07-31 06:23:51 UTC (rev 3991)
@@ -1,103 +0,0 @@
-Index: libs/picomodel/pm_obj.c
-===================================================================
---- libs/picomodel/pm_obj.c	(revision 290)
-+++ libs/picomodel/pm_obj.c	(working copy)
-@@ -215,10 +215,9 @@
- 	}
- }
- 
--#if 0
- static int _obj_mtl_load( picoModel_t *model )
- {
--	//picoShader_t *curShader = NULL;
-+	picoShader_t *curShader = NULL;
- 	picoParser_t *p;
- 	picoByte_t   *mtlBuffer;
- 	int			  mtlBufSize;
-@@ -266,7 +265,7 @@
- 		/* get next token in material file */
- 		if (_pico_parse( p,1 ) == NULL)
- 			break;
--#if 0
-+#if 1
- 
- 		/* skip empty lines */
- 		if (p->token == NULL || !strlen( p->token ))
-@@ -308,6 +307,7 @@
- 		else if (!_pico_stricmp(p->token,"map_kd"))
- 		{
- 			char *mapName;
-+			picoShader_t *shader;
- 
- 			/* pointer to current shader must be valid */
- 			if (curShader == NULL)
-@@ -322,6 +322,10 @@
- 				_pico_printf( PICO_ERROR,"Missing material map name in MTL, line %d.",p->curLine);
- 				_obj_mtl_error_return;
- 			}
-+			/* create a new pico shader */
-+			shader = PicoNewShader( model );
-+			if (shader == NULL)
-+				_obj_mtl_error_return;
- 			/* set shader map name */
- 			PicoSetShaderMapName( shader,mapName );
- 		}
-@@ -478,7 +482,6 @@
- 	/* return with success */
- 	return 1;
- }
--#endif
- 
- /* _obj_load:
-  *  loads a wavefront obj model file.
-@@ -523,7 +526,7 @@
- 	PicoSetModelFileName( model,fileName );
- 
- 	/* try loading the materials; we don't handle the result */
--#if 0
-+#if 1
- 	_obj_mtl_load( model );
- #endif
- 
-@@ -832,6 +835,41 @@
- 				curVertex += max;
- 			}
- 		}
-+		else if (!_pico_stricmp(p->token,"usemtl"))
-+		{
-+			picoShader_t *shader;
-+			char *name;
-+
-+			/* get material name */
-+			name = _pico_parse( p,0 );
-+
-+			/* validate material name */
-+			if (name == NULL || !strlen(name))
-+			{
-+				_pico_printf( PICO_ERROR,"Missing material name in OBJ, line %d.",p->curLine);
-+			}
-+			else
-+			{
-+				shader = PicoFindShader( model, name, 1 );
-+				if (shader == NULL)
-+				{
-+					_pico_printf( PICO_ERROR,"Undefined material name in OBJ, line %d. Making a default shader.",p->curLine);
-+
-+					/* create a new pico shader */
-+					shader = PicoNewShader( model );
-+					if (shader != NULL)
-+					{
-+						PicoSetShaderName( shader,name );
-+						PicoSetShaderMapName( shader,name );
-+						PicoSetSurfaceShader( curSurface, shader );
-+					}
-+				}
-+				else
-+				{
-+					PicoSetSurfaceShader( curSurface, shader );
-+				}
-+			}
-+		}
- 		/* skip unparsed rest of line and continue */
- 		_pico_parse_skip_rest( p );
- 	}

Copied: branches/nexuiz-2.0/misc/gtkradiant/singlepatches/both-obj.diff (from rev 3990, trunk/misc/gtkradiant/singlepatches/both-obj.diff)
===================================================================
--- branches/nexuiz-2.0/misc/gtkradiant/singlepatches/both-obj.diff	                        (rev 0)
+++ branches/nexuiz-2.0/misc/gtkradiant/singlepatches/both-obj.diff	2008-07-31 06:23:51 UTC (rev 3991)
@@ -0,0 +1,103 @@
+Index: libs/picomodel/pm_obj.c
+===================================================================
+--- libs/picomodel/pm_obj.c	(revision 290)
++++ libs/picomodel/pm_obj.c	(working copy)
+@@ -215,10 +215,9 @@
+ 	}
+ }
+ 
+-#if 0
+ static int _obj_mtl_load( picoModel_t *model )
+ {
+-	//picoShader_t *curShader = NULL;
++	picoShader_t *curShader = NULL;
+ 	picoParser_t *p;
+ 	picoByte_t   *mtlBuffer;
+ 	int			  mtlBufSize;
+@@ -266,7 +265,7 @@
+ 		/* get next token in material file */
+ 		if (_pico_parse( p,1 ) == NULL)
+ 			break;
+-#if 0
++#if 1
+ 
+ 		/* skip empty lines */
+ 		if (p->token == NULL || !strlen( p->token ))
+@@ -308,6 +307,7 @@
+ 		else if (!_pico_stricmp(p->token,"map_kd"))
+ 		{
+ 			char *mapName;
++			picoShader_t *shader;
+ 
+ 			/* pointer to current shader must be valid */
+ 			if (curShader == NULL)
+@@ -322,6 +322,10 @@
+ 				_pico_printf( PICO_ERROR,"Missing material map name in MTL, line %d.",p->curLine);
+ 				_obj_mtl_error_return;
+ 			}
++			/* create a new pico shader */
++			shader = PicoNewShader( model );
++			if (shader == NULL)
++				_obj_mtl_error_return;
+ 			/* set shader map name */
+ 			PicoSetShaderMapName( shader,mapName );
+ 		}
+@@ -478,7 +482,6 @@
+ 	/* return with success */
+ 	return 1;
+ }
+-#endif
+ 
+ /* _obj_load:
+  *  loads a wavefront obj model file.
+@@ -523,7 +526,7 @@
+ 	PicoSetModelFileName( model,fileName );
+ 
+ 	/* try loading the materials; we don't handle the result */
+-#if 0
++#if 1
+ 	_obj_mtl_load( model );
+ #endif
+ 
+@@ -832,6 +835,41 @@
+ 				curVertex += max;
+ 			}
+ 		}
++		else if (!_pico_stricmp(p->token,"usemtl"))
++		{
++			picoShader_t *shader;
++			char *name;
++
++			/* get material name */
++			name = _pico_parse( p,0 );
++
++			/* validate material name */
++			if (name == NULL || !strlen(name))
++			{
++				_pico_printf( PICO_ERROR,"Missing material name in OBJ, line %d.",p->curLine);
++			}
++			else
++			{
++				shader = PicoFindShader( model, name, 1 );
++				if (shader == NULL)
++				{
++					_pico_printf( PICO_ERROR,"Undefined material name in OBJ, line %d. Making a default shader.",p->curLine);
++
++					/* create a new pico shader */
++					shader = PicoNewShader( model );
++					if (shader != NULL)
++					{
++						PicoSetShaderName( shader,name );
++						PicoSetShaderMapName( shader,name );
++						PicoSetSurfaceShader( curSurface, shader );
++					}
++				}
++				else
++				{
++					PicoSetSurfaceShader( curSurface, shader );
++				}
++			}
++		}
+ 		/* skip unparsed rest of line and continue */
+ 		_pico_parse_skip_rest( p );
+ 	}

Modified: branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-UTavgcolorfix.diff
===================================================================
--- branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-UTavgcolorfix.diff	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-UTavgcolorfix.diff	2008-07-31 06:23:51 UTC (rev 3991)
@@ -1,8 +1,8 @@
 Index: tools/quake3/q3map2/shaders.c
 ===================================================================
---- tools/quake3/q3map2/shaders.c	(revision 290)
+--- tools/quake3/q3map2/shaders.c	(revision 191)
 +++ tools/quake3/q3map2/shaders.c	(working copy)
-@@ -747,8 +747,14 @@
+@@ -793,8 +793,14 @@
  	}
  	
  	if( VectorLength( si->color ) <= 0.0f )

Modified: branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-UTfloodlight.diff
===================================================================
--- branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-UTfloodlight.diff	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-UTfloodlight.diff	2008-07-31 06:23:51 UTC (rev 3991)
@@ -1,21 +1,20 @@
 Index: tools/quake3/q3map2/light_ydnar.c
 ===================================================================
---- tools/quake3/q3map2/light_ydnar.c	(revision 290)
+--- tools/quake3/q3map2/light_ydnar.c	(revision 191)
 +++ tools/quake3/q3map2/light_ydnar.c	(working copy)
-@@ -1449,6 +1449,8 @@
- 	vec3_t				color, averageColor, averageDir, total, temp, temp2;
+@@ -1767,6 +1864,8 @@
  	float				tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
  	trace_t				trace;
+ 	float				stackLightLuxels[ STACK_LL_SIZE ];
 +	vec3_t				flood;
 +	float				*floodlight;
  	
  	
  	/* bail if this number exceeds the number of raw lightmaps */
-@@ -1871,6 +1873,78 @@
- 	/* free light list */
+@@ -2223,6 +2332,78 @@
  	FreeTraceLights( &trace );
  	
-+  	/*	-----------------------------------------------------------------
+ 	/*	-----------------------------------------------------------------
 +		floodlight pass
 +		----------------------------------------------------------------- */
 +
@@ -87,10 +86,11 @@
 +		}
 +	}
 +	
- 	/* -----------------------------------------------------------------
- 	   filter pass
- 	   ----------------------------------------------------------------- */
-@@ -3123,7 +3197,320 @@
++	/*	-----------------------------------------------------------------
+ 		dirt pass
+ 		----------------------------------------------------------------- */
+ 	
+@@ -3587,7 +3768,320 @@
  	CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
  }
  
@@ -413,9 +413,9 @@
 +
 Index: tools/quake3/q3map2/light.c
 ===================================================================
---- tools/quake3/q3map2/light.c	(revision 290)
+--- tools/quake3/q3map2/light.c	(revision 191)
 +++ tools/quake3/q3map2/light.c	(working copy)
-@@ -1363,6 +1363,56 @@
+@@ -1378,6 +1378,56 @@
  			break;
  	}
  	
@@ -472,9 +472,9 @@
  	/* normalize to get primary light direction */
  	VectorNormalize( gp->dir, gp->dir );
  	
-@@ -1544,6 +1594,12 @@
- 	qboolean	minVertex, minGrid;
- 	const char	*value;
+@@ -1661,6 +1711,12 @@
+ 		RunThreadsOnIndividual( numRawLightmaps, qtrue, DirtyRawLightmap );
+ 	}
  	
 +	/* floodlight them up */
 +	if( floodlighty )
@@ -483,19 +483,19 @@
 +		RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
 +	}
  
- 	/* ydnar: smooth normals */
- 	if( shade )
-@@ -1675,6 +1731,7 @@
+ 	/* ydnar: set up light envelopes */
+ 	SetupEnvelopes( qfalse, fast );
+@@ -1703,6 +1759,7 @@
  		/* flag bouncing */
  		bouncing = qtrue;
  		VectorClear( ambientColor );
-+		floodlighty = qfalse;
++		floodlighty = false;
  		
  		/* generate diffuse lights */
  		RadFreeLights();
-@@ -2114,6 +2171,21 @@
- 			loMem = qtrue;
- 			Sys_Printf( "Enabling low-memory (potentially slower) lighting mode\n" );
+@@ -2191,6 +2256,21 @@
+ 			cpmaHack = qtrue;
+ 			Sys_Printf( "Enabling Challenge Pro Mode Asstacular Vertex Lighting Mode (tm)\n" );
  		}
 +		else if( !strcmp( argv[ i ], "-floodlight" ) )
 +		{
@@ -513,21 +513,21 @@
 +			Sys_Printf( "Low Quality FloodLighting enabled\n" );
 +		}
  		
- 		else
- 			Sys_Printf( "WARNING: Unknown option \"%s\"\n", argv[ i ] );
-@@ -2156,6 +2228,7 @@
- 	
+ 		/* r7: dirtmapping */
+ 		else if( !strcmp( argv[ i ], "-dirty" ) )
+@@ -2279,6 +2359,7 @@
  	/* ydnar: set up optimization */
  	SetupBrushes();
+ 	SetupDirt();
 +	SetupFloodLight();
  	SetupSurfaceLightmaps();
  	
  	/* initialize the surface facet tracing */
 Index: tools/quake3/q3map2/lightmaps_ydnar.c
 ===================================================================
---- tools/quake3/q3map2/lightmaps_ydnar.c	(revision 290)
+--- tools/quake3/q3map2/lightmaps_ydnar.c	(revision 191)
 +++ tools/quake3/q3map2/lightmaps_ydnar.c	(working copy)
-@@ -413,6 +413,12 @@
+@@ -414,6 +414,12 @@
  		lm->superNormals = safe_malloc( size );
  	memset( lm->superNormals, 0, size );
  	
@@ -542,25 +542,25 @@
  	if( lm->superClusters == NULL )
 Index: tools/quake3/q3map2/q3map2.h
 ===================================================================
---- tools/quake3/q3map2/q3map2.h	(revision 290)
+--- tools/quake3/q3map2/q3map2.h	(revision 191)
 +++ tools/quake3/q3map2/q3map2.h	(working copy)
-@@ -265,6 +265,7 @@
- #define SUPER_NORMAL_SIZE		3
+@@ -267,6 +267,7 @@
+ #define SUPER_NORMAL_SIZE		4
  #define SUPER_DELUXEL_SIZE		3
  #define BSP_DELUXEL_SIZE		3
 +#define SUPER_FLOODLIGHT_SIZE	1
  
  #define VERTEX_LUXEL( s, v )	(vertexLuxels[ s ] + ((v) * VERTEX_LUXEL_SIZE))
  #define RAD_VERTEX_LUXEL( s, v )(radVertexLuxels[ s ] + ((v) * VERTEX_LUXEL_SIZE))
-@@ -273,6 +274,7 @@
- #define SUPER_LUXEL( s, x, y )	(lm->superLuxels[ s ] + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
+@@ -279,6 +280,7 @@
  #define SUPER_ORIGIN( x, y )	(lm->superOrigins + ((((y) * lm->sw) + (x)) * SUPER_ORIGIN_SIZE))
  #define SUPER_NORMAL( x, y )	(lm->superNormals + ((((y) * lm->sw) + (x)) * SUPER_NORMAL_SIZE))
+ #define SUPER_DIRT( x, y )		(lm->superNormals + ((((y) * lm->sw) + (x)) * SUPER_NORMAL_SIZE) + 3)	/* stash dirtyness in normal[ 3 ] */
 +#define SUPER_FLOODLIGHT( x, y )	(lm->superFloodLight + ((((y) * lm->sw) + (x)) * SUPER_FLOODLIGHT_SIZE) )	
- #define SUPER_CLUSTER( x, y )	(lm->superClusters + (((y) * lm->sw) + (x)))
- #define SUPER_DELUXEL( x, y )	(lm->superDeluxels + ((((y) * lm->sw) + (x)) * SUPER_DELUXEL_SIZE))
- #define BSP_DELUXEL( x, y )		(lm->bspDeluxels + ((((y) * lm->w) + (x)) * BSP_DELUXEL_SIZE))
-@@ -1364,6 +1366,7 @@
+ 
+ 
+ 
+@@ -1392,6 +1395,7 @@
  	
  	float					*superDeluxels;	/* average light direction */
  	float					*bspDeluxels;
@@ -568,58 +568,28 @@
  }
  rawLightmap_t;
  
-@@ -1670,6 +1673,9 @@
- void						SmoothNormals( void );
+@@ -1704,6 +1708,10 @@
+ float						DirtForSample( trace_t *trace );
+ void						DirtyRawLightmap( int num );
  
- void						MapRawLightmap( int num );
 +void						SetupFloodLight();
 +float						FloodLightForSample( trace_t *trace );
 +void						FloodLightRawLightmap( int num );
++
  void						IlluminateRawLightmap( int num );
  void						IlluminateVertexes( int num );
  
-@@ -2037,6 +2043,12 @@
- Q_EXTERN qboolean			sunOnly;
- Q_EXTERN int				approximateTolerance Q_ASSIGN( 0 );
- Q_EXTERN qboolean			noCollapse;
+@@ -2098,6 +2106,13 @@
+ Q_EXTERN float				dirtScale Q_ASSIGN( 1.0f );
+ Q_EXTERN float				dirtGain Q_ASSIGN( 1.0f );
+ 
 +Q_EXTERN qboolean			debugnormals Q_ASSIGN( qfalse );
 +Q_EXTERN qboolean			floodlighty Q_ASSIGN( qfalse );
 +Q_EXTERN qboolean			floodlight_lowquality Q_ASSIGN( qfalse );
 +Q_EXTERN vec3_t				floodlightRGB;
 +Q_EXTERN float				floodlightIntensity Q_ASSIGN( 512 );
 +Q_EXTERN float				floodlightDistance Q_ASSIGN( 1024 );
- Q_EXTERN qboolean			debug;
- Q_EXTERN qboolean			debugSurfaces;
- Q_EXTERN qboolean			debugUnused;
-Index: tools/quake3/q3map2/q3map2.h
-===================================================================
---- tools/quake3/q3map2/q3map2.h	(revision 290)
-+++ tools/quake3/q3map2/q3map2.h	(working copy)
-@@ -1274,6 +1274,7 @@
- 	vec3_t				color;			/* starts out at full color, may be reduced if transparent surfaces are crossed */
- 	
- 	/* output */
-+	vec3_t				hit;
- 	int					compileFlags;	/* for determining surface compile flags traced through */
- 	qboolean			passSolid;
- 	qboolean			opaque;
-Index: tools/quake3/q3map2/light_trace.c
-===================================================================
---- tools/quake3/q3map2/light_trace.c	(revision 290)
-+++ tools/quake3/q3map2/light_trace.c	(working copy)
-@@ -1596,6 +1596,7 @@
- 	/* bogus node number means solid, end tracing unless testing all */
- 	if( nodeNum < 0 )
- 	{
-+		VectorCopy( origin, trace->hit );
- 		trace->passSolid = qtrue;
- 		return qtrue;
- 	}
-@@ -1606,6 +1607,7 @@
- 	/* solid? */
- 	if( node->type == TRACE_LEAF_SOLID )
- 	{
-+		VectorCopy( origin, trace->hit );
- 		trace->passSolid = qtrue;
- 		return qtrue;
- 	}
++
+ Q_EXTERN qboolean			dump Q_ASSIGN( qfalse );
+ Q_EXTERN qboolean			debug Q_ASSIGN( qfalse );
+ Q_EXTERN qboolean			debugUnused Q_ASSIGN( qfalse );

Copied: branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-UTlmexposure.diff (from rev 3990, trunk/misc/gtkradiant/singlepatches/q3map2-UTlmexposure.diff)
===================================================================
--- branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-UTlmexposure.diff	                        (rev 0)
+++ branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-UTlmexposure.diff	2008-07-31 06:23:51 UTC (rev 3991)
@@ -0,0 +1,244 @@
+Index: tools/quake3/q3map2/game_ja.h
+===================================================================
+--- tools/quake3/q3map2/game_ja.h	(revision 191)
++++ tools/quake3/q3map2/game_ja.h	(working copy)
+@@ -67,6 +67,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"RBSP",				/* bsp file prefix */
+ 	1,					/* bsp file version */
+Index: tools/quake3/q3map2/game_tremulous.h
+===================================================================
+--- tools/quake3/q3map2/game_tremulous.h	(revision 191)
++++ tools/quake3/q3map2/game_tremulous.h	(working copy)
+@@ -70,6 +70,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	46,					/* bsp file version */
+Index: tools/quake3/q3map2/game_wolfet.h
+===================================================================
+--- tools/quake3/q3map2/game_wolfet.h	(revision 191)
++++ tools/quake3/q3map2/game_wolfet.h	(working copy)
+@@ -66,6 +66,7 @@
+ 	qtrue,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	47,					/* bsp file version */
+Index: tools/quake3/q3map2/game_wolf.h
+===================================================================
+--- tools/quake3/q3map2/game_wolf.h	(revision 191)
++++ tools/quake3/q3map2/game_wolf.h	(working copy)
+@@ -129,6 +129,7 @@
+ 	qtrue,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	47,					/* bsp file version */
+Index: tools/quake3/q3map2/game_sof2.h
+===================================================================
+--- tools/quake3/q3map2/game_sof2.h	(revision 191)
++++ tools/quake3/q3map2/game_sof2.h	(working copy)
+@@ -139,6 +139,7 @@
+ 	qfalse,					/* wolf lighting model? */
+ 	128,					/* lightmap width/height */
+ 	1.0f,					/* lightmap gamma */
++	1.0f,					/* lightmap exposure */
+ 	1.0f,					/* lightmap compensate */
+ 	"RBSP",					/* bsp file prefix */
+ 	1,						/* bsp file version */
+Index: tools/quake3/q3map2/game_etut.h
+===================================================================
+--- tools/quake3/q3map2/game_etut.h	(revision 191)
++++ tools/quake3/q3map2/game_etut.h	(working copy)
+@@ -148,6 +148,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	2.2f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	47,					/* bsp file version */
+Index: tools/quake3/q3map2/game_jk2.h
+===================================================================
+--- tools/quake3/q3map2/game_jk2.h	(revision 191)
++++ tools/quake3/q3map2/game_jk2.h	(working copy)
+@@ -64,6 +64,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"RBSP",				/* bsp file prefix */
+ 	1,					/* bsp file version */
+Index: tools/quake3/q3map2/game_qfusion.h
+===================================================================
+--- tools/quake3/q3map2/game_qfusion.h	(revision 191)
++++ tools/quake3/q3map2/game_qfusion.h	(working copy)
+@@ -115,6 +115,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	512,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"FBSP",				/* bsp file prefix */
+ 	1,					/* bsp file version */
+Index: tools/quake3/q3map2/game_tenebrae.h
+===================================================================
+--- tools/quake3/q3map2/game_tenebrae.h	(revision 191)
++++ tools/quake3/q3map2/game_tenebrae.h	(working copy)
+@@ -112,6 +112,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	512,				/* lightmap width/height */
+ 	2.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	46,					/* bsp file version */
+Index: tools/quake3/q3map2/game_quake3.h
+===================================================================
+--- tools/quake3/q3map2/game_quake3.h	(revision 191)
++++ tools/quake3/q3map2/game_quake3.h	(working copy)
+@@ -112,6 +112,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	46,					/* bsp file version */
+Index: tools/quake3/q3map2/game_ef.h
+===================================================================
+--- tools/quake3/q3map2/game_ef.h	(revision 191)
++++ tools/quake3/q3map2/game_ef.h	(working copy)
+@@ -113,6 +113,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	46,					/* bsp file version */
+Index: tools/quake3/q3map2/light_ydnar.c
+===================================================================
+--- tools/quake3/q3map2/light_ydnar.c	(revision 191)
++++ tools/quake3/q3map2/light_ydnar.c	(working copy)
+@@ -49,6 +49,7 @@
+ 	int		i;
+ 	float	max, gamma;
+ 	vec3_t	sample;
++	float 	inv, dif;
+ 	
+ 	
+ 	/* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
+@@ -72,16 +73,51 @@
+ 		/* gamma */
+ 		sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
+ 	}
++
++	if (lightmapExposure == 1)
++	{
++		/* clamp with color normalization */
++		max = sample[ 0 ];
++		if( sample[ 1 ] > max )
++			max = sample[ 1 ];
++		if( sample[ 2 ] > max )
++			max = sample[ 2 ];
++		if( max > 255.0f )
++			VectorScale( sample, (255.0f / max), sample );
++	}
++	else
++	{
++		if (lightmapExposure==0)
++		{
++			lightmapExposure=1.0f;
++		}
++		inv=1.f/lightmapExposure;
++		//Exposure
++    	
++		max = sample[ 0 ];
++		if( sample[ 1 ] > max )
++			max = sample[ 1 ];
++		if( sample[ 2 ] > max )
++			max = sample[ 2 ];  
++      
++		dif = (1-  exp(-max * inv) )  *  255;
++
++		if (max >0) 
++		{
++			dif = dif / max;
++		}
++		else
++		{
++			dif = 0;
++		}
++
++		for (i=0;i<3;i++)
++		{
++			sample[i]*=dif;
++		}
++	}
++
+ 	
+-	/* clamp with color normalization */
+-	max = sample[ 0 ];
+-	if( sample[ 1 ] > max )
+-		max = sample[ 1 ];
+-	if( sample[ 2 ] > max )
+-		max = sample[ 2 ];
+-	if( max > 255.0f )
+-		VectorScale( sample, (255.0f / max), sample );
+-	
+ 	/* compensate for ingame overbrighting/bitshifting */
+ 	VectorScale( sample, (1.0f / lightmapCompensate), sample );
+ 	
+Index: tools/quake3/q3map2/light.c
+===================================================================
+--- tools/quake3/q3map2/light.c (revision 191)
++++ tools/quake3/q3map2/light.c (working copy)
+@@ -1836,6 +1893,14 @@
+ 			i++;
+ 		}
+ 		
++		else if( !strcmp( argv[ i ], "-exposure" ) )
++		{
++			f = atof( argv[ i + 1 ] );
++			lightmapExposure = f;
++			Sys_Printf( "Lighting exposure set to %f\n", lightmapExposure );
++			i++;
++		}
++		
+ 		else if( !strcmp( argv[ i ], "-compensate" ) )
+ 		{
+ 			f = atof( argv[ i + 1 ] );
+Index: tools/quake3/q3map2/q3map2.h
+===================================================================
+--- tools/quake3/q3map2/q3map2.h	(revision 191)
++++ tools/quake3/q3map2/q3map2.h	(working copy)
+@@ -543,6 +545,7 @@
+ 	qboolean			wolfLight;						/* when true, lights work like wolf q3map  */
+ 	int					lightmapSize;					/* bsp lightmap width/height */
+ 	float				lightmapGamma;					/* default lightmap gamma */
++	float				lightmapExposure;				/* default lightmap exposure */
+ 	float				lightmapCompensate;				/* default lightmap compensate value */
+ 	char				*bspIdent;						/* 4-letter bsp file prefix */
+ 	int					bspVersion;						/* bsp version to use */
+@@ -2117,6 +2132,7 @@
+ 
+ /* ydnar: lightmap gamma/compensation */
+ Q_EXTERN float				lightmapGamma Q_ASSIGN( 1.0f );
++Q_EXTERN float				lightmapExposure Q_ASSIGN( 1.0f );
+ Q_EXTERN float				lightmapCompensate Q_ASSIGN( 1.0f );
+ 
+ /* ydnar: for runtime tweaking of falloff tolerance */

Modified: branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-UTtrianglecheck.diff
===================================================================
--- branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-UTtrianglecheck.diff	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-UTtrianglecheck.diff	2008-07-31 06:23:51 UTC (rev 3991)
@@ -1,8 +1,8 @@
 Index: tools/quake3/q3map2/light_ydnar.c
 ===================================================================
---- tools/quake3/q3map2/light_ydnar.c	(revision 290)
+--- tools/quake3/q3map2/light_ydnar.c	(revision 191)
 +++ tools/quake3/q3map2/light_ydnar.c	(working copy)
-@@ -372,7 +372,7 @@
+@@ -384,7 +420,7 @@
  #define NUDGE			0.5f
  #define BOGUS_NUDGE		-99999.0f
  
@@ -11,7 +11,7 @@
  {
  	int				i, x, y, numClusters, *clusters, pointCluster, *cluster;
  	float			*luxel, *origin, *normal, d, lightmapSampleOffset;
-@@ -380,6 +380,12 @@
+@@ -392,6 +428,12 @@
  	vec3_t			pNormal;
  	vec3_t			vecs[ 3 ];
  	vec3_t			nudged;
@@ -24,7 +24,7 @@
  	float			*nudge;
  	static float	nudges[][ 2 ] =
  					{
-@@ -473,6 +479,51 @@
+@@ -485,6 +527,51 @@
  	/* non axial lightmap projection (explicit xyz) */
  	else
  		VectorCopy( dv->xyz, origin );
@@ -76,7 +76,7 @@
  	
  	/* planar surfaces have precalculated lightmap vectors for nudging */
  	if( lm->plane != NULL )
-@@ -504,8 +555,13 @@
+@@ -516,8 +603,13 @@
  	else
  		origin[ lm->axisNum ] += lightmapSampleOffset;
  	
@@ -91,7 +91,7 @@
  	
  	/* another retarded hack, storing nudge count in luxel[ 1 ] */
  	luxel[ 1 ] = 0.0f;	
-@@ -521,14 +577,14 @@
+@@ -533,14 +625,14 @@
  			for( i = 0; i < 3; i++ )
  			{
  				/* set nudged point*/
@@ -109,7 +109,7 @@
  			luxel[ 1 ] += 1.0f;
  		}
  	}
-@@ -538,8 +594,8 @@
+@@ -550,8 +642,8 @@
  	{
  		VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
  		pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
@@ -120,7 +120,7 @@
  		luxel[ 1 ] += 1.0f;
  	}
  	
-@@ -585,7 +641,7 @@
+@@ -597,7 +689,7 @@
  than the distance between two luxels (thanks jc :)
  */
  
@@ -129,7 +129,7 @@
  {
  	bspDrawVert_t	mid, *dv2[ 3 ];
  	int				max;
-@@ -633,7 +689,7 @@
+@@ -645,7 +737,7 @@
  	
  	/* split the longest edge and map it */
  	LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
@@ -138,7 +138,7 @@
  	
  	/* push the point up a little bit to account for fp creep (fixme: revisit this) */
  	//%	VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
-@@ -641,12 +697,12 @@
+@@ -653,12 +745,12 @@
  	/* recurse to first triangle */
  	VectorCopy( dv, dv2 );
  	dv2[ max ] = &mid;
@@ -153,7 +153,7 @@
  }
  
  
-@@ -662,6 +718,7 @@
+@@ -674,6 +766,7 @@
  	int				i;
  	vec4_t			plane;
  	vec3_t			*stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
@@ -161,7 +161,7 @@
  	
  	
  	/* get plane if possible */
-@@ -687,16 +744,20 @@
+@@ -699,16 +792,20 @@
  		ttv = NULL;
  	}
  	
@@ -186,7 +186,7 @@
  		return qtrue;
  	}
  	
-@@ -718,7 +779,7 @@
+@@ -730,7 +827,7 @@
  			dv2[ 2 ] = dv[ (i + 1) % 3 ];
  			
  			/* map the degenerate triangle */
@@ -195,7 +195,7 @@
  		}
  	}
  	
-@@ -780,8 +841,8 @@
+@@ -792,8 +889,8 @@
  	LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );
  	
  	/* map the vertexes */
@@ -206,7 +206,7 @@
  	
  	/* 0 and 2 */
  	if( max == 0 )
-@@ -866,10 +927,10 @@
+@@ -878,10 +975,10 @@
  	}
  	
  	/* map the vertexes */
@@ -221,7 +221,7 @@
  	
  	/* subdivide the quad */
  	MapQuad_r( lm, info, dv, plane, stv, ttv );
-@@ -1161,7 +1222,7 @@
+@@ -1173,7 +1270,7 @@
  					continue;
  				
  				/* map the fake vert */
@@ -230,7 +230,7 @@
  			}
  		}
  	}
-@@ -1636,22 +1697,32 @@
+@@ -1963,22 +2062,32 @@
  					deluxel = SUPER_DELUXEL( x, y );
  					origin = SUPER_ORIGIN( x, y );
  					normal = SUPER_NORMAL( x, y );

Modified: branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-decomptexcoords.diff
===================================================================
--- branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-decomptexcoords.diff	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-decomptexcoords.diff	2008-07-31 06:23:51 UTC (rev 3991)
@@ -1,8 +1,8 @@
 Index: tools/quake3/q3map2/convert_map.c
 ===================================================================
---- tools/quake3/q3map2/convert_map.c	(revision 290)
+--- tools/quake3/q3map2/convert_map.c	(revision 191)
 +++ tools/quake3/q3map2/convert_map.c	(working copy)
-@@ -45,6 +45,105 @@
+@@ -46,6 +46,105 @@
  #define	SNAP_FLOAT_TO_INT	4
  #define	SNAP_INT_TO_FLOAT	(1.0 / SNAP_FLOAT_TO_INT)
  
@@ -108,7 +108,7 @@
  static void ConvertBrush( FILE *f, int num, bspBrush_t *brush, vec3_t origin )
  {
  	int				i, j;
-@@ -53,12 +152,17 @@
+@@ -54,12 +153,17 @@
  	bspShader_t		*shader;
  	char			*texture;
  	bspPlane_t		*plane;
@@ -126,7 +126,7 @@
  	
  	/* clear out build brush */
  	for( i = 0; i < buildBrush->numsides; i++ )
-@@ -108,9 +212,88 @@
+@@ -109,9 +213,88 @@
  		/* get build side */
  		buildSide = &buildBrush->sides[ i ];
  		
@@ -215,7 +215,7 @@
  		
  		/* get texture name */
  		if( !Q_strncasecmp( buildSide->shaderInfo->shader, "textures/", 9 ) )
-@@ -129,14 +312,21 @@
+@@ -130,14 +313,21 @@
  		
  		/* print brush side */
  		/* ( 640 24 -224 ) ( 448 24 -224 ) ( 448 -232 -224 ) common/caulk 0 48 0 0.500000 0.500000 0 0 0 */
@@ -241,11 +241,11 @@
  
 Index: tools/quake3/q3map2/main.c
 ===================================================================
---- tools/quake3/q3map2/main.c	(revision 290)
+--- tools/quake3/q3map2/main.c	(revision 191)
 +++ tools/quake3/q3map2/main.c	(working copy)
-@@ -276,6 +276,18 @@
- 			else
- 				Sys_Printf( "Unknown conversion format \"%s\". Defaulting to ASE.\n", argv[ i ] );
+@@ -541,6 +541,18 @@
+ 					Sys_Printf( "Unknown conversion format \"%s\". Defaulting to ASE.\n", argv[ i ] );
+ 			}
   		}
 +		else if( !strcmp( argv[ i ],  "-ne" ) )
 + 		{

Modified: branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-snapplane.diff
===================================================================
--- branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-snapplane.diff	2008-07-31 06:07:22 UTC (rev 3990)
+++ branches/nexuiz-2.0/misc/gtkradiant/singlepatches/q3map2-snapplane.diff	2008-07-31 06:23:51 UTC (rev 3991)
@@ -1,8 +1,8 @@
 Index: tools/quake3/q3map2/model.c
 ===================================================================
---- tools/quake3/q3map2/model.c	(revision 290)
+--- tools/quake3/q3map2/model.c	(revision 193)
 +++ tools/quake3/q3map2/model.c	(working copy)
-@@ -221,6 +221,8 @@
+@@ -222,6 +222,8 @@
  	byte				*color;
  	picoIndex_t			*indexes;
  	remap_t				*rm, *glob;
@@ -11,7 +11,7 @@
  	
  	
  	/* get model */
-@@ -399,9 +401,8 @@
+@@ -398,9 +400,8 @@
  		/* ydnar: giant hack land: generate clipping brushes for model triangles */
  		if( si->clipModel || (spawnFlags & 2) )	/* 2nd bit */
  		{
@@ -224,27 +224,32 @@
  		}
 Index: tools/quake3/q3map2/map.c
 ===================================================================
---- tools/quake3/q3map2/map.c	(revision 290)
+--- tools/quake3/q3map2/map.c	(revision 193)
 +++ tools/quake3/q3map2/map.c	(working copy)
-@@ -183,9 +183,15 @@
+@@ -184,7 +184,7 @@
  snaps a plane to normal/distance epsilons
  */
  
 -void SnapPlane( vec3_t normal, vec_t *dist )
 +void SnapPlane( vec3_t normal, vec_t *dist, vec3_t center )
  {
--	SnapNormal( normal );
+ // SnapPlane disabled by LordHavoc because it often messes up collision
+ // brushes made from triangles of embedded models, and it has little effect
+@@ -193,7 +193,13 @@
+   SnapPlane reenabled by namespace because of multiple reports of
+   q3map2-crashes which were triggered by this patch.
+ */
 +	// div0: ensure the point "center" stays on the plane (actually, this
 +	// rotates the plane around the point center).
 +	// if center lies on the plane, it is guaranteed to stay on the plane by
 +	// this fix.
 +	vec_t centerDist = DotProduct(normal, center);
-+  	SnapNormal( normal );
+ 	SnapNormal( normal );
 +	*dist += (DotProduct(normal, center) - centerDist);
  
  	if( fabs( *dist - Q_rint( *dist ) ) < distanceEpsilon )
  		*dist = Q_rint( *dist );
-@@ -199,7 +205,7 @@
+@@ -207,7 +213,7 @@
  must be within an epsilon distance of the plane
  */
  
@@ -253,7 +258,7 @@
  
  #ifdef USE_HASHING
  
-@@ -207,10 +213,14 @@
+@@ -215,10 +221,14 @@
  	int		i, j, hash, h;
  	plane_t	*p;
  	vec_t	d;
@@ -270,7 +275,7 @@
  	hash = (PLANE_HASHES - 1) & (int) fabs( dist );
  	
  	/* search the border bins as well */
-@@ -251,7 +261,13 @@
+@@ -259,7 +269,13 @@
  	plane_t	*p;
  	
  

Copied: branches/nexuiz-2.0/misc/ttf2conchars (from rev 3990, trunk/misc/ttf2conchars)




More information about the nexuiz-commits mailing list