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