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