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