]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge remote-tracking branch 'origin/mirceakitsune/func_train_beizer_curve'
authorRudolf Polzer <divverent@xonotic.org>
Fri, 11 Oct 2013 14:51:19 +0000 (16:51 +0200)
committerRudolf Polzer <divverent@xonotic.org>
Fri, 11 Oct 2013 14:51:19 +0000 (16:51 +0200)
Conflicts:
qcsrc/server/t_plats.qc

qcsrc/common/util.qc
qcsrc/server/defs.qh
qcsrc/server/g_subs.qc
qcsrc/server/t_plats.qc

index 965431773e3d572162192da7b5ce2a2076c133c8..85efe4144a17355d23cac22ab1ec02dfa37f4070 100644 (file)
@@ -2492,6 +2492,28 @@ float cubic_speedfunc_is_sane(float startspeedfactor, float endspeedfactor)
        // (3, [0..3])
        // (3.5, [0.2..2.3])
        // (4, 1)
+
+       /*
+          On another note:
+          inflection point is always at (2s + e - 3) / (3s + 3e - 6).
+
+          s + e - 2 == 0: no inflection
+
+          s + e > 2:
+          0 < inflection < 1 if:
+          0 < 2s + e - 3 < 3s + 3e - 6
+          2s + e > 3 and 2e + s > 3
+
+          s + e < 2:
+          0 < inflection < 1 if:
+          0 > 2s + e - 3 > 3s + 3e - 6
+          2s + e < 3 and 2e + s < 3
+
+          Therefore: there is an inflection point iff:
+          e outside (3 - s)/2 .. 3 - s*2
+          
+          in other words, if (s,e) in triangle (1,1)(0,3)(0,1.5) or in triangle (1,1)(3,0)(1.5,0)
+       */
 }
 
 .float FindConnectedComponent_processing;
index c7aebd58f500fe35862ba387133f4a5fd2da5fe4..fe41a23fabc2f7585c3cef8a0d8bd9448b303226 100644 (file)
@@ -73,7 +73,8 @@ float server_is_dedicated;
 //.float       style;
 //.float       skill;
 .float sounds;
-.float  platmovetype;
+.string  platmovetype;
+.float platmovetype_start, platmovetype_end;
 
 .string killtarget;
 
@@ -470,6 +471,7 @@ void target_voicescript_clear(entity pl);
 .string target2;
 .string target3;
 .string target4;
+.string curvetarget;
 .float target_random;
 .float trigger_reverse;
 
index 6444ffdb3cec98fcf7f97114736f340c493964ef..96ccc267c4e417613b8ed5f4a725750a077164b2 100644 (file)
@@ -1,6 +1,5 @@
 void SUB_NullThink(void) { }
 
-void(vector destangle, float tspeed, void() func) SUB_CalcAngleMove;
 void()  SUB_CalcMoveDone;
 void() SUB_CalcAngleMoveDone;
 //void() SUB_UseTargets;
@@ -153,6 +152,7 @@ void SUB_CalcMoveDone (void)
                self.think1 ();
 }
 
+.float platmovetype_turn;
 void SUB_CalcMove_controller_think (void)
 {
        entity oldself;
@@ -162,6 +162,7 @@ void SUB_CalcMove_controller_think (void)
        vector delta;
        vector delta2;
        vector veloc;
+       vector angloc;
        vector nextpos;
        delta = self.destvec;
        delta2 = self.destvec2;
@@ -170,24 +171,32 @@ void SUB_CalcMove_controller_think (void)
 
                traveltime = self.animstate_endtime - self.animstate_starttime;
                phasepos = (nexttick - self.animstate_starttime) / traveltime; // range: [0, 1]
-               if(self.platmovetype != 1)
-               {
-                       phasepos = 3.14159265 + (phasepos * 3.14159265); // range: [pi, 2pi]
-                       phasepos = cos(phasepos); // cos [pi, 2pi] is in [-1, 1]
-                       phasepos = phasepos + 1; // correct range to [0, 2]
-                       phasepos = phasepos / 2; // correct range to [0, 1]
-               }
+               phasepos = cubic_speedfunc(self.platmovetype_start, self.platmovetype_end, phasepos);
                nextpos = self.origin + (delta * phasepos) + (delta2 * phasepos * phasepos);
                // derivative: delta + 2 * delta2 * phasepos (e.g. for angle positioning)
 
-               if(nexttick < self.animstate_endtime) {
+               if(self.owner.platmovetype_turn)
+               {
+                       vector destangle;
+                       destangle = delta + 2 * delta2 * phasepos;
+                       destangle = vectoangles(destangle);
+                       destangle_x = -destangle_x; // flip up / down orientation
+
+                       // take the shortest distance for the angles
+                       self.owner.angles_x -= 360 * floor((self.owner.angles_x - destangle_x) / 360 + 0.5);
+                       self.owner.angles_y -= 360 * floor((self.owner.angles_y - destangle_y) / 360 + 0.5);
+                       self.owner.angles_z -= 360 * floor((self.owner.angles_z - destangle_z) / 360 + 0.5);
+                       angloc = destangle - self.owner.angles;
+                       angloc = angloc * (1 / sys_frametime); // so it arrives for the next frame
+               }
+               if(nexttick < self.animstate_endtime)
                        veloc = nextpos - self.owner.origin;
-                       veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame
-               } else {
+               else
                        veloc = self.finaldest - self.owner.origin;
-                       veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame
-               }
+               veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame
+
                self.owner.velocity = veloc;
+               self.owner.avelocity = angloc;
                self.nextthink = nexttick;
        } else {
                // derivative: delta + 2 * delta2 (e.g. for angle positioning)
@@ -211,6 +220,7 @@ void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector co
 
        controller.destvec = 2 * control; // control point
        controller.destvec2 = dest - 2 * control; // quadratic part required to reach end point
+       // also: initial d/dphasepos origin = 2 * control, final speed = 2 * (dest - control)
 }
 
 void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector dest)
@@ -226,7 +236,13 @@ void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector de
        controller.destvec2 = '0 0 0';
 }
 
-void SUB_CalcMove_Bezier (vector tcontrol, vector tdest, float tspeed, void() func)
+float TSPEED_TIME = -1;
+float TSPEED_LINEAR = 0;
+float TSPEED_START = 1;
+float TSPEED_END = 2;
+// TODO average too?
+
+void SUB_CalcMove_Bezier (vector tcontrol, vector tdest, float tspeedtype, float tspeed, void() func)
 {
        float   traveltime;
        entity controller;
@@ -238,10 +254,22 @@ void SUB_CalcMove_Bezier (vector tcontrol, vector tdest, float tspeed, void() fu
        self.finaldest = tdest;
        self.think = SUB_CalcMoveDone;
 
-       if(tspeed > 0) // positive: start speed
-               traveltime = 2 * vlen(tcontrol - self.origin) /  tspeed;
-       else // negative: end speed
-               traveltime = 2 * vlen(tcontrol - tdest)       / -tspeed;
+       switch(tspeedtype)
+       {
+               default:
+               case TSPEED_START:
+                       traveltime = 2 * vlen(tcontrol - self.origin) / tspeed;
+                       break;
+               case TSPEED_END:
+                       traveltime = 2 * vlen(tcontrol - tdest)       / tspeed;
+                       break;
+               case TSPEED_LINEAR:
+                       traveltime = vlen(tdest - self.origin)        / tspeed;
+                       break;
+               case TSPEED_TIME:
+                       traveltime = tspeed;
+                       break;
+       }
 
        if (traveltime < 0.1) // useless anim
        {
@@ -254,6 +282,8 @@ void SUB_CalcMove_Bezier (vector tcontrol, vector tdest, float tspeed, void() fu
        controller.classname = "SUB_CalcMove_controller";
        controller.owner = self;
        controller.platmovetype = self.platmovetype;
+       controller.platmovetype_start = self.platmovetype_start;
+       controller.platmovetype_end = self.platmovetype_end;
        SUB_CalcMove_controller_setbezier(controller, self.origin, tcontrol, tdest);
        controller.finaldest = (tdest + '0 0 0.125'); // where do we want to end? Offset to overshoot a bit.
        controller.animstate_starttime = time;
@@ -271,7 +301,7 @@ void SUB_CalcMove_Bezier (vector tcontrol, vector tdest, float tspeed, void() fu
        self = self.owner;
 }
 
-void SUB_CalcMove (vector tdest, float tspeed, void() func)
+void SUB_CalcMove (vector tdest, float tspeedtype, float tspeed, void() func)
 {
        vector  delta;
        float   traveltime;
@@ -291,13 +321,25 @@ void SUB_CalcMove (vector tdest, float tspeed, void() func)
        }
 
        delta = tdest - self.origin;
-       traveltime = vlen (delta) / tspeed;
+
+       switch(tspeedtype)
+       {
+               default:
+               case TSPEED_START:
+               case TSPEED_END:
+               case TSPEED_LINEAR:
+                       traveltime = vlen (delta) / tspeed;
+                       break;
+               case TSPEED_TIME:
+                       traveltime = tspeed;
+                       break;
+       }
 
        // Very short animations don't really show off the effect
        // of controlled animation, so let's just use linear movement.
        // Alternatively entities can choose to specify non-controlled movement.
         // The only currently implemented alternative movement is linear (value 1)
-       if (traveltime < 0.15 || self.platmovetype == 1)
+       if (traveltime < 0.15 || (self.platmovetype_start == 1 && self.platmovetype_end == 1)) // is this correct?
        {
                self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
                self.nextthink = self.ltime + traveltime;
@@ -305,17 +347,17 @@ void SUB_CalcMove (vector tdest, float tspeed, void() func)
        }
 
        // now just run like a bezier curve...
-       SUB_CalcMove_Bezier((self.origin + tdest) * 0.5, tdest, tspeed, func);
+       SUB_CalcMove_Bezier((self.origin + tdest) * 0.5, tdest, tspeedtype, tspeed, func);
 }
 
-void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
+void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeedtype, float tspeed, void() func)
 {
        entity  oldself;
 
        oldself = self;
        self = ent;
 
-       SUB_CalcMove (tdest, tspeed, func);
+       SUB_CalcMove (tdest, tspeedtype, tspeed, func);
 
        self = oldself;
 }
@@ -341,7 +383,7 @@ void SUB_CalcAngleMoveDone (void)
 }
 
 // FIXME: I fixed this function only for rotation around the main axes
-void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
+void SUB_CalcAngleMove (vector destangle, float tspeedtype, float tspeed, void() func)
 {
        vector  delta;
        float   traveltime;
@@ -354,7 +396,19 @@ void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
        self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
        self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
        delta = destangle - self.angles;
-       traveltime = vlen (delta) / tspeed;
+
+       switch(tspeedtype)
+       {
+               default:
+               case TSPEED_START:
+               case TSPEED_END:
+               case TSPEED_LINEAR:
+                       traveltime = vlen (delta) / tspeed;
+                       break;
+               case TSPEED_TIME:
+                       traveltime = tspeed;
+                       break;
+       }
 
        self.think1 = func;
        self.finalangle = destangle;
@@ -371,14 +425,14 @@ void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
        self.nextthink = self.ltime + traveltime;
 }
 
-void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
+void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void() func)
 {
        entity  oldself;
 
        oldself = self;
        self = ent;
 
-       SUB_CalcAngleMove (destangle, tspeed, func);
+       SUB_CalcAngleMove (destangle, tspeedtype, tspeed, func);
 
        self = oldself;
 }
index 24da476d72bad18cb5d19089a883492b98d392c5..9ca98afb897e361bce33687e6b721af5f115c1bb 100644 (file)
@@ -83,14 +83,14 @@ void plat_go_down()
 {
        sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_NORM);
        self.state = 3;
-       SUB_CalcMove (self.pos2, self.speed, plat_hit_bottom);
+       SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, plat_hit_bottom);
 }
 
 void plat_go_up()
 {
        sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_NORM);
        self.state = 4;
-       SUB_CalcMove (self.pos1, self.speed, plat_hit_top);
+       SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, plat_hit_top);
 }
 
 void plat_center_touch()
@@ -177,7 +177,43 @@ void plat_reset()
        }
 }
 
-void spawnfunc_path_corner() { }
+.float platmovetype_start_default, platmovetype_end_default;
+float set_platmovetype(entity e, string s)
+{
+       // sets platmovetype_start and platmovetype_end based on a string consisting of two values
+
+       float n;
+       n = tokenize_console(s);
+       if(n > 0)
+               e.platmovetype_start = stof(argv(0));
+       else
+               e.platmovetype_start = 0;
+
+       if(n > 1)
+               e.platmovetype_end = stof(argv(1));
+       else
+               e.platmovetype_end = e.platmovetype_start;
+
+       if(n > 2)
+               if(argv(2) == "force")
+                       return TRUE; // no checking, return immediately
+
+       if(!cubic_speedfunc_is_sane(e.platmovetype_start, e.platmovetype_end))
+       {
+               objerror("Invalid platform move type; platform would go in reverse, which is not allowed.");
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+void spawnfunc_path_corner()
+{
+       // setup values for overriding train movement
+       // if a second value does not exist, both start and end speeds are the single value specified
+       if(!set_platmovetype(self, self.platmovetype))
+               return;
+}
 void spawnfunc_func_plat()
 {
        if (self.sounds == 0)
@@ -246,15 +282,49 @@ void spawnfunc_func_plat()
        plat_spawn_inside_trigger ();   // the "start moving" trigger
 }
 
-
+.float train_wait_turning;
 void() train_next;
 void train_wait()
 {
+       entity oldself;
+       oldself = self;
+       self = self.enemy;
+       SUB_UseTargets();
+       self = oldself;
+       self.enemy = world;
+
+       // if turning is enabled, the train will turn toward the next point while waiting
+       if(self.platmovetype_turn && !self.train_wait_turning)
+       {
+               entity targ, cp;
+               vector ang;
+               targ = find(world, targetname, self.target);
+               if((self.spawnflags & 1) && targ.curvetarget)
+                       cp = find(world, targetname, targ.curvetarget);
+               else
+                       cp = world;
+
+               if(cp) // bezier curves movement
+                       ang = cp.origin - (self.origin - self.view_ofs); // use the origin of the control point of the next path_corner
+               else // linear movement
+                       ang = targ.origin - (self.origin - self.view_ofs); // use the origin of the next path_corner
+               ang = vectoangles(ang);
+               ang_x = -ang_x; // flip up / down orientation
+
+               if(self.wait > 0) // slow turning
+                       SUB_CalcAngleMove(ang, TSPEED_TIME, self.ltime - time + self.wait, train_wait);
+               else // instant turning
+                       SUB_CalcAngleMove(ang, TSPEED_TIME, 0.0000001, train_wait);
+               self.train_wait_turning = TRUE;
+               return;
+       }
+
        if(self.noise != "")
                stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway
 
-       if(self.wait < 0)
+       if(self.wait < 0 || self.train_wait_turning) // no waiting or we already waited while turning
        {
+               self.train_wait_turning = FALSE;
                train_next();
        }
        else
@@ -262,31 +332,58 @@ void train_wait()
                self.think = train_next;
                self.nextthink = self.ltime + self.wait;
        }
-
-       entity oldself;
-       oldself = self;
-       self = self.enemy;
-       SUB_UseTargets();
-       self = oldself;
-       self.enemy = world;
 }
 
 void train_next()
 {
-       entity targ;
+       entity targ, cp;
+       vector cp_org;
+
        targ = find(world, targetname, self.target);
-       self.enemy = targ;
        self.target = targ.target;
+       if (self.spawnflags & 1)
+       {
+               if(targ.curvetarget)
+               {
+                       cp = find(world, targetname, targ.curvetarget); // get its second target (the control point)
+                       cp_org = cp.origin - self.view_ofs; // no control point found, assume a straight line to the destination
+               }
+               else
+                       cp = world; // no cp
+       }
        if (self.target == "")
                objerror("train_next: no next target");
        self.wait = targ.wait;
        if (!self.wait)
                self.wait = 0.1;
 
+       if(targ.platmovetype)
+       {
+               // this path_corner contains a movetype overrider, apply it
+               self.platmovetype_start = targ.platmovetype_start;
+               self.platmovetype_end = targ.platmovetype_end;
+       }
+       else
+       {
+               // this path_corner doesn't contain a movetype overrider, use the train's defaults
+               self.platmovetype_start = self.platmovetype_start_default;
+               self.platmovetype_end = self.platmovetype_end_default;
+       }
+
        if (targ.speed)
-               SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
+       {
+               if (cp)
+                       SUB_CalcMove_Bezier(cp_org, targ.origin - self.view_ofs, TSPEED_LINEAR, targ.speed, train_wait);
+               else
+                       SUB_CalcMove(targ.origin - self.view_ofs, TSPEED_LINEAR, targ.speed, train_wait);
+       }
        else
-               SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
+       {
+               if (cp)
+                       SUB_CalcMove_Bezier(cp_org, targ.origin - self.view_ofs, TSPEED_LINEAR, self.speed, train_wait);
+               else
+                       SUB_CalcMove(targ.origin - self.view_ofs, TSPEED_LINEAR, self.speed, train_wait);
+       }
 
        if(self.noise != "")
                sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
@@ -299,7 +396,7 @@ void func_train_find()
        self.target = targ.target;
        if (self.target == "")
                objerror("func_train_find: no next target");
-       setorigin(self, targ.origin - self.mins);
+       setorigin(self, targ.origin - self.view_ofs);
        self.nextthink = self.ltime + 1;
        self.think = train_next;
 }
@@ -319,6 +416,14 @@ void spawnfunc_func_train()
        if (!self.speed)
                self.speed = 100;
 
+       if (self.spawnflags & 2)
+       {
+               self.platmovetype_turn = TRUE;
+               self.view_ofs = '0 0 0'; // don't offset a rotating train, origin works differently now
+       }
+       else
+               self.view_ofs = self.mins;
+
        if not(InitMovingBrushTrigger())
                return;
        self.effects |= EF_LOWPRECISION;
@@ -335,6 +440,11 @@ void spawnfunc_func_train()
                self.dmgtime = 0.25;
        self.dmgtime2 = time;
 
+       if(!set_platmovetype(self, self.platmovetype))
+               return;
+       self.platmovetype_start_default = self.platmovetype_start;
+       self.platmovetype_end_default = self.platmovetype_end;
+
        // TODO make a reset function for this one
 }
 
@@ -597,7 +707,7 @@ void button_done()
 void button_return()
 {
        self.state = STATE_DOWN;
-       SUB_CalcMove (self.pos1, self.speed, button_done);
+       SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, button_done);
        self.frame = 0;                 // use normal textures
        if (self.health)
                self.takedamage = DAMAGE_YES;   // can be shot again
@@ -622,7 +732,7 @@ void button_fire()
                sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
 
        self.state = STATE_UP;
-       SUB_CalcMove (self.pos2, self.speed, button_wait);
+       SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, button_wait);
 }
 
 void button_reset()
@@ -846,7 +956,7 @@ void door_go_down()
        }
 
        self.state = STATE_DOWN;
-       SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
+       SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, door_hit_bottom);
 }
 
 void door_go_up()
@@ -863,7 +973,7 @@ void door_go_up()
        if (self.noise2 != "")
                sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
        self.state = STATE_UP;
-       SUB_CalcMove (self.pos2, self.speed, door_hit_top);
+       SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, door_hit_top);
 
        string oldmessage;
        oldmessage = self.message;
@@ -1148,7 +1258,7 @@ void door_rotating_go_down()
        }
 
        self.state = STATE_DOWN;
-       SUB_CalcAngleMove (self.pos1, self.speed, door_rotating_hit_bottom);
+       SUB_CalcAngleMove (self.pos1, TSPEED_LINEAR, self.speed, door_rotating_hit_bottom);
 }
 
 void door_rotating_go_up()
@@ -1164,7 +1274,7 @@ void door_rotating_go_up()
        if (self.noise2 != "")
                sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
        self.state = STATE_UP;
-       SUB_CalcAngleMove (self.pos2, self.speed, door_rotating_hit_top);
+       SUB_CalcAngleMove (self.pos2, TSPEED_LINEAR, self.speed, door_rotating_hit_top);
 
        string oldmessage;
        oldmessage = self.message;
@@ -1639,7 +1749,7 @@ void fd_secret_use()
                self.dest1 = self.origin + v_right * (self.t_width * temp);
 
        self.dest2 = self.dest1 + v_forward * self.t_length;
-       SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
+       SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move1);
        if (self.noise2 != "")
                sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
 }
@@ -1663,7 +1773,7 @@ void fd_secret_move2()
 {
        if (self.noise2 != "")
                sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
-       SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
+       SUB_CalcMove(self.dest2, TSPEED_LINEAR, self.speed, fd_secret_move3);
 }
 
 // Wait here until time to go back...
@@ -1683,7 +1793,7 @@ void fd_secret_move4()
 {
        if (self.noise2 != "")
                sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
-       SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
+       SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move5);
 }
 
 // Wait 1 second...
@@ -1699,7 +1809,7 @@ void fd_secret_move6()
 {
        if (self.noise2 != "")
                sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
-       SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
+       SUB_CalcMove(self.oldorigin, TSPEED_LINEAR, self.speed, fd_secret_done);
 }
 
 void fd_secret_done()