Use Erebus animations for the nex fox models, which are a lot prettier. Also fix...
[voretournament/voretournament.git] / data / qcsrc / server / g_subs.qc
1 void SUB_Null() {};\r
2 float SUB_True() { return 1; }\r
3 float SUB_False() { return 0; }\r
4 \r
5 void(vector destangle, float tspeed, void() func) SUB_CalcAngleMove;\r
6 void()  SUB_CalcMoveDone;\r
7 void() SUB_CalcAngleMoveDone;\r
8 //void() SUB_UseTargets;\r
9 void() SUB_Remove;\r
10 \r
11 void spawnfunc_info_null (void)\r
12 {\r
13         remove(self);\r
14         // if anything breaks, tell the mapper to fix his map! info_null is meant to remove itself immediately.\r
15 }\r
16 \r
17 void setanim(entity e, vector anim, float looping, float override, float restart)\r
18 {\r
19         if (anim_x == e.animstate_startframe)\r
20         if (anim_y == e.animstate_numframes)\r
21         if (anim_z == e.animstate_framerate)\r
22         {\r
23                 if(restart)\r
24                 {\r
25                         if(restart > 0)\r
26                         if(anim_y == 1) // ZYM animation\r
27                                 BITXOR_ASSIGN(e.effects, EF_RESTARTANIM_BIT);\r
28                 }\r
29                 else\r
30                         return;\r
31         }\r
32         e.animstate_startframe = anim_x;\r
33         e.animstate_numframes = anim_y;\r
34         e.animstate_framerate = anim_z;\r
35         e.animstate_starttime = servertime - 0.1 * serverframetime; // shift it a little bit into the past to prevent float inaccuracy hiccups\r
36         e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;\r
37         e.animstate_looping = looping;\r
38         e.animstate_override = override;\r
39         e.frame = e.animstate_startframe;\r
40 };\r
41 \r
42 void updateanim(entity e)\r
43 {\r
44         if (time >= e.animstate_endtime)\r
45         {\r
46                 if (e.animstate_looping)\r
47                 {\r
48                         e.animstate_starttime = e.animstate_endtime;\r
49                         e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;\r
50                 }\r
51                 e.animstate_override = FALSE;\r
52         }\r
53         e.frame = e.animstate_startframe + bound(0, (time - e.animstate_starttime) * e.animstate_framerate, e.animstate_numframes - 1);\r
54         //print(ftos(time), " -> ", ftos(e.frame), "\n");\r
55 };\r
56 \r
57 vector animfixfps(entity e, vector a)\r
58 {\r
59         // multi-frame anim: keep as-is\r
60         if(a_y == 1)\r
61         {\r
62                 float dur;\r
63                 dur = frameduration(e.modelindex, a_x);\r
64                 if(dur > 0)\r
65                         a_z = 1.0 / dur;\r
66         }\r
67         return a;\r
68 }\r
69 \r
70 /*\r
71 ==================\r
72 SUB_Remove\r
73 \r
74 Remove self\r
75 ==================\r
76 */\r
77 void SUB_Remove (void)\r
78 {\r
79         remove (self);\r
80 }\r
81 \r
82 /*\r
83 ==================\r
84 SUB_Friction\r
85 \r
86 Applies some friction to self\r
87 ==================\r
88 */\r
89 .float friction;\r
90 void SUB_Friction (void)\r
91 {\r
92         self.nextthink = time;\r
93         if(self.flags & FL_ONGROUND)\r
94                 self.velocity = self.velocity * (1 - frametime * self.friction);\r
95 }\r
96 \r
97 /*\r
98 ==================\r
99 SUB_VanishOrRemove\r
100 \r
101 Makes client invisible or removes non-client\r
102 ==================\r
103 */\r
104 void SUB_VanishOrRemove (entity ent)\r
105 {\r
106         if (ent.flags & FL_CLIENT)\r
107         {\r
108                 // vanish\r
109                 ent.model = "";\r
110                 ent.effects = 0;\r
111                 ent.glow_size = 0;\r
112                 ent.pflags = 0;\r
113         }\r
114         else\r
115         {\r
116                 // remove\r
117                 remove (ent);\r
118         }\r
119 }\r
120 \r
121 void SUB_SetFade_Think (void)\r
122 {\r
123         self.think = SUB_SetFade_Think;\r
124         self.nextthink = self.fade_time;\r
125         self.alpha = 1 - (time - self.fade_time) * self.fade_rate;\r
126         if (self.alpha < 0.01)\r
127                 SUB_VanishOrRemove(self);\r
128         self.alpha = bound(0.01, self.alpha, 1);\r
129 }\r
130 \r
131 /*\r
132 ==================\r
133 SUB_SetFade\r
134 \r
135 Fade 'ent' out when time >= 'when'\r
136 ==================\r
137 */\r
138 void SUB_SetFade (entity ent, float when, float fadetime)\r
139 {\r
140         //if (ent.flags & FL_CLIENT) // && ent.deadflag != DEAD_NO)\r
141         //      return;\r
142         //ent.alpha = 1;\r
143         ent.fade_rate = 1/fadetime;\r
144         ent.fade_time = when;\r
145         ent.think = SUB_SetFade_Think;\r
146         ent.nextthink = when;\r
147 }\r
148 \r
149 /*\r
150 =============\r
151 SUB_CalcMove\r
152 \r
153 calculate self.velocity and self.nextthink to reach dest from\r
154 self.origin traveling at speed\r
155 ===============\r
156 */\r
157 void SUB_CalcMoveDone (void)\r
158 {\r
159         // After moving, set origin to exact final destination\r
160 \r
161         setorigin (self, self.finaldest);\r
162         self.velocity = '0 0 0';\r
163         self.nextthink = -1;\r
164         if (self.think1)\r
165                 self.think1 ();\r
166 }\r
167 \r
168 void SUB_CalcMove (vector tdest, float tspeed, void() func)\r
169 {\r
170         vector  delta;\r
171         float   traveltime;\r
172 \r
173         if (!tspeed)\r
174                 objerror ("No speed is defined!");\r
175 \r
176         self.think1 = func;\r
177         self.finaldest = tdest;\r
178         self.think = SUB_CalcMoveDone;\r
179 \r
180         if (tdest == self.origin)\r
181         {\r
182                 self.velocity = '0 0 0';\r
183                 self.nextthink = self.ltime + 0.1;\r
184                 return;\r
185         }\r
186 \r
187         delta = tdest - self.origin;\r
188         traveltime = vlen (delta) / tspeed;\r
189 \r
190         if (traveltime < 0.1)\r
191         {\r
192                 self.velocity = '0 0 0';\r
193                 self.nextthink = self.ltime + 0.1;\r
194                 return;\r
195         }\r
196 \r
197         self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division\r
198 \r
199         self.nextthink = self.ltime + traveltime;\r
200 }\r
201 \r
202 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)\r
203 {\r
204         entity  oldself;\r
205 \r
206         oldself = self;\r
207         self = ent;\r
208 \r
209         SUB_CalcMove (tdest, tspeed, func);\r
210 \r
211         self = oldself;\r
212 }\r
213 \r
214 /*\r
215 =============\r
216 SUB_CalcAngleMove\r
217 \r
218 calculate self.avelocity and self.nextthink to reach destangle from\r
219 self.angles rotating\r
220 \r
221 The calling function should make sure self.think is valid\r
222 ===============\r
223 */\r
224 void SUB_CalcAngleMoveDone (void)\r
225 {\r
226         // After rotating, set angle to exact final angle\r
227         self.angles = self.finalangle;\r
228         self.avelocity = '0 0 0';\r
229         self.nextthink = -1;\r
230         if (self.think1)\r
231                 self.think1 ();\r
232 }\r
233 \r
234 // FIXME: I fixed this function only for rotation around the main axes\r
235 void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)\r
236 {\r
237         vector  delta;\r
238         float   traveltime;\r
239 \r
240         if (!tspeed)\r
241                 objerror ("No speed is defined!");\r
242 \r
243         // take the shortest distance for the angles\r
244         self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);\r
245         self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);\r
246         self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);\r
247         delta = destangle - self.angles;\r
248         traveltime = vlen (delta) / tspeed;\r
249 \r
250         self.think1 = func;\r
251         self.finalangle = destangle;\r
252         self.think = SUB_CalcAngleMoveDone;\r
253 \r
254         if (traveltime < 0.1)\r
255         {\r
256                 self.avelocity = '0 0 0';\r
257                 self.nextthink = self.ltime + 0.1;\r
258                 return;\r
259         }\r
260 \r
261         self.avelocity = delta * (1 / traveltime);\r
262         self.nextthink = self.ltime + traveltime;\r
263 }\r
264 \r
265 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)\r
266 {\r
267         entity  oldself;\r
268 \r
269         oldself = self;\r
270         self = ent;\r
271 \r
272         SUB_CalcAngleMove (destangle, tspeed, func);\r
273 \r
274         self = oldself;\r
275 }\r
276 \r
277 /*\r
278 ==================\r
279 main\r
280 \r
281 unused but required by the engine\r
282 ==================\r
283 */\r
284 void main (void)\r
285 {\r
286 \r
287 }\r
288 \r
289 // Misc\r
290 \r
291 /*\r
292 ==================\r
293 traceline_antilag\r
294 \r
295 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack\r
296 Additionally it moves players back into the past before the trace and restores them afterward.\r
297 ==================\r
298 */\r
299 void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag, float wz)\r
300 {\r
301         local entity player;\r
302         local float oldsolid;\r
303 \r
304         // check whether antilagged traces are enabled\r
305         if (lag < 0.001)\r
306                 lag = 0;\r
307         if (clienttype(forent) != CLIENTTYPE_REAL)\r
308                 lag = 0; // only antilag for clients\r
309 \r
310         // change shooter to SOLID_BBOX so the shot can hit corpses\r
311         if(source)\r
312         {\r
313                 oldsolid = source.dphitcontentsmask;\r
314                 source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;\r
315         }\r
316 \r
317         if (lag)\r
318         {\r
319                 // take players back into the past\r
320                 player = player_list;\r
321                 while (player)\r
322                 {\r
323                         antilag_takeback(player, time - lag);\r
324                         player = player.nextplayer;\r
325                 }\r
326         }\r
327 \r
328         // do the trace\r
329         if(wz)\r
330                 WarpZone_TraceBox (v1, mi, ma, v2, nomonst, forent);\r
331         else\r
332                 tracebox (v1, mi, ma, v2, nomonst, forent);\r
333 \r
334         // restore players to current positions\r
335         if (lag)\r
336         {\r
337                 player = player_list;\r
338                 while (player)\r
339                 {\r
340                         antilag_restore(player);\r
341                         player = player.nextplayer;\r
342                 }\r
343         }\r
344 \r
345         // restore shooter solid type\r
346         if(source)\r
347                 source.dphitcontentsmask = oldsolid;\r
348 }\r
349 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)\r
350 {\r
351         tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, FALSE);\r
352 }\r
353 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)\r
354 {\r
355         if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)\r
356                 lag = 0;\r
357         traceline_antilag_force(source, v1, v2, nomonst, forent, lag);\r
358 }\r
359 void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)\r
360 {\r
361         if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)\r
362                 lag = 0;\r
363         tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, FALSE);\r
364 }\r
365 void WarpZone_traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)\r
366 {\r
367         tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, TRUE);\r
368 }\r
369 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)\r
370 {\r
371         if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)\r
372                 lag = 0;\r
373         WarpZone_traceline_antilag_force(source, v1, v2, nomonst, forent, lag);\r
374 }\r
375 void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)\r
376 {\r
377         if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)\r
378                 lag = 0;\r
379         tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, TRUE);\r
380 }\r
381 \r
382 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking\r
383 {\r
384         vector pos, dir, t;\r
385         float nudge;\r
386 \r
387         //nudge = 2 * cvar("collision_impactnudge"); // why not?\r
388         nudge = 0.5;\r
389 \r
390         dir = normalize(v2 - v1);\r
391 \r
392         pos = v1 + dir * nudge;\r
393 \r
394         float c;\r
395         c = 0;\r
396 \r
397         for(;;)\r
398         {\r
399                 if((pos - v1) * dir >= (v2 - v1) * dir)\r
400                 {\r
401                         // went too far\r
402                         trace_fraction = 1;\r
403                         trace_endpos = v2;\r
404                         return c;\r
405                 }\r
406 \r
407                 tracebox(pos, mi, ma, v2, nomonsters, forent);\r
408                 ++c;\r
409 \r
410                 if(c == 50)\r
411                 {\r
412                         dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");\r
413                         dprint("  Nudging gets us nowhere at ", vtos(pos), "\n");\r
414                         dprint("  trace_endpos is ", vtos(trace_endpos), "\n");\r
415                         dprint("  trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");\r
416                 }\r
417 \r
418                 if(trace_startsolid)\r
419                 {\r
420                         // we started inside solid.\r
421                         // then trace from endpos to pos\r
422                         t = trace_endpos;\r
423                         tracebox(t, mi, ma, pos, nomonsters, forent);\r
424                         ++c;\r
425                         if(trace_startsolid)\r
426                         {\r
427                                 // t is still inside solid? bad\r
428                                 // force advance, then, and retry\r
429                                 pos = t + dir * nudge;\r
430                         }\r
431                         else\r
432                         {\r
433                                 // we actually LEFT solid!\r
434                                 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);\r
435                                 return c;\r
436                         }\r
437                 }\r
438                 else\r
439                 {\r
440                         // pos is outside solid?!? but why?!? never mind, just return it.\r
441                         trace_endpos = pos;\r
442                         trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);\r
443                         return c;\r
444                 }\r
445         }\r
446 }\r
447 \r
448 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)\r
449 {\r
450 #if 0\r
451         vector pos, dir, t;\r
452         float nudge;\r
453 \r
454         //nudge = 2 * cvar("collision_impactnudge"); // why not?\r
455         nudge = 0.5;\r
456 \r
457         dir = normalize(v2 - v1);\r
458 \r
459         pos = v1 + dir * nudge;\r
460 \r
461         for(;;)\r
462         {\r
463                 if((pos - v1) * dir >= (v2 - v1) * dir)\r
464                 {\r
465                         // went too far\r
466                         trace_fraction = 1;\r
467                         return;\r
468                 }\r
469 \r
470                 traceline(pos, v2, nomonsters, forent);\r
471 \r
472                 if(trace_startsolid)\r
473                 {\r
474                         // we started inside solid.\r
475                         // then trace from endpos to pos\r
476                         t = trace_endpos;\r
477                         traceline(t, pos, nomonsters, forent);\r
478                         if(trace_startsolid)\r
479                         {\r
480                                 // t is inside solid? bad\r
481                                 // force advance, then\r
482                                 pos = pos + dir * nudge;\r
483                         }\r
484                         else\r
485                         {\r
486                                 // we actually LEFT solid!\r
487                                 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);\r
488                                 return;\r
489                         }\r
490                 }\r
491                 else\r
492                 {\r
493                         // pos is outside solid?!? but why?!? never mind, just return it.\r
494                         trace_endpos = pos;\r
495                         trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);\r
496                         return;\r
497                 }\r
498         }\r
499 #else\r
500         tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);\r
501 }\r
502 \r
503 /*\r
504 ==================\r
505 findbetterlocation\r
506 \r
507 Returns a point at least 12 units away from walls\r
508 (useful for explosion animations, although the blast is performed where it really happened)\r
509 Ripped from DPMod\r
510 ==================\r
511 */\r
512 vector findbetterlocation (vector org, float mindist)\r
513 {\r
514         vector  loc;\r
515         vector vec;\r
516         float c, h;\r
517 \r
518         vec = mindist * '1 0 0';\r
519         c = 0;\r
520         while (c < 6)\r
521         {\r
522                 traceline (org, org + vec, TRUE, world);\r
523                 vec = vec * -1;\r
524                 if (trace_fraction < 1)\r
525                 {\r
526                         loc = trace_endpos;\r
527                         traceline (loc, loc + vec, TRUE, world);\r
528                         if (trace_fraction >= 1)\r
529                                 org = loc + vec;\r
530                 }\r
531                 if (c & 1)\r
532                 {\r
533                         h = vec_y;\r
534                         vec_y = vec_x;\r
535                         vec_x = vec_z;\r
536                         vec_z = h;\r
537                 }\r
538                 c = c + 1;\r
539         }\r
540 \r
541         return org;\r
542 }\r
543 \r
544 /*\r
545 ==================\r
546 crandom\r
547 \r
548 Returns a random number between -1.0 and 1.0\r
549 ==================\r
550 */\r
551 float crandom (void)\r
552 {\r
553         return 2 * (random () - 0.5);\r
554 }\r
555 \r
556 /*\r
557 ==================\r
558 Angc used for animations\r
559 ==================\r
560 */\r
561 \r
562 \r
563 float angc (float a1, float a2)\r
564 {\r
565         float   a;\r
566 \r
567         while (a1 > 180)\r
568                 a1 = a1 - 360;\r
569         while (a1 < -179)\r
570                 a1 = a1 + 360;\r
571 \r
572         while (a2 > 180)\r
573                 a2 = a2 - 360;\r
574         while (a2 < -179)\r
575                 a2 = a2 + 360;\r
576 \r
577         a = a1 - a2;\r
578         while (a > 180)\r
579                 a = a - 360;\r
580         while (a < -179)\r
581                 a = a + 360;\r
582 \r
583         return a;\r
584 }\r
585 \r
586 vector NearestPointOnBox(entity box, vector org);\r
587 \r
588 void SetBrushEntityModel()\r
589 {\r
590         if(self.model != "")\r
591         {\r
592                 precache_model(self.model);\r
593                 setmodel(self, self.model); // no precision needed\r
594         }\r
595         setorigin(self, self.origin);\r
596         if(self.scale)\r
597                 setsize(self, self.mins * self.scale, self.maxs * self.scale);\r
598         else\r
599                 setsize(self, self.mins, self.maxs);\r
600 }\r
601 \r
602 void SetBrushEntityModelNoLOD()\r
603 {\r
604         if(self.model != "")\r
605         {\r
606                 precache_model(self.model);\r
607                 setmodel(self, self.model); // no precision needed\r
608         }\r
609         setorigin(self, self.origin);\r
610         if(self.scale)\r
611                 setsize(self, self.mins * self.scale, self.maxs * self.scale);\r
612         else\r
613                 setsize(self, self.mins, self.maxs);\r
614 }\r
615 \r
616 /*\r
617 ================\r
618 InitTrigger\r
619 ================\r
620 */\r
621 \r
622 void SetMovedir()\r
623 {\r
624         if (self.movedir != '0 0 0')\r
625                 self.movedir = normalize(self.movedir);\r
626         else\r
627         {\r
628                 makevectors (self.angles);\r
629                 self.movedir = v_forward;\r
630         }\r
631 \r
632         self.angles = '0 0 0';\r
633 };\r
634 \r
635 void InitTrigger()\r
636 {\r
637 // trigger angles are used for one-way touches.  An angle of 0 is assumed\r
638 // to mean no restrictions, so use a yaw of 360 instead.\r
639         if (self.movedir == '0 0 0')\r
640         if (self.angles != '0 0 0')\r
641                 SetMovedir ();\r
642         self.solid = SOLID_TRIGGER;\r
643         SetBrushEntityModel();\r
644         self.movetype = MOVETYPE_NONE;\r
645         self.modelindex = 0;\r
646         self.model = "";\r
647 };\r
648 \r
649 void InitSolidBSPTrigger()\r
650 {\r
651 // trigger angles are used for one-way touches.  An angle of 0 is assumed\r
652 // to mean no restrictions, so use a yaw of 360 instead.\r
653         if (self.movedir == '0 0 0')\r
654         if (self.angles != '0 0 0')\r
655                 SetMovedir ();\r
656         self.solid = SOLID_BSP;\r
657         SetBrushEntityModel();\r
658         self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0\r
659 //      self.modelindex = 0;\r
660         self.model = "";\r
661 };\r
662 \r
663 float InitMovingBrushTrigger()\r
664 {\r
665 // trigger angles are used for one-way touches.  An angle of 0 is assumed\r
666 // to mean no restrictions, so use a yaw of 360 instead.\r
667         self.solid = SOLID_BSP;\r
668         SetBrushEntityModel();\r
669         self.movetype = MOVETYPE_PUSH;\r
670         if(self.modelindex == 0)\r
671         {\r
672                 objerror("InitMovingBrushTrigger: no brushes found!");\r
673                 return 0;\r
674         }\r
675         return 1;\r
676 };\r