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