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