]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/g_subs.qc
factor the ballistic bullet tracing into a separate inverted traceline function
[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 void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
180 {
181         vector  delta;
182         float   traveltime;
183
184         if (!tspeed)
185                 objerror ("No speed is defined!");
186
187         delta = destangle - self.angles;
188         traveltime = vlen (delta) / tspeed;
189
190         self.avelocity = delta * (1 / traveltime);
191
192         self.think1 = func;
193         self.finalangle = destangle;
194
195         self.think = SUB_CalcAngleMoveDone;
196         self.nextthink = self.ltime + traveltime;
197 }
198
199 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
200 {
201         entity  oldself;
202
203         oldself = self;
204         self = ent;
205
206         SUB_CalcAngleMove (destangle, tspeed, func);
207
208         self = oldself;
209 }
210
211 /*
212 ==================
213 main
214
215 unused but required by the engine
216 ==================
217 */
218 void main (void)
219 {
220
221 }
222
223 // Misc
224
225 /*
226 ==================
227 traceline_antilag
228
229 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
230 Additionally it moves players back into the past before the trace and restores them afterward.
231 ==================
232 */
233 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
234 {
235         local entity player;
236         local float oldsolid;
237
238         // check whether antilagged traces are enabled
239         if (lag < 0.001)
240                 lag = 0;
241         if (clienttype(forent) != CLIENTTYPE_REAL)
242                 lag = 0; // only antilag for clients
243
244         // change shooter to SOLID_BBOX so the shot can hit corpses
245         oldsolid = source.dphitcontentsmask;
246         source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
247
248         if (lag)
249         {
250                 // take players back into the past
251                 player = player_list;
252                 while (player)
253                 {
254                         antilag_takeback(player, time - lag);
255                         player = player.nextplayer;
256                 }
257         }
258
259         // do the trace
260         traceline (v1, v2, nomonst, forent);
261
262         // restore players to current positions
263         if (lag)
264         {
265                 player = player_list;
266                 while (player)
267                 {
268                         antilag_restore(player);
269                         player = player.nextplayer;
270                 }
271         }
272
273         // restore shooter solid type
274         source.dphitcontentsmask = oldsolid;
275 }
276 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
277 {
278         if (cvar("g_antilag") != 2)
279                 lag = 0;
280         traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
281 }
282
283 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
284 {
285         vector pos, dir, t;
286         float nudge;
287
288         //nudge = 2 * cvar("collision_impactnudge"); // why not?
289         nudge = 0.5;
290
291         dir = normalize(v2 - v1);
292
293         pos = v1 + dir * nudge;
294
295         for(;;)
296         {
297                 if((pos - v1) * dir >= (v2 - v1) * dir)
298                 {
299                         // went too far
300                         trace_fraction = 1;
301                         return;
302                 }
303
304                 traceline(pos, v2, nomonsters, forent);
305
306                 if(trace_startsolid)
307                 {
308                         // we started inside solid.
309                         // then trace from endpos to pos
310                         t = trace_endpos;
311                         traceline(t, pos, nomonsters, forent);
312                         if(trace_startsolid)
313                         {
314                                 // t is inside solid? bad
315                                 // force advance, then
316                                 pos = pos + dir * nudge;
317                         }
318                         else
319                         {
320                                 // we actually LEFT solid!
321                                 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
322                                 return;
323                         }
324                 }
325                 else
326                 {
327                         // pos is outside solid?!? but why?!? never mind, just return it.
328                         trace_endpos = pos;
329                         trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
330                         return;
331                 }
332         }
333 }
334
335 /*
336 ==================
337 findbetterlocation
338
339 Returns a point at least 12 units away from walls
340 (useful for explosion animations, although the blast is performed where it really happened)
341 Ripped from DPMod
342 ==================
343 */
344 vector findbetterlocation (vector org, float mindist)
345 {
346         vector  loc;
347         vector vec;
348         float c;
349
350         vec = mindist * '1 0 0';
351         c = 0;
352         while (c < 6)
353         {
354                 traceline (org, org + vec, TRUE, world);
355                 vec = vec * -1;
356                 if (trace_fraction < 1)
357                 {
358                         loc = trace_endpos;
359                         traceline (loc, loc + vec, TRUE, world);
360                         if (trace_fraction >= 1)
361                                 org = loc + vec;
362                 }
363                 if (c & 1)
364                 {
365                         vec_z = vec_y;
366                         vec_y = vec_x;
367                         vec_x = vec_z;
368                 }
369                 c = c + 1;
370         }
371
372         return org;
373 }
374
375 /*
376 ==================
377 crandom
378
379 Returns a random number between -1.0 and 1.0
380 ==================
381 */
382 float crandom (void)
383 {
384         return 2 * (random () - 0.5);
385 }
386
387 /*
388 ==================
389 Angc used for animations
390 ==================
391 */
392
393
394 float angc (float a1, float a2)
395 {
396         float   a;
397
398         while (a1 > 180)
399                 a1 = a1 - 360;
400         while (a1 < -179)
401                 a1 = a1 + 360;
402
403         while (a2 > 180)
404                 a2 = a2 - 360;
405         while (a2 < -179)
406                 a2 = a2 + 360;
407
408         a = a1 - a2;
409         while (a > 180)
410                 a = a - 360;
411         while (a < -179)
412                 a = a + 360;
413
414         return a;
415 }
416
417 .string lodtarget1;
418 .string lodtarget2;
419 .string lodmodel1;
420 .string lodmodel2;
421 .float lodmodelindex0;
422 .float lodmodelindex1;
423 .float lodmodelindex2;
424 .float loddistance1;
425 .float loddistance2;
426
427 vector NearestPointOnBox(entity box, vector org);
428 float LOD_customize()
429 {
430         float d;
431
432         if(cvar("loddebug"))
433         {
434                 d = cvar("loddebug");
435                 if(d == 1)
436                         self.modelindex = self.lodmodelindex0;
437                 else if(d == 2)
438                         self.modelindex = self.lodmodelindex1;
439                 else // if(d == 3)
440                         self.modelindex = self.lodmodelindex2;
441                 return TRUE;
442         }
443
444         // TODO csqc network this so it only gets sent once
445         d = vlen(NearestPointOnBox(self, other.origin) - other.origin);
446         if(d < self.loddistance1)
447                 self.modelindex = self.lodmodelindex0;
448         else if(!self.lodmodelindex2 || d < self.loddistance2)
449                 self.modelindex = self.lodmodelindex1;
450         else
451                 self.modelindex = self.lodmodelindex2;
452
453         return TRUE;
454 }
455
456 void LOD_uncustomize()
457 {
458         self.modelindex = self.lodmodelindex0;
459 }
460
461 void LODmodel_attach()
462 {
463         entity e;
464
465         if(!self.loddistance1)
466                 self.loddistance1 = 1000;
467         if(!self.loddistance2)
468                 self.loddistance2 = 2000;
469         self.lodmodelindex0 = self.modelindex;
470
471         if(self.lodtarget1 != "")
472         {
473                 e = find(world, targetname, self.lodtarget1);
474                 if(e)
475                 {
476                         self.lodmodel1 = e.model;
477                         remove(e);
478                 }
479         }
480         if(self.lodtarget2 != "")
481         {
482                 e = find(world, targetname, self.lodtarget2);
483                 if(e)
484                 {
485                         self.lodmodel2 = e.model;
486                         remove(e);
487                 }
488         }
489
490         if(self.lodmodel1 != "")
491         {
492                 vector mi, ma;
493                 mi = self.mins;
494                 ma = self.maxs;
495
496                 precache_model(self.lodmodel1);
497                 setmodel(self, self.lodmodel1);
498                 self.lodmodelindex1 = self.modelindex;
499
500                 if(self.lodmodel2 != "")
501                 {
502                         precache_model(self.lodmodel2);
503                         setmodel(self, self.lodmodel2);
504                         self.lodmodelindex2 = self.modelindex;
505                 }
506
507                 self.modelindex = self.lodmodelindex0;
508                 setsize(self, mi, ma);
509         }
510
511         if(self.lodmodelindex1)
512                 SetCustomizer(self, LOD_customize, LOD_uncustomize);
513 }
514
515 void SetBrushEntityModel()
516 {
517         if(self.model != "")
518         {
519                 precache_model(self.model);
520                 setmodel(self, self.model); // no precision needed
521                 InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
522         }
523         setorigin(self, self.origin);
524         if(self.scale)
525                 setsize(self, self.mins * self.scale, self.maxs * self.scale);
526         else
527                 setsize(self, self.mins, self.maxs);
528 }
529
530 /*
531 ================
532 InitTrigger
533 ================
534 */
535
536 void SetMovedir()
537 {
538         if (self.movedir != '0 0 0')
539                 self.movedir = normalize(self.movedir);
540         else
541         {
542                 if (self.angles == '0 -1 0')
543                         self.movedir = '0 0 1';
544                 else if (self.angles == '0 -2 0')
545                         self.movedir = '0 0 -1';
546                 else
547                 {
548                         makevectors (self.angles);
549                         self.movedir = v_forward;
550                 }
551         }
552
553         self.angles = '0 0 0';
554 };
555
556 void InitTrigger()
557 {
558 // trigger angles are used for one-way touches.  An angle of 0 is assumed
559 // to mean no restrictions, so use a yaw of 360 instead.
560         if (self.movedir == '0 0 0')
561         if (self.angles != '0 0 0')
562                 SetMovedir ();
563         self.solid = SOLID_TRIGGER;
564         SetBrushEntityModel();
565         self.movetype = MOVETYPE_NONE;
566         self.modelindex = 0;
567         self.model = "";
568 };
569
570 void InitSolidBSPTrigger()
571 {
572 // trigger angles are used for one-way touches.  An angle of 0 is assumed
573 // to mean no restrictions, so use a yaw of 360 instead.
574         if (self.movedir == '0 0 0')
575         if (self.angles != '0 0 0')
576                 SetMovedir ();
577         self.solid = SOLID_BSP;
578         SetBrushEntityModel();
579         self.movetype = MOVETYPE_PUSH;
580 //      self.modelindex = 0;
581         self.model = "";
582 };
583
584 void InitMovingBrushTrigger()
585 {
586 // trigger angles are used for one-way touches.  An angle of 0 is assumed
587 // to mean no restrictions, so use a yaw of 360 instead.
588         self.solid = SOLID_BSP;
589         SetBrushEntityModel();
590         self.movetype = MOVETYPE_PUSH;
591 };