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