]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/g_subs.qc
add lots of TSPEED_ constants and use them :P
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / g_subs.qc
1 void SUB_Null() {}
2 float SUB_True() { return 1; }
3 float SUB_False() { return 0; }
4
5 void()  SUB_CalcMoveDone;
6 void() SUB_CalcAngleMoveDone;
7 //void() SUB_UseTargets;
8 void() SUB_Remove;
9
10 void spawnfunc_info_null (void)
11 {
12         remove(self);
13         // if anything breaks, tell the mapper to fix his map! info_null is meant to remove itself immediately.
14 }
15
16 void setanim(entity e, vector anim, float looping, float override, float restart)
17 {
18         if (!anim)
19                 return; // no animation was given to us! We can't use this. 
20                 
21         if (anim_x == e.animstate_startframe)
22         if (anim_y == e.animstate_numframes)
23         if (anim_z == e.animstate_framerate)
24         {
25                 if(restart)
26                 {
27                         if(restart > 0)
28                         if(anim_y == 1) // ZYM animation
29                                 BITXOR_ASSIGN(e.effects, EF_RESTARTANIM_BIT);
30                 }
31                 else
32                         return;
33         }
34         e.animstate_startframe = anim_x;
35         e.animstate_numframes = anim_y;
36         e.animstate_framerate = anim_z;
37         e.animstate_starttime = servertime - 0.1 * serverframetime; // shift it a little bit into the past to prevent float inaccuracy hiccups
38         e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
39         e.animstate_looping = looping;
40         e.animstate_override = override;
41         e.frame = e.animstate_startframe;
42         e.frame1time = servertime;
43 }
44
45 void updateanim(entity e)
46 {
47         if (time >= e.animstate_endtime)
48         {
49                 if (e.animstate_looping)
50                 {
51                         e.animstate_starttime = e.animstate_endtime;
52                         e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
53                 }
54                 e.animstate_override = FALSE;
55         }
56         e.frame = e.animstate_startframe + bound(0, (time - e.animstate_starttime) * e.animstate_framerate, e.animstate_numframes - 1);
57         //print(ftos(time), " -> ", ftos(e.frame), "\n");
58 }
59
60 vector animfixfps(entity e, vector a)
61 {
62         // multi-frame anim: keep as-is
63         if(a_y == 1)
64         {
65                 float dur;
66                 dur = frameduration(e.modelindex, a_x);
67                 if(dur > 0)
68                         a_z = 1.0 / dur;
69         }
70         return a;
71 }
72
73 /*
74 ==================
75 SUB_Remove
76
77 Remove self
78 ==================
79 */
80 void SUB_Remove (void)
81 {
82         remove (self);
83 }
84
85 /*
86 ==================
87 SUB_Friction
88
89 Applies some friction to self
90 ==================
91 */
92 .float friction;
93 void SUB_Friction (void)
94 {
95         self.nextthink = time;
96         if(self.flags & FL_ONGROUND)
97                 self.velocity = self.velocity * (1 - frametime * self.friction);
98 }
99
100 /*
101 ==================
102 SUB_VanishOrRemove
103
104 Makes client invisible or removes non-client
105 ==================
106 */
107 void SUB_VanishOrRemove (entity ent)
108 {
109         if (ent.flags & FL_CLIENT)
110         {
111                 // vanish
112                 ent.alpha = -1;
113                 ent.effects = 0;
114                 ent.glow_size = 0;
115                 ent.pflags = 0;
116         }
117         else
118         {
119                 // remove
120                 remove (ent);
121         }
122 }
123
124 void SUB_SetFade_Think (void)
125 {
126         if(self.alpha == 0)
127                 self.alpha = 1;
128         self.think = SUB_SetFade_Think;
129         self.nextthink = time;
130         self.alpha -= frametime * self.fade_rate;
131         if (self.alpha < 0.01)
132                 SUB_VanishOrRemove(self);
133         else
134                 self.nextthink = time;
135 }
136
137 /*
138 ==================
139 SUB_SetFade
140
141 Fade 'ent' out when time >= 'when'
142 ==================
143 */
144 void SUB_SetFade (entity ent, float when, float fadetime)
145 {
146         //if (ent.flags & FL_CLIENT) // && ent.deadflag != DEAD_NO)
147         //      return;
148         //ent.alpha = 1;
149         ent.fade_rate = 1/fadetime;
150         ent.think = SUB_SetFade_Think;
151         ent.nextthink = when;
152 }
153
154 /*
155 =============
156 SUB_CalcMove
157
158 calculate self.velocity and self.nextthink to reach dest from
159 self.origin traveling at speed
160 ===============
161 */
162 void SUB_CalcMoveDone (void)
163 {
164         // After moving, set origin to exact final destination
165
166         setorigin (self, self.finaldest);
167         self.velocity = '0 0 0';
168         self.nextthink = -1;
169         if (self.think1)
170                 self.think1 ();
171 }
172
173 .float bezier_turn;
174 void SUB_CalcMove_controller_think (void)
175 {
176         entity oldself;
177         float traveltime;
178         float phasepos;
179         float nexttick;
180         vector delta;
181         vector delta2;
182         vector veloc;
183         vector nextpos;
184         delta = self.destvec;
185         delta2 = self.destvec2;
186         if(time < self.animstate_endtime) {
187                 nexttick = time + sys_frametime;
188
189                 traveltime = self.animstate_endtime - self.animstate_starttime;
190                 phasepos = (nexttick - self.animstate_starttime) / traveltime; // range: [0, 1]
191                 if(self.platmovetype != 1)
192                 {
193                         phasepos = 3.14159265 + (phasepos * 3.14159265); // range: [pi, 2pi]
194                         phasepos = cos(phasepos); // cos [pi, 2pi] is in [-1, 1]
195                         phasepos = phasepos + 1; // correct range to [0, 2]
196                         phasepos = phasepos / 2; // correct range to [0, 1]
197                 }
198                 nextpos = self.origin + (delta * phasepos) + (delta2 * phasepos * phasepos);
199                 // derivative: delta + 2 * delta2 * phasepos (e.g. for angle positioning)
200
201                 if(nexttick < self.animstate_endtime) {
202                         veloc = nextpos - self.owner.origin;
203                         veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame
204                 } else {
205                         veloc = self.finaldest - self.owner.origin;
206                         veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame
207                 }
208                 self.owner.velocity = veloc;
209                 if(self.owner.bezier_turn)
210                 {
211                         vector vel;
212                         vel = delta + 2 * delta2 * phasepos;
213                         vel_z = -vel_z; // invert z velocity
214                         vel = vectoangles(vel);
215                         self.owner.angles = vel;
216                 }
217                 self.nextthink = nexttick;
218         } else {
219                 // derivative: delta + 2 * delta2 (e.g. for angle positioning)
220                 oldself = self;
221                 self.owner.think = self.think1;
222                 self = self.owner;
223                 remove(oldself);
224                 self.think();
225         }
226 }
227
228 void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector dest)
229 {
230         // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + dest * t * t
231         // 2 * control * t - 2 * control * t * t + dest * t * t
232         // 2 * control * t + (dest - 2 * control) * t * t
233
234         controller.origin = org; // starting point
235         control -= org;
236         dest -= org;
237
238         controller.destvec = 2 * control; // control point
239         controller.destvec2 = dest - 2 * control; // quadratic part required to reach end point
240         // also: initial d/dphasepos origin = 2 * control, final speed = 2 * (dest - control)
241 }
242
243 void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector dest)
244 {
245         // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + dest * t * t
246         // 2 * control * t - 2 * control * t * t + dest * t * t
247         // 2 * control * t + (dest - 2 * control) * t * t
248
249         controller.origin = org; // starting point
250         dest -= org;
251
252         controller.destvec = dest; // end point
253         controller.destvec2 = '0 0 0';
254 }
255
256 float TSPEED_TIME = -1;
257 float TSPEED_LINEAR = 0;
258 float TSPEED_START = 1;
259 float TSPEED_END = 2;
260 // TODO average too?
261
262 void SUB_CalcMove_Bezier (vector tcontrol, vector tdest, float tspeedtype, float tspeed, void() func)
263 {
264         float   traveltime;
265         entity controller;
266
267         if (!tspeed)
268                 objerror ("No speed is defined!");
269
270         self.think1 = func;
271         self.finaldest = tdest;
272         self.think = SUB_CalcMoveDone;
273
274         switch(tspeedtype)
275         {
276                 default:
277                 case TSPEED_START:
278                         traveltime = 2 * vlen(tcontrol - self.origin) / tspeed;
279                         break;
280                 case TSPEED_END:
281                         traveltime = 2 * vlen(tcontrol - tdest)       / tspeed;
282                         break;
283                 case TSPEED_LINEAR:
284                         traveltime = vlen(tdest - self.origin)        / tspeed;
285                         break;
286                 case TSPEED_TIME:
287                         traveltime = tspeed;
288                         break;
289         }
290
291         if (traveltime < 0.1) // useless anim
292         {
293                 self.velocity = '0 0 0';
294                 self.nextthink = self.ltime + 0.1;
295                 return;
296         }
297
298         controller = spawn();
299         controller.classname = "SUB_CalcMove_controller";
300         controller.owner = self;
301         controller.platmovetype = self.platmovetype;
302         SUB_CalcMove_controller_setbezier(controller, self.origin, tcontrol, tdest);
303         controller.finaldest = (tdest + '0 0 0.125'); // where do we want to end? Offset to overshoot a bit.
304         controller.animstate_starttime = time;
305         controller.animstate_endtime = time + traveltime;
306         controller.think = SUB_CalcMove_controller_think;
307         controller.think1 = self.think;
308
309         // the thinking is now done by the controller
310         self.think = SUB_Null;
311         self.nextthink = self.ltime + traveltime;
312         
313         // invoke controller
314         self = controller;
315         self.think();
316         self = self.owner;
317 }
318
319 void SUB_CalcMove (vector tdest, float tspeedtype, float tspeed, void() func)
320 {
321         vector  delta;
322         float   traveltime;
323
324         if (!tspeed)
325                 objerror ("No speed is defined!");
326
327         self.think1 = func;
328         self.finaldest = tdest;
329         self.think = SUB_CalcMoveDone;
330
331         if (tdest == self.origin)
332         {
333                 self.velocity = '0 0 0';
334                 self.nextthink = self.ltime + 0.1;
335                 return;
336         }
337
338         delta = tdest - self.origin;
339
340         switch(tspeedtype)
341         {
342                 default:
343                 case TSPEED_START:
344                 case TSPEED_END:
345                 case TSPEED_LINEAR:
346                         traveltime = vlen (delta) / tspeed;
347                         break;
348                 case TSPEED_TIME:
349                         traveltime = tspeed;
350                         break;
351         }
352
353         // Very short animations don't really show off the effect
354         // of controlled animation, so let's just use linear movement.
355         // Alternatively entities can choose to specify non-controlled movement.
356         // The only currently implemented alternative movement is linear (value 1)
357         if (traveltime < 0.15 || self.platmovetype == 1)
358         {
359                 self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
360                 self.nextthink = self.ltime + traveltime;
361                 return;
362         }
363
364         // now just run like a bezier curve...
365         SUB_CalcMove_Bezier((self.origin + tdest) * 0.5, tdest, tspeedtype, tspeed, func);
366 }
367
368 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeedtype, float tspeed, void() func)
369 {
370         entity  oldself;
371
372         oldself = self;
373         self = ent;
374
375         SUB_CalcMove (tdest, tspeedtype, tspeed, func);
376
377         self = oldself;
378 }
379
380 /*
381 =============
382 SUB_CalcAngleMove
383
384 calculate self.avelocity and self.nextthink to reach destangle from
385 self.angles rotating
386
387 The calling function should make sure self.think is valid
388 ===============
389 */
390 void SUB_CalcAngleMoveDone (void)
391 {
392         // After rotating, set angle to exact final angle
393         self.angles = self.finalangle;
394         self.avelocity = '0 0 0';
395         self.nextthink = -1;
396         if (self.think1)
397                 self.think1 ();
398 }
399
400 // FIXME: I fixed this function only for rotation around the main axes
401 void SUB_CalcAngleMove (vector destangle, float tspeedtype, float tspeed, void() func)
402 {
403         vector  delta;
404         float   traveltime;
405
406         if (!tspeed)
407                 objerror ("No speed is defined!");
408
409         // take the shortest distance for the angles
410         self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
411         self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
412         self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
413         delta = destangle - self.angles;
414
415         switch(tspeedtype)
416         {
417                 default:
418                 case TSPEED_START:
419                 case TSPEED_END:
420                 case TSPEED_LINEAR:
421                         traveltime = vlen (delta) / tspeed;
422                         break;
423                 case TSPEED_TIME:
424                         traveltime = tspeed;
425                         break;
426         }
427
428         self.think1 = func;
429         self.finalangle = destangle;
430         self.think = SUB_CalcAngleMoveDone;
431
432         if (traveltime < 0.1)
433         {
434                 self.avelocity = '0 0 0';
435                 self.nextthink = self.ltime + 0.1;
436                 return;
437         }
438
439         self.avelocity = delta * (1 / traveltime);
440         self.nextthink = self.ltime + traveltime;
441 }
442
443 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void() func)
444 {
445         entity  oldself;
446
447         oldself = self;
448         self = ent;
449
450         SUB_CalcAngleMove (destangle, tspeedtype, tspeed, func);
451
452         self = oldself;
453 }
454
455 /*
456 ==================
457 main
458
459 unused but required by the engine
460 ==================
461 */
462 void main (void)
463 {
464
465 }
466
467 // Misc
468
469 /*
470 ==================
471 traceline_antilag
472
473 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
474 Additionally it moves players back into the past before the trace and restores them afterward.
475 ==================
476 */
477 void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag, float wz)
478 {
479         entity player;
480         float oldsolid;
481
482         // check whether antilagged traces are enabled
483         if (lag < 0.001)
484                 lag = 0;
485         if (clienttype(forent) != CLIENTTYPE_REAL)
486                 lag = 0; // only antilag for clients
487
488         // change shooter to SOLID_BBOX so the shot can hit corpses
489         oldsolid = source.dphitcontentsmask;
490         if(source)
491                 source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
492
493         if (lag)
494         {
495                 // take players back into the past
496                 FOR_EACH_PLAYER(player)
497                         if(player != forent)
498                                 antilag_takeback(player, time - lag);
499         }
500
501         // do the trace
502         if(wz)
503                 WarpZone_TraceBox (v1, mi, ma, v2, nomonst, forent);
504         else
505                 tracebox (v1, mi, ma, v2, nomonst, forent);
506
507         // restore players to current positions
508         if (lag)
509         {
510                 FOR_EACH_PLAYER(player)
511                         if(player != forent)
512                                 antilag_restore(player);
513         }
514
515         // restore shooter solid type
516         if(source)
517                 source.dphitcontentsmask = oldsolid;
518 }
519 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
520 {
521         tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, FALSE);
522 }
523 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
524 {
525         if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
526                 lag = 0;
527         traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
528 }
529 void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
530 {
531         if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
532                 lag = 0;
533         tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, FALSE);
534 }
535 void WarpZone_traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
536 {
537         tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, TRUE);
538 }
539 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
540 {
541         if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
542                 lag = 0;
543         WarpZone_traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
544 }
545 void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
546 {
547         if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
548                 lag = 0;
549         tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, TRUE);
550 }
551
552 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking
553 {
554         vector pos, dir, t;
555         float nudge;
556
557         //nudge = 2 * cvar("collision_impactnudge"); // why not?
558         nudge = 0.5;
559
560         dir = normalize(v2 - v1);
561
562         pos = v1 + dir * nudge;
563
564         float c;
565         c = 0;
566
567         for(;;)
568         {
569                 if((pos - v1) * dir >= (v2 - v1) * dir)
570                 {
571                         // went too far
572                         trace_fraction = 1;
573                         trace_endpos = v2;
574                         return c;
575                 }
576
577                 tracebox(pos, mi, ma, v2, nomonsters, forent);
578                 ++c;
579
580                 if(c == 50)
581                 {
582                         dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
583                         dprint("  Nudging gets us nowhere at ", vtos(pos), "\n");
584                         dprint("  trace_endpos is ", vtos(trace_endpos), "\n");
585                         dprint("  trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
586                 }
587
588                 if(trace_startsolid)
589                 {
590                         // we started inside solid.
591                         // then trace from endpos to pos
592                         t = trace_endpos;
593                         tracebox(t, mi, ma, pos, nomonsters, forent);
594                         ++c;
595                         if(trace_startsolid)
596                         {
597                                 // t is still inside solid? bad
598                                 // force advance, then, and retry
599                                 pos = t + dir * nudge;
600                         }
601                         else
602                         {
603                                 // we actually LEFT solid!
604                                 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
605                                 return c;
606                         }
607                 }
608                 else
609                 {
610                         // pos is outside solid?!? but why?!? never mind, just return it.
611                         trace_endpos = pos;
612                         trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
613                         return c;
614                 }
615         }
616 }
617
618 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
619 {
620 #if 0
621         vector pos, dir, t;
622         float nudge;
623
624         //nudge = 2 * cvar("collision_impactnudge"); // why not?
625         nudge = 0.5;
626
627         dir = normalize(v2 - v1);
628
629         pos = v1 + dir * nudge;
630
631         for(;;)
632         {
633                 if((pos - v1) * dir >= (v2 - v1) * dir)
634                 {
635                         // went too far
636                         trace_fraction = 1;
637                         return;
638                 }
639
640                 traceline(pos, v2, nomonsters, forent);
641
642                 if(trace_startsolid)
643                 {
644                         // we started inside solid.
645                         // then trace from endpos to pos
646                         t = trace_endpos;
647                         traceline(t, pos, nomonsters, forent);
648                         if(trace_startsolid)
649                         {
650                                 // t is inside solid? bad
651                                 // force advance, then
652                                 pos = pos + dir * nudge;
653                         }
654                         else
655                         {
656                                 // we actually LEFT solid!
657                                 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
658                                 return;
659                         }
660                 }
661                 else
662                 {
663                         // pos is outside solid?!? but why?!? never mind, just return it.
664                         trace_endpos = pos;
665                         trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
666                         return;
667                 }
668         }
669 #else
670         tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);
671 }
672
673 /*
674 ==================
675 findbetterlocation
676
677 Returns a point at least 12 units away from walls
678 (useful for explosion animations, although the blast is performed where it really happened)
679 Ripped from DPMod
680 ==================
681 */
682 vector findbetterlocation (vector org, float mindist)
683 {
684         vector  loc;
685         vector vec;
686         float c, h;
687
688         vec = mindist * '1 0 0';
689         c = 0;
690         while (c < 6)
691         {
692                 traceline (org, org + vec, TRUE, world);
693                 vec = vec * -1;
694                 if (trace_fraction < 1)
695                 {
696                         loc = trace_endpos;
697                         traceline (loc, loc + vec, TRUE, world);
698                         if (trace_fraction >= 1)
699                                 org = loc + vec;
700                 }
701                 if (c & 1)
702                 {
703                         h = vec_y;
704                         vec_y = vec_x;
705                         vec_x = vec_z;
706                         vec_z = h;
707                 }
708                 c = c + 1;
709         }
710
711         return org;
712 }
713
714 /*
715 ==================
716 crandom
717
718 Returns a random number between -1.0 and 1.0
719 ==================
720 */
721 float crandom (void)
722 {
723         return 2 * (random () - 0.5);
724 }
725
726 /*
727 ==================
728 Angc used for animations
729 ==================
730 */
731
732
733 float angc (float a1, float a2)
734 {
735         float   a;
736
737         while (a1 > 180)
738                 a1 = a1 - 360;
739         while (a1 < -179)
740                 a1 = a1 + 360;
741
742         while (a2 > 180)
743                 a2 = a2 - 360;
744         while (a2 < -179)
745                 a2 = a2 + 360;
746
747         a = a1 - a2;
748         while (a > 180)
749                 a = a - 360;
750         while (a < -179)
751                 a = a + 360;
752
753         return a;
754 }
755
756 .string lodtarget1;
757 .string lodtarget2;
758 .string lodmodel1;
759 .string lodmodel2;
760 .float lodmodelindex0;
761 .float lodmodelindex1;
762 .float lodmodelindex2;
763 .float loddistance1;
764 .float loddistance2;
765
766 float LOD_customize()
767 {
768         float d;
769
770         if(autocvar_loddebug)
771         {
772                 d = autocvar_loddebug;
773                 if(d == 1)
774                         self.modelindex = self.lodmodelindex0;
775                 else if(d == 2 || !self.lodmodelindex2)
776                         self.modelindex = self.lodmodelindex1;
777                 else // if(d == 3)
778                         self.modelindex = self.lodmodelindex2;
779                 return TRUE;
780         }
781
782         // TODO csqc network this so it only gets sent once
783         d = vlen(NearestPointOnBox(self, other.origin) - other.origin);
784         if(d < self.loddistance1)
785                 self.modelindex = self.lodmodelindex0;
786         else if(!self.lodmodelindex2 || d < self.loddistance2)
787                 self.modelindex = self.lodmodelindex1;
788         else
789                 self.modelindex = self.lodmodelindex2;
790
791         return TRUE;
792 }
793
794 void LOD_uncustomize()
795 {
796         self.modelindex = self.lodmodelindex0;
797 }
798
799 void LODmodel_attach()
800 {
801         entity e;
802
803         if(!self.loddistance1)
804                 self.loddistance1 = 1000;
805         if(!self.loddistance2)
806                 self.loddistance2 = 2000;
807         self.lodmodelindex0 = self.modelindex;
808
809         if(self.lodtarget1 != "")
810         {
811                 e = find(world, targetname, self.lodtarget1);
812                 if(e)
813                 {
814                         self.lodmodel1 = e.model;
815                         remove(e);
816                 }
817         }
818         if(self.lodtarget2 != "")
819         {
820                 e = find(world, targetname, self.lodtarget2);
821                 if(e)
822                 {
823                         self.lodmodel2 = e.model;
824                         remove(e);
825                 }
826         }
827
828         if(autocvar_loddebug < 0)
829         {
830                 self.lodmodel1 = self.lodmodel2 = ""; // don't even initialize
831         }
832
833         if(self.lodmodel1 != "")
834         {
835                 vector mi, ma;
836                 mi = self.mins;
837                 ma = self.maxs;
838
839                 precache_model(self.lodmodel1);
840                 setmodel(self, self.lodmodel1);
841                 self.lodmodelindex1 = self.modelindex;
842
843                 if(self.lodmodel2 != "")
844                 {
845                         precache_model(self.lodmodel2);
846                         setmodel(self, self.lodmodel2);
847                         self.lodmodelindex2 = self.modelindex;
848                 }
849
850                 self.modelindex = self.lodmodelindex0;
851                 setsize(self, mi, ma);
852         }
853
854         if(self.lodmodelindex1)
855                 if not(self.SendEntity)
856                         SetCustomizer(self, LOD_customize, LOD_uncustomize);
857 }
858
859 void ApplyMinMaxScaleAngles(entity e)
860 {
861         if(e.angles_x != 0 || e.angles_z != 0 || self.avelocity_x != 0 || self.avelocity_z != 0) // "weird" rotation
862         {
863                 e.maxs = '1 1 1' * vlen(
864                         '1 0 0' * max(-e.mins_x, e.maxs_x) +
865                         '0 1 0' * max(-e.mins_y, e.maxs_y) +
866                         '0 0 1' * max(-e.mins_z, e.maxs_z)
867                 );
868                 e.mins = -e.maxs;
869         }
870         else if(e.angles_y != 0 || self.avelocity_y != 0) // yaw only is a bit better
871         {
872                 e.maxs_x = vlen(
873                         '1 0 0' * max(-e.mins_x, e.maxs_x) +
874                         '0 1 0' * max(-e.mins_y, e.maxs_y)
875                 );
876                 e.maxs_y = e.maxs_x;
877                 e.mins_x = -e.maxs_x;
878                 e.mins_y = -e.maxs_x;
879         }
880         if(e.scale)
881                 setsize(e, e.mins * e.scale, e.maxs * e.scale);
882         else
883                 setsize(e, e.mins, e.maxs);
884 }
885
886 void SetBrushEntityModel()
887 {
888         if(self.model != "")
889         {
890                 precache_model(self.model);
891                 setmodel(self, self.model); // no precision needed
892                 InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
893         }
894         setorigin(self, self.origin);
895         ApplyMinMaxScaleAngles(self);
896 }
897
898 void SetBrushEntityModelNoLOD()
899 {
900         if(self.model != "")
901         {
902                 precache_model(self.model);
903                 setmodel(self, self.model); // no precision needed
904         }
905         setorigin(self, self.origin);
906         ApplyMinMaxScaleAngles(self);
907 }
908
909 /*
910 ================
911 InitTrigger
912 ================
913 */
914
915 void SetMovedir()
916 {
917         if (self.movedir != '0 0 0')
918                 self.movedir = normalize(self.movedir);
919         else
920         {
921                 makevectors (self.angles);
922                 self.movedir = v_forward;
923         }
924
925         self.angles = '0 0 0';
926 }
927
928 void InitTrigger()
929 {
930 // trigger angles are used for one-way touches.  An angle of 0 is assumed
931 // to mean no restrictions, so use a yaw of 360 instead.
932         SetMovedir ();
933         self.solid = SOLID_TRIGGER;
934         SetBrushEntityModel();
935         self.movetype = MOVETYPE_NONE;
936         self.modelindex = 0;
937         self.model = "";
938 }
939
940 void InitSolidBSPTrigger()
941 {
942 // trigger angles are used for one-way touches.  An angle of 0 is assumed
943 // to mean no restrictions, so use a yaw of 360 instead.
944         SetMovedir ();
945         self.solid = SOLID_BSP;
946         SetBrushEntityModel();
947         self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
948 //      self.modelindex = 0;
949         self.model = "";
950 }
951
952 float InitMovingBrushTrigger()
953 {
954 // trigger angles are used for one-way touches.  An angle of 0 is assumed
955 // to mean no restrictions, so use a yaw of 360 instead.
956         self.solid = SOLID_BSP;
957         SetBrushEntityModel();
958         self.movetype = MOVETYPE_PUSH;
959         if(self.modelindex == 0)
960         {
961                 objerror("InitMovingBrushTrigger: no brushes found!");
962                 return 0;
963         }
964         return 1;
965 }