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.SUB_NEXTTHINK = time;
30 if(self.SUB_FLAGS & FL_ONGROUND)
31 self.SUB_VELOCITY = self.SUB_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.SUB_THINK = SUB_SetFade_Think;
65 self.SUB_NEXTTHINK = time;
66 self.alpha -= frametime * self.fade_rate;
67 if (self.alpha < 0.01)
68 SUB_VanishOrRemove(self);
70 self.SUB_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.SUB_THINK = SUB_SetFade_Think;
84 ent.SUB_NEXTTHINK = when;
91 calculate self.SUB_VELOCITY and self.SUB_NEXTTHINK to reach dest from
92 self.SUB_ORIGIN traveling at speed
95 void SUB_CalcMoveDone (void)
97 // After moving, set origin to exact final destination
99 SUB_SETORIGIN (self, self.finaldest);
100 self.SUB_VELOCITY = '0 0 0';
101 self.SUB_NEXTTHINK = -1;
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 SUB_ANGLES(self.owner)_x -= 360 * floor((SUB_ANGLES(self.owner)_x - destangle_x) / 360 + 0.5);
139 SUB_ANGLES(self.owner)_y -= 360 * floor((SUB_ANGLES(self.owner)_y - destangle_y) / 360 + 0.5);
140 SUB_ANGLES(self.owner)_z -= 360 * floor((SUB_ANGLES(self.owner)_z - destangle_z) / 360 + 0.5);
141 angloc = destangle - SUB_ANGLES(self.owner);
142 angloc = angloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame
143 self.owner.SUB_AVELOCITY = angloc;
145 if(nexttick < self.animstate_endtime)
146 veloc = nextpos - self.owner.SUB_ORIGIN;
148 veloc = self.finaldest - self.owner.SUB_ORIGIN;
149 veloc = veloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame
151 self.owner.SUB_VELOCITY = veloc;
152 self.nextthink = nexttick;
156 // derivative: delta + 2 * delta2 (e.g. for angle positioning)
158 self.owner.SUB_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 setorigin(controller, org);
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 setorigin(controller, org);
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.SUB_THINK = SUB_CalcMoveDone;
215 traveltime = 2 * vlen(tcontrol - self.SUB_ORIGIN) / tspeed;
218 traveltime = 2 * vlen(tcontrol - tdest) / tspeed;
221 traveltime = vlen(tdest - self.SUB_ORIGIN) / tspeed;
228 if (traveltime < 0.1) // useless anim
230 self.SUB_VELOCITY = '0 0 0';
231 self.SUB_NEXTTHINK = self.SUB_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.SUB_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.SUB_THINK;
248 // the thinking is now done by the controller
249 self.SUB_THINK = SUB_NullThink; // for PushMove
250 self.SUB_NEXTTHINK = self.SUB_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.SUB_THINK = SUB_CalcMoveDone;
270 if (tdest == self.SUB_ORIGIN)
272 self.SUB_VELOCITY = '0 0 0';
273 self.SUB_NEXTTHINK = self.SUB_LTIME + 0.1;
277 delta = tdest - self.SUB_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.SUB_VELOCITY = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
299 self.SUB_NEXTTHINK = self.SUB_LTIME + traveltime;
303 // now just run like a bezier curve...
304 SUB_CalcMove_Bezier((self.SUB_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.SUB_AVELOCITY and self.SUB_NEXTTHINK to reach destangle from
326 The calling function should make sure self.SUB_THINK is valid
329 void SUB_CalcAngleMoveDone (void)
331 // After rotating, set angle to exact final angle
332 self.angles = self.finalangle;
333 self.SUB_AVELOCITY = '0 0 0';
334 self.SUB_NEXTTHINK = -1;
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.SUB_THINK = SUB_CalcAngleMoveDone;
371 if (traveltime < 0.1)
373 self.SUB_AVELOCITY = '0 0 0';
374 self.SUB_NEXTTHINK = self.SUB_LTIME + 0.1;
378 self.SUB_AVELOCITY = delta * (1 / traveltime);
379 self.SUB_NEXTTHINK = self.SUB_LTIME + traveltime;
382 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void() func)
389 SUB_CalcAngleMove (destangle, tspeedtype, tspeed, func);