]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/triggers/subs.qc
Merge branch 'master' into 'terencehill/bot_waypoints'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / triggers / subs.qc
1 #include "subs.qh"
2 void SUB_NullThink(entity this) { }
3
4 void SUB_CalcMoveDone(entity this);
5 void SUB_CalcAngleMoveDone(entity this);
6
7 /*
8 ==================
9 SUB_Friction
10
11 Applies some friction to this
12 ==================
13 */
14 .float friction;
15 void SUB_Friction (entity this)
16 {
17         this.nextthink = time;
18         if(IS_ONGROUND(this))
19                 this.velocity = this.velocity * (1 - frametime * this.friction);
20 }
21
22 /*
23 ==================
24 SUB_VanishOrRemove
25
26 Makes client invisible or removes non-client
27 ==================
28 */
29 void SUB_VanishOrRemove (entity ent)
30 {
31         if (IS_CLIENT(ent))
32         {
33                 // vanish
34                 ent.alpha = -1;
35                 ent.effects = 0;
36 #ifdef SVQC
37                 ent.glow_size = 0;
38                 ent.pflags = 0;
39 #endif
40         }
41         else
42         {
43                 // remove
44                 delete(ent);
45         }
46 }
47
48 void SUB_SetFade_Think (entity this)
49 {
50         if(this.alpha == 0)
51                 this.alpha = 1;
52         setthink(this, SUB_SetFade_Think);
53         this.nextthink = time;
54         this.alpha -= frametime * this.fade_rate;
55         if (this.alpha < 0.01)
56                 SUB_VanishOrRemove(this);
57         else
58                 this.nextthink = time;
59 }
60
61 /*
62 ==================
63 SUB_SetFade
64
65 Fade 'ent' out when time >= 'when'
66 ==================
67 */
68 void SUB_SetFade (entity ent, float when, float fading_time)
69 {
70         ent.fade_rate = 1/fading_time;
71         setthink(ent, SUB_SetFade_Think);
72         ent.nextthink = when;
73 }
74
75 /*
76 =============
77 SUB_CalcMove
78
79 calculate this.velocity and this.nextthink to reach dest from
80 this.origin traveling at speed
81 ===============
82 */
83 void SUB_CalcMoveDone(entity this)
84 {
85         // After moving, set origin to exact final destination
86
87         setorigin (this, this.finaldest);
88         this.velocity = '0 0 0';
89         this.nextthink = -1;
90         if (this.think1 && this.think1 != SUB_CalcMoveDone)
91                 this.think1 (this);
92 }
93
94 .float platmovetype_turn;
95 void SUB_CalcMove_controller_think (entity this)
96 {
97         float traveltime;
98         float phasepos;
99         float nexttick;
100         vector delta;
101         vector delta2;
102         vector veloc;
103         vector angloc;
104         vector nextpos;
105         delta = this.destvec;
106         delta2 = this.destvec2;
107         if(time < this.animstate_endtime)
108         {
109                 nexttick = time + PHYS_INPUT_FRAMETIME;
110
111                 traveltime = this.animstate_endtime - this.animstate_starttime;
112                 phasepos = (nexttick - this.animstate_starttime) / traveltime; // range: [0, 1]
113                 phasepos = cubic_speedfunc(this.platmovetype_start, this.platmovetype_end, phasepos);
114                 nextpos = this.origin + (delta * phasepos) + (delta2 * phasepos * phasepos);
115                 // derivative: delta + 2 * delta2 * phasepos (e.g. for angle positioning)
116
117                 if(this.owner.platmovetype_turn)
118                 {
119                         vector destangle;
120                         destangle = delta + 2 * delta2 * phasepos;
121                         destangle = vectoangles(destangle);
122                         destangle_x = -destangle_x; // flip up / down orientation
123
124                         // take the shortest distance for the angles
125                         vector v = this.owner.angles;
126                         v.x -= 360 * floor((v.x - destangle_x) / 360 + 0.5);
127                         v.y -= 360 * floor((v.y - destangle_y) / 360 + 0.5);
128                         v.z -= 360 * floor((v.z - destangle_z) / 360 + 0.5);
129                         this.owner.angles = v;
130                         angloc = destangle - this.owner.angles;
131                         angloc = angloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame
132                         this.owner.avelocity = angloc;
133                 }
134                 if(nexttick < this.animstate_endtime)
135                         veloc = nextpos - this.owner.origin;
136                 else
137                         veloc = this.finaldest - this.owner.origin;
138                 veloc = veloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame
139
140                 this.owner.velocity = veloc;
141                 this.nextthink = nexttick;
142         }
143         else
144         {
145                 // derivative: delta + 2 * delta2 (e.g. for angle positioning)
146                 entity own = this.owner;
147                 setthink(own, this.think1);
148                 // set the owner's reference to this entity to NULL
149                 own.move_controller = NULL;
150                 delete(this);
151                 getthink(own)(own);
152         }
153 }
154
155 void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector destin)
156 {
157         // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + destin * t * t
158         // 2 * control * t - 2 * control * t * t + destin * t * t
159         // 2 * control * t + (destin - 2 * control) * t * t
160
161         setorigin(controller, org);
162         control -= org;
163         destin -= org;
164
165         controller.destvec = 2 * control; // control point
166         controller.destvec2 = destin - 2 * control; // quadratic part required to reach end point
167         // also: initial d/dphasepos origin = 2 * control, final speed = 2 * (destin - control)
168 }
169
170 void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector destin)
171 {
172         // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + destin * t * t
173         // 2 * control * t - 2 * control * t * t + destin * t * t
174         // 2 * control * t + (destin - 2 * control) * t * t
175
176         setorigin(controller, org);
177         destin -= org;
178
179         controller.destvec = destin; // end point
180         controller.destvec2 = '0 0 0';
181 }
182
183 float TSPEED_TIME = -1;
184 float TSPEED_LINEAR = 0;
185 float TSPEED_START = 1;
186 float TSPEED_END = 2;
187 // TODO average too?
188
189 void SUB_CalcMove_Bezier (entity this, vector tcontrol, vector tdest, float tspeedtype, float tspeed, void(entity this) func)
190 {
191         float   traveltime;
192         entity controller;
193
194         if (!tspeed)
195                 objerror (this, "No speed is defined!");
196
197         this.think1 = func;
198         this.finaldest = tdest;
199         setthink(this, SUB_CalcMoveDone);
200
201         switch(tspeedtype)
202         {
203                 default:
204                 case TSPEED_START:
205                         traveltime = 2 * vlen(tcontrol - this.origin) / tspeed;
206                         break;
207                 case TSPEED_END:
208                         traveltime = 2 * vlen(tcontrol - tdest)       / tspeed;
209                         break;
210                 case TSPEED_LINEAR:
211                         traveltime = vlen(tdest - this.origin)        / tspeed;
212                         break;
213                 case TSPEED_TIME:
214                         traveltime = tspeed;
215                         break;
216         }
217
218         if (traveltime < 0.1) // useless anim
219         {
220                 this.velocity = '0 0 0';
221                 this.nextthink = this.ltime + 0.1;
222                 return;
223         }
224
225         // delete the previous controller, otherwise changing movement midway is glitchy
226         if (this.move_controller != NULL)
227         {
228                 delete(this.move_controller);
229         }
230         controller = new(SUB_CalcMove_controller);
231         controller.owner = this;
232         this.move_controller = controller;
233         controller.platmovetype = this.platmovetype;
234         controller.platmovetype_start = this.platmovetype_start;
235         controller.platmovetype_end = this.platmovetype_end;
236         SUB_CalcMove_controller_setbezier(controller, this.origin, tcontrol, tdest);
237         controller.finaldest = (tdest + '0 0 0.125'); // where do we want to end? Offset to overshoot a bit.
238         controller.animstate_starttime = time;
239         controller.animstate_endtime = time + traveltime;
240         setthink(controller, SUB_CalcMove_controller_think);
241         controller.think1 = getthink(this);
242
243         // the thinking is now done by the controller
244         setthink(this, SUB_NullThink); // for PushMove
245         this.nextthink = this.ltime + traveltime;
246
247         // invoke controller
248         getthink(controller)(controller);
249 }
250
251 void SUB_CalcMove (entity this, vector tdest, float tspeedtype, float tspeed, void(entity this) func)
252 {
253         vector  delta;
254         float   traveltime;
255
256         if (!tspeed)
257                 objerror (this, "No speed is defined!");
258
259         this.think1 = func;
260         this.finaldest = tdest;
261         setthink(this, SUB_CalcMoveDone);
262
263         if (tdest == this.origin)
264         {
265                 this.velocity = '0 0 0';
266                 this.nextthink = this.ltime + 0.1;
267                 return;
268         }
269
270         delta = tdest - this.origin;
271
272         switch(tspeedtype)
273         {
274                 default:
275                 case TSPEED_START:
276                 case TSPEED_END:
277                 case TSPEED_LINEAR:
278                         traveltime = vlen (delta) / tspeed;
279                         break;
280                 case TSPEED_TIME:
281                         traveltime = tspeed;
282                         break;
283         }
284
285         // Very short animations don't really show off the effect
286         // of controlled animation, so let's just use linear movement.
287         // Alternatively entities can choose to specify non-controlled movement.
288         // The only currently implemented alternative movement is linear (value 1)
289         if (traveltime < 0.15 || (this.platmovetype_start == 1 && this.platmovetype_end == 1)) // is this correct?
290         {
291                 this.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
292                 this.nextthink = this.ltime + traveltime;
293                 return;
294         }
295
296         // now just run like a bezier curve...
297         SUB_CalcMove_Bezier(this, (this.origin + tdest) * 0.5, tdest, tspeedtype, tspeed, func);
298 }
299
300 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeedtype, float tspeed, void(entity this) func)
301 {
302         SUB_CalcMove(ent, tdest, tspeedtype, tspeed, func);
303 }
304
305 /*
306 =============
307 SUB_CalcAngleMove
308
309 calculate this.avelocity and this.nextthink to reach destangle from
310 this.angles rotating
311
312 The calling function should make sure this.setthink is valid
313 ===============
314 */
315 void SUB_CalcAngleMoveDone(entity this)
316 {
317         // After rotating, set angle to exact final angle
318         this.angles = this.finalangle;
319         this.avelocity = '0 0 0';
320         this.nextthink = -1;
321         if (this.think1 && this.think1 != SUB_CalcAngleMoveDone) // avoid endless loops
322                 this.think1 (this);
323 }
324
325 // FIXME: I fixed this function only for rotation around the main axes
326 void SUB_CalcAngleMove (entity this, vector destangle, float tspeedtype, float tspeed, void(entity this) func)
327 {
328         if (!tspeed)
329                 objerror (this, "No speed is defined!");
330
331         // take the shortest distance for the angles
332         this.angles_x -= 360 * floor((this.angles_x - destangle_x) / 360 + 0.5);
333         this.angles_y -= 360 * floor((this.angles_y - destangle_y) / 360 + 0.5);
334         this.angles_z -= 360 * floor((this.angles_z - destangle_z) / 360 + 0.5);
335         vector delta = destangle - this.angles;
336         float traveltime;
337
338         switch(tspeedtype)
339         {
340                 default:
341                 case TSPEED_START:
342                 case TSPEED_END:
343                 case TSPEED_LINEAR:
344                         traveltime = vlen (delta) / tspeed;
345                         break;
346                 case TSPEED_TIME:
347                         traveltime = tspeed;
348                         break;
349         }
350
351         this.think1 = func;
352         this.finalangle = destangle;
353         setthink(this, SUB_CalcAngleMoveDone);
354
355         if (traveltime < 0.1)
356         {
357                 this.avelocity = '0 0 0';
358                 this.nextthink = this.ltime + 0.1;
359                 return;
360         }
361
362         this.avelocity = delta * (1 / traveltime);
363         this.nextthink = this.ltime + traveltime;
364 }
365
366 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void(entity this) func)
367 {
368         SUB_CalcAngleMove (ent, destangle, tspeedtype, tspeed, func);
369 }