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