]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/triggers/subs.qc
Merge branch 'master' into Lyberta/TeamplayFixes_
[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.SUB_NEXTTHINK = time;
18         if(IS_ONGROUND(this))
19                 this.SUB_VELOCITY = this.SUB_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         SUB_THINK(this, SUB_SetFade_Think);
53         this.SUB_NEXTTHINK = time;
54         this.alpha -= frametime * this.fade_rate;
55         if (this.alpha < 0.01)
56                 SUB_VanishOrRemove(this);
57         else
58                 this.SUB_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         SUB_THINK(ent, SUB_SetFade_Think);
72         ent.SUB_NEXTTHINK = when;
73 }
74
75 /*
76 =============
77 SUB_CalcMove
78
79 calculate this.SUB_VELOCITY and this.SUB_NEXTTHINK to reach dest from
80 this.SUB_ORIGIN traveling at speed
81 ===============
82 */
83 void SUB_CalcMoveDone(entity this)
84 {
85         // After moving, set origin to exact final destination
86
87         SUB_SETORIGIN (this, this.finaldest);
88         this.SUB_VELOCITY = '0 0 0';
89         this.SUB_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 = SUB_ANGLES(this.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(this.owner) = v;
130                         angloc = destangle - SUB_ANGLES(this.owner);
131                         angloc = angloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame
132                         this.owner.SUB_AVELOCITY = angloc;
133                 }
134                 if(nexttick < this.animstate_endtime)
135                         veloc = nextpos - this.owner.SUB_ORIGIN;
136                 else
137                         veloc = this.finaldest - this.owner.SUB_ORIGIN;
138                 veloc = veloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame
139
140                 this.owner.SUB_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                 SUB_THINK(own, this.think1);
148                 delete(this);
149                 SUB_THUNK(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         SUB_THINK(this, SUB_CalcMoveDone);
198
199         switch(tspeedtype)
200         {
201                 default:
202                 case TSPEED_START:
203                         traveltime = 2 * vlen(tcontrol - this.SUB_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.SUB_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.SUB_VELOCITY = '0 0 0';
219                 this.SUB_NEXTTHINK = this.SUB_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.SUB_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 = SUB_THUNK(this);
234
235         // the thinking is now done by the controller
236         SUB_THINK(this, SUB_NullThink); // for PushMove
237         this.SUB_NEXTTHINK = this.SUB_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         SUB_THINK(this, SUB_CalcMoveDone);
254
255         if (tdest == this.SUB_ORIGIN)
256         {
257                 this.SUB_VELOCITY = '0 0 0';
258                 this.SUB_NEXTTHINK = this.SUB_LTIME + 0.1;
259                 return;
260         }
261
262         delta = tdest - this.SUB_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.SUB_VELOCITY = delta * (1/traveltime);     // QuakeC doesn't allow vector/float division
284                 this.SUB_NEXTTHINK = this.SUB_LTIME + traveltime;
285                 return;
286         }
287
288         // now just run like a bezier curve...
289         SUB_CalcMove_Bezier(this, (this.SUB_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.SUB_AVELOCITY and this.SUB_NEXTTHINK to reach destangle from
302 this.angles rotating
303
304 The calling function should make sure this.SUB_THINK 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.SUB_AVELOCITY = '0 0 0';
312         this.SUB_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         vector  delta;
321         float   traveltime;
322
323         if (!tspeed)
324                 objerror (this, "No speed is defined!");
325
326         // take the shortest distance for the angles
327         this.angles_x -= 360 * floor((this.angles_x - destangle_x) / 360 + 0.5);
328         this.angles_y -= 360 * floor((this.angles_y - destangle_y) / 360 + 0.5);
329         this.angles_z -= 360 * floor((this.angles_z - destangle_z) / 360 + 0.5);
330         delta = destangle - this.angles;
331
332         switch(tspeedtype)
333         {
334                 default:
335                 case TSPEED_START:
336                 case TSPEED_END:
337                 case TSPEED_LINEAR:
338                         traveltime = vlen (delta) / tspeed;
339                         break;
340                 case TSPEED_TIME:
341                         traveltime = tspeed;
342                         break;
343         }
344
345         this.think1 = func;
346         this.finalangle = destangle;
347         SUB_THINK(this, SUB_CalcAngleMoveDone);
348
349         if (traveltime < 0.1)
350         {
351                 this.SUB_AVELOCITY = '0 0 0';
352                 this.SUB_NEXTTHINK = this.SUB_LTIME + 0.1;
353                 return;
354         }
355
356         this.SUB_AVELOCITY = delta * (1 / traveltime);
357         this.SUB_NEXTTHINK = this.SUB_LTIME + traveltime;
358 }
359
360 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void(entity this) func)
361 {
362         SUB_CalcAngleMove (ent, destangle, tspeedtype, tspeed, func);
363 }