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