]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/triggers/subs.qc
Merge branch 'master' into Mario/vaporizer_damage
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / triggers / subs.qc
1 void SUB_NullThink(void) { }
2
3 void()  SUB_CalcMoveDone;
4 void() SUB_CalcAngleMoveDone;
5 //void() SUB_UseTargets;
6
7 /*
8 ==================
9 SUB_Friction
10
11 Applies some friction to self
12 ==================
13 */
14 .float friction;
15 void SUB_Friction (void)
16 {
17         self.SUB_NEXTTHINK = time;
18         if(self.SUB_FLAGS & FL_ONGROUND)
19                 self.SUB_VELOCITY = self.SUB_VELOCITY * (1 - frametime * self.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                 remove (ent);
45         }
46 }
47
48 void SUB_SetFade_Think (void)
49 {
50         if(self.alpha == 0)
51                 self.alpha = 1;
52         self.SUB_THINK = SUB_SetFade_Think;
53         self.SUB_NEXTTHINK = time;
54         self.alpha -= frametime * self.fade_rate;
55         if (self.alpha < 0.01)
56                 SUB_VanishOrRemove(self);
57         else
58                 self.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         ent.SUB_THINK = SUB_SetFade_Think;
72         ent.SUB_NEXTTHINK = when;
73 }
74
75 /*
76 =============
77 SUB_CalcMove
78
79 calculate self.SUB_VELOCITY and self.SUB_NEXTTHINK to reach dest from
80 self.SUB_ORIGIN traveling at speed
81 ===============
82 */
83 void SUB_CalcMoveDone (void)
84 {
85         // After moving, set origin to exact final destination
86
87         SUB_SETORIGIN (self, self.finaldest);
88         self.SUB_VELOCITY = '0 0 0';
89         self.SUB_NEXTTHINK = -1;
90         if (self.think1)
91                 self.think1 ();
92 }
93
94 .float platmovetype_turn;
95 void SUB_CalcMove_controller_think (void)
96 {
97         entity oldself;
98         float traveltime;
99         float phasepos;
100         float nexttick;
101         vector delta;
102         vector delta2;
103         vector veloc;
104         vector angloc;
105         vector nextpos;
106         delta = self.destvec;
107         delta2 = self.destvec2;
108         if(time < self.animstate_endtime)
109         {
110                 nexttick = time + PHYS_INPUT_FRAMETIME;
111
112                 traveltime = self.animstate_endtime - self.animstate_starttime;
113                 phasepos = (nexttick - self.animstate_starttime) / traveltime; // range: [0, 1]
114                 phasepos = cubic_speedfunc(self.platmovetype_start, self.platmovetype_end, phasepos);
115                 nextpos = self.origin + (delta * phasepos) + (delta2 * phasepos * phasepos);
116                 // derivative: delta + 2 * delta2 * phasepos (e.g. for angle positioning)
117
118                 if(self.owner.platmovetype_turn)
119                 {
120                         vector destangle;
121                         destangle = delta + 2 * delta2 * phasepos;
122                         destangle = vectoangles(destangle);
123                         destangle_x = -destangle_x; // flip up / down orientation
124
125                         // take the shortest distance for the angles
126                         SUB_ANGLES(self.owner)_x -= 360 * floor((SUB_ANGLES(self.owner)_x - destangle_x) / 360 + 0.5);
127                         SUB_ANGLES(self.owner)_y -= 360 * floor((SUB_ANGLES(self.owner)_y - destangle_y) / 360 + 0.5);
128                         SUB_ANGLES(self.owner)_z -= 360 * floor((SUB_ANGLES(self.owner)_z - destangle_z) / 360 + 0.5);
129                         angloc = destangle - SUB_ANGLES(self.owner);
130                         angloc = angloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame
131                         self.owner.SUB_AVELOCITY = angloc;
132                 }
133                 if(nexttick < self.animstate_endtime)
134                         veloc = nextpos - self.owner.SUB_ORIGIN;
135                 else
136                         veloc = self.finaldest - self.owner.SUB_ORIGIN;
137                 veloc = veloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame
138
139                 self.owner.SUB_VELOCITY = veloc;
140                 self.nextthink = nexttick;
141         }
142         else
143         {
144                 // derivative: delta + 2 * delta2 (e.g. for angle positioning)
145                 oldself = self;
146                 self.owner.SUB_THINK = self.think1;
147                 self = self.owner;
148                 remove(oldself);
149                 self.SUB_THINK();
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 (vector tcontrol, vector tdest, float tspeedtype, float tspeed, void() func)
188 {
189         float   traveltime;
190         entity controller;
191
192         if (!tspeed)
193                 objerror ("No speed is defined!");
194
195         self.think1 = func;
196         self.finaldest = tdest;
197         self.SUB_THINK = SUB_CalcMoveDone;
198
199         switch(tspeedtype)
200         {
201                 default:
202                 case TSPEED_START:
203                         traveltime = 2 * vlen(tcontrol - self.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 - self.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                 self.SUB_VELOCITY = '0 0 0';
219                 self.SUB_NEXTTHINK = self.SUB_LTIME + 0.1;
220                 return;
221         }
222
223         controller = spawn();
224         controller.classname = "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         self = controller;
242         self.think();
243         self = self.owner;
244 }
245
246 void SUB_CalcMove (vector tdest, float tspeedtype, float tspeed, void() func)
247 {
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 {
297         entity  oldself;
298
299         oldself = self;
300         self = ent;
301
302         SUB_CalcMove (tdest, tspeedtype, tspeed, func);
303
304         self = oldself;
305 }
306
307 /*
308 =============
309 SUB_CalcAngleMove
310
311 calculate self.SUB_AVELOCITY and self.SUB_NEXTTHINK to reach destangle from
312 self.angles rotating
313
314 The calling function should make sure self.SUB_THINK is valid
315 ===============
316 */
317 void SUB_CalcAngleMoveDone (void)
318 {
319         // After rotating, set angle to exact final angle
320         self.angles = self.finalangle;
321         self.SUB_AVELOCITY = '0 0 0';
322         self.SUB_NEXTTHINK = -1;
323         if (self.think1)
324                 self.think1 ();
325 }
326
327 // FIXME: I fixed this function only for rotation around the main axes
328 void SUB_CalcAngleMove (vector destangle, float tspeedtype, float tspeed, void() func)
329 {
330         vector  delta;
331         float   traveltime;
332
333         if (!tspeed)
334                 objerror ("No speed is defined!");
335
336         // take the shortest distance for the angles
337         self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
338         self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
339         self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
340         delta = destangle - self.angles;
341
342         switch(tspeedtype)
343         {
344                 default:
345                 case TSPEED_START:
346                 case TSPEED_END:
347                 case TSPEED_LINEAR:
348                         traveltime = vlen (delta) / tspeed;
349                         break;
350                 case TSPEED_TIME:
351                         traveltime = tspeed;
352                         break;
353         }
354
355         self.think1 = func;
356         self.finalangle = destangle;
357         self.SUB_THINK = SUB_CalcAngleMoveDone;
358
359         if (traveltime < 0.1)
360         {
361                 self.SUB_AVELOCITY = '0 0 0';
362                 self.SUB_NEXTTHINK = self.SUB_LTIME + 0.1;
363                 return;
364         }
365
366         self.SUB_AVELOCITY = delta * (1 / traveltime);
367         self.SUB_NEXTTHINK = self.SUB_LTIME + traveltime;
368 }
369
370 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void() func)
371 {
372         entity  oldself;
373
374         oldself = self;
375         self = ent;
376
377         SUB_CalcAngleMove (destangle, tspeedtype, tspeed, func);
378
379         self = oldself;
380 }