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