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