]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/triggers/subs.qc
take3: format 903 files
[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 ==================
25 SUB_VanishOrRemove
26
27 Makes client invisible or removes non-client
28 ==================
29 */
30 void SUB_VanishOrRemove(entity ent)
31 {
32         if (IS_CLIENT(ent)) {
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         } else {
41                 // remove
42                 delete(ent);
43         }
44 }
45
46 void SUB_SetFade_Think(entity this)
47 {
48         if (this.alpha == 0) {
49                 this.alpha = 1;
50         }
51         setthink(this, SUB_SetFade_Think);
52         this.nextthink = time;
53         this.alpha -= frametime * this.fade_rate;
54         if (this.alpha < 0.01) {
55                 SUB_VanishOrRemove(this);
56         } else {
57                 this.nextthink = time;
58         }
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
95 .float platmovetype_turn;
96 void SUB_CalcMove_controller_think(entity this)
97 {
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 = this.destvec;
107         delta2 = this.destvec2;
108         if (time < this.animstate_endtime) {
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                         vector destangle;
119                         destangle = delta + 2 * delta2 * phasepos;
120                         destangle = vectoangles(destangle);
121                         destangle_x = -destangle_x; // flip up / down orientation
122
123                         // take the shortest distance for the angles
124                         vector v = this.owner.angles;
125                         v.x -= 360 * floor((v.x - destangle_x) / 360 + 0.5);
126                         v.y -= 360 * floor((v.y - destangle_y) / 360 + 0.5);
127                         v.z -= 360 * floor((v.z - destangle_z) / 360 + 0.5);
128                         this.owner.angles = v;
129                         angloc = destangle - this.owner.angles;
130                         angloc = angloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame
131                         this.owner.avelocity = angloc;
132                 }
133                 if (nexttick < this.animstate_endtime) {
134                         veloc = nextpos - this.owner.origin;
135                 } else {
136                         veloc = this.finaldest - this.owner.origin;
137                 }
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         } else {
143                 // derivative: delta + 2 * delta2 (e.g. for angle positioning)
144                 entity own = this.owner;
145                 setthink(own, this.think1);
146                 delete(this);
147                 getthink(own)(own);
148         }
149 }
150
151 void SUB_CalcMove_controller_setbezier(entity controller, vector org, vector control, vector destin)
152 {
153         // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + destin * t * t
154         // 2 * control * t - 2 * control * t * t + destin * t * t
155         // 2 * control * t + (destin - 2 * control) * t * t
156
157         setorigin(controller, org);
158         control -= org;
159         destin -= org;
160
161         controller.destvec = 2 * control;           // control point
162         controller.destvec2 = destin - 2 * control; // quadratic part required to reach end point
163         // also: initial d/dphasepos origin = 2 * control, final speed = 2 * (destin - control)
164 }
165
166 void SUB_CalcMove_controller_setlinear(entity controller, vector org, vector destin)
167 {
168         // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + destin * t * t
169         // 2 * control * t - 2 * control * t * t + destin * t * t
170         // 2 * control * t + (destin - 2 * control) * t * t
171
172         setorigin(controller, org);
173         destin -= org;
174
175         controller.destvec = destin; // end point
176         controller.destvec2 = '0 0 0';
177 }
178
179 float TSPEED_TIME = -1;
180 float TSPEED_LINEAR = 0;
181 float TSPEED_START = 1;
182 float TSPEED_END = 2;
183 // TODO average too?
184
185 void SUB_CalcMove_Bezier(entity this, vector tcontrol, vector tdest, float tspeedtype, float tspeed, void(entity this) func)
186 {
187         float   traveltime;
188         entity controller;
189
190         if (!tspeed) {
191                 objerror(this, "No speed is defined!");
192         }
193
194         this.think1 = func;
195         this.finaldest = tdest;
196         setthink(this, SUB_CalcMoveDone);
197
198         switch (tspeedtype) {
199                 default:
200                 case TSPEED_START:
201                         traveltime = 2 * vlen(tcontrol - this.origin) / tspeed;
202                         break;
203                 case TSPEED_END:
204                         traveltime = 2 * vlen(tcontrol - tdest)       / tspeed;
205                         break;
206                 case TSPEED_LINEAR:
207                         traveltime = vlen(tdest - this.origin)        / tspeed;
208                         break;
209                 case TSPEED_TIME:
210                         traveltime = tspeed;
211                         break;
212         }
213
214         if (traveltime < 0.1) { // useless anim
215                 this.velocity = '0 0 0';
216                 this.nextthink = this.ltime + 0.1;
217                 return;
218         }
219
220         controller = new(SUB_CalcMove_controller);
221         controller.owner = this;
222         controller.platmovetype = this.platmovetype;
223         controller.platmovetype_start = this.platmovetype_start;
224         controller.platmovetype_end = this.platmovetype_end;
225         SUB_CalcMove_controller_setbezier(controller, this.origin, tcontrol, tdest);
226         controller.finaldest = (tdest + '0 0 0.125'); // where do we want to end? Offset to overshoot a bit.
227         controller.animstate_starttime = time;
228         controller.animstate_endtime = time + traveltime;
229         setthink(controller, SUB_CalcMove_controller_think);
230         controller.think1 = getthink(this);
231
232         // the thinking is now done by the controller
233         setthink(this, SUB_NullThink); // for PushMove
234         this.nextthink = this.ltime + traveltime;
235
236         // invoke controller
237         getthink(controller)(controller);
238 }
239
240 void SUB_CalcMove(entity this, vector tdest, float tspeedtype, float tspeed, void(entity this) func)
241 {
242         vector  delta;
243         float   traveltime;
244
245         if (!tspeed) {
246                 objerror(this, "No speed is defined!");
247         }
248
249         this.think1 = func;
250         this.finaldest = tdest;
251         setthink(this, SUB_CalcMoveDone);
252
253         if (tdest == this.origin) {
254                 this.velocity = '0 0 0';
255                 this.nextthink = this.ltime + 0.1;
256                 return;
257         }
258
259         delta = tdest - this.origin;
260
261         switch (tspeedtype) {
262                 default:
263                 case TSPEED_START:
264                 case TSPEED_END:
265                 case TSPEED_LINEAR:
266                         traveltime = vlen(delta) / tspeed;
267                         break;
268                 case TSPEED_TIME:
269                         traveltime = tspeed;
270                         break;
271         }
272
273         // Very short animations don't really show off the effect
274         // of controlled animation, so let's just use linear movement.
275         // Alternatively entities can choose to specify non-controlled movement.
276         // The only currently implemented alternative movement is linear (value 1)
277         if (traveltime < 0.15 || (this.platmovetype_start == 1 && this.platmovetype_end == 1)) { // is this correct?
278                 this.velocity = delta * (1 / traveltime); // QuakeC doesn't allow vector/float division
279                 this.nextthink = this.ltime + traveltime;
280                 return;
281         }
282
283         // now just run like a bezier curve...
284         SUB_CalcMove_Bezier(this, (this.origin + tdest) * 0.5, tdest, tspeedtype, tspeed, func);
285 }
286
287 void SUB_CalcMoveEnt(entity ent, vector tdest, float tspeedtype, float tspeed, void(entity this) func)
288 {
289         SUB_CalcMove(ent, tdest, tspeedtype, tspeed, func);
290 }
291
292 /*
293 =============
294 SUB_CalcAngleMove
295
296 calculate this.avelocity and this.nextthink to reach destangle from
297 this.angles rotating
298
299 The calling function should make sure this.setthink is valid
300 ===============
301 */
302 void SUB_CalcAngleMoveDone(entity this)
303 {
304         // After rotating, set angle to exact final angle
305         this.angles = this.finalangle;
306         this.avelocity = '0 0 0';
307         this.nextthink = -1;
308         if (this.think1 && this.think1 != SUB_CalcAngleMoveDone) { // avoid endless loops
309                 this.think1(this);
310         }
311 }
312
313 // FIXME: I fixed this function only for rotation around the main axes
314 void SUB_CalcAngleMove(entity this, vector destangle, float tspeedtype, float tspeed, void(entity this) func)
315 {
316         if (!tspeed) {
317                 objerror(this, "No speed is defined!");
318         }
319
320         // take the shortest distance for the angles
321         this.angles_x -= 360 * floor((this.angles_x - destangle_x) / 360 + 0.5);
322         this.angles_y -= 360 * floor((this.angles_y - destangle_y) / 360 + 0.5);
323         this.angles_z -= 360 * floor((this.angles_z - destangle_z) / 360 + 0.5);
324         vector delta = destangle - this.angles;
325         float traveltime;
326
327         switch (tspeedtype) {
328                 default:
329                 case TSPEED_START:
330                 case TSPEED_END:
331                 case TSPEED_LINEAR:
332                         traveltime = vlen(delta) / tspeed;
333                         break;
334                 case TSPEED_TIME:
335                         traveltime = tspeed;
336                         break;
337         }
338
339         this.think1 = func;
340         this.finalangle = destangle;
341         setthink(this, SUB_CalcAngleMoveDone);
342
343         if (traveltime < 0.1) {
344                 this.avelocity = '0 0 0';
345                 this.nextthink = this.ltime + 0.1;
346                 return;
347         }
348
349         this.avelocity = delta * (1 / traveltime);
350         this.nextthink = this.ltime + traveltime;
351 }
352
353 void SUB_CalcAngleMoveEnt(entity ent, vector destangle, float tspeedtype, float tspeed, void(entity this) func)
354 {
355         SUB_CalcAngleMove(ent, destangle, tspeedtype, tspeed, func);
356 }