a trigger_warpzone entity (a silent everything-teleporting teleporter). IT HAS NO...
authordiv0 <div0@f962a42d-fe04-0410-a3ab-8c8b0445ebaa>
Mon, 8 Feb 2010 10:42:18 +0000 (10:42 +0000)
committerdiv0 <div0@f962a42d-fe04-0410-a3ab-8c8b0445ebaa>
Mon, 8 Feb 2010 10:42:18 +0000 (10:42 +0000)
Neither does it have hitscan or radiusdamage support yet, but it should get that, even though UT's warpzones didn't.
Projectiles should already be able to get past it.
More to come once rendering supports them in some way...

git-svn-id: svn://svn.icculus.org/nexuiz/trunk@8626 f962a42d-fe04-0410-a3ab-8c8b0445ebaa

data/qcsrc/server/portals.qc
data/qcsrc/server/portals.qh
data/qcsrc/server/t_teleporters.qc

index 294f24c..96b6cb4 100644 (file)
@@ -19,14 +19,69 @@ float PlayerEdgeDistance(entity p, vector v)
        return vbest * v;
 }
 
+vector Portal_ApplyTransformToPlayerAngle(vector transform, vector vangle)
+{
+       vector old_forward, old_up;
+       vector old_yawforward;
+       vector new_forward, new_up;
+       vector new_yawforward;
+
+       vector ang;
+       ang = vangle;
+       /*
+          ang_x = bound(-89, mod(-ang_x + 180, 360) - 180, 89);
+          ang = AnglesTransform_Multiply(transform, ang);
+        */
+
+       // PLAYERS use different math
+       ang_x = -ang_x;
+
+       //print("reference: ", vtos(AnglesTransform_Multiply(transform, ang)), "\n");
+
+       fixedmakevectors(ang);
+       old_forward = v_forward;
+       old_up = v_up;
+       fixedmakevectors(ang_y * '0 1 0');
+       old_yawforward = v_forward;
+
+       // their aiming directions are portalled...
+       new_forward = AnglesTransform_Apply(transform, old_forward);
+       new_up = AnglesTransform_Apply(transform, old_up);
+       new_yawforward = AnglesTransform_Apply(transform, old_yawforward);
+
+       // but now find a new sense of direction
+       // this is NOT easy!
+       // assume new_forward points straight up.
+       // What is our yaw?
+       //
+       // new_up could now point forward OR backward... which direction to choose?
+
+       if(new_forward_z > 0.7 || new_forward_z < -0.7) // far up; in this case, the "up" vector points backwards
+       {
+               // new_yawforward and new_yawup define the new aiming half-circle
+               // we "just" need to find out whether new_up or -new_up is in that half circle
+               ang = fixedvectoangles(new_forward); // this still gets us a nice pitch value...
+               if(new_up * new_yawforward < 0)
+                       new_up = -1 * new_up;
+               ang_y = vectoyaw(new_up); // this vector is the yaw we want
+               //print("UP/DOWN path: ", vtos(ang), "\n");
+       }
+       else
+       {
+               // good angles; here, "forward" suffices
+               ang = fixedvectoangles(new_forward);
+               //print("GOOD path: ", vtos(ang), "\n");
+       }
+
+       ang_x = -ang_x;
+       ang_z = vangle_z;
+       return ang;
+}
+
 .vector right_vector;
 float Portal_TeleportPlayer(entity teleporter, entity player)
 {
        vector from, to, safe, step, transform, ang, newvel;
-       vector new_forward, new_up;
-       vector old_forward, old_up;
-       vector new_yawforward;
-       vector old_yawforward;
        float planeshift, s, t;
 
        if not(teleporter.enemy)
@@ -87,61 +142,9 @@ float Portal_TeleportPlayer(entity teleporter, entity player)
 
        // ang_x stuff works around weird quake angles
        if(player.classname == "player")
-       {
-               ang = player.v_angle;
-               /*
-               ang_x = bound(-89, mod(-ang_x + 180, 360) - 180, 89);
-               ang = AnglesTransform_Multiply(transform, ang);
-               */
-
-               // PLAYERS use different math
-               ang_x = -ang_x;
-
-               //print("reference: ", vtos(AnglesTransform_Multiply(transform, ang)), "\n");
-
-               fixedmakevectors(ang);
-               old_forward = v_forward;
-               old_up = v_up;
-               fixedmakevectors(ang_y * '0 1 0');
-               old_yawforward = v_forward;
-
-               // their aiming directions are portalled...
-               new_forward = AnglesTransform_Apply(transform, old_forward);
-               new_up = AnglesTransform_Apply(transform, old_up);
-               new_yawforward = AnglesTransform_Apply(transform, old_yawforward);
-
-               // but now find a new sense of direction
-               // this is NOT easy!
-               // assume new_forward points straight up.
-               // What is our yaw?
-               //
-               // new_up could now point forward OR backward... which direction to choose?
-
-               if(new_forward_z > 0.7 || new_forward_z < -0.7) // far up; in this case, the "up" vector points backwards
-               {
-                       // new_yawforward and new_yawup define the new aiming half-circle
-                       // we "just" need to find out whether new_up or -new_up is in that half circle
-                       ang = fixedvectoangles(new_forward); // this still gets us a nice pitch value...
-                       if(new_up * new_yawforward < 0)
-                               new_up = -1 * new_up;
-                       ang_y = vectoyaw(new_up); // this vector is the yaw we want
-                       //print("UP/DOWN path: ", vtos(ang), "\n");
-               }
-               else
-               {
-                       // good angles; here, "forward" suffices
-                       ang = fixedvectoangles(new_forward);
-                       //print("GOOD path: ", vtos(ang), "\n");
-               }
-
-               ang_x = -ang_x;
-               ang_z = player.angles_z;
-       }
+               ang = Portal_ApplyTransformToPlayerAngle(transform, player.v_angle);
        else
-       {
-               ang = player.angles;
                ang = AnglesTransform_Multiply(transform, player.angles);
-       }
 
        // factor -1 allows chaining portals, but may be weird
        player.right_vector = -1 * AnglesTransform_Apply(transform, player.right_vector);
@@ -156,7 +159,7 @@ float Portal_TeleportPlayer(entity teleporter, entity player)
        }
 
        tdeath_hit = 0;
-       TeleportPlayer(teleporter, player, to, ang, newvel, teleporter.enemy.absmin, teleporter.enemy.absmax);
+       TeleportPlayer(teleporter, player, to, ang, newvel, teleporter.enemy.absmin, teleporter.enemy.absmax, TELEPORT_FLAGS_PORTAL);
        if(tdeath_hit)
        {
                // telefrag within 1 second of portal creation = amazing
index 9a5a544..b2d282c 100644 (file)
@@ -6,3 +6,5 @@ void Portal_ClearAllLater(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);
+
+vector Portal_ApplyTransformToPlayerAngle(vector transform, vector vangle);
index da8243a..035384c 100644 (file)
@@ -65,7 +65,14 @@ void spawn_tdeath(vector v0, entity e, vector v)
 }
 
 .entity pusher;
-void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity, vector telefragmin, vector telefragmax)
+#define TELEPORT_FLAG_SOUND 1
+#define TELEPORT_FLAG_PARTICLES 2
+#define TELEPORT_FLAG_TDEATH 4
+
+#define TELEPORT_FLAGS_WARPZONE   0
+#define TELEPORT_FLAGS_PORTAL     (TELEPORT_FLAG_SOUND | TELEPORT_FLAG_PARTICLES)
+#define TELEPORT_FLAGS_TELEPORTER (TELEPORT_FLAG_SOUND | TELEPORT_FLAG_PARTICLES | TELEPORT_FLAG_TDEATH)
+void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity, vector telefragmin, vector telefragmax, float tflags)
 {
        entity oldself;
        entity telefragger;
@@ -80,9 +87,13 @@ void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angle
 
        if(self.pushltime < time) // only show one teleport effect per teleporter per 0.2 seconds, for better fps
        {
-               sound (player, CHAN_TRIGGER, "misc/teleport.wav", VOL_BASE, ATTN_NORM);
-               pointparticles(particleeffectnum("teleport"), player.origin, '0 0 0', 1);
-               pointparticles(particleeffectnum("teleport"), to + v_forward * 32, '0 0 0', 1);
+               if(tflags & TELEPORT_FLAG_SOUND)
+                       sound (player, CHAN_TRIGGER, "misc/teleport.wav", VOL_BASE, ATTN_NORM);
+               if(tflags & TELEPORT_FLAG_PARTICLES)
+               {
+                       pointparticles(particleeffectnum("teleport"), player.origin, '0 0 0', 1);
+                       pointparticles(particleeffectnum("teleport"), to + v_forward * 32, '0 0 0', 1);
+               }
                self.pushltime = time + 0.2;
        }
 
@@ -100,8 +111,9 @@ void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angle
 
        if(player.classname == "player")
        {
-               if(player.takedamage && player.deadflag == DEAD_NO && !g_race && !g_cts && cvar("g_telefrags"))
-                       tdeath(player, teleporter, telefragger, telefragmin, telefragmax);
+               if(tflags & TELEPORT_FLAG_TDEATH)
+                       if(player.takedamage && player.deadflag == DEAD_NO && !g_race && !g_cts && cvar("g_telefrags"))
+                               tdeath(player, teleporter, telefragger, telefragmin, telefragmax);
 
                // player no longer is on ground
                player.flags &~= FL_ONGROUND;
@@ -192,7 +204,7 @@ void Teleport_Touch (void)
                        other.velocity = normalize(other.velocity) * max(0, cvar("g_teleport_maxspeed"));
 
        o = e.origin + '0 0 1' * (1 - other.mins_z - 24);
-       TeleportPlayer(self, other, o, e.mangle, v_forward * vlen(other.velocity), '0 0 0', '0 0 0');
+       TeleportPlayer(self, other, o, e.mangle, v_forward * vlen(other.velocity), '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER);
 
        if(e.target)
        {
@@ -285,3 +297,147 @@ void spawnfunc_trigger_teleport (void)
                return;
        }
 }
+
+
+// the transform
+.vector warpzone_origin, warpzone_angles;
+.vector warpzone_forward;
+.vector warpzone_transform;
+
+void warpzone_updatetransform()
+{
+       // 1. update this, and the enemy, warp zone
+       self.warpzone_origin = self.aiment.origin;
+       self.warpzone_angles = self.aiment.angles;
+       self.enemy.warpzone_origin = self.enemy.aiment.origin;
+       self.enemy.warpzone_angles = self.enemy.aiment.angles;
+
+       // 2. combine the angle transforms
+       //    current forward must be turned into previous backward
+       self.warpzone_transform = AnglesTransform_Divide(AnglesTransform_TurnDirection(self.enemy.warpzone_angles), self.warpzone_angles);
+
+       // 3. store off a saved forward vector for plane hit decisions
+       fixedmakevectors(self.warpzone_angles);
+       self.warpzone_forward = v_forward;
+}
+
+float warpzone_teleport(entity player)
+{
+       vector o0, a0, v0, o1, a1, v1;
+
+       o0 = player.origin;
+       v0 = player.velocity;
+       a0 = player.angles;
+
+       if((o0 - self.warpzone_origin) * self.warpzone_forward >= 0) // wrong side of the portal
+               return 2;
+               // no failure, we simply don't want to teleport yet; TODO in
+               // this situation we may want to create a temporary clone
+               // entity of the player to fix graphics glitch
+
+       o1 = AnglesTransform_Apply(self.warpzone_transform, o0 - self.warpzone_origin) + self.enemy.warpzone_origin;
+       v1 = AnglesTransform_Apply(self.warpzone_transform, v0);
+       if(player.classname == "player")
+               a1 = Portal_ApplyTransformToPlayerAngle(self.warpzone_transform, player.v_angle);
+       else
+               a1 = AnglesTransform_Multiply(self.warpzone_transform, a0);
+
+       // put him inside solid
+       tracebox(o1, player.mins, player.maxs, o1, MOVE_NOMONSTERS, player);
+       if(trace_startsolid)
+       {
+               print("in solid\n");
+               return 0; // TODO nudge out of solid here!
+       }
+
+       if((o1 - self.enemy.warpzone_origin) * self.enemy.warpzone_forward <= 0) // wrong side of the portal post-teleport
+       {
+               print("inconsistent warp zones or evil roundoff error\n");
+               return 0;
+       }
+
+       //print(sprintf("warpzone: %f %f %f -> %f %f %f\n", o0_x, o0_y, o0_z, o1_x, o1_y, o1_z));
+
+       //o1 = trace_endpos;
+       TeleportPlayer(self, other, o1, a1, v1, '0 0 0', '0 0 0', TELEPORT_FLAGS_WARPZONE);
+
+       return 1;
+}
+
+void warpzone_touch (void)
+{
+       entity oldself, e;
+
+       if (other.health < 1)
+               return;
+
+       if(self.team)
+               if((self.spawnflags & 4 == 0) == (self.team != other.team))
+                       return;
+
+       EXACTTRIGGER_TOUCH;
+
+       e = self.enemy;
+       if(warpzone_teleport(other))
+       {
+       }
+       else
+       {
+               dprint("WARPZONE FAIL AHAHAHAHAH))\n");
+       }
+
+       if(self.aiment.target)
+       {
+               oldself = self;
+               activator = other;
+               self = self.aiment;
+               SUB_UseTargets();
+               self = oldself;
+       }
+}
+void warpzone_findtarget (void)
+{
+       if(self.target == "")
+       {
+               objerror("Warp zone with no target");
+               return;
+       }
+       self.enemy = find(world, targetname, self.target);
+       if(self.enemy == world)
+       {
+               objerror("Warp zone with nonexisting target");
+               return;
+       }
+
+       if(self.killtarget == "")
+       {
+               objerror("Warp zone with no killtarget");
+               return;
+       }
+       self.aiment = find(world, targetname, self.killtarget);
+       if(self.aiment == world)
+       {
+               objerror("Warp zone with nonexisting killtarget");
+               return;
+       }
+
+       // now enable touch
+       self.touch = warpzone_touch;
+}
+
+void spawnfunc_trigger_warpzone(void)
+{
+       // warp zone entities must have:
+       // "killtarget" pointing to a target_position with a direction arrow
+       //              that points AWAY from the warp zone, and that is inside
+       //              the warp zone trigger
+       // "target"     pointing to an identical warp zone at another place in
+       //              the map, with another killtarget to designate its
+       //              orientation
+       
+       EXACTTRIGGER_INIT;
+
+       self.use = trigger_teleport_use; // set team
+       InitializeEntity(self, warpzone_findtarget, INITPRIO_FINDTARGET);
+       InitializeEntity(self, warpzone_updatetransform, INITPRIO_LAST);
+}