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