r4222 - in branches/nexuiz-2.0: . Docs data data/particles data/qcsrc/client data/qcsrc/common data/qcsrc/server data/scripts

DONOTREPLY at icculus.org DONOTREPLY at icculus.org
Thu Aug 28 09:26:20 EDT 2008


Author: div0
Date: 2008-08-28 09:26:19 -0400 (Thu, 28 Aug 2008)
New Revision: 4222

Modified:
   branches/nexuiz-2.0/.patchsets
   branches/nexuiz-2.0/Docs/basics.aft
   branches/nexuiz-2.0/Docs/basics.html
   branches/nexuiz-2.0/data/effectinfo.txt
   branches/nexuiz-2.0/data/particles/laserbeam.tga
   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/hook.qc
   branches/nexuiz-2.0/data/qcsrc/client/laser.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/server/cl_client.qc
   branches/nexuiz-2.0/data/qcsrc/server/ctf.qc
   branches/nexuiz-2.0/data/qcsrc/server/defs.qh
   branches/nexuiz-2.0/data/qcsrc/server/domination.qc
   branches/nexuiz-2.0/data/qcsrc/server/g_lights.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/miscfunctions.qc
   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_jumppads.qc
   branches/nexuiz-2.0/data/qcsrc/server/t_plats.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/scripts/entities.def
Log:
r4215 | div0 | 2008-08-27 15:22:10 +0200 (Wed, 27 Aug 2008) | 2 lines
fix (unrelated) bug in misc_laser code, causing CSQC entity message spam
r4216 | div0 | 2008-08-28 09:39:24 +0200 (Thu, 28 Aug 2008) | 3 lines
- get rid of delayed init where I found it and replace it by something that runs BEFORE think (in the very first StartFrame); makes sure misc_follow can work right (as all entities are on their INITIAL location then)
- make misc_laser work with misc_follow
r4217 | esteel | 2008-08-28 09:46:02 +0200 (Thu, 28 Aug 2008) | 2 lines
explain jumping and pressing/holding space better, add a few more links at the end
r4218 | div0 | 2008-08-28 11:37:08 +0200 (Thu, 28 Aug 2008) | 2 lines
more laser madness (now: dark lasers, pulsating laser, laser beam interpolation)
r4219 | m0rfar | 2008-08-28 13:10:47 +0200 (Thu, 28 Aug 2008) | 4 lines
Changed default respawn times for 2 items.
50 health = 20 sec (was 15 sec)
25 armor = 20 sec (was 30 sec)
This will hopefully result in a better experience for us all :)
r4220 | div0 | 2008-08-28 14:14:11 +0200 (Thu, 28 Aug 2008) | 2 lines
fix TE_SPARK again
r4221 | div0 | 2008-08-28 15:23:46 +0200 (Thu, 28 Aug 2008) | 2 lines
zoom: do it entirely client side

Modified: branches/nexuiz-2.0/.patchsets
===================================================================
--- branches/nexuiz-2.0/.patchsets	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/.patchsets	2008-08-28 13:26:19 UTC (rev 4222)
@@ -1,2 +1,2 @@
 master = svn://svn.icculus.org/nexuiz/trunk
-revisions_applied = 1-4213
+revisions_applied = 1-4221

Modified: branches/nexuiz-2.0/Docs/basics.aft
===================================================================
--- branches/nexuiz-2.0/Docs/basics.aft	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/Docs/basics.aft	2008-08-28 13:26:19 UTC (rev 4222)
@@ -54,7 +54,7 @@
 
 The health and armor system in Nexuiz is different to other games in that it tends toward an equilibrium of 100 health and 100 armor.  You can collect as much health and armor as you like, but it will degenerate toward these values.  The more health and armor you have, the faster it will degenerate - but when you pick them up the degeneration stops momentarily.  Because of this, you can't just load up on health and armor and head off into battle - you need to constantly replenish to remain strong.  On the converse, though, when your health is below 100 it will slowly ''re''generate (armor will not).  The lower it is, the faster it will replenish.  This regeneration stops for a few seconds if you are hurt, so just running away after a fight is not usually enough to restore you to useful levels again - you have to wait a while to regain your full health, and so picking up health after a fight is still important.
 
-In regard to the previous point of working a map to control items, and also shooting at where your opponent will be, it's important to try to predict where on the map your opponents are even when you can't directly see them.  Firstly, try to listen for them; secondly, learn the maps so you know the most useful routes and the most popular areas to which people tend to gravitate.  It gives you a marked advantage to be able to say, "I saw or heard someone pick up an armor, so they must be at such-and-such location, and from there he can only go to this place or that place." Just shooting something to the likely places your opponent will be - a rocket or a grenade for example - can often result in either a surprise kill, or in surprise damage which you can then follow up.  Don't underestimate how likely a well-aimed educated guess is to hit someone.  Maybe he'll walk into it - rockets can be hard to see coming, and grenades lying on the floor or spammed into a small space can be really hard to avoid.  Once you've hurt him you'll hear the tink of your damage, and you will also know for sure were he is and can finish him off.  You can pretty much 'lock down' a map by picking up items, and making sure others have a hard time getting around which will also mess with their concentration.
+In regard to the previous point of working a map to control items, and also shooting at where your opponent will be, it's important to try to predict where on the map your opponents are even when you can't directly see them.  First, try to listen for them; secondly, learn the maps so you know the most useful routes and the most popular areas to which people tend to gravitate.  It gives you a marked advantage to be able to say, "I saw or heard someone pick up an armor, so they must be at such-and-such location, and from there he can only go to this place or that place." Just shooting something to the likely places your opponent will be - a rocket or a grenade for example - can often result in either a surprise kill, or in surprise damage which you can then follow up.  Don't underestimate how likely a well-aimed educated guess is to hit someone.  Maybe he'll walk into it - rockets can be hard to see coming, and grenades lying on the floor or spammed into a small space can be really hard to avoid.  Once you've hurt him you'll hear the tink of your damage, and you will also know for sure were he is and can finish him off.  You can pretty much 'lock down' a map by picking up items, and making sure others have a hard time getting around which will also mess with their concentration.
 
 **''Using Weapons Effectively''
 Among the items you can pick up are the weapons.  Some of them are very unique to Nexuiz, and to get the most out of them you need to be familiar with every aspect of how they work - including the rate of fire, the damage per hit, the spread and speed of the payload, and so on.
@@ -84,7 +84,7 @@
 **''Using Jumping Effectively''
 You will need to be able to get around the map quickly and efficiently if you are to hunt down your opponents, and collect items well.  As already mentioned, the laser is useful for jumping and gaining speed - but you can use all explosive weapons for a similar effect.  Other weapons, however, will cause more damage to your health, and not all offer the same strong push as the laser.  The rocket launcher will push you twice as far, but you will also take ''lots'' more damage.  The mortar has the same push as the laser, but its explosion is bigger so the timing and the right angle are less of an issue.  For this reason, the mortar is better for a beginner to learn these sorts of jumps.
 
-You should also know that you gain a bit extra speed moving forward simply by jumping.  When you walk, there is friction from the floor which keeps you at a constant speed.  If you jump as you walk, the amount you touch the floor is considerably less, and so the friction does not slow you down as much.  The longer you jump, the faster you go.  The most simple way to do this is to run forward, jump, and then while you are in the air release and repress the jump button.  Your character will jump again the moment you land, without you having to worry about timing hitting the jump key.  Unlike other games, Nexuiz does not need you to do this with strange key combinations or even with circular movements of the mouse - trying to do these will generally make you slower.  The only time that strafe-jumping (holding one of the strafe-keys while jumping) is useful is for the very first jump you make.  This can increase your jump speed a little - although not by much.
+You should also know that you gain a bit extra speed moving forward simply by jumping.  When you walk, there is friction from the floor which keeps you at a constant speed.  If you jump as you walk, the amount you touch the floor is considerably less, and so the friction does not slow you down as much.  The longer you jump, the faster you go.  The most simple way to do this is to run forward, jump, and then while you are in the air release and repress/hold the jump button.  Your character will jump again the moment you land, without you having to worry about timing hitting the jump key.  Just repeat it a few times and you will notice you get faster and faster.  Unlike other games, Nexuiz does not need you to do this with strange key combinations or even with circular movements of the mouse - trying to do these will generally make you slower.  The only time that strafe-jumping (holding one of the strafe-keys while jumping) is useful is for the very first jump you make.  This can increase your jump speed a little - although not by much.
 
 Another useful thing to know about movement is that if you run and jump, you can release the forward key and instead press the left or right key, and turn your mouse in the same direction.  Doing so will turn or 'bend' your jump in this direction.  This is a useful trick to get around corners, to become less predictable, and also to avoid having to stop jumping to change direction.
 
@@ -119,11 +119,16 @@
 There are some more of these mutators, but those are the most important ones.
 
 A list of useful links:
+	*The official Nexuiz forum: {+http://alientrap.org/forum+}
+	*The semiofficial Nexuiz wiki: {+http://alientrap.org/wiki+}
+	*The official Nexuiz ladder: {+http://planetnexuiz.com/ladder+}
+	*The Official Nexuiz tournaments: {+http://planetnexuiz.com/tourney+}
+	*General Nexuiz Tips and Tricks: {+http://alientrap.org/forum/viewtopic.php?t=90+}
+	*How to get more help via IRC: {+http://alientrap.org/forum/viewtopic.php?t=497+}
 	*CTF guide and ctf MAPS guide: {+http://alientrap.org/forum/viewtopic.php?t=849+}
 	*Team communication explained: {+http://alientrap.org/forum/viewtopic.php?t=555+}
-	*General Nexuiz Tips and Tricks: {+http://alientrap.org/forum/viewtopic.php?t=90+}
-	*How to get more help via IRC: {+http://alientrap.org/forum/viewtopic.php?t=497+}
 	*A few useful jumps: http://esteel.planetnexuiz.de/nexuiz/demos/jumps.pk3  (copy the file into Nexuiz/data and you should find the demos in the demos menu)
 	*FPS settings: {+http://alientrap.org/forum/viewtopic.php?t=1628+}
 	*A list of all available variables in Nexuiz: {+http://alientrap.org/wiki/pmwiki.php?n=Main.GameTweaks+}
 	*A list of all available commands in Nexuiz: {+http://alientrap.org/wiki/pmwiki.php?n=Main.ConsoleCommands+}
+	*Some info about generic strategy in games: {+http://alientrap.org/forum/viewtopic.php?t=3496+}

Modified: branches/nexuiz-2.0/Docs/basics.html
===================================================================
--- branches/nexuiz-2.0/Docs/basics.html	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/Docs/basics.html	2008-08-28 13:26:19 UTC (rev 4222)
@@ -188,7 +188,7 @@
 The health and armor system in Nexuiz is different to other games in that it tends toward an equilibrium of 100 health and 100 armor.  You can collect as much health and armor as you like, but it will degenerate toward these values.  The more health and armor you have, the faster it will degenerate - but when you pick them up the degeneration stops momentarily.  Because of this, you can't just load up on health and armor and head off into battle - you need to constantly replenish to remain strong.  On the converse, though, when your health is below 100 it will slowly <em>re</em>generate (armor will not).  The lower it is, the faster it will replenish.  This regeneration stops for a few seconds if you are hurt, so just running away after a fight is not usually enough to restore you to useful levels again - you have to wait a while to regain your full health, and so picking up health after a fight is still important.
 </p>
 <p class="Body">
-In regard to the previous point of working a map to control items, and also shooting at where your opponent will be, it's important to try to predict where on the map your opponents are even when you can't directly see them.  Firstly, try to listen for them; secondly, learn the maps so you know the most useful routes and the most popular areas to which people tend to gravitate.  It gives you a marked advantage to be able to say, &quot;I saw or heard someone pick up an armor, so they must be at such-and-such location, and from there he can only go to this place or that place.&quot; Just shooting something to the likely places your opponent will be - a rocket or a grenade for example - can often result in either a surprise kill, or in surprise damage which you can then follow up.  Don't underestimate how likely a well-aimed educated guess is to hit someone.  Maybe he'll walk into it - rockets can be hard to see coming, and grenades lying on the floor or spammed into a small space can be really hard to avoid.  Once you've hurt him you'll hear the tink of your damage, and you will also know for sure were he is and can finish him off.  You can pretty much 'lock down' a map by picking up items, and making sure others have a hard time getting around which will also mess with their concentration.
+In regard to the previous point of working a map to control items, and also shooting at where your opponent will be, it's important to try to predict where on the map your opponents are even when you can't directly see them.  First, try to listen for them; secondly, learn the maps so you know the most useful routes and the most popular areas to which people tend to gravitate.  It gives you a marked advantage to be able to say, &quot;I saw or heard someone pick up an armor, so they must be at such-and-such location, and from there he can only go to this place or that place.&quot; Just shooting something to the likely places your opponent will be - a rocket or a grenade for example - can often result in either a surprise kill, or in surprise damage which you can then follow up.  Don't underestimate how likely a well-aimed educated guess is to hit someone.  Maybe he'll walk into it - rockets can be hard to see coming, and grenades lying on the floor or spammed into a small space can be really hard to avoid.  Once you've hurt him you'll hear the tink of your damage, and you will also know for sure were he is and can finish him off.  You can pretty much 'lock down' a map by picking up items, and making sure others have a hard time getting around which will also mess with their concentration.
 </p>
 <!--End Section 2-->
 <h3><a name="''Using Weapons Effectively''"><em>Using Weapons Effectively</em></a></h3>
@@ -234,7 +234,7 @@
 You will need to be able to get around the map quickly and efficiently if you are to hunt down your opponents, and collect items well.  As already mentioned, the laser is useful for jumping and gaining speed - but you can use all explosive weapons for a similar effect.  Other weapons, however, will cause more damage to your health, and not all offer the same strong push as the laser.  The rocket launcher will push you twice as far, but you will also take <em>lots</em> more damage.  The mortar has the same push as the laser, but its explosion is bigger so the timing and the right angle are less of an issue.  For this reason, the mortar is better for a beginner to learn these sorts of jumps.
 </p>
 <p class="Body">
-You should also know that you gain a bit extra speed moving forward simply by jumping.  When you walk, there is friction from the floor which keeps you at a constant speed.  If you jump as you walk, the amount you touch the floor is considerably less, and so the friction does not slow you down as much.  The longer you jump, the faster you go.  The most simple way to do this is to run forward, jump, and then while you are in the air release and repress the jump button.  Your character will jump again the moment you land, without you having to worry about timing hitting the jump key.  Unlike other games, Nexuiz does not need you to do this with strange key combinations or even with circular movements of the mouse - trying to do these will generally make you slower.  The only time that strafe-jumping (holding one of the strafe-keys while jumping) is useful is for the very first jump you make.  This can increase your jump speed a little - although not by much.
+You should also know that you gain a bit extra speed moving forward simply by jumping.  When you walk, there is friction from the floor which keeps you at a constant speed.  If you jump as you walk, the amount you touch the floor is considerably less, and so the friction does not slow you down as much.  The longer you jump, the faster you go.  The most simple way to do this is to run forward, jump, and then while you are in the air release and repress/hold the jump button.  Your character will jump again the moment you land, without you having to worry about timing hitting the jump key.  Just repeat it a few times and you will notice you get faster and faster.  Unlike other games, Nexuiz does not need you to do this with strange key combinations or even with circular movements of the mouse - trying to do these will generally make you slower.  The only time that strafe-jumping (holding one of the strafe-keys while jumping) is useful is for the very first jump you make.  This can increase your jump speed a little - although not by much.
 </p>
 <p class="Body">
 Another useful thing to know about movement is that if you run and jump, you can release the forward key and instead press the left or right key, and turn your mouse in the same direction.  Doing so will turn or 'bend' your jump in this direction.  This is a useful trick to get around corners, to become less predictable, and also to avoid having to stop jumping to change direction.
@@ -284,14 +284,19 @@
 <p class="Body">
 A list of useful links:
 <ul>
+<li>The official Nexuiz forum: <a class="link" href="http://alientrap.org/forum">http://alientrap.org/forum</a></li>
+<li>The semiofficial Nexuiz wiki: <a class="link" href="http://alientrap.org/wiki">http://alientrap.org/wiki</a></li>
+<li>The official Nexuiz ladder: <a class="link" href="http://planetnexuiz.com/ladder">http://planetnexuiz.com/ladder</a></li>
+<li>The Official Nexuiz tournaments: <a class="link" href="http://planetnexuiz.com/tourney">http://planetnexuiz.com/tourney</a></li>
+<li>General Nexuiz Tips and Tricks: <a class="link" href="http://alientrap.org/forum/viewtopic.php?t=90">http://alientrap.org/forum/viewtopic.php?t=90</a></li>
+<li>How to get more help via IRC: <a class="link" href="http://alientrap.org/forum/viewtopic.php?t=497">http://alientrap.org/forum/viewtopic.php?t=497</a></li>
 <li>CTF guide and ctf MAPS guide: <a class="link" href="http://alientrap.org/forum/viewtopic.php?t=849">http://alientrap.org/forum/viewtopic.php?t=849</a></li>
 <li>Team communication explained: <a class="link" href="http://alientrap.org/forum/viewtopic.php?t=555">http://alientrap.org/forum/viewtopic.php?t=555</a></li>
-<li>General Nexuiz Tips and Tricks: <a class="link" href="http://alientrap.org/forum/viewtopic.php?t=90">http://alientrap.org/forum/viewtopic.php?t=90</a></li>
-<li>How to get more help via IRC: <a class="link" href="http://alientrap.org/forum/viewtopic.php?t=497">http://alientrap.org/forum/viewtopic.php?t=497</a></li>
 <li>A few useful jumps: <a class="link" href="http://esteel.planetnexuiz.de/nexuiz/demos/jumps.pk3">http://esteel.planetnexuiz.de/nexuiz/demos/jumps.pk3</a>  (copy the file into Nexuiz/data and you should find the demos in the demos menu)</li>
 <li>FPS settings: <a class="link" href="http://alientrap.org/forum/viewtopic.php?t=1628">http://alientrap.org/forum/viewtopic.php?t=1628</a></li>
 <li>A list of all available variables in Nexuiz: <a class="link" href="http://alientrap.org/wiki/pmwiki.php?n=Main.GameTweaks">http://alientrap.org/wiki/pmwiki.php?n=Main.GameTweaks</a></li>
 <li>A list of all available commands in Nexuiz: <a class="link" href="http://alientrap.org/wiki/pmwiki.php?n=Main.ConsoleCommands">http://alientrap.org/wiki/pmwiki.php?n=Main.ConsoleCommands</a></li>
+<li>Some info about generic strategy in games: <a class="link" href="http://alientrap.org/forum/viewtopic.php?t=3496">http://alientrap.org/forum/viewtopic.php?t=3496</a></li>
 </ul>
 </p>
 <!--End Section 2-->

Modified: branches/nexuiz-2.0/data/effectinfo.txt
===================================================================
--- branches/nexuiz-2.0/data/effectinfo.txt	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/effectinfo.txt	2008-08-28 13:26:19 UTC (rev 4222)
@@ -600,7 +600,7 @@
 
 // sparks (quake effect)
 effect TE_SPARK
-count 1000
+count 1
 type spark
 color 0x8f4333 0xfff31b
 size 0.4 0.4
@@ -2289,15 +2289,3 @@
 velocityjitter 512 512 512
 
 
-// sparks
-effect func_sparks
-count 1000
-type spark
-color 0x8f4333 0xfff31b
-size 0.4 0.4
-alpha 0 256 768
-gravity 1
-bounce -1
-velocityoffset 0 0 80
-velocityjitter 64 64 64
-velocitymultiplier 1

Modified: branches/nexuiz-2.0/data/particles/laserbeam.tga
===================================================================
(Binary files differ)

Modified: branches/nexuiz-2.0/data/qcsrc/client/Main.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/Main.qc	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/qcsrc/client/Main.qc	2008-08-28 13:26:19 UTC (rev 4222)
@@ -67,6 +67,11 @@
 	registercmd("sbar_columns_set");
 	registercmd("sbar_columns_help");
 
+	registercmd("+button3");
+	registercmd("-button3");
+	registercmd("+button4");
+	registercmd("-button4");
+
 	registercvar("sbar_usecsqc", "1");
 	registercvar("sbar_columns", "default", CVAR_SAVE);
 
@@ -197,6 +202,7 @@
 
 // CSQC_ConsoleCommand : Used to parse commands in the console that have been registered with the "registercmd" function
 // Return value should be 1 if CSQC handled the command, otherwise return 0 to have the engine handle it.
+float button_zoom;
 void Cmd_Sbar_SetFields(float);
 void Cmd_Sbar_Help(float);
 float CSQC_ConsoleCommand(string strMessage)
@@ -210,11 +216,19 @@
 	local string strCmd;
 	strCmd = argv(0);
 
-	/*if(strCmd == "ctf_menu") {
-		ctf_menu_show();
-		nReturn = true;
-		} else*/
-	if(strCmd == "ons_map") {
+	if(strCmd == "+button4") { // zoom
+		button_zoom = 1;
+		return true;
+	} else if(strCmd == "-button4") { // zoom
+		button_zoom = 0;
+		return true;
+	} else if(strCmd == "+button3") { // secondary
+		button_attack2 = 1;
+		return false;
+	} else if(strCmd == "-button3") { // secondary
+		button_attack2 = 0;
+		return false;
+	} else if(strCmd == "ons_map") {
 		Cmd_ons_map();
 		return true;
 	} else if(strCmd == "sbar_columns_set") {
@@ -600,6 +614,7 @@
 {
 	csqc_revision = ReadShort();
 	maxclients = ReadByte();
+	minstagib = ReadByte();
 
 	CSQC_CheckRevision();
 }
@@ -723,6 +738,12 @@
 	spectatee_status = newspectatee_status;
 }
 
+void Net_ReadSpawn()
+{
+	zoomin_effect = 1;
+	current_viewzoom = 0.6;
+}
+
 // 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.
@@ -769,6 +790,10 @@
 			Net_GrapplingHook();
 			bHandled = true;
 			break;
+		case TE_CSQC_SPAWN: // TE_BEAM
+			Net_ReadSpawn();
+			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-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/qcsrc/client/View.qc	2008-08-28 13:26:19 UTC (rev 4222)
@@ -2,6 +2,68 @@
 
 float drawtime;
 
+float tan(float x)
+{ 
+	return sin(x) / cos(x);
+}
+float atan2(float y, float x)
+{
+	vector v;
+	v = '1 0 0' * x + '0 1 0' * y;
+	v = vectoangles(v);
+	return v_y * 0.01745329251994329576;
+}
+
+vector GetCurrentFov()
+{
+	float zoomspeed, zoomfactor, zoomdir;
+
+	zoomfactor = cvar("cl_zoomfactor");
+	if(zoomfactor < 1 || zoomfactor > 16)
+		zoomfactor = 2.5;
+	zoomspeed = cvar("cl_zoomspeed");
+	if(zoomspeed >= 0)
+		if(zoomspeed < 0.5 || zoomspeed > 16)
+			zoomspeed = 3.5;
+
+	zoomdir = button_zoom;
+	if(activeweapon == 7 && !minstagib)
+		zoomdir += button_attack2;
+
+	if(zoomdir)
+		zoomin_effect = 0;
+
+	if(zoomin_effect)
+	{
+		current_viewzoom = min(1, current_viewzoom + drawframetime);
+	}
+	else
+	{
+		if(zoomspeed < 0) // instant zoom
+		{
+			if(zoomdir)
+				current_viewzoom = 1 / zoomfactor;
+			else
+				current_viewzoom = 1;
+		}
+		else
+		{
+			if(zoomdir)
+				current_viewzoom = 1 / bound(1, 1 / current_viewzoom + drawframetime * zoomspeed * (zoomfactor - 1), zoomfactor);
+			else
+				current_viewzoom = bound(1 / zoomfactor, current_viewzoom + drawframetime * zoomspeed * (1 - 1 / zoomfactor), 1);
+		}
+	}
+
+	float frustumx, frustumy, fovx, fovy;
+	frustumy = tan(cvar("fov") * 0.00872664625997164788) * 0.75 * current_viewzoom;
+	frustumx = frustumy * cvar("vid_width") / cvar("vid_height") / cvar("vid_pixelheight");
+	fovx = atan2(frustumx, 1) / 0.00872664625997164788;
+	fovy = atan2(frustumy, 1) / 0.00872664625997164788;
+
+	return '1 0 0' * fovx + '0 1 0' * fovy;
+}
+
 void CSQC_common_hud(void);
 
 void CSQC_kh_hud(void);
@@ -37,6 +99,8 @@
 	// Assign Standard Viewflags
 	// Draw the World (and sky)
 	R_SetView(VF_DRAWWORLD, 1);
+
+	R_SetView(VF_FOV, GetCurrentFov());
 	
 	// Draw the Crosshair
 	R_SetView(VF_DRAWCROSSHAIR, !Sbar_WouldDrawScoreboard());

Modified: branches/nexuiz-2.0/data/qcsrc/client/hook.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/hook.qc	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/qcsrc/client/hook.qc	2008-08-28 13:26:19 UTC (rev 4222)
@@ -2,18 +2,14 @@
 .vector HookEnd;
 .float HookKillTime;
 
-void Draw_CylindricLine(vector from, vector to, float thickness, string texture, float aspect, vector rgb, float drawflag)
+void Draw_CylindricLine(vector from, vector to, float thickness, string texture, float aspect, float shift, vector rgb, float alpha, float drawflag)
 {
 	// I want to draw a quad...
 	// from and to are MIDPOINTS.
 	
-	float t;
 	vector axis, thickdir, A, B, C, D;
 	float length_tex;
 
-	t = -2 * time;
-	t = random();
-
 	axis = normalize(to - from);
 	length_tex = aspect * vlen(to - from) / thickness;
 
@@ -26,10 +22,10 @@
 	D = to - thickdir * (thickness / 2);
 
 	R_BeginPolygon(texture, drawflag);
-	R_PolygonVertex(A, '0 0 0' + t * '1 0 0', rgb, 1);
-	R_PolygonVertex(B, '0 1 0' + t * '1 0 0', rgb, 1);
-	R_PolygonVertex(C, '0 1 0' + (t + length_tex) * '1 0 0', rgb, 1);
-	R_PolygonVertex(D, '0 0 0' + (t + length_tex) * '1 0 0', rgb, 1);
+	R_PolygonVertex(A, '0 0 0' + shift * '1 0 0', rgb, alpha);
+	R_PolygonVertex(B, '0 1 0' + shift * '1 0 0', rgb, alpha);
+	R_PolygonVertex(C, '0 1 0' + (shift + length_tex) * '1 0 0', rgb, alpha);
+	R_PolygonVertex(D, '0 0 0' + (shift + length_tex) * '1 0 0', rgb, alpha);
 	R_EndPolygon();
 }
 
@@ -72,9 +68,9 @@
 		rgb = '.3 1 .3';
 	}
 	if(checkextension("DP_SV_WRITEPICTURE"))
-		Draw_CylindricLine(b, a, 8, tex, 0.25, '1 1 1', DRAWFLAG_NORMAL);
+		Draw_CylindricLine(b, a, 8, tex, 0.25, random(), '1 1 1', 1, DRAWFLAG_NORMAL);
 	else
-		Draw_CylindricLine(b, a, 1, "", 0.25, rgb, DRAWFLAG_NORMAL);
+		Draw_CylindricLine(b, a, 1, "", 0.25, 0, rgb, 1, DRAWFLAG_NORMAL);
 }
 
 void Net_GrapplingHook()

Modified: branches/nexuiz-2.0/data/qcsrc/client/laser.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/laser.qc	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/qcsrc/client/laser.qc	2008-08-28 13:26:19 UTC (rev 4222)
@@ -1,3 +1,63 @@
+// FIXME make this generic code, to be used for other entities too?
+.vector iorigin1, iorigin2;
+.vector ivelocity1, ivelocity2;
+.vector iangles1, iangles2;
+.float itime1, itime2;
+void InterpolateOrigin_Note()
+{
+	float dt;
+
+	self.iorigin1 = self.iorigin2;
+	self.iangles1 = self.iangles2;
+	self.ivelocity1 = self.ivelocity2;
+
+	self.iorigin2 = self.origin;
+	self.iangles2 = self.angles;
+	self.ivelocity2 = self.velocity;
+
+	dt = time - self.itime1;
+
+	if(vlen(self.iorigin2 - self.iorigin1) > 1000)
+	{
+		self.iorigin1 = self.iorigin2;
+		self.iangles1 = self.iangles2;
+		self.ivelocity1 = self.ivelocity2;
+	}
+	else if(vlen(self.ivelocity2 - self.ivelocity1) > 1000)
+	{
+		self.iangles1 = self.iangles2;
+		self.ivelocity1 = self.ivelocity2;
+	}
+
+	if(dt < 0.2)
+	{
+		self.itime1 = time;
+		self.itime2 = time + getstatf(STAT_SYS_TICRATE);
+	}
+	else
+	{
+		// don't lerp
+		self.itime1 = self.itime2 = time;
+	}
+}
+void InterpolateOrigin_Do()
+{
+	if(self.itime1 && self.itime2 && self.itime1 != self.itime2)
+	{
+		float f;
+		f = bound(0, (time - self.itime1) / (self.itime2 - self.itime1), 1);
+		self.origin = (1 - f) * self.iorigin1 + f * self.iorigin2;
+		self.angles = (1 - f) * self.iangles1 + f * self.iangles2;
+		self.velocity = (1 - f) * self.ivelocity1 + f * self.ivelocity2;
+	}
+}
+void InterpolateOrigin_Undo()
+{
+	self.origin = self.iorigin2;
+	self.angles = self.iangles2;
+	self.velocity = self.ivelocity2;
+}
+
 // a laser goes from origin in direction angles
 // it has color 'colormod'
 // and stops when something is in the way
@@ -4,14 +64,32 @@
 .float cnt; // end effect
 .vector colormod;
 .float state; // on-off
+.float count; // flags for the laser
+.vector velocity;
+.float alpha;
 
 void Draw_Laser()
 {
 	if(!self.state)
 		return;
-	makevectors(self.angles);
-	traceline(self.origin, self.origin + v_forward * 32768, 0, self);
-	Draw_CylindricLine(self.origin, trace_endpos, 2, "particles/laserbeam", 1, self.colormod, DRAWFLAG_ADDITIVE); // TODO make a texture to make the laser look smoother
+	InterpolateOrigin_Do();
+	if(self.count & 0x80)
+	{
+		traceline(self.origin, self.velocity, 0, self);
+	}
+	else
+	{
+		makevectors(self.angles);
+		traceline(self.origin, self.origin + v_forward * 32768, 0, self);
+	}
+	if(self.alpha)
+	{
+		Draw_CylindricLine(self.origin, trace_endpos, 2, "particles/laserbeam", 0, time * 3, self.colormod, self.alpha, DRAWFLAG_NORMAL); // TODO make a texture to make the laser look smoother
+	}
+	else
+	{
+		Draw_CylindricLine(self.origin, trace_endpos, 2, "particles/laserbeam", 0, time * 3, self.colormod, 1, DRAWFLAG_ADDITIVE); // TODO make a texture to make the laser look smoother
+	}
 	pointparticles(self.cnt, trace_endpos, trace_plane_normal, 256 * drawframetime);
 }
 
@@ -20,6 +98,8 @@
 	float f;
 	// 30 bytes, or 13 bytes for just moving
 	f = ReadByte();
+	self.count = (f & 0xC0);
+	InterpolateOrigin_Undo();
 	if(f & 1)
 	{
 		self.origin_x = ReadCoord();
@@ -31,14 +111,28 @@
 		self.colormod_x = ReadByte() / 255.0;
 		self.colormod_y = ReadByte() / 255.0;
 		self.colormod_z = ReadByte() / 255.0;
+		if(f & 0x40)
+			self.alpha = ReadByte() / 255.0;
+		else
+			self.alpha = 0;
 		self.cnt = ReadShort(); // effect number
 	}
 	if(f & 2)
 	{
-		self.angles_x = ReadCoord();
-		self.angles_y = ReadCoord();
+		if(f & 0x80)
+		{
+			self.velocity_x = ReadCoord();
+			self.velocity_y = ReadCoord();
+			self.velocity_z = ReadCoord();
+		}
+		else
+		{
+			self.angles_x = ReadCoord();
+			self.angles_y = ReadCoord();
+		}
 	}
 	if(f & 4)
 		self.state = ReadByte();
+	InterpolateOrigin_Note();
 	self.draw = Draw_Laser;
 }

Modified: branches/nexuiz-2.0/data/qcsrc/client/main.qh
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/main.qh	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/qcsrc/client/main.qh	2008-08-28 13:26:19 UTC (rev 4222)
@@ -113,3 +113,10 @@
 .void(void) draw;
 float drawframetime;
 vector view_origin, view_angles, view_forward, view_right, view_up;
+
+float button_zoom;
+float button_attack2;
+float activeweapon;
+float minstagib;
+float current_viewzoom;
+float zoomin_effect;

Modified: branches/nexuiz-2.0/data/qcsrc/client/sbar.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/client/sbar.qc	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/qcsrc/client/sbar.qc	2008-08-28 13:26:19 UTC (rev 4222)
@@ -1,6 +1,5 @@
 
 float last_weapon;
-float activeweapon;
 float weapontime;
 
 float sb_lines; // still don't know what to do with that NOTE: check dp's sbar.c to see what that should be

Modified: branches/nexuiz-2.0/data/qcsrc/common/constants.qh
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/common/constants.qh	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/qcsrc/common/constants.qh	2008-08-28 13:26:19 UTC (rev 4222)
@@ -197,9 +197,11 @@
 const float TE_CSQC_RACE = 109;
 const float TE_CSQC_FORCESCOREBOARD = 110;
 const float TE_CSQC_SPECTATING = 111;
+const float TE_CSQC_SPAWN = 112;
 
 const float STAT_KH_KEYS = 32;
 const float STAT_CTF_STATE = 33;
+const float STAT_SYS_TICRATE = 34;
 const float CTF_STATE_ATTACK = 1;
 const float CTF_STATE_DEFEND = 2;
 const float CTF_STATE_COMMANDER = 3;

Modified: branches/nexuiz-2.0/data/qcsrc/server/cl_client.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/cl_client.qc	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/qcsrc/server/cl_client.qc	2008-08-28 13:26:19 UTC (rev 4222)
@@ -473,7 +473,6 @@
 	self.punchvector = '0 0 0';
 	self.oldvelocity = self.velocity;
 	self.customizeentityforclient = Client_customizeentityforclient;
-	self.viewzoom = 1;
 	self.wantswelcomemessage = 1;
 
 	if(g_arena)
@@ -680,8 +679,11 @@
 		self.punchvector = '0 0 0';
 		self.oldvelocity = self.velocity;
 
-		self.viewzoom = 0.6;
-		self.has_zoomed = 0;
+		msg_entity = self;
+		WRITESPECTATABLE_MSG_ONE({
+			WriteByte(MSG_ONE, SVC_TEMPENTITY);
+			WriteByte(MSG_ONE, TE_CSQC_SPAWN);
+		});
 
 		self.customizeentityforclient = Client_customizeentityforclient;
 
@@ -772,6 +774,7 @@
 	WriteByte(MSG_ONE, TE_CSQC_INIT);
 	WriteShort(MSG_ONE, CSQC_REVISION);
 	WriteByte(MSG_ONE, maxclients);
+	WriteByte(MSG_ONE, g_minstagib);
 }
 
 /*
@@ -1668,7 +1671,6 @@
 	self.punchangle = spectatee.punchangle;
 	self.view_ofs = spectatee.view_ofs;
 	self.v_angle = spectatee.v_angle;
-	self.viewzoom = spectatee.viewzoom;
 	self.velocity = spectatee.velocity;
 	self.dmg_take = spectatee.dmg_take;
 	self.dmg_save = spectatee.dmg_save;
@@ -1922,6 +1924,7 @@
 .float spectatee_status;
 void PlayerPreThink (void)
 {
+	self.stat_sys_ticrate = cvar("sys_ticrate");
 	if(blockSpectators)
 		checkSpectatorBlock();
 	
@@ -2118,53 +2121,6 @@
 
 		W_WeaponFrame();
 
-		{
-			float zoomfactor, zoomspeed, zoomdir;
-			zoomfactor = self.cvar_cl_zoomfactor;
-			if(zoomfactor < 1 || zoomfactor > 16)
-				zoomfactor = 2.5;
-			zoomspeed = self.cvar_cl_zoomspeed;
-			if(zoomspeed >= 0) // < 0 is instant zoom
-				if(zoomspeed < 0.5 || zoomspeed > 16)
-					zoomspeed = 3.5;
-
-			zoomdir = self.BUTTON_ZOOM;
-			if(self.BUTTON_ATCK2)
-				if(self.weapon == WEP_NEX)
-					if(!g_minstagib)
-						zoomdir = 1;
-
-			if(zoomdir)
-				self.has_zoomed = 1;
-
-			if(self.has_zoomed)
-			{
-				if(zoomspeed <= 0) // instant zoom
-				{
-					if(zoomdir)
-						self.viewzoom = 1 / zoomfactor;
-					else
-						self.viewzoom = 1;
-				}
-				else
-				{
-					// geometric zoom would be:
-					//   self.viewzoom = bound(1 / zoomfactor, self.viewzoom * pow(zoomfactor, (zoomdir ? -1 : 1) * frametime * zoomspeed), 1);
-					// however, testing showed that arithmetic/harmonic zoom works better
-					if(zoomdir)
-						// self.viewzoom = 1 / bound(1, 1 / self.viewzoom + (zoomdir ? 1 : -1) * frametime * zoomspeed * (zoomfactor - 1), zoomfactor);
-						// zoom in = arithmetic: 1x, 2x, 3x, 4x, ..., 8x
-						self.viewzoom = 1 / bound(1, 1 / self.viewzoom + frametime * zoomspeed * (zoomfactor - 1), zoomfactor);
-					else
-						// self.viewzoom = bound(1 / zoomfactor, self.viewzoom + (zoomdir ? -1 : 1) * frametime * zoomspeed * (1 - 1 / zoomfactor), 1);
-						// zoom out = harmonic: 8/1x, 8/2x, 8/3x, 8/4x, ..., 8/8x
-						self.viewzoom = bound(1 / zoomfactor, self.viewzoom + frametime * zoomspeed * (1 - 1 / zoomfactor), 1);
-				}
-			}
-			else
-				self.viewzoom = min(1, self.viewzoom + frametime); // spawn zoom-in
-		}
-
 		player_powerups();
 		player_regen();
 		player_anim();

Modified: branches/nexuiz-2.0/data/qcsrc/server/ctf.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/ctf.qc	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/qcsrc/server/ctf.qc	2008-08-28 13:26:19 UTC (rev 4222)
@@ -662,8 +662,6 @@
 
 void ctf_delayedinit()
 {
-	self.think = SUB_Remove;
-	self.nextthink = time;
 	// if no teams are found, spawn defaults
 	if (find(world, classname, "ctf_team") == world)
 		ctf_spawnteams();
@@ -673,11 +671,7 @@
 
 void ctf_init()
 {
-	local entity e;
-
-	e = spawn();
-	e.think = ctf_delayedinit;
-	e.nextthink = time + 0.1;
+	InitializeEntity(world, ctf_delayedinit, INITPRIO_GAMETYPE);
 	flagcaptimerecord = stof(db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time")));
 };
 

Modified: branches/nexuiz-2.0/data/qcsrc/server/defs.qh
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/defs.qh	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/qcsrc/server/defs.qh	2008-08-28 13:26:19 UTC (rev 4222)
@@ -28,6 +28,7 @@
 
 entity	activator;
 string	string_null;
+const var void(void)	func_null;
 
 float player_count;
 float currentbots;
@@ -489,3 +490,4 @@
 #define MAKE_INDEPENDENT_PLAYER(e) ((e).solid = SOLID_TRIGGER)
 
 string clientstuff;
+.float stat_sys_ticrate;

Modified: branches/nexuiz-2.0/data/qcsrc/server/domination.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/domination.qc	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/qcsrc/server/domination.qc	2008-08-28 13:26:19 UTC (rev 4222)
@@ -560,8 +560,6 @@
 {
 	local entity head;
 
-	self.think = SUB_Remove;
-	self.nextthink = time;
 	// if no teams are found, spawn defaults
 	if (find(world, classname, "dom_team") == world)
 		dom_spawnteams();
@@ -592,7 +590,6 @@
 
 void dom_init()
 {
-	local entity e;
 	// we have to precache default models/sounds even if they might not be
 	// used because spawnfunc_worldspawn is executed before any other entities are read,
 	// so we don't even know yet if this map is set up for domination...
@@ -602,9 +599,7 @@
 	precache_model("models/domination/dom_pink.md3");
 	precache_model("models/domination/dom_unclaimed.md3");
 	precache_sound("domination/claim.wav");
-	e = spawn();
-	e.think = dom_delayedinit;
-	e.nextthink = time + 0.1;
+	InitializeEntity(world, dom_delayedinit, INITPRIO_GAMETYPE);
 
 	// teamplay is always on in domination, defaults to hurt self but not teammates
 	//if(!cvar("teamplay"))

Modified: branches/nexuiz-2.0/data/qcsrc/server/g_lights.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/g_lights.qc	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/qcsrc/server/g_lights.qc	2008-08-28 13:26:19 UTC (rev 4222)
@@ -33,11 +33,13 @@
 	if(!self.owner)
 		remove(self);
 
-	self.nextthink = 0.1;
+	self.nextthink = time + 0.1;
 };
 void dynlight_find_aiment()
 {
 	local entity targ;
+	if (!self.target)
+		objerror ("dynlight: no target to follow");
 
 	targ = find(world, targetname, self.target);
 	self.movetype = MOVETYPE_FOLLOW;
@@ -46,19 +48,33 @@
 	self.punchangle = targ.angles;
 	self.view_ofs = self.origin - targ.origin;
 	self.v_angle = self.angles - targ.angles;
-	self.nextthink = 0.1;
 	self.think = dynlight_think;
+	self.nextthink = time + 0.1;
 };
 void dynlight_find_path()
 {
 	local entity targ;
+	if (!self.target)
+		objerror ("dynlight: no target to follow");
 
 	targ = find(world, targetname, self.target);
 	self.target = targ.target;
 	setorigin (self, targ.origin);
-	self.nextthink = self.ltime + 0.1;
 	self.think = train_next;
+	self.nextthink = time + 0.1;
 };
+void dynlight_find_target()
+{
+	local entity targ;
+	if (!self.target)
+		objerror ("dynlight: no target to follow");
+
+	targ = find(world, targetname, self.target);
+	setattachment(self, targ, self.dtagname);
+	self.owner = targ;
+	self.think = dynlight_think;
+	self.nextthink = time + 0.1;
+}
 void dynlight_use()
 {
 	if (self.light_lev == 0)
@@ -89,22 +105,14 @@
 //tag attaching
 	if (self.dtagname)
 	{
-		if (!self.target)
-			objerror ("dynlight: no target to follow");
-		targ = find(world, targetname, self.target);
-		setattachment(self, targ, self.dtagname);
-		self.owner = targ;
-		self.think = dynlight_think;
+		InitializeEntity(self, dynlight_find_target, INITPRIO_FINDTARGET);
 		return;
 	}
 
 // entity following
 	if (self.spawnflags & DFOLLOW)
 	{
-		if (!self.target)
-			objerror ("dynlight: no target to follow");
-		self.nextthink = time + 0.1;
-		self.think = dynlight_find_aiment;
+		InitializeEntity(self, dynlight_find_aiment, INITPRIO_FINDTARGET);
 		return;
 	}
 // path following
@@ -114,7 +122,7 @@
 		self.movetype = MOVETYPE_PUSH;
 		if (!self.speed)
 			self.speed = 100;
-		self.nextthink = self.ltime + 0.1;
-		self.think = dynlight_find_path;
+		InitializeEntity(self, dynlight_find_path, INITPRIO_FINDTARGET);
+		return;
 	}
 };

Modified: branches/nexuiz-2.0/data/qcsrc/server/g_triggers.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/g_triggers.qc	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/qcsrc/server/g_triggers.qc	2008-08-28 13:26:19 UTC (rev 4222)
@@ -598,7 +598,7 @@
 	self.mins = '0 0 0';
 	self.maxs = '0 0 0';
 	self.velocity = '0 0 -1';
-	self.mdl = "func_sparks";
+	self.mdl = "TE_SPARK";
 	self.impulse = 0.1 / self.wait;
 	self.wait = 0;
 
@@ -710,37 +710,51 @@
 	vector a;
 	if(self.enemy)
 	{
-		a = vectoangles(self.enemy.origin - self.origin);
-		a_x = -a_x;
-		if(a != self.angles)
+		if(self.spawnflags & 2)
 		{
-			self.angles = a;
-			self.SendFlags |= 2;
+			if(self.enemy.origin != self.mangle)
+			{
+				self.mangle = self.enemy.origin;
+				self.SendFlags |= 2;
+			}
 		}
+		else
+		{
+			a = vectoangles(self.enemy.origin - self.origin);
+			a_x = -a_x;
+			if(a != self.mangle)
+			{
+				self.mangle = a;
+				self.SendFlags |= 2;
+			}
+		}
 	}
 	if(self.origin != self.oldorigin)
+	{
 		self.SendFlags |= 1;
+		self.oldorigin = self.origin;
+	}
 }
 
+void misc_laser_init()
+{
+	self.enemy = find(world, targetname, self.target);
+}
+
 void misc_laser_think()
 {
 	vector o;
 
 	self.nextthink = time;
 
-	if(!self.count)
-	{
-		self.enemy = find(world, targetname, self.target);
-		self.count = 1;
-	}
-
 	if(!self.state)
 		return;
 
 	misc_laser_aim();
 
-	makevectors(self.angles);
-	o = self.origin + 32768 * v_forward;
+	o = self.enemy.origin;
+	if not(self.spawnflags & 2)
+		o = self.origin + normalize(o - self.origin) * 32768;
 
 	if(self.dmg)
 	{
@@ -754,6 +768,11 @@
 float laser_SendEntity(entity to, float fl)
 {
 	WriteByte(MSG_ENTITY, ENT_CLIENT_LASER);
+	fl = fl - (fl & 0xC0); // use that bit to indicate finite length laser
+	if(self.spawnflags & 2)
+		fl |= 0x80;
+	if(self.alpha)
+		fl |= 0x40;
 	WriteByte(MSG_ENTITY, fl);
 	if(fl & 1)
 	{
@@ -766,19 +785,30 @@
 		WriteByte(MSG_ENTITY, self.colormod_x * 255.0);
 		WriteByte(MSG_ENTITY, self.colormod_y * 255.0);
 		WriteByte(MSG_ENTITY, self.colormod_z * 255.0);
+		if(fl & 0x40)
+			WriteByte(MSG_ENTITY, self.alpha * 255.0);
 		WriteShort(MSG_ENTITY, self.cnt);
 	}
 	if(fl & 2)
 	{
-		WriteCoord(MSG_ENTITY, self.angles_x);
-		WriteCoord(MSG_ENTITY, self.angles_y);
+		if(fl & 0x80)
+		{
+			WriteCoord(MSG_ENTITY, self.enemy.origin_x);
+			WriteCoord(MSG_ENTITY, self.enemy.origin_y);
+			WriteCoord(MSG_ENTITY, self.enemy.origin_z);
+		}
+		else
+		{
+			WriteCoord(MSG_ENTITY, self.mangle_x);
+			WriteCoord(MSG_ENTITY, self.mangle_y);
+		}
 	}
 	if(fl & 4)
 		WriteByte(MSG_ENTITY, self.state);
 	return 1;
 }
 
-/*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON
+/*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
 Any object touching the beam will be hurt
 Keys:
 "target"
@@ -810,11 +840,13 @@
 		self.cnt = particleeffectnum("misc_laser_beam_end");
 	}
 	if(self.colormod == '0 0 0')
-		self.colormod = '1 0 0';
+		if(!self.alpha)
+			self.colormod = '1 0 0';
 	if(!self.message)
 		self.message = "saw the light";
 	self.think = misc_laser_think;
 	self.nextthink = time;
+	InitializeEntity(self, misc_laser_init, INITPRIO_FINDTARGET);
 
 	self.effects = EF_NODEPTHTEST;
 	self.SendEntity = laser_SendEntity;
@@ -1066,7 +1098,7 @@
 	self.state = 0;
 }
 
-void follow_think()
+void follow_init()
 {
 	entity src, dst;
 	src = find(world, targetname, self.killtarget);
@@ -1089,6 +1121,5 @@
 
 void spawnfunc_misc_follow()
 {
-	self.think = follow_think;
-	self.nextthink = time;
+	InitializeEntity(self, follow_init, INITPRIO_FINDTARGET);
 }

Modified: branches/nexuiz-2.0/data/qcsrc/server/g_world.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/g_world.qc	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/qcsrc/server/g_world.qc	2008-08-28 13:26:19 UTC (rev 4222)
@@ -365,6 +365,8 @@
 		}
 	}
 
+	addstat(STAT_SYS_TICRATE, AS_FLOAT, stat_sys_ticrate);
+
 	next_pingtime = time + 5;
 	world_initialized = 1;
 }

Modified: branches/nexuiz-2.0/data/qcsrc/server/miscfunctions.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/miscfunctions.qc	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/qcsrc/server/miscfunctions.qc	2008-08-28 13:26:19 UTC (rev 4222)
@@ -1190,3 +1190,73 @@
 // WARNING: this kills the trace globals
 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
 #define EXACTTRIGGER_INIT  InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
+
+#define INITPRIO_FIRST              0
+#define INITPRIO_GAMETYPE           0
+#define INITPRIO_GAMETYPE_FALLBACK  1
+#define INITPRIO_FINDTARGET        10
+#define INITPRIO_SETLOCATION       90
+#define INITPRIO_LAST              99
+
+.void(void) initialize_entity;
+.float initialize_entity_order;
+.entity initialize_entity_next;
+entity initialize_entity_first;
+void InitializeEntity(entity e, void(void) func, float order)
+{
+	entity prev, cur;
+
+	if(!e || e.initialize_entity)
+	{
+		// make a proxy initializer entity
+		entity e_old;
+		e_old = e;
+		e = spawn();
+		e.classname = "initialize_entity";
+		e.enemy = e_old;
+	}
+
+	e.initialize_entity = func;
+	e.initialize_entity_order = order;
+
+	cur = initialize_entity_first;
+	for(;;)
+	{
+		if(!cur || cur.initialize_entity_order > order)
+		{
+			// insert between prev and cur
+			if(prev)
+				prev.initialize_entity_next = e;
+			else
+				initialize_entity_first = e;
+			e.initialize_entity_next = cur;
+			return;
+		}
+		prev = cur;
+		cur = cur.initialize_entity_next;
+	}
+}
+void InitializeEntitiesRun()
+{
+	for(self = initialize_entity_first; self; )
+	{
+		entity e;
+		var void(void) func;
+		e = self.initialize_entity_next;
+		func = self.initialize_entity;
+		self.initialize_entity_order = 0;
+		self.initialize_entity = func_null;
+		self.initialize_entity_next = world;
+		if(self.classname == "initialize_entity")
+		{
+			entity e_old;
+			e_old = self.enemy;
+			remove(self);
+			self = e_old;
+		}
+		dprint("Delayed initialization: ", self.classname, "\n");
+		func();
+		self = e;
+	}
+	initialize_entity_first = world;
+}

Modified: branches/nexuiz-2.0/data/qcsrc/server/sv_main.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/sv_main.qc	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/qcsrc/server/sv_main.qc	2008-08-28 13:26:19 UTC (rev 4222)
@@ -136,6 +136,8 @@
 	if(RedirectionThink())
 		return;
 
+	InitializeEntitiesRun();
+
 	sv_maxairspeed = cvar("sv_maxairspeed");
 	sv_maxspeed = cvar ("sv_maxspeed");
 	sv_friction = cvar ("sv_friction");

Modified: branches/nexuiz-2.0/data/qcsrc/server/t_items.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/t_items.qc	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/qcsrc/server/t_items.qc	2008-08-28 13:26:19 UTC (rev 4222)
@@ -683,7 +683,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", 30, "25 Armor", IT_ARMOR, 0, commodity_pickupevalfunc, 20000);
+	StartItem ("models/items/g_armormedium.md3", "misc/armor1.wav", 20, "25 Armor", IT_ARMOR, 0, commodity_pickupevalfunc, 20000);
 }
 
 void spawnfunc_item_armor_large (void) {
@@ -715,7 +715,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", 15, "50 Health", IT_25HP, 0, commodity_pickupevalfunc, 20000);
+	StartItem ("models/items/g_h50.md3", "misc/mediumhealth.wav", 20, "50 Health", IT_25HP, 0, commodity_pickupevalfunc, 20000);
 }
 
 void spawnfunc_item_health_mega (void) {

Modified: branches/nexuiz-2.0/data/qcsrc/server/t_jumppads.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/t_jumppads.qc	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/qcsrc/server/t_jumppads.qc	2008-08-28 13:26:19 UTC (rev 4222)
@@ -255,8 +255,7 @@
 	precache_sound (self.noise);
 
 	// this must be called to spawn the teleport waypoints for bots
-	self.think = trigger_push_findtarget;
-	self.nextthink = time + 0.2;
+	InitializeEntity(self, trigger_push_findtarget, INITPRIO_FINDTARGET);
 };
 
 void spawnfunc_target_push() {};

Modified: branches/nexuiz-2.0/data/qcsrc/server/t_plats.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/t_plats.qc	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/qcsrc/server/t_plats.qc	2008-08-28 13:26:19 UTC (rev 4222)
@@ -344,6 +344,8 @@
 	local entity targ;
 	targ = find(world, targetname, self.target);
 	self.target = targ.target;
+	if (!self.target)
+		objerror("func_train_find: no next target");
 	setorigin(self, targ.origin - self.mins);
 	self.nextthink = self.ltime + 1;
 	self.think = train_next;
@@ -365,8 +367,7 @@
 	self.effects |= EF_LOWPRECISION;
 
 	// wait for targets to spawn
-	self.nextthink = self.ltime + 0.1;
-	self.think = func_train_find;
+	InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
 };
 
 
@@ -1126,8 +1127,7 @@
 
 // LinkDoors can't be done until all of the doors have been spawned, so
 // the sizes can be detected properly.
-	self.think = LinkDoors;
-	self.nextthink = self.ltime + 0.1;
+	InitializeEntity(self, LinkDoors, INITPRIO_FINDTARGET);
 };
 
 /*

Modified: branches/nexuiz-2.0/data/qcsrc/server/t_teleporters.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/t_teleporters.qc	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/qcsrc/server/t_teleporters.qc	2008-08-28 13:26:19 UTC (rev 4222)
@@ -129,8 +129,8 @@
 
 	EXACTTRIGGER_INIT;
 
-	self.think = teleport_findtarget;
-	self.nextthink = time + 0.2;
+	// this must be called to spawn the teleport waypoints for bots
+	InitializeEntity(self, teleport_findtarget, INITPRIO_FINDTARGET);
 
 	if (!self.target)
 		objerror ("Teleporter with no target");

Modified: branches/nexuiz-2.0/data/qcsrc/server/teamplay.qc
===================================================================
--- branches/nexuiz-2.0/data/qcsrc/server/teamplay.qc	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/qcsrc/server/teamplay.qc	2008-08-28 13:26:19 UTC (rev 4222)
@@ -142,8 +142,6 @@
 
 void default_delayedinit()
 {
-	remove(self);
-
 	if(!scores_initialized)
 		ScoreRules_generic();
 }
@@ -333,10 +331,7 @@
 		cvar_set("fraglimit", "0");
 	}
 
-	entity e;
-	e = spawn();
-	e.nextthink = time + 0.3; // MUST be after all other delayed inits!
-	e.think = default_delayedinit;
+	InitializeEntity(world, default_delayedinit, INITPRIO_GAMETYPE_FALLBACK);
 }
 
 string GetClientVersionMessage() {
@@ -1323,8 +1318,6 @@
 
 void tdm_delayedinit()
 {
-	self.think = SUB_Remove;
-	self.nextthink = time;
 	// if no teams are found, spawn defaults
 	if (find(world, classname, "tdm_team") == world)
 		tdm_spawnteams();
@@ -1332,10 +1325,5 @@
 
 void tdm_init()
 {
-	local entity e;
-	e = spawn();
-	e.think = tdm_delayedinit;
-	e.nextthink = time + 0.1;
+	InitializeEntity(world, tdm_delayedinit, INITPRIO_GAMETYPE);
 };
-
-

Modified: branches/nexuiz-2.0/data/scripts/entities.def
===================================================================
--- branches/nexuiz-2.0/data/scripts/entities.def	2008-08-28 13:23:46 UTC (rev 4221)
+++ branches/nexuiz-2.0/data/scripts/entities.def	2008-08-28 13:26:19 UTC (rev 4222)
@@ -283,7 +283,7 @@
 /*QUAKED item_armor_medium (.4 .8 .4) (-30 -30 0) (30 30 32) FLOATING
 Medium Armor (default 25 armor points)
 -------- KEYS --------
-respawntime: time till it respawns (default: 30)
+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))
 -------- SPAWNFLAGS --------
@@ -351,7 +351,7 @@
 /*QUAKED item_health_large (.9 .3 .3) (-30 -30 0) (30 30 48) FLOATING
 Large Health (default 50 health points)
 -------- KEYS --------
-respawntime: time till it respawns (default: 15)
+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))
 -------- SPAWNFLAGS --------
@@ -496,7 +496,7 @@
 NOANGLE: Ignore angle attenuation.
 */
 
-/*QUAKED misc_laser (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON
+/*QUAKED misc_laser (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON FINITE
 Laser beam emitter
 -------- KEYS --------
 target: target_position the laser targets (may be another entity, the laser will then target its origin. Use origin brushes, that is!)
@@ -504,8 +504,10 @@
 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)
+alpha: when set, makes a dark laser of the given strength; may be combined with colormod
 -------- SPAWNFLAGS --------
 START_ON: when targeted, the laser will start switched on
+FINITE: the laser does not extend over its target like light would do, but stops there (takes more bandwidth)
 -------- NOTES --------
 Use trigger_monoflop if you want the laser to turn off for a while, then turn back on
 */




More information about the nexuiz-commits mailing list