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