1 void SUB_NullThink(entity this) { }
3 void SUB_CalcMoveDone(entity this);
4 void SUB_CalcAngleMoveDone(entity this);
10 Applies some friction to this
14 void SUB_Friction (entity this)
16 this.SUB_NEXTTHINK = time;
17 if(this.SUB_FLAGS & FL_ONGROUND)
18 this.SUB_VELOCITY = this.SUB_VELOCITY * (1 - frametime * this.friction);
25 Makes client invisible or removes non-client
28 void SUB_VanishOrRemove (entity ent)
47 void SUB_SetFade_Think (entity this)
51 SUB_THINK(this, SUB_SetFade_Think);
52 this.SUB_NEXTTHINK = time;
53 this.alpha -= frametime * this.fade_rate;
54 if (this.alpha < 0.01)
55 SUB_VanishOrRemove(this);
57 this.SUB_NEXTTHINK = time;
64 Fade 'ent' out when time >= 'when'
67 void SUB_SetFade (entity ent, float when, float fading_time)
69 ent.fade_rate = 1/fading_time;
70 SUB_THINK(ent, SUB_SetFade_Think);
71 ent.SUB_NEXTTHINK = when;
78 calculate this.SUB_VELOCITY and this.SUB_NEXTTHINK to reach dest from
79 this.SUB_ORIGIN traveling at speed
82 void SUB_CalcMoveDone(entity this)
84 // After moving, set origin to exact final destination
86 SUB_SETORIGIN (this, this.finaldest);
87 this.SUB_VELOCITY = '0 0 0';
88 this.SUB_NEXTTHINK = -1;
93 .float platmovetype_turn;
94 void SUB_CalcMove_controller_think (entity this)
104 delta = this.destvec;
105 delta2 = this.destvec2;
106 if(time < this.animstate_endtime)
108 nexttick = time + PHYS_INPUT_FRAMETIME;
110 traveltime = this.animstate_endtime - this.animstate_starttime;
111 phasepos = (nexttick - this.animstate_starttime) / traveltime; // range: [0, 1]
112 phasepos = cubic_speedfunc(this.platmovetype_start, this.platmovetype_end, phasepos);
113 nextpos = this.origin + (delta * phasepos) + (delta2 * phasepos * phasepos);
114 // derivative: delta + 2 * delta2 * phasepos (e.g. for angle positioning)
116 if(this.owner.platmovetype_turn)
119 destangle = delta + 2 * delta2 * phasepos;
120 destangle = vectoangles(destangle);
121 destangle_x = -destangle_x; // flip up / down orientation
123 // take the shortest distance for the angles
124 vector v = SUB_ANGLES(this.owner);
125 v.x -= 360 * floor((v.x - destangle_x) / 360 + 0.5);
126 v.y -= 360 * floor((v.y - destangle_y) / 360 + 0.5);
127 v.z -= 360 * floor((v.z - destangle_z) / 360 + 0.5);
128 SUB_ANGLES(this.owner) = v;
129 angloc = destangle - SUB_ANGLES(this.owner);
130 angloc = angloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame
131 this.owner.SUB_AVELOCITY = angloc;
133 if(nexttick < this.animstate_endtime)
134 veloc = nextpos - this.owner.SUB_ORIGIN;
136 veloc = this.finaldest - this.owner.SUB_ORIGIN;
137 veloc = veloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame
139 this.owner.SUB_VELOCITY = veloc;
140 this.nextthink = nexttick;
144 // derivative: delta + 2 * delta2 (e.g. for angle positioning)
145 entity own = this.owner;
146 SUB_THINK(own, this.think1);
148 WITHSELF(own, SUB_THUNK(own)(own));
152 void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector destin)
154 // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + destin * t * t
155 // 2 * control * t - 2 * control * t * t + destin * t * t
156 // 2 * control * t + (destin - 2 * control) * t * t
158 setorigin(controller, org);
162 controller.destvec = 2 * control; // control point
163 controller.destvec2 = destin - 2 * control; // quadratic part required to reach end point
164 // also: initial d/dphasepos origin = 2 * control, final speed = 2 * (destin - control)
167 void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector destin)
169 // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + destin * t * t
170 // 2 * control * t - 2 * control * t * t + destin * t * t
171 // 2 * control * t + (destin - 2 * control) * t * t
173 setorigin(controller, org);
176 controller.destvec = destin; // end point
177 controller.destvec2 = '0 0 0';
180 float TSPEED_TIME = -1;
181 float TSPEED_LINEAR = 0;
182 float TSPEED_START = 1;
183 float TSPEED_END = 2;
186 void SUB_CalcMove_Bezier (entity this, vector tcontrol, vector tdest, float tspeedtype, float tspeed, void(entity this) func)
192 objerror ("No speed is defined!");
195 this.finaldest = tdest;
196 SUB_THINK(this, SUB_CalcMoveDone);
202 traveltime = 2 * vlen(tcontrol - this.SUB_ORIGIN) / tspeed;
205 traveltime = 2 * vlen(tcontrol - tdest) / tspeed;
208 traveltime = vlen(tdest - this.SUB_ORIGIN) / tspeed;
215 if (traveltime < 0.1) // useless anim
217 this.SUB_VELOCITY = '0 0 0';
218 this.SUB_NEXTTHINK = this.SUB_LTIME + 0.1;
222 controller = new(SUB_CalcMove_controller);
223 controller.owner = this;
224 controller.platmovetype = this.platmovetype;
225 controller.platmovetype_start = this.platmovetype_start;
226 controller.platmovetype_end = this.platmovetype_end;
227 SUB_CalcMove_controller_setbezier(controller, this.SUB_ORIGIN, tcontrol, tdest);
228 controller.finaldest = (tdest + '0 0 0.125'); // where do we want to end? Offset to overshoot a bit.
229 controller.animstate_starttime = time;
230 controller.animstate_endtime = time + traveltime;
231 setthink(controller, SUB_CalcMove_controller_think);
232 controller.think1 = SUB_THUNK(this);
234 // the thinking is now done by the controller
235 SUB_THINK(this, SUB_NullThink); // for PushMove
236 this.SUB_NEXTTHINK = this.SUB_LTIME + traveltime;
239 WITHSELF(controller, getthink(controller)(controller));
242 void SUB_CalcMove (entity this, vector tdest, float tspeedtype, float tspeed, void(entity this) func)
248 objerror ("No speed is defined!");
251 this.finaldest = tdest;
252 SUB_THINK(this, SUB_CalcMoveDone);
254 if (tdest == this.SUB_ORIGIN)
256 this.SUB_VELOCITY = '0 0 0';
257 this.SUB_NEXTTHINK = this.SUB_LTIME + 0.1;
261 delta = tdest - this.SUB_ORIGIN;
269 traveltime = vlen (delta) / tspeed;
276 // Very short animations don't really show off the effect
277 // of controlled animation, so let's just use linear movement.
278 // Alternatively entities can choose to specify non-controlled movement.
279 // The only currently implemented alternative movement is linear (value 1)
280 if (traveltime < 0.15 || (this.platmovetype_start == 1 && this.platmovetype_end == 1)) // is this correct?
282 this.SUB_VELOCITY = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
283 this.SUB_NEXTTHINK = this.SUB_LTIME + traveltime;
287 // now just run like a bezier curve...
288 SUB_CalcMove_Bezier(this, (this.SUB_ORIGIN + tdest) * 0.5, tdest, tspeedtype, tspeed, func);
291 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeedtype, float tspeed, void(entity this) func)
293 WITHSELF(ent, SUB_CalcMove(ent, tdest, tspeedtype, tspeed, func));
300 calculate this.SUB_AVELOCITY and this.SUB_NEXTTHINK to reach destangle from
303 The calling function should make sure this.SUB_THINK is valid
306 void SUB_CalcAngleMoveDone(entity this)
308 // After rotating, set angle to exact final angle
309 this.angles = this.finalangle;
310 this.SUB_AVELOCITY = '0 0 0';
311 this.SUB_NEXTTHINK = -1;
316 // FIXME: I fixed this function only for rotation around the main axes
317 void SUB_CalcAngleMove (entity this, vector destangle, float tspeedtype, float tspeed, void(entity this) func)
323 objerror ("No speed is defined!");
325 // take the shortest distance for the angles
326 this.angles_x -= 360 * floor((this.angles_x - destangle_x) / 360 + 0.5);
327 this.angles_y -= 360 * floor((this.angles_y - destangle_y) / 360 + 0.5);
328 this.angles_z -= 360 * floor((this.angles_z - destangle_z) / 360 + 0.5);
329 delta = destangle - this.angles;
337 traveltime = vlen (delta) / tspeed;
345 this.finalangle = destangle;
346 SUB_THINK(this, SUB_CalcAngleMoveDone);
348 if (traveltime < 0.1)
350 this.SUB_AVELOCITY = '0 0 0';
351 this.SUB_NEXTTHINK = this.SUB_LTIME + 0.1;
355 this.SUB_AVELOCITY = delta * (1 / traveltime);
356 this.SUB_NEXTTHINK = this.SUB_LTIME + traveltime;
359 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void(entity this) func)
361 WITHSELF(ent, SUB_CalcAngleMove (ent, destangle, tspeedtype, tspeed, func));