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