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