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