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