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