Properly support team field on trigger_multiple
[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                 delete(this);
149                 getthink(own)(own);
150         }
151 }
152
153 void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector destin)
154 {
155         // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + destin * t * t
156         // 2 * control * t - 2 * control * t * t + destin * t * t
157         // 2 * control * t + (destin - 2 * control) * t * t
158
159         setorigin(controller, org);
160         control -= org;
161         destin -= org;
162
163         controller.destvec = 2 * control; // control point
164         controller.destvec2 = destin - 2 * control; // quadratic part required to reach end point
165         // also: initial d/dphasepos origin = 2 * control, final speed = 2 * (destin - control)
166 }
167
168 void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector destin)
169 {
170         // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + destin * t * t
171         // 2 * control * t - 2 * control * t * t + destin * t * t
172         // 2 * control * t + (destin - 2 * control) * t * t
173
174         setorigin(controller, org);
175         destin -= org;
176
177         controller.destvec = destin; // end point
178         controller.destvec2 = '0 0 0';
179 }
180
181 float TSPEED_TIME = -1;
182 float TSPEED_LINEAR = 0;
183 float TSPEED_START = 1;
184 float TSPEED_END = 2;
185 // TODO average too?
186
187 void SUB_CalcMove_Bezier (entity this, vector tcontrol, vector tdest, float tspeedtype, float tspeed, void(entity this) func)
188 {
189         float   traveltime;
190         entity controller;
191
192         if (!tspeed)
193                 objerror (this, "No speed is defined!");
194
195         this.think1 = func;
196         this.finaldest = tdest;
197         setthink(this, SUB_CalcMoveDone);
198
199         switch(tspeedtype)
200         {
201                 default:
202                 case TSPEED_START:
203                         traveltime = 2 * vlen(tcontrol - this.origin) / tspeed;
204                         break;
205                 case TSPEED_END:
206                         traveltime = 2 * vlen(tcontrol - tdest)       / tspeed;
207                         break;
208                 case TSPEED_LINEAR:
209                         traveltime = vlen(tdest - this.origin)        / tspeed;
210                         break;
211                 case TSPEED_TIME:
212                         traveltime = tspeed;
213                         break;
214         }
215
216         if (traveltime < 0.1) // useless anim
217         {
218                 this.velocity = '0 0 0';
219                 this.nextthink = this.ltime + 0.1;
220                 return;
221         }
222
223         controller = new(SUB_CalcMove_controller);
224         controller.owner = this;
225         controller.platmovetype = this.platmovetype;
226         controller.platmovetype_start = this.platmovetype_start;
227         controller.platmovetype_end = this.platmovetype_end;
228         SUB_CalcMove_controller_setbezier(controller, this.origin, tcontrol, tdest);
229         controller.finaldest = (tdest + '0 0 0.125'); // where do we want to end? Offset to overshoot a bit.
230         controller.animstate_starttime = time;
231         controller.animstate_endtime = time + traveltime;
232         setthink(controller, SUB_CalcMove_controller_think);
233         controller.think1 = getthink(this);
234
235         // the thinking is now done by the controller
236         setthink(this, SUB_NullThink); // for PushMove
237         this.nextthink = this.ltime + traveltime;
238
239         // invoke controller
240         getthink(controller)(controller);
241 }
242
243 void SUB_CalcMove (entity this, vector tdest, float tspeedtype, float tspeed, void(entity this) func)
244 {
245         vector  delta;
246         float   traveltime;
247
248         if (!tspeed)
249                 objerror (this, "No speed is defined!");
250
251         this.think1 = func;
252         this.finaldest = tdest;
253         setthink(this, SUB_CalcMoveDone);
254
255         if (tdest == this.origin)
256         {
257                 this.velocity = '0 0 0';
258                 this.nextthink = this.ltime + 0.1;
259                 return;
260         }
261
262         delta = tdest - this.origin;
263
264         switch(tspeedtype)
265         {
266                 default:
267                 case TSPEED_START:
268                 case TSPEED_END:
269                 case TSPEED_LINEAR:
270                         traveltime = vlen (delta) / tspeed;
271                         break;
272                 case TSPEED_TIME:
273                         traveltime = tspeed;
274                         break;
275         }
276
277         // Very short animations don't really show off the effect
278         // of controlled animation, so let's just use linear movement.
279         // Alternatively entities can choose to specify non-controlled movement.
280         // The only currently implemented alternative movement is linear (value 1)
281         if (traveltime < 0.15 || (this.platmovetype_start == 1 && this.platmovetype_end == 1)) // is this correct?
282         {
283                 this.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
284                 this.nextthink = this.ltime + traveltime;
285                 return;
286         }
287
288         // now just run like a bezier curve...
289         SUB_CalcMove_Bezier(this, (this.origin + tdest) * 0.5, tdest, tspeedtype, tspeed, func);
290 }
291
292 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeedtype, float tspeed, void(entity this) func)
293 {
294         SUB_CalcMove(ent, tdest, tspeedtype, tspeed, func);
295 }
296
297 /*
298 =============
299 SUB_CalcAngleMove
300
301 calculate this.avelocity and this.nextthink to reach destangle from
302 this.angles rotating
303
304 The calling function should make sure this.setthink is valid
305 ===============
306 */
307 void SUB_CalcAngleMoveDone(entity this)
308 {
309         // After rotating, set angle to exact final angle
310         this.angles = this.finalangle;
311         this.avelocity = '0 0 0';
312         this.nextthink = -1;
313         if (this.think1 && this.think1 != SUB_CalcAngleMoveDone) // avoid endless loops
314                 this.think1 (this);
315 }
316
317 // FIXME: I fixed this function only for rotation around the main axes
318 void SUB_CalcAngleMove (entity this, vector destangle, float tspeedtype, float tspeed, void(entity this) func)
319 {
320         if (!tspeed)
321                 objerror (this, "No speed is defined!");
322
323         // take the shortest distance for the angles
324         this.angles_x -= 360 * floor((this.angles_x - destangle_x) / 360 + 0.5);
325         this.angles_y -= 360 * floor((this.angles_y - destangle_y) / 360 + 0.5);
326         this.angles_z -= 360 * floor((this.angles_z - destangle_z) / 360 + 0.5);
327         vector delta = destangle - this.angles;
328         float traveltime;
329
330         switch(tspeedtype)
331         {
332                 default:
333                 case TSPEED_START:
334                 case TSPEED_END:
335                 case TSPEED_LINEAR:
336                         traveltime = vlen (delta) / tspeed;
337                         break;
338                 case TSPEED_TIME:
339                         traveltime = tspeed;
340                         break;
341         }
342
343         this.think1 = func;
344         this.finalangle = destangle;
345         setthink(this, SUB_CalcAngleMoveDone);
346
347         if (traveltime < 0.1)
348         {
349                 this.avelocity = '0 0 0';
350                 this.nextthink = this.ltime + 0.1;
351                 return;
352         }
353
354         this.avelocity = delta * (1 / traveltime);
355         this.nextthink = this.ltime + traveltime;
356 }
357
358 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void(entity this) func)
359 {
360         SUB_CalcAngleMove (ent, destangle, tspeedtype, tspeed, func);
361 }