[nexuiz-commits] r6568 - in trunk: Docs data data/gfx data/models data/models/nexball data/qcsrc/client data/qcsrc/common data/qcsrc/server data/scripts data/sound data/sound/nexball data/textures data/textures/nexball misc/mediasource/graphics

DONOTREPLY at icculus.org DONOTREPLY at icculus.org
Wed Apr 22 13:52:54 EDT 2009


Author: mrbougo
Date: 2009-04-22 13:52:53 -0400 (Wed, 22 Apr 2009)
New Revision: 6568

Added:
   trunk/data/gfx/sb_nexball_carrying.tga
   trunk/data/models/nexball/
   trunk/data/models/nexball/ball.md3
   trunk/data/qcsrc/server/nexball.qc
   trunk/data/sound/nexball/
   trunk/data/sound/nexball/bounce.ogg
   trunk/data/sound/nexball/drop.ogg
   trunk/data/sound/nexball/steal.ogg
   trunk/data/textures/nexball/
   trunk/data/textures/nexball/ball.tga
   trunk/data/textures/nexball/ball_gloss.tga
   trunk/data/textures/nexball/ball_norm.tga
   trunk/misc/mediasource/graphics/sb-nexball-carrying.svg
Modified:
   trunk/Docs/mapping.txt
   trunk/data/defaultNexuiz.cfg
   trunk/data/qcsrc/client/Defs.qc
   trunk/data/qcsrc/client/Main.qc
   trunk/data/qcsrc/client/main.qh
   trunk/data/qcsrc/client/sbar.qc
   trunk/data/qcsrc/common/constants.qh
   trunk/data/qcsrc/common/mapinfo.qc
   trunk/data/qcsrc/common/mapinfo.qh
   trunk/data/qcsrc/common/util.qc
   trunk/data/qcsrc/server/cl_client.qc
   trunk/data/qcsrc/server/cl_physics.qc
   trunk/data/qcsrc/server/cl_player.qc
   trunk/data/qcsrc/server/cl_weapons.qc
   trunk/data/qcsrc/server/clientcommands.qc
   trunk/data/qcsrc/server/defs.qh
   trunk/data/qcsrc/server/g_triggers.qc
   trunk/data/qcsrc/server/g_world.qc
   trunk/data/qcsrc/server/miscfunctions.qc
   trunk/data/qcsrc/server/progs.src
   trunk/data/qcsrc/server/scores_rules.qc
   trunk/data/qcsrc/server/teamplay.qc
   trunk/data/qcsrc/server/w_grenadelauncher.qc
   trunk/data/scripts/entities.def
Log:
Nexball goes official :D
Also, removing the useless CSQC_REVISION checks.


Modified: trunk/Docs/mapping.txt
===================================================================
--- trunk/Docs/mapping.txt	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/Docs/mapping.txt	2009-04-22 17:52:53 UTC (rev 6568)
@@ -21,9 +21,9 @@
 
 V        Rune Match
 
-VI       Assault
+VI       Race
 
-VII      Onslaught
+VII      Nexball
 
 Appendix A - Advanced mapinfo
 
@@ -362,7 +362,24 @@
 such entities, delete the data/data/mapname.mapinfo file - or simply edit it to
 add the "type race" line.
 
+=============
+=VI. Nexball=
+=============
 
+There are three required entities: ball_redgoal, ball_bluegoal, and one of ball_basketball
+or ball_football. Pay extra attention to the goals, as their names can be somewhat counter-
+intuitive: ball_redgoal will give a point to the red team when scored in, NOT to the blue team!
+
+There are also two other goal-like triggers, ball_fault and ball_bound, the first giving a point to
+the opposing team when the ball hits it, the second simply returning it. You can spawn the ball inside
+a goal-like trigger, this can be useful for basketball maps with separate teams and a common ball spawn.
+The different keys for the entities are documented in entities.def.
+
+The ball is affected by trigger_impulse, but not by trigger_push or teleporters.
+
+You should better avoid patches on the field, as collisions can sometimes get buggy on these.
+
+
 ===============================
 =Appendix A - Advanced mapinfo=
 ===============================

Modified: trunk/data/defaultNexuiz.cfg
===================================================================
--- trunk/data/defaultNexuiz.cfg	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/defaultNexuiz.cfg	2009-04-22 17:52:53 UTC (rev 6568)
@@ -459,6 +459,7 @@
 seta g_runematch_point_limit -1	"Runematch point limit limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
 seta g_keyhunt_point_limit -1	"Keyhunt point limit limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
 seta g_race_laps_limit -1	"Race laps limit limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta g_nexball_goallimit -1 "Nexball goal limit limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
 
 seta g_ctf_win_mode 0	"0: captures only, 1: captures, then points, 2: points only"
 seta g_ctf_ignore_frags 0	"1: regular frags give no points"
@@ -646,6 +647,58 @@
 // ready-restart it turns into a race. TODO not done yet (e.g. timing display)
 set g_race_teams 0	"when 2, 3, or 4, the race is played as a team game (the team members can add up their laps)"
 
+// nexball
+set g_nexball 0
+
+set g_nexball_basketball_effects_default     8    "default: dim light. The original version used 1024 (fire) but it gives bad performance"
+set g_balance_nexball_primary_speed       1000    "launching speed"
+set g_balance_nexball_primary_refire         0.7  "launching refire"
+set g_balance_nexball_primary_animtime       0.3  "launching animtime"
+set g_balance_nexball_secondary_animtime     0.3  "launching animtime"
+set g_balance_nexball_secondary_speed     3000    "stealing projectile speed"
+set g_balance_nexball_secondary_lifetime     0.15 "stealing projectile lifetime"
+set g_balance_nexball_secondary_force      500    "stealing projectile force"
+set g_balance_nexball_secondary_refire       0.6  "stealing projectile refire"
+set g_balance_nexball_secondary_animtime     0.3  "stealing projectile animtime"
+
+// -1: MrBougo's first try, not very playable but working...
+//     The ball gets the player's velocity * 1.5 + a vertical boost
+//  0: Revenant style
+//     Player's velocity + a boost where he's looking at + a boost 
+//     perpendicularly to the first boost, that is upwards relatively
+//     to the view angle
+//  1: MrBougo's modded Rev style 1
+//     The 2nd Rev boost is always vertical
+//  2: MrBougo's modded Rev style 2
+//     The 1st Rev boost is always horizontal
+//     The 2nd Rev boost is always vertical
+set g_nexball_football_physics  2  "0: Revenant's original movement, 1: 0 but half independant of aiming height, 2: 1 fully independant, -1: first recode try"
+
+set g_nexball_football_boost_forward      100   "forward velocity boost when the ball is touched"
+set g_nexball_football_boost_up           200   "vertical velocity boost when the ball is touched"
+
+set g_nexball_basketball_delay_hold           20    "time before a player who caught the ball loses it (anti-ballcamp)"
+set g_nexball_basketball_delay_hold_forteam   60    "time before a ball reset when a team holds the ball for too long"
+set g_nexball_basketball_teamsteal             1    "1 to allow players to steal from teammates, 0 to disallow"
+
+set g_nexball_basketball_carrier_speed         0.9  "speed multiplier for the ballcarrier"
+
+set g_nexball_meter_period                  1    "time to make a full cycle on the power meter"
+set g_nexball_basketball_meter              1    "use the power meter for basketball"
+set g_nexball_basketball_meter_minpower   0.5    "minimal multiplier to the launching speed when using the power meter"
+set g_nexball_basketball_meter_maxpower   1.2    "maximal multiplier to the launching speed when using the power meter"
+
+set g_nexball_delay_goal     3    "delay between a goal and a ball reset"
+set g_nexball_delay_idle     10   "maximal idle time before a reset"
+set g_nexball_delay_start    3    "time the ball stands on its spawn before being released"
+set g_nexball_delay_collect  0.5  "time before the same player can catch the ball he launched"
+
+set g_nexball_sound_bounce   1    "bouncing sound (0: off)"
+
+set g_nexball_basketball_trail  1  "1 to leave a trail"
+set g_nexball_football_trail    0  "1 to leave a trail"
+set g_nexball_trail_color     254  "1-256 for different colors (Quake palette, 254 is white)"
+
 // server game balance settings
 set g_balance_armor_regen 0
 set g_balance_armor_rot 0.1

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


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

Added: trunk/data/models/nexball/ball.md3
===================================================================
(Binary files differ)


Property changes on: trunk/data/models/nexball/ball.md3
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Modified: trunk/data/qcsrc/client/Defs.qc
===================================================================
--- trunk/data/qcsrc/client/Defs.qc	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/qcsrc/client/Defs.qc	2009-04-22 17:52:53 UTC (rev 6568)
@@ -200,6 +200,9 @@
 string race_othercheckpointenemy;
 float sb_showscores_force;
 
+// Nexball
+float nb_pb_period;
+
 // Spectating
 float spectatee_status;
 

Modified: trunk/data/qcsrc/client/Main.qc
===================================================================
--- trunk/data/qcsrc/client/Main.qc	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/qcsrc/client/Main.qc	2009-04-22 17:52:53 UTC (rev 6568)
@@ -836,8 +836,6 @@
 	centerprint(strMessage);
 }
 
-void CSQC_CheckRevision();
-
 void Fog_Force()
 {
 	// TODO somehow thwart prvm_globalset client ...
@@ -871,7 +869,7 @@
 	float i;
 	self.classname = "ent_client_init";
 
-	csqc_revision = ReadShort();
+	nb_pb_period = ReadByte() / 32; //Accuracy of 1/32th
 
 	for(i = 0; i < 24; ++i)
 		weaponimpulse[i] = ReadByte() - 1;
@@ -883,7 +881,6 @@
 		strunzone(forcefog);
 	forcefog = strzone(ReadString());
 
-	CSQC_CheckRevision();
 	if(!postinit)
 		PostInit();
 }
@@ -1033,20 +1030,6 @@
 	return bHandled;
 }
 
-// COMMIT-TODO: Update if necessare, before committing
-void CSQC_CheckRevision()
-{
-	if(csqc_revision == CSQC_REVISION)
-	{
-		print("^2SVQC and CSQC revisions are compatible.\n");
-	} else if(csqc_revision < CSQC_REVISION) {
-		print("^1Your csprogs.dat (CSQC) version is newer than the one on the server.\n");
-	} else if(csqc_revision > CSQC_REVISION) {
-		print("^1Your csprogs.dat (CSQC) is too old for this server.\n");
-		print("^1Please update to a newer version.\n");
-	}
-}
-
 string getcommandkey(string text, string command)
 {
 	string keys;

Modified: trunk/data/qcsrc/client/main.qh
===================================================================
--- trunk/data/qcsrc/client/main.qh	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/qcsrc/client/main.qh	2009-04-22 17:52:53 UTC (rev 6568)
@@ -36,8 +36,6 @@
 // --------------------------------------------------------------------------
 // General stuff
 
-float csqc_revision;
-
 float drawfont;
 float postinit;
 float gametype;

Modified: trunk/data/qcsrc/client/sbar.qc
===================================================================
--- trunk/data/qcsrc/client/sbar.qc	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/qcsrc/client/sbar.qc	2009-04-22 17:52:53 UTC (rev 6568)
@@ -13,6 +13,7 @@
 
 void CSQC_kh_hud();
 void CSQC_ctf_hud();
+void CSQC_nb_hud();
 void MapVote_Draw();
 void Sbar_FinaleOverlay()
 {
@@ -2123,6 +2124,9 @@
 			} else if(gametype == GAME_CTF)
 			{
 				CSQC_ctf_hud();
+			} else if(gametype == GAME_NEXBALL)
+			{
+				CSQC_nb_hud();
 			}
 		}
 		Sbar_DrawCenterPrint();
@@ -2190,3 +2194,64 @@
 				drawpic(pos, "gfx/sb_flag_blue_shielded", '128 64 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL); break;
 	}
 }
+
+//box style
+#define NBPB_SIZE '128 66 0'
+#define NBPB_BT 2                   //thickness
+#define NBPB_BRGB '1 1 1'
+#define NBPB_BALPH 1                //alpha
+#define NBPB_BFLAG DRAWFLAG_NORMAL
+#define NBPB_IALPH 0.4
+#define NBPB_IFLAG DRAWFLAG_ADDITIVE
+#define NBPB_IRGB '0.7 0.1 0'
+
+void CSQC_nb_hud(void)
+{
+	float stat_items, nb_pb_starttime, dt, p;
+	vector pos;
+	
+	stat_items = getstati(STAT_ITEMS);
+	nb_pb_starttime = getstatf(STAT_NB_METERSTART);
+	
+	if(cvar("sbar_flagstatus_right"))
+		pos_x = vid_conwidth - 10 - sbar_x - 64;
+	else
+		pos_x = 10 - sbar_x;
+	
+	pos_z = 0;
+
+	if(sbar_hudselector == 1)
+		pos_y = (vid_conheight - sbar_y) - cvar("sbar_flagstatus_pos") - 64;
+	else
+		pos_y = -117;
+
+	pos += sbar;
+	pos_y -= 1; //vertical margin to the picture
+
+	//Manage the progress bar if any
+	if (nb_pb_starttime > 0)
+	{
+		vector s;
+		dt = mod(time - nb_pb_starttime, nb_pb_period);
+		// one period of positive triangle
+		p = 2 * dt / nb_pb_period;
+		if (p > 1)
+			p = 2 - p;
+
+		s = NBPB_SIZE;
+		//Draw the filling
+		drawfill(pos, p * s_x * '1 0 0' + s_y * '0 1 0', NBPB_IRGB, NBPB_IALPH, NBPB_IFLAG);
+
+		//Draw the box
+		s = NBPB_SIZE;
+		drawline(NBPB_BT, pos    , pos + '1 0 0' * s_x, NBPB_BRGB, NBPB_BALPH, NBPB_BFLAG);
+		drawline(NBPB_BT, pos    , pos + '0 1 0' * s_y, NBPB_BRGB, NBPB_BALPH, NBPB_BFLAG);
+		drawline(NBPB_BT, pos + s, pos + '1 0 0' * s_x, NBPB_BRGB, NBPB_BALPH, NBPB_BFLAG);
+		drawline(NBPB_BT, pos + s, pos + '0 1 0' * s_y, NBPB_BRGB, NBPB_BALPH, NBPB_BFLAG);
+	}
+
+	pos_y += 1; //vertical margin to the picture
+	
+	if (stat_items & IT_KEY1)
+		drawpic(pos, "gfx/sb_nexball_carrying", '128 64 0', '1 1 1', 1, DRAWFLAG_NORMAL);
+}

Modified: trunk/data/qcsrc/common/constants.qh
===================================================================
--- trunk/data/qcsrc/common/constants.qh	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/qcsrc/common/constants.qh	2009-04-22 17:52:53 UTC (rev 6568)
@@ -36,6 +36,7 @@
 const float GAME_ASSAULT		= 9;
 const float GAME_ONSLAUGHT	= 10;
 const float GAME_RACE	= 11;
+const float GAME_NEXBALL = 12;
 
 const float AS_STRING		= 1;
 const float AS_INT		= 2;
@@ -248,6 +249,7 @@
 const float STAT_PRESSED_KEYS = 42;
 const float STAT_ALLOW_OLDNEXBEAM = 43; // this stat could later contain some other bits of info, like, more server-side particle config
 const float STAT_FUEL = 44;
+const float STAT_NB_METERSTART = 45;
 const float CTF_STATE_ATTACK = 1;
 const float CTF_STATE_DEFEND = 2;
 const float CTF_STATE_COMMANDER = 3;

Modified: trunk/data/qcsrc/common/mapinfo.qc
===================================================================
--- trunk/data/qcsrc/common/mapinfo.qc	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/qcsrc/common/mapinfo.qc	2009-04-22 17:52:53 UTC (rev 6568)
@@ -399,6 +399,8 @@
 					MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ASSAULT;
 				else if(v == "onslaught_generator")
 					MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ONSLAUGHT;
+				else if(v == "ball_bluegoal")
+					MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_NEXBALL;
 				else if(v == "info_player_team1")
 					++spawnpoints;
 				else if(v == "info_player_team2")
@@ -521,19 +523,20 @@
 
 float MapInfo_Type_FromString(string t)
 {
-	if     (t == "dm")    return MAPINFO_TYPE_DEATHMATCH;
-	else if(t == "tdm")   return MAPINFO_TYPE_TEAM_DEATHMATCH;
-	else if(t == "dom")   return MAPINFO_TYPE_DOMINATION;
-	else if(t == "ctf")   return MAPINFO_TYPE_CTF;
-	else if(t == "rune")  return MAPINFO_TYPE_RUNEMATCH;
-	else if(t == "lms")   return MAPINFO_TYPE_LMS;
-	else if(t == "arena") return MAPINFO_TYPE_ARENA;
-	else if(t == "kh")    return MAPINFO_TYPE_KEYHUNT;
-	else if(t == "as")    return MAPINFO_TYPE_ASSAULT;
-	else if(t == "ons")   return MAPINFO_TYPE_ONSLAUGHT;
-	else if(t == "race")  return MAPINFO_TYPE_RACE;
-	else if(t == "all")   return MAPINFO_TYPE_ALL;
-	else                  return 0;
+	if     (t == "dm")      return MAPINFO_TYPE_DEATHMATCH;
+	else if(t == "tdm")     return MAPINFO_TYPE_TEAM_DEATHMATCH;
+	else if(t == "dom")     return MAPINFO_TYPE_DOMINATION;
+	else if(t == "ctf")     return MAPINFO_TYPE_CTF;
+	else if(t == "rune")    return MAPINFO_TYPE_RUNEMATCH;
+	else if(t == "lms")     return MAPINFO_TYPE_LMS;
+	else if(t == "arena")   return MAPINFO_TYPE_ARENA;
+	else if(t == "kh")      return MAPINFO_TYPE_KEYHUNT;
+	else if(t == "as")      return MAPINFO_TYPE_ASSAULT;
+	else if(t == "ons")     return MAPINFO_TYPE_ONSLAUGHT;
+	else if(t == "race")    return MAPINFO_TYPE_RACE;
+	else if(t == "nexball") return MAPINFO_TYPE_NEXBALL;
+	else if(t == "all")     return MAPINFO_TYPE_ALL;
+	else                    return 0;
 }
 
 // load info about a map by name into the MapInfo_Map_* globals
@@ -611,6 +614,7 @@
 		if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_ASSAULT)         fputs(fh, "type as 20\n");
 		if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_RACE)            fputs(fh, "type race 5 20 15\n");
 		if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_ONSLAUGHT)       fputs(fh, "type ons 20\n");
+		if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_NEXBALL)         fputs(fh, "type nexball 5 20\n");
 
 		fh2 = fopen(strcat("scripts/", pFilename, ".arena"), FILE_READ);
 		if(fh2 >= 0)
@@ -856,7 +860,7 @@
 {
 	float req;
 	req = 0;
-	if(!(cvar("g_lms") || cvar("g_instagib") || cvar("g_minstagib") || cvar("g_nixnex") || cvar("g_rocketarena") || !cvar("g_pickup_items") || cvar("g_race")))
+	if(!(cvar("g_lms") || cvar("g_instagib") || cvar("g_minstagib") || cvar("g_nixnex") || cvar("g_rocketarena") || !cvar("g_pickup_items") || cvar("g_race") || cvar("g_nexball")))
 		req |= MAPINFO_FEATURE_WEAPONS;
 	return req;
 }
@@ -883,6 +887,8 @@
 		return MAPINFO_TYPE_ONSLAUGHT;
 	else if(cvar("g_race"))
 		return MAPINFO_TYPE_RACE;
+	else if(cvar("g_nexball"))
+		return MAPINFO_TYPE_NEXBALL;
 	else
 		return MAPINFO_TYPE_DEATHMATCH;
 }
@@ -939,6 +945,7 @@
 	cvar_set("g_assault",    (t == MAPINFO_TYPE_ASSAULT)         ? "1" : "0");
 	cvar_set("g_onslaught",  (t == MAPINFO_TYPE_ONSLAUGHT)       ? "1" : "0");
 	cvar_set("g_race",       (t == MAPINFO_TYPE_RACE)            ? "1" : "0");
+	cvar_set("g_nexball",    (t == MAPINFO_TYPE_NEXBALL)         ? "1" : "0");
 }
 
 void MapInfo_LoadMap(string s)

Modified: trunk/data/qcsrc/common/mapinfo.qh
===================================================================
--- trunk/data/qcsrc/common/mapinfo.qh	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/qcsrc/common/mapinfo.qh	2009-04-22 17:52:53 UTC (rev 6568)
@@ -9,6 +9,7 @@
 float MAPINFO_TYPE_LMS				= 256;
 float MAPINFO_TYPE_ARENA			= 512;
 float MAPINFO_TYPE_KEYHUNT			= 1024;
+float MAPINFO_TYPE_NEXBALL          = 2048;
 float MAPINFO_TYPE_ALL              = 65535; // this has to include all above bits
 
 float MAPINFO_FEATURE_WEAPONS       = 1; // not defined for minstagib-only maps

Modified: trunk/data/qcsrc/common/util.qc
===================================================================
--- trunk/data/qcsrc/common/util.qc	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/qcsrc/common/util.qc	2009-04-22 17:52:53 UTC (rev 6568)
@@ -426,6 +426,7 @@
 	else if (g == GAME_ONSLAUGHT) return "ons";
 	else if (g == GAME_ASSAULT) return "as";
 	else if (g == GAME_RACE) return "race";
+	else if (g == GAME_NEXBALL) return "nexball";
 	return "dm";
 }
 

Modified: trunk/data/qcsrc/server/cl_client.qc
===================================================================
--- trunk/data/qcsrc/server/cl_client.qc	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/qcsrc/server/cl_client.qc	2009-04-22 17:52:53 UTC (rev 6568)
@@ -489,6 +489,8 @@
 
 	if(self.flagcarried)
 		DropFlag(self.flagcarried, world, world);
+	if(self.ballcarried)
+		DropBall(self.ballcarried, self.origin + self.ballcarried.origin, self.velocity);
 
 	WaypointSprite_PlayerDead();
 	
@@ -802,6 +804,8 @@
 		self.hook_time = 0;
 		self.dmg_team = 0;
 
+		self.metertime = 0;
+
 		self.runes = 0;
 
 		self.deadflag = DEAD_NO;
@@ -908,7 +912,7 @@
 {
 	float i;
 	WriteByte(MSG_ENTITY, ENT_CLIENT_INIT);
-	WriteShort(MSG_ENTITY, CSQC_REVISION);
+	WriteByte(MSG_ENTITY, g_nexball_meter_period * 32);
 	for(i = 1; i <= 24; ++i)
 		WriteByte(MSG_ENTITY, (get_weaponinfo(i)).impulse + 1);
 	WriteCoord(MSG_ENTITY, hook_shotorigin_x);
@@ -1329,8 +1333,6 @@
 
 	if(clienttype(self) == CLIENTTYPE_REAL)
 	{
-		sprint(self, strcat("nexuiz-csqc protocol ", ftos(CSQC_REVISION), "\n"));
-
 		if(cvar("g_bugrigs"))
 			stuffcmd(self, "cl_cmd settemp chase_active 1\n");
 	}
@@ -1387,6 +1389,8 @@
 
 	if(self.flagcarried)
 		DropFlag(self.flagcarried, world, world);
+	if(self.ballcarried)
+		DropBall(self.ballcarried, self.origin + self.ballcarried.origin, self.velocity);
 
 	// Here, everything has been done that requires this player to be a client.
 
@@ -1895,6 +1899,7 @@
 	self.health = spectatee.health;
 	self.impulse = 0;
 	self.items = spectatee.items;
+	self.metertime = spectatee.metertime;
 	self.strength_finished = spectatee.strength_finished;
 	self.invincible_finished = spectatee.invincible_finished;
 	self.pressedkeys = spectatee.pressedkeys;
@@ -2160,6 +2165,7 @@
 =============
 */
 void() ctf_setstatus;
+void() nexball_setstatus;
 void PlayerPreThink (void)
 {
 	self.stat_sys_ticrate = cvar("sys_ticrate");
@@ -2400,6 +2406,7 @@
 
 		ctf_setstatus();
 		kh_setstatus();
+		nexball_setstatus();
 
 		self.dmg_team = max(0, self.dmg_team - cvar("g_teamdamage_resetspeed") * frametime);
 

Modified: trunk/data/qcsrc/server/cl_physics.qc
===================================================================
--- trunk/data/qcsrc/server/cl_physics.qc	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/qcsrc/server/cl_physics.qc	2009-04-22 17:52:53 UTC (rev 6568)
@@ -436,6 +436,11 @@
 		maxspd_mod = cvar("g_minstagib_speed_moverate");
 	}
 
+	if(g_nexball && self.ballcarried)
+	{
+		maxspd_mod = cvar("g_nexball_basketball_carrier_speed");
+	}
+
 	swampspd_mod = 1;
 	if(self.in_swamp) {
 		swampspd_mod = self.swamp_slowdown; //cvar("g_balance_swamp_moverate");

Modified: trunk/data/qcsrc/server/cl_player.qc
===================================================================
--- trunk/data/qcsrc/server/cl_player.qc	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/qcsrc/server/cl_player.qc	2009-04-22 17:52:53 UTC (rev 6568)
@@ -429,6 +429,8 @@
 			else
 				DropFlag(self.flagcarried, world, attacker);
 		}
+		if(self.ballcarried)
+			DropBall(self.ballcarried, self.origin, self.velocity);
 		Portal_ClearAllLater(self);
 		// clear waypoints
 		WaypointSprite_PlayerDead();

Modified: trunk/data/qcsrc/server/cl_weapons.qc
===================================================================
--- trunk/data/qcsrc/server/cl_weapons.qc	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/qcsrc/server/cl_weapons.qc	2009-04-22 17:52:53 UTC (rev 6568)
@@ -259,6 +259,8 @@
 		return;
 	if (g_nixnex)
 		return;
+	if (g_nexball && w == WEP_GRENADE_LAUNCHER)
+		return;
 	if (!cvar("g_pickup_items"))
 		return;
 	if(!W_IsWeaponThrowable(w))

Modified: trunk/data/qcsrc/server/clientcommands.qc
===================================================================
--- trunk/data/qcsrc/server/clientcommands.qc	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/qcsrc/server/clientcommands.qc	2009-04-22 17:52:53 UTC (rev 6568)
@@ -178,6 +178,8 @@
 		if(self.classname == "player" && cvar("sv_spectate") == 1) {
 			if(self.flagcarried)
 				DropFlag(self.flagcarried, world, world);
+			if(self.ballcarried)
+				DropBall(self.ballcarried, self.origin, self.velocity);
 			kh_Key_DropAll(self, TRUE);
 			WaypointSprite_PlayerDead();
 			self.classname = "observer";

Modified: trunk/data/qcsrc/server/defs.qh
===================================================================
--- trunk/data/qcsrc/server/defs.qh	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/qcsrc/server/defs.qh	2009-04-22 17:52:53 UTC (rev 6568)
@@ -19,7 +19,7 @@
 
 float ctf_score_value(string parameter);
 
-float g_dm, g_domination, g_ctf, g_tdm, g_keyhunt, g_onslaught, g_assault, g_arena, g_lms, g_runematch, g_race;
+float g_dm, g_domination, g_ctf, g_tdm, g_keyhunt, g_onslaught, g_assault, g_arena, g_lms, g_runematch, g_race, g_nexball;
 float g_cloaked, g_footsteps, g_jump_grunt, g_grappling_hook, g_laserguided_missile, g_midair, g_minstagib, g_nixnex, g_nixnex_with_laser, g_pinata, g_norecoil, g_vampire, g_minstagib_invis_alpha, g_bloodloss;
 float g_warmup_limit;
 float g_warmup_allguns;
@@ -342,6 +342,7 @@
 float startitem_failed;
 
 void DropFlag(entity flag, entity penalty_receiver, entity attacker);
+void DropBall(entity ball, vector org, vector vel);
 void DropAllRunes(entity pl);
 
 
@@ -537,6 +538,11 @@
 .string target4;
 .float trigger_reverse;
 
+// Nexball
+.entity ballcarried;
+.float metertime;
+float g_nexball_meter_period;
+
 void SUB_DontUseTargets();
 void SUB_UseTargets();
 

Modified: trunk/data/qcsrc/server/g_triggers.qc
===================================================================
--- trunk/data/qcsrc/server/g_triggers.qc	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/qcsrc/server/g_triggers.qc	2009-04-22 17:52:53 UTC (rev 6568)
@@ -1098,6 +1098,8 @@
 	if (other.classname != "plasma_prim")
 	if (other.classname != "plasma_chain")
 	if (other.classname != "droppedweapon")
+	if (other.classname != "ball_basketball")
+	if (other.classname != "ball_football")
 		return;
 
 	if (other.deadflag && other.iscreature)
@@ -1147,6 +1149,8 @@
 	if (other.classname != "plasma_prim")
 	if (other.classname != "plasma_chain")
 	if (other.classname != "droppedweapon")
+	if (other.classname != "ball_basketball")
+	if (other.classname != "ball_football")
 		return;
 
 	if (other.deadflag && other.iscreature)
@@ -1184,6 +1188,8 @@
 	if (other.classname != "plasma_prim")
 	if (other.classname != "plasma_chain")
 	if (other.classname != "droppedweapon")
+	if (other.classname != "ball_basketball")
+	if (other.classname != "ball_football")
 		return;
 
 	if (other.deadflag && other.iscreature)

Modified: trunk/data/qcsrc/server/g_world.qc
===================================================================
--- trunk/data/qcsrc/server/g_world.qc	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/qcsrc/server/g_world.qc	2009-04-22 17:52:53 UTC (rev 6568)
@@ -1669,7 +1669,7 @@
 		limit = -limit;
 	}
 
-	if(g_dm || g_tdm || g_arena || (g_race && !g_race_qualifying))
+	if(g_dm || g_tdm || g_arena || (g_race && !g_race_qualifying) || g_nexball)
 	// these modes always score in increments of 1, thus this makes sense
 	{
 		if(leaderfrags != WinningConditionHelper_topscore)

Modified: trunk/data/qcsrc/server/miscfunctions.qc
===================================================================
--- trunk/data/qcsrc/server/miscfunctions.qc	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/qcsrc/server/miscfunctions.qc	2009-04-22 17:52:53 UTC (rev 6568)
@@ -238,7 +238,7 @@
 	if(!self.cnt)
 		self.cnt = 1;
 
-	if(g_ctf || g_assault || g_onslaught || g_domination)
+	if(g_ctf || g_assault || g_onslaught || g_domination || g_nexball)
 	if(self.team)
 		have_team_spawns = 1;
 
@@ -892,6 +892,9 @@
 					t += (i == WEP_HOOK);
 			}
 
+			if(g_nexball && i == WEP_GRENADE_LAUNCHER)
+					t=1;
+
 			if(t)
 			{
 				start_weapons |= e.weapons;

Added: trunk/data/qcsrc/server/nexball.qc
===================================================================
--- trunk/data/qcsrc/server/nexball.qc	                        (rev 0)
+++ trunk/data/qcsrc/server/nexball.qc	2009-04-22 17:52:53 UTC (rev 6568)
@@ -0,0 +1,667 @@
+//EF_BRIGHTFIELD|EF_BRIGHTLIGHT|EF_DIMLIGHT|EF_BLUE|EF_RED|EF_FLAME
+#define BALL_EFFECTMASK 1229
+#define BALL_MINS '-16 -16 -16'  // The model is 24*24*24
+#define BALL_MAXS '16 16 16'
+#define BALL_ATTACHORG '3 0 16'
+#define BALL_FOOT   1
+#define BALL_BASKET 2
+#define GOAL_TOUCHPLAYER 1
+#define CVTOV(s) s = cvar( #s )
+
+float g_nexball_football_boost_forward;
+float g_nexball_football_boost_up;
+float g_nexball_football_physics;
+float g_nexball_delay_idle;
+float g_nexball_basketball_delay_hold;
+float g_nexball_basketball_delay_hold_forteam;
+float g_nexball_basketball_effects_default;
+float g_nexball_basketball_teamsteal;
+float balls;
+float ball_scale;
+
+.float teamtime;
+
+void nb_init() // Called early (worldspawn stage)
+{
+	CVTOV(g_nexball_meter_period); //sent with the client init entity
+	if (g_nexball_meter_period <= 0)
+		g_nexball_meter_period = 2; // avoid division by zero etc. due to silly users
+	g_nexball_meter_period = rint(g_nexball_meter_period * 32) / 32; //Round to 1/32ths to send as a byte multiplied by 32
+	addstat(STAT_NB_METERSTART, AS_FLOAT, metertime);
+
+	// General settings
+	CVTOV(g_nexball_football_boost_forward);   //100
+	CVTOV(g_nexball_football_boost_up);        //200
+	CVTOV(g_nexball_delay_idle);               //10
+	CVTOV(g_nexball_football_physics);         //0
+}
+
+void ResetBall();
+
+void LogNB(string mode, entity actor)
+{
+	string s;
+	if(!cvar("sv_eventlog"))
+		return;
+	s = strcat(":nexball:", mode);
+	if(actor != world)
+		s = strcat(s, ":", ftos(actor.playerid));
+	GameLogEcho(s);
+}
+
+void ball_restart (void)
+{
+	if(self.owner)
+		DropBall(self, self.owner.origin, '0 0 0');
+	ResetBall();
+}
+
+void nexball_setstatus (void)
+{
+	local entity oldself;
+	if (!g_nexball)
+		return;
+	if (self.ballcarried)
+	{
+		if (self.ballcarried.teamtime && (self.ballcarried.teamtime < time))
+		{
+			bprint("The ", ColoredTeamName(self.team), " held the ball for too long.\n");
+			oldself = self;
+			self = self.ballcarried;
+			DropBall(self, self.owner.origin, '0 0 0');
+			ResetBall();
+			self = oldself;
+		} else
+			self.items |= IT_KEY1;
+	}
+}
+
+void relocate_nexball (void)
+{
+	tracebox(self.origin, BALL_MINS, BALL_MAXS, self.origin, TRUE, self);
+	if (trace_startsolid)
+	{
+		vector o;
+		o = self.origin;
+		if(!move_out_of_solid(self))
+			objerror("could not get out of solid at all!");
+		print("^1NOTE: this map needs FIXING. ", self.classname, " at ", vtos(o - '0 0 1'));
+		print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
+		print(" ", ftos(self.origin_y - o_y));
+		print(" ", ftos(self.origin_z - o_z), "'\n");
+		self.origin = o;
+	}
+}
+
+void basketball_touch();
+void football_touch();
+
+void DropOwner (void)
+{
+	local entity ownr;
+	ownr = self.owner;
+	DropBall(self, ownr.origin, ownr.velocity);
+	makevectors(ownr.v_angle_y * '0 1 0');
+	ownr.velocity += ('0 0 0.75' - v_forward) * 1000;
+	ownr.flags &~= FL_ONGROUND;
+}
+
+void DropBall (entity ball, vector org, vector vel)
+{
+	ball.effects |= g_nexball_basketball_effects_default;
+	ball.effects &~= EF_NOSHADOW;
+	ball.owner.effects &~= g_nexball_basketball_effects_default; // this may be problematic.
+
+	setattachment(ball, world, "");
+	setorigin (ball, org);
+	ball.movetype = MOVETYPE_BOUNCE;
+	ball.flags &~= FL_ONGROUND;
+	ball.scale = ball_scale;
+	ball.velocity = vel;
+	ball.ctf_droptime = time;
+	ball.touch = basketball_touch;
+	ball.think = ResetBall;
+	ball.nextthink = min(time + g_nexball_delay_idle, ball.teamtime);
+
+	if (ball.owner.metertime)
+	{
+		ball.owner.metertime = 0;
+		ball.owner.weaponentity.state = WS_READY;
+	}
+
+	ball.owner.ballcarried = world;
+	ball.owner = world;
+}
+
+void InitBall (void)
+{
+	if (gameover) return;
+	self.flags &~= FL_ONGROUND;
+	self.movetype = MOVETYPE_BOUNCE;
+	if (self.classname == "ball_basketball")
+		self.touch = basketball_touch;
+	else if (self.classname == "ball_football")
+		self.touch = football_touch;
+	self.cnt = 0;
+	self.think = ResetBall;
+	self.nextthink = time + g_nexball_delay_idle + 3;
+	self.teamtime = 0;
+	self.pusher = world;
+	self.team = FALSE;
+	sound (self, CHAN_PROJECTILE, self.noise1, VOL_BASE, ATTN_NORM);
+	LogNB("init", world);
+}
+
+void ResetBall (void)
+{
+	if (self.cnt < 2) { // step 1
+		if (time == self.teamtime)
+			bprint("The ", ColoredTeamName(self.team), " held the ball for too long.\n");
+		self.touch = SUB_Null;
+		self.movetype = MOVETYPE_NOCLIP;
+		self.velocity = '0 0 0'; // just in case?
+		if(!self.cnt)
+			LogNB("resetidle", world);
+		self.cnt = 2;
+		self.nextthink = time;
+	} else if (self.cnt < 4) { // step 2 and 3
+//		dprint("Step ", ftos(self.cnt), ": Calculated velocity: ", vtos(self.spawnorigin - self.origin), ", time: ", ftos(time), "\n");
+		self.velocity = (self.spawnorigin - self.origin) * (self.cnt - 1); // 1 or 0.5 second movement
+		self.nextthink = time + 0.5;
+		self.cnt += 1;
+	} else { // step 4
+//		dprint("Step 4: time: ", ftos(time), "\n");
+		if (vlen(self.origin - self.spawnorigin) > 10) // should not happen anymore
+			dprint("The ball moved too far away from its spawn origin.\nOffset: ",
+			       vtos(self.origin - self.spawnorigin), " Velocity: ", vtos(self.velocity), "\n");
+		self.velocity = '0 0 0';
+		setorigin(self, self.spawnorigin); // make sure it's positioned correctly anyway
+		self.movetype = MOVETYPE_NONE;
+		self.think = InitBall;
+		self.nextthink = max(time, game_starttime) + cvar("g_nexball_delay_start");
+	}
+}
+
+void football_touch (void)
+{
+	if (other.solid == SOLID_BSP) {
+		if (time > self.lastground + 0.1)
+		{
+			sound (self, CHAN_PROJECTILE, self.noise, VOL_BASE, ATTN_NORM);
+			self.lastground = time;
+		}
+		if (vlen(self.velocity) && !self.cnt)
+			self.nextthink = time + g_nexball_delay_idle;
+		return;
+	}
+	if (other.classname != "player")
+		return;
+	if (other.health < 1)
+		return;
+	if (!self.cnt)
+		self.nextthink = time + g_nexball_delay_idle;
+
+	self.pusher = other;
+	self.team = other.team;
+
+	if (g_nexball_football_physics == -1) { // MrBougo try 1, before decompiling Rev's original
+		if (vlen(other.velocity))
+			self.velocity = other.velocity * 1.5 + '0 0 1' * g_nexball_football_boost_up;
+	} else if (g_nexball_football_physics == 1) { // MrBougo's modded Rev style: partially independant of the height of the aiming point
+		makevectors(other.v_angle);
+		self.velocity = other.velocity + v_forward * g_nexball_football_boost_forward + '0 0 1' * g_nexball_football_boost_up;
+	} else if (g_nexball_football_physics == 2) { // 2nd mod try: totally independant. Really playable!
+		makevectors(other.v_angle_y * '0 1 0');
+		self.velocity = other.velocity + v_forward * g_nexball_football_boost_forward + v_up * g_nexball_football_boost_up;
+	} else { // Revenant's original style (from the original mod's disassembly, acknowledged by Revenant)
+		makevectors(other.v_angle);
+		self.velocity = other.velocity + v_forward * g_nexball_football_boost_forward + v_up * g_nexball_football_boost_up;
+	}
+	self.avelocity = -250 * v_forward;  // maybe there is a way to make it look better?
+}
+
+void basketball_touch (void)
+{
+	if (other.ballcarried)
+	{
+		football_touch();
+		return;
+	}
+	if (!self.cnt && other.classname == "player" && (other.playerid != self.dropperid || time > self.ctf_droptime + cvar("g_nexball_delay_collect"))) {
+		if (other.health <= 0)
+			return;
+		LogNB("caught", other);
+
+		if (self.team != other.team)
+			self.teamtime = time + g_nexball_basketball_delay_hold_forteam;
+
+		self.owner = self.pusher = other;
+		self.team = other.team;
+		other.ballcarried = self;
+		other.effects = other.effects | g_nexball_basketball_effects_default;
+		self.effects &~= g_nexball_basketball_effects_default;
+		self.effects |= EF_NOSHADOW;
+		self.scale = 1; // scale down.
+		self.dropperid = other.playerid;
+		setattachment(self, other, "");
+		setorigin(self, BALL_ATTACHORG);
+		self.velocity = '0 0 0';
+		self.movetype = MOVETYPE_NONE;
+		self.touch = SUB_Null;
+		if (g_nexball_basketball_delay_hold)
+		{
+			self.think = DropOwner;
+			self.nextthink = time + g_nexball_basketball_delay_hold;
+		}
+	} else if (other.solid == SOLID_BSP) {
+		sound (self, CHAN_PROJECTILE, self.noise, VOL_BASE, ATTN_NORM);
+		if (vlen(self.velocity) && !self.cnt)
+			self.nextthink = min(time + g_nexball_delay_idle, self.teamtime);
+	}
+}
+
+void ReturnTouch (void)
+{
+	string othername;
+	entity tempother;
+	tempother = other;
+
+	if (gameover) return;
+	if ((self.spawnflags & GOAL_TOUCHPLAYER) && tempother.ballcarried)
+		tempother = tempother.ballcarried; // Can't drop here yet because of EXACTTRIGGER
+	if (tempother.classname != "ball_basketball")
+	if (tempother.classname != "ball_football")
+		return;
+	if (tempother.cnt)
+		return;
+	EXACTTRIGGER_TOUCH;
+	other = tempother;
+
+	if (other.owner)
+	{
+		othername = other.owner.netname;
+		DropBall(other, other.owner.origin, '0 0 0');
+	}
+	else
+		othername = "The ball";
+
+	LogNB("out", other.pusher);
+	if (self.spawnflags & GOAL_TOUCHPLAYER)
+		bprint(othername, "^7 went out of bounds.\n");
+	else
+		bprint("The ball was returned.\n");
+	sound (other, CHAN_AUTO, self.noise, VOL_BASE, ATTN_NONE);
+
+	other.cnt = 1;
+	other.think = ResetBall;
+	if (other.classname == "ball_basketball")
+		other.touch = football_touch;
+	other.nextthink = time + 1.5;
+}
+
+void GoalTouch (void)
+{
+	string othername;
+	entity tempother;
+	float isclient;
+	tempother = other; // EXACTTRIGGER uses "other"
+
+	if (gameover) return;
+	if ((self.spawnflags & GOAL_TOUCHPLAYER) && tempother.ballcarried)
+		tempother = tempother.ballcarried; // Can't drop here yet because of EXACTTRIGGER
+	if (tempother.classname != "ball_basketball")
+	if (tempother.classname != "ball_football")
+		return;
+	if (!tempother.pusher || tempother.cnt)
+		return;
+	EXACTTRIGGER_TOUCH;
+	other = tempother;
+
+	isclient = other.pusher.flags & FL_CLIENT;
+
+	if (isclient)
+		othername = other.pusher.netname;
+	else
+		othername = "Someone (?)";
+
+	if (self.classname == "ball_fault") { // FIXME?: this is ugly
+		     if (other.team == COLOR_TEAM1) self.team = COLOR_TEAM2;
+		else if (other.team == COLOR_TEAM2) self.team = COLOR_TEAM1;
+		else                                self.team = FALSE;
+
+		if (isclient)
+			PlayerScore_Add(other.pusher, SP_NEXBALL_OWNGOALS, 1);
+		if (self.team)
+		{
+			bprint(ColoredTeamName(self.team), " gets awarded 1 point for ", othername, "^7's silliness.\n");
+			LogNB("fault-score", other.pusher);
+		}
+		else
+		{
+			bprint("The ball was reset.\n");
+			LogNB("fault-reset", other.pusher);
+		}
+	} else if (other.team == self.team) { // the goal's .team is set to the team it gives points to
+		if (isclient)
+			PlayerScore_Add(other.pusher, SP_NEXBALL_GOALS, 1);
+		bprint("Goaaaaal! ", othername, "^7 scored a point for the ", ColoredTeamName(self.team), ".\n");
+		LogNB("goal", other.pusher);
+	} else if (other.team) {
+		if (isclient)
+			PlayerScore_Add(other.pusher, SP_NEXBALL_OWNGOALS, 1);
+		bprint("Boo! ", othername, "^7 scored a point for the opposing ", ColoredTeamName(self.team), ".\n");
+		LogNB("owngoal", other.pusher);
+	} else {
+		bprint(othername, "^7 scored a point for the ", ColoredTeamName(self.team), ".\n"); //Should not happen
+		LogNB("goal-noteam", other.pusher);
+	}
+	sound (other, CHAN_AUTO, self.noise, VOL_BASE, ATTN_NONE);
+
+	if (self.team)
+		TeamScore_AddToTeam(self.team, ST_NEXBALL_GOALS, 1);
+
+	if (other.owner) // Happens on spawnflag GOAL_TOUCHPLAYER
+		DropBall(other, other.owner.origin, other.owner.velocity);
+	other.cnt = 1;
+	other.think = ResetBall;
+	if (other.classname == "ball_basketball")
+		other.touch = football_touch; // better than SUB_Null: football control until the ball gets reset
+	other.nextthink = time + cvar("g_nexball_delay_goal");
+}
+
+//=======================//
+//      spawnfuncs       //
+//=======================//
+
+void SpawnBall (void)
+{
+	if(!g_nexball) { remove(self); return; }
+
+	if (!self.model) {
+		self.model = "models/nexball/ball.md3";
+		self.scale = 1.3;
+	}
+
+	precache_model (self.model);
+	setmodel (self, self.model);
+	setsize (self, BALL_MINS, BALL_MAXS);
+	ball_scale = self.scale;
+
+	relocate_nexball();
+	self.spawnorigin = self.origin;
+
+	self.effects = self.effects | EF_LOWPRECISION;
+
+	if (cvar(strcat("g_nex", self.classname, "_trail")))
+	{
+		self.glow_color = cvar("g_nexball_trail_color");
+		self.glow_trail = TRUE;
+	}
+
+	self.movetype = MOVETYPE_FLY;
+
+	if (!cvar("g_nexball_sound_bounce"))
+		self.noise = "";
+	else if (!self.noise)
+		self.noise = "sound/nexball/bounce.ogg";
+		//bounce sound placeholder (FIXME)
+	if (!self.noise1)
+		self.noise1 = "sound/nexball/drop.ogg";
+		//ball drop sound placeholder (FIXME)
+	if (!self.noise2)
+		self.noise2 = "sound/nexball/steal.ogg";
+		//stealing sound placeholder (FIXME)
+	if (self.noise) precache_sound (self.noise);
+	precache_sound (self.noise1);
+	precache_sound (self.noise2);
+
+	self.reset = ball_restart;
+	self.think = InitBall;
+	self.nextthink = game_starttime + cvar("g_nexball_delay_start");
+}
+
+void spawnfunc_ball_basketball (void)
+{
+	if(!balls & BALL_BASKET)
+	{
+		CVTOV(g_nexball_basketball_effects_default);
+		CVTOV(g_nexball_basketball_delay_hold);
+		CVTOV(g_nexball_basketball_delay_hold_forteam);
+		CVTOV(g_nexball_basketball_teamsteal);
+		g_nexball_basketball_effects_default = g_nexball_basketball_effects_default & BALL_EFFECTMASK;
+	}
+	if (!self.effects)
+		self.effects = g_nexball_basketball_effects_default;
+	self.solid = SOLID_TRIGGER;
+
+	balls |= BALL_BASKET;
+
+	SpawnBall();
+}
+
+void spawnfunc_ball_football (void)
+{
+	self.solid = SOLID_TRIGGER;
+
+	balls |= BALL_FOOT;
+
+	SpawnBall();
+}
+
+void spawnfunc_ball (void)
+{
+	self.classname = "ball_football";
+	spawnfunc_ball_football();
+}
+
+// The "red goal" is defended by blue team. A ball in there counts as a point for red.
+void spawnfunc_ball_redgoal (void)
+{
+	if(!g_nexball) { remove(self); return; }
+	//COLOR_TEAM1 is red
+	EXACTTRIGGER_INIT;
+	self.team = COLOR_TEAM1;
+	if (!self.noise)
+		self.noise = "ctf/redcapture.wav";
+	precache_sound(self.noise);
+	self.touch = GoalTouch;
+}
+
+//Same comment.
+void spawnfunc_ball_bluegoal (void)
+{
+	if(!g_nexball) { remove(self); return; }
+	EXACTTRIGGER_INIT;
+	self.team = COLOR_TEAM2;
+	if (!self.noise)
+		self.noise = "ctf/bluecapture.wav";
+	precache_sound(self.noise);
+	self.touch = GoalTouch;
+}
+
+void spawnfunc_ball_fault (void)
+{
+	if(!g_nexball) { remove(self); return; }
+	EXACTTRIGGER_INIT;
+	if (!self.noise)
+		self.noise = "misc/typehit.wav";
+	precache_sound(self.noise);
+	self.touch = GoalTouch;
+}
+
+void spawnfunc_ball_bound (void)
+{
+	if(!g_nexball) { remove(self); return; }
+	EXACTTRIGGER_INIT;
+	if (!self.noise)
+		self.noise = "misc/typehit.wav";
+	precache_sound(self.noise);
+	self.touch = ReturnTouch;
+}
+
+
+//=======================//
+//      Weapon code      //
+//=======================//
+
+void W_Nexball_Touch (void)
+{
+	local entity ball, attacker;
+	attacker = self.owner;
+
+	PROJECTILE_TOUCH;
+	if(attacker.team != other.team || g_nexball_basketball_teamsteal)
+	if((ball = other.ballcarried))
+	{
+		other.velocity = other.velocity + normalize(self.velocity) * other.damageforcescale * cvar("g_balance_nexball_secondary_force");
+		other.flags &~= FL_ONGROUND;
+		if(!attacker.ballcarried)
+		{
+			other.effects &~= g_nexball_basketball_effects_default;
+			other.ballcarried = world;
+
+			if (g_nexball_basketball_delay_hold)
+			{
+				ball.think = DropOwner;
+				ball.nextthink = time + g_nexball_basketball_delay_hold;
+			}
+
+			attacker.effects = attacker.effects | g_nexball_basketball_effects_default;
+			attacker.ballcarried = ball;
+			if (other.metertime)
+			{
+				other.metertime = 0;
+				other.weaponentity.state = WS_READY;
+			}
+
+			setattachment(ball, attacker, "");
+			ball.owner = ball.pusher = attacker;
+			ball.team = attacker.team;
+			ball.dropperid = attacker.playerid;
+
+			if (other.team != attacker.team)
+				ball.teamtime = time + g_nexball_basketball_delay_hold_forteam;
+
+			LogNB("stole", attacker);
+			sound (other, CHAN_AUTO, ball.noise2, VOL_BASE, ATTN_NORM);
+		}
+	}
+	remove(self);
+}
+
+void W_Nexball_Attack (float t)
+{
+	local entity ball;
+	local float mul, mi, ma;
+	if (!(ball = self.ballcarried))
+		return;
+
+	W_SetupShot (self, FALSE, 4, "weapons/grenade_fire.wav");
+	tracebox(w_shotorg, BALL_MINS, BALL_MAXS, w_shotorg, MOVE_WORLDONLY, world);
+	if(trace_startsolid)
+	{
+		if(self.metertime)
+			self.metertime = 0; // Shot failed, hide the power meter
+		return;
+	}
+
+	//Calculate multiplier
+	if (t < 0)
+		mul = 1;
+	else
+	{
+		mi = cvar("g_nexball_basketball_meter_minpower");
+		ma = max(mi, cvar("g_nexball_basketball_meter_maxpower")); // avoid confusion
+		//One triangle wave period with 1 as max
+		mul = 2 * mod(t, g_nexball_meter_period) / g_nexball_meter_period;
+		if (mul > 1)
+			mul = 2 - mul;
+		mul = mi + (ma - mi) * mul; // range from the minimal power to the maximal power
+	}
+	DropBall (ball, w_shotorg, W_CalculateProjectileVelocity(self.velocity, w_shotdir * cvar("g_balance_nexball_primary_speed") * mul));
+	//TODO: use the speed_up cvar too ??
+}
+
+void W_Nexball_Attack2 (void)
+{
+	local entity missile;
+	if (!(balls & BALL_BASKET))
+		return;
+	W_SetupShot (self, FALSE, 2, "weapons/grenade_fire.wav");
+	pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+	missile = spawn ();
+
+	missile.owner = self;
+	missile.classname = "ballstealer";
+
+	missile.movetype = MOVETYPE_FLY;
+	missile.solid = SOLID_BBOX;
+
+	setmodel (missile, "models/elaser.mdl"); // precision set below
+	setsize (missile, '0 0 0', '0 0 0');
+	setorigin (missile, w_shotorg);
+
+	missile.velocity = w_shotdir * cvar("g_balance_nexball_secondary_speed");
+	W_SetupProjectileVelocity(missile);
+	missile.angles = vectoangles (missile.velocity);
+	missile.touch = W_Nexball_Touch;
+	missile.think = SUB_Remove;
+	missile.nextthink = time + cvar("g_balance_nexball_secondary_lifetime"); //FIXME: use a distance instead?
+
+	missile.effects = EF_BRIGHTFIELD | EF_LOWPRECISION;
+	missile.flags = FL_PROJECTILE;
+}
+
+float w_nexball_weapon(float req)
+{
+	if (req == WR_THINK)
+	{
+		if (self.BUTTON_ATCK)
+		if (weapon_prepareattack(0, cvar("g_balance_nexball_primary_refire")))
+		if (cvar("g_nexball_basketball_meter"))
+		{
+			if (self.ballcarried && !self.metertime)
+				self.metertime = time;
+			else
+				weapon_thinkf(WFRAME_FIRE1, cvar("g_balance_nexball_primary_animtime"), w_ready);
+		}
+		else
+		{
+			W_Nexball_Attack(-1);
+			weapon_thinkf(WFRAME_FIRE1, cvar("g_balance_nexball_primary_animtime"), w_ready);
+		}
+		if (self.BUTTON_ATCK2)
+		if (weapon_prepareattack(1, cvar("g_balance_nexball_secondary_refire")))
+		{
+			W_Nexball_Attack2();
+			weapon_thinkf(WFRAME_FIRE2, cvar("g_balance_nexball_secondary_animtime"), w_ready);
+		}
+		
+		if (!self.BUTTON_ATCK && self.metertime && self.ballcarried)
+		{
+			W_Nexball_Attack(time - self.metertime);
+			// DropBall or stealing will set metertime back to 0
+			weapon_thinkf(WFRAME_FIRE1, cvar("g_balance_nexball_primary_animtime"), w_ready);
+		}
+	}
+	else if (req == WR_PRECACHE)
+	{
+		precache_model ("models/weapons/g_gl.md3");
+		precache_model ("models/weapons/v_gl.md3");
+		precache_model ("models/weapons/h_gl.dpm");
+		precache_model ("models/elaser.mdl");
+		precache_sound ("weapons/grenade_fire.wav");
+	}
+	else if (req == WR_SETUP)
+		weapon_setup(WEP_GRENADE_LAUNCHER);
+	else if (req == WR_SUICIDEMESSAGE)
+	{
+		w_deathtypestring = "is a weirdo";
+	}
+	else if (req == WR_KILLMESSAGE)
+	{
+		w_deathtypestring = "got killed by #'s black magic";
+	}
+	// No need to check WR_CHECKAMMO* or WR_AIM, it should always return TRUE
+	return TRUE;
+}

Modified: trunk/data/qcsrc/server/progs.src
===================================================================
--- trunk/data/qcsrc/server/progs.src	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/qcsrc/server/progs.src	2009-04-22 17:52:53 UTC (rev 6568)
@@ -120,6 +120,7 @@
 ctf.qc
 domination.qc
 mode_onslaught.qc
+nexball.qc
 g_hook.qc
 
 t_swamp.qc

Modified: trunk/data/qcsrc/server/scores_rules.qc
===================================================================
--- trunk/data/qcsrc/server/scores_rules.qc	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/qcsrc/server/scores_rules.qc	2009-04-22 17:52:53 UTC (rev 6568)
@@ -172,3 +172,16 @@
 	ScoreInfo_SetLabel_PlayerScore(SP_ASSAULT_OBJECTIVES,    "objectives",      SFL_SORT_PRIO_PRIMARY);
 	ScoreRules_basics_end();
 }
+
+// Nexball stuff
+#define ST_NEXBALL_GOALS 1
+#define SP_NEXBALL_GOALS 4
+#define SP_NEXBALL_OWNGOALS 5
+void ScoreRules_nexball()
+{
+	ScoreRules_basics(2, 0, 0, TRUE);
+	ScoreInfo_SetLabel_TeamScore  (   ST_NEXBALL_GOALS,    "goals", SFL_SORT_PRIO_PRIMARY);
+	ScoreInfo_SetLabel_PlayerScore(   SP_NEXBALL_GOALS,    "goals", SFL_SORT_PRIO_PRIMARY);
+	ScoreInfo_SetLabel_PlayerScore(SP_NEXBALL_OWNGOALS, "owngoals", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER);
+	ScoreRules_basics_end();
+}

Modified: trunk/data/qcsrc/server/teamplay.qc
===================================================================
--- trunk/data/qcsrc/server/teamplay.qc	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/qcsrc/server/teamplay.qc	2009-04-22 17:52:53 UTC (rev 6568)
@@ -69,6 +69,7 @@
 void ctf_init();
 void runematch_init();
 void tdm_init();
+void nb_init();
 void entcs_init();
 
 void LogTeamchange(entity pl)
@@ -119,6 +120,7 @@
 		found += (g_assault = (!found && (prev != GAME_ASSAULT) && cvar("g_assault")));
 		found += (g_onslaught = (!found && (prev != GAME_ONSLAUGHT) && cvar("g_onslaught")));
 		found += (g_race = (!found && (prev != GAME_RACE) && cvar("g_race")));
+		found += (g_nexball = (!found && (prev != GAME_NEXBALL) && cvar("g_nexball")));
 
 		if(found)
 			break;
@@ -322,6 +324,16 @@
 		ScoreRules_race();
 	}
 
+	if(g_nexball)
+	{
+		game = GAME_NEXBALL;
+		gamemode_name = "Nexball";
+		fraglimit_override = cvar("g_nexball_goallimit");
+		nb_init();
+		ActivateTeamplay();
+		ScoreRules_nexball();
+	}
+
 	if(teams_matter)
 		entcs_init();
 
@@ -560,9 +572,9 @@
 		teament_name = "ctf_team";
 	else if(g_tdm)
 		teament_name = "tdm_team";
-	else if(g_assault)
+	else if(g_assault || g_nexball)
 	{
-		c1 = c2 = 0; // Assault always has 2 teams
+		c1 = c2 = 0; // Assault and nexball always have 2 teams
 		return;
 	}
 	else

Modified: trunk/data/qcsrc/server/w_grenadelauncher.qc
===================================================================
--- trunk/data/qcsrc/server/w_grenadelauncher.qc	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/qcsrc/server/w_grenadelauncher.qc	2009-04-22 17:52:53 UTC (rev 6568)
@@ -146,8 +146,10 @@
 }
 
 .float bot_secondary_grenademooth;
+float w_nexball_weapon(float req);
 float w_glauncher(float req)
 {
+	if (g_nexball) { return w_nexball_weapon(req); }
 	if (req == WR_AIM)
 	{
 		self.BUTTON_ATCK = FALSE;

Modified: trunk/data/scripts/entities.def
===================================================================
--- trunk/data/scripts/entities.def	2009-04-22 17:52:42 UTC (rev 6567)
+++ trunk/data/scripts/entities.def	2009-04-22 17:52:53 UTC (rev 6568)
@@ -1550,6 +1550,57 @@
 targetname: name that identifies this entity so it can be triggered
 */
 
+/*QUAKED ball_redgoal (1 0 0) ?
+"Red" goal. Defended by blue team, the red team tries to put the ball in there.
+-------- KEYS --------
+noise: sound played when a point is scored
+-------- SPAWNFLAGS --------
+GOAL_TOUCHPLAYER: The trigger also affects ball-carrying players (the ball is then auto-dropped)
+*/
+
+/*QUAKED ball_bluegoal (0 0 1) ?
+"Blue" goal. Defended by red team, the blue team tries to put the ball in there.
+-------- KEYS --------
+noise: sound played when a point is scored
+-------- SPAWNFLAGS --------
+GOAL_TOUCHPLAYER: The trigger also affects ball-carrying players (the ball is then auto-dropped)
+*/
+
+/*QUAKED ball_fault (0.6 0.1 0) ?
+This acts as a goal that always gives points to the opposing team.
+-------- KEYS --------
+noise: sound played when a point is scored
+-------- SPAWNFLAGS --------
+GOAL_TOUCHPLAYER: The trigger also affects ball-carrying players (the ball is then auto-dropped)
+*/
+
+/*QUAKED ball_bound (0.1 0.6 0) ?
+When the ball touches this, it is returned.
+-------- KEYS --------
+noise: sound played when a point is scored
+-------- SPAWNFLAGS --------
+GOAL_TOUCHPLAYER: The trigger also affects ball-carrying players (the ball is then auto-dropped)
+*/
+
+/*QUAKED ball_football (.9 .9 .9) (-16 -16 -16) (16 16 16)
+The soccer type Nexball.
+-------- KEYS --------
+model: set this if you want to use your own model
+scale: if you're using your own model, change this to scale it to 32*32*32
+noise: played when the ball bounces
+noise1: played when the ball is dropped on the map
+*/
+
+/*QUAKED ball_basketball (.5 .2 0) (-16 -16 -16) (16 16 16)
+The basket ball type Nexball.
+-------- KEYS --------
+model: set this if you want to use your own model
+scale: if you're using your own model, change this to scale it to 32*32*32
+noise: played when the ball bounces
+noise1: played when the ball is dropped on the map
+noise2: played when the ball is stolen from the enemy
+*/
+
 /*QUAKED item_fuel (.3 .3 1) (-30 -30 0) (30 30 32) FLOATING
 Jetpack fuel
 -------- KEYS --------

Added: trunk/data/sound/nexball/bounce.ogg
===================================================================
(Binary files differ)


Property changes on: trunk/data/sound/nexball/bounce.ogg
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/sound/nexball/drop.ogg
===================================================================
(Binary files differ)


Property changes on: trunk/data/sound/nexball/drop.ogg
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/sound/nexball/steal.ogg
===================================================================
(Binary files differ)


Property changes on: trunk/data/sound/nexball/steal.ogg
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/textures/nexball/ball.tga
===================================================================
(Binary files differ)


Property changes on: trunk/data/textures/nexball/ball.tga
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/textures/nexball/ball_gloss.tga
===================================================================
(Binary files differ)


Property changes on: trunk/data/textures/nexball/ball_gloss.tga
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/data/textures/nexball/ball_norm.tga
===================================================================
(Binary files differ)


Property changes on: trunk/data/textures/nexball/ball_norm.tga
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/misc/mediasource/graphics/sb-nexball-carrying.svg
===================================================================
--- trunk/misc/mediasource/graphics/sb-nexball-carrying.svg	                        (rev 0)
+++ trunk/misc/mediasource/graphics/sb-nexball-carrying.svg	2009-04-22 17:52:53 UTC (rev 6568)
@@ -0,0 +1,242 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="160"
+   height="160"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   sodipodi:docname="nexball.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0"
+   inkscape:export-filename="/tmp/nexball.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient3174">
+      <stop
+         id="stop3176"
+         offset="0"
+         style="stop-color:#dddddd;stop-opacity:1;" />
+      <stop
+         id="stop3178"
+         offset="1"
+         style="stop-color:#7d7d7d;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3192">
+      <stop
+         style="stop-color:#ffe800;stop-opacity:1;"
+         offset="0"
+         id="stop3194" />
+      <stop
+         style="stop-color:#eb0303;stop-opacity:1;"
+         offset="1"
+         id="stop3196" />
+    </linearGradient>
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3192"
+       id="radialGradient3204"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.9636104,-0.3788912,0.1609347,0.8340468,-417.33709,205.85007)"
+       cx="350.32599"
+       cy="477.15244"
+       fx="350.32599"
+       fy="477.15244"
+       r="72.248924" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3174"
+       id="radialGradient3304"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0759078,-0.9724596,1.3927158,1.5408724,-719.39671,95.262834)"
+       cx="380.36703"
+       cy="504.13748"
+       fx="380.36703"
+       fy="504.13748"
+       r="49.857143" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3192"
+       id="radialGradient3309"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.9636104,-0.3788912,0.1609347,0.8340468,-458.56885,233.20173)"
+       cx="350.32599"
+       cy="477.15244"
+       fx="350.32599"
+       fy="477.15244"
+       r="72.248924" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.979899"
+     inkscape:cx="173.86289"
+     inkscape:cy="95.140549"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1440"
+     inkscape:window-height="825"
+     inkscape:window-x="0"
+     inkscape:window-y="26"
+     showguides="true"
+     inkscape:guide-bbox="true">
+    <sodipodi:guide
+       orientation="0,1"
+       position="12.142857,609.28571"
+       id="guide2394" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <cc:license
+           rdf:resource="http://www.gnu.org/licenses/gpl-2.0.txt" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="Rings"
+     style="display:inline"
+     transform="translate(-253.89393,-446.78742)">
+    <path
+       sodipodi:type="arc"
+       style="opacity:0.2;fill:#ffffff;fill-opacity:0.69019607999999999;fill-rule:nonzero;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+       id="path3186"
+       sodipodi:cx="363.57144"
+       sodipodi:cy="473.07648"
+       sodipodi:rx="47.857143"
+       sodipodi:ry="47.857143"
+       d="M 411.42859,473.07648 A 47.857143,47.857143 0 1 1 315.7143,473.07648 A 47.857143,47.857143 0 1 1 411.42859,473.07648 z"
+       transform="matrix(0.742807,0,0,0.742807,34.45497,182.70599)"
+       inkscape:export-filename="/tmp/path2389.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90" />
+    <path
+       sodipodi:type="arc"
+       style="opacity:0.3;fill:#ffffff;fill-opacity:0.69019607999999999;fill-rule:nonzero;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+       id="path3212"
+       sodipodi:cx="363.57144"
+       sodipodi:cy="473.07648"
+       sodipodi:rx="47.857143"
+       sodipodi:ry="47.857143"
+       d="M 411.42859,473.07648 A 47.857143,47.857143 0 1 1 315.7143,473.07648 A 47.857143,47.857143 0 1 1 411.42859,473.07648 z"
+       transform="matrix(0.8001136,0,0,0.8001136,24.50879,156.45428)"
+       inkscape:export-filename="/tmp/path2389.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90" />
+    <path
+       sodipodi:type="arc"
+       style="opacity:0.4;fill:#ffffff;fill-opacity:0.69019607999999999;fill-rule:nonzero;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+       id="path3210"
+       sodipodi:cx="363.57144"
+       sodipodi:cy="473.07648"
+       sodipodi:rx="47.857143"
+       sodipodi:ry="47.857143"
+       d="M 411.42859,473.07648 A 47.857143,47.857143 0 1 1 315.7143,473.07648 A 47.857143,47.857143 0 1 1 411.42859,473.07648 z"
+       transform="matrix(0.8717469,0,0,0.8717469,11.54061,125.69741)"
+       inkscape:export-filename="/tmp/path2389.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90" />
+    <path
+       sodipodi:type="arc"
+       style="opacity:0.5;fill:#ffffff;fill-opacity:0.69019607999999999;fill-rule:nonzero;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+       id="path3208"
+       sodipodi:cx="363.57144"
+       sodipodi:cy="473.07648"
+       sodipodi:rx="47.857143"
+       sodipodi:ry="47.857143"
+       d="M 411.42859,473.07648 A 47.857143,47.857143 0 1 1 315.7143,473.07648 A 47.857143,47.857143 0 1 1 411.42859,473.07648 z"
+       transform="matrix(0.9572736,0,0,0.9572736,-4.50125,90.78161)"
+       inkscape:export-filename="/tmp/path2389.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90" />
+    <path
+       sodipodi:type="arc"
+       style="opacity:0.6;fill:#ffffff;fill-opacity:0.69019607999999999;fill-rule:nonzero;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+       id="path3206"
+       sodipodi:cx="363.57144"
+       sodipodi:cy="473.07648"
+       sodipodi:rx="47.857143"
+       sodipodi:ry="47.857143"
+       d="M 411.42859,473.07648 A 47.857143,47.857143 0 1 1 315.7143,473.07648 A 47.857143,47.857143 0 1 1 411.42859,473.07648 z"
+       transform="translate(-7.43076,76.76486)"
+       inkscape:export-filename="/tmp/path2389.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer3"
+     inkscape:label="Fire"
+     style="display:inline"
+     transform="translate(-253.89393,-446.78742)">
+    <path
+       style="fill:url(#radialGradient3309);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline"
+       d="M 400.04641,525.80689 C 400.01034,525.90309 404.5631,486.15158 379.33829,466.96551 C 354.96964,448.43064 352.31671,449.28784 352.31671,449.28784 C 352.31671,449.28784 376.05529,482.1178 365.19615,493.73455 C 354.33701,505.35131 350.80148,498.78531 350.80148,498.78531 L 312.41568,453.83352 C 312.41568,453.83352 343.47788,507.87669 323.7799,515.95791 C 304.08193,524.03913 263.17075,483.12795 263.17075,483.12795 C 263.17075,483.12795 280.34334,505.60384 282.36364,508.38176 C 284.38395,511.15968 314.94107,549.79801 287.66695,542.22187 C 260.39283,534.64572 255.84714,535.1508 255.84714,535.1508 C 255.84714,535.1508 310.39538,564.19269 317.71899,573.78914 C 325.04259,583.38559 401.05656,525.30182 400.04641,525.80689 z"
+       id="path3190"
+       sodipodi:nodetypes="cscsccscsscsc"
+       inkscape:export-filename="/tmp/path2389.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90" />
+    <path
+       style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+       d="M 400.75444,526.06094 C 400.71837,526.15714 405.27113,486.40563 380.04632,467.21956 C 355.67767,448.68469 353.02474,449.54189 353.02474,449.54189 C 353.02474,449.54189 376.76332,482.37185 365.90418,493.9886 C 355.04504,505.60536 351.50951,499.03936 351.50951,499.03936 L 313.12371,454.08757 C 313.12371,454.08757 344.18591,508.13074 324.48793,516.21196 C 304.78996,524.29318 263.87878,483.382 263.87878,483.382 C 263.87878,483.382 281.05137,505.85789 283.07167,508.63581 C 285.09198,511.41373 315.6491,550.05206 288.37498,542.47592 C 261.10086,534.89977 256.55517,535.40485 256.55517,535.40485 C 256.55517,535.40485 311.10341,564.44674 318.42702,574.04319 C 325.75062,583.63964 401.76459,525.55587 400.75444,526.06094 z"
+       id="path3214"
+       sodipodi:nodetypes="cscsccscsscsc"
+       inkscape:export-filename="/tmp/path2389.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90" />
+  </g>
+  <g
+     inkscape:label="Ball"
+     inkscape:groupmode="layer"
+     id="layer1"
+     style="display:inline"
+     transform="translate(-253.89393,-446.78742)">
+    <path
+       sodipodi:type="arc"
+       style="fill:url(#radialGradient3304);fill-opacity:1;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+       id="path2389"
+       sodipodi:cx="363.57144"
+       sodipodi:cy="473.07648"
+       sodipodi:rx="47.857143"
+       sodipodi:ry="47.857143"
+       d="M 411.42859,473.07648 A 47.857143,47.857143 0 1 1 315.7143,473.07648 A 47.857143,47.857143 0 1 1 411.42859,473.07648 z"
+       transform="translate(-1.1958991,82.105473)"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90" />
+  </g>
+</svg>



More information about the nexuiz-commits mailing list