r4365 - in branches/nexuiz-2.0: . Docs Docs/server data data/gfx data/maps data/models data/models/weapons data/qcsrc/client data/qcsrc/common data/qcsrc/menu/nexuiz data/qcsrc/server data/scripts data/textures

DONOTREPLY at icculus.org DONOTREPLY at icculus.org
Fri Sep 5 15:40:38 EDT 2008


Author: div0
Date: 2008-09-05 15:40:34 -0400 (Fri, 05 Sep 2008)
New Revision: 4365

Added:
   branches/nexuiz-2.0/data/gfx/inv_weapon10.tga
   branches/nexuiz-2.0/data/gfx/inv_weapon9.tga
   branches/nexuiz-2.0/data/models/portal.md3
   branches/nexuiz-2.0/data/models/weapons/g_minstanex.md3
   branches/nexuiz-2.0/data/models/weapons/g_porto.md3
   branches/nexuiz-2.0/data/models/weapons/v_minstanex.md3
   branches/nexuiz-2.0/data/models/weapons/v_porto.md3
   branches/nexuiz-2.0/data/models/weapons/w_minstanex.zym
   branches/nexuiz-2.0/data/models/weapons/w_porto.zym
   branches/nexuiz-2.0/data/qcsrc/server/portals.qc
   branches/nexuiz-2.0/data/qcsrc/server/portals.qh
   branches/nexuiz-2.0/data/qcsrc/server/w_minstanex.qc
   branches/nexuiz-2.0/data/qcsrc/server/w_porto.qc
   branches/nexuiz-2.0/data/textures/nex.tga
   branches/nexuiz-2.0/data/textures/nex_bump.tga
   branches/nexuiz-2.0/data/textures/nex_gloss.tga
   branches/nexuiz-2.0/data/textures/nex_glow.tga
   branches/nexuiz-2.0/data/textures/nex_pants.tga
   branches/nexuiz-2.0/data/textures/nex_shirt.tga
   branches/nexuiz-2.0/data/textures/portalgun.tga
   branches/nexuiz-2.0/data/textures/portalgun_gloss.tga
   branches/nexuiz-2.0/data/textures/portalgun_glow.tga
   branches/nexuiz-2.0/data/textures/portalgun_norm.tga
Modified:
   branches/nexuiz-2.0/.patchsets
   branches/nexuiz-2.0/Docs/FAQ.aft
   branches/nexuiz-2.0/Docs/FAQ.html
   branches/nexuiz-2.0/Docs/server/server.cfg
   branches/nexuiz-2.0/changes-since-last-release
   branches/nexuiz-2.0/data/defaultNexuiz.cfg
   branches/nexuiz-2.0/data/keybinds.txt
   branches/nexuiz-2.0/data/maps/dieselpower.bsp
   branches/nexuiz-2.0/data/maps/dieselpower.map
   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/main.qh
   branches/nexuiz-2.0/data/qcsrc/client/sbar.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/dialog_multiplayer_create_mutators.c
   branches/nexuiz-2.0/data/qcsrc/server/builtins.qh
   branches/nexuiz-2.0/data/qcsrc/server/cl_client.qc
   branches/nexuiz-2.0/data/qcsrc/server/cl_impulse.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/cl_weaponsystem.qc
   branches/nexuiz-2.0/data/qcsrc/server/clientcommands.qc
   branches/nexuiz-2.0/data/qcsrc/server/constants.qh
   branches/nexuiz-2.0/data/qcsrc/server/defs.qh
   branches/nexuiz-2.0/data/qcsrc/server/g_damage.qc
   branches/nexuiz-2.0/data/qcsrc/server/g_hook.qc
   branches/nexuiz-2.0/data/qcsrc/server/g_triggers.qc
   branches/nexuiz-2.0/data/qcsrc/server/g_world.qc
   branches/nexuiz-2.0/data/qcsrc/server/havocbot.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/race.qc
   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/sv_main.qc
   branches/nexuiz-2.0/data/qcsrc/server/t_items.qc
   branches/nexuiz-2.0/data/qcsrc/server/t_plats.qc
   branches/nexuiz-2.0/data/qcsrc/server/t_quake.qc
   branches/nexuiz-2.0/data/qcsrc/server/t_teleporters.qc
   branches/nexuiz-2.0/data/qcsrc/server/teamplay.qc
   branches/nexuiz-2.0/data/qcsrc/server/w_common.qc
   branches/nexuiz-2.0/data/qcsrc/server/w_crylink.qc
   branches/nexuiz-2.0/data/qcsrc/server/w_electro.qc
   branches/nexuiz-2.0/data/qcsrc/server/w_grenadelauncher.qc
   branches/nexuiz-2.0/data/qcsrc/server/w_hagar.qc
   branches/nexuiz-2.0/data/qcsrc/server/w_laser.qc
   branches/nexuiz-2.0/data/qcsrc/server/w_nex.qc
   branches/nexuiz-2.0/data/qcsrc/server/w_rocketlauncher.qc
   branches/nexuiz-2.0/data/qcsrc/server/w_shotgun.qc
   branches/nexuiz-2.0/data/qcsrc/server/w_uzi.qc
   branches/nexuiz-2.0/data/scripts/entities.def
   branches/nexuiz-2.0/data/weapons.cfg
   branches/nexuiz-2.0/data/weaponsPro.cfg
Log:
fix attack_finished bug blocking weapon switching
weapon priority lists; non-critical impulses reordered (cleaned up)
fix inverted condition in func_door_secret
i wanna see the packetloss with sbar_columns all
Dieselpower: Changed MG ammo to SG ammo. There is no MG here.
Added new entity: "func_breakable"; functions the same way func_assault_destructible does, but it's for general gameplay use (IE destructible glass, crates, etc.)
enabled chat flood protection by default
support teamed items
g_weaponreplace_*
Capitalize some words in the FAQ
renamed g_tourney_warmup_unlimited_time to g_tourney_warmup_limit and default it to 60 (-1 is totally unlimited, 0 is up to timelimit, other values limit it further)
being limited to 60 seconds, next step is to enable g_tourney too by default (i think CSQC needs to show this in a good way first, we try this later)
do not require items in Race
maybe missing initialization breaks ready-restart so much on my ctf server?


Modified: branches/nexuiz-2.0/.patchsets
===================================================================
--- branches/nexuiz-2.0/.patchsets	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/.patchsets	2008-09-05 19:40:34 UTC (rev 4365)
@@ -1,2 +1,2 @@
 master = svn://svn.icculus.org/nexuiz/trunk
-revisions_applied = 1-4273
+revisions_applied = 1-4363

Modified: branches/nexuiz-2.0/Docs/FAQ.aft
===================================================================
--- branches/nexuiz-2.0/Docs/FAQ.aft	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/Docs/FAQ.aft	2008-09-05 19:40:34 UTC (rev 4365)
@@ -48,6 +48,9 @@
 	4. the engine could not initialize OpenGL.  Please install the latest drivers for your graphic card.  You will probably find one for your card there: [intel (http://intel.com)] [ati (http://ati.com)] [nvidia (http://nvidia.com)]
 	5. your download might be corrupted, please download Nexuiz again
 
+** When i start Nexuiz my screen is flickering
+This in known to happen on Windows with Intel grahpic chips and is a bug in the graphics drivers.  A workaround is to set |Flip-Policiy| to |blit|.  Open the control panel, there should be an icon called |Intel(R) GMA driver| (or something like that), double click it. Click on |3D Settings| to find the screen with that settings.
+
 ** How can I speed up my frame rate?
 You can choose predefined performance settings in the |Settings / Video| menu.  |Normal| is the default, |Medium| and |Low| run faster while |High|, |Ultra| and |Ultimate| enable more features you might want to test if you have a high end graphic card.  Or you can dis/enable single features.  The greatest performance boost can be achieved by turning off dynamic lights and shadows in the |Settings / Effects| menu.  Bloom is also quite resource intensive.  Other fps boots include disabling |Deluxemapping| and |Coronas|.  On older graphics cards or on-board/notebook chips with little video ram you can try to lower the texture quality in the |Settings / Video| menu.
 Some graphic cards (mostly ATI or quite old cards) run A LOT faster if you disable the |Vertex Buffer Objects| in the |Settings / Video| menu.  An other thing that can greatly help on such cards is to disable the |OpenGL 2.0 Shaders|.  Having that option enabled is faster on most cards however thats why both are active by default.

Modified: branches/nexuiz-2.0/Docs/FAQ.html
===================================================================
--- branches/nexuiz-2.0/Docs/FAQ.html	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/Docs/FAQ.html	2008-09-05 19:40:34 UTC (rev 4365)
@@ -125,6 +125,7 @@
 <ul>
 <li> <a class="link" href="#How do I install Nexuiz?">How do I install Nexuiz?</a></li>
 <li> <a class="link" href="#When I start Nexuiz all I see is a black screen or a black screen with some checkered squares">When I start Nexuiz all I see is a black screen or a black screen with some checkered squares</a></li>
+<li> <a class="link" href="#When i start Nexuiz my screen is flickering">When i start Nexuiz my screen is flickering</a></li>
 <li> <a class="link" href="#How can I speed up my frame rate?">How can I speed up my frame rate?</a></li>
 <li> <a class="link" href="#The sound is broken, it crackles and stutters">The sound is broken, it crackles and stutters</a></li>
 <li> <a class="link" href="#Mouse is too slow and sensitivity is at top (on Mac / Linux)">Mouse is too slow and sensitivity is at top (on Mac / Linux)</a></li>
@@ -199,6 +200,11 @@
 </ol>
 </p>
 <!--End Section 2-->
+<h3><a name="When i start Nexuiz my screen is flickering">When i start Nexuiz my screen is flickering</a></h3>
+<p class="Body">
+This in known to happen on Windows with Intel grahpic chips and is a bug in the graphics drivers.  A workaround is to set <tt>Flip-Policiy</tt> to <tt>blit</tt>.  Open the control panel, there should be an icon called <tt>Intel(R) GMA driver</tt> (or something like that), double click it. Click on <tt>3D Settings</tt> to find the screen with that settings.
+</p>
+<!--End Section 2-->
 <h3><a name="How can I speed up my frame rate?">How can I speed up my frame rate?</a></h3>
 <p class="Body">
 You can choose predefined performance settings in the <tt>Settings / Video</tt> menu.  <tt>Normal</tt> is the default, <tt>Medium</tt> and <tt>Low</tt> run faster while <tt>High</tt>, <tt>Ultra</tt> and <tt>Ultimate</tt> enable more features you might want to test if you have a high end graphic card.  Or you can dis/enable single features.  The greatest performance boost can be achieved by turning off dynamic lights and shadows in the <tt>Settings / Effects</tt> menu.  Bloom is also quite resource intensive.  Other fps boots include disabling <tt>Deluxemapping</tt> and <tt>Coronas</tt>.  On older graphics cards or on-board/notebook chips with little video ram you can try to lower the texture quality in the <tt>Settings / Video</tt> menu.

Modified: branches/nexuiz-2.0/Docs/server/server.cfg
===================================================================
--- branches/nexuiz-2.0/Docs/server/server.cfg	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/Docs/server/server.cfg	2008-09-05 19:40:34 UTC (rev 4365)
@@ -50,7 +50,6 @@
 //sv_gravity 800           // for low gravity, set this somewhere between 50 and 400
 
 // select AT MOST one of these arena mutators
-//g_instagib 0           // set to 1 to make the Nex the only weapon
 //g_minstagib 0          // set to 1 for MinstaGib
 //g_nixnex 0             // set to 1 for No Items Nexuiz
 //g_nixnex_with_laser 0  // set to 1 to always give players the laser in NixNex
@@ -76,7 +75,7 @@
 //g_maplist_votable_abstain 0 // when 1, people get a "don't care" selection in the vote screen
 //g_maplist_votable_nodetail 1 // when 1, people can't see how many voted for what (to thwart abusive "influential" first votes)
 
-//sv_vote_commands "restart timelimit fraglimit chmap gotomap g_grappling_hook sv_defaultplayer_fbskin_green sv_defaultplayer_fbskin_red sv_defaultplayer_fbskin_orange sv_defaultplayer_fbskin_off" // players can vote for those commands or use them if they are masters
+//sv_vote_commands "restart timelimit fraglimit chmap gotomap g_grappling_hook sv_defaultplayer_fbskin_green sv_defaultplayer_fbskin_red sv_defaultplayer_fbskin_orange sv_defaultplayer_fbskin_off endmatch reducematchtime extendmatchtime allready" // players can vote for those commands or use them if they are masters
 //sv_vote_call 1 // 0 will disable the normal voting
 //sv_vote_master 1 // 0 will disable voting to become masters
 //sv_vote_master_password "" // when set, vdo login master will allow you to run votable commands directly using vdo

Modified: branches/nexuiz-2.0/changes-since-last-release
===================================================================
--- branches/nexuiz-2.0/changes-since-last-release	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/changes-since-last-release	2008-09-05 19:40:34 UTC (rev 4365)
@@ -28,7 +28,6 @@
 game: hook can pull players (but detaches when that player shoots you)
 game: hook detaches when teleporting
 game: hook jitter-less
-game: hook no longer goes through walls
 game: hook sky-attach bug fixed
 game: item respawn: 20s for 50health and 25armor
 game: lemmings countdown ;)

Modified: branches/nexuiz-2.0/data/defaultNexuiz.cfg
===================================================================
--- branches/nexuiz-2.0/data/defaultNexuiz.cfg	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/defaultNexuiz.cfg	2008-09-05 19:40:34 UTC (rev 4365)
@@ -138,7 +138,7 @@
 
 //tournament mod
 set g_tourney 0 //enables tourney mode which splits the game into a warmup- and match-stage
-set g_tourney_warmup_unlimited_time 1 //if set the warmup-stage is not affected by any timelimit, otherwise the usual timelimit also affects warmup-stage
+set g_tourney_warmup_limit 60 //if set to -1 the warmup-stage is not affected by any timelimit, if set to 0 the usual timelimit also affects warmup-stage, otherwise warmup will be limited to this time (useful for public matches)
 set g_tourney_warmup_allow_timeout 0 //if set to 0 you cannot use the calltimeout command during the warmup-stage but only during the match stage
 set g_tourney_disable_spec_chat 1 //if set the chat sent by spectators or observers while being in match-stage can only seen by other specs/observers
 set g_tourney_disable_spec_vote 1 //if set only players can call a vote during the match-stage (thus spectators and observers can't call a vote then)
@@ -270,7 +270,6 @@
 set g_powerup_shield 1
 set g_use_ammunition 1
 set g_pickup_items 1
-set g_instagib 0
 set g_minstagib 0		// enable minstagib
 set g_minstagib_extralives 2	// how many extra lives you will get per powerup
 set g_minstagib_ammo_start 10	// starting ammo
@@ -613,7 +612,7 @@
 bind 7 "impulse 7"
 bind 8 "impulse 8"
 bind 9 "impulse 9"
-bind 0 "impulse 210"
+bind 0 "impulse 14" // cycles the superweapons
 bind MOUSE1 +attack
 bind MOUSE2 +attack2
 bind MOUSE3 +zoom
@@ -830,14 +829,14 @@
 seta g_maplist_textonly 0 // use old style centerprint
 alias suggestmap "cmd suggestmap $1"
 
-set g_chat_flood_spl 0                   // seconds between lines to not count as flooding
-set g_chat_flood_lmax 0                  // maximum number of lines per chat message at once
-set g_chat_flood_burst 0                 // allow bursts of so many chat lines
-set g_chat_flood_spl_team 0              // \
-set g_chat_flood_lmax_team 0             //  } same for say_team
-set g_chat_flood_burst_team 0            // /
-set g_chat_flood_notify_flooder 1        // when 0, the flooder still can see his own message
-set g_chat_teamcolors 0                  // colorize nicknames in team color for chat
+set g_chat_flood_spl 3            // seconds between lines to not count as flooding
+set g_chat_flood_lmax 2           // maximum number of lines per chat message at once
+set g_chat_flood_burst 2          // allow bursts of so many chat lines
+set g_chat_flood_spl_team 1       // \
+set g_chat_flood_lmax_team 2      //  } same for say_team
+set g_chat_flood_burst_team 2     // /
+set g_chat_flood_notify_flooder 1 // when 0, the flooder still can see his own message
+set g_chat_teamcolors 0           // colorize nicknames in team color for chat
 
 set g_waypointsprite_normdistance 512
 set g_waypointsprite_minscale 1
@@ -1077,3 +1076,33 @@
 set g_jump_grunt 0 // Do you make a grunting noise every time you jump? Is it the same grunting noise every time?
 
 alias allready "sv_cmd allready"
+
+seta cl_weaponpriority "10 9 8 11 7 6 5 4 3 2 1"
+// impulse 200, 210, 220:
+seta cl_weaponpriority0 "9 8 4"      // explosives    (RL Hagar Mortar)
+// impulse 201, 211, 221:
+seta cl_weaponpriority1 "7 6 5 1"    // energy        (Nex Crylink Electro Laser)
+seta cl_weaponpriority2 "7 3"        // hitscan exact (Nex MG)
+seta cl_weaponpriority3 "7 3 2"      // hitscan all   (Nex MG Shotgun)
+seta cl_weaponpriority4 "8 6 5 2"    // spam          (Hagar Crylink Electro Shotgun)
+seta cl_weaponpriority5 ""
+seta cl_weaponpriority6 ""
+seta cl_weaponpriority7 ""
+seta cl_weaponpriority8 ""
+// impulse 209, 219, 229:
+seta cl_weaponpriority9 ""
+
+// NOTE: this only replaces weapons on the map
+// use g_start_weapon_* to also replace the on-startup weapons!
+// example: g_weaponreplace_7 "7 11", then Nexes become MinstaNexes 50% of the times
+set g_weaponreplace_1 ""
+set g_weaponreplace_2 ""
+set g_weaponreplace_3 ""
+set g_weaponreplace_4 ""
+set g_weaponreplace_5 ""
+set g_weaponreplace_6 ""
+set g_weaponreplace_7 ""
+set g_weaponreplace_8 ""
+set g_weaponreplace_9 ""
+set g_weaponreplace_10 ""
+set g_weaponreplace_11 ""

Copied: branches/nexuiz-2.0/data/gfx/inv_weapon10.tga (from rev 4363, trunk/data/gfx/inv_weapon10.tga)
===================================================================
(Binary files differ)

Copied: branches/nexuiz-2.0/data/gfx/inv_weapon9.tga (from rev 4363, trunk/data/gfx/inv_weapon9.tga)
===================================================================
(Binary files differ)

Modified: branches/nexuiz-2.0/data/keybinds.txt
===================================================================
--- branches/nexuiz-2.0/data/keybinds.txt	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/keybinds.txt	2008-09-05 19:40:34 UTC (rev 4365)
@@ -24,6 +24,7 @@
 "impulse 7"                             "nex"
 "impulse 8"                             "hagar"
 "impulse 9"                             "rocket launcher"
+"impulse 14"                            "special weapons"
 ""                                      ""
 ""                                      "View"
 "+zoom"                                 "zoom"

Modified: branches/nexuiz-2.0/data/maps/dieselpower.bsp
===================================================================
(Binary files differ)

Modified: branches/nexuiz-2.0/data/maps/dieselpower.map
===================================================================
--- branches/nexuiz-2.0/data/maps/dieselpower.map	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/maps/dieselpower.map	2008-09-05 19:40:34 UTC (rev 4365)
@@ -25408,7 +25408,7 @@
 // entity 12
 {
 "origin" "1232 -864 -1008"
-"classname" "ammo_bullets"
+"classname" "ammo_shells"
 }
 // entity 13
 {
@@ -25519,7 +25519,7 @@
 // entity 31
 {
 "origin" "816 912 -232"
-"classname" "ammo_bullets"
+"classname" "ammo_shells"
 }
 // entity 32
 {

Copied: branches/nexuiz-2.0/data/models/portal.md3 (from rev 4363, trunk/data/models/portal.md3)
===================================================================
(Binary files differ)

Copied: branches/nexuiz-2.0/data/models/weapons/g_minstanex.md3 (from rev 4363, trunk/data/models/weapons/g_minstanex.md3)
===================================================================
(Binary files differ)

Copied: branches/nexuiz-2.0/data/models/weapons/g_porto.md3 (from rev 4363, trunk/data/models/weapons/g_porto.md3)
===================================================================
(Binary files differ)

Copied: branches/nexuiz-2.0/data/models/weapons/v_minstanex.md3 (from rev 4363, trunk/data/models/weapons/v_minstanex.md3)
===================================================================
(Binary files differ)

Copied: branches/nexuiz-2.0/data/models/weapons/v_porto.md3 (from rev 4363, trunk/data/models/weapons/v_porto.md3)
===================================================================
(Binary files differ)

Copied: branches/nexuiz-2.0/data/models/weapons/w_minstanex.zym (from rev 4363, trunk/data/models/weapons/w_minstanex.zym)
===================================================================
(Binary files differ)

Copied: branches/nexuiz-2.0/data/models/weapons/w_porto.zym (from rev 4363, trunk/data/models/weapons/w_porto.zym)
===================================================================
(Binary files differ)

Modified: branches/nexuiz-2.0/data/qcsrc/client/Main.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/Main.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/client/Main.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -187,6 +187,7 @@
 	self.nextthink = time + 0.2;
 }
 
+void Porto_Init();
 void PostInit(void)
 {
 	print(strcat("PostInit\n    maxclients = ", ftos(maxclients), "\n"));
@@ -197,6 +198,8 @@
 	playerchecker.think = Playerchecker_Think;
 	playerchecker.nextthink = time + 0.2;
 
+	Porto_Init();
+
 	postinit = true;
 }
 
@@ -623,10 +626,11 @@
 
 void Net_ReadInit()
 {
+	float i;
 	csqc_revision = ReadShort();
 	maxclients = ReadByte();
-	minstagib = ReadByte();
-
+	for(i = 0; i < 24; ++i)
+		weaponimpulse[i] = ReadByte() - 1;
 	CSQC_CheckRevision();
 }
 
@@ -763,6 +767,7 @@
 // CSQC_Parse_TempEntity : Handles all temporary entity network data in the CSQC layer.
 // You must ALWAYS first acquire the temporary ID, which is sent as a byte.
 // Return value should be 1 if CSQC handled the temporary entity, otherwise return 0 to have the engine process the event.
+void Net_ReadHoldAngles();
 float CSQC_Parse_TempEntity()
 {
 	local float bHandled;
@@ -814,6 +819,10 @@
 			Net_ReadZoomNotify();
 			bHandled = true;
 			break;
+		case TE_CSQC_HOLDANGLES:
+			Net_ReadHoldAngles();
+			bHandled = true;
+			break;
 		default:
 			// No special logic for this temporary entity; return 0 so the engine can handle it
 			bHandled = false;

Modified: branches/nexuiz-2.0/data/qcsrc/client/View.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/View.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/client/View.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -1,5 +1,102 @@
-//include "main.qh"
+float angles_held_status[24];
+vector angles_held[24];
+void Net_ReadHoldAngles()
+{
+	float wpn;
+	vector v;
+	wpn = ReadByte();
+	--wpn;
+	angles_held_status[wpn] = ReadByte();
+	if(angles_held_status[wpn])
+	{
+		v_x = ReadCoord();
+		v_y = ReadCoord();
+		v_z = 0;
+		angles_held[wpn] = v;
+	}
+}
 
+entity porto;
+vector polyline[16];
+float Q3SURFACEFLAG_SLICK = 2; // low friction surface
+void Porto_Draw()
+{
+	vector p, dir, ang, q, nextdir;
+	float idx, portal_number, portal1_idx;
+
+	if(activeweapon != WEP_PORTO)
+		return;
+
+	dir = view_forward;
+
+	if(angles_held_status[WEP_PORTO-1])
+	{
+		makevectors(angles_held[WEP_PORTO-1]);
+		dir = v_forward;
+	}
+
+	p = view_origin;
+
+	polyline[0] = p;
+	idx = 1;
+	portal_number = 0;
+	nextdir = dir;
+
+	for(;;)
+	{
+		dir = nextdir;
+		traceline(p, p + 65536 * dir, TRUE, world);
+		if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
+			return;
+		nextdir = dir - 2 * (dir * trace_plane_normal) * trace_plane_normal; // mirror dir at trace_plane_normal
+		p = trace_endpos;
+		polyline[idx] = p;
+		++idx;
+		if(idx >= 16)
+			return;
+		if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK)
+			continue;
+		++portal_number;
+		ang = vectoangles(trace_plane_normal, dir);
+		ang_x = -ang_x;
+		makevectors(ang);
+		if(!CheckWireframeBox(porto, p - 48 * v_right - 48 * v_up + 16 * v_forward, 96 * v_right, 96 * v_up, 96 * v_forward))
+			return;
+		if(portal_number == 1)
+			portal1_idx = idx;
+		if(portal_number >= 2)
+			break;
+	}
+
+	while(idx >= 2)
+	{
+		p = polyline[idx-2];
+		q = polyline[idx-1];
+		if(idx == 2)
+			p = p - view_up * 16;
+		if(idx-1 >= portal1_idx)
+		{
+			Draw_CylindricLine(p, q, 4, "", 1, 0, '0 0 1', 0.5, DRAWFLAG_NORMAL);
+		}
+		else
+		{
+			Draw_CylindricLine(p, q, 4, "", 1, 0, '1 0 0', 0.5, DRAWFLAG_NORMAL);
+		}
+		--idx;
+	}
+}
+
+float DPCONTENTS_SOLID = 1; // hit a bmodel, not a bounding box
+float DPCONTENTS_BODY = 32; // hit a bounding box, not a bmodel
+float DPCONTENTS_PLAYERCLIP = 256; // blocks player movement
+void Porto_Init()
+{
+	porto = spawn();
+	porto.classname = "porto";
+	porto.draw = Porto_Draw;
+	porto.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP;
+}
+
 float drawtime;
 
 float tan(float x)
@@ -28,7 +125,7 @@
 			zoomspeed = 3.5;
 
 	zoomdir = button_zoom;
-	if(activeweapon == 7 && !minstagib)
+	if(activeweapon == WEP_NEX)
 		zoomdir += button_attack2;
 	if(spectatee_status > 0 || isdemo())
 	{
@@ -121,7 +218,7 @@
 	
 	sbar_alpha_fg = cvar("sbar_alpha_fg" );
 	sbar_hudselector = cvar("sbar_hudselector");
-	activeweapon = getstati(STAT_ACTIVEWEAPON);
+	activeweapon = getstati(STAT_SWITCHWEAPON);
 	teamplay = cvar("teamplay");
 
 	if(last_weapon != activeweapon) {

Modified: branches/nexuiz-2.0/data/qcsrc/client/csqc_constants.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/csqc_constants.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/client/csqc_constants.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -164,24 +164,6 @@
 const float BUTTON_16 = 262144;
 
 
-const float NEX_IT_UZI              = 1;
-const float NEX_IT_SHOTGUN          = 2;
-const float NEX_IT_GRENADE_LAUNCHER = 4;
-const float NEX_IT_ELECTRO          = 8;
-const float NEX_IT_CRYLINK          = 16;
-const float NEX_IT_NEX              = 32;
-const float NEX_IT_HAGAR            = 64;
-const float NEX_IT_ROCKET_LAUNCHER  = 128;
-const float NEX_IT_SHELLS           = 256;
-const float NEX_IT_BULLETS          = 512;
-const float NEX_IT_ROCKETS          = 1024;
-const float NEX_IT_CELLS            = 2048;
-const float NEX_IT_LASER            = 4094;
-const float NEX_IT_STRENGTH         = 8192;
-const float NEX_IT_INVINCIBLE       = 16384;
-const float NEX_IT_SPEED            = 32768;
-const float NEX_IT_SLOWMO           = 65536;
-
 const float DRAWFLAG_NORMAL = 0;
 const float DRAWFLAG_ADDITIVE = 1;
 const float DRAWFLAG_MODULATE = 2;

Modified: branches/nexuiz-2.0/data/qcsrc/client/main.qh
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/main.qh	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/client/main.qh	2008-09-05 19:40:34 UTC (rev 4365)
@@ -118,8 +118,9 @@
 float spectatorbutton_zoom;
 float button_attack2;
 float activeweapon;
-float minstagib;
 float current_viewzoom;
 float zoomin_effect;
 float ignore_plus_zoom;
 float ignore_minus_zoom;
+
+float weaponimpulse[24];

Modified: branches/nexuiz-2.0/data/qcsrc/client/sbar.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/sbar.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/client/sbar.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -27,10 +27,35 @@
 	MapVote_Draw();
 }
 
+float weaponspace[10];
+float weapon_first, weapon_last;
+void Sbar_DrawWeapon_Clear()
+{
+	float idx;
+	weapon_first = -2;
+	weapon_last = -1;
+	for(idx = 0; idx < 10; ++idx)
+		weaponspace[idx] = 0;
+	for(idx = 0; idx <= 23; ++idx)
+	{
+		if(weaponimpulse[idx] >= 0)
+		{
+			if(weapon_first < 0)
+				weapon_first = idx;
+			weapon_last = idx;
+		}
+	}
+}
 void Sbar_DrawWeapon(float nr, float fade, float active)
 {
 	vector pos, vsize, color;
-	float value;
+	float value, idx, imp, sp;
+
+	imp = weaponimpulse[nr];
+	if(imp == 0)
+		idx = 9;
+	else
+		idx = imp - 1;
 	
 	value = (active) ? 1 : 0.6;
 	color_x = color_y = color_z = value;
@@ -39,9 +64,12 @@
 	{
 		// width = 300, height = 100
 		const float w_width = 32, w_height = 12, w_space = 2, font_size = 8;
+
+		sp = weaponspace[idx] + 1;
+		weaponspace[idx] = sp;
 		
-		pos_x = (vid_conwidth - w_width * 9) * 0.5 + w_width * nr;
-		pos_y = (vid_conheight - w_height);
+		pos_x = (vid_conwidth - w_width * 9) * 0.5 + w_width * idx;
+		pos_y = (vid_conheight - w_height * sp);
 		pos_z = 0;
 		vsize_x = w_width;
 		vsize_y = w_height;
@@ -52,8 +80,7 @@
 		vsize_x = font_size;
 		vsize_y = font_size;
 		vsize_z = 0;
-		drawstring(pos, ftos(nr+1), vsize, '1 1 0', sbar_alpha_fg, 0);
-
+		drawstring(pos, ftos(imp), vsize, '1 1 0', sbar_alpha_fg, 0);
 	}
 	else
 	{
@@ -61,11 +88,14 @@
 		const float w2_width = 300, w2_height = 100, w2_space = 10;
 		const float w2_scale = 0.4;
 
-		pos_x = vid_conwidth - (w2_width + w2_space) * w2_scale;
-		pos_y = (w2_height + w2_space) * w2_scale * nr + w2_space;
+		float f;
+		f = 9 / (weapon_last + 1 - weapon_first);
+
+		pos_x = vid_conwidth - (w2_width + w2_space) * w2_scale * f;
+		pos_y = (w2_height + w2_space) * w2_scale * nr * f + w2_space;
 		pos_z = 0;
-		vsize_x = w2_width * w2_scale;
-		vsize_y = w2_height * w2_scale;
+		vsize_x = w2_width * w2_scale * f;
+		vsize_y = w2_height * w2_scale * f;
 		vsize_z = 0;
 		
 		drawpic(pos, strcat("gfx/inv_weapon", ftos(nr)), vsize, color, value * fade * sbar_alpha_fg, 0);
@@ -341,7 +371,7 @@
 		else if(argv(1) == "all")
 		{
 			string s;
-			s = "ping name |";
+			s = "ping pl name |";
 			for(i = 0; i < MAX_SCORE; ++i)
 			{
 				if(i != ps_primary)
@@ -1296,7 +1326,7 @@
 {
 	float i;
 	float x, fade;
-	float stat_items;
+	float stat_items, stat_weapons;
 
 	sbar_fontsize = Sbar_GetFontsize();
 
@@ -1402,6 +1432,7 @@
 			if (sb_lines && sbar_hudselector == 1)
 			{
 				stat_items = getstati(STAT_ITEMS);
+				stat_weapons = getstati(STAT_WEAPONS);
 
 				sbar_x = (vid_conwidth - 320.0)*0.5;
 				sbar_y = vid_conheight - 24.0 - 16.0;
@@ -1411,19 +1442,16 @@
 				fade = bound(0.7, fade, 1);
 
 				x = 1.0;
-				for(i = 0; i < 8; ++i)
+				Sbar_DrawWeapon_Clear();
+				for(i = 1; i <= 24; ++i)
 				{
-					if(stat_items & x)
+					if(weaponimpulse[i-1] >= 0)
+					if(stat_weapons & x)
 					{
-						Sbar_DrawWeapon(i+1, fade, (i + 2 == activeweapon));
+						Sbar_DrawWeapon(i-1, fade, (i == activeweapon));
 					}
 					x *= 2;
 				}
-				x *= 2*2*2*2;
-				if(stat_items & x)
-				{
-					Sbar_DrawWeapon(0, fade, (activeweapon == 1));
-				}
 
 				// armor
 				x = getstati(STAT_ARMOR);
@@ -1465,15 +1493,15 @@
 
 				// ammo
 				x = getstati(STAT_AMMO);
-				if ((stat_items & (NEX_IT_SHELLS | NEX_IT_BULLETS | NEX_IT_ROCKETS | NEX_IT_CELLS)) || x != 0)
+				if ((stat_items & IT_AMMO) || x != 0)
 				{
-					if (stat_items & NEX_IT_SHELLS)
+					if (stat_items & IT_SHELLS)
 						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)
+					else if (stat_items & IT_NAILS)
 						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)
+					else if (stat_items & IT_ROCKETS)
 						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)
+					else if (stat_items & IT_CELLS)
 						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 0', x, 3, 24, '0.6 0.7 0.8', 1, 0);
@@ -1492,6 +1520,7 @@
 			{
 			
 				stat_items = getstati(STAT_ITEMS);
+				stat_weapons = getstati(STAT_WEAPONS);
 			
 				sbar_x = (vid_conwidth - 640.0)*0.5;
 				sbar_y = vid_conheight - 47;
@@ -1500,19 +1529,16 @@
 				fade = 3 - 2 * (time - weapontime);
 
 				x = 1.0;
-				for(i = 0; i < 8; ++i)
+				Sbar_DrawWeapon_Clear();
+				for(i = 1; i <= 24; ++i)
 				{
-					if(stat_items & x)
+					if(weaponimpulse[i-1] >= 0)
+					if(stat_weapons & x)
 					{
-						Sbar_DrawWeapon(i+1, fade, (i + 2 == activeweapon));
+						Sbar_DrawWeapon(i-1, fade, (i == activeweapon));
 					}
 					x *= 2;
 				}
-				x *= 2*2*2*2;
-				if(stat_items & x)
-				{
-					Sbar_DrawWeapon(0, fade, (activeweapon == 1));
-				}
 
 				if (sb_lines > 24)
 					drawpic(sbar, "gfx/sbar", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
@@ -1538,13 +1564,13 @@
 				if(activeweapon != 12)
 				{
 					// (519-3*24) = 447
-					if (stat_items & NEX_IT_SHELLS)
+					if (stat_items & IT_SHELLS)
 						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)
+					else if (stat_items & IT_NAILS)
 						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)
+					else if (stat_items & IT_ROCKETS)
 						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)
+					else if (stat_items & IT_CELLS)
 						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 0', x, 3, 24, '0.6 0.7 0.8', 1, 0);

Modified: branches/nexuiz-2.0/data/qcsrc/common/constants.qh
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/common/constants.qh	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/common/constants.qh	2008-09-05 19:40:34 UTC (rev 4365)
@@ -14,7 +14,9 @@
 // Revision 13: pointparticles
 // Revision 14: laser
 // Revision 15: zoom
-#define CSQC_REVISION 15
+// Revision 16: multi-weapons
+// Revision 17: multi-weaponimpulses
+#define CSQC_REVISION 17
 
 // probably put these in common/
 // so server/ and client/ can be synced better
@@ -200,10 +202,13 @@
 const float TE_CSQC_SPECTATING = 111;
 const float TE_CSQC_SPAWN = 112;
 const float TE_CSQC_ZOOMNOTIFY = 113;
+const float TE_CSQC_HOLDANGLES = 114;
 
 const float STAT_KH_KEYS = 32;
 const float STAT_CTF_STATE = 33;
 const float STAT_SYS_TICRATE = 34;
+const float STAT_WEAPONS = 35;
+const float STAT_SWITCHWEAPON = 36;
 const float CTF_STATE_ATTACK = 1;
 const float CTF_STATE_DEFEND = 2;
 const float CTF_STATE_COMMANDER = 3;
@@ -270,3 +275,48 @@
 #define SP_SUICIDES 2
 #define SP_SCORE 3
 // game mode specific indices are not in common/, but in server/scores_rules.qc!
+
+// Weapon indexes
+// %weaponaddpoint
+float WEP_FIRST				=  1;
+float WEP_LASER				=  1; float WEPBIT_LASER			= 1; // always: 2^(w-1)
+float WEP_SHOTGUN			=  2; float WEPBIT_SHOTGUN			= 2;
+float WEP_UZI				=  3; float WEPBIT_UZI				= 4;
+float WEP_GRENADE_LAUNCHER	=  4; float WEPBIT_GRENADE_LAUNCHER	= 8;
+float WEP_ELECTRO			=  5; float WEPBIT_ELECTRO			= 16;
+float WEP_CRYLINK			=  6; float WEPBIT_CRYLINK			= 32;
+float WEP_NEX				=  7; float WEPBIT_NEX				= 64;
+float WEP_HAGAR				=  8; float WEPBIT_HAGAR			= 128;
+float WEP_ROCKET_LAUNCHER	=  9; float WEPBIT_ROCKET_LAUNCHER	= 256;
+float WEP_PORTO				= 10; float WEPBIT_PORTO			= 512;
+float WEP_MINSTANEX			= 11; float WEPBIT_MINSTANEX		= 1024;
+float WEP_LAST				= 11; float WEPBIT_ALL              = 2047;
+float WEP_COUNT             = 12;
+
+float	IT_UNLIMITED_AMMO       = 1;
+// when this bit is set, using a weapon does not reduce ammo. Checkpoints can give this powerup.
+float	IT_SHELLS				= 256;
+float	IT_NAILS				= 512;
+float	IT_ROCKETS				= 1024;
+float	IT_CELLS				= 2048;
+float	IT_SUPERWEAPON			= 4096;
+float	IT_AMMO					= 7936;
+float	IT_STRENGTH				= 8192;
+float	IT_INVINCIBLE			= 16384;
+float	IT_HEALTH				= 32768;
+// union:
+	// for items:
+	float	IT_KEY1					= 131072;
+	float	IT_KEY2					= 262144;
+	// for players:
+	float	IT_RED_FLAG_TAKEN		= 32768;
+	float	IT_RED_FLAG_LOST		= 65536;
+	float	IT_RED_FLAG_CARRING		= 98304;
+	float	IT_BLUE_FLAG_TAKEN		= 131072;
+	float	IT_BLUE_FLAG_LOST		= 262144;
+	float	IT_BLUE_FLAG_CARRING	= 393216;
+// end
+float	IT_5HP					= 524288;
+float	IT_25HP					= 1048576;
+float	IT_ARMOR_SHARD			= 2097152;
+float	IT_ARMOR				= 4194304;

Modified: branches/nexuiz-2.0/data/qcsrc/common/mapinfo.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/common/mapinfo.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/common/mapinfo.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -788,7 +788,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")))
+	if(!(cvar("g_lms") || cvar("g_instagib") || cvar("g_minstagib") || cvar("g_nixnex") || cvar("g_rocketarena") || !cvar("g_pickup_items") || cvar("g_race")))
 		req |= MAPINFO_FEATURE_WEAPONS;
 	return req;
 }

Modified: branches/nexuiz-2.0/data/qcsrc/common/util.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/common/util.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/common/util.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -615,3 +615,28 @@
 	}
 }
 
+#ifndef MENUQC
+float CheckWireframeBox(entity forent, vector v0, vector dvx, vector dvy, vector dvz)
+{
+	traceline(v0, v0 + dvx, TRUE, forent); if(trace_fraction < 1) return 0;
+	traceline(v0, v0 + dvy, TRUE, forent); if(trace_fraction < 1) return 0;
+	traceline(v0, v0 + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
+	traceline(v0 + dvx, v0 + dvx + dvy, TRUE, forent); if(trace_fraction < 1) return 0;
+	traceline(v0 + dvx, v0 + dvx + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
+	traceline(v0 + dvy, v0 + dvy + dvx, TRUE, forent); if(trace_fraction < 1) return 0;
+	traceline(v0 + dvy, v0 + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
+	traceline(v0 + dvz, v0 + dvz + dvx, TRUE, forent); if(trace_fraction < 1) return 0;
+	traceline(v0 + dvz, v0 + dvz + dvy, TRUE, forent); if(trace_fraction < 1) return 0;
+	traceline(v0 + dvx + dvy, v0 + dvx + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
+	traceline(v0 + dvx + dvz, v0 + dvx + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
+	traceline(v0 + dvy + dvz, v0 + dvx + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
+	return 1;
+}
+
+// a makevectors that inverts vectoangles
+void fixedmakevectors(vector a)
+{
+	a_x = -a_x;
+	makevectors(a);
+}
+#endif

Modified: branches/nexuiz-2.0/data/qcsrc/common/util.qh
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/common/util.qh	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/common/util.qh	2008-09-05 19:40:34 UTC (rev 4365)
@@ -65,3 +65,9 @@
 void compressShortVector_init();
 vector decompressShortVector(float data);
 float compressShortVector(vector vec);
+
+#ifndef MENUQC
+float CheckWireframeBox(entity forent, vector v0, vector dvx, vector dvy, vector dvz);
+
+void fixedmakevectors(vector a);
+#endif

Modified: branches/nexuiz-2.0/data/qcsrc/menu/nexuiz/dialog_multiplayer_create_mutators.c
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/menu/nexuiz/dialog_multiplayer_create_mutators.c	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/menu/nexuiz/dialog_multiplayer_create_mutators.c	2008-09-05 19:40:34 UTC (rev 4365)
@@ -22,8 +22,6 @@
 {
 	string s;
 	s = "";
-	if(cvar("g_instagib"))
-		s = strcat(s, ", InstaGib");
 	if(cvar("g_minstagib"))
 		s = strcat(s, ", MinstaGib");
 	if(cvar("g_nixnex"))
@@ -72,8 +70,6 @@
 	me.TR(me);
 		me.TD(me, 1, 2, e = makeNexuizRadioButton(1, string_null, string_null, "Regular"));
 	me.TR(me);
-		me.TD(me, 1, 2, e = makeNexuizRadioButton(1, "g_instagib", string_null, "InstaGib"));
-	me.TR(me);
 		me.TD(me, 1, 2, e = makeNexuizRadioButton(1, "g_minstagib", string_null, "MinstaGib"));
 	me.TR(me);
 		me.TD(me, 1, 2, e = makeNexuizRadioButton(1, "g_nixnex", string_null, "NixNex"));

Modified: branches/nexuiz-2.0/data/qcsrc/server/builtins.qh
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/builtins.qh	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/builtins.qh	2008-09-05 19:40:34 UTC (rev 4365)
@@ -49,7 +49,7 @@
 void	particle (vector v, vector d, float colour, float count)			= #48;
 void	ChangeYaw (void)								= #49;
 
-vector	vectoangles (vector v)								= #51;
+vector	vectoangles (vector v, ...)								= #51;
 void(float to, float f)		WriteByte = #52;
 void(float to, float f)		WriteChar = #53;
 void(float to, float f)		WriteShort = #54;

Modified: branches/nexuiz-2.0/data/qcsrc/server/cl_client.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/cl_client.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/cl_client.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -401,6 +401,8 @@
 	DropAllRunes(self);
 	kh_Key_DropAll(self, TRUE);
 
+	Portal_ClearAll(self);
+
 	if(self.flagcarried)
 		DropFlag(self.flagcarried);
 
@@ -461,6 +463,7 @@
 	setsize (self, '0 0 0', '0 0 0');
 	self.oldorigin = self.origin;
 	self.items = 0;
+	self.weapons = 0;
 	self.model = "";
 	self.modelindex = 0;
 	self.weapon = 0;
@@ -631,6 +634,7 @@
 		self.health = start_health;
 		self.armorvalue = start_armorvalue;
 		self.items = start_items;
+		self.weapons = start_weapons;
 		self.switchweapon = start_switchweapon;
 		self.cnt = start_switchweapon;
 		self.weapon = 0;
@@ -767,6 +771,7 @@
 */
 void SendCSQCInfo(void)
 {
+	float i;
 	if(clienttype(self) != CLIENTTYPE_REAL)
 		return;
 	msg_entity = self;
@@ -774,7 +779,8 @@
 	WriteByte(MSG_ONE, TE_CSQC_INIT);
 	WriteShort(MSG_ONE, CSQC_REVISION);
 	WriteByte(MSG_ONE, maxclients);
-	WriteByte(MSG_ONE, g_minstagib);
+	for(i = 1; i <= 24; ++i)
+		WriteByte(MSG_ONE, (get_weaponinfo(i)).impulse + 1);
 }
 
 /*
@@ -1029,7 +1035,6 @@
 void ClientConnect (void)
 {
 	local string s;
-	float wep;
 
 	if(self.flags & FL_CLIENT)
 	{
@@ -1123,15 +1128,6 @@
 	// get version info from player
 	stuffcmd(self, "cmd clientversion $gameversion\n");
 
-	// send all weapon info strings
-	stuffcmd(self, "register_bestweapon clear\n"); // clear the Quake stuff
-	wep = WEP_FIRST;
-	while (wep <= WEP_LAST)
-	{
-		weapon_action(wep, WR_REGISTER);
-		wep = wep + 1;
-	}
-
 	// get other cvars from player
 	GetCvars(0);
 
@@ -1260,6 +1256,8 @@
 	DropAllRunes(self);
 	kh_Key_DropAll(self, TRUE);
 
+	Portal_ClearAll(self);
+
 	if(self.flagcarried)
 		DropFlag(self.flagcarried);
 
@@ -1683,6 +1681,7 @@
 	self.health = spectatee.health;
 	self.impulse = 0;
 	self.items = spectatee.items;
+	self.weapons = spectatee.weapons;
 	self.punchangle = spectatee.punchangle;
 	self.view_ofs = spectatee.view_ofs;
 	self.v_angle = spectatee.v_angle;
@@ -2163,7 +2162,7 @@
 	}
 
 	if(!zoomstate_set)
-		SetZoomState(self.BUTTON_ZOOM || (self.BUTTON_ATCK2 && !g_minstagib && self.weapon == WEP_NEX));
+		SetZoomState(self.BUTTON_ZOOM || (self.BUTTON_ATCK2 && self.weapon == WEP_NEX));
 
 	float oldspectatee_status;
 	oldspectatee_status = self.spectatee_status;

Modified: branches/nexuiz-2.0/data/qcsrc/server/cl_impulse.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/cl_impulse.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/cl_impulse.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -56,9 +56,50 @@
 	}
 };
 
+/*
+ * Impulse map:
+ *
+ * 0 reserved (no input)
+ * 1 to 9: weapon shortcuts
+ * 10: next weapon
+ * 11: most recently used weapon
+ * 12: previous weapon
+ * 13: best weapon
+ * 17: throw weapon
+ *
+ * 30 to 39: create waypoints
+ * 47: clear personal waypoints
+ * 48: clear team waypoints
+ * 49: turn base waypoints on/off
+ *
+ * 99: loaded
+ *
+ * 140: moving clone
+ * 141: ctf speedrun
+ * 142: fixed clone
+ * 143: emergency teleport
+ * 144: printsurfaceinfo
+ * 145: distance
+ *
+ * TODO:
+ * 200 to 209: prev weapon shortcuts
+ * 210 to 219: best weapon shortcuts
+ * 220 to 229: next weapon shortcuts
+ * 230 to 253: individual weapons (up to 24)
+ */
+
 void ImpulseCommands (void)
 {
 	local float imp;
+	vector start, end, enddown;
+	float i;
+	float m;
+	float good, evil, evilsurf;
+	float maxattempts;
+	vector org, delta;
+	float wep;
+	entity e;
+
 	imp = self.impulse;
 	if (!imp || gameover)
 		return;
@@ -67,130 +108,178 @@
 	if (timeoutStatus == 2) //don't allow any impulses while the game is paused
 		return;
 
-	if (imp >= 1 && imp <= 12)
+	if (imp >= 1 && imp <= 9)
 	{
 		// weapon switching impulses
 		if(self.deadflag == DEAD_NO)
+			W_NextWeaponOnImpulse(imp);
+		else
+			self.impulse = imp; // retry in next frame
+	}
+	else if(imp >= 10 && imp <= 17)
+	{
+		if(self.deadflag == DEAD_NO)
 		{
-			if (imp <= 9)
-				W_SwitchWeapon (imp);
-			else if (imp == 10)
-				W_NextWeapon ();
-			else if (imp == 12)
-				W_PreviousWeapon ();
-			else if (imp == 11) // last weapon
-				W_SwitchWeapon (self.cnt);
+			switch(imp)
+			{
+				case 10:
+					W_NextWeapon ();
+					break;
+				case 11:
+					W_SwitchWeapon (self.cnt); // previously used
+					break;
+				case 12:
+					W_PreviousWeapon ();
+					break;
+				case 13:
+					W_SwitchWeapon (w_getbestweapon(self));
+					break;
+				case 14:
+					W_NextWeaponOnImpulse(0);
+					break;
+				case 17:
+					if (!g_minstagib)
+						W_ThrowWeapon(W_CalculateProjectileVelocity(self.velocity, v_forward * 750), '0 0 0', TRUE);
+					break;
+				}
 		}
 		else
 			self.impulse = imp; // retry in next frame
 	}
-	// throw weapon
-	else if (imp == 17)
+	else if(imp >= 200 && imp <= 229)
 	{
 		if(self.deadflag == DEAD_NO)
-		if (!g_minstagib)
-			W_ThrowWeapon(W_CalculateProjectileVelocity(self.velocity, v_forward * 750), '0 0 0', TRUE);
+		{
+			// custom order weapon cycling
+			i = mod(imp, 10);
+			m = (imp - (210 + i)); // <0 for prev, =0 for best, >0 for next
+			W_CycleWeapon(self.(cvar_cl_weaponpriorities[i]), m);
+		}
+		else
+			self.impulse = imp; // retry in next frame
 	}
+	else if(imp >= 230 && imp <= 253)
+	{
+		if(self.deadflag == DEAD_NO)
+			W_SwitchWeapon (imp - 230 + WEP_FIRST);
+		else
+			self.impulse = imp; // retry in next frame
+	}
 	// deploy waypoints
 	else if (imp >= 30 && imp <= 49)
 	{
-		if(imp == 30)
+		switch(imp)
 		{
-			WaypointSprite_DeployPersonal("waypoint", self.origin);
-			self.personal_v_angle = self.v_angle;
-			self.personal_velocity = self.velocity;
-			sprint(self, "personal waypoint spawned at location\n");
+			case 30:
+				WaypointSprite_DeployPersonal("waypoint", self.origin);
+				self.personal_v_angle = self.v_angle;
+				self.personal_velocity = self.velocity;
+				sprint(self, "personal waypoint spawned at location\n");
+				break;
+			case 31:
+				WaypointSprite_DeployPersonal("waypoint", self.cursor_trace_endpos);
+				self.personal_v_angle = self.v_angle;
+				self.personal_velocity = '0 0 0';
+				sprint(self, "personal waypoint spawned at crosshair\n");
+				break;
+			case 32:
+				if(vlen(self.death_origin))
+				{
+					WaypointSprite_DeployPersonal("waypoint", self.death_origin);
+					self.personal_v_angle = self.v_angle;
+					self.personal_velocity = '0 0 0';
+					sprint(self, "personal waypoint spawned at death location\n");
+				}
+				break;
+			case 33:
+				if(self.deadflag == DEAD_NO && teams_matter)
+				{
+					WaypointSprite_Attach("helpme", TRUE);
+					sprint(self, "HELP ME attached\n");
+				}
+				break;
+			case 34:
+				WaypointSprite_DeployFixed("here", FALSE, self.origin);
+				sprint(self, "HERE spawned at location\n");
+				break;
+			case 35:
+				WaypointSprite_DeployFixed("here", FALSE, self.cursor_trace_endpos);
+				sprint(self, "HERE spawned at crosshair\n");
+				break;
+			case 36:
+				if(vlen(self.death_origin))
+				{
+					WaypointSprite_DeployFixed("here", FALSE, self.death_origin);
+					sprint(self, "HERE spawned at death location\n");
+				}
+				break;
+			case 37:
+				WaypointSprite_DeployFixed("danger", FALSE, self.origin);
+				sprint(self, "DANGER spawned at location\n");
+				break;
+			case 38:
+				WaypointSprite_DeployFixed("danger", FALSE, self.cursor_trace_endpos);
+				sprint(self, "DANGER spawned at crosshair\n");
+				break;
+			case 39:
+				if(vlen(self.death_origin))
+				{
+					WaypointSprite_DeployFixed("danger", FALSE, self.death_origin);
+					sprint(self, "DANGER spawned at death location\n");
+				}
+				break;
+			case 47:
+				WaypointSprite_ClearPersonal();
+				sprint(self, "personal waypoint cleared\n");
+				break;
+			case 48:
+				WaypointSprite_ClearOwned();
+				sprint(self, "all waypoints cleared\n");
+				break;
+			case 49:
+				self.cvar_cl_hidewaypoints = !(self.cvar_cl_hidewaypoints);
+				sprint(self, "fixed waypoints now ");
+				if(self.cvar_cl_hidewaypoints)
+					sprint(self, "OFF\n");
+				else
+					sprint(self, "ON\n");
+				break;
 		}
-		else if(imp == 31)
-		{
-			WaypointSprite_DeployPersonal("waypoint", self.cursor_trace_endpos);
-			self.personal_v_angle = self.v_angle;
-			self.personal_velocity = '0 0 0';
-			sprint(self, "personal waypoint spawned at crosshair\n");
-		}
-		else if(imp == 32 && vlen(self.death_origin))
-		{
-			WaypointSprite_DeployPersonal("waypoint", self.death_origin);
-			self.personal_v_angle = self.v_angle;
-			self.personal_velocity = '0 0 0';
-			sprint(self, "personal waypoint spawned at death location\n");
-		}
-		else if(imp == 33 && self.deadflag == DEAD_NO && teams_matter)
-		{
-			WaypointSprite_Attach("helpme", TRUE);
-			sprint(self, "HELP ME attached\n");
-		}
-		else if(imp == 34)
-		{
-			WaypointSprite_DeployFixed("here", FALSE, self.origin);
-			sprint(self, "HERE spawned at location\n");
-		}
-		else if(imp == 35)
-		{
-			WaypointSprite_DeployFixed("here", FALSE, self.cursor_trace_endpos);
-			sprint(self, "HERE spawned at crosshair\n");
-		}
-		else if(imp == 36 && vlen(self.death_origin))
-		{
-			WaypointSprite_DeployFixed("here", FALSE, self.death_origin);
-			sprint(self, "HERE spawned at death location\n");
-		}
-		else if(imp == 37)
-		{
-			WaypointSprite_DeployFixed("danger", FALSE, self.origin);
-			sprint(self, "DANGER spawned at location\n");
-		}
-		else if(imp == 38)
-		{
-			WaypointSprite_DeployFixed("danger", FALSE, self.cursor_trace_endpos);
-			sprint(self, "DANGER spawned at crosshair\n");
-		}
-		else if(imp == 39 && vlen(self.death_origin))
-		{
-			WaypointSprite_DeployFixed("danger", FALSE, self.death_origin);
-			sprint(self, "DANGER spawned at death location\n");
-		}
-		else if(imp == 47)
-		{
-			WaypointSprite_ClearPersonal();
-			sprint(self, "personal waypoint cleared\n");
-		}
-		else if(imp == 48)
-		{
-			WaypointSprite_ClearOwned();
-			sprint(self, "all waypoints cleared\n");
-		}
-		else if(imp == 49)
-		{
-			self.cvar_cl_hidewaypoints = !(self.cvar_cl_hidewaypoints);
-			sprint(self, "fixed waypoints now ");
-			if(self.cvar_cl_hidewaypoints)
-				sprint(self, "OFF\n");
-			else
-				sprint(self, "ON\n");
-		}
 	}
-	else
+	else if(imp >= 140 && imp <= 149 || imp == 99) // 10 cheats ought to be enough for anyone
 	{
-		if (sv_cheats)
+		if(sv_cheats)
+		if(self.deadflag == DEAD_NO)
 		{
-			if(imp == 19)
+			switch(imp)
 			{
-				makevectors(self.v_angle);
-				traceline(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * MAX_SHOT_DISTANCE, FALSE, self);
-				if (trace_fraction < 1)
-					printsurfaceinfo(trace_ent, trace_endpos);
-			}
-			else if(imp == 20)
-			{
-				makevectors(self.v_angle);
-				traceline(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * 65536, FALSE, self);
-				sprint(self, strcat("distance: ", ftos(fabs(vlen(trace_endpos - (self.origin + self.view_ofs)))), "\n"));
-			}
-			else if(self.deadflag == DEAD_NO)
-			{
-				if(imp == 77)
-				{
+				case 99:
+					self.weapons |= WEPBIT_ALL;
+					self.ammo_shells = g_pickup_shells_max;
+					self.ammo_nails = g_pickup_nails_max;
+					self.ammo_rockets = g_pickup_rockets_max;
+					self.ammo_cells = g_pickup_cells_max;
+					self.health = g_pickup_healthsmall_max;
+					self.armorvalue = g_pickup_armorsmall_max;
+					self.pauserotarmor_finished = time + cvar("g_balance_pause_armor_rot_spawn");
+					self.pauserothealth_finished = time + cvar("g_balance_pause_health_rot_spawn");
+					self.pauseregen_finished = time + cvar("g_balance_pause_health_regen_spawn");
+					// precache weapon models/sounds
+					wep = WEP_FIRST;
+					while (wep <= WEP_LAST)
+					{
+						weapon_action(wep, WR_PRECACHE);
+						wep = wep + 1;
+					}
+					break;
+				case 140:
+					makevectors (self.v_angle);
+					self.velocity = self.velocity + v_forward * 300;
+					CopyBody(1);
+					self.lip += 1;
+					self.velocity = self.velocity - v_forward * 300;
+					break;
+				case 141:
 					if(self.waypointsprite_deployed_personal)
 					{
 						self.speedrunning = TRUE;
@@ -218,57 +307,21 @@
 						self.ammo_shells = 999;
 						self.health = start_health;
 						self.armorvalue = start_armorvalue;
-						self.items |= itemsInMap & (IT_LASER | IT_SHOTGUN | IT_UZI | IT_GRENADE_LAUNCHER | IT_ELECTRO | IT_CRYLINK | IT_NEX | IT_HAGAR | IT_ROCKET_LAUNCHER);
+						self.weapons |= weaponsInMap;
 						self.pauserotarmor_finished = time + cvar("g_balance_pause_armor_rot_spawn");
 						self.pauserothealth_finished = time + cvar("g_balance_pause_health_rot_spawn");
 						self.pauseregen_finished = time + cvar("g_balance_pause_health_regen_spawn");
 					}
+					else if(self.deadflag != DEAD_NO)
+						sprint(self, "UR DEAD AHAHAH))\n");
 					else
 						sprint(self, "No waypoint set, cheater (use g_waypointsprite_personal to set one)\n");
-				}
-				else if(imp == 99)
-				{
-					self.items |= (IT_LASER | IT_UZI | IT_SHOTGUN | IT_GRENADE_LAUNCHER | IT_ELECTRO | IT_CRYLINK | IT_NEX | IT_HAGAR | IT_ROCKET_LAUNCHER);
-					self.ammo_shells = g_pickup_shells_max;
-					self.ammo_nails = g_pickup_nails_max;
-					self.ammo_rockets = g_pickup_rockets_max;
-					self.ammo_cells = g_pickup_cells_max;
-					self.health = g_pickup_healthsmall_max;
-					self.armorvalue = g_pickup_armorsmall_max;
-					self.pauserotarmor_finished = time + cvar("g_balance_pause_armor_rot_spawn");
-					self.pauserothealth_finished = time + cvar("g_balance_pause_health_rot_spawn");
-					self.pauseregen_finished = time + cvar("g_balance_pause_health_regen_spawn");
-					// precache weapon models/sounds
-					local float wep;
-					wep = WEP_FIRST;
-					while (wep <= WEP_LAST)
-					{
-						weapon_action(wep, WR_PRECACHE);
-						wep = wep + 1;
-					}
-				}
-				else if (imp == 13)
-				{
-					makevectors (self.v_angle);
-					self.velocity = self.velocity + v_forward * 300;
-					CopyBody(1);
-					self.lip += 1;
-					self.velocity = self.velocity - v_forward * 300;
-				}
-				else if (imp == 14)
-				{
+					break;
+				case 142:
 					CopyBody(0);
 					self.lip += 1;
-				}
-				else if (imp == 143) // actually: impulse 911
-				{
-					vector start, end, enddown;
-					float i;
-					float m;
-					float good, evil, evilsurf;
-					float maxattempts;
-					vector org, delta;
-
+					break;
+				case 143:
 					good = DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP;
 					evil = DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER;
 					evilsurf = Q3SURFACEFLAG_SKY;
@@ -362,18 +415,43 @@
 						sprint(self, "Emergency teleport could not find a good location, forget it!\n");
 
 					self.dphitcontentsmask = m;
-				}
+					break;
+				case 144:
+					makevectors(self.v_angle);
+					traceline(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * MAX_SHOT_DISTANCE, FALSE, self);
+					if (trace_fraction < 1)
+						printsurfaceinfo(trace_ent, trace_endpos);
+					break;
+				case 145:
+					makevectors(self.v_angle);
+					traceline(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * 65536, FALSE, self);
+					sprint(self, strcat("distance: ", ftos(fabs(vlen(trace_endpos - (self.origin + self.view_ofs)))), "\n"));
+					break;
 			}
 		}
 	}
-
-	if (cvar("g_waypointeditor"))
+	else if(imp >= 103 && imp <= 106)
 	{
-		local entity e;
-		if (imp == 103) waypoint_schedulerelink(waypoint_spawn(self.origin, self.origin, 0));
-		else if (imp == 104) {e = navigation_findnearestwaypoint(self, FALSE);if (e) waypoint_remove(e);}
-		else if (imp == 105) waypoint_schedulerelinkall();
-		else if (imp == 106) waypoint_saveall();
+		if(cvar("g_waypointeditor"))
+		{
+			switch(imp)
+			{
+				case 103:
+					waypoint_schedulerelink(waypoint_spawn(self.origin, self.origin, 0));
+					break;
+				case 104:
+					e = navigation_findnearestwaypoint(self, FALSE);
+					if (e)
+						waypoint_remove(e);
+					break;
+				case 105:
+					waypoint_schedulerelinkall();
+					break;
+				case 106:
+					waypoint_saveall();
+					break;
+			}
+		}
 	}
 
 	//TetrisImpulses(imp);

Modified: branches/nexuiz-2.0/data/qcsrc/server/cl_player.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/cl_player.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/cl_player.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -413,7 +413,7 @@
 					else
 						player_setanim(self.anim_pain2, FALSE, TRUE, TRUE);
 
-					if(deathtype != IT_LASER || attacker != self || self.health < 2 * cvar("g_balance_laser_primary_damage") * cvar("g_balance_selfdamagepercent") + 1)
+					if(deathtype != WEP_LASER || attacker != self || self.health < 2 * cvar("g_balance_laser_primary_damage") * cvar("g_balance_selfdamagepercent") + 1)
 					// exclude pain sounds for laserjumps as long as you aren't REALLY low on health and would die of the next two
 					{
 						if(self.health > 75) // TODO make a "gentle" version?
@@ -504,6 +504,7 @@
 			kh_Key_DropAll(self, FALSE);
 		else
 			kh_Key_DropAll(self, TRUE);
+		Portal_ClearAll(self);
 		if(self.flagcarried)
 			DropFlag(self.flagcarried);
 		// clear waypoints

Modified: branches/nexuiz-2.0/data/qcsrc/server/cl_weapons.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/cl_weapons.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/cl_weapons.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -1,99 +1,233 @@
+// switch between weapons
+void W_SwitchWeapon(float imp)
+{
+	if (self.weapon != imp)
+	if (client_hasweapon(self, imp, TRUE, TRUE))
+		W_SwitchWeapon_Force(self, imp);
+};
 
+float W_GetCycleWeapon(entity pl, string weaponorder, float dir, float imp, float complain)
+{
+	float n, i, weaponwant, first_valid, prev_valid, switchtonext, switchtolast;
+	n = tokenize(weaponorder);
+	switchtonext = switchtolast = 0;
+	first_valid = prev_valid = 0;
+
+	if(dir == 0)
+		switchtonext = 1;
+
+	for(i = 0; i < n; ++i)
+	{
+		weaponwant = stof(argv(i));
+
+		if(imp >= 0)
+			if((get_weaponinfo(weaponwant)).impulse != imp)
+				continue;
+
+		if(client_hasweapon(pl, weaponwant, TRUE, FALSE))
+		{
+			if(switchtonext)
+				return weaponwant;
+			if(!first_valid)
+				first_valid = weaponwant;
+			if(weaponwant == pl.switchweapon)
+			{
+				if(dir >= 0)
+					switchtonext = 1;
+				else if(prev_valid)
+					return prev_valid;
+				else
+					switchtolast = 1;
+			}
+			prev_valid = weaponwant;
+		}
+	}
+	if(first_valid)
+	{
+		if(switchtolast)
+			return prev_valid;
+		else
+			return first_valid;
+	}
+	// complain
+	if(complain)
+	{
+		for(i = 0; i < n; ++i)
+		{
+			weaponwant = stof(argv(i));
+			if(imp >= 0)
+				if((get_weaponinfo(weaponwant)).impulse != imp)
+					continue;
+			client_hasweapon(pl, weaponwant, TRUE, TRUE);
+		}
+	}
+	return 0;
+}
+
+void W_CycleWeapon(string weaponorder, float dir)
+{
+	float w;
+	w = W_GetCycleWeapon(self, weaponorder, dir, -1, 1);
+	if(w > 0)
+		W_SwitchWeapon(w);
+}
+
+void W_NextWeaponOnImpulse(float imp)
+{
+	float w;
+	w = W_GetCycleWeapon(self, self.cvar_cl_weaponpriority, +1, imp, 1);
+	if(w > 0)
+		W_SwitchWeapon(w);
+}
+
+// next weapon
+void W_NextWeapon()
+{
+	W_CycleWeapon(self.cvar_cl_weaponpriority, -1);
+}
+
+// prev weapon
+void W_PreviousWeapon()
+{
+	W_CycleWeapon(self.cvar_cl_weaponpriority, +1);
+}
+
+string W_FixWeaponOrder(string order, float complete)
+{
+	string neworder;
+	float i, n, w;
+
+	n = tokenize(order);
+	for(i = 0; i < n; ++i)
+	{
+		w = stof(argv(i));
+		if(w >= WEP_FIRST && w <= WEP_LAST && w == floor(w))
+			neworder = strcat(neworder, ftos(w), " ");
+	}
+
+	if(complete)
+	{
+		n = tokenize(neworder);
+		for(w = WEP_LAST; w >= WEP_FIRST; --w)
+		{
+			for(i = 0; i < n; ++i)
+				if(stof(argv(i)) == w)
+					break;
+			if(i == n) // not found
+				neworder = strcat(neworder, ftos(w), " ");
+		}
+	}
+	
+	return substring(neworder, 0, strlen(neworder) - 1);
+}
+
+string W_FixWeaponOrder_AllowIncomplete(string order)
+{
+	return W_FixWeaponOrder(order, 0);
+}
+
+string W_FixWeaponOrder_ForceComplete(string order)
+{
+	if(order == "")
+		order = cvar_string("cl_weaponpriority");
+	return W_FixWeaponOrder(order, 1);
+}
+
+float w_getbestweapon(entity e)
+{ 
+	return W_GetCycleWeapon(e, e.cvar_cl_weaponpriority, 0, -1, 0);
+};
+
 // generic weapons table
-// add new weapons here
+// TODO should they be macros instead?
 float weapon_action(float wpn, float wrequest)
 {
-	if (wpn == WEP_LASER)
-		return w_laser(wrequest);
-	else if (wpn == WEP_SHOTGUN)
-		return w_shotgun(wrequest);
-	else if (wpn == WEP_UZI)
-		return w_uzi(wrequest);
-	else if (wpn == WEP_GRENADE_LAUNCHER)
-		return w_glauncher(wrequest);
-	else if (wpn == WEP_ELECTRO)
-		return w_electro(wrequest);
-	else if (wpn == WEP_CRYLINK)
-		return w_crylink(wrequest);
-	else if (wpn == WEP_NEX)
-		return w_nex(wrequest);
-	else if (wpn == WEP_HAGAR)
-		return w_hagar(wrequest);
-	else if (wpn == WEP_ROCKET_LAUNCHER)
-		return w_rlauncher(wrequest);
-	return FALSE;
+	return (get_weaponinfo(wpn)).weapon_func(wrequest);
 };
 
 string W_Name(float weaponid)
 {
-	if(weaponid == WEP_LASER)             return "Laser";
-	if(weaponid == WEP_UZI)               return "Machine Gun";
-	if(weaponid == WEP_SHOTGUN)           return "Shotgun";
-	if(weaponid == WEP_GRENADE_LAUNCHER)  return "Mortar";
-	if(weaponid == WEP_ELECTRO)           return "Electro";
-	if(weaponid == WEP_NEX)               return "Nex";
-	if(weaponid == WEP_HAGAR)             return "Hagar";
-	if(weaponid == WEP_ROCKET_LAUNCHER)   return "Rocket Launcher";
-	if(weaponid == WEP_CRYLINK)           return "Crylink";
-	return "@!#%'n Tuba";
+	return (get_weaponinfo(weaponid)).message;
 }
 
-float W_ItemCode(float wpn)
+float W_WeaponBit(float wpn)
 {
-	switch(wpn)
-	{
-		case WEP_LASER:            return IT_LASER;
-		case WEP_SHOTGUN:          return IT_SHOTGUN;
-		case WEP_UZI:              return IT_UZI;
-		case WEP_GRENADE_LAUNCHER: return IT_GRENADE_LAUNCHER;
-		case WEP_ELECTRO:          return IT_ELECTRO;
-		case WEP_CRYLINK:          return IT_CRYLINK;
-		case WEP_NEX:              return IT_NEX;
-		case WEP_HAGAR:            return IT_HAGAR;
-		case WEP_ROCKET_LAUNCHER:  return IT_ROCKET_LAUNCHER;
-		default:                   return 0;
-	}
+	return (get_weaponinfo(wpn)).weapons;
 }
 
 float W_AmmoItemCode(float wpn)
 {
-	switch(wpn)
-	{
-		case WEP_SHOTGUN:          return IT_SHELLS;
-		case WEP_UZI:              return IT_NAILS;
-		case WEP_GRENADE_LAUNCHER: return IT_ROCKETS;
-		case WEP_ELECTRO:          return IT_CELLS;
-		case WEP_CRYLINK:          return IT_CELLS;
-		case WEP_NEX:              return IT_CELLS;
-		case WEP_HAGAR:            return IT_ROCKETS;
-		case WEP_ROCKET_LAUNCHER:  return IT_ROCKETS;
-		default:                   return 0;
-	}
+	return (get_weaponinfo(wpn)).items;
 }
 
-// think function for tossed weapons
 void thrown_wep_think()
 {
 	self.solid = SOLID_TRIGGER;
 	self.owner = world;
 	SUB_SetFade(self, time + 20, 1);
-	setorigin(self, self.origin);
-};
+}
 
+// returns amount of ammo used, or -1 for failure, or 0 for no ammo count
+float W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo)
+{
+	entity oldself, wep;
+	float wa, ammo;
+	var .float ammofield;
+
+	wep = spawn();
+
+	setorigin(wep, org);
+	wep.classname = "droppedweapon";
+	wep.velocity = velo;
+	wep.owner = wep.enemy = own;
+	wep.classname = "droppedweapon";
+	wep.flags = wep.flags | FL_TOSSED;
+	wep.colormap = own.colormap;
+
+	wa = W_AmmoItemCode(wpn);
+	if(wa == IT_SUPERWEAPON || wa == 0)
+	{
+		oldself = self;
+		self = wep;
+		weapon_defaultspawnfunc(wpn);
+		self = oldself;
+		if(startitem_failed)
+			return -1;
+		wep.think = thrown_wep_think;
+		wep.nextthink = time + 0.5;
+		return 0;
+	}
+	else
+	{
+		ammofield = Item_CounterField(wa);
+		oldself = self;
+		self = wep;
+		weapon_defaultspawnfunc(wpn);
+		self = oldself;
+		if(startitem_failed)
+			return -1;
+		if(doreduce)
+		{
+			ammo = min(own.ammofield, wep.ammofield);
+			wep.ammofield = ammo;
+			own.ammofield -= ammo;
+		}
+		wep.think = thrown_wep_think;
+		wep.nextthink = time + 0.5;
+		return wep.ammofield;
+	}
+}
+
 // toss current weapon
 void W_ThrowWeapon(vector velo, vector delta, float doreduce)
 {
-	local float w, ammo;
-	local entity wep, e;
-	local .float ammofield;
+	local float w, a, wb;
 
 	w = self.weapon;
 	if (w == 0)
 		return; // just in case
 	if (w == WEP_LASER)
-		return;
-	if (g_instagib)
-		return;
+		return; // just in case
 	if (g_rocketarena)
 		return;
 	if (g_lms)
@@ -103,120 +237,21 @@
 	if (!cvar("g_pickup_items"))
 		return;
 
-	e = self;
-	wep = spawn();
-	self = wep;
-
-	setorigin(wep, e.origin + delta);
-	makevectors(e.angles);
-	wep.classname = "droppedweapon";
-	wep.velocity = velo; // e.velocity * 0.5 + v_forward * 750;
-	SUB_SetFade(wep, time + 20, 1);
-
-	ammofield = Item_CounterField(W_AmmoItemCode(w));
-	w = W_ItemCode(w);
-	if(!(e.items & w))
+	wb = W_WeaponBit(w);
+	self.weapons = self.weapons - (self.weapons & wb);
+	W_SwitchWeapon_Force(self, w_getbestweapon(self));
+	a = W_ThrowNewWeapon(self, w, doreduce, self.origin + delta, velo);
+	if(a < 0)
+		return;
+	if(self.health >= 1)
 	{
-		remove(wep);
-		goto leave;
+		if(a == 0)
+			sprint(self, strcat("You dropped the ^2", W_Name(w), "\n"));
+		else
+			sprint(self, strcat("You dropped the ^2", W_Name(w), " with ", ftos(a), " ", Item_CounterFieldName(W_AmmoItemCode(w)), "\n"));
 	}
-	Item_SpawnByItemCode(w);
-	if(startitem_failed)
-		goto leave;
-	if(doreduce)
-	{
-		ammo = min(e.ammofield, wep.ammofield);
-		wep.ammofield = ammo;
-		e.ammofield -= ammo;
-	}
-
-	if(e.items & w)
-		if(e.health >= 1)
-			sprint(e, strcat("You dropped the ^2", wep.netname, " with ", ftos(wep.ammofield), " ammo", "\n"));
-
-	wep.owner = e;
-	setorigin(wep, wep.origin);
-	wep.nextthink = time + 0.5;
-	wep.think = thrown_wep_think;
-	wep.classname = "droppedweapon";
-	wep.flags = wep.flags | FL_TOSSED;
-	e.items = e.items - (e.items & w);
-	e.switchweapon = w_getbestweapon(e);
-	wep.colormap = e.colormap;
-	if (e.switchweapon != e.weapon)
-		e.cnt = e.weapon;
-
-:leave
-	self = e;
 };
 
-// switch between weapons
-void W_SwitchWeapon(float imp)
-{
-	if (self.weapon != imp)
-	if (client_hasweapon(self, imp, TRUE, TRUE))
-	{
-		self.cnt = self.weapon ? self.weapon : self.switchweapon;
-		self.switchweapon = imp;
-	}
-};
-
-// next weapon
-void W_NextWeapon()
-{
-	local float weaponwant, maxtries;
-
-	maxtries = WEP_LAST;
-
-	weaponwant = self.switchweapon + 1;
-	if (weaponwant < WEP_FIRST)
-		weaponwant = WEP_LAST;
-	if (weaponwant > WEP_LAST)
-		weaponwant = WEP_FIRST;
-	while(!client_hasweapon(self, weaponwant, TRUE, FALSE))
-	{
-		if(!maxtries)
-			return;
-
-		maxtries -= 1;
-		weaponwant = weaponwant + 1;
-		if (weaponwant < WEP_FIRST)
-			weaponwant = WEP_LAST;
-		if (weaponwant > WEP_LAST)
-			weaponwant = WEP_FIRST;
-	}
-	self.cnt = self.weapon ? self.weapon : self.switchweapon;
-	self.switchweapon = weaponwant;
-};
-
-// prev weapon
-void W_PreviousWeapon()
-{
-	local float weaponwant, maxtries;
-
-	maxtries = WEP_LAST;
-
-	weaponwant = self.switchweapon - 1;
-	if (weaponwant < WEP_FIRST)
-		weaponwant = WEP_LAST;
-	if (weaponwant > WEP_LAST)
-		weaponwant = WEP_FIRST;
-	while(!client_hasweapon(self, weaponwant, TRUE, FALSE))
-	{
-		if(!maxtries)
-			return;
-
-		maxtries -= 1;
-		weaponwant = weaponwant - 1;
-		if (weaponwant < WEP_FIRST)
-			weaponwant = WEP_LAST;
-		if (weaponwant > WEP_LAST)
-			weaponwant = WEP_FIRST;
-	}
-	self.cnt = self.weapon ? self.weapon : self.switchweapon;
-	self.switchweapon = weaponwant;
-};
-
 // Bringed back weapon frame
 void W_WeaponFrame()
 {
@@ -250,8 +285,8 @@
 		}
 		else if (self.weaponentity.state == WS_READY)
 		{
-#ifdef INDEPENDENT_ATTACK_FINISHED
-			if(ATTACK_FINISHED(self) > time + frametime * 0.5)
+#ifndef INDEPENDENT_ATTACK_FINISHED
+			if(ATTACK_FINISHED(self) <= time + frametime * 0.5)
 			{
 #endif
 			sound (self, CHAN_WEAPON, "weapons/weapon_switch.wav", VOL_BASE, ATTN_NORM);
@@ -259,12 +294,15 @@
 			// set up weapon switch think in the future, and start drop anim
 			weapon_thinkf(WFRAME_IDLE, cvar("g_balance_weaponswitchdelay"), w_clear);
 			weapon_boblayer1(PLAYER_WEAPONSELECTION_SPEED, PLAYER_WEAPONSELECTION_RANGE);
-#ifdef INDEPENDENT_ATTACK_FINISHED
+#ifndef INDEPENDENT_ATTACK_FINISHED
 			}
 #endif
 		}
 	}
 
+	float wb;
+	wb = W_WeaponBit(self.weapon);
+
 	// call the think code which may fire the weapon
 	// and do so multiple times to resolve framerate dependency issues if the
 	// server framerate is very low and the weapon fire rate very high
@@ -273,7 +311,13 @@
 	while (c < 5)
 	{
 		c = c + 1;
-		weapon_action(self.weapon, WR_THINK);
+		if(wb && self.weapons & wb == 0)
+		{
+			W_SwitchWeapon_Force(self, w_getbestweapon(self));
+			wb = 0;
+		}
+		if(wb)
+			weapon_action(self.weapon, WR_THINK);
 		if (time + frametime * 0.5 >= self.weapon_nextthink)
 			self.weapon_think();
 	}
@@ -355,20 +399,20 @@
 		if(nixnex_nextchange != self.nixnex_lastchange_id) // this shall only be called once per round!
 		{
 			self.nixnex_lastchange_id = nixnex_nextchange;
-			if (cvar("g_use_ammunition"))
+			if (self.items & IT_UNLIMITED_AMMO)
 			{
+				self.ammo_shells = cvar("g_pickup_shells_max");
+				self.ammo_nails = cvar("g_pickup_nails_max");
+				self.ammo_rockets = cvar("g_pickup_rockets_max");
+				self.ammo_cells = cvar("g_pickup_cells_max");
+			}
+			else
+			{
 				self.ammo_shells = cvar("g_balance_nixnex_ammo_shells");
 				self.ammo_nails = cvar("g_balance_nixnex_ammo_nails");
 				self.ammo_rockets = cvar("g_balance_nixnex_ammo_rockets");
 				self.ammo_cells = cvar("g_balance_nixnex_ammo_cells");
 			}
-			else
-			{
-				self.ammo_shells = cvar("g_pickup_shells_max");
-				self.ammo_nails = cvar("g_pickup_nails_max");
-				self.ammo_rockets = cvar("g_pickup_rockets_max");
-				self.ammo_cells = cvar("g_pickup_cells_max");
-			}
 			self.nixnex_nextincr = time + cvar("g_balance_nixnex_incrtime");
 			if(dt >= 1 && dt <= 5)
 				self.nixnex_lastinfotime = -42;
@@ -382,7 +426,7 @@
 				centerprint(self, strcat("^3", ftos(dt), "^2 seconds until weapon change...\n\nNext weapon: ^3", W_Name(nixnex_nextweapon), "\n"));
 		}
 
-		if(cvar("g_use_ammunition") && time > self.nixnex_nextincr)
+		if(!(self.items & IT_UNLIMITED_AMMO) && time > self.nixnex_nextincr)
 		{
 			self.ammo_shells = self.ammo_shells + cvar("g_balance_nixnex_ammoincr_shells");
 			self.ammo_nails = self.ammo_nails + cvar("g_balance_nixnex_ammoincr_nails");
@@ -391,10 +435,10 @@
 			self.nixnex_nextincr = time + cvar("g_balance_nixnex_incrtime");
 		}
 
-		self.items = self.items - (self.items & (IT_LASER | IT_SHOTGUN | IT_UZI | IT_GRENADE_LAUNCHER | IT_ELECTRO | IT_CRYLINK | IT_NEX | IT_HAGAR | IT_ROCKET_LAUNCHER));
+		self.weapons = 0;
 		if(g_nixnex_with_laser)
-			self.items = self.items | IT_LASER;
-		self.items = self.items | W_ItemCode(nixnex_weapon);
+			self.weapons = self.weapons | WEPBIT_LASER;
+		self.weapons = self.weapons | W_WeaponBit(nixnex_weapon);
 
 		if(self.switchweapon != nixnex_weapon)
 			if(!client_hasweapon(self, self.switchweapon, TRUE, FALSE))
@@ -403,3 +447,19 @@
 	}
 }
 
+void RegisterWeapons()
+{
+	register_dummy_weapon();
+	// %weaponaddpoint
+	register_weapon(WEP_LASER,            w_laser,     0,              1, 1,     0, "laser",     "laser",           "Laser");
+	register_weapon(WEP_SHOTGUN,          w_shotgun,   IT_SHELLS,      2, 1,  2500, "shotgun",   "shotgun",         "Shotgun");
+	register_weapon(WEP_UZI,              w_uzi,       IT_NAILS,       3, 1,  5000, "uzi",       "uzi",             "Machine Gun");
+	register_weapon(WEP_GRENADE_LAUNCHER, w_glauncher, IT_ROCKETS,     4, 1,  5000, "gl",        "grenadelauncher", "Mortar");
+	register_weapon(WEP_ELECTRO,          w_electro,   IT_CELLS,       5, 1,  5000, "electro",   "electro",         "Electro");
+	register_weapon(WEP_CRYLINK,          w_crylink,   IT_CELLS,       6, 1,  5000, "crylink",   "crylink",         "Crylink");
+	register_weapon(WEP_NEX,              w_nex,       IT_CELLS,       7, 1, 10000, "nex",       "nex",             "Nex");
+	register_weapon(WEP_HAGAR,            w_hagar,     IT_ROCKETS,     8, 1,  5000, "hagar",     "hagar",           "Hagar");
+	register_weapon(WEP_ROCKET_LAUNCHER,  w_rlauncher, IT_ROCKETS,     9, 1, 10000, "rl",        "rocketlauncher",  "Rocket Launcher");
+	register_weapon(WEP_PORTO,            w_porto,     IT_SUPERWEAPON, 0, 0,     0, "porto" ,    "porto",           "Port-O-Launch");
+	register_weapon(WEP_MINSTANEX,        w_minstanex, IT_CELLS,       7, 0, 10000, "minstanex", "minstanex",       "MinstaNex");
+}

Modified: branches/nexuiz-2.0/data/qcsrc/server/cl_weaponsystem.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/cl_weaponsystem.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/cl_weaponsystem.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -7,6 +7,12 @@
 ===========================================================================
 */
 
+void W_SwitchWeapon_Force(entity e, float w)
+{
+	e.cnt = e.weapon ? e.weapon : e.switchweapon;
+	e.switchweapon = w;
+}
+
 .float antilag_debug;
 
 // VorteX: static frame globals
@@ -324,7 +330,7 @@
 
 float client_hasweapon(entity cl, float wpn, float andammo, float complain)
 {
-	local float itemcode, f;
+	local float weaponbit, f;
 	local entity oldself;
 
 	if (wpn < WEP_FIRST || wpn > WEP_LAST)
@@ -333,8 +339,8 @@
 			sprint(self, "Invalid weapon\n");
 		return FALSE;
 	}
-	itemcode = W_ItemCode(wpn);
-	if (cl.items & itemcode)
+	weaponbit = W_WeaponBit(wpn);
+	if (cl.weapons & weaponbit)
 	{
 		if (andammo)
 		{
@@ -346,7 +352,7 @@
 			if (!f)
 			{
 				if (complain)
-					sprint(self, "You don't have any ammo for that weapon\n");
+					sprint(cl, strcat("You don't have any ammo for the ^2", W_Name(wpn), "\n"));
 				return FALSE;
 			}
 		}
@@ -356,10 +362,10 @@
 	{
 		// DRESK - 3/16/07
 		// Report Proper Weapon Status / Modified Weapon Ownership Message
-		if(itemsInMap & itemcode)
-			sprint(self, strcat("You do not have the ^2", W_Name(wpn), "\n") );
+		if(weaponsInMap & weaponbit)
+			sprint(cl, strcat("You do not have the ^2", W_Name(wpn), "\n") );
 		else
-			sprint(self, strcat("The ^2", W_Name(wpn), "^7 is ^1NOT AVAILABLE^7 in this map\n") );
+			sprint(cl, strcat("The ^2", W_Name(wpn), "^7 is ^1NOT AVAILABLE^7 in this map\n") );
 	}
 	return FALSE;
 };
@@ -384,41 +390,17 @@
 	weapon_thinkf(WFRAME_IDLE, 1000000, w_ready);
 };
 
-// FIXME: add qw-style client-custom weaponrating (cl_weaponrating)?
-float w_getbestweapon(entity e)
-{ 
-// add new weapons here
-	if (client_hasweapon(e, WEP_ROCKET_LAUNCHER, TRUE, FALSE))
-		return WEP_ROCKET_LAUNCHER;
-	else if (client_hasweapon(e, WEP_NEX, TRUE, FALSE))
-		return WEP_NEX;
-	else if (client_hasweapon(e, WEP_HAGAR, TRUE, FALSE))
-		return WEP_HAGAR;
-	else if (client_hasweapon(e, WEP_GRENADE_LAUNCHER, TRUE, FALSE))
-		return WEP_GRENADE_LAUNCHER;
-	else if (client_hasweapon(e, WEP_ELECTRO, TRUE, FALSE))
-		return WEP_ELECTRO;
-	else if (client_hasweapon(e, WEP_CRYLINK, TRUE, FALSE))
-		return WEP_CRYLINK;
-	else if (client_hasweapon(e, WEP_UZI, TRUE, FALSE))
-		return WEP_UZI;
-	else if (client_hasweapon(e, WEP_SHOTGUN, TRUE, FALSE))
-		return WEP_SHOTGUN;
-	else if (client_hasweapon(e, WEP_LASER, FALSE, FALSE))
-		return WEP_LASER;
-	else
-		return 0;
-};
-
 // Setup weapon for client (after this raise frame will be launched)
-void weapon_setup(float windex, string wname, float hudammo)
+void weapon_setup(float windex)
 {
-	self.items = self.items - (self.items & (IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS));
-	self.items = self.items | hudammo;
+	entity e;
+	e = get_weaponinfo(windex);
+	self.items = self.items - (self.items & IT_AMMO);
+	self.items = self.items | e.items;
 
 	// the two weapon entities will notice this has changed and update their models
 	self.weapon = windex;
-	self.weaponname = wname;
+	self.weaponname = e.mdl;
 };
 
 // perform weapon to attack (weaponstate and attack_finished check is here)
@@ -434,9 +416,7 @@
 	
 	if (!weapon_action(self.weapon, WR_CHECKAMMO1 + secondary))
 	{
-		self.switchweapon = w_getbestweapon(self);
-		if (self.switchweapon != self.weapon)
-			self.cnt = self.weapon;
+		W_SwitchWeapon_Force(self, w_getbestweapon(self));
 		return FALSE;
 	}
 
@@ -603,27 +583,52 @@
 	missile.velocity = W_CalculateProjectileVelocity(missile.owner.velocity, missile.velocity);
 }
 
-void weapon_register(float wepcode, float minammo)
+
+// WEAPON PLUGIN SYSTEM
+entity weapon_info[24];
+entity dummy_weapon_info;
+.float(float) weapon_func;
+void register_weapon(float id, float(float) func, float ammotype, float i, float normalweapon, float pickupbasevalue, string modelname, string shortname, string name)
 {
-	string s;
-	float itemcode;
-	s = strcat("register_bestweapon ", ftos(wepcode), " "); // char for bestweapon
-	s = strcat(s, ftos(wepcode), " "); // impulse
-	s = strcat(s, ftos(W_ItemCode(wepcode)), " "); // item code
-	s = strcat(s, ftos(wepcode), " "); // self.weapon code
-
-	// ammo stat
-	itemcode = W_AmmoItemCode(wepcode);
-	if(itemcode == IT_SHELLS)
-		s = strcat(s, "6 ");
-	else if(itemcode == IT_NAILS)
-		s = strcat(s, "7 ");
-	else if(itemcode == IT_ROCKETS)
-		s = strcat(s, "8 ");
-	else // if(itemcode == IT_CELLS)
-		s = strcat(s, "9 ");
-
-	s = strcat(s, ftos(minammo), "\n");
-	//dprint(s);
-	stuffcmd(self, s);
+	entity e;
+	weapon_info[id - 1] = e = spawn();
+	e.classname = "weapon_info";
+	e.weapon = id;
+	e.weapons = power2of(id - 1);
+	e.netname = shortname;
+	e.message = name;
+	e.items = ammotype;
+	e.weapon_func = func;
+	e.mdl = modelname;
+	e.model = strzone(strcat("models/weapons/g_", modelname, ".md3"));
+	e.spawnflags = normalweapon;
+	e.impulse = i;
+	e.bot_pickupbasevalue = pickupbasevalue;
 }
+float w_null(float dummy)
+{
+	return 0;
+}
+void register_dummy_weapon()
+{
+	dummy_weapon_info = spawn();
+	dummy_weapon_info.classname = "weapon_info";
+	dummy_weapon_info.weapon = 0;
+	dummy_weapon_info.weapons = 0;
+	dummy_weapon_info.netname = "@!#%'n Tuba";
+	dummy_weapon_info.items = 0;
+	dummy_weapon_info.weapon_func = w_null;
+	dummy_weapon_info.mdl = "";
+	dummy_weapon_info.model = "";
+	dummy_weapon_info.spawnflags = 0;
+	dummy_weapon_info.impulse = -1;
+	dummy_weapon_info.bot_pickupbasevalue = 0;
+}
+entity get_weaponinfo(float id)
+{
+	entity w;
+	w = weapon_info[id - 1];
+	if(w)
+		return w;
+	return dummy_weapon_info;
+}

Modified: branches/nexuiz-2.0/data/qcsrc/server/clientcommands.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/clientcommands.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/clientcommands.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -265,7 +265,8 @@
 	} else if(argv(0) == "ready") {
 		if not(self.flags & FL_CLIENT)
 			return;
-		if(cvar("sv_ready_restart"))
+		if((g_tourney && 0 < g_tourney_warmup_limit) // with unlimited warmup players have to be able to restart
+		   || cvar("sv_ready_restart"))
 		{
 			if(timeoutStatus) {
 				return sprint(self, "^1You cannot reset the game while a timeout is active!\n");
@@ -401,7 +402,8 @@
 	if(g_tourney) {
 		tourneyInMatchStage = 1; //once the game is restarted the game is in match stage
 		//reset weapons and ammo, health and armor to default:
-		start_items = IT_LASER | IT_SHOTGUN;
+		// TODO is this really the right way?!? see miscfunction.qc readlevelcvars()
+		start_weapons = WEPBIT_LASER | WEPBIT_SHOTGUN;
 		start_switchweapon = WEP_SHOTGUN;
 		start_ammo_shells = cvar("g_start_ammo_shells");
 		start_ammo_nails = cvar("g_start_ammo_nails");
@@ -413,11 +415,11 @@
 	restart_countdown = time + RESTART_COUNTDOWN;
 	restart_mapalreadyrestarted = 0; //reset this var, needed when cvar sv_ready_restart_repeatable is in use
 	//reset the .ready status of all players (also spectators)
-	FOR_EACH_CLIENT(e)
+	FOR_EACH_CLIENTSLOT(e)
 	{
 		e.ready = 0;
 	}
-	if(0<cvar("timelimit") || (g_tourney && cvar("g_tourney_warmup_unlimited_time")) )
+	if(0<cvar("timelimit") || (g_tourney && (-1 == g_tourney_warmup_limit)) )
 	{
 		// remember original timelimit on first restart
 		if(!timelimit_orig)
@@ -472,6 +474,8 @@
 	local entity e;
 	local float r, p;
 
+	r = p = 0;
+
 	FOR_EACH_REALPLAYER(e)
 	{
 		p += 1;

Modified: branches/nexuiz-2.0/data/qcsrc/server/constants.qh
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/constants.qh	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/constants.qh	2008-09-05 19:40:34 UTC (rev 4365)
@@ -154,43 +154,6 @@
 float	DEATH_MIRRORDAMAGE		= 10014;
 float   DEATH_TURRET            = 10100;
 
-
-
-
-float	IT_LASER				= 4096;
-float	IT_SHOTGUN				= 1;
-float	IT_UZI					= 2;
-float	IT_GRENADE_LAUNCHER			= 4;
-float	IT_ELECTRO				= 8;
-float	IT_CRYLINK				= 16;
-float	IT_NEX					= 32;
-float	IT_HAGAR				= 64;
-float	IT_ROCKET_LAUNCHER			= 128;
-
-float	IT_SHELLS				= 256;
-float	IT_NAILS				= 512;
-float	IT_ROCKETS				= 1024;
-float	IT_CELLS				= 2048;
-
-float	IT_STRENGTH				= 8192;
-float	IT_INVINCIBLE				= 16384;
-float	IT_HEALTH				= 32768;
-
-float	IT_KEY1					= 131072;
-float	IT_KEY2					= 262144;
-
-float	IT_RED_FLAG_TAKEN			= 32768;
-float	IT_RED_FLAG_LOST			= 65536;
-float	IT_RED_FLAG_CARRING			= 98304;
-float	IT_BLUE_FLAG_TAKEN			= 131072;
-float	IT_BLUE_FLAG_LOST			= 262144;
-float	IT_BLUE_FLAG_CARRING			= 393216;
-
-float	IT_5HP					= 524288;
-float	IT_25HP					= 1048576;
-float	IT_ARMOR_SHARD				= 2097152;
-float	IT_ARMOR				= 4194304;
-
 vector	PL_VIEW_OFS				= '0 0 35';
 vector	PL_MIN					= '-16 -16 -24';
 vector	PL_MAX					= '16 16 45';

Modified: branches/nexuiz-2.0/data/qcsrc/server/defs.qh
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/defs.qh	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/defs.qh	2008-09-05 19:40:34 UTC (rev 4365)
@@ -17,11 +17,16 @@
 // Globals
 
 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_cloaked, g_footsteps, g_jump_grunt, 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_cloaked, g_footsteps, g_jump_grunt, g_grappling_hook, 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_tourney_warmup_limit;
 float g_ctf_win_mode;
 float g_race_qualifying;
 float tourneyInMatchStage;
+float g_pickup_respawntime_short;
+float g_pickup_respawntime_medium;
+float g_pickup_respawntime_long;
+float g_pickup_respawntime_powerup;
 
 float sv_cheats;
 float sv_gentle;
@@ -196,29 +201,19 @@
 float WS_READY			= 4; // idle frame
 
 // weapon requests
-float WR_SETUP		  = 1; // setup weapon data
-float WR_THINK		  = 2; // logic to run every frame
-float WR_CHECKAMMO1	  = 3; // checks ammo for weapon
-float WR_CHECKAMMO2	  = 4; // checks ammo for weapon
-float WR_AIM		  = 5; // runs bot aiming code for this weapon
-float WR_PRECACHE	  = 6; // precaches models/sounds used by this weapon
-float WR_REGISTER	  = 7; // send data about the weapon to the client self (for ClientConnect)
+float WR_SETUP		    = 1; // setup weapon data
+float WR_THINK		    = 2; // logic to run every frame
+float WR_CHECKAMMO1	    = 3; // checks ammo for weapon
+float WR_CHECKAMMO2	    = 4; // checks ammo for weapon
+float WR_AIM		    = 5; // runs bot aiming code for this weapon
+float WR_PRECACHE	    = 6; // precaches models/sounds used by this weapon
+float WR_SUICIDEMESSAGE = 7; // sets w_deathtypestring or leaves it alone
+float WR_KILLMESSAGE    = 8; // sets w_deathtypestring or leaves it alone
 
-// Weapon indexes
-float WEP_LASER				= 1; // float	IT_LASER				= 4096;
-float WEP_SHOTGUN			= 2; // float	IT_SHOTGUN				= 1;
-float WEP_UZI				= 3; // float	IT_UZI					= 2;
-float WEP_GRENADE_LAUNCHER	= 4; // float	IT_GRENADE_LAUNCHER		= 4;
-float WEP_ELECTRO			= 5; // float	IT_ELECTRO				= 8;
-float WEP_CRYLINK			= 6; // float	IT_CRYLINK				= 16;
-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;
-// For weapon cycling commands
-float WEP_FIRST				= 1;
-float WEP_LAST				= 9;
-float WEP_COUNT             = 10;
+void weapon_defaultspawnfunc(float wpn);
 
+string w_deathtypestring;
+
 void(entity client, string s) centerprint_builtin = #73;
 .vector dest1, dest2;
 
@@ -340,14 +335,14 @@
 
 .float() customizeentityforclient;
 .float cvar_cl_handicap;
-.float cvar_cl_zoomfactor;
-.float cvar_cl_zoomspeed;
 .float cvar_cl_playerdetailreduction;
 .float cvar_cl_nogibs;
 .float cvar_scr_centertime;
 .float cvar_cl_shownames;
 .float cvar_cl_hidewaypoints;
 .string cvar_g_nexuizversion;
+.string cvar_cl_weaponpriority;
+.string cvar_cl_weaponpriorities[10];
 
 .float version_nagtime;
 
@@ -372,11 +367,9 @@
 
 typedef .float floatfield;
 floatfield Item_CounterField(float it);
-float Item_WeaponCode(float it);
-void Item_SpawnByItemCode(float it);
 
 float W_AmmoItemCode(float wpn);
-float W_ItemCode(float wpn);
+float W_WeaponBit(float wpn);
 string W_Name(float weaponid);
 
 void UpdateSelectedPlayer();
@@ -391,7 +384,7 @@
 void FixIntermissionClient(entity e);
 void FixClientCvars(entity e);
 
-float itemsInMap;
+float weaponsInMap;
 
 void centerprint_atprio(entity e, float prio, string s);
 void centerprint_expire(entity e, float prio);
@@ -494,3 +487,4 @@
 string clientstuff;
 .float stat_sys_ticrate;
 .float phase;
+.float weapons;

Modified: branches/nexuiz-2.0/data/qcsrc/server/g_damage.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/g_damage.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/g_damage.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -105,7 +105,7 @@
 	//	w = player.switchweapon;
 	if(w == 0)
 		w = player.cnt; // previous weapon!
-	s = strcat(s, ftos(W_ItemCode(w)));
+	s = strcat(s, ftos(w));
 	if(time < player.strength_finished)
 		s = strcat(s, "S");
 	if(time < player.invincible_finished)
@@ -143,6 +143,7 @@
 void Obituary (entity attacker, entity targ, float deathtype)
 {
 	string	s, a;
+	float p;
 
 	if (targ.classname == "player" || targ.classname == "corpse")
 	{
@@ -202,13 +203,12 @@
 				if (targ.killcount > 2)
 					bprint ("^1",s,"^1 ended it all with a ",ftos(targ.killcount)," scoring spree\n");
 			} else {
-
-				if (deathtype == IT_GRENADE_LAUNCHER)
-					bprint ("^1",s, "^1 detonated\n");
-				else if (deathtype == IT_ELECTRO)
-					bprint ("^1",s, "^1 played with plasma\n");
-				else if (deathtype == IT_ROCKET_LAUNCHER)
-					bprint ("^1",s, "^1 exploded\n");
+				if(deathtype >= WEP_FIRST && deathtype <= WEP_LAST)
+				{
+					w_deathtypestring = "couldn't resist the urge to self-destruct";
+					weapon_action(deathtype, WR_SUICIDEMESSAGE);
+					bprint("^1", s, "^1 ", w_deathtypestring, "\n");
+				}
 				else if (deathtype == DEATH_KILL)
 					bprint ("^1",s, "^1 couldn't take it anymore\n");
 				else if (deathtype == DEATH_ROT)
@@ -283,24 +283,16 @@
 				if(sv_gentle) {
 					bprint ("^1",s, "^1 needs a restart thanks to ", a, "\n");
 				} else {
-					if (deathtype == IT_LASER)
-						bprint ("^1",s, "^1 was blasted by ", a, "\n");
-					else if (deathtype == IT_UZI)
-						bprint ("^1",s, "^1 was riddled full of holes by ", a, "\n");
-					else if (deathtype == IT_SHOTGUN)
-						bprint ("^1",s, "^1 was gunned by ", a, "\n");
-					else if (deathtype == IT_GRENADE_LAUNCHER)
-						bprint ("^1", s, "^1 was blasted by ", a, "\n");
-					else if (deathtype == IT_ELECTRO)
-						bprint ("^1",s, "^1 was blasted by ", a, "\n");
-					else if (deathtype == IT_CRYLINK)
-						bprint ("^1",s, "^1 was blasted by ", a, "\n");
-					else if (deathtype == IT_NEX)
-						bprint ("^1",s, "^1 has been vaporized by ", a, "\n");
-					else if (deathtype == IT_HAGAR)
-						bprint ("^1",s, "^1 was pummeled by ", a, "\n");
-					else if (deathtype == IT_ROCKET_LAUNCHER)
-						bprint ("^1",s, "^1 was blasted by ", a, "\n");
+					if(deathtype >= WEP_FIRST && deathtype <= WEP_LAST)
+					{
+						w_deathtypestring = "was blasted by";
+						weapon_action(deathtype, WR_KILLMESSAGE);
+						p = strstrofs(w_deathtypestring, "#", 0);
+						if(p < 0)
+							bprint("^1", s, "^1 ", w_deathtypestring, " ", a, "\n");
+						else
+							bprint("^1", s, "^1 ", substring(w_deathtypestring, 0, p), a, "^1", substring(w_deathtypestring, p+1, strlen(w_deathtypestring) - (p+1)), "\n");
+					}
 					else if (deathtype == DEATH_TELEFRAG)
 						bprint ("^1",s, "^1 was telefragged by ", a, "\n");
 					else if (deathtype == DEATH_DROWN)
@@ -572,20 +564,20 @@
 				(deathtype == DEATH_SLIME) ||
 				(deathtype == DEATH_LAVA))
 				return;
-			if (targ.armorvalue && (deathtype == IT_NEX) && damage)
+			if (targ.armorvalue && (deathtype == WEP_MINSTANEX) && damage)
 			{
 				targ.armorvalue -= 1;
 				centerprint(targ, strcat("^3Remaining extra lives: ",ftos(targ.armorvalue),"\n"));
 				damage = 0;
 				targ.hitsound += 1;
 			}
-			else if (deathtype == IT_NEX && targ.items & IT_STRENGTH)
+			else if (deathtype == WEP_MINSTANEX && targ.items & IT_STRENGTH)
 			{
 				if(clienttype(attacker) == CLIENTTYPE_REAL)
 					if(IsDifferentTeam(targ, attacker))
 						yoda = 1;
 			}
-			if (deathtype == IT_LASER)
+			if (deathtype == WEP_LASER)
 			{
 				damage = 0;
 				if (targ != attacker)
@@ -671,7 +663,6 @@
 		// Savage: vampire mode
 		if (g_vampire)
 		if (!g_minstagib)
-		if (!g_instagib)
 		if (time > self.spawnshieldtime)
 		{
 			attacker.health += damage;

Modified: branches/nexuiz-2.0/data/qcsrc/server/g_hook.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/g_hook.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/g_hook.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -130,6 +130,7 @@
 	makevectors(self.owner.v_angle);
 	org = self.owner.origin + self.owner.view_ofs + v_forward * 8 - v_right * 8 + v_up * -12;
 
+#if 0
 	tracebox(org, self.mins, self.maxs, self.origin, MOVE_NOMONSTERS, self.owner);
 	// do not hit players with this, as they tend to get in the way just too often
 	// NOTE: this assumes sky brushes cannot get in the way
@@ -152,6 +153,7 @@
 			if(trace_ent.movetype != MOVETYPE_NONE)
 				SetMovetypeFollow(self, trace_ent);
 	}
+#endif
 
 	if(self.rope_length < 0)
 		self.rope_length = vlen(org - self.origin);

Modified: branches/nexuiz-2.0/data/qcsrc/server/g_triggers.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/g_triggers.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/g_triggers.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -469,16 +469,34 @@
 }
 
 
-void target_speaker_use() {sound(self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);}
+// TODO add a way to do looped sounds with sound(); then complete this entity
+.float volume, atten;
+void target_speaker_use() {sound(self, CHAN_TRIGGER, self.noise, VOL_BASE * self.volume, self.atten);}
 
 void spawnfunc_target_speaker()
 {
 	if(self.noise)
 		precache_sound (self.noise);
 	IFTARGETED
+	{
+		if(!self.atten)
+			self.atten = ATTN_NORM;
+		else if(self.atten < 0)
+			self.atten = 0;
+		if(!self.volume)
+			self.volume = 1;
 		self.use = target_speaker_use;
+	}
 	else
-		ambientsound (self.origin, self.noise, VOL_BASE, ATTN_STATIC);
+	{
+		if(!self.atten)
+			self.atten = ATTN_STATIC;
+		else if(self.atten < 0)
+			self.atten = 0;
+		if(!self.volume)
+			self.volume = 1;
+		ambientsound (self.origin, self.noise, VOL_BASE * self.volume, self.atten);
+	}
 };
 
 

Modified: branches/nexuiz-2.0/data/qcsrc/server/g_world.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/g_world.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/g_world.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -157,6 +157,7 @@
 }
 
 float world_already_spawned;
+void RegisterWeapons();
 void spawnfunc_worldspawn (void)
 {
 	float fd, l;
@@ -179,6 +180,9 @@
 		head = nextent(head);
 	}
 
+	// needs to be done so early as they would still spawn
+	RegisterWeapons();
+
 	if(GotoFirstMap())
 		return;
 
@@ -312,7 +316,7 @@
 	MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), 1);
 
 	//if tourney is used map starts in warmup mode. if this mode shall stay unlimited, reset timelimit, but save the original one
-	if(g_tourney && cvar("g_tourney_warmup_unlimited_time")) {
+	if(g_tourney && (-1 == g_tourney_warmup_limit)) {
 		timelimit_orig = cvar("timelimit");
 		cvar_set("timelimit", "0");
 	}
@@ -353,6 +357,8 @@
 	}
 
 	addstat(STAT_SYS_TICRATE, AS_FLOAT, stat_sys_ticrate);
+	addstat(STAT_WEAPONS, AS_INT, weapons);
+	addstat(STAT_SWITCHWEAPON, AS_INT, switchweapon);
 
 	next_pingtime = time + 5;
 	world_initialized = 1;

Modified: branches/nexuiz-2.0/data/qcsrc/server/havocbot.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/havocbot.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/havocbot.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -340,6 +340,7 @@
 	local float uzi     ; uzi      =-1000;
 	local float shotgun ; shotgun  =-1000;
 	local float laser   ; laser    =-1000;
+	local float minstanex ; minstanex =-1000;
 	local float currentscore;
 	local float bestscore; bestscore = 0;
 	local float bestweapon; bestweapon=self.switchweapon;
@@ -354,6 +355,10 @@
 	//	*(Time to get to target * weapon specfic hitchange bonus) / (in a time of maxdelaytime)
 	//	*(Spread change of hit) // if it applies
 	//	*(Penality for target beeing in air)
+	// %weaponaddpoint
+	if (client_hasweapon(self, WEP_MINSTANEX, TRUE, FALSE))
+		minstanex = (1000/cvar("g_balance_minstanex_refire")*1.0)
+			* (0.5);
 	if (client_hasweapon(self, WEP_ROCKET_LAUNCHER, TRUE, FALSE))
 		rocket = (cvar("g_balance_rocketlauncher_damage")/cvar("g_balance_rocketlauncher_refire")*0.75)
 			* bound(0,(cvar("g_balance_rocketlauncher_speed")/distance*maxdelaytime),1)*1.5;
@@ -400,15 +405,16 @@
 	dprint("Laser   :", ftos(laser   ), "\n\n");
 	*/
 	currentscore = -1;
-	w = WEP_ROCKET_LAUNCHER  ;s = rocket ;if (s > bestscore){bestscore = s;bestweapon = w;} if (self.switchweapon == w) currentscore = s;
-	w = WEP_NEX              ;s = nex    ;if (s > bestscore){bestscore = s;bestweapon = w;} if (self.switchweapon == w) currentscore = s;
-	w = WEP_HAGAR            ;s = hagar  ;if (s > bestscore){bestscore = s;bestweapon = w;} if (self.switchweapon == w) currentscore = s;
-	w = WEP_GRENADE_LAUNCHER ;s = grenade;if (s > bestscore){bestscore = s;bestweapon = w;} if (self.switchweapon == w) currentscore = s;
-	w = WEP_ELECTRO          ;s = electro;if (s > bestscore){bestscore = s;bestweapon = w;} if (self.switchweapon == w) currentscore = s;
-	w = WEP_CRYLINK          ;s = crylink;if (s > bestscore){bestscore = s;bestweapon = w;} if (self.switchweapon == w) currentscore = s;
-	w = WEP_UZI              ;s = uzi    ;if (s > bestscore){bestscore = s;bestweapon = w;} if (self.switchweapon == w) currentscore = s;
-	w = WEP_SHOTGUN          ;s = shotgun;if (s > bestscore){bestscore = s;bestweapon = w;} if (self.switchweapon == w) currentscore = s;
-	w = WEP_LASER            ;s = laser  ;if (s > bestscore){bestscore = s;bestweapon = w;} if (self.switchweapon == w) currentscore = s;
+	w = WEP_MINSTANEX        ;s = minstanex;if (s > bestscore){bestscore = s;bestweapon = w;} if (self.switchweapon == w) currentscore = s;
+	w = WEP_ROCKET_LAUNCHER  ;s = rocket   ;if (s > bestscore){bestscore = s;bestweapon = w;} if (self.switchweapon == w) currentscore = s;
+	w = WEP_NEX              ;s = nex      ;if (s > bestscore){bestscore = s;bestweapon = w;} if (self.switchweapon == w) currentscore = s;
+	w = WEP_HAGAR            ;s = hagar    ;if (s > bestscore){bestscore = s;bestweapon = w;} if (self.switchweapon == w) currentscore = s;
+	w = WEP_GRENADE_LAUNCHER ;s = grenade  ;if (s > bestscore){bestscore = s;bestweapon = w;} if (self.switchweapon == w) currentscore = s;
+	w = WEP_ELECTRO          ;s = electro  ;if (s > bestscore){bestscore = s;bestweapon = w;} if (self.switchweapon == w) currentscore = s;
+	w = WEP_CRYLINK          ;s = crylink  ;if (s > bestscore){bestscore = s;bestweapon = w;} if (self.switchweapon == w) currentscore = s;
+	w = WEP_UZI              ;s = uzi      ;if (s > bestscore){bestscore = s;bestweapon = w;} if (self.switchweapon == w) currentscore = s;
+	w = WEP_SHOTGUN          ;s = shotgun  ;if (s > bestscore){bestscore = s;bestweapon = w;} if (self.switchweapon == w) currentscore = s;
+	w = WEP_LASER            ;s = laser    ;if (s > bestscore){bestscore = s;bestweapon = w;} if (self.switchweapon == w) currentscore = s;
 
 	// switch if the best weapon would provide a significant damage increase
 	if (bestscore > currentscore*1.5)

Modified: branches/nexuiz-2.0/data/qcsrc/server/miscfunctions.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/miscfunctions.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/miscfunctions.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -73,21 +73,18 @@
 	}
 }
 
-void move_out_of_solid(entity e)
+float move_out_of_solid(entity e)
 {
 	vector o, m0, m1;
 
 	o = e.origin;
 	traceline(o, o, MOVE_WORLDONLY, e);
 	if(trace_startsolid)
-	{
-		dprint("origin is in solid too! (", vtos(o), ")");
-		return;
-	}
+		return 0;
 
 	tracebox(o, e.mins, e.maxs, o, MOVE_WORLDONLY, e);
 	if(!trace_startsolid)
-		return;
+		return 1;
 
 	m0 = e.mins;
 	m1 = e.maxs;
@@ -104,9 +101,11 @@
 	tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_WORLDONLY, e);
 	if(trace_startsolid)
 	{
-		dprint("could not get out of solid (", vtos(o), ")\n");
-		return;
+		setorigin(e, o);
+		return 0;
 	}
+
+	return 1;
 }
 
 string STR_PLAYER = "player";
@@ -202,7 +201,8 @@
 		o = self.origin;
 		self.mins = PL_MIN;
 		self.maxs = PL_MAX;
-		move_out_of_solid(self);
+		if(!move_out_of_solid(self))
+			objerror("could not get out of solid at all!");
 		print("^1NOTE: this map needs FIXING. Spawnpoint 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));
@@ -489,6 +489,21 @@
 	else
 		stuffcmd(self, strcat("sendcvar ", name, "\n"));
 }
+void GetCvars_handleString_Fixup(float f, .string field, string name, string(string) func)
+{
+	GetCvars_handleString(f, field, name);
+	if(f >= 0) // also initialize to the fitting value for "" when sending cvars out
+	if(argv(f) == name)
+	{
+		string s;
+		s = func(strcat1(self.field));
+		if(s != self.field)
+		{
+			strunzone(self.field);
+			self.field = strzone(s);
+		}
+	}
+}
 void GetCvars_handleFloat(float f, .float field, string name)
 {
 	if(f < 0)
@@ -502,18 +517,29 @@
 	else
 		stuffcmd(self, strcat("sendcvar ", name, "\n"));
 }
+string W_FixWeaponOrder_ForceComplete(string s);
+string W_FixWeaponOrder_AllowIncomplete(string s);
 void GetCvars(float f)
 {
 	GetCvars_handleFloat(f, autoswitch, "cl_autoswitch");
 	GetCvars_handleFloat(f, cvar_cl_hidewaypoints, "cl_hidewaypoints");
-	GetCvars_handleFloat(f, cvar_cl_zoomfactor, "cl_zoomfactor");
-	GetCvars_handleFloat(f, cvar_cl_zoomspeed, "cl_zoomspeed");
 	GetCvars_handleFloat(f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
 	GetCvars_handleFloat(f, cvar_cl_nogibs, "cl_nogibs");
 	GetCvars_handleFloat(f, cvar_scr_centertime, "scr_centertime");
 	GetCvars_handleFloat(f, cvar_cl_shownames, "cl_shownames");
 	GetCvars_handleString(f, cvar_g_nexuizversion, "g_nexuizversion");
 	GetCvars_handleFloat(f, cvar_cl_handicap, "cl_handicap");
+	GetCvars_handleString_Fixup(f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
+	GetCvars_handleString_Fixup(f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
+	GetCvars_handleString_Fixup(f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
+	GetCvars_handleString_Fixup(f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
+	GetCvars_handleString_Fixup(f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
+	GetCvars_handleString_Fixup(f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
+	GetCvars_handleString_Fixup(f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
+	GetCvars_handleString_Fixup(f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
+	GetCvars_handleString_Fixup(f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
+	GetCvars_handleString_Fixup(f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
+	GetCvars_handleString_Fixup(f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
 }
 
 float fexists(string f)
@@ -674,6 +700,7 @@
 float g_pickup_healthmega;
 float g_pickup_healthmega_max;
 
+float start_weapons;
 float start_items;
 float start_switchweapon;
 float start_ammo_shells;
@@ -683,8 +710,13 @@
 float start_health;
 float start_armorvalue;
 
+entity get_weaponinfo(float w);
+
 void readlevelcvars(void)
 {
+	entity e;
+	float i;
+
 	sv_cheats = cvar("sv_cheats");
 	sv_gentle = cvar("sv_gentle");
 	sv_foginterval = cvar("sv_foginterval");
@@ -692,7 +724,6 @@
 	g_jump_grunt = cvar("g_jump_grunt");
 	g_footsteps = cvar("g_footsteps");
 	g_grappling_hook = cvar("g_grappling_hook");
-	g_instagib = cvar("g_instagib");
 	g_laserguided_missile = cvar("g_laserguided_missile");
 	g_midair = cvar("g_midair");
 	g_minstagib = cvar("g_minstagib");
@@ -702,12 +733,17 @@
 	g_rocketarena = cvar("g_rocketarena");
 	g_vampire = cvar("g_vampire");
 	g_tourney = cvar("g_tourney");
+	g_tourney_warmup_limit = cvar("g_tourney_warmup_limit");
 	sv_maxidle = cvar("sv_maxidle");
 	sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
 	sv_pogostick = cvar("sv_pogostick");
 	sv_doublejump = cvar("sv_doublejump");
 
-	if(g_instagib) g_minstagib = g_nixnex = g_rocketarena = 0;
+	g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
+	g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
+	g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
+	g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
+
 	if(g_minstagib) g_nixnex = g_rocketarena = 0;
 	if(g_nixnex) g_rocketarena = 0;
 
@@ -735,6 +771,7 @@
 	g_pickup_healthmega_max            = cvar("g_pickup_healthmega_max");
 
 	// initialize starting values for players
+	start_weapons = 0;
 	start_items = 0;
 	start_switchweapon = 0;
 	start_ammo_shells = 0;
@@ -744,31 +781,25 @@
 	start_health = cvar("g_balance_health_start");
 	start_armorvalue = cvar("g_balance_armor_start");
 
-	if(g_instagib)
+	if(g_rocketarena)
 	{
-		start_items = IT_NEX;
-		start_switchweapon = WEP_NEX;
-		weapon_action(start_switchweapon, WR_PRECACHE);
-		start_ammo_cells = 999;
-	}
-	else if(g_rocketarena)
-	{
-		start_items = IT_ROCKET_LAUNCHER;
+		start_weapons = WEPBIT_ROCKET_LAUNCHER;
 		start_switchweapon = WEP_ROCKET_LAUNCHER;
 		weapon_action(start_switchweapon, WR_PRECACHE);
 		start_ammo_rockets = 999;
+		start_items |= IT_UNLIMITED_AMMO;
 	}
 	else if(g_nixnex)
 	{
-		start_items = 0;
+		start_weapons = 0;
 		// will be done later
 	}
 	else if(g_minstagib)
 	{
 		start_health = 100;
 		start_armorvalue = 0;
-		start_items = IT_NEX;
-		start_switchweapon = WEP_NEX;
+		start_weapons = WEPBIT_MINSTANEX;
+		start_switchweapon = WEP_MINSTANEX;
 		weapon_action(start_switchweapon, WR_PRECACHE);
 		start_ammo_cells = cvar("g_minstagib_ammo_start");
 		g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
@@ -802,62 +833,21 @@
 			start_ammo_nails = cvar("g_pickup_nails_max");
 			start_ammo_rockets = cvar("g_pickup_rockets_max");
 			start_ammo_cells = cvar("g_pickup_cells_max");
+			start_items |= IT_UNLIMITED_AMMO;
 		}
 
-		if (cvar("g_start_weapon_laser") || g_lms)
+		for(i = WEP_FIRST; i <= WEP_LAST; ++i)
 		{
-			start_items = start_items | IT_LASER;
-			start_switchweapon = WEP_LASER;
-			weapon_action(start_switchweapon, WR_PRECACHE);
+			e = get_weaponinfo(i);
+			if(!(e.weapon))
+				continue;
+			if(((e.spawnflags & 1) && (g_lms || g_tourney)) || cvar(strcat("g_start_weapon_", e.netname)))
+			{
+				start_weapons |= e.weapons;
+				start_switchweapon = e.weapon;
+				weapon_action(e.weapon, WR_PRECACHE);
+			}
 		}
-		if (cvar("g_start_weapon_shotgun") || g_lms)
-		{
-			start_items = start_items | IT_SHOTGUN;
-			start_switchweapon = WEP_SHOTGUN;
-			weapon_action(start_switchweapon, WR_PRECACHE);
-		}
-		if (cvar("g_start_weapon_uzi") || g_lms || g_tourney)
-		{
-			start_items = start_items | IT_UZI;
-			start_switchweapon = WEP_UZI;
-			weapon_action(start_switchweapon, WR_PRECACHE);
-		}
-		if (cvar("g_start_weapon_grenadelauncher") || g_lms || g_tourney)
-		{
-			start_items = start_items | IT_GRENADE_LAUNCHER;
-			start_switchweapon = WEP_GRENADE_LAUNCHER;
-			weapon_action(start_switchweapon, WR_PRECACHE);
-		}
-		if (cvar("g_start_weapon_electro") || g_lms || g_tourney)
-		{
-			start_items = start_items | IT_ELECTRO;
-			start_switchweapon = WEP_ELECTRO;
-			weapon_action(start_switchweapon, WR_PRECACHE);
-		}
-		if (cvar("g_start_weapon_crylink") || g_lms || g_tourney)
-		{
-			start_items = start_items | IT_CRYLINK;
-			start_switchweapon = WEP_CRYLINK;
-			weapon_action(start_switchweapon, WR_PRECACHE);
-		}
-		if (cvar("g_start_weapon_nex") || g_lms || g_tourney)
-		{
-			start_items = start_items | IT_NEX;
-			start_switchweapon = WEP_NEX;
-			weapon_action(start_switchweapon, WR_PRECACHE);
-		}
-		if (cvar("g_start_weapon_hagar") || g_lms || g_tourney)
-		{
-			start_items = start_items | IT_HAGAR;
-			start_switchweapon = WEP_HAGAR;
-			weapon_action(start_switchweapon, WR_PRECACHE);
-		}
-		if (cvar("g_start_weapon_rocketlauncher") || g_lms || g_tourney)
-		{
-			start_items = start_items | IT_ROCKET_LAUNCHER;
-			start_switchweapon = WEP_ROCKET_LAUNCHER;
-			weapon_action(start_switchweapon, WR_PRECACHE);
-		}
 	}
 }
 
@@ -1238,3 +1228,176 @@
 
 .float nottargeted;
 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
+
+float power2of(float e)
+{
+	return pow(2, e);
+}
+float log2of(float x)
+{
+	// NOTE: generated code
+	if(x > 2048)
+		if(x > 131072)
+			if(x > 1048576)
+				if(x > 4194304)
+					return 23;
+				else
+					if(x > 2097152)
+						return 22;
+					else
+						return 21;
+			else
+				if(x > 524288)
+					return 20;
+				else
+					if(x > 262144)
+						return 19;
+					else
+						return 18;
+		else
+			if(x > 16384)
+				if(x > 65536)
+					return 17;
+				else
+					if(x > 32768)
+						return 16;
+					else
+						return 15;
+			else
+				if(x > 8192)
+					return 14;
+				else
+					if(x > 4096)
+						return 13;
+					else
+						return 12;
+	else
+		if(x > 32)
+			if(x > 256)
+				if(x > 1024)
+					return 11;
+				else
+					if(x > 512)
+						return 10;
+					else
+						return 9;
+			else
+				if(x > 128)
+					return 8;
+				else
+					if(x > 64)
+						return 7;
+					else
+						return 6;
+		else
+			if(x > 4)
+				if(x > 16)
+					return 5;
+				else
+					if(x > 8)
+						return 4;
+					else
+						return 3;
+			else
+				if(x > 2)
+					return 2;
+				else
+					if(x > 1)
+						return 1;
+					else
+						return 0;
+}
+
+//
+// func_breakable 
+// - basically func_assault_destructible for general gameplay use
+//
+float () crandom;
+void () SUB_UseTargets, assault_destructible_use, SUB_Remove, SUB_Null;
+void LaunchDebris (string debrisname) =
+{
+	local	entity dbr;
+	
+	if (debrisname == "" || !debrisname)
+		return;
+	
+	dbr = spawn();
+	dbr.origin = self.origin;
+	setmodel (dbr, debrisname );
+	setsize (dbr, '0 0 0', '0 0 0');
+	dbr.velocity_x = 70 * crandom();
+	dbr.velocity_y = 70 * crandom();
+	dbr.velocity_z = 140 + 70 * random();
+	dbr.movetype = MOVETYPE_BOUNCE;
+	dbr.solid = SOLID_BBOX;
+	dbr.avelocity_x = random()*600;
+	dbr.avelocity_y = random()*600;
+	dbr.avelocity_z = random()*600;
+	dbr.think = SUB_Remove;
+	dbr.nextthink = time + 13 + random()*10;
+};
+
+.string debris1, debris2, debris3;
+.string mdl_dead;
+void func_breakable_destroy() {
+	if (self.mdl_dead)
+		setmodel(self, self.mdl_dead);
+	else {
+		self.model = "";
+		self.solid = SOLID_NOT;
+	}
+	self.takedamage = DAMAGE_NO;
+	
+	// now throw around the debris
+	LaunchDebris(self.debris1);
+	LaunchDebris(self.debris2);
+	LaunchDebris(self.debris3);
+	
+	SUB_UseTargets();
+	
+	self.event_damage = SUB_Null;
+}
+
+void func_breakable_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) {
+
+	if(self.cnt > 0) {
+		self.health = self.health - damage;
+		// add pain effects?
+	}
+
+	if(self.health < 0) {
+		activator = attacker;
+		func_breakable_destroy();
+	}
+}
+
+// destructible walls that can be used to trigger target_objective_decrease
+void spawnfunc_func_breakable() {
+	if(!self.health)
+		self.health = 100;
+
+	self.max_health = self.health;
+
+	//self.cnt = 0; // not yet activated
+
+
+
+	self.classname = "func_breakable";	
+	self.mdl = self.model;
+	setmodel(self, self.mdl);
+	
+	self.solid = SOLID_BSP;
+	
+	// precache all the models
+	if (self.mdl_dead)
+		precache_model(self.mdl_dead);
+	if (self.debris1)
+		precache_model(self.debris1);
+	if (self.debris2)
+		precache_model(self.debris2);
+	if (self.debris3)
+		precache_model(self.debris3);
+	
+	self.use = assault_destructible_use;	// shared use function, b/c they woudl do the same thing anyways
+	self.event_damage = func_breakable_damage;
+}

Copied: branches/nexuiz-2.0/data/qcsrc/server/portals.qc (from rev 4363, trunk/data/qcsrc/server/portals.qc)
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/portals.qc	                        (rev 0)
+++ branches/nexuiz-2.0/data/qcsrc/server/portals.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -0,0 +1,573 @@
+.vector portal_transform;
+.vector portal_safe_origin;
+.float portal_wants_to_vanish;
+.float portal_activatetime;
+.float portal_id;
+
+.entity portal_in, portal_out;
+
+vector Portal_Transform_Apply(vector transform, vector v)
+{
+	fixedmakevectors(transform);
+	return v_forward * v_x
+	     + v_right   * (-v_y)
+		 + v_up      * v_z;
+}
+
+vector Portal_Transform_Multiply(vector t1, vector t2)
+{
+	vector m_forward, m_up;
+	fixedmakevectors(t2); m_forward = v_forward; m_up = v_up;
+	m_forward = Portal_Transform_Apply(t1, m_forward);
+	m_up = Portal_Transform_Apply(t1, m_up);
+	return vectoangles2(m_forward, m_up);
+}
+
+vector Portal_Transform_Invert(vector transform)
+{
+	vector i_forward, i_up;
+	fixedmakevectors(transform);
+	// we want angles that turn v_forward into '1 0 0', v_right into '0 1 0' and v_up into '0 0 1'
+	// but these are orthogonal unit vectors!
+	// so to invert, we can simply vectoangles the TRANSPOSED matrix
+	// TODO is this always -transform?
+	i_forward_x = v_forward_x;
+	i_forward_y = -v_right_x;
+	i_forward_z = v_up_x;
+	i_up_x = v_forward_z;
+	i_up_y = -v_right_z;
+	i_up_z = v_up_z;
+#ifdef DEBUG
+	vector v;
+	v = vectoangles2(i_forward, i_up);
+	print("Transform: ", vtos(transform), "\n");
+	print("Inverted: ", vtos(v), "\n");
+	print("Verify: ", vtos(Portal_Transform_Multiply(v, transform)), "\n");
+	fixedmakevectors(Portal_Transform_Multiply(v, transform));
+	print("Verify: ", vtos(v_forward), "\n");
+	print("Verify: ", vtos(v_right), "\n");
+	print("Verify: ", vtos(v_up), "\n");
+#endif
+	return vectoangles2(i_forward, i_up);
+}
+
+vector Portal_Transform_TurnDirection(vector transform)
+{
+	// turn 180 degrees around v_up
+	// changes in-direction to out-direction
+	fixedmakevectors(transform);
+	return vectoangles2(-1 * v_forward, 1 * v_up);
+}
+
+vector Portal_Transform_Divide(vector to_transform, vector from_transform)
+{
+	return Portal_Transform_Multiply(to_transform, Portal_Transform_Invert(from_transform));
+}
+
+float PlayerEdgeDistance(entity p, vector v)
+{
+	vector vbest;
+
+	if(v_x < 0) vbest_x = p.mins_x; else vbest_x = p.maxs_x;
+	if(v_y < 0) vbest_y = p.mins_y; else vbest_y = p.maxs_y;
+	if(v_z < 0) vbest_z = p.mins_z; else vbest_z = p.maxs_z;
+
+	return vbest * v;
+}
+
+.vector right_vector;
+float Portal_TeleportPlayer(entity teleporter, entity player)
+{
+	vector from, to, safe, step, transform, ang, newvel;
+	float planeshift, s, t;
+	from = teleporter.origin;
+	transform = teleporter.portal_transform;
+
+	to = teleporter.enemy.origin;
+	to = to + Portal_Transform_Apply(teleporter.portal_transform, player.origin - from);
+	newvel = Portal_Transform_Apply(transform, player.velocity);
+	// this now is INSIDE the plane... can't use that
+	
+	// shift it out
+	fixedmakevectors(teleporter.enemy.angles);
+
+	// first shift it ON the plane if needed
+	planeshift = ((teleporter.enemy.origin - to) * v_forward) + PlayerEdgeDistance(player, v_forward) + 1;
+	if(planeshift > 0 && (newvel * v_forward) > vlen(newvel) * 0.01)
+		// if we can't, let us not do the planeshift and do somewhat incorrect transformation in the end
+		to += newvel * (planeshift / (newvel * v_forward));
+	else
+		to += trace_plane_normal * planeshift;
+	
+	s = (to - teleporter.enemy.origin) * v_right;
+	t = (to - teleporter.enemy.origin) * v_up;
+	s = bound(-48, s, 48);
+	t = bound(-48, t, 48);
+	to = teleporter.enemy.origin
+	   + ((to - teleporter.enemy.origin) * v_forward) * v_forward
+	   +     s                                        * v_right
+	   +     t                                        * v_up;
+
+	safe = teleporter.enemy.portal_safe_origin; // a valid player origin
+	step = to + ((safe - to) * v_forward) * v_forward;
+	tracebox(safe, PL_MIN, PL_MAX, step, MOVE_NOMONSTERS, player);
+	if(trace_startsolid)
+	{
+		bprint("'safe' teleport location is not safe!\n");
+		// FAIL TODO why does this happen?
+		return 0;
+	}
+	safe = trace_endpos;
+	tracebox(safe, PL_MIN, PL_MAX, to, MOVE_NOMONSTERS, player);
+	if(trace_startsolid)
+		error("trace_endpos in solid!");
+	to = trace_endpos;
+
+	// ang_x stuff works around weird quake angles
+	if(player.classname == "player")
+	{
+		ang = player.v_angle;
+		ang_x = -ang_x;
+		ang = Portal_Transform_Multiply(transform, ang);
+		ang_z = player.angles_z;
+	}
+	else
+	{
+		ang = player.angles;
+		ang_x = -ang_x;
+		ang = Portal_Transform_Multiply(transform, player.angles);
+	}
+	ang_x = -ang_x;
+
+	// factor -1 allows chaining portals, but may be weird
+	player.right_vector = -1 * Portal_Transform_Apply(transform, player.right_vector);
+
+	if(player.flagcarried)
+		DropFlag(player.flagcarried);
+	TeleportPlayer(teleporter, player, to, ang, newvel, teleporter.enemy.absmin, teleporter.enemy.absmax);
+
+	// reset fade counter
+	teleporter.portal_wants_to_vanish = 0;
+	teleporter.fade_time = time + 15;
+	teleporter.enemy.health = 300;
+
+	return 1;
+}
+
+float Portal_FindSafeOrigin(entity portal)
+{
+	vector o;
+	o = portal.origin;
+	portal.mins = PL_MIN - '8 8 8';
+	portal.maxs = PL_MAX + '8 8 8';
+	fixedmakevectors(portal.angles);
+	portal.origin += 16 * v_forward;
+	if(!move_out_of_solid(portal))
+	{
+#ifdef DEBUG
+		print("NO SAFE ORIGIN\n");
+#endif
+		return 0;
+	}
+	portal.portal_safe_origin = portal.origin;
+	setorigin(portal, o);
+	return 1;
+}
+
+void Portal_Touch()
+{
+	if(trace_fraction < 1)
+		return; // only handle TouchAreaGrid ones (only these can teleport)
+		// for some unknown reason, this also gets collisions from SV_Impact sometimes
+	if(!self.enemy)
+	{
+		objerror("Portal_Touch called for [unconnected] portal\n");
+		return;
+	}
+	if(other.classname == "porto")
+	{
+		if(other.portal_id == self.portal_id)
+			return;
+	}
+	if(time < self.portal_activatetime)
+		if(other == self.owner)
+		{
+			self.portal_activatetime = time + 0.1;
+			return;
+		}
+	if(other != self.owner)
+		if(other.classname == "player")
+			if(IS_INDEPENDENT_PLAYER(other) || IS_INDEPENDENT_PLAYER(self.owner))
+				return; // cannot go through someone else's portal
+	if(other.owner != self.owner)
+		if(other.owner.classname == "player")
+			if(IS_INDEPENDENT_PLAYER(other.owner) || IS_INDEPENDENT_PLAYER(self.owner))
+				return; // cannot go through someone else's portal
+	fixedmakevectors(self.angles);
+	if((other.origin - self.origin) * v_forward < 0)
+		return;
+	if(other.mins_x < PL_MIN_x || other.mins_y < PL_MIN_y || other.mins_z < PL_MIN_z
+	|| other.maxs_x > PL_MAX_x || other.maxs_y > PL_MAX_y || other.maxs_z > PL_MAX_z)
+	{
+		// can't teleport this
+		return;
+	}
+
+	if(Portal_TeleportPlayer(self, other))
+		if(other.classname == "porto")
+			if(other.effects & EF_RED)
+				other.effects += EF_BLUE - EF_RED;
+}
+
+void Portal_MakeBrokenPortal(entity portal)
+{
+	portal.solid = SOLID_NOT;
+	portal.touch = SUB_Null;
+	portal.effects = 0;
+	//portal.colormod = '1 1 1';
+	portal.nextthink = 0;
+	portal.takedamage = DAMAGE_NO;
+}
+
+void Portal_MakeWaitingPortal(entity portal)
+{
+	portal.solid = SOLID_NOT;
+	portal.touch = SUB_Null;
+	portal.effects = EF_ADDITIVE;
+	portal.colormod = '1 1 1';
+	portal.nextthink = 0;
+	portal.takedamage = DAMAGE_YES;
+}
+
+void Portal_MakeInPortal(entity portal)
+{
+	portal.solid = SOLID_TRIGGER;
+	portal.touch = Portal_Touch;
+	portal.effects = EF_RED;
+	portal.colormod = '1 0 0';
+	portal.nextthink = time;
+	portal.takedamage = DAMAGE_NO;
+}
+
+void Portal_MakeOutPortal(entity portal)
+{
+	portal.solid = SOLID_NOT;
+	portal.touch = SUB_Null;
+	portal.effects = EF_STARDUST | EF_BLUE;
+	portal.colormod = '0 0 1';
+	portal.nextthink = 0;
+	portal.takedamage = DAMAGE_YES;
+}
+
+void Portal_Disconnect(entity teleporter, entity destination)
+{
+	teleporter.enemy = world;
+	destination.enemy = world;
+	Portal_MakeBrokenPortal(teleporter);
+	Portal_MakeBrokenPortal(destination);
+}
+
+void Portal_Connect(entity teleporter, entity destination)
+{
+	teleporter.portal_transform = Portal_Transform_Divide(Portal_Transform_TurnDirection(destination.angles), teleporter.angles);
+
+#ifdef DEBUG
+	{
+		// let's verify the transform
+		vector in_f, in_r, in_u;
+		vector out_f, out_r, out_u;
+		fixedmakevectors(teleporter.angles);
+		in_f = v_forward;
+		in_r = v_right;
+		in_u = v_up;
+		print("teleporter: ", vtos(in_f), " ", vtos(in_r), " ", vtos(in_u), "\n");
+		fixedmakevectors(destination.angles);
+		out_f = v_forward;
+		out_r = v_right;
+		out_u = v_up;
+		print("dest: ", vtos(out_f), " ", vtos(out_r), " ", vtos(out_u), "\n");
+		// INTENDED TRANSFORM:
+		//   in_f -> -out_f
+		//   in_r -> -out_r
+		//   in_u -> +out_u
+		print("FORWARD: ", vtos(in_f), " -> ", vtos(Portal_Transform_Apply(teleporter.portal_transform, in_f)), ", should be", vtos(-1 * out_f), "\n");
+		print("RIGHT: ", vtos(in_r), " -> ", vtos(Portal_Transform_Apply(teleporter.portal_transform, in_r)), ", should be", vtos(-1 * out_r), "\n");
+		print("UP: ", vtos(in_u), " -> ", vtos(Portal_Transform_Apply(teleporter.portal_transform, in_u)), ", should be", vtos(out_u), "\n");
+
+		te_lightning3(world, teleporter.origin, teleporter.origin + in_r * 1000);
+		te_lightning3(world, destination.origin, destination.origin + out_r * 1000);
+	}
+#endif
+
+	teleporter.enemy = destination;
+	destination.enemy = teleporter;
+	Portal_MakeInPortal(teleporter);
+	Portal_MakeOutPortal(destination);
+	teleporter.fade_time = time + 15;
+	destination.fade_time = time + 15;
+	teleporter.portal_wants_to_vanish = 0;
+	destination.portal_wants_to_vanish = 0;
+}
+
+void Portal_Remove(entity portal, float killed)
+{
+	entity e;
+	e = portal.enemy;
+
+	if(e)
+	{
+		Portal_Disconnect(portal, e);
+		Portal_Remove(e, killed);
+	}
+
+	if(portal == portal.owner.portal_in)
+		portal.owner.portal_in = world;
+	if(portal == portal.owner.portal_out)
+		portal.owner.portal_out = world;
+	portal.owner = world;
+
+	// makes the portal vanish
+	if(killed)
+	{
+		fixedmakevectors(portal.angles);
+		pointparticles(particleeffectnum("rocket_explode"), portal.origin + v_forward * 16, v_forward * 1024, 4);
+		remove(portal);
+	}
+	else
+	{
+		Portal_MakeBrokenPortal(portal);
+		SUB_SetFade(portal, time, 0.5);
+	}
+}
+
+void Portal_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+{
+	if(deathtype == DEATH_TELEFRAG)
+		return;
+	if(attacker != self.owner)
+		if(IS_INDEPENDENT_PLAYER(attacker) || IS_INDEPENDENT_PLAYER(self.owner))
+			return;
+	self.health -= damage;
+	if(self.health < 0)
+	{
+		Portal_Remove(self, 1);
+	}
+}
+
+void Portal_Think()
+{
+	entity e, o;
+
+	if(self.solid != SOLID_TRIGGER)
+		error("Portal_Think called for a portal that should not be thinking");
+
+	o = self.owner;
+	self.solid = SOLID_BBOX;
+	self.owner = world;
+	FOR_EACH_PLAYER(e)
+	{
+		if(time < self.portal_activatetime)
+			if(e == o)
+				continue;
+		if(e != o)
+			if(IS_INDEPENDENT_PLAYER(e) || IS_INDEPENDENT_PLAYER(o))
+				continue; // cannot go through someone else's portal
+		// if e would hit the portal in a frame...
+		// already teleport him
+		tracebox(e.origin, e.mins, e.maxs, e.origin + e.velocity * 2 * frametime, MOVE_NORMAL, e);
+		if(trace_ent == self)
+			Portal_TeleportPlayer(self, e);
+	}
+	self.solid = SOLID_TRIGGER;
+	self.owner = o;
+
+	self.nextthink = time;
+
+	if(time > self.fade_time)
+		Portal_Remove(self, 0);
+}
+
+float Portal_Customize()
+{
+	if(other.classname == "spectator")
+		other = other.enemy;
+	if(other == self.owner)
+		return TRUE;
+	if(IS_INDEPENDENT_PLAYER(other))
+		return FALSE;
+	if(IS_INDEPENDENT_PLAYER(self.owner))
+		return FALSE;
+	return TRUE;
+}
+
+// cleanup:
+//   when creating in-portal:
+//     disconnect
+//     clear existing in-portal
+//     set as in-portal
+//     connect
+//   when creating out-portal:
+//     disconnect
+//     clear existing out-portal
+//     set as out-portal
+//   when player dies:
+//     disconnect portals
+//     clear both portals
+//   after timeout of in-portal:
+//     disconnect portals
+//     clear both portals
+//   TODO: ensure only one portal shot at once
+float Portal_SetInPortal(entity own, entity portal)
+{
+	if(own.portal_in)
+	{
+		if(own.portal_out)
+			Portal_Disconnect(own.portal_in, own.portal_out);
+		Portal_Remove(own.portal_in, 0);
+	}
+	own.portal_in = portal;
+	if(own.portal_out)
+	{
+		own.portal_out.portal_id = portal.portal_id;
+		Portal_Connect(own.portal_in, own.portal_out);
+	}
+	return 2;
+}
+float Portal_SetOutPortal(entity own, entity portal)
+{
+	if(own.portal_out)
+	{
+		if(own.portal_in)
+			Portal_Disconnect(own.portal_in, own.portal_out);
+		Portal_Remove(own.portal_out, 0);
+	}
+	own.portal_out = portal;
+	if(own.portal_in)
+	{
+		own.portal_in.portal_id = portal.portal_id;
+		Portal_Connect(own.portal_in, own.portal_out);
+	}
+	return 1;
+}
+void Portal_ClearAll(entity own)
+{
+	if(own.portal_in)
+		Portal_Remove(own.portal_in, 0);
+	if(own.portal_out)
+		Portal_Remove(own.portal_out, 0);
+}
+void Portal_ClearWithID(entity own, float id)
+{
+	if(own.portal_in)
+		if(own.portal_in.portal_id == id)
+		{
+			if(own.portal_out)
+				Portal_Disconnect(own.portal_in, own.portal_out);
+			Portal_Remove(own.portal_in, 0);
+		}
+	if(own.portal_out)
+		if(own.portal_out.portal_id == id)
+		{
+			if(own.portal_in)
+				Portal_Disconnect(own.portal_in, own.portal_out);
+			Portal_Remove(own.portal_out, 0);
+		}
+}
+
+entity Portal_Spawn(entity own, vector org, vector ang)
+{
+	entity portal;
+
+	fixedmakevectors(ang);
+	if(!CheckWireframeBox(own, org - 48 * v_right - 48 * v_up + 16 * v_forward, 96 * v_right, 96 * v_up, 96 * v_forward))
+		return world;
+
+	portal = spawn();
+	portal.classname = "portal";
+	portal.owner = own;
+	portal.origin = org;
+	portal.angles = ang;
+	portal.think = Portal_Think;
+	portal.nextthink = 0;
+	portal.fade_time = time + 15;
+	portal.portal_activatetime = time + 0.1;
+	portal.event_damage = Portal_Damage;
+	portal.health = 300;
+	setmodel(portal, "models/portal.md3");
+	portal.customizeentityforclient = Portal_Customize;
+
+	if(!Portal_FindSafeOrigin(portal))
+	{
+		remove(portal);
+		return world;
+	}
+
+	setsize(portal, '-48 -48 -48', '48 48 48');
+	Portal_MakeWaitingPortal(portal);
+
+	return portal;
+}
+
+float Portal_SpawnInPortalAtTrace(entity own, vector dir, float portal_id_val)
+{
+	entity portal;
+	vector ang;
+	vector org;
+
+	if(trace_ent.movetype == MOVETYPE_WALK)
+	{
+		trace_endpos = trace_ent.origin + '0 0 1' * PL_MIN_z;
+		trace_plane_normal = '0 0 1';
+		dir = -1 * dir; // create telefrag portals the other way round
+	}
+
+	org = trace_endpos;
+	ang = vectoangles2(trace_plane_normal, dir);
+	fixedmakevectors(ang);
+
+	portal = Portal_Spawn(own, org, ang);
+	if(!portal)
+	{
+		if(!self.portal_out || self.portal_out.portal_id == portal_id_val)
+			Portal_ClearAll(own);
+		return 0;
+	}
+
+	portal.portal_id = portal_id_val;
+	Portal_SetInPortal(own, portal);
+	sound(portal, CHAN_PROJECTILE, "misc/invshot.wav", VOL_BASE, ATTN_NORM);
+
+	return 1;
+}
+
+float Portal_SpawnOutPortalAtTrace(entity own, vector dir, float portal_id_val)
+{
+	entity portal;
+	vector ang;
+	vector org;
+
+	if(trace_ent.movetype == MOVETYPE_WALK)
+	{
+		trace_endpos = trace_ent.origin + '0 0 1' * PL_MIN_z;
+		trace_plane_normal = '0 0 1';
+		dir = -1 * dir; // create telefrag portals the other way round
+	}
+
+	org = trace_endpos;
+	ang = vectoangles2(trace_plane_normal, dir);
+	fixedmakevectors(ang);
+
+	portal = Portal_Spawn(own, org, ang);
+	if(!portal)
+	{
+		if(!self.portal_in || self.portal_in.portal_id == portal_id_val)
+			Portal_ClearAll(own);
+		return 0;
+	}
+
+	portal.portal_id = portal_id_val;
+	Portal_SetOutPortal(own, portal);
+	sound(portal, CHAN_PROJECTILE, "misc/invshot.wav", VOL_BASE, ATTN_NORM);
+
+	return 1;
+}

Copied: branches/nexuiz-2.0/data/qcsrc/server/portals.qh (from rev 4363, trunk/data/qcsrc/server/portals.qh)
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/portals.qh	                        (rev 0)
+++ branches/nexuiz-2.0/data/qcsrc/server/portals.qh	2008-09-05 19:40:34 UTC (rev 4365)
@@ -0,0 +1,4 @@
+void Portal_ClearAll(entity own);
+float Portal_SpawnOutPortalAtTrace(entity own, vector dir, float id);
+float Portal_SpawnInPortalAtTrace(entity own, vector dir, float id);
+void Portal_ClearWithID(entity own, float id);

Modified: branches/nexuiz-2.0/data/qcsrc/server/progs.src
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/progs.src	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/progs.src	2008-09-05 19:40:34 UTC (rev 4365)
@@ -18,6 +18,8 @@
 
 ../common/mapinfo.qh
 
+portals.qh
+
 scores.qh
 
 ipban.qh
@@ -76,8 +78,10 @@
 w_electro.qc
 w_crylink.qc
 w_nex.qc
+w_minstanex.qc
 w_hagar.qc
 w_rocketlauncher.qc
+w_porto.qc
 
 t_items.qc
 cl_weapons.qc
@@ -131,3 +135,5 @@
 tturrets/include/turret_tturrets.qh
 
 scores.qc
+
+portals.qc

Modified: branches/nexuiz-2.0/data/qcsrc/server/race.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/race.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/race.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -301,13 +301,61 @@
 
 void checkpoint_touch()
 {
+	string oldmsg;
+	entity oldself;
+
+	EXACTTRIGGER_TOUCH;
+
+	if(other.classname == "porto")
+	{
+		// do not allow portalling through checkpoints
+		trace_plane_normal = normalize(-1 * other.velocity);
+		self = other;
+		W_Porto_Fail(0);
+		return;
+	}
+
+	/*
+	 * Trigger targets
+	 */
+	if not(self.spawnflags & 2)
+	{
+		activator = other;
+		oldmsg = self.message;
+		self.message = "";
+		SUB_UseTargets();
+		self.message = oldmsg;
+	}
+
 	if(other.classname != "player")
 		return;
 
-	EXACTTRIGGER_TOUCH;
+	/*
+	 * Remove unauthorized equipment
+	 */
+	Portal_ClearAll(other);
+	if(other.porto_current)
+	{
+		oldself = self;
+		self = other.porto_current;
+		W_Porto_Fail(1);
+		self = oldself;
+	}
 
 	if(other.race_checkpoint == -1 || other.race_checkpoint == self.race_checkpoint)
 	{
+		/*
+		 * Trigger targets
+		 */
+		if(self.spawnflags & 2)
+		{
+			activator = other;
+			oldmsg = self.message;
+			self.message = "";
+			SUB_UseTargets();
+			self.message = oldmsg;
+		}
+
 		other.race_checkpoint = race_NextCheckpoint(self.race_checkpoint);
 
 		race_SendTime(other, self.race_checkpoint, time - other.race_laptime, !!other.race_laptime);

Modified: branches/nexuiz-2.0/data/qcsrc/server/scores.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/scores.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/scores.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -1,6 +1,3 @@
-.float scores[MAX_SCORE];
-.float teamscores[MAX_TEAMSCORE];
-
 .entity scorekeeper;
 entity teamscorekeepers[16];
 string scores_label[MAX_SCORE];

Modified: branches/nexuiz-2.0/data/qcsrc/server/scores.qh
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/scores.qh	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/scores.qh	2008-09-05 19:40:34 UTC (rev 4365)
@@ -1,4 +1,6 @@
 float scores_initialized; // 1 when scores labels/rules have been set
+.float scores[MAX_SCORE];
+.float teamscores[MAX_TEAMSCORE];
 
 /**
  * Attaches a PlayerScore entity to a player. Use that in ClientConnect.

Modified: branches/nexuiz-2.0/data/qcsrc/server/sv_main.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/sv_main.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/sv_main.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -152,6 +152,16 @@
 
 	Spawnqueue_Check();
 
+	// if in warmup stage and limit for warmup is hit start match
+	if (g_tourney
+	    && !tourneyInMatchStage
+	    && 0 < g_tourney_warmup_limit
+	    && (time + RESTART_COUNTDOWN) >= g_tourney_warmup_limit)
+	{
+		ReadyRestart();
+		return;
+	}
+
 	CreatureFrame ();
 	CheckRules_World ();
 

Modified: branches/nexuiz-2.0/data/qcsrc/server/t_items.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/t_items.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/t_items.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -1,4 +1,35 @@
+floatfield Item_CounterField(float it)
+{
+	switch(it)
+	{
+		case IT_SHELLS:      return ammo_shells;
+		case IT_NAILS:       return ammo_nails;
+		case IT_ROCKETS:     return ammo_rockets;
+		case IT_CELLS:       return ammo_cells;
+		case IT_5HP:         return health;
+		case IT_25HP:        return health;
+		case IT_HEALTH:      return health;
+		case IT_ARMOR_SHARD: return armorvalue;
+		case IT_ARMOR:       return armorvalue;
+		// add more things here (health, armor)
+		default:             error("requested item has no counter field");
+	}
+}
 
+string Item_CounterFieldName(float it)
+{
+	switch(it)
+	{
+		case IT_SHELLS:      return "shells";
+		case IT_NAILS:       return "nails";
+		case IT_ROCKETS:     return "rockets";
+		case IT_CELLS:       return "cells";
+
+		// add more things here (health, armor)
+		default:             error("requested item has no counter field name");
+	}
+}
+
 .float max_armorvalue;
 
 void Item_Respawn (void)
@@ -12,205 +43,247 @@
 	pointparticles(particleeffectnum("item_respawn"), self.origin + 0.5 * (self.mins + self.maxs), '0 0 0', 1);
 }
 
-void Item_Touch (void)
+float Item_GiveTo(entity item, entity player)
 {
-	local entity oldself;
-	local float _switchweapon;
-	local float pickedup;
-	local float it;
+	float _switchweapon;
+	float pickedup;
+	float it;
+	float i;
+	entity e;
 
-	// remove the item if it's currnetly in a NODROP brush or hits a NOIMPACT surface (such as sky)
-	if (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
-	{
-		remove(self);
-		return;
-	}
-	if (other.classname != "player")
-		return;
-	if (other.deadflag)
-		return;
-	if (self.solid != SOLID_TRIGGER)
-		return;
-	if (self.owner == other)
-		return;
-
-	// if nothing happens to other, just return without taking the item
+	// if nothing happens to player, just return without taking the item
 	pickedup = FALSE;
 	_switchweapon = FALSE;
 
 	if (g_minstagib)
 	{
 		_switchweapon = TRUE;
-		if (self.ammo_cells)
+		if (item.ammo_cells)
 		{
 			pickedup = TRUE;
 			// play some cool sounds ;)
-			centerprint(other, "\n");
-			if(other.health <= 5)
-				announce(other, "announcer/robotic/lastsecond.ogg");
-			else if(other.health < 50)
-				announce(other, "announcer/robotic/narrowly.ogg");
+			centerprint(player, "\n");
+			if(player.health <= 5)
+				announce(player, "announcer/robotic/lastsecond.ogg");
+			else if(player.health < 50)
+				announce(player, "announcer/robotic/narrowly.ogg");
 			// sound not available
-			// else if(self.items == IT_CELLS)
-			//	play2(other, "announce/robotic/ammo.ogg");
+			// else if(item.items == IT_CELLS)
+			//	play2(player, "announce/robotic/ammo.ogg");
 
-			if (self.items & IT_NEX)
-				W_GiveWeapon (other, IT_NEX, "Nex");
-			if (self.ammo_cells)
-				other.ammo_cells = min (other.ammo_cells + cvar("g_minstagib_ammo_drop"), 999);
-			other.health = 100;
+			if (item.weapons & WEPBIT_MINSTANEX)
+				W_GiveWeapon (player, WEP_MINSTANEX, "Nex");
+			if (item.ammo_cells)
+				player.ammo_cells = min (player.ammo_cells + cvar("g_minstagib_ammo_drop"), 999);
+			player.health = 100;
 		}
 
 		// extralife powerup
-		if (self.max_health)
+		if (item.max_health)
 		{
 			pickedup = TRUE;
 			// sound not available
-			// play2(other, "announce/robotic/extra.ogg\nplay2 announce/robotic/_lives.ogg");
-			other.armorvalue = other.armorvalue + cvar("g_minstagib_extralives");
-			sprint(other, "^3You picked up some extra lives\n");
+			// play2(player, "announce/robotic/extra.ogg\nplay2 announce/robotic/_lives.ogg");
+			player.armorvalue = player.armorvalue + cvar("g_minstagib_extralives");
+			sprint(player, "^3You picked up some extra lives\n");
 		}
 
 		// invis powerup
-		if (self.strength_finished)
+		if (item.strength_finished)
 		{
 			pickedup = TRUE;
 			// sound not available
-			// play2(other, "announce/robotic/invisible.ogg");
-			other.strength_finished = max(other.strength_finished, time) + cvar("g_balance_powerup_strength_time");
+			// play2(player, "announce/robotic/invisible.ogg");
+			player.strength_finished = max(player.strength_finished, time) + cvar("g_balance_powerup_strength_time");
 		}
 
 		// speed powerup
-		if (self.invincible_finished)
+		if (item.invincible_finished)
 		{
 			pickedup = TRUE;
 			// sound not available
-			// play2(other, "announce/robotic/speed.ogg");
-			other.invincible_finished = max(other.invincible_finished, time) + cvar("g_balance_powerup_strength_time");
+			// play2(player, "announce/robotic/speed.ogg");
+			player.invincible_finished = max(player.invincible_finished, time) + cvar("g_balance_powerup_strength_time");
 		}
 	}
 	else
 	{
 		if (cvar("deathmatch") == 2 || cvar("g_weapon_stay"))
 		{
-			if (self.flags & FL_WEAPON && other.items & self.items && self.classname != "droppedweapon")
-				return;
-			if (other.items & self.items && self.flags & FL_TOSSED)	// don't let players stack ammo by tossing weapons
-				return;
+			if (item.flags & FL_WEAPON && player.weapons & item.weapons && item.classname != "droppedweapon")
+				return 0;
+			if (player.weapons & item.weapons && item.flags & FL_TOSSED)	// don't let players stack ammo by tossing weapons
+				return 0;
 		}
 
 		// in case the player has autoswitch enabled do the following:
 		// if the player is using their best weapon before items are given, they
 		// probably want to switch to an even better weapon after items are given
-		if (other.autoswitch)
-		if (other.switchweapon == w_getbestweapon(other))
+		if (player.autoswitch)
+		if (player.switchweapon == w_getbestweapon(player))
 			_switchweapon = TRUE;
 
-		if (self.ammo_shells)
-		if (other.ammo_shells < g_pickup_shells_max)
+		if not(player.weapons & W_WeaponBit(player.switchweapon))
+			_switchweapon = TRUE;
+
+		if (item.ammo_shells)
+		if (player.ammo_shells < g_pickup_shells_max)
 		{
 			pickedup = TRUE;
-			other.ammo_shells = min (other.ammo_shells + self.ammo_shells, g_pickup_shells_max);
+			player.ammo_shells = min (player.ammo_shells + item.ammo_shells, g_pickup_shells_max);
 		}
-		if (self.ammo_nails)
-		if (other.ammo_nails < g_pickup_nails_max)
+		if (item.ammo_nails)
+		if (player.ammo_nails < g_pickup_nails_max)
 		{
 			pickedup = TRUE;
-			other.ammo_nails = min (other.ammo_nails + self.ammo_nails, g_pickup_nails_max);
+			player.ammo_nails = min (player.ammo_nails + item.ammo_nails, g_pickup_nails_max);
 		}
-		if (self.ammo_rockets)
-		if (other.ammo_rockets < g_pickup_rockets_max)
+		if (item.ammo_rockets)
+		if (player.ammo_rockets < g_pickup_rockets_max)
 		{
 			pickedup = TRUE;
-			other.ammo_rockets = min (other.ammo_rockets + self.ammo_rockets, g_pickup_rockets_max);
+			player.ammo_rockets = min (player.ammo_rockets + item.ammo_rockets, g_pickup_rockets_max);
 		}
-		if (self.ammo_cells)
-		if (other.ammo_cells < g_pickup_cells_max)
+		if (item.ammo_cells)
+		if (player.ammo_cells < g_pickup_cells_max)
 		{
 			pickedup = TRUE;
-			other.ammo_cells = min (other.ammo_cells + self.ammo_cells, g_pickup_cells_max);
+			player.ammo_cells = min (player.ammo_cells + item.ammo_cells, g_pickup_cells_max);
 		}
 
-		if (self.flags & FL_WEAPON)
-		if ((it = self.items - (self.items & other.items)))
+		if (item.flags & FL_WEAPON)
+		if ((it = item.weapons - (item.weapons & player.weapons)))
 		{
 			pickedup = TRUE;
-			if (it & IT_UZI)                W_GiveWeapon (other, IT_UZI, self.netname);
-			if (it & IT_SHOTGUN)            W_GiveWeapon (other, IT_SHOTGUN, self.netname);
-			if (it & IT_GRENADE_LAUNCHER)   W_GiveWeapon (other, IT_GRENADE_LAUNCHER, self.netname);
-			if (it & IT_ELECTRO)            W_GiveWeapon (other, IT_ELECTRO, self.netname);
-			if (it & IT_NEX)                W_GiveWeapon (other, IT_NEX, self.netname);
-			if (it & IT_HAGAR)              W_GiveWeapon (other, IT_HAGAR, self.netname);
-			if (it & IT_ROCKET_LAUNCHER)    W_GiveWeapon (other, IT_ROCKET_LAUNCHER, self.netname);
-			if (it & IT_CRYLINK)            W_GiveWeapon (other, IT_CRYLINK, self.netname);
+			for(i = WEP_FIRST; i <= WEP_LAST; ++i)
+			{
+				e = get_weaponinfo(i);
+				if(it & e.weapons)
+					W_GiveWeapon (player, e.weapon, item.netname);
+			}
 		}
 
-		if (self.strength_finished)
+		if (item.strength_finished)
 		{
 			pickedup = TRUE;
-			other.strength_finished = max(other.strength_finished, time) + cvar("g_balance_powerup_strength_time");
+			player.strength_finished = max(player.strength_finished, time) + cvar("g_balance_powerup_strength_time");
 		}
-		if (self.invincible_finished)
+		if (item.invincible_finished)
 		{
 			pickedup = TRUE;
-			other.invincible_finished = max(other.invincible_finished, time) + cvar("g_balance_powerup_invincible_time");
+			player.invincible_finished = max(player.invincible_finished, time) + cvar("g_balance_powerup_invincible_time");
 		}
-		//if (self.speed_finished)
+		//if (item.speed_finished)
 		//{
 		//	pickedup = TRUE;
-		//	other.speed_finished = max(other.speed_finished, time) + cvar("g_balance_powerup_speed_time");
+		//	player.speed_finished = max(player.speed_finished, time) + cvar("g_balance_powerup_speed_time");
 		//}
-		//if (self.slowmo_finished)
+		//if (item.slowmo_finished)
 		//{
 		//	pickedup = TRUE;
-		//	other.slowmo_finished = max(other.slowmo_finished, time) + (cvar("g_balance_powerup_slowmo_time") * cvar("g_balance_powerup_slowmo_speed"));
+		//	player.slowmo_finished = max(player.slowmo_finished, time) + (cvar("g_balance_powerup_slowmo_time") * cvar("g_balance_powerup_slowmo_speed"));
 		//}
 
-		if (self.health)
-		if (other.health < self.max_health)
+		if (item.health)
+		if (player.health < item.max_health)
 		{
 			pickedup = TRUE;
-			other.health = min(other.health + self.health, self.max_health);
-			other.pauserothealth_finished = max(other.pauserothealth_finished, time + cvar("g_balance_pause_health_rot"));
+			player.health = min(player.health + item.health, item.max_health);
+			player.pauserothealth_finished = max(player.pauserothealth_finished, time + cvar("g_balance_pause_health_rot"));
 		}
-		if (self.armorvalue)
-		if (other.armorvalue < self.max_armorvalue)
+		if (item.armorvalue)
+		if (player.armorvalue < item.max_armorvalue)
 		{
 			pickedup = TRUE;
-			other.armorvalue = min(other.armorvalue + self.armorvalue, self.max_armorvalue);
-			other.pauserotarmor_finished = max(other.pauserotarmor_finished, time + cvar("g_balance_pause_armor_rot"));
+			player.armorvalue = min(player.armorvalue + item.armorvalue, item.max_armorvalue);
+			player.pauserotarmor_finished = max(player.pauserotarmor_finished, time + cvar("g_balance_pause_armor_rot"));
 		}
 	}
 
+	// always eat teamed entities
+	if(item.team)
+		pickedup = TRUE;
+
 	if (!pickedup)
-		return;
+		return 0;
 
-	sound (other, CHAN_AUTO, self.item_pickupsound, VOL_BASE, ATTN_NORM);
+	sound (player, CHAN_AUTO, item.item_pickupsound, VOL_BASE, ATTN_NORM);
+	if (_switchweapon)
+		W_SwitchWeapon_Force(player, w_getbestweapon(player));
 
-	oldself = self;
-	self = other;
+	return 1;
+}
 
-	if (_switchweapon)
-		self.switchweapon = w_getbestweapon(self);
-	if (self.switchweapon != self.weapon)
-		self.cnt = self.weapon;
+void Item_Touch (void)
+{
+	entity e, head;
 
-	self = oldself;
+	// remove the item if it's currnetly in a NODROP brush or hits a NOIMPACT surface (such as sky)
+	if (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
+	{
+		remove(self);
+		return;
+	}
+	if (other.classname != "player")
+		return;
+	if (other.deadflag)
+		return;
+	if (self.solid != SOLID_TRIGGER)
+		return;
+	if (self.owner == other)
+		return;
 
+	if(!Item_GiveTo(self, other))
+		return;
+
 	if (self.classname == "droppedweapon")
 		remove (self);
-	else if(self.flags & FL_WEAPON && (cvar("deathmatch") == 2 || cvar("g_weapon_stay")))
+	else if((self.flags & FL_WEAPON) && !self.team && (cvar("deathmatch") == 2 || cvar("g_weapon_stay")))
 		return;
 	else
 	{
 		self.solid = SOLID_NOT;
 		self.model = string_null;
-		self.nextthink = time + self.respawntime;
-		self.think = Item_Respawn;
-		setorigin (self, self.origin);
+		if(self.team)
+		{
+			RandomSelection_Init();
+			for(head = world; (head = findfloat(head, team, self.team)); ) if(head.flags & FL_ITEM)
+				RandomSelection_Add(head, 0, 1, 0);
+			e = RandomSelection_chosen_ent;
+		}
+		else
+			e = self;
+		e.nextthink = time + self.respawntime;
+		e.think = Item_Respawn;
 	}
 }
 
+void Item_FindTeam()
+{
+	entity head, e;
+
+	if(self.effects & EF_NODRAW)
+	{
+		// marker for item team search
+		dprint("Initializing item team ", ftos(self.team), "\n");
+		RandomSelection_Init();
+		for(head = world; (head = findfloat(head, team, self.team)); ) if(head.flags & FL_ITEM)
+			RandomSelection_Add(head, 0, 1, 0);
+		e = RandomSelection_chosen_ent;
+
+		for(head = world; (head = findfloat(head, team, self.team)); ) if(head.flags & FL_ITEM)
+		{
+			if(head != e)
+			{
+				// make it a non-spawned item
+				head.solid = SOLID_NOT;
+				head.model = string_null;
+			}
+			head.effects = head.effects - (head.effects & EF_NODRAW);
+		}
+	}
+}
+
 // Savage: used for item garbage-collection
 // TODO: perhaps nice special effect?
 void RemoveItem(void) /*FIXDECL*/
@@ -226,7 +299,7 @@
 float weapon_pickupevalfunc(entity player, entity item)
 {
 	// if we already have the weapon, rate it 1/5th normal value
-	if ((player.items & item.items) == item.items)
+	if ((player.weapons & item.weapons) == item.weapons)
 		return item.bot_pickupbasevalue * 0.2;
 	return item.bot_pickupbasevalue;
 };
@@ -257,18 +330,12 @@
 	if (player.health < item.max_health)
 		c = c + max(0, 1 - player.health / item.max_health);
 
-	if (cvar("deathmatch") == 2) // weapon stay is on, so weapons the player already has are of no interest
-	if (item.flags & FL_WEAPON)
-	if (self.items & item.items)
-	if (item.classname != "droppedweapon")
-		c = 0;
-
 	return item.bot_pickupbasevalue * c;
 };
 
 
 .float is_item;
-void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, string itemname, float itemid, float itemflags, float(entity player, entity item) pickupevalfunc, float pickupbasevalue)
+void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, string itemname, float itemid, float weaponid, float itemflags, float(entity player, entity item) pickupevalfunc, float pickupbasevalue)
 {
 	startitem_failed = FALSE;
 
@@ -374,9 +441,9 @@
 			self.is_item = TRUE;
 		}
 
-		itemsInMap |= itemid;
+		weaponsInMap |= weaponid;
 
-		if(g_lms || g_instagib || g_rocketarena)
+		if(g_lms || g_rocketarena)
 		{
 			startitem_failed = TRUE;
 			remove(self);
@@ -403,9 +470,7 @@
 		precache_sound (pickupsound);
 		precache_sound ("misc/itemrespawn.wav");
 
-		if(itemid & (IT_STRENGTH | IT_INVINCIBLE | IT_HEALTH | IT_ARMOR | IT_KEY1 | IT_KEY2 |
-					IT_ROCKET_LAUNCHER | IT_HAGAR | IT_NEX | IT_CRYLINK | IT_ELECTRO |
-					IT_GRENADE_LAUNCHER | IT_UZI | IT_SHOTGUN | IT_LASER))
+		if((itemid & (IT_STRENGTH | IT_INVINCIBLE | IT_HEALTH | IT_ARMOR | IT_KEY1 | IT_KEY2)) || (weaponid & WEPBIT_ALL))
 			self.target = "###item###"; // for finding the nearest item using find()
 	}
 
@@ -419,6 +484,7 @@
 		self.respawntime = defaultrespawntime;
 	self.netname = itemname;
 	self.items = itemid;
+	self.weapons = weaponid;
 	self.flags = FL_ITEM | itemflags;
 	self.touch = Item_Touch;
 	setmodel (self, self.mdl); // precision set below
@@ -435,6 +501,12 @@
 
 	if (cvar("g_fullbrightitems"))
 		self.effects = self.effects | EF_FULLBRIGHT;
+	
+	if(self.team)
+	{
+		self.effects = self.effects | EF_NODRAW; // marker for item team search
+		InitializeEntity(self, Item_FindTeam, INITPRIO_FINDTARGET);
+	}
 }
 
 /* replace items in minstagib
@@ -450,7 +522,7 @@
 		self.ammo_cells = 25;
 		StartItem ("models/weapons/g_nex.md3",
 			"weapons/weaponpickup.wav", 15,
-			"Nex Gun", IT_NEX, FL_WEAPON, generic_pickupevalfunc, 1000);
+			"MinstaNex", 0, WEPBIT_MINSTANEX, FL_WEAPON, generic_pickupevalfunc, 1000);
 		return;
 	}
 
@@ -463,7 +535,7 @@
 		self.ammo_cells = 1;
 		StartItem ("models/items/a_cells.md3",
 			"misc/itempickup.wav", 45,
-			"Nex Ammo", IT_CELLS, 0, generic_pickupevalfunc, 100);
+			"Nex Ammo", IT_CELLS, 0, 0, generic_pickupevalfunc, 100);
 		return;
 	}
 
@@ -482,16 +554,16 @@
 		self.effects = EF_ADDITIVE;
 		self.strength_finished = 30;
 		StartItem ("models/items/g_strength.md3",
-			"misc/powerup.wav", 120,
-			"Invisibility", IT_STRENGTH, FL_POWERUP, generic_pickupevalfunc, 1000);
+			"misc/powerup.wav", g_pickup_respawntime_powerup,
+			"Invisibility", IT_STRENGTH, 0, FL_POWERUP, generic_pickupevalfunc, 1000);
 	}
 	// replace with extra lives
 	if (itemid == IT_NAILS)
 	{
 		self.max_health = 1;
 		StartItem ("models/items/g_h100.md3",
-			"misc/megahealth.wav", 120,
-			"Extralife", IT_NAILS, FL_POWERUP, generic_pickupevalfunc, 1000);
+			"misc/megahealth.wav", g_pickup_respawntime_powerup,
+			"Extralife", IT_NAILS, 0, FL_POWERUP, generic_pickupevalfunc, 1000);
 
 	}
 	// replace with speed
@@ -500,8 +572,8 @@
 		self.effects = EF_ADDITIVE;
 		self.invincible_finished = 30;
 		StartItem ("models/items/g_invincible.md3",
-			"misc/powerup_shield.wav", 120,
-			"Speed", IT_INVINCIBLE, FL_POWERUP, generic_pickupevalfunc, 1000);
+			"misc/powerup_shield.wav", g_pickup_respawntime_powerup,
+			"Speed", IT_INVINCIBLE, 0, FL_POWERUP, generic_pickupevalfunc, 1000);
 	}
 
 }
@@ -513,74 +585,81 @@
 }
 
 float weaponswapping;
+float internalteam;
 
+void weapon_defaultspawnfunc(float wpn)
+{
+	entity e;
+	float t;
+	var .float ammofield;
+	string s;
+	entity oldself;
+	float i;
+
+	if(self.classname != "droppedweapon" && self.classname != "replacedweapon")
+	{
+		s = cvar_string(strcat("g_weaponreplace_", ftos(wpn)));
+		t = tokenize(s);
+		if(t >= 2)
+		{
+			self.team = --internalteam;
+			for(i = 1; i < t; ++i)
+			{
+				oldself = self;
+				self = spawn();
+				copyentity(oldself, self);
+				self.classname = "replacedweapon";
+				weapon_defaultspawnfunc(stof(argv(i)));
+				self = oldself;
+				print("replaced by ", argv(i), "\n");
+			}
+		}
+		if(t >= 1)
+			wpn = stof(argv(0));
+	}
+
+	e = get_weaponinfo(wpn);
+
+	t = g_pickup_respawntime_short;
+
+	if(e.items && e.items != IT_SUPERWEAPON)
+	{
+		ammofield = Item_CounterField(e.items);
+		if(!self.ammofield)
+			self.ammofield = cvar(strcat("g_pickup_", Item_CounterFieldName(e.items)));
+	}
+
+	if(e.items == IT_SUPERWEAPON)
+		t = g_pickup_respawntime_powerup;
+
+	StartItem(e.model, "weapons/weaponpickup.wav", t, e.message, 0, e.weapons, FL_WEAPON, weapon_pickupevalfunc, e.bot_pickupbasevalue);
+	if (self.modelindex) // don't precache if self was removed
+		weapon_action(e.weapon, WR_PRECACHE);
+}
+
 void spawnfunc_weapon_shotgun (void);
 void spawnfunc_weapon_uzi (void) {
-	if(!weaponswapping)
 	if(q3acompat_machineshotgunswap)
 	if(self.classname != "droppedweapon")
 	{
-		weaponswapping = TRUE;
-		spawnfunc_weapon_shotgun();
-		weaponswapping = FALSE;
+		weapon_defaultspawnfunc(WEP_SHOTGUN);
 		return;
 	}
-
-	if(!self.ammo_nails)
-		self.ammo_nails = cvar("g_pickup_nails");
-	StartItem ("models/weapons/g_uzi.md3", "weapons/weaponpickup.wav", 15, W_Name(WEP_UZI), IT_UZI, FL_WEAPON, weapon_pickupevalfunc, 5000);
-	if (self.modelindex) // don't precache if self was removed
-		weapon_action(WEP_UZI, WR_PRECACHE);
+	weapon_defaultspawnfunc(WEP_UZI);
 }
 
 void spawnfunc_weapon_shotgun (void) {
-	if(!weaponswapping)
 	if(q3acompat_machineshotgunswap)
 	if(self.classname != "droppedweapon")
 	{
-		weaponswapping = TRUE;
-		spawnfunc_weapon_uzi();
-		weaponswapping = FALSE;
+		weapon_defaultspawnfunc(WEP_UZI);
 		return;
 	}
-
-	if(!self.ammo_shells)
-		self.ammo_shells = cvar("g_pickup_shells");
-	StartItem ("models/weapons/g_shotgun.md3", "weapons/weaponpickup.wav", 15, W_Name(WEP_SHOTGUN), IT_SHOTGUN, FL_WEAPON, weapon_pickupevalfunc, 2500);
-	if (self.modelindex) // don't precache if self was removed
-		weapon_action(WEP_SHOTGUN, WR_PRECACHE);
+	weapon_defaultspawnfunc(WEP_SHOTGUN);
 }
 
-void spawnfunc_weapon_grenadelauncher (void)
-{
-	if(!self.ammo_rockets)
-		self.ammo_rockets = cvar("g_pickup_rockets");
-	StartItem ("models/weapons/g_gl.md3", "weapons/weaponpickup.wav", 15, W_Name(WEP_GRENADE_LAUNCHER), IT_GRENADE_LAUNCHER, FL_WEAPON, weapon_pickupevalfunc, 5000);
-	if (self.modelindex) // don't precache if self was removed
-		weapon_action(WEP_GRENADE_LAUNCHER, WR_PRECACHE);
-}
-
-void spawnfunc_weapon_electro (void)
-{
-	if(!self.ammo_cells)
-		self.ammo_cells = cvar("g_pickup_cells");
-	StartItem ("models/weapons/g_electro.md3", "weapons/weaponpickup.wav", 15, W_Name(WEP_ELECTRO), IT_ELECTRO, FL_WEAPON, weapon_pickupevalfunc, 5000);
-	if (self.modelindex) // don't precache if self was removed
-		weapon_action(WEP_ELECTRO, WR_PRECACHE);
-}
-
-void spawnfunc_weapon_crylink (void)
-{
-	if(!self.ammo_cells)
-		self.ammo_cells = cvar("g_pickup_cells");
-	StartItem ("models/weapons/g_crylink.md3", "weapons/weaponpickup.wav", 15, W_Name(WEP_CRYLINK), IT_CRYLINK, FL_WEAPON, weapon_pickupevalfunc, 2500);
-	if (self.modelindex) // don't precache if self was removed
-		weapon_action(WEP_CRYLINK, WR_PRECACHE);
-}
-
 void spawnfunc_weapon_nex (void)
 {
-	float nextime;
 	if (g_minstagib)
 	{
 		minstagib_items(IT_CELLS);
@@ -588,25 +667,19 @@
 		self.nextthink = time + cvar("sys_ticrate");
 		return;
 	}
-	if(!self.ammo_cells)
-		self.ammo_cells = cvar("g_pickup_cells");
-	nextime = cvar("g_balance_nex_respawntime_modifier");
-	if(nextime)
-		nextime = 15 * nextime;
-	else
-		nextime = 15;
-	StartItem ("models/weapons/g_nex.md3", "weapons/weaponpickup.wav", nextime, W_Name(WEP_NEX), IT_NEX, FL_WEAPON, weapon_pickupevalfunc, 10000);
-	if (self.modelindex) // don't precache if self was removed
-		weapon_action(WEP_NEX, WR_PRECACHE);
+	weapon_defaultspawnfunc(WEP_NEX);
 }
 
-void spawnfunc_weapon_hagar (void)
+void spawnfunc_weapon_minstanex (void)
 {
-	if(!self.ammo_rockets)
-		self.ammo_rockets = cvar("g_pickup_rockets");
-	StartItem ("models/weapons/g_hagar.md3", "weapons/weaponpickup.wav", 15, W_Name(WEP_HAGAR), IT_HAGAR, FL_WEAPON, weapon_pickupevalfunc, 5000);
-	if (self.modelindex) // don't precache if self was removed
-		weapon_action(WEP_HAGAR, WR_PRECACHE);
+	if (g_minstagib)
+	{
+		minstagib_items(IT_CELLS);
+		self.think = minst_remove_item;
+		self.nextthink = time + cvar("sys_ticrate");
+		return;
+	}
+	weapon_defaultspawnfunc(WEP_MINSTANEX);
 }
 
 void spawnfunc_weapon_rocketlauncher (void)
@@ -618,17 +691,13 @@
 		self.nextthink = time + cvar("sys_ticrate");
 		return;
 	}
-	if(!self.ammo_rockets)
-		self.ammo_rockets = g_pickup_rockets;
-	StartItem ("models/weapons/g_rl.md3", "weapons/weaponpickup.wav", 15, W_Name(WEP_ROCKET_LAUNCHER), IT_ROCKET_LAUNCHER, FL_WEAPON, weapon_pickupevalfunc, 10000);
-	if (self.modelindex) // don't precache if self was removed
-		weapon_action(WEP_ROCKET_LAUNCHER, WR_PRECACHE);
+	weapon_defaultspawnfunc(WEP_ROCKET_LAUNCHER);
 }
 
 void spawnfunc_item_rockets (void) {
 	if(!self.ammo_rockets)
 		self.ammo_rockets = g_pickup_rockets;
-	StartItem ("models/items/a_rockets.md3", "misc/itempickup.wav", 15, "rockets", IT_ROCKETS, 0, commodity_pickupevalfunc, 3000);
+	StartItem ("models/items/a_rockets.md3", "misc/itempickup.wav", g_pickup_respawntime_short, "rockets", IT_ROCKETS, 0, 0, commodity_pickupevalfunc, 3000);
 }
 
 void spawnfunc_item_shells (void);
@@ -645,13 +714,13 @@
 
 	if(!self.ammo_nails)
 		self.ammo_nails = g_pickup_nails;
-	StartItem ("models/items/a_bullets.mdl", "misc/itempickup.wav", 15, "bullets", IT_NAILS, 0, commodity_pickupevalfunc, 2000);
+	StartItem ("models/items/a_bullets.mdl", "misc/itempickup.wav", g_pickup_respawntime_short, "bullets", IT_NAILS, 0, 0, commodity_pickupevalfunc, 2000);
 }
 
 void spawnfunc_item_cells (void) {
 	if(!self.ammo_cells)
 		self.ammo_cells = g_pickup_cells;
-	StartItem ("models/items/a_cells.md3", "misc/itempickup.wav", 15, "cells", IT_CELLS, 0, commodity_pickupevalfunc, 2000);
+	StartItem ("models/items/a_cells.md3", "misc/itempickup.wav", g_pickup_respawntime_short, "cells", IT_CELLS, 0, 0, commodity_pickupevalfunc, 2000);
 }
 
 void spawnfunc_item_shells (void) {
@@ -667,7 +736,7 @@
 
 	if(!self.ammo_shells)
 		self.ammo_shells = g_pickup_shells;
-	StartItem ("models/items/a_shells.md3", "misc/itempickup.wav", 15, "shells", IT_SHELLS, 0, commodity_pickupevalfunc, 500);
+	StartItem ("models/items/a_shells.md3", "misc/itempickup.wav", g_pickup_respawntime_short, "shells", IT_SHELLS, 0, 0, commodity_pickupevalfunc, 500);
 }
 
 void spawnfunc_item_armor_small (void) {
@@ -675,7 +744,7 @@
 		self.armorvalue = g_pickup_armorsmall;
 	if(!self.max_armorvalue)
 		self.max_armorvalue = g_pickup_armorsmall_max;
-	StartItem ("models/items/g_a1.md3", "misc/armor1.wav", 15, "5 Armor", IT_ARMOR_SHARD, 0, commodity_pickupevalfunc, 1000);
+	StartItem ("models/items/g_a1.md3", "misc/armor1.wav", g_pickup_respawntime_short, "5 Armor", IT_ARMOR_SHARD, 0, 0, commodity_pickupevalfunc, 1000);
 }
 
 void spawnfunc_item_armor_medium (void) {
@@ -683,7 +752,7 @@
 		self.armorvalue = g_pickup_armormedium;
 	if(!self.max_armorvalue)
 		self.max_armorvalue = g_pickup_armormedium_max;
-	StartItem ("models/items/g_armormedium.md3", "misc/armor1.wav", 20, "25 Armor", IT_ARMOR, 0, commodity_pickupevalfunc, 20000);
+	StartItem ("models/items/g_armormedium.md3", "misc/armor1.wav", g_pickup_respawntime_medium, "25 Armor", IT_ARMOR, 0, 0, commodity_pickupevalfunc, 20000);
 }
 
 void spawnfunc_item_armor_large (void) {
@@ -691,7 +760,7 @@
 		self.armorvalue = g_pickup_armorlarge;
 	if(!self.max_armorvalue)
 		self.max_armorvalue = g_pickup_armorlarge_max;
-	StartItem ("models/items/g_a25.md3", "misc/armor25.wav", 30, "100 Armor", IT_ARMOR, 0, commodity_pickupevalfunc, 20000);
+	StartItem ("models/items/g_a25.md3", "misc/armor25.wav", g_pickup_respawntime_long, "100 Armor", IT_ARMOR, 0, 0, commodity_pickupevalfunc, 20000);
 }
 
 void spawnfunc_item_health_small (void) {
@@ -699,7 +768,7 @@
 		self.max_health = g_pickup_healthsmall_max;
 	if(!self.health)
 		self.health = g_pickup_healthsmall;
-	StartItem ("models/items/g_h1.md3", "misc/minihealth.wav", 15, "5 Health", IT_5HP, 0, commodity_pickupevalfunc, 20000);
+	StartItem ("models/items/g_h1.md3", "misc/minihealth.wav", g_pickup_respawntime_short, "5 Health", IT_5HP, 0, 0, commodity_pickupevalfunc, 20000);
 }
 
 void spawnfunc_item_health_medium (void) {
@@ -707,7 +776,7 @@
 		self.max_health = g_pickup_healthmedium_max;
 	if(!self.health)
 		self.health = g_pickup_healthmedium;
-	StartItem ("models/items/g_h25.md3", "misc/mediumhealth.wav", 15, "25 Health", IT_25HP, 0, commodity_pickupevalfunc, 20000);
+	StartItem ("models/items/g_h25.md3", "misc/mediumhealth.wav", g_pickup_respawntime_short, "25 Health", IT_25HP, 0, 0, commodity_pickupevalfunc, 20000);
 }
 
 void spawnfunc_item_health_large (void) {
@@ -715,7 +784,7 @@
 		self.max_health = g_pickup_healthlarge_max;
 	if(!self.health)
 		self.health = g_pickup_healthlarge;
-	StartItem ("models/items/g_h50.md3", "misc/mediumhealth.wav", 20, "50 Health", IT_25HP, 0, commodity_pickupevalfunc, 20000);
+	StartItem ("models/items/g_h50.md3", "misc/mediumhealth.wav", g_pickup_respawntime_medium, "50 Health", IT_25HP, 0, 0, commodity_pickupevalfunc, 20000);
 }
 
 void spawnfunc_item_health_mega (void) {
@@ -732,7 +801,7 @@
 			self.max_health = g_pickup_healthmega_max;
 		if(!self.health)
 			self.health = g_pickup_healthmega;
-		StartItem ("models/items/g_h100.md3", "misc/megahealth.wav", 30, "100 Health", IT_HEALTH, 0, commodity_pickupevalfunc, 20000);
+		StartItem ("models/items/g_h100.md3", "misc/megahealth.wav", g_pickup_respawntime_long, "100 Health", IT_HEALTH, 0, 0, commodity_pickupevalfunc, 20000);
 	}
 }
 
@@ -755,7 +824,8 @@
 	} else {
 		precache_sound("weapons/strength_fire.wav");
 		self.strength_finished = 30;
-		self.effects = EF_ADDITIVE;StartItem ("models/items/g_strength.md3", "misc/powerup.wav", 120, "Strength Powerup", IT_STRENGTH, FL_POWERUP, generic_pickupevalfunc, 100000);
+		self.effects = EF_ADDITIVE;
+		StartItem ("models/items/g_strength.md3", "misc/powerup.wav", g_pickup_respawntime_powerup, "Strength Powerup", IT_STRENGTH, 0, FL_POWERUP, generic_pickupevalfunc, 100000);
 	}
 }
 
@@ -771,11 +841,9 @@
 	} else {
 		self.invincible_finished = 30;
 		self.effects = EF_ADDITIVE;
-		StartItem ("models/items/g_invincible.md3", "misc/powerup_shield.wav", 120, "Invulnerability", IT_INVINCIBLE, FL_POWERUP, generic_pickupevalfunc, 100000);
+		StartItem ("models/items/g_invincible.md3", "misc/powerup_shield.wav", g_pickup_respawntime_powerup, "Invulnerability", IT_INVINCIBLE, 0, FL_POWERUP, generic_pickupevalfunc, 100000);
 	}
 }
-//void item_speed (void) {self.speed_finished = 30;StartItem ("models/items/g_speed.md3", "misc/powerup.wav", 120, "Speed Powerup", IT_SPEED, FL_POWERUP, generic_pickupevalfunc, 10000);}
-//void item_slowmo (void) {self.slowmo_finished = 30;StartItem ("models/items/g_slowmo.md3", "misc/powerup.wav", 120, "Slow Motion", IT_SLOWMO, FL_POWERUP, generic_pickupevalfunc, 10000);}
 
 void spawnfunc_item_minst_cells (void) {
 	if (g_minstagib)
@@ -816,55 +884,218 @@
 	self.use = func_wall_use;
 }
 
-floatfield Item_CounterField(float it)
+float trigger_item_func_set(float a, float b)
 {
-	switch(it)
+	if(b == 0)
+		return a;
+	else if(b < 0)
+		return 0;
+	else
+		return b;
+}
+
+float trigger_item_func_min(float a, float b)
+{
+	if(b == 0)
+		return a;
+	else if(b < 0)
+		return 0;
+	else
+		return min(a, b);
+}
+
+float trigger_item_func_max(float a, float b)
+{
+	return max(a, b);
+}
+
+float trigger_item_func_bitset(float a, float b)
+{
+	return b;
+}
+
+float trigger_item_func_and(float a, float b)
+{
+	return a & b;
+}
+
+float trigger_item_func_or(float a, float b)
+{
+	return a | b;
+}
+
+float trigger_item_func_andnot(float a, float b)
+{
+	return a - (a & b);
+}
+
+float trigger_item_changed;
+void trigger_item_change(float binary, .float field, float(float a, float b) func, string sound_increase, string sound_decrease)
+{
+	float n, d;
+	n = func(activator.field, self.field);
+
+	if(binary)
 	{
-		case IT_SHELLS:      return ammo_shells;
-		case IT_NAILS:       return ammo_nails;
-		case IT_ROCKETS:     return ammo_rockets;
-		case IT_CELLS:       return ammo_cells;
-		case IT_5HP:         return health;
-		case IT_25HP:        return health;
-		case IT_HEALTH:      return health;
-		case IT_ARMOR_SHARD: return armorvalue;
-		case IT_ARMOR:       return armorvalue;
-		// add more things here (health, armor)
-		default:             error("requested item has no counter field");
+		d = n & activator.field;
+		if(d != n) // bits added?
+			d = +1;
+		else if(d != activator.field) // bits removed?
+			d = -1;
+		else
+			d = 0;
 	}
+	else
+		d = n - activator.field;
+
+	if(d < 0)
+	{
+		if(sound_decrease != "")
+			sound (activator, CHAN_AUTO, sound_decrease, VOL_BASE, ATTN_NORM);
+		trigger_item_changed = 1;
+	}
+	else if(d > 0)
+	{
+		if(sound_increase != "")
+			sound (activator, CHAN_AUTO, sound_increase, VOL_BASE, ATTN_NORM);
+		trigger_item_changed = 1;
+	}
+	activator.field = n;
 }
 
-float Item_WeaponCode(float it)
+void trigger_items_use (void)
 {
-	switch(it)
+	float h0, a0;
+	if(activator.classname != "player")
+		return;
+	if(activator.deadflag != DEAD_NO)
+		return;
+	EXACTTRIGGER_TOUCH;
+
+	entity e;
+	for(e = world; (e = find(e, classname, "droppedweapon")); )
+		if(e.enemy == activator)
+			remove(e);
+
+	float _switchweapon;
+	_switchweapon = FALSE;
+	if (activator.autoswitch)
+		if (activator.switchweapon == w_getbestweapon(activator))
+			_switchweapon = TRUE;
+
+	a0 = activator.armorvalue;
+	h0 = activator.health;
+	trigger_item_changed = 0;
+
+	if(self.spawnflags == 0) // SET
 	{
-		case IT_LASER:            return WEP_LASER;
-		case IT_SHOTGUN:          return WEP_SHOTGUN;
-		case IT_UZI:              return WEP_UZI;
-		case IT_GRENADE_LAUNCHER: return WEP_GRENADE_LAUNCHER;
-		case IT_ELECTRO:          return WEP_ELECTRO;
-		case IT_CRYLINK:          return WEP_CRYLINK;
-		case IT_NEX:              return WEP_NEX;
-		case IT_HAGAR:            return WEP_HAGAR;
-		case IT_ROCKET_LAUNCHER:  return WEP_ROCKET_LAUNCHER;
-		default:                  return 0;
+		trigger_item_change(0, ammo_shells, trigger_item_func_set, "misc/itempickup.wav", "");
+		trigger_item_change(0, ammo_nails, trigger_item_func_set, "misc/itempickup.wav", "");
+		trigger_item_change(0, ammo_rockets, trigger_item_func_set, "misc/itempickup.wav", "");
+		trigger_item_change(0, ammo_cells, trigger_item_func_set, "misc/itempickup.wav", "");
+		trigger_item_change(0, health, trigger_item_func_set, "misc/megahealth.wav", "");
+		trigger_item_change(0, armorvalue, trigger_item_func_set, "misc/armor25.wav", "");
+		trigger_item_change(1, items, trigger_item_func_bitset, "misc/powerup.wav", "");
+		trigger_item_change(1, weapons, trigger_item_func_bitset, "weapons/weaponpickup.wav", "");
+
+		if((self.items & activator.items) & IT_STRENGTH)
+			activator.strength_finished = time + self.strength_finished;
+		if((self.items & activator.items) & IT_INVINCIBLE)
+			activator.invincible_finished = time + self.invincible_finished;
 	}
+	else if(self.spawnflags == 1) // AND/MIN
+	{
+		trigger_item_change(0, ammo_shells, trigger_item_func_min, "misc/itempickup.wav", "");
+		trigger_item_change(0, ammo_nails, trigger_item_func_min, "misc/itempickup.wav", "");
+		trigger_item_change(0, ammo_rockets, trigger_item_func_min, "misc/itempickup.wav", "");
+		trigger_item_change(0, ammo_cells, trigger_item_func_min, "misc/itempickup.wav", "");
+		trigger_item_change(0, health, trigger_item_func_min, "misc/megahealth.wav", "");
+		trigger_item_change(0, armorvalue, trigger_item_func_min, "misc/armor25.wav", "");
+		trigger_item_change(1, items, trigger_item_func_and, "misc/powerup.wav", "");
+		trigger_item_change(1, weapons, trigger_item_func_and, "weapons/weaponpickup.wav", "");
+
+		if((self.items & activator.items) & IT_STRENGTH)
+			activator.strength_finished = min(activator.strength_finished, time + self.strength_finished);
+		if((self.items & activator.items) & IT_INVINCIBLE)
+			activator.invincible_finished = min(activator.invincible_finished, time + self.invincible_finished);
+	}
+	else if(self.spawnflags == 2) // OR/MAX
+	{
+		trigger_item_change(0, ammo_shells, trigger_item_func_max, "misc/itempickup.wav", "");
+		trigger_item_change(0, ammo_nails, trigger_item_func_max, "misc/itempickup.wav", "");
+		trigger_item_change(0, ammo_rockets, trigger_item_func_max, "misc/itempickup.wav", "");
+		trigger_item_change(0, ammo_cells, trigger_item_func_max, "misc/itempickup.wav", "");
+		trigger_item_change(0, health, trigger_item_func_max, "misc/megahealth.wav", "");
+		trigger_item_change(0, armorvalue, trigger_item_func_max, "misc/armor25.wav", "");
+		trigger_item_change(1, items, trigger_item_func_or, "misc/powerup.wav", "");
+		trigger_item_change(1, weapons, trigger_item_func_or, "weapons/weaponpickup.wav", "");
+
+		if((self.items & activator.items) & IT_STRENGTH)
+			activator.strength_finished = max(activator.strength_finished, time + self.strength_finished);
+		if((self.items & activator.items) & IT_INVINCIBLE)
+			activator.invincible_finished = max(activator.invincible_finished, time + self.invincible_finished);
+	}
+	else if(self.spawnflags == 4) // ANDNOT/MIN
+	{
+		trigger_item_change(0, ammo_shells, trigger_item_func_min, "misc/itempickup.wav", "");
+		trigger_item_change(0, ammo_nails, trigger_item_func_min, "misc/itempickup.wav", "");
+		trigger_item_change(0, ammo_rockets, trigger_item_func_min, "misc/itempickup.wav", "");
+		trigger_item_change(0, ammo_cells, trigger_item_func_min, "misc/itempickup.wav", "");
+		trigger_item_change(0, health, trigger_item_func_min, "misc/megahealth.wav", "");
+		trigger_item_change(0, armorvalue, trigger_item_func_min, "misc/armor25.wav", "");
+		trigger_item_change(1, items, trigger_item_func_andnot, "misc/powerup.wav", "");
+		trigger_item_change(1, weapons, trigger_item_func_andnot, "weapons/weaponpickup.wav", "");
+
+		if((self.items & activator.items) & IT_STRENGTH)
+			activator.strength_finished = min(activator.strength_finished, time + self.strength_finished);
+		if((self.items & activator.items) & IT_INVINCIBLE)
+			activator.invincible_finished = min(activator.invincible_finished, time + self.invincible_finished);
+	}
+
+	if not(activator.items & IT_STRENGTH)
+		activator.strength_finished = 0;
+	if not(activator.items & IT_INVINCIBLE)
+		activator.invincible_finished = 0;
+	
+	if(activator.health > h0)
+		activator.pauserothealth_finished = max(activator.pauserothealth_finished, time + cvar("g_balance_pause_health_rot"));
+	else if(activator.health < h0)
+		activator.pauseregen_finished = max(activator.pauseregen_finished, time + cvar("g_balance_pause_health_regen"));
+
+	if(activator.armorvalue > a0)
+		activator.pauserotarmor_finished = max(activator.pauserothealth_finished, time + cvar("g_balance_pause_health_rot"));
+
+	if not(activator.weapons & W_WeaponBit(activator.switchweapon))
+		_switchweapon = TRUE;
+	if(_switchweapon)
+		W_SwitchWeapon_Force(activator, w_getbestweapon(activator));
+
+	if(trigger_item_changed)
+		centerprint(activator, self.message);
 }
 
-void Item_SpawnByItemCode(float it)
+void spawnfunc_trigger_items (void)
 {
-	switch(it)
+	float n, i, j;
+	entity e;
+	EXACTTRIGGER_INIT;
+	self.use = trigger_items_use;
+	if(!self.strength_finished)
+		self.strength_finished = cvar("g_balance_powerup_strength_time");
+	if(!self.invincible_finished)
+		self.invincible_finished = cvar("g_balance_powerup_invincible_time");
+	
+	n = tokenize(self.netname);
+	for(i = 0; i < n; ++i)
 	{
-		case IT_SHOTGUN:          spawnfunc_weapon_shotgun(); break;
-		case IT_UZI:              spawnfunc_weapon_uzi(); break;
-		case IT_GRENADE_LAUNCHER: spawnfunc_weapon_grenadelauncher(); break;
-		case IT_ELECTRO:          spawnfunc_weapon_electro(); break;
-		case IT_CRYLINK:          spawnfunc_weapon_crylink(); break;
-		case IT_NEX:              spawnfunc_weapon_nex(); break;
-		case IT_HAGAR:            spawnfunc_weapon_hagar(); break;
-		case IT_ROCKET_LAUNCHER:  spawnfunc_weapon_rocketlauncher(); break;
-		// add all other item spawn functions here
-		default:
-			error("requested item can't be spawned");
+		if(argv(i) == "unlimited_ammo") self.items |= IT_UNLIMITED_AMMO;
+		if(argv(i) == "strength")       self.items |= IT_STRENGTH;
+		if(argv(i) == "invincible")     self.items |= IT_INVINCIBLE;
+		for(j = WEP_FIRST; j <= WEP_LAST; ++j)
+		{
+			e = get_weaponinfo(j);
+			if(argv(i) == e.netname)
+				self.weapons |= e.weapons;
+		}
 	}
 }

Modified: branches/nexuiz-2.0/data/qcsrc/server/t_plats.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/t_plats.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/t_plats.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -1252,6 +1252,9 @@
 	self.speed = 50;
 	self.use = fd_secret_use;
 	IFTARGETED
+	{
+	}
+	else
 		self.spawnflags |= SECRET_YES_SHOOT;
 
 	if(self.spawnflags&SECRET_YES_SHOOT)

Modified: branches/nexuiz-2.0/data/qcsrc/server/t_quake.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/t_quake.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/t_quake.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -53,5 +53,8 @@
 	require_spawnfunc_prefix = 1;
 	self.movetypesteplandevent = SUB_Null;
 	self.viewzoom = 0;
+	self.cvar_cl_weaponpriorities = "";
+	self.scores = 0;
+	self.teamscores = 0;
 }
 

Modified: branches/nexuiz-2.0/data/qcsrc/server/t_teleporters.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/t_teleporters.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/t_teleporters.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -1,80 +1,111 @@
-void Teleport_Touch (void)
+.entity pusher;
+void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity, vector telefragmin, vector telefragmax)
 {
 	entity head;
 	entity oldself;
+	entity telefragger;
 
-	if (other.health < 1)
-		return;
-	if (!other.flags & FL_CLIENT)	// FIXME: Make missiles firable through the teleport too
-		return;
+	if(teleporter.owner)
+		telefragger = teleporter.owner;
+	else
+		telefragger = player;
 
-	EXACTTRIGGER_TOUCH;
+	sound (player, CHAN_TRIGGER, "misc/teleport.wav", VOL_BASE, ATTN_NORM);
+	pointparticles(particleeffectnum("teleport"), player.origin, '0 0 0', 1);
 
-	sound (other, CHAN_TRIGGER, "misc/teleport.wav", VOL_BASE, ATTN_NORM);
-	pointparticles(particleeffectnum("teleport"), other.origin, '0 0 0', 1);
+	makevectors (to_angles);
+	pointparticles(particleeffectnum("teleport"), to + v_forward * 32, '0 0 0', 1);
 
-	/*
-	// Make teleport effect where the player left
-	sound (self, CHAN_TRIGGER, "misc/teleport.wav", VOL_BASE, ATTN_NORM);
-	pointparticles(particleeffectnum("teleport"), other.origin, '0 0 0', 1);
+	// Relocate the player
+	// assuming to allows PL_MIN to PL_MAX box and some more
+	setorigin (player, to + '0 0 1' * (1 - player.mins_z - 24));
+	player.angles = to_angles;
+	player.fixangle = TRUE;
+	player.velocity = to_velocity;
 
-	// Make teleport effect where the player arrived
-	sound (self.enemy, CHAN_TRIGGER, "misc/teleport.wav", VOL_BASE, ATTN_NORM);
-	*/
+	if(player.classname == "player")
+	{
+		RemoveGrapplingHook(player);
 
-	makevectors (self.enemy.mangle);
-	pointparticles(particleeffectnum("teleport"), self.enemy.origin + v_forward * 32, '0 0 0', 1);
+		// Kill anyone else in the teleporter box (NO MORE TDEATH)
+		if(player.takedamage && player.deadflag == DEAD_NO && !g_race)
+		{
+			vector deathmin;
+			vector deathmax;
+			float deathradius;
+			deathmin = player.absmin;
+			deathmax = player.absmax;
+			if(telefragmin != telefragmax)
+			{
+				if(deathmin_x > telefragmin_x) deathmin_x = telefragmin_x;
+				if(deathmin_y > telefragmin_y) deathmin_y = telefragmin_y;
+				if(deathmin_z > telefragmin_z) deathmin_z = telefragmin_z;
+				if(deathmax_x < telefragmax_x) deathmax_x = telefragmax_x;
+				if(deathmax_y < telefragmax_y) deathmax_y = telefragmax_y;
+				if(deathmax_z < telefragmax_z) deathmax_z = telefragmax_z;
+			}
+			deathradius = max(vlen(deathmin), vlen(deathmax));
+			for(head = findradius(player.origin, deathradius); head; head = head.chain)
+				if(head != player)
+					if(head.takedamage)
+						if(boxesoverlap(deathmin, deathmax, head.absmin, head.absmax))
+						{
+							if ((player.classname == "player") && (player.health >= 1))
+								Damage (head, teleporter, telefragger, 10000, DEATH_TELEFRAG, head.origin, '0 0 0');
+							else if (telefragger.health < 1) // corpses gib
+								Damage (head, teleporter, telefragger, 10000, DEATH_TELEFRAG, head.origin, '0 0 0');
+							else // dead bodies and monsters gib themselves instead of telefragging
+								Damage (telefragger, teleporter, telefragger, 10000, DEATH_TELEFRAG, telefragger.origin, '0 0 0');
+						}
+		}
 
-	// Relocate the player
-	setorigin (other, self.enemy.origin + '0 0 1' * (1 - other.mins_z - 24));
-	other.angles = self.enemy.mangle;
-	other.fixangle = TRUE;
-	other.velocity = v_forward * vlen(other.velocity);
-	RemoveGrapplingHook(other);
+		// hide myself a tic
+		player.effects = player.effects | EF_NODRAW;
+		if (player.weaponentity) // misuse FL_FLY to avoid EF_NODRAW on viewmodel
+			player.weaponentity.flags = player.weaponentity.flags | FL_FLY;
+		player.teleport_time = time + cvar("sys_ticrate");
 
-	// Kill anyone else in the teleporter box (NO MORE TDEATH)
-	if(other.takedamage && !g_race)
-	{
-		vector deathmin;
-		vector deathmax;
-		float deathradius;
-		deathmin = other.absmin;
-		deathmax = other.absmax;
-		deathradius = max(vlen(deathmin), vlen(deathmax));
-		for(head = findradius(other.origin, deathradius); head; head = head.chain)
-			if(head != other)
-				if(head.takedamage)
-					if(boxesoverlap(deathmin, deathmax, head.absmin, head.absmax))
-					{
-						if ((other.classname == "player") && (other.health >= 1))
-							Damage (head, self, other, 10000, DEATH_TELEFRAG, head.origin, '0 0 0');
-						else if (other.health < 1) // corpses gib
-							Damage (head, self, other, 10000, DEATH_TELEFRAG, head.origin, '0 0 0');
-						else // dead bodies and monsters gib themselves instead of telefragging
-							Damage (other, self, other, 10000, DEATH_TELEFRAG, other.origin, '0 0 0');
-					}
-	}
+		// player no longer is on ground
+		player.flags = player.flags - (player.flags & FL_ONGROUND);
 
-	// hide myself a tic
-	other.effects = other.effects | EF_NODRAW;
-	if (other.weaponentity) // misuse FL_FLY to avoid EF_NODRAW on viewmodel
-		other.weaponentity.flags = other.weaponentity.flags | FL_FLY;
-	other.teleport_time = time + cvar("sys_ticrate");
+		// reset tracking of oldvelocity for impact damage (sudden velocity changes)
+		player.oldvelocity = player.velocity;
 
-	other.flags = other.flags - (other.flags & FL_ONGROUND);
-	// reset tracking of oldvelocity for impact damage (sudden velocity changes)
-	other.oldvelocity = other.velocity;
-	// reset tracking of who pushed you into a hazard (for kill credit)
-	other.pushltime = 0;
+		// reset tracking of who pushed you into a hazard (for kill credit)
+		if(teleporter.owner)
+		{
+			player.pusher = teleporter.owner;
+			player.pushltime = time + cvar("g_maxpushtime");
+		}
+		else
+		{
+			player.pushltime = 0;
+		}
 
-	// stop player name display
-	{
-		oldself = self;
-		self = other;
-		ClearSelectedPlayer();
-		self = oldself;
+		// stop player name display
+		{
+			oldself = self;
+			self = player;
+			ClearSelectedPlayer();
+			self = oldself;
+		}
 	}
+}
 
+void Teleport_Touch (void)
+{
+	entity oldself;
+
+	if (other.health < 1)
+		return;
+	if (!other.flags & FL_CLIENT)	// FIXME: Make missiles firable through the teleport too
+		return;
+
+	EXACTTRIGGER_TOUCH;
+
+	makevectors(self.enemy.mangle);
+	TeleportPlayer(self, other, self.enemy.origin + '0 0 1' * (1 - other.mins_z - 24), self.enemy.mangle, v_forward * vlen(other.velocity), '0 0 0', '0 0 0');
+
 	if(self.enemy.target)
 	{
 		oldself = self;

Modified: branches/nexuiz-2.0/data/qcsrc/server/teamplay.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/teamplay.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/teamplay.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -383,8 +383,6 @@
 
 	if(g_minstagib)
 		mutator = "^2Minstagib ^1";
-	else if(g_instagib)
-		mutator = "^2Instagib ^1";
 	else if(g_rocketarena)
 		mutator = "^2Rocketarena ^1";
 	else if(g_nixnex)

Modified: branches/nexuiz-2.0/data/qcsrc/server/w_common.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/w_common.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/w_common.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -6,7 +6,7 @@
 	if (!wep)
 		return;
 
-	e.items = e.items | wep;
+	e.weapons = e.weapons | W_WeaponBit(wep);
 
 	oldself = self;
 	self = e;
@@ -141,7 +141,7 @@
 	{
 		if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
 		{
-			if (dtype == IT_SHOTGUN)
+			if (dtype == WEP_SHOTGUN)
 				pointparticles(particleeffectnum("shotgun_impact"), trace_endpos, trace_plane_normal * 1000, 1);
 			else
 				pointparticles(particleeffectnum("machinegun_impact"), trace_endpos, trace_plane_normal * 1000, 1);

Modified: branches/nexuiz-2.0/data/qcsrc/server/w_crylink.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/w_crylink.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/w_crylink.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -20,7 +20,7 @@
 		f = cvar("g_balance_crylink_primary_bouncedamagefactor");
 	if(self.alpha)
 		f *= self.alpha;
-	RadiusDamage (self, self.realowner, cvar("g_balance_crylink_primary_damage") * f, cvar("g_balance_crylink_primary_edgedamage") * f, cvar("g_balance_crylink_primary_radius"), world, cvar("g_balance_crylink_primary_force") * f, IT_CRYLINK);
+	RadiusDamage (self, self.realowner, cvar("g_balance_crylink_primary_damage") * f, cvar("g_balance_crylink_primary_edgedamage") * f, cvar("g_balance_crylink_primary_radius"), world, cvar("g_balance_crylink_primary_force") * f, WEP_CRYLINK);
 	if (finalhit)
 	{
 		remove (self);
@@ -49,7 +49,7 @@
 		f = cvar("g_balance_crylink_secondary_bouncedamagefactor");
 	if(self.alpha)
 		f *= self.alpha;
-	RadiusDamage (self, self.realowner, cvar("g_balance_crylink_secondary_damage") * f, cvar("g_balance_crylink_secondary_edgedamage") * f, cvar("g_balance_crylink_secondary_radius"), world, cvar("g_balance_crylink_secondary_force") * f, IT_CRYLINK);
+	RadiusDamage (self, self.realowner, cvar("g_balance_crylink_secondary_damage") * f, cvar("g_balance_crylink_secondary_edgedamage") * f, cvar("g_balance_crylink_secondary_radius"), world, cvar("g_balance_crylink_secondary_force") * f, WEP_CRYLINK);
 	if (finalhit)
 	{
 		remove (self);
@@ -67,7 +67,7 @@
 	local entity proj;
 	local vector s;
 
-	if (cvar("g_use_ammunition"))
+	if not(self.items & IT_UNLIMITED_AMMO)
 		self.ammo_cells = self.ammo_cells - cvar("g_balance_crylink_primary_ammo");
 
 	W_SetupShot (self, '25 8 -8', FALSE, 2, "weapons/crylink_fire.wav");
@@ -131,7 +131,7 @@
 	local float counter, shots;
 	local entity proj;
 
-	if (cvar("g_use_ammunition"))
+	if not(self.items & IT_UNLIMITED_AMMO)
 		self.ammo_cells = self.ammo_cells - cvar("g_balance_crylink_secondary_ammo");
 
 	W_SetupShot (self, '25 8 -8', FALSE, 2, "weapons/crylink_fire.wav");
@@ -180,7 +180,7 @@
 // experimental lightning gun
 void W_Crylink_Attack3 (void)
 {
-	if (cvar("g_use_ammunition"))
+	if not(self.items & IT_UNLIMITED_AMMO)
 		self.ammo_cells = self.ammo_cells - cvar("g_balance_crylink_primary_ammo");
 	W_SetupShot (self, '25 8 -8', TRUE, 0, "weapons/crylink_fire.wav");
 
@@ -191,10 +191,15 @@
 	trailparticles(world, particleeffectnum("lightning_beam", w_shotorg, trace_endpos);
 
 	if (trace_fraction < 1)
-		Damage(trace_ent, self, self, cvar("g_balance_crylink_primary_damage"), IT_CRYLINK, trace_endpos, '0 0 0');
+		Damage(trace_ent, self, self, cvar("g_balance_crylink_primary_damage"), WEP_CRYLINK, trace_endpos, '0 0 0');
 }
 */
 
+void spawnfunc_weapon_crylink (void)
+{
+	weapon_defaultspawnfunc(WEP_CRYLINK);
+}
+
 float w_crylink(float req)
 {
 	if (req == WR_AIM)
@@ -229,12 +234,12 @@
 		precache_sound ("weapons/crylink_fire.wav");
 	}
 	else if (req == WR_SETUP)
-		weapon_setup(WEP_CRYLINK, "crylink", IT_CELLS);
+		weapon_setup(WEP_CRYLINK);
 	else if (req == WR_CHECKAMMO1)
 		return self.ammo_cells >= cvar("g_balance_crylink_primary_ammo");
 	else if (req == WR_CHECKAMMO2)
 		return self.ammo_cells >= cvar("g_balance_crylink_secondary_ammo");
-	else if (req == WR_REGISTER)
-		weapon_register(WEP_CRYLINK, min(cvar("g_balance_crylink_primary_ammo"), cvar("g_balance_crylink_secondary_ammo")));
+	// else if (req == WR_SUICIDEMESSAGE) // TODO
+	// else if (req == WR_KILLMESSAGE) // TODO
 	return TRUE;
 };

Modified: branches/nexuiz-2.0/data/qcsrc/server/w_electro.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/w_electro.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/w_electro.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -8,12 +8,12 @@
 	if (self.movetype == MOVETYPE_BOUNCE)
 	{
 		pointparticles(particleeffectnum("electro_ballexplode"), org2, '0 0 0', 1);
-		RadiusDamage (self, self.owner, cvar("g_balance_electro_secondary_damage"), cvar("g_balance_electro_secondary_edgedamage"), cvar("g_balance_electro_secondary_radius"), world, cvar("g_balance_electro_secondary_force"), IT_ELECTRO);
+		RadiusDamage (self, self.owner, cvar("g_balance_electro_secondary_damage"), cvar("g_balance_electro_secondary_edgedamage"), cvar("g_balance_electro_secondary_radius"), world, cvar("g_balance_electro_secondary_force"), WEP_ELECTRO);
 	}
 	else
 	{
 		pointparticles(particleeffectnum("electro_impact"), org2, '0 0 0', 1);
-		RadiusDamage (self, self.owner, cvar("g_balance_electro_primary_damage"), cvar("g_balance_electro_primary_edgedamage"), cvar("g_balance_electro_primary_radius"), world, cvar("g_balance_electro_primary_force"), IT_ELECTRO);
+		RadiusDamage (self, self.owner, cvar("g_balance_electro_primary_damage"), cvar("g_balance_electro_primary_edgedamage"), cvar("g_balance_electro_primary_radius"), world, cvar("g_balance_electro_primary_force"), WEP_ELECTRO);
 	}
 	sound (self, CHAN_PROJECTILE, "weapons/electro_impact.wav", VOL_BASE, ATTN_NORM);
 
@@ -29,7 +29,7 @@
 	sound (self, CHAN_PROJECTILE, "weapons/electro_impact_combo.wav", VOL_BASE, ATTN_NORM);
 
 	self.event_damage = SUB_Null;
-	RadiusDamage (self, self.owner, cvar("g_balance_electro_combo_damage"), cvar("g_balance_electro_combo_edgedamage"), cvar("g_balance_electro_combo_radius"), world, cvar("g_balance_electro_combo_force"), IT_ELECTRO);
+	RadiusDamage (self, self.owner, cvar("g_balance_electro_combo_damage"), cvar("g_balance_electro_combo_edgedamage"), cvar("g_balance_electro_combo_radius"), world, cvar("g_balance_electro_combo_force"), WEP_ELECTRO);
 	remove (self);
 }
 
@@ -109,7 +109,7 @@
 	proj.solid = SOLID_BBOX;
 	setorigin(proj, w_shotorg);
 
-	if (cvar("g_use_ammunition"))
+	if not(self.items & IT_UNLIMITED_AMMO)
 		self.ammo_cells = self.ammo_cells - cvar("g_balance_electro_primary_ammo");
 	proj.effects = EF_BRIGHTFIELD | EF_LOWPRECISION;
 	proj.movetype = MOVETYPE_FLY;
@@ -143,7 +143,7 @@
 	proj.solid = SOLID_BBOX;
 	setorigin(proj, w_shotorg);
 
-	if (cvar("g_use_ammunition"))
+	if not(self.items & IT_UNLIMITED_AMMO)
 		self.ammo_cells = self.ammo_cells - cvar("g_balance_electro_secondary_ammo");
 	proj.effects = EF_LOWPRECISION;
 	//proj.glow_size = 50;
@@ -164,6 +164,11 @@
 	//sound (proj, CHAN_PROJECTILE, "weapons/electro_fly.wav", VOL_BASE, ATTN_NORM);
 }
 
+void spawnfunc_weapon_electro (void)
+{
+	weapon_defaultspawnfunc(WEP_ELECTRO);
+}
+
 .float bot_secondary_electromooth;
 float w_electro(float req)
 {
@@ -220,12 +225,13 @@
 		precache_sound ("weapons/electro_impact_combo.wav");
 	}
 	else if (req == WR_SETUP)
-		weapon_setup(WEP_ELECTRO, "electro", IT_CELLS);
+		weapon_setup(WEP_ELECTRO);
 	else if (req == WR_CHECKAMMO1)
 		return self.ammo_cells >= cvar("g_balance_electro_primary_ammo");
 	else if (req == WR_CHECKAMMO2)
 		return self.ammo_cells >= cvar("g_balance_electro_secondary_ammo");
-	else if (req == WR_REGISTER)
-		weapon_register(WEP_ELECTRO, min(cvar("g_balance_electro_primary_ammo"), cvar("g_balance_electro_secondary_ammo")));
+	else if (req == WR_SUICIDEMESSAGE)
+		w_deathtypestring = "played with plasma";
+	// else if (req == WR_KILLMESSAGE) // TODO
 	return TRUE;
 };

Modified: branches/nexuiz-2.0/data/qcsrc/server/w_grenadelauncher.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/w_grenadelauncher.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/w_grenadelauncher.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -1,4 +1,3 @@
-
 void W_Grenade_Explode (void)
 {
 	vector	org2;
@@ -7,7 +6,7 @@
 	sound (self, CHAN_PROJECTILE, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM);
 
 	self.event_damage = SUB_Null;
-	RadiusDamage (self, self.owner, cvar("g_balance_grenadelauncher_primary_damage"), cvar("g_balance_grenadelauncher_primary_edgedamage"), cvar("g_balance_grenadelauncher_primary_radius"), world, cvar("g_balance_grenadelauncher_primary_force"), IT_GRENADE_LAUNCHER);
+	RadiusDamage (self, self.owner, cvar("g_balance_grenadelauncher_primary_damage"), cvar("g_balance_grenadelauncher_primary_edgedamage"), cvar("g_balance_grenadelauncher_primary_radius"), world, cvar("g_balance_grenadelauncher_primary_force"), WEP_GRENADE_LAUNCHER);
 
 	remove (self);
 }
@@ -20,7 +19,7 @@
 	sound (self, CHAN_PROJECTILE, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM);
 
 	self.event_damage = SUB_Null;
-	RadiusDamage (self, self.owner, cvar("g_balance_grenadelauncher_secondary_damage"), cvar("g_balance_grenadelauncher_secondary_edgedamage"), cvar("g_balance_grenadelauncher_secondary_radius"), world, cvar("g_balance_grenadelauncher_secondary_force"), IT_GRENADE_LAUNCHER);
+	RadiusDamage (self, self.owner, cvar("g_balance_grenadelauncher_secondary_damage"), cvar("g_balance_grenadelauncher_secondary_edgedamage"), cvar("g_balance_grenadelauncher_secondary_radius"), world, cvar("g_balance_grenadelauncher_secondary_force"), WEP_GRENADE_LAUNCHER);
 
 	remove (self);
 }
@@ -77,7 +76,7 @@
 {
 	local entity gren;
 
-	if (cvar("g_use_ammunition"))
+	if not(self.items & IT_UNLIMITED_AMMO)
 		self.ammo_rockets = self.ammo_rockets - cvar("g_balance_grenadelauncher_primary_ammo");
 	W_SetupShot (self, '25 6 -8', FALSE, 4, "weapons/grenade_fire.wav");
 	//W_SetupShot (self, '25 8 -8', FALSE, 4, "weapons/grenade_fire.wav"); // TODO: move model to the right a little
@@ -112,7 +111,7 @@
 {
 	local entity gren;
 
-	if (cvar("g_use_ammunition"))
+	if not(self.items & IT_UNLIMITED_AMMO)
 		self.ammo_rockets = self.ammo_rockets - cvar("g_balance_grenadelauncher_secondary_ammo");
 	W_SetupShot (self, '25 6 -8', FALSE, 4, "weapons/grenade_fire.wav");
 	//W_SetupShot (self, '25 8 -8', FALSE, 4, "weapons/grenade_fire.wav"); // TODO: move model to the right a little
@@ -147,6 +146,11 @@
 	gren.flags = FL_PROJECTILE;
 }
 
+void spawnfunc_weapon_grenadelauncher (void)
+{
+	weapon_defaultspawnfunc(WEP_GRENADE_LAUNCHER);
+}
+
 .float bot_secondary_grenademooth;
 float w_glauncher(float req)
 {
@@ -202,12 +206,14 @@
 		precache_sound ("weapons/grenade_impact.wav");
 	}
 	else if (req == WR_SETUP)
-		weapon_setup(WEP_GRENADE_LAUNCHER, "gl", IT_ROCKETS);
+		weapon_setup(WEP_GRENADE_LAUNCHER);
 	else if (req == WR_CHECKAMMO1)
 		return self.ammo_rockets >= cvar("g_balance_grenadelauncher_primary_ammo");
 	else if (req == WR_CHECKAMMO2)
 		return self.ammo_rockets >= cvar("g_balance_grenadelauncher_secondary_ammo");
-	else if (req == WR_REGISTER)
-		weapon_register(WEP_GRENADE_LAUNCHER, min(cvar("g_balance_grenadelauncher_primary_ammo"), cvar("g_balance_grenadelauncher_secondary_ammo")));
+	else if (req == WR_SUICIDEMESSAGE)
+		w_deathtypestring = "detonated";
+	else if (req == WR_KILLMESSAGE)
+		w_deathtypestring = "was blasted by";
 	return TRUE;
 };

Modified: branches/nexuiz-2.0/data/qcsrc/server/w_hagar.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/w_hagar.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/w_hagar.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -13,7 +13,7 @@
 		sound (self, CHAN_PROJECTILE, "weapons/hagexp3.wav", VOL_BASE, ATTN_NORM);
 
 	self.event_damage = SUB_Null;
-	RadiusDamage (self, self.realowner, cvar("g_balance_hagar_primary_damage"), cvar("g_balance_hagar_primary_edgedamage"), cvar("g_balance_hagar_primary_radius"), world, cvar("g_balance_hagar_primary_force"), IT_HAGAR);
+	RadiusDamage (self, self.realowner, cvar("g_balance_hagar_primary_damage"), cvar("g_balance_hagar_primary_edgedamage"), cvar("g_balance_hagar_primary_radius"), world, cvar("g_balance_hagar_primary_force"), WEP_HAGAR);
 
 	remove (self);
 }
@@ -33,7 +33,7 @@
 		sound (self, CHAN_PROJECTILE, "weapons/hagexp3.wav", VOL_BASE, ATTN_NORM);
 
 	self.event_damage = SUB_Null;
-	RadiusDamage (self, self.realowner, cvar("g_balance_hagar_secondary_damage"), cvar("g_balance_hagar_secondary_edgedamage"), cvar("g_balance_hagar_secondary_radius"), world, cvar("g_balance_hagar_secondary_force"), IT_HAGAR);
+	RadiusDamage (self, self.realowner, cvar("g_balance_hagar_secondary_damage"), cvar("g_balance_hagar_secondary_edgedamage"), cvar("g_balance_hagar_secondary_radius"), world, cvar("g_balance_hagar_secondary_force"), WEP_HAGAR);
 
 	remove (self);
 }
@@ -80,7 +80,7 @@
 {
 	local entity missile;
 
-	if (cvar("g_use_ammunition"))
+	if not(self.items & IT_UNLIMITED_AMMO)
 		self.ammo_rockets = self.ammo_rockets - cvar("g_balance_hagar_primary_ammo");
 	W_SetupShot (self, '25 5 -8', FALSE, 2, "weapons/hagar_fire.wav");
 	//W_SetupShot (self, '25 8 -8', FALSE, 2, "weapons/hagar_fire.wav"); // TODO: move model a little to the right
@@ -119,7 +119,7 @@
 {
 	local entity missile;
 
-	if (cvar("g_use_ammunition"))
+	if not(self.items & IT_UNLIMITED_AMMO)
 		self.ammo_rockets = self.ammo_rockets - cvar("g_balance_hagar_secondary_ammo");
 	W_SetupShot (self, '25 5 -8', FALSE, 2, "weapons/hagar_fire.wav");
 	//W_SetupShot (self, '25 8 -8', FALSE, 2, "weapons/hagar_fire.wav"); // TODO: move model a little to the right
@@ -156,6 +156,11 @@
 	missile.flags = FL_PROJECTILE;
 }
 
+void spawnfunc_weapon_hagar (void)
+{
+	weapon_defaultspawnfunc(WEP_HAGAR);
+}
+
 float w_hagar(float req)
 {
 	if (req == WR_AIM)
@@ -193,12 +198,13 @@
 		precache_sound ("weapons/hagexp3.wav");
 	}
 	else if (req == WR_SETUP)
-		weapon_setup(WEP_HAGAR, "hagar", IT_ROCKETS);
+		weapon_setup(WEP_HAGAR);
 	else if (req == WR_CHECKAMMO1)
 		return self.ammo_rockets >= cvar("g_balance_hagar_primary_ammo");
 	else if (req == WR_CHECKAMMO2)
 		return self.ammo_rockets >= cvar("g_balance_hagar_secondary_ammo");
-	else if (req == WR_REGISTER)
-		weapon_register(WEP_HAGAR, min(cvar("g_balance_hagar_primary_ammo"), cvar("g_balance_hagar_secondary_ammo")));
+	// else if (req == WR_SUICIDEMESSAGE) // TODO
+	else if (req == WR_KILLMESSAGE)
+		w_deathtypestring = "was pummeled by";
 	return TRUE;
 };

Modified: branches/nexuiz-2.0/data/qcsrc/server/w_laser.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/w_laser.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/w_laser.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -24,9 +24,9 @@
 
 	self.event_damage = SUB_Null;
 	if (self.dmg)
-		RadiusDamage (self, self.owner, cvar("g_balance_laser_secondary_damage"), cvar("g_balance_laser_secondary_edgedamage"), cvar("g_balance_laser_secondary_radius"), world, cvar("g_balance_laser_secondary_force"), IT_LASER);
+		RadiusDamage (self, self.owner, cvar("g_balance_laser_secondary_damage"), cvar("g_balance_laser_secondary_edgedamage"), cvar("g_balance_laser_secondary_radius"), world, cvar("g_balance_laser_secondary_force"), WEP_LASER);
 	else
-		RadiusDamage (self, self.owner, cvar("g_balance_laser_primary_damage"), cvar("g_balance_laser_primary_edgedamage"), cvar("g_balance_laser_primary_radius"), world, cvar("g_balance_laser_primary_force"), IT_LASER);
+		RadiusDamage (self, self.owner, cvar("g_balance_laser_primary_damage"), cvar("g_balance_laser_primary_edgedamage"), cvar("g_balance_laser_primary_radius"), world, cvar("g_balance_laser_primary_force"), WEP_LASER);
 	sound (self, CHAN_PROJECTILE, "weapons/laserimpact.wav", VOL_BASE, ATTN_NORM);
 
 	remove (self);
@@ -128,12 +128,12 @@
 		precache_sound ("weapons/laserimpact.wav");
 	}
 	else if (req == WR_SETUP)
-		weapon_setup(WEP_LASER, "laser", 0);
+		weapon_setup(WEP_LASER);
 	else if (req == WR_CHECKAMMO1)
 		return TRUE;
 	else if (req == WR_CHECKAMMO2)
 		return TRUE;
-	else if (req == WR_REGISTER)
-		weapon_register(WEP_LASER, 0);
+	// else if (req == WR_SUICIDEMESSAGE) // TODO
+	// else if (req == WR_KILLMESSAGE) // TODO
 	return TRUE;
 };

Copied: branches/nexuiz-2.0/data/qcsrc/server/w_minstanex.qc (from rev 4363, trunk/data/qcsrc/server/w_minstanex.qc)
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/w_minstanex.qc	                        (rev 0)
+++ branches/nexuiz-2.0/data/qcsrc/server/w_minstanex.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -0,0 +1,165 @@
+void W_MinstaNex_Attack (void)
+{
+	float flying;
+	flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last
+
+	W_SetupShot (self, '25 8 -4', TRUE, 5, "weapons/nexfire.wav");
+
+	yoda = 0;
+	FireRailgunBullet (w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, 1000, 800, WEP_MINSTANEX);
+
+	if(!g_minstagib)
+	if(yoda)
+	if(flying)
+		announce(self, "announcer/male/yoda.ogg");
+
+	pointparticles(particleeffectnum("nex_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+
+	// beam effect
+	trailparticles(world, particleeffectnum("TE_TEI_G3"), w_shotorg, trace_endpos);
+	// flash and burn the wall
+	if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
+		pointparticles(particleeffectnum("nex_impact"), trace_endpos - w_shotdir * 6, '0 0 0', 1);
+	// play a sound
+	soundat (self, trace_endpos, CHAN_PROJECTILE, "weapons/neximpact.wav", VOL_BASE, ATTN_NORM);
+
+	if not(self.items & IT_UNLIMITED_AMMO)
+	{
+		if (g_minstagib)
+			self.ammo_cells = self.ammo_cells - 1;
+		else
+			self.ammo_cells = self.ammo_cells - cvar("g_balance_minstanex_ammo");
+	}
+}
+
+
+.float minstagib_nextthink;
+void minstagib_ammocheck (void)
+{
+	if (time < self.minstagib_nextthink || self.deadflag || gameover)
+		return;
+
+	if (self.ammo_cells <= 0)
+	{
+		if (self.health == 5)
+		{
+			centerprint(self, "you're dead now...\n");
+			Damage(self, self, self, 5, DEATH_NOAMMO, self.origin, '0 0 0');
+			announce(self, "announcer/robotic/terminated.ogg");
+		}
+		if (self.health == 10)
+		{
+			centerprint(self, "^11^7 second left to find some ammo\n");
+			Damage(self, self, self, 5, DEATH_NOAMMO, self.origin, '0 0 0');
+			announce(self, "announcer/robotic/1.ogg");
+		}
+		if (self.health == 20)
+		{
+			centerprint(self, "^12^7 seconds left to find some ammo\n");
+			Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
+			announce(self, "announcer/robotic/2.ogg");
+		}
+		if (self.health == 30)
+		{
+			centerprint(self, "^13^7 seconds left to find some ammo\n");
+			Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
+			announce(self, "announcer/robotic/3.ogg");
+		}
+		if (self.health == 40)
+		{
+			centerprint(self, "^14^7 seconds left to find some ammo\n");
+			Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
+			announce(self, "announcer/robotic/4.ogg");
+		}
+		if (self.health == 50)
+		{
+			centerprint(self, "^15^7 seconds left to find some ammo\n");
+			Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
+			announce(self, "announcer/robotic/5.ogg");
+		}
+		if (self.health == 60)
+		{
+			centerprint(self, "^36^7 seconds left to find some ammo\n");
+			Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
+			announce(self, "announcer/robotic/6.ogg");
+		}
+		if (self.health == 70)
+		{
+			centerprint(self, "^37^7 seconds left to find some ammo\n");
+			Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
+			announce(self, "announcer/robotic/7.ogg");
+		}
+		if (self.health == 80)
+		{
+			centerprint(self, "^38^7 seconds left to find some ammo\n");
+			Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
+			announce(self, "announcer/robotic/8.ogg");
+		}
+		if (self.health == 90)
+		{
+			centerprint(self, "^39^7 seconds left to find some ammo\n");
+			Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
+			announce(self, "announcer/robotic/9.ogg");
+		}
+		if (self.health == 100)
+		{
+			centerprint(self, "get some ammo or\nyou'll be dead in ^310^7 seconds...");
+			Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
+			announce(self, "announcer/robotic/10.ogg");
+		}
+	}
+	self.minstagib_nextthink = time + 1;
+}
+
+void spawnfunc_weapon_minstanex (void); // defined in t_items.qc
+
+float w_minstanex(float req)
+{
+	if (req == WR_AIM)
+		self.BUTTON_ATCK = bot_aim(1000000, 0, 1, FALSE);
+	else if (req == WR_THINK)
+	{
+		if (self.BUTTON_ATCK)
+		{
+			if (weapon_prepareattack(0, cvar("g_balance_minstanex_refire")))
+			{
+				W_MinstaNex_Attack();
+				weapon_thinkf(WFRAME_FIRE1, cvar("g_balance_minstanex_animtime"), w_ready);
+			}
+		}
+		else if (self.BUTTON_ATCK2)
+		{
+			if (self.jump_interval <= time)
+			{
+				self.jump_interval = time + 0.9;
+				W_Laser_Attack(FALSE);
+			}
+		}
+	}
+	else if (req == WR_PRECACHE)
+	{
+		precache_model ("models/nexflash.md3");
+		precache_model ("models/weapons/g_minstanex.md3");
+		precache_model ("models/weapons/v_minstanex.md3");
+		precache_model ("models/weapons/w_minstanex.zym");
+		precache_sound ("weapons/nexfire.wav");
+		precache_sound ("weapons/neximpact.wav");
+		w_laser(WR_PRECACHE);
+	}
+	else if (req == WR_SETUP)
+		weapon_setup(WEP_MINSTANEX);
+	else if (req == WR_CHECKAMMO1)
+	{
+		if (g_minstagib)
+			return self.ammo_cells >= 1;
+		else
+			return self.ammo_cells >= cvar("g_balance_minstanex_ammo");
+	}
+	else if (req == WR_CHECKAMMO2)
+		return FALSE;
+	else if (req == WR_SUICIDEMESSAGE)
+		w_deathtypestring = "did the impossible";
+	else if (req == WR_KILLMESSAGE)
+		w_deathtypestring = "has been vaporized by";
+	return TRUE;
+};

Modified: branches/nexuiz-2.0/data/qcsrc/server/w_nex.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/w_nex.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/w_nex.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -1,25 +1,16 @@
-
 void W_Nex_Attack (void)
 {
 	float flying;
-	if not(g_minstagib || g_instagib)
-		flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last
+	flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last
 
 	W_SetupShot (self, '25 4 -4', TRUE, 5, "weapons/nexfire.wav");
-	//W_SetupShot (self, '25 8 -8', TRUE, 2, "weapons/nexfire.wav"); // TODO: move model down a little
 
-	// assure that nexdamage is high enough in minstagib
-	if (g_minstagib || g_instagib)
-		FireRailgunBullet (w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, 1000, 800, IT_NEX);
-	else
-	{
-		yoda = 0;
-		FireRailgunBullet (w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, cvar("g_balance_nex_damage"), cvar("g_balance_nex_force"), IT_NEX);
+	yoda = 0;
+	FireRailgunBullet (w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, cvar("g_balance_nex_damage"), cvar("g_balance_nex_force"), WEP_NEX);
 
-		if(yoda)
-			if(flying)
-				announce(self, "announcer/male/yoda.ogg");
-	}
+	if(yoda)
+	if(flying)
+		announce(self, "announcer/male/yoda.ogg");
 
 	pointparticles(particleeffectnum("nex_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
 
@@ -31,94 +22,14 @@
 	// play a sound
 	soundat (self, trace_endpos, CHAN_PROJECTILE, "weapons/neximpact.wav", VOL_BASE, ATTN_NORM);
 
-	if (cvar("g_use_ammunition") && !g_instagib)
+	if not(self.items & IT_UNLIMITED_AMMO)
 	{
-		if (g_minstagib)
-			self.ammo_cells = self.ammo_cells - 1;
-		else
-			self.ammo_cells = self.ammo_cells - cvar("g_balance_nex_ammo");
+		self.ammo_cells = self.ammo_cells - cvar("g_balance_nex_ammo");
 	}
 }
 
+void spawnfunc_weapon_nex (void); // defined in t_items.qc
 
-.float minstagib_nextthink;
-void minstagib_ammocheck (void)
-{
-	if (time < self.minstagib_nextthink || self.deadflag || gameover)
-		return;
-
-	if (self.ammo_cells <= 0)
-	{
-		if (self.health == 5)
-		{
-			centerprint(self, "you're dead now...\n");
-			Damage(self, self, self, 5, DEATH_NOAMMO, self.origin, '0 0 0');
-			announce(self, "announcer/robotic/terminated.ogg");
-		}
-		if (self.health == 10)
-		{
-			centerprint(self, "^11^7 second left to find some ammo\n");
-			Damage(self, self, self, 5, DEATH_NOAMMO, self.origin, '0 0 0');
-			announce(self, "announcer/robotic/1.ogg");
-		}
-		if (self.health == 20)
-		{
-			centerprint(self, "^12^7 seconds left to find some ammo\n");
-			Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
-			announce(self, "announcer/robotic/2.ogg");
-		}
-		if (self.health == 30)
-		{
-			centerprint(self, "^13^7 seconds left to find some ammo\n");
-			Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
-			announce(self, "announcer/robotic/3.ogg");
-		}
-		if (self.health == 40)
-		{
-			centerprint(self, "^14^7 seconds left to find some ammo\n");
-			Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
-			announce(self, "announcer/robotic/4.ogg");
-		}
-		if (self.health == 50)
-		{
-			centerprint(self, "^15^7 seconds left to find some ammo\n");
-			Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
-			announce(self, "announcer/robotic/5.ogg");
-		}
-		if (self.health == 60)
-		{
-			centerprint(self, "^36^7 seconds left to find some ammo\n");
-			Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
-			announce(self, "announcer/robotic/6.ogg");
-		}
-		if (self.health == 70)
-		{
-			centerprint(self, "^37^7 seconds left to find some ammo\n");
-			Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
-			announce(self, "announcer/robotic/7.ogg");
-		}
-		if (self.health == 80)
-		{
-			centerprint(self, "^38^7 seconds left to find some ammo\n");
-			Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
-			announce(self, "announcer/robotic/8.ogg");
-		}
-		if (self.health == 90)
-		{
-			centerprint(self, "^39^7 seconds left to find some ammo\n");
-			Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
-			announce(self, "announcer/robotic/9.ogg");
-		}
-		if (self.health == 100)
-		{
-			centerprint(self, "get some ammo or\nyou'll be dead in ^310^7 seconds...");
-			Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
-			announce(self, "announcer/robotic/10.ogg");
-		}
-	}
-	self.minstagib_nextthink = time + 1;
-}
-
 float w_nex(float req)
 {
 	if (req == WR_AIM)
@@ -127,34 +38,12 @@
 	{
 		if (self.BUTTON_ATCK)
 		{
-			if(g_minstagib)
+			if (weapon_prepareattack(0, cvar("g_balance_nex_refire")))
 			{
-				if (weapon_prepareattack(0, cvar("g_balance_minstagib_nex_refire")))
-				{
-					W_Nex_Attack();
-					weapon_thinkf(WFRAME_FIRE1, cvar("g_balance_minstagib_nex_animtime"), w_ready);
-				}
+				W_Nex_Attack();
+				weapon_thinkf(WFRAME_FIRE1, cvar("g_balance_nex_animtime"), w_ready);
 			}
-			else
-			{
-				if (weapon_prepareattack(0, cvar("g_balance_nex_refire")))
-				{
-					W_Nex_Attack();
-					weapon_thinkf(WFRAME_FIRE1, cvar("g_balance_nex_animtime"), w_ready);
-				}
-			}
 		}
-		else if (self.BUTTON_ATCK2)
-		{
-			if (g_minstagib)
-			{
-				if (self.jump_interval <= time)
-				{
-					self.jump_interval = time + 0.9;
-					W_Laser_Attack(FALSE);
-				}
-			}
-		}
 	}
 	else if (req == WR_PRECACHE)
 	{
@@ -164,26 +53,16 @@
 		precache_model ("models/weapons/w_nex.zym");
 		precache_sound ("weapons/nexfire.wav");
 		precache_sound ("weapons/neximpact.wav");
-		if(g_minstagib)
-			w_laser(WR_PRECACHE);
 	}
 	else if (req == WR_SETUP)
-		weapon_setup(WEP_NEX, "nex", IT_CELLS);
+		weapon_setup(WEP_NEX);
 	else if (req == WR_CHECKAMMO1)
-	{
-		if (g_minstagib)
-			return self.ammo_cells >= 1;
-		else
-			return self.ammo_cells >= cvar("g_balance_nex_ammo");
-	}
+		return self.ammo_cells >= cvar("g_balance_nex_ammo");
 	else if (req == WR_CHECKAMMO2)
 		return FALSE;
-	else if (req == WR_REGISTER)
-	{
-		if(g_minstagib)
-			weapon_register(WEP_NEX, 1);
-		else
-			weapon_register(WEP_NEX, cvar("g_balance_nex_ammo"));
-	}
+	else if (req == WR_SUICIDEMESSAGE)
+		w_deathtypestring = "did the impossible";
+	else if (req == WR_KILLMESSAGE)
+		w_deathtypestring = "has been vaporized by";
 	return TRUE;
 };

Copied: branches/nexuiz-2.0/data/qcsrc/server/w_porto.qc (from rev 4363, trunk/data/qcsrc/server/w_porto.qc)
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/w_porto.qc	                        (rev 0)
+++ branches/nexuiz-2.0/data/qcsrc/server/w_porto.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -0,0 +1,229 @@
+.float portal_id;
+.entity porto_current;
+.vector porto_v_angle; // holds "held" view angles
+.float porto_v_angle_held;
+.vector right_vector;
+
+void W_Porto_Success (void)
+{
+	self.owner.porto_current = world;
+	remove(self);
+}
+
+float W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo);
+void W_Porto_Fail (float failhard)
+{
+	// no portals here!
+	Portal_ClearWithID(self.owner, self.portal_id);
+	self.owner.porto_current = world;
+
+	if(!failhard && !(self.owner.weapons & WEPBIT_PORTO))
+	{
+		centerprint(self.owner, "^1Portal deployment failed.\n\n^2Catch it to try again!");
+		setsize (self, '-16 -16 0', '16 16 32');
+		setorigin(self, self.origin + trace_plane_normal);
+		if(move_out_of_solid(self))
+		{
+			self.flags = FL_ITEM;
+			self.velocity = trigger_push_calculatevelocity(self.origin, self.owner, 128);
+			tracetoss(self, self);
+			if(vlen(trace_endpos - self.owner.origin) > 128)
+				self.velocity = '0 0 0';
+			W_ThrowNewWeapon(self.owner, WEP_PORTO, 0, self.origin, self.velocity);
+		}
+		else
+		{
+			W_ThrowNewWeapon(self.owner, WEP_PORTO, 0, self.origin, '0 0 0');
+		}
+	}
+	remove(self);
+}
+
+void W_Porto_Think (void)
+{
+	trace_plane_normal = '0 0 0';
+	if(self.owner.playerid != self.playerid)
+		remove(self);
+	else
+		W_Porto_Fail(0);
+}
+
+void W_Porto_Touch (void)
+{
+	vector norm;
+
+	if(other.classname == "portal")
+		return; // handled by the portal
+
+	norm = trace_plane_normal;
+	if(self.owner.playerid != self.playerid)
+	{
+		remove(self);
+	}
+	else if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK)
+	{
+		// just reflect
+		self.right_vector = self.right_vector - 2 * trace_plane_normal * (self.right_vector * trace_plane_normal);
+		self.angles = vectoangles(self.velocity - 2 * trace_plane_normal * (self.velocity * trace_plane_normal));
+	}
+	else if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
+	{
+		W_Porto_Fail(0);
+	}
+	else if(self.effects & EF_RED)
+	{
+		self.effects += EF_BLUE - EF_RED;
+		if(Portal_SpawnInPortalAtTrace(self.owner, self.right_vector, self.portal_id))
+		{
+			trace_plane_normal = norm;
+			self.right_vector = self.right_vector - 2 * trace_plane_normal * (self.right_vector * trace_plane_normal);
+			self.angles = vectoangles(self.velocity - 2 * trace_plane_normal * (self.velocity * trace_plane_normal));
+		}
+		else
+		{
+			trace_plane_normal = norm;
+			W_Porto_Fail(0);
+		}
+	}
+	else
+	{
+		if(Portal_SpawnOutPortalAtTrace(self.owner, self.right_vector, self.portal_id))
+		{
+			W_Porto_Success();
+		}
+		else
+		{
+			trace_plane_normal = norm;
+			W_Porto_Fail(0);
+		}
+	}
+
+}
+
+void W_Porto_Attack (void)
+{
+	local entity gren;
+
+	if not(self.items & IT_UNLIMITED_AMMO)
+		self.weapons = self.weapons - (self.weapons & WEPBIT_PORTO);
+	W_SetupShot (self, '0 0 0', FALSE, 4, "weapons/grenade_fire.wav");
+
+	//pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+
+	gren = spawn ();
+	gren.owner = self;
+	gren.classname = "porto";
+	gren.bot_dodge = TRUE;
+	gren.bot_dodgerating = 200;
+	gren.movetype = MOVETYPE_BOUNCEMISSILE;
+	gren.solid = SOLID_BBOX;
+	gren.effects = EF_LOWPRECISION | EF_RED;
+	gren.scale = 4;
+	gren.modelflags = MF_GRENADE;
+	setmodel(gren, "models/grenademodel.md3"); // precision set above
+	setsize(gren, '0 0 0', '0 0 0');
+	setorigin(gren, w_shotorg);
+
+	gren.nextthink = time + cvar("g_balance_porto_primary_lifetime");
+	gren.think = W_Porto_Think;
+	gren.touch = W_Porto_Touch;
+	gren.velocity = w_shotdir * cvar("g_balance_porto_primary_speed");
+	W_SetupProjectileVelocity(gren);
+
+	gren.angles = vectoangles (gren.velocity);
+	gren.flags = FL_PROJECTILE;
+
+	gren.portal_id = time;
+	self.porto_current = gren;
+	gren.playerid = self.playerid;
+	fixedmakevectors(vectoangles(gren.velocity));
+	gren.right_vector = v_right;
+
+	gren.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP;
+}
+
+void spawnfunc_weapon_porto (void)
+{
+	if(g_race)
+	{
+		startitem_failed = TRUE;
+		remove(self);
+		return;
+	}
+	else
+		weapon_defaultspawnfunc(WEP_PORTO);
+}
+
+float w_porto(float req)
+{
+	vector v_angle_save;
+
+	if (req == WR_AIM)
+	{
+		self.BUTTON_ATCK = FALSE;
+		self.BUTTON_ATCK2 = FALSE;
+		if(bot_aim(cvar("g_balance_porto_primary_speed"), 0, cvar("g_balance_grenadelauncher_primary_lifetime"), FALSE))
+			self.BUTTON_ATCK = TRUE;
+	}
+	else if (req == WR_THINK)
+	{
+		if(self.porto_v_angle_held)
+		{
+			if(!self.BUTTON_ATCK2)
+			{
+				msg_entity = self;
+				WRITESPECTATABLE_MSG_ONE({
+					WriteByte(MSG_ONE, SVC_TEMPENTITY);
+					WriteByte(MSG_ONE, TE_CSQC_HOLDANGLES);
+					WriteByte(MSG_ONE, WEP_PORTO);
+					WriteByte(MSG_ONE, 0);
+				});
+				self.porto_v_angle_held = 0;
+			}
+		}
+		else
+		{
+			if(self.BUTTON_ATCK2)
+			{
+				self.porto_v_angle = self.v_angle;
+				msg_entity = self;
+				WRITESPECTATABLE_MSG_ONE({
+					WriteByte(MSG_ONE, SVC_TEMPENTITY);
+					WriteByte(MSG_ONE, TE_CSQC_HOLDANGLES);
+					WriteByte(MSG_ONE, WEP_PORTO);
+					WriteByte(MSG_ONE, 1);
+					WriteCoord(MSG_ONE, self.v_angle_x);
+					WriteCoord(MSG_ONE, self.v_angle_y);
+				});
+				self.porto_v_angle_held = 1;
+			}
+		}
+		v_angle_save = self.v_angle;
+		if(self.porto_v_angle_held)
+			makevectors(self.porto_v_angle); // override the previously set angles
+
+		if (self.BUTTON_ATCK)
+		if (!self.porto_current)
+		if (weapon_prepareattack(0, cvar("g_balance_porto_primary_refire")))
+		{
+			W_Porto_Attack();
+			weapon_thinkf(WFRAME_FIRE1, cvar("g_balance_porto_primary_animtime"), w_ready);
+		}
+	}
+	else if (req == WR_PRECACHE)
+	{
+		precache_model ("models/grenademodel.md3");
+		precache_model ("models/weapons/g_porto.md3");
+		precache_model ("models/weapons/v_porto.md3");
+		precache_model ("models/weapons/w_porto.zym");
+		precache_sound ("weapons/grenade_fire.wav");
+		precache_model ("models/portal.md3");
+	}
+	else if (req == WR_SETUP)
+		weapon_setup(WEP_PORTO);
+	else if (req == WR_SUICIDEMESSAGE)
+		w_deathtypestring = "did the impossible";
+	else if (req == WR_KILLMESSAGE)
+		w_deathtypestring = "felt # doing the impossible";
+	return TRUE;
+};

Modified: branches/nexuiz-2.0/data/qcsrc/server/w_rocketlauncher.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/w_rocketlauncher.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/w_rocketlauncher.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -12,7 +12,7 @@
 
 	//effect (org2, "models/sprites/rockexpl.spr", 0, 12, 35);
 	self.event_damage = SUB_Null;
-	RadiusDamage (self, self.owner, cvar("g_balance_rocketlauncher_damage"), cvar("g_balance_rocketlauncher_edgedamage"), cvar("g_balance_rocketlauncher_radius"), world, cvar("g_balance_rocketlauncher_force"), IT_ROCKET_LAUNCHER);
+	RadiusDamage (self, self.owner, cvar("g_balance_rocketlauncher_damage"), cvar("g_balance_rocketlauncher_edgedamage"), cvar("g_balance_rocketlauncher_radius"), world, cvar("g_balance_rocketlauncher_force"), WEP_ROCKET_LAUNCHER);
 
 	if (self.owner.weapon == WEP_ROCKET_LAUNCHER)
 	{
@@ -213,7 +213,7 @@
 	local entity missile;
 	local entity flash, flash2;
 
-	if (cvar("g_use_ammunition") && !g_rocketarena)
+	if not(self.items & IT_UNLIMITED_AMMO)
 		self.ammo_rockets = self.ammo_rockets - cvar("g_balance_rocketlauncher_ammo");
 
 	W_SetupShot (self, '25 3 -4', FALSE, 5, "weapons/rocket_fire.wav");
@@ -283,6 +283,8 @@
 	flash.effects = flash2.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
 }
 
+void spawnfunc_weapon_rocketlauncher (void); // defined in t_items.qc
+
 float w_rlauncher(float req)
 {
 	if (req == WR_AIM)
@@ -418,7 +420,7 @@
 			precache_model ("models/laser_dot.mdl"); // rocket launcher
 	}
 	else if (req == WR_SETUP)
-		weapon_setup(WEP_ROCKET_LAUNCHER, "rl", IT_ROCKETS);
+		weapon_setup(WEP_ROCKET_LAUNCHER);
 	else if (req == WR_CHECKAMMO1)
 	{
 		// don't switch while guiding a missile
@@ -428,7 +430,9 @@
 	}
 	else if (req == WR_CHECKAMMO2)
 		return FALSE;
-	else if (req == WR_REGISTER)
-		weapon_register(WEP_ROCKET_LAUNCHER, cvar("g_balance_rocketlauncher_ammo"));
+	else if (req == WR_SUICIDEMESSAGE)
+		w_deathtypestring = "exploded";
+	else if (req == WR_KILLMESSAGE)
+		w_deathtypestring = "almost dodged #'s rocket";
 	return TRUE;
 };

Modified: branches/nexuiz-2.0/data/qcsrc/server/w_shotgun.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/w_shotgun.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/w_shotgun.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -15,8 +15,8 @@
 
 	W_SetupShot (self, '25 8 -8', TRUE, 5, "weapons/shotgun_fire.wav");
 	for (sc = 0;sc < bullets;sc = sc + 1)
-		fireBullet (w_shotorg, w_shotdir, spread, d, f, IT_SHOTGUN, sc < 3);
-	if (cvar("g_use_ammunition"))
+		fireBullet (w_shotorg, w_shotdir, spread, d, f, WEP_SHOTGUN, sc < 3);
+	if not(self.items & IT_UNLIMITED_AMMO)
 		self.ammo_shells = self.ammo_shells - cvar("g_balance_shotgun_primary_ammo");
 
 	pointparticles(particleeffectnum("shotgun_muzzleflash"), w_shotorg, w_shotdir * 1000, cvar("g_balance_shotgun_primary_ammo"));
@@ -58,8 +58,8 @@
 
 	W_SetupShot (self, '25 8 -8', TRUE, 5, "weapons/shotgun_fire.wav");
 	for (sc = 0;sc < bullets;sc = sc + 1)
-		fireBullet (w_shotorg, w_shotdir, spread, d, f, IT_SHOTGUN, sc < 3);
-	if (cvar("g_use_ammunition"))
+		fireBullet (w_shotorg, w_shotdir, spread, d, f, WEP_SHOTGUN, sc < 3);
+	if not(self.items & IT_UNLIMITED_AMMO)
 		self.ammo_shells = self.ammo_shells - cvar("g_balance_shotgun_secondary_ammo");
 
 	pointparticles(particleeffectnum("shotgun_muzzleflash"), w_shotorg, w_shotdir * 1000, cvar("g_balance_shotgun_secondary_ammo"));
@@ -95,6 +95,8 @@
 	weapon_thinkf(WFRAME_FIRE1, cvar("g_balance_shotgun_secondary_animtime"), shotgun_fire2_03);
 }
 
+void spawnfunc_weapon_shotgun(); // defined in t_items.qc
+
 float w_shotgun(float req)
 {
 	if (req == WR_AIM)
@@ -134,12 +136,14 @@
 			precache_model ("models/casing_shell.mdl");
 	}
 	else if (req == WR_SETUP)
-		weapon_setup(WEP_SHOTGUN, "shotgun", IT_SHELLS);
+		weapon_setup(WEP_SHOTGUN);
 	else if (req == WR_CHECKAMMO1)
 		return self.ammo_shells >= cvar("g_balance_shotgun_primary_ammo");
 	else if (req == WR_CHECKAMMO2)
 		return self.ammo_shells >= cvar("g_balance_shotgun_secondary_ammo") * 3;
-	else if (req == WR_REGISTER)
-		weapon_register(WEP_SHOTGUN, min(cvar("g_balance_shotgun_primary_ammo"), cvar("g_balance_shotgun_secondary_ammo")));
+	else if (req == WR_SUICIDEMESSAGE)
+		w_deathtypestring = "did the impossible";
+	else if (req == WR_KILLMESSAGE)
+		w_deathtypestring = "was gunned by";
 	return TRUE;
 };

Modified: branches/nexuiz-2.0/data/qcsrc/server/w_uzi.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/w_uzi.qc	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/qcsrc/server/w_uzi.qc	2008-09-05 19:40:34 UTC (rev 4365)
@@ -15,7 +15,7 @@
 {
 	local entity flash, flash2;
 
-	if (cvar("g_use_ammunition"))
+	if not(self.items & IT_UNLIMITED_AMMO)
 	{
 		if (self.uzi_bulletcounter == 1)
 			self.ammo_nails = self.ammo_nails - cvar("g_balance_uzi_first_ammo");
@@ -34,9 +34,9 @@
 	ATTACK_FINISHED(self) = time + cvar("g_balance_uzi_first_refire");
 
 	if (self.uzi_bulletcounter == 1)
-		fireBullet (w_shotorg, w_shotdir, cvar("g_balance_uzi_first_spread"), cvar("g_balance_uzi_first_damage"), cvar("g_balance_uzi_first_force"), IT_UZI, TRUE);
+		fireBullet (w_shotorg, w_shotdir, cvar("g_balance_uzi_first_spread"), cvar("g_balance_uzi_first_damage"), cvar("g_balance_uzi_first_force"), WEP_UZI, TRUE);
 	else
-		fireBullet (w_shotorg, w_shotdir, cvar("g_balance_uzi_sustained_spread"), cvar("g_balance_uzi_sustained_damage"), cvar("g_balance_uzi_sustained_force"), IT_UZI, (self.uzi_bulletcounter & 3) == 0);
+		fireBullet (w_shotorg, w_shotdir, cvar("g_balance_uzi_sustained_spread"), cvar("g_balance_uzi_sustained_damage"), cvar("g_balance_uzi_sustained_force"), WEP_UZI, (self.uzi_bulletcounter & 3) == 0);
 
 	pointparticles(particleeffectnum("uzi_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
 
@@ -85,9 +85,7 @@
 	{
 		if (!weapon_action(self.weapon, WR_CHECKAMMO2))
 		{
-			self.switchweapon = w_getbestweapon(self);
-			if (self.switchweapon != self.weapon)
-				self.cnt = self.weapon;
+			W_SwitchWeapon_Force(self, w_getbestweapon(self));
 			w_ready();
 			return;
 		}
@@ -100,6 +98,8 @@
 		weapon_thinkf(WFRAME_FIRE1, cvar("g_balance_uzi_sustained_refire"), w_ready);
 };
 
+void spawnfunc_weapon_machinegun(); // defined in t_items.qc
+
 float w_uzi(float req)
 {
 	if (req == WR_AIM)
@@ -142,12 +142,14 @@
 			precache_model ("models/casing_bronze.mdl");
 	}
 	else if (req == WR_SETUP)
-		weapon_setup(WEP_UZI, "uzi", IT_NAILS);
+		weapon_setup(WEP_UZI);
 	else if (req == WR_CHECKAMMO1)
 		return self.ammo_nails >= cvar("g_balance_uzi_first_ammo");
 	else if (req == WR_CHECKAMMO2)
 		return self.ammo_nails >= cvar("g_balance_uzi_first_ammo");
-	else if (req == WR_REGISTER)
-		weapon_register(WEP_UZI, cvar("g_balance_uzi_first_ammo"));
+	else if (req == WR_SUICIDEMESSAGE)
+		w_deathtypestring = "did the impossible";
+	else if (req == WR_KILLMESSAGE)
+		w_deathtypestring = "was riddled full of holes by";
 	return TRUE;
 };

Modified: branches/nexuiz-2.0/data/scripts/entities.def
===================================================================
--- branches/nexuiz-2.0/data/scripts/entities.def	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/scripts/entities.def	2008-09-05 19:40:34 UTC (rev 4365)
@@ -274,6 +274,7 @@
 respawntime: time till it respawns (default: 30)
 armorvalue: amount of armor it gives (default: 100 (g_pickup_armorlarge))
 max_armorvalue: max of armor it increases to (default: 999 (g_pickup_armorlarge_max))
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
 -------- SPAWNFLAGS --------
 FLOATING: the item will float in air, instead of aligning to the floor by falling
 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
@@ -286,6 +287,7 @@
 respawntime: time till it respawns (default: 20)
 armorvalue: amount of armor it gives (default: 25 (g_pickup_armormedium))
 max_armorvalue: max of armor it increases to (default: 999 (g_pickup_armormedium_max))
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
 -------- SPAWNFLAGS --------
 FLOATING: the item will float in air, instead of aligning to the floor by falling
 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
@@ -298,6 +300,7 @@
 respawntime: time till it respawns (default: 15)
 armorvalue: amount of armor it gives (default: 5 (g_pickup_armorsmall))
 max_armorvalue: max of armor it increases to (default: 999 (g_pickup_armorsmall_max))
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
 -------- SPAWNFLAGS --------
 FLOATING: the item will float in air, instead of aligning to the floor by falling
 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
@@ -309,6 +312,7 @@
 -------- KEYS --------
 ammo_nails: bullets gained by this item (if unset, g_pickup_nails is used)
 respawntime: time till it respawns (default: 15)
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
 -------- SPAWNFLAGS --------
 FLOATING: the item will float in air, instead of aligning to the floor by falling
 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
@@ -320,6 +324,7 @@
 -------- KEYS --------
 ammo_cells: cells gained by this item (if unset, g_pickup_cells is used)
 respawntime: time till it respawns (default: 15)
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
 -------- SPAWNFLAGS --------
 FLOATING: the item will float in air, instead of aligning to the floor by falling
 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
@@ -354,6 +359,7 @@
 respawntime: time till it respawns (default: 20)
 health: amount of health it gives (default: 50 (g_pickup_healthlarge))
 max_health: max of health it increases to (default: 999 (g_pickup_healthlarge_max))
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
 -------- SPAWNFLAGS --------
 FLOATING: the item will float in air, instead of aligning to the floor by falling
 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
@@ -366,6 +372,7 @@
 respawntime: time till it respawns (default: 15)
 health: amount of health it gives (default: 25 (g_pickup_healthmedium))
 max_health: max of health it increases to (default: 999 (g_pickup_healthmedium_max))
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
 -------- SPAWNFLAGS --------
 FLOATING: the item will float in air, instead of aligning to the floor by falling
 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
@@ -379,6 +386,7 @@
 respawntime: time till it respawns (default: 30)
 health: amount of health it gives (default: 100 (g_pickup_healthmega))
 max_health: max of health it increases to (default: 999 (g_pickup_healthmega_max))
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
 -------- SPAWNFLAGS --------
 FLOATING: the item will float in air, instead of aligning to the floor by falling
 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
@@ -391,6 +399,7 @@
 respawntime: time till it respawns (default: 15)
 health: amount of health it gives (default: 5 (g_pickup_healthsmall))
 max_health: max of health it increases to (default: 5 (g_pickup_healthsmall_max))
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
 -------- SPAWNFLAGS --------
 FLOATING: the item will float in air, instead of aligning to the floor by falling
 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
@@ -402,6 +411,7 @@
 In Minstagib, this randomly turns into either an invisibility, an extra lives or a speed power-up with a default respawn time of 120.
 -------- KEYS --------
 respawntime: time till it respawns (default: 120)
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
 -------- SPAWNFLAGS --------
 FLOATING: the item will float in air, instead of aligning to the floor by falling
 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
@@ -414,6 +424,7 @@
 It only appears when playing Minstagib and prevents auto-replacement of weapon_nex & weapon_rocketlauncher when used.
 -------- KEYS --------
 respawntime: time till it respawns (default: 45)
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
 -------- SPAWNFLAGS --------
 FLOATING: the item will float in air, instead of aligning to the floor by falling
 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
@@ -425,6 +436,7 @@
 -------- KEYS --------
 ammo_rockets: rockets gained by this item (if unset, g_pickup_rockets is used)
 respawntime: time till it respawns (default: 15)
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
 -------- SPAWNFLAGS --------
 FLOATING: the item will float in air, instead of aligning to the floor by falling
 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
@@ -436,6 +448,7 @@
 -------- KEYS --------
 ammo_shells: shells gained by this item (if unset, g_pickup_shells is used)
 respawntime: time till it respawns (default: 15)
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
 -------- SPAWNFLAGS --------
 FLOATING: the item will float in air, instead of aligning to the floor by falling
 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
@@ -447,6 +460,7 @@
 In Minstagib, this randomly turns into either an invisibility, an extra lives or a speed power-up with a default respawn time of 120.
 -------- KEYS --------
 respawntime: time till it respawns (default: 120)
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
 -------- SPAWNFLAGS --------
 FLOATING: the item will float in air, instead of aligning to the floor by falling
 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
@@ -500,7 +514,7 @@
 Laser beam emitter
 -------- KEYS --------
 target: target_position the laser targets (may be another entity, preferably target_position, possibly controlled by misc_follow)
-mdl: name of particle effect for the beam end point (see effectinfo.txt; default is misc_laser_beam_end)
+mdl: name of particle effect for the beam end point (see effectinfo.txt; default is laser_deadly if dmg is set, and none if not)
 colormod: color of the laser beam (default: red, that is, 1 0 0)
 dmg: damage inflicted by the beam per second, or -1 for an instant-death ray
 targetname: name to target this (then its state is toggled)
@@ -658,6 +672,8 @@
 -------- KEYS --------
 noise: path/name of .wav/.ogg file to play
 targetname: the activating button or trigger points to this.
+atten: distance attenuation of the sound (a value from 0.1 to 3.9), default is 0.5 if targeted, 3 otherwise; set to -1 for no attenuation (global sound)
+volume: volume of the sound
 */
 
 /*QUAKED trigger_counter (.5 .5 .5) ? NOMESSAGE
@@ -783,6 +799,7 @@
 -------- KEYS --------
 ammo_cells: initial cells of the weapon (if unset, g_pickup_cells is used)
 respawntime: time till it respawns (default: 15)
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
 -------- SPAWNFLAGS --------
 FLOATING: the item will float in air, instead of aligning to the floor by falling
 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
@@ -794,6 +811,7 @@
 -------- KEYS --------
 ammo_cells: initial cells of the weapon (if unset, g_pickup_cells is used)
 respawntime: time till it respawns (default: 15)
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
 -------- SPAWNFLAGS --------
 FLOATING: the item will float in air, instead of aligning to the floor by falling
 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
@@ -805,6 +823,7 @@
 -------- KEYS --------
 ammo_rockets: initial rockets of the weapon (if unset, g_pickup_rockets is used)
 respawntime: time till it respawns (default: 15)
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
 -------- SPAWNFLAGS --------
 FLOATING: the item will float in air, instead of aligning to the floor by falling
 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
@@ -816,6 +835,7 @@
 -------- KEYS --------
 ammo_rockets: initial rockets of the weapon (if unset, g_pickup_rockets is used)
 respawntime: time till it respawns (default: 15)
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
 -------- SPAWNFLAGS --------
 FLOATING: the item will float in air, instead of aligning to the floor by falling
 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
@@ -828,6 +848,7 @@
 -------- KEYS --------
 ammo_cells: initial cells of the weapon (if unset, g_pickup_cells is used)
 respawntime: time till it respawns (default: 15 * g_balance_nex_respawntime_modifier)
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
 -------- SPAWNFLAGS --------
 FLOATING: the item will float in air, instead of aligning to the floor by falling
 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
@@ -840,6 +861,7 @@
 -------- KEYS --------
 ammo_rockets: initial rockets of the weapon (if unset, g_pickup_rockets is used)
 respawntime: time till it respawns (default: 15)
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
 -------- SPAWNFLAGS --------
 FLOATING: the item will float in air, instead of aligning to the floor by falling
 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
@@ -851,6 +873,7 @@
 -------- KEYS --------
 ammo_shells: initial shells of the weapon (if unset, g_pickup_shells is used)
 respawntime: time till it respawns (default: 15)
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
 -------- SPAWNFLAGS --------
 FLOATING: the item will float in air, instead of aligning to the floor by falling
 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
@@ -862,6 +885,7 @@
 -------- KEYS --------
 ammo_nails: initial bullets of the weapon (if unset, g_pickup_nails is used)
 respawntime: time till it respawns (default: 15)
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
 -------- SPAWNFLAGS --------
 FLOATING: the item will float in air, instead of aligning to the floor by falling
 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
@@ -894,14 +918,16 @@
 _celshader: Sets the cel shader used for this geometry. Note: omit the "textures/" prefix.
 */
 
-/*QUAKED trigger_race_checkpoint (0 1 0) ? NOTOUCH - CRUSH
+/*QUAKED trigger_race_checkpoint (0 1 0) ? NOTOUCH STRICTTRIGGER CRUSH
 A checkpoint, for the race game mode. Be sure to make them quite long, so they actually catch a player reliably!
 -------- KEYS --------
 cnt: Number of the checkpoint. 0 for finish line, and at least two other checkpoints have to exist. They MUST be touched in sequential order!
 message: Death message, when touching checkpoints in the wrong order.
 targetname: Name of the checkpoint. info_player_race can target this to assign a spawn to a checkpoint. Also used for triggering a checkpoint by an event.
+target: when the checkpoint is passed, these entities are triggered. Useful for forcing items in certain areas using trigger_items
 -------- SPAWNFLAGS --------
 NOTOUCH: the checkpoint will not become active when touched, it HAS to be targeted
+STRICTTRIGGER: only trigger the targets when the checkpoint actually was reached in a valid way (that is, not when going back)
 CRUSH: the checkpoint kills when used at the wrong time
 */
 
@@ -969,3 +995,47 @@
 target: points to the entity to move (e.g. something that won't move by itself)
 killtarget: points to the entity that is to be used as the source (e.g. a func_plat)
 */
+
+/*QUAKED weapon_minstanex (1 0 .5) (-30 -30 0) (30 30 32) FLOATING
+the MinstaGib Nex. Always kills with one shot.
+-------- KEYS --------
+ammo_cells: initial cells of the weapon (if unset, g_pickup_cells is used)
+respawntime: time till it respawns (default: 15)
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
+-------- SPAWNFLAGS --------
+FLOATING: the item will float in air, instead of aligning to the floor by falling
+-------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
+model="models/weapons/g_minstanex.md3"
+*/
+
+/*QUAKED weapon_porto (1 0 .5) (-30 -30 0) (30 30 32) FLOATING
+the Port-O-Launch. Only can be shot once.
+Portals cannot be made on noimpact surfaces, and the portal missile will bounce on slick surfaces.
+-------- KEYS --------
+respawntime: time till it respawns (default: 120)
+team: out of items with the same value here, only one (random one) will spawn. Useful to put multiple items on one spot.
+-------- SPAWNFLAGS --------
+FLOATING: the item will float in air, instead of aligning to the floor by falling
+-------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
+model="models/weapons/g_porto.md3"
+*/
+
+/*QUAKED trigger_items (0 0 1) (-8 -8 -8) (8 8 8) AND OR ANDNOT
+Sets the items of any player who triggers this.
+For the number fields, not specifying a value means not changing it. To clear armor, you need to explicitly set "armor" to "-1".
+-------- KEYS --------
+netname: space separated list of items (either weapon short names (like in g_start_weapon_* cvars), or item short names "unlimited_ammo", "invincible" and "strength"
+message: message to print
+ammo_shells: amount of shells
+ammo_nails: amount of bullets
+ammo_rockets: amount of rockets
+ammo_cells: amount of cells
+health: amount of health
+armorvalue: amount of armor
+strength_finished: if "strength" is specified, the time in seconds for which the strength will hold
+invincible_finished: if "invincible" is specified, the time in seconds for which the invincibility will hold
+-------- SPAWNFLAGS --------
+AND: any items not listed will get removed, and none will get added
+OR: the player may keep items not listed
+ANDNOT: the items listed will get removed from the player
+*/

Copied: branches/nexuiz-2.0/data/textures/nex.tga (from rev 4363, trunk/data/textures/nex.tga)
===================================================================
(Binary files differ)

Copied: branches/nexuiz-2.0/data/textures/nex_bump.tga (from rev 4363, trunk/data/textures/nex_bump.tga)
===================================================================
(Binary files differ)

Copied: branches/nexuiz-2.0/data/textures/nex_gloss.tga (from rev 4363, trunk/data/textures/nex_gloss.tga)
===================================================================
(Binary files differ)

Copied: branches/nexuiz-2.0/data/textures/nex_glow.tga (from rev 4363, trunk/data/textures/nex_glow.tga)
===================================================================
(Binary files differ)

Copied: branches/nexuiz-2.0/data/textures/nex_pants.tga (from rev 4363, trunk/data/textures/nex_pants.tga)
===================================================================
(Binary files differ)

Copied: branches/nexuiz-2.0/data/textures/nex_shirt.tga (from rev 4363, trunk/data/textures/nex_shirt.tga)
===================================================================
(Binary files differ)

Copied: branches/nexuiz-2.0/data/textures/portalgun.tga (from rev 4363, trunk/data/textures/portalgun.tga)
===================================================================
(Binary files differ)

Copied: branches/nexuiz-2.0/data/textures/portalgun_gloss.tga (from rev 4363, trunk/data/textures/portalgun_gloss.tga)
===================================================================
(Binary files differ)

Copied: branches/nexuiz-2.0/data/textures/portalgun_glow.tga (from rev 4363, trunk/data/textures/portalgun_glow.tga)
===================================================================
(Binary files differ)

Copied: branches/nexuiz-2.0/data/textures/portalgun_norm.tga (from rev 4363, trunk/data/textures/portalgun_norm.tga)
===================================================================
(Binary files differ)

Modified: branches/nexuiz-2.0/data/weapons.cfg
===================================================================
--- branches/nexuiz-2.0/data/weapons.cfg	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/weapons.cfg	2008-09-05 19:40:34 UTC (rev 4365)
@@ -7,6 +7,8 @@
 set g_start_weapon_nex 0
 set g_start_weapon_hagar 0
 set g_start_weapon_rocketlauncher 0
+set g_start_weapon_minstanex 0
+set g_start_weapon_porto 0
 set g_start_ammo_shells 50
 set g_start_ammo_nails 0
 set g_start_ammo_rockets 0
@@ -33,6 +35,10 @@
 set g_pickup_healthlarge_max 999
 set g_pickup_healthmega 100
 set g_pickup_healthmega_max 999
+set g_pickup_respawntime_short 15
+set g_pickup_respawntime_medium 20
+set g_pickup_respawntime_long 30
+set g_pickup_respawntime_powerup 120
 
 set g_balance_laser_primary_damage 35
 set g_balance_laser_primary_edgedamage 10
@@ -166,11 +172,12 @@
 set g_balance_nex_force 600
 set g_balance_nex_refire 1.5
 set g_balance_nex_animtime 0.3
-set g_balance_minstagib_nex_refire 1.5
-set g_balance_minstagib_nex_animtime 0.3
 set g_balance_nex_ammo 5
-set g_balance_nex_respawntime_modifier 1
 
+set g_balance_minstanex_refire 1
+set g_balance_minstanex_animtime 0.3
+set g_balance_minstanex_ammo 10
+
 set g_balance_hagar_primary_damage 40
 set g_balance_hagar_primary_edgedamage 15
 set g_balance_hagar_primary_force 100
@@ -191,10 +198,10 @@
 set g_balance_hagar_secondary_ammo 1
 
 set g_balance_rocketlauncher_damage 130
-set g_balance_rocketlauncher_edgedamage 50
+set g_balance_rocketlauncher_edgedamage 20
 set g_balance_rocketlauncher_force 600
-set g_balance_rocketlauncher_radius 170
-set g_balance_rocketlauncher_speed 850
+set g_balance_rocketlauncher_radius 150
+set g_balance_rocketlauncher_speed 880
 set g_balance_rocketlauncher_speedaccel 0
 set g_balance_rocketlauncher_speedstart 850
 set g_balance_rocketlauncher_lifetime 30
@@ -207,3 +214,9 @@
 set g_balance_rocketlauncher_laserguided_speedstart 1000
 set g_balance_rocketlauncher_laserguided_turnrate	0.75  //0.5
 set g_balance_rocketlauncher_laserguided_allow_steal	1
+
+// TESTING: port-o-launch
+set g_balance_porto_primary_refire 1.5
+set g_balance_porto_primary_speed 2000
+set g_balance_porto_primary_lifetime 30
+set g_balance_porto_primary_ammo 25

Modified: branches/nexuiz-2.0/data/weaponsPro.cfg
===================================================================
--- branches/nexuiz-2.0/data/weaponsPro.cfg	2008-09-05 19:39:18 UTC (rev 4364)
+++ branches/nexuiz-2.0/data/weaponsPro.cfg	2008-09-05 19:40:34 UTC (rev 4365)
@@ -7,6 +7,8 @@
 set g_start_weapon_nex 0
 set g_start_weapon_hagar 0
 set g_start_weapon_rocketlauncher 0
+set g_start_weapon_minstanex 0
+set g_start_weapon_porto 0
 set g_start_ammo_shells 40
 set g_start_ammo_nails 0
 set g_start_ammo_rockets 0
@@ -33,6 +35,10 @@
 set g_pickup_healthlarge_max 100
 set g_pickup_healthmega 100
 set g_pickup_healthmega_max 200
+set g_pickup_respawntime_short 15
+set g_pickup_respawntime_medium 20
+set g_pickup_respawntime_long 30
+set g_pickup_respawntime_powerup 120
 
 set g_balance_laser_primary_damage 20
 set g_balance_laser_primary_edgedamage 10
@@ -131,12 +137,19 @@
 set g_balance_crylink_primary_speed 7000
 set g_balance_crylink_primary_spread 0
 set g_balance_crylink_primary_shots 1
-set g_balance_crylink_primary_lifetime 30
 set g_balance_crylink_primary_bounces 0
 set g_balance_crylink_primary_refire 0.1
 set g_balance_crylink_primary_animtime 0.1
 set g_balance_crylink_primary_ammo 1
 set g_balance_crylink_primary_bouncedamagefactor 0.5
+
+set g_balance_crylink_primary_middle_lifetime 30 // range: virtually infinite
+set g_balance_crylink_primary_middle_fadetime 0
+set g_balance_crylink_primary_star_lifetime 0.1 // range: 700 full, fades to 2100
+set g_balance_crylink_primary_star_fadetime 0.2
+set g_balance_crylink_primary_other_lifetime 0.1 // range: 700 full, fades to 2100
+set g_balance_crylink_primary_other_fadetime 0.2
+
 set g_balance_crylink_secondary_damage 15
 set g_balance_crylink_secondary_edgedamage 0
 set g_balance_crylink_secondary_force 100
@@ -144,22 +157,27 @@
 set g_balance_crylink_secondary_speed 7000
 set g_balance_crylink_secondary_spread 0.08
 set g_balance_crylink_secondary_shots 5
-set g_balance_crylink_secondary_lifetime 30
 set g_balance_crylink_secondary_bounces 0
 set g_balance_crylink_secondary_refire 0.6
 set g_balance_crylink_secondary_animtime 0.3
 set g_balance_crylink_secondary_ammo 5
 set g_balance_crylink_secondary_bouncedamagefactor 0.5
 
+set g_balance_crylink_secondary_middle_lifetime 30 // range: virtually infinite
+set g_balance_crylink_secondary_middle_fadetime 0
+set g_balance_crylink_secondary_line_lifetime 30 // range: virtually infinite
+set g_balance_crylink_secondary_line_fadetime 0
+
 set g_balance_nex_damage 70
 set g_balance_nex_force 0
 set g_balance_nex_refire 0.7
 set g_balance_nex_animtime 0.3
-set g_balance_minstagib_nex_refire 1.5
-set g_balance_minstagib_nex_animtime 0.3
 set g_balance_nex_ammo 5
-set g_balance_nex_respawntime_modifier 1
 
+set g_balance_minstanex_refire 1
+set g_balance_minstanex_animtime 0.3
+set g_balance_minstanex_ammo 10
+
 set g_balance_hagar_primary_damage 40
 set g_balance_hagar_primary_edgedamage 20
 set g_balance_hagar_primary_force 0
@@ -196,3 +214,9 @@
 set g_balance_rocketlauncher_laserguided_speedstart 1000
 set g_balance_rocketlauncher_laserguided_turnrate	0.75  //0.5
 set g_balance_rocketlauncher_laserguided_allow_steal	1
+
+// TESTING: port-o-launch
+set g_balance_porto_primary_refire 1.5
+set g_balance_porto_primary_speed 2000
+set g_balance_porto_primary_lifetime 30
+set g_balance_porto_primary_ammo 25




More information about the nexuiz-commits mailing list