+void SUB_CalcMove_controller_think (void)
+{
+ entity oldself;
+ float traveltime;
+ float phasepos;
+ float nexttick;
+ vector delta;
+ vector delta2;
+ vector veloc;
+ vector nextpos;
+ delta = self.destvec;
+ delta2 = self.destvec2;
+ if(time < self.animstate_endtime) {
+ nexttick = time + sys_frametime;
+
+ 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]
+ }
+ nextpos = self.origin + (delta * phasepos) + (delta2 * phasepos * phasepos);
+ // derivative: delta + 2 * delta2 * phasepos (e.g. for angle positioning)
+
+ if(nexttick < self.animstate_endtime) {
+ veloc = nextpos - self.owner.origin;
+ veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame
+ } else {
+ veloc = self.finaldest - self.owner.origin;
+ veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame
+ }
+ self.owner.velocity = veloc;
+ self.nextthink = nexttick;
+ } else {
+ // derivative: delta + 2 * delta2 (e.g. for angle positioning)
+ oldself = self;
+ self.owner.think = self.think1;
+ self = self.owner;
+ remove(oldself);
+ self.think();
+ }
+}
+
+void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector dest)
+{
+ // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + dest * t * t
+ // 2 * control * t - 2 * control * t * t + dest * t * t
+ // 2 * control * t + (dest - 2 * control) * t * t
+
+ controller.origin = org; // starting point
+ control -= org;
+ dest -= org;
+
+ controller.destvec = 2 * control; // control point
+ controller.destvec2 = dest - 2 * control; // quadratic part required to reach end point
+}
+
+void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector dest)
+{
+ // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + dest * t * t
+ // 2 * control * t - 2 * control * t * t + dest * t * t
+ // 2 * control * t + (dest - 2 * control) * t * t
+
+ controller.origin = org; // starting point
+ dest -= org;
+
+ controller.destvec = dest; // end point
+ controller.destvec2 = '0 0 0';
+}
+
+void SUB_CalcMove_Bezier (vector tcontrol, vector tdest, float tspeed, void() func)
+{
+ float traveltime;
+ entity controller;
+
+ if (!tspeed)
+ objerror ("No speed is defined!");
+
+ self.think1 = func;
+ 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;
+
+ if (traveltime < 0.1) // useless anim
+ {
+ self.velocity = '0 0 0';
+ self.nextthink = self.ltime + 0.1;
+ return;
+ }
+
+ controller = spawn();
+ controller.classname = "SUB_CalcMove_controller";
+ controller.owner = self;
+ controller.platmovetype = self.platmovetype;
+ 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;
+ controller.animstate_endtime = time + traveltime;
+ controller.think = SUB_CalcMove_controller_think;
+ controller.think1 = self.think;
+
+ // the thinking is now done by the controller
+ self.think = SUB_NullThink; // for PushMove
+ self.nextthink = self.ltime + traveltime;
+
+ // invoke controller
+ self = controller;
+ self.think();
+ self = self.owner;
+}
+