]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/g_subs.qc
remove all 2.4.2 compatibility code (do not merge this into 2.5 bugfix releases)
[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 traceline_antilag_force (entity source, vector v1, 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         oldsolid = source.dphitcontentsmask;
313         source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
314
315         if (lag)
316         {
317                 // take players back into the past
318                 player = player_list;
319                 while (player)
320                 {
321                         antilag_takeback(player, time - lag);
322                         player = player.nextplayer;
323                 }
324         }
325
326         // do the trace
327         traceline (v1, v2, nomonst, forent);
328
329         // restore players to current positions
330         if (lag)
331         {
332                 player = player_list;
333                 while (player)
334                 {
335                         antilag_restore(player);
336                         player = player.nextplayer;
337                 }
338         }
339
340         // restore shooter solid type
341         source.dphitcontentsmask = oldsolid;
342 }
343 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
344 {
345         if (cvar("g_antilag") != 2)
346                 lag = 0;
347         traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
348 }
349
350 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking
351 {
352         vector pos, dir, t;
353         float nudge;
354
355         //nudge = 2 * cvar("collision_impactnudge"); // why not?
356         nudge = 0.5;
357
358         dir = normalize(v2 - v1);
359
360         pos = v1 + dir * nudge;
361
362         float c;
363         c = 0;
364
365         for(;;)
366         {
367                 if((pos - v1) * dir >= (v2 - v1) * dir)
368                 {
369                         // went too far
370                         trace_fraction = 1;
371                         trace_endpos = v2;
372                         return c;
373                 }
374
375                 tracebox(pos, mi, ma, v2, nomonsters, forent);
376                 ++c;
377
378                 if(c == 50)
379                 {
380                         dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
381                         dprint("  Nudging gets us nowhere at ", vtos(pos), "\n");
382                         dprint("  trace_endpos is ", vtos(trace_endpos), "\n");
383                         dprint("  trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
384                 }
385
386                 if(trace_startsolid)
387                 {
388                         // we started inside solid.
389                         // then trace from endpos to pos
390                         t = trace_endpos;
391                         tracebox(t, mi, ma, pos, nomonsters, forent);
392                         ++c;
393                         if(trace_startsolid)
394                         {
395                                 // t is still inside solid? bad
396                                 // force advance, then, and retry
397                                 pos = t + dir * nudge;
398                         }
399                         else
400                         {
401                                 // we actually LEFT solid!
402                                 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
403                                 return c;
404                         }
405                 }
406                 else
407                 {
408                         // pos is outside solid?!? but why?!? never mind, just return it.
409                         trace_endpos = pos;
410                         trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
411                         return c;
412                 }
413         }
414 }
415
416 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
417 {
418 #if 0
419         vector pos, dir, t;
420         float nudge;
421
422         //nudge = 2 * cvar("collision_impactnudge"); // why not?
423         nudge = 0.5;
424
425         dir = normalize(v2 - v1);
426
427         pos = v1 + dir * nudge;
428
429         for(;;)
430         {
431                 if((pos - v1) * dir >= (v2 - v1) * dir)
432                 {
433                         // went too far
434                         trace_fraction = 1;
435                         return;
436                 }
437
438                 traceline(pos, v2, nomonsters, forent);
439
440                 if(trace_startsolid)
441                 {
442                         // we started inside solid.
443                         // then trace from endpos to pos
444                         t = trace_endpos;
445                         traceline(t, pos, nomonsters, forent);
446                         if(trace_startsolid)
447                         {
448                                 // t is inside solid? bad
449                                 // force advance, then
450                                 pos = pos + dir * nudge;
451                         }
452                         else
453                         {
454                                 // we actually LEFT solid!
455                                 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
456                                 return;
457                         }
458                 }
459                 else
460                 {
461                         // pos is outside solid?!? but why?!? never mind, just return it.
462                         trace_endpos = pos;
463                         trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
464                         return;
465                 }
466         }
467 #else
468         tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);
469 }
470
471 /*
472 ==================
473 findbetterlocation
474
475 Returns a point at least 12 units away from walls
476 (useful for explosion animations, although the blast is performed where it really happened)
477 Ripped from DPMod
478 ==================
479 */
480 vector findbetterlocation (vector org, float mindist)
481 {
482         vector  loc;
483         vector vec;
484         float c, h;
485
486         vec = mindist * '1 0 0';
487         c = 0;
488         while (c < 6)
489         {
490                 traceline (org, org + vec, TRUE, world);
491                 vec = vec * -1;
492                 if (trace_fraction < 1)
493                 {
494                         loc = trace_endpos;
495                         traceline (loc, loc + vec, TRUE, world);
496                         if (trace_fraction >= 1)
497                                 org = loc + vec;
498                 }
499                 if (c & 1)
500                 {
501                         h = vec_y;
502                         vec_y = vec_x;
503                         vec_x = vec_z;
504                         vec_z = h;
505                 }
506                 c = c + 1;
507         }
508
509         return org;
510 }
511
512 /*
513 ==================
514 crandom
515
516 Returns a random number between -1.0 and 1.0
517 ==================
518 */
519 float crandom (void)
520 {
521         return 2 * (random () - 0.5);
522 }
523
524 /*
525 ==================
526 Angc used for animations
527 ==================
528 */
529
530
531 float angc (float a1, float a2)
532 {
533         float   a;
534
535         while (a1 > 180)
536                 a1 = a1 - 360;
537         while (a1 < -179)
538                 a1 = a1 + 360;
539
540         while (a2 > 180)
541                 a2 = a2 - 360;
542         while (a2 < -179)
543                 a2 = a2 + 360;
544
545         a = a1 - a2;
546         while (a > 180)
547                 a = a - 360;
548         while (a < -179)
549                 a = a + 360;
550
551         return a;
552 }
553
554 .string lodtarget1;
555 .string lodtarget2;
556 .string lodmodel1;
557 .string lodmodel2;
558 .float lodmodelindex0;
559 .float lodmodelindex1;
560 .float lodmodelindex2;
561 .float loddistance1;
562 .float loddistance2;
563
564 vector NearestPointOnBox(entity box, vector org);
565 float LOD_customize()
566 {
567         float d;
568
569         if(cvar("loddebug"))
570         {
571                 d = cvar("loddebug");
572                 if(d == 1)
573                         self.modelindex = self.lodmodelindex0;
574                 else if(d == 2)
575                         self.modelindex = self.lodmodelindex1;
576                 else // if(d == 3)
577                         self.modelindex = self.lodmodelindex2;
578                 return TRUE;
579         }
580
581         // TODO csqc network this so it only gets sent once
582         d = vlen(NearestPointOnBox(self, other.origin) - other.origin);
583         if(d < self.loddistance1)
584                 self.modelindex = self.lodmodelindex0;
585         else if(!self.lodmodelindex2 || d < self.loddistance2)
586                 self.modelindex = self.lodmodelindex1;
587         else
588                 self.modelindex = self.lodmodelindex2;
589
590         return TRUE;
591 }
592
593 void LOD_uncustomize()
594 {
595         self.modelindex = self.lodmodelindex0;
596 }
597
598 void LODmodel_attach()
599 {
600         entity e;
601
602         if(!self.loddistance1)
603                 self.loddistance1 = 1000;
604         if(!self.loddistance2)
605                 self.loddistance2 = 2000;
606         self.lodmodelindex0 = self.modelindex;
607
608         if(self.lodtarget1 != "")
609         {
610                 e = find(world, targetname, self.lodtarget1);
611                 if(e)
612                 {
613                         self.lodmodel1 = e.model;
614                         remove(e);
615                 }
616         }
617         if(self.lodtarget2 != "")
618         {
619                 e = find(world, targetname, self.lodtarget2);
620                 if(e)
621                 {
622                         self.lodmodel2 = e.model;
623                         remove(e);
624                 }
625         }
626
627         if(cvar("loddebug") < 0)
628         {
629                 self.lodmodel1 = self.lodmodel2 = ""; // don't even initialize
630         }
631
632         if(self.lodmodel1 != "")
633         {
634                 vector mi, ma;
635                 mi = self.mins;
636                 ma = self.maxs;
637
638                 precache_model(self.lodmodel1);
639                 setmodel(self, self.lodmodel1);
640                 self.lodmodelindex1 = self.modelindex;
641
642                 if(self.lodmodel2 != "")
643                 {
644                         precache_model(self.lodmodel2);
645                         setmodel(self, self.lodmodel2);
646                         self.lodmodelindex2 = self.modelindex;
647                 }
648
649                 self.modelindex = self.lodmodelindex0;
650                 setsize(self, mi, ma);
651         }
652
653         if(self.lodmodelindex1)
654                 SetCustomizer(self, LOD_customize, LOD_uncustomize);
655 }
656
657 void SetBrushEntityModel()
658 {
659         if(self.model != "")
660         {
661                 precache_model(self.model);
662                 setmodel(self, self.model); // no precision needed
663                 InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
664         }
665         setorigin(self, self.origin);
666         if(self.scale)
667                 setsize(self, self.mins * self.scale, self.maxs * self.scale);
668         else
669                 setsize(self, self.mins, self.maxs);
670 }
671
672 void SetBrushEntityModelNoLOD()
673 {
674         if(self.model != "")
675         {
676                 precache_model(self.model);
677                 setmodel(self, self.model); // no precision needed
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 /*
687 ================
688 InitTrigger
689 ================
690 */
691
692 void SetMovedir()
693 {
694         if (self.movedir != '0 0 0')
695                 self.movedir = normalize(self.movedir);
696         else
697         {
698                 makevectors (self.angles);
699                 self.movedir = v_forward;
700         }
701
702         self.angles = '0 0 0';
703 };
704
705 void InitTrigger()
706 {
707 // trigger angles are used for one-way touches.  An angle of 0 is assumed
708 // to mean no restrictions, so use a yaw of 360 instead.
709         if (self.movedir == '0 0 0')
710         if (self.angles != '0 0 0')
711                 SetMovedir ();
712         self.solid = SOLID_TRIGGER;
713         SetBrushEntityModel();
714         self.movetype = MOVETYPE_NONE;
715         self.modelindex = 0;
716         self.model = "";
717 };
718
719 void InitSolidBSPTrigger()
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_BSP;
727         SetBrushEntityModel();
728         self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
729 //      self.modelindex = 0;
730         self.model = "";
731 };
732
733 float InitMovingBrushTrigger()
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         self.solid = SOLID_BSP;
738         SetBrushEntityModel();
739         self.movetype = MOVETYPE_PUSH;
740         if(self.modelindex == 0)
741         {
742                 objerror("InitMovingBrushTrigger: no brushes found!");
743                 return 0;
744         }
745         return 1;
746 };