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