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