]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/g_subs.qc
do less audio spam for triggers that don't even respond to players
[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 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
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         for(;;)
307         {
308                 if((pos - v1) * dir >= (v2 - v1) * dir)
309                 {
310                         // went too far
311                         trace_fraction = 1;
312                         return;
313                 }
314
315                 traceline(pos, v2, nomonsters, forent);
316
317                 if(trace_startsolid)
318                 {
319                         // we started inside solid.
320                         // then trace from endpos to pos
321                         t = trace_endpos;
322                         traceline(t, pos, nomonsters, forent);
323                         if(trace_startsolid)
324                         {
325                                 // t is inside solid? bad
326                                 // force advance, then
327                                 pos = pos + dir * nudge;
328                         }
329                         else
330                         {
331                                 // we actually LEFT solid!
332                                 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
333                                 return;
334                         }
335                 }
336                 else
337                 {
338                         // pos is outside solid?!? but why?!? never mind, just return it.
339                         trace_endpos = pos;
340                         trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
341                         return;
342                 }
343         }
344 }
345
346 /*
347 ==================
348 findbetterlocation
349
350 Returns a point at least 12 units away from walls
351 (useful for explosion animations, although the blast is performed where it really happened)
352 Ripped from DPMod
353 ==================
354 */
355 vector findbetterlocation (vector org, float mindist)
356 {
357         vector  loc;
358         vector vec;
359         float c;
360
361         vec = mindist * '1 0 0';
362         c = 0;
363         while (c < 6)
364         {
365                 traceline (org, org + vec, TRUE, world);
366                 vec = vec * -1;
367                 if (trace_fraction < 1)
368                 {
369                         loc = trace_endpos;
370                         traceline (loc, loc + vec, TRUE, world);
371                         if (trace_fraction >= 1)
372                                 org = loc + vec;
373                 }
374                 if (c & 1)
375                 {
376                         vec_z = vec_y;
377                         vec_y = vec_x;
378                         vec_x = vec_z;
379                 }
380                 c = c + 1;
381         }
382
383         return org;
384 }
385
386 /*
387 ==================
388 crandom
389
390 Returns a random number between -1.0 and 1.0
391 ==================
392 */
393 float crandom (void)
394 {
395         return 2 * (random () - 0.5);
396 }
397
398 /*
399 ==================
400 Angc used for animations
401 ==================
402 */
403
404
405 float angc (float a1, float a2)
406 {
407         float   a;
408
409         while (a1 > 180)
410                 a1 = a1 - 360;
411         while (a1 < -179)
412                 a1 = a1 + 360;
413
414         while (a2 > 180)
415                 a2 = a2 - 360;
416         while (a2 < -179)
417                 a2 = a2 + 360;
418
419         a = a1 - a2;
420         while (a > 180)
421                 a = a - 360;
422         while (a < -179)
423                 a = a + 360;
424
425         return a;
426 }
427
428 .string lodtarget1;
429 .string lodtarget2;
430 .string lodmodel1;
431 .string lodmodel2;
432 .float lodmodelindex0;
433 .float lodmodelindex1;
434 .float lodmodelindex2;
435 .float loddistance1;
436 .float loddistance2;
437
438 vector NearestPointOnBox(entity box, vector org);
439 float LOD_customize()
440 {
441         float d;
442
443         if(cvar("loddebug"))
444         {
445                 d = cvar("loddebug");
446                 if(d == 1)
447                         self.modelindex = self.lodmodelindex0;
448                 else if(d == 2)
449                         self.modelindex = self.lodmodelindex1;
450                 else // if(d == 3)
451                         self.modelindex = self.lodmodelindex2;
452                 return TRUE;
453         }
454
455         // TODO csqc network this so it only gets sent once
456         d = vlen(NearestPointOnBox(self, other.origin) - other.origin);
457         if(d < self.loddistance1)
458                 self.modelindex = self.lodmodelindex0;
459         else if(!self.lodmodelindex2 || d < self.loddistance2)
460                 self.modelindex = self.lodmodelindex1;
461         else
462                 self.modelindex = self.lodmodelindex2;
463
464         return TRUE;
465 }
466
467 void LOD_uncustomize()
468 {
469         self.modelindex = self.lodmodelindex0;
470 }
471
472 void LODmodel_attach()
473 {
474         entity e;
475
476         if(!self.loddistance1)
477                 self.loddistance1 = 1000;
478         if(!self.loddistance2)
479                 self.loddistance2 = 2000;
480         self.lodmodelindex0 = self.modelindex;
481
482         if(self.lodtarget1 != "")
483         {
484                 e = find(world, targetname, self.lodtarget1);
485                 if(e)
486                 {
487                         self.lodmodel1 = e.model;
488                         remove(e);
489                 }
490         }
491         if(self.lodtarget2 != "")
492         {
493                 e = find(world, targetname, self.lodtarget2);
494                 if(e)
495                 {
496                         self.lodmodel2 = e.model;
497                         remove(e);
498                 }
499         }
500
501         if(self.lodmodel1 != "")
502         {
503                 vector mi, ma;
504                 mi = self.mins;
505                 ma = self.maxs;
506
507                 precache_model(self.lodmodel1);
508                 setmodel(self, self.lodmodel1);
509                 self.lodmodelindex1 = self.modelindex;
510
511                 if(self.lodmodel2 != "")
512                 {
513                         precache_model(self.lodmodel2);
514                         setmodel(self, self.lodmodel2);
515                         self.lodmodelindex2 = self.modelindex;
516                 }
517
518                 self.modelindex = self.lodmodelindex0;
519                 setsize(self, mi, ma);
520         }
521
522         if(self.lodmodelindex1)
523                 SetCustomizer(self, LOD_customize, LOD_uncustomize);
524 }
525
526 void SetBrushEntityModel()
527 {
528         if(self.model != "")
529         {
530                 precache_model(self.model);
531                 setmodel(self, self.model); // no precision needed
532                 InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
533         }
534         setorigin(self, self.origin);
535         if(self.scale)
536                 setsize(self, self.mins * self.scale, self.maxs * self.scale);
537         else
538                 setsize(self, self.mins, self.maxs);
539 }
540
541 /*
542 ================
543 InitTrigger
544 ================
545 */
546
547 void SetMovedir()
548 {
549         if (self.movedir != '0 0 0')
550                 self.movedir = normalize(self.movedir);
551         else
552         {
553                 if (self.angles == '0 -1 0')
554                         self.movedir = '0 0 1';
555                 else if (self.angles == '0 -2 0')
556                         self.movedir = '0 0 -1';
557                 else
558                 {
559                         makevectors (self.angles);
560                         self.movedir = v_forward;
561                 }
562         }
563
564         self.angles = '0 0 0';
565 };
566
567 void InitTrigger()
568 {
569 // trigger angles are used for one-way touches.  An angle of 0 is assumed
570 // to mean no restrictions, so use a yaw of 360 instead.
571         if (self.movedir == '0 0 0')
572         if (self.angles != '0 0 0')
573                 SetMovedir ();
574         self.solid = SOLID_TRIGGER;
575         SetBrushEntityModel();
576         self.movetype = MOVETYPE_NONE;
577         self.modelindex = 0;
578         self.model = "";
579 };
580
581 void InitSolidBSPTrigger()
582 {
583 // trigger angles are used for one-way touches.  An angle of 0 is assumed
584 // to mean no restrictions, so use a yaw of 360 instead.
585         if (self.movedir == '0 0 0')
586         if (self.angles != '0 0 0')
587                 SetMovedir ();
588         self.solid = SOLID_BSP;
589         SetBrushEntityModel();
590         self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
591 //      self.modelindex = 0;
592         self.model = "";
593 };
594
595 float InitMovingBrushTrigger()
596 {
597 // trigger angles are used for one-way touches.  An angle of 0 is assumed
598 // to mean no restrictions, so use a yaw of 360 instead.
599         self.solid = SOLID_BSP;
600         SetBrushEntityModel();
601         self.movetype = MOVETYPE_PUSH;
602         if(self.modelindex == 0)
603         {
604                 objerror("InitMovingBrushTrigger: no brushes found!");
605                 return 0;
606         }
607         return 1;
608 };