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