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