1 void SUB_NullThink(void) { }
3 void() SUB_CalcMoveDone;
4 void() SUB_CalcAngleMoveDone;
5 //void() SUB_UseTargets;
23 Applies some friction to self
27 void SUB_Friction (void)
29 self.nextthink = time;
30 if(self.flags & FL_ONGROUND)
31 self.velocity = self.velocity * (1 - frametime * self.friction);
38 Makes client invisible or removes non-client
41 void SUB_VanishOrRemove (entity ent)
60 void SUB_SetFade_Think (void)
64 self.think = SUB_SetFade_Think;
65 self.nextthink = time;
66 self.alpha -= frametime * self.fade_rate;
67 if (self.alpha < 0.01)
68 SUB_VanishOrRemove(self);
70 self.nextthink = time;
77 Fade 'ent' out when time >= 'when'
80 void SUB_SetFade (entity ent, float when, float fading_time)
82 ent.fade_rate = 1/fading_time;
83 ent.think = SUB_SetFade_Think;
91 calculate self.velocity and self.nextthink to reach dest from
92 self.origin traveling at speed
95 void SUB_CalcMoveDone (void)
97 // After moving, set origin to exact final destination
99 setorigin (self, self.finaldest);
100 self.velocity = '0 0 0';
106 .float platmovetype_turn;
107 void SUB_CalcMove_controller_think (void)
118 delta = self.destvec;
119 delta2 = self.destvec2;
120 if(time < self.animstate_endtime)
122 nexttick = time + PHYS_INPUT_FRAMETIME;
124 traveltime = self.animstate_endtime - self.animstate_starttime;
125 phasepos = (nexttick - self.animstate_starttime) / traveltime; // range: [0, 1]
126 phasepos = cubic_speedfunc(self.platmovetype_start, self.platmovetype_end, phasepos);
127 nextpos = self.origin + (delta * phasepos) + (delta2 * phasepos * phasepos);
128 // derivative: delta + 2 * delta2 * phasepos (e.g. for angle positioning)
130 if(self.owner.platmovetype_turn)
133 destangle = delta + 2 * delta2 * phasepos;
134 destangle = vectoangles(destangle);
135 destangle_x = -destangle_x; // flip up / down orientation
137 // take the shortest distance for the angles
138 self.owner.angles_x -= 360 * floor((self.owner.angles_x - destangle_x) / 360 + 0.5);
139 self.owner.angles_y -= 360 * floor((self.owner.angles_y - destangle_y) / 360 + 0.5);
140 self.owner.angles_z -= 360 * floor((self.owner.angles_z - destangle_z) / 360 + 0.5);
141 angloc = destangle - self.owner.angles;
142 angloc = angloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame
143 self.owner.avelocity = angloc;
145 if(nexttick < self.animstate_endtime)
146 veloc = nextpos - self.owner.origin;
148 veloc = self.finaldest - self.owner.origin;
149 veloc = veloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame
151 self.owner.velocity = veloc;
152 self.nextthink = nexttick;
156 // derivative: delta + 2 * delta2 (e.g. for angle positioning)
158 self.owner.think = self.think1;
165 void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector destin)
167 // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + destin * t * t
168 // 2 * control * t - 2 * control * t * t + destin * t * t
169 // 2 * control * t + (destin - 2 * control) * t * t
171 controller.origin = org; // starting point
175 controller.destvec = 2 * control; // control point
176 controller.destvec2 = destin - 2 * control; // quadratic part required to reach end point
177 // also: initial d/dphasepos origin = 2 * control, final speed = 2 * (destin - control)
180 void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector destin)
182 // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + destin * t * t
183 // 2 * control * t - 2 * control * t * t + destin * t * t
184 // 2 * control * t + (destin - 2 * control) * t * t
186 controller.origin = org; // starting point
189 controller.destvec = destin; // end point
190 controller.destvec2 = '0 0 0';
193 float TSPEED_TIME = -1;
194 float TSPEED_LINEAR = 0;
195 float TSPEED_START = 1;
196 float TSPEED_END = 2;
199 void SUB_CalcMove_Bezier (vector tcontrol, vector tdest, float tspeedtype, float tspeed, void() func)
205 objerror ("No speed is defined!");
208 self.finaldest = tdest;
209 self.think = SUB_CalcMoveDone;
215 traveltime = 2 * vlen(tcontrol - self.origin) / tspeed;
218 traveltime = 2 * vlen(tcontrol - tdest) / tspeed;
221 traveltime = vlen(tdest - self.origin) / tspeed;
228 if (traveltime < 0.1) // useless anim
230 self.velocity = '0 0 0';
231 trigger_setnextthink(self, self.ltime + 0.1);
235 controller = spawn();
236 controller.classname = "SUB_CalcMove_controller";
237 controller.owner = self;
238 controller.platmovetype = self.platmovetype;
239 controller.platmovetype_start = self.platmovetype_start;
240 controller.platmovetype_end = self.platmovetype_end;
241 SUB_CalcMove_controller_setbezier(controller, self.origin, tcontrol, tdest);
242 controller.finaldest = (tdest + '0 0 0.125'); // where do we want to end? Offset to overshoot a bit.
243 controller.animstate_starttime = time;
244 controller.animstate_endtime = time + traveltime;
245 controller.think = SUB_CalcMove_controller_think;
246 controller.think1 = self.think;
248 // the thinking is now done by the controller
249 self.think = SUB_NullThink; // for PushMove
250 trigger_setnextthink(self, self.ltime + traveltime);
258 void SUB_CalcMove (vector tdest, float tspeedtype, float tspeed, void() func)
264 objerror ("No speed is defined!");
267 self.finaldest = tdest;
268 self.think = SUB_CalcMoveDone;
270 if (tdest == self.origin)
272 self.velocity = '0 0 0';
273 trigger_setnextthink(self, self.ltime + 0.1);
277 delta = tdest - self.origin;
285 traveltime = vlen (delta) / tspeed;
292 // Very short animations don't really show off the effect
293 // of controlled animation, so let's just use linear movement.
294 // Alternatively entities can choose to specify non-controlled movement.
295 // The only currently implemented alternative movement is linear (value 1)
296 if (traveltime < 0.15 || (self.platmovetype_start == 1 && self.platmovetype_end == 1)) // is this correct?
298 self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
299 trigger_setnextthink(self, self.ltime + traveltime);
303 // now just run like a bezier curve...
304 SUB_CalcMove_Bezier((self.origin + tdest) * 0.5, tdest, tspeedtype, tspeed, func);
307 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeedtype, float tspeed, void() func)
314 SUB_CalcMove (tdest, tspeedtype, tspeed, func);
323 calculate self.avelocity and self.nextthink to reach destangle from
326 The calling function should make sure self.think is valid
329 void SUB_CalcAngleMoveDone (void)
331 // After rotating, set angle to exact final angle
332 self.angles = self.finalangle;
333 self.avelocity = '0 0 0';
339 // FIXME: I fixed this function only for rotation around the main axes
340 void SUB_CalcAngleMove (vector destangle, float tspeedtype, float tspeed, void() func)
346 objerror ("No speed is defined!");
348 // take the shortest distance for the angles
349 self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
350 self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
351 self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
352 delta = destangle - self.angles;
360 traveltime = vlen (delta) / tspeed;
368 self.finalangle = destangle;
369 self.think = SUB_CalcAngleMoveDone;
371 if (traveltime < 0.1)
373 self.avelocity = '0 0 0';
374 trigger_setnextthink(self, self.ltime + 0.1);
378 self.avelocity = delta * (1 / traveltime);
379 trigger_setnextthink(self, self.ltime + traveltime);
382 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void() func)
389 SUB_CalcAngleMove (destangle, tspeedtype, tspeed, func);