]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/g_subs.qc
Working more on effects -- still can't get the damn circular effect to work properly...
[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)
20                 return; // no animation was given to us! We can't use this. 
21                 
22         if (anim_x == e.animstate_startframe)
23         if (anim_y == e.animstate_numframes)
24         if (anim_z == e.animstate_framerate)
25         {
26                 if(restart)
27                 {
28                         if(restart > 0)
29                         if(anim_y == 1) // ZYM animation
30                                 BITXOR_ASSIGN(e.effects, EF_RESTARTANIM_BIT);
31                 }
32                 else
33                         return;
34         }
35         e.animstate_startframe = anim_x;
36         e.animstate_numframes = anim_y;
37         e.animstate_framerate = anim_z;
38         e.animstate_starttime = servertime - 0.1 * serverframetime; // shift it a little bit into the past to prevent float inaccuracy hiccups
39         e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
40         e.animstate_looping = looping;
41         e.animstate_override = override;
42         e.frame = e.animstate_startframe;
43         e.frame1time = servertime;
44 }
45
46 void updateanim(entity e)
47 {
48         if (time >= e.animstate_endtime)
49         {
50                 if (e.animstate_looping)
51                 {
52                         e.animstate_starttime = e.animstate_endtime;
53                         e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
54                 }
55                 e.animstate_override = FALSE;
56         }
57         e.frame = e.animstate_startframe + bound(0, (time - e.animstate_starttime) * e.animstate_framerate, e.animstate_numframes - 1);
58         //print(ftos(time), " -> ", ftos(e.frame), "\n");
59 }
60
61 vector animfixfps(entity e, vector a)
62 {
63         // multi-frame anim: keep as-is
64         if(a_y == 1)
65         {
66                 float dur;
67                 dur = frameduration(e.modelindex, a_x);
68                 if(dur > 0)
69                         a_z = 1.0 / dur;
70         }
71         return a;
72 }
73
74 /*
75 ==================
76 SUB_Remove
77
78 Remove self
79 ==================
80 */
81 void SUB_Remove (void)
82 {
83         remove (self);
84 }
85
86 /*
87 ==================
88 SUB_Friction
89
90 Applies some friction to self
91 ==================
92 */
93 .float friction;
94 void SUB_Friction (void)
95 {
96         self.nextthink = time;
97         if(self.flags & FL_ONGROUND)
98                 self.velocity = self.velocity * (1 - frametime * self.friction);
99 }
100
101 /*
102 ==================
103 SUB_VanishOrRemove
104
105 Makes client invisible or removes non-client
106 ==================
107 */
108 void SUB_VanishOrRemove (entity ent)
109 {
110         if (ent.flags & FL_CLIENT)
111         {
112                 // vanish
113                 ent.alpha = -1;
114                 ent.effects = 0;
115                 ent.glow_size = 0;
116                 ent.pflags = 0;
117         }
118         else
119         {
120                 // remove
121                 remove (ent);
122         }
123 }
124
125 void SUB_SetFade_Think (void)
126 {
127         if(self.alpha == 0)
128                 self.alpha = 1;
129         self.think = SUB_SetFade_Think;
130         self.nextthink = time;
131         self.alpha -= frametime * self.fade_rate;
132         if (self.alpha < 0.01)
133                 SUB_VanishOrRemove(self);
134         else
135                 self.nextthink = time;
136 }
137
138 /*
139 ==================
140 SUB_SetFade
141
142 Fade 'ent' out when time >= 'when'
143 ==================
144 */
145 void SUB_SetFade (entity ent, float when, float fadetime)
146 {
147         //if (ent.flags & FL_CLIENT) // && ent.deadflag != DEAD_NO)
148         //      return;
149         //ent.alpha = 1;
150         ent.fade_rate = 1/fadetime;
151         ent.think = SUB_SetFade_Think;
152         ent.nextthink = when;
153 }
154
155 /*
156 =============
157 SUB_CalcMove
158
159 calculate self.velocity and self.nextthink to reach dest from
160 self.origin traveling at speed
161 ===============
162 */
163 void SUB_CalcMoveDone (void)
164 {
165         // After moving, set origin to exact final destination
166
167         setorigin (self, self.finaldest);
168         self.velocity = '0 0 0';
169         self.nextthink = -1;
170         if (self.think1)
171                 self.think1 ();
172 }
173
174 void SUB_CalcMove_controller_think (void)
175 {
176         entity oldself;
177         float traveltime;
178         float phasepos;
179         float nexttick;
180         vector delta;
181         vector veloc;
182         vector nextpos;
183         if(time < self.animstate_endtime) {
184                 delta = self.destvec;
185                 nexttick = time + sys_frametime;
186
187                 if(nexttick < self.animstate_endtime) {
188                         traveltime = self.animstate_endtime - self.animstate_starttime;
189                         phasepos = (nexttick - self.animstate_starttime) / traveltime; // range: [0, 1]
190                         phasepos = 3.14159265 + (phasepos * 3.14159265); // range: [pi, 2pi]
191                         phasepos = cos(phasepos); // cos [pi, 2pi] is in [-1, 1]
192                         phasepos = phasepos + 1; // correct range to [0, 2]
193                         phasepos = phasepos / 2; // correct range to [0, 1]
194                         nextpos = self.origin + (delta * phasepos);
195
196                         veloc = nextpos - self.owner.origin;
197                         veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame
198
199                 } else {
200                         veloc = self.finaldest - self.owner.origin;
201                         veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame
202                 }
203                 self.owner.velocity = veloc;
204                 self.nextthink = nexttick;
205         } else {
206                 oldself = self;
207                 self.owner.think = self.think1;
208                 self = self.owner;
209                 remove(oldself);
210                 self.think();
211         }
212 }
213
214 void SUB_CalcMove (vector tdest, float tspeed, void() func)
215 {
216         vector  delta;
217         float   traveltime;
218         entity controller;
219
220         if (!tspeed)
221                 objerror ("No speed is defined!");
222
223         self.think1 = func;
224         self.finaldest = tdest;
225         self.think = SUB_CalcMoveDone;
226
227         if (tdest == self.origin)
228         {
229                 self.velocity = '0 0 0';
230                 self.nextthink = self.ltime + 0.1;
231                 return;
232         }
233
234         delta = tdest - self.origin;
235         traveltime = vlen (delta) / tspeed;
236
237         if (traveltime < 0.1)
238         {
239                 self.velocity = '0 0 0';
240                 self.nextthink = self.ltime + 0.1;
241                 return;
242         }
243
244         // Very short animations don't really show off the effect
245         // of controlled animation, so let's just use linear movement.
246         // Alternatively entities can choose to specify non-controlled movement.
247         // The only currently implemented alternative movement is linear (value 1)
248         if (traveltime < 0.15 || self.platmovetype == 1)
249         {
250                 self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
251                 self.nextthink = self.ltime + traveltime;
252                 return;
253         }
254
255         controller = spawn();
256         controller.classname = "SUB_CalcMove_controller";
257         controller.owner = self;
258         controller.origin = self.origin; // starting point
259         controller.finaldest = (tdest + '0 0 0.125'); // where do we want to end? Offset to overshoot a bit.
260         controller.destvec = delta;
261         controller.animstate_starttime = time;
262         controller.animstate_endtime = time + traveltime;
263         controller.think = SUB_CalcMove_controller_think;
264         controller.think1 = self.think;
265
266         // the thinking is now done by the controller
267         self.think = SUB_Null;
268         self.nextthink = self.ltime + traveltime;
269         
270         // invoke controller
271         self = controller;
272         self.think();
273         self = self.owner;
274 }
275
276 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
277 {
278         entity  oldself;
279
280         oldself = self;
281         self = ent;
282
283         SUB_CalcMove (tdest, tspeed, func);
284
285         self = oldself;
286 }
287
288 /*
289 =============
290 SUB_CalcAngleMove
291
292 calculate self.avelocity and self.nextthink to reach destangle from
293 self.angles rotating
294
295 The calling function should make sure self.think is valid
296 ===============
297 */
298 void SUB_CalcAngleMoveDone (void)
299 {
300         // After rotating, set angle to exact final angle
301         self.angles = self.finalangle;
302         self.avelocity = '0 0 0';
303         self.nextthink = -1;
304         if (self.think1)
305                 self.think1 ();
306 }
307
308 // FIXME: I fixed this function only for rotation around the main axes
309 void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
310 {
311         vector  delta;
312         float   traveltime;
313
314         if (!tspeed)
315                 objerror ("No speed is defined!");
316
317         // take the shortest distance for the angles
318         self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
319         self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
320         self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
321         delta = destangle - self.angles;
322         traveltime = vlen (delta) / tspeed;
323
324         self.think1 = func;
325         self.finalangle = destangle;
326         self.think = SUB_CalcAngleMoveDone;
327
328         if (traveltime < 0.1)
329         {
330                 self.avelocity = '0 0 0';
331                 self.nextthink = self.ltime + 0.1;
332                 return;
333         }
334
335         self.avelocity = delta * (1 / traveltime);
336         self.nextthink = self.ltime + traveltime;
337 }
338
339 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
340 {
341         entity  oldself;
342
343         oldself = self;
344         self = ent;
345
346         SUB_CalcAngleMove (destangle, tspeed, func);
347
348         self = oldself;
349 }
350
351 /*
352 ==================
353 main
354
355 unused but required by the engine
356 ==================
357 */
358 void main (void)
359 {
360
361 }
362
363 // Misc
364
365 /*
366 ==================
367 traceline_antilag
368
369 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
370 Additionally it moves players back into the past before the trace and restores them afterward.
371 ==================
372 */
373 void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag, float wz)
374 {
375         entity player;
376         float oldsolid;
377
378         // check whether antilagged traces are enabled
379         if (lag < 0.001)
380                 lag = 0;
381         if (clienttype(forent) != CLIENTTYPE_REAL)
382                 lag = 0; // only antilag for clients
383
384         // change shooter to SOLID_BBOX so the shot can hit corpses
385         oldsolid = source.dphitcontentsmask;
386         if(source)
387                 source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
388
389         if (lag)
390         {
391                 // take players back into the past
392                 FOR_EACH_PLAYER(player)
393                         if(player != forent)
394                                 antilag_takeback(player, time - lag);
395         }
396
397         // do the trace
398         if(wz)
399                 WarpZone_TraceBox (v1, mi, ma, v2, nomonst, forent);
400         else
401                 tracebox (v1, mi, ma, v2, nomonst, forent);
402
403         // restore players to current positions
404         if (lag)
405         {
406                 FOR_EACH_PLAYER(player)
407                         if(player != forent)
408                                 antilag_restore(player);
409         }
410
411         // restore shooter solid type
412         if(source)
413                 source.dphitcontentsmask = oldsolid;
414 }
415 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
416 {
417         tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, FALSE);
418 }
419 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
420 {
421         if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
422                 lag = 0;
423         traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
424 }
425 void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
426 {
427         if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
428                 lag = 0;
429         tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, FALSE);
430 }
431 void WarpZone_traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
432 {
433         tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, TRUE);
434 }
435 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
436 {
437         if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
438                 lag = 0;
439         WarpZone_traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
440 }
441 void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
442 {
443         if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
444                 lag = 0;
445         tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, TRUE);
446 }
447
448 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking
449 {
450         vector pos, dir, t;
451         float nudge;
452
453         //nudge = 2 * cvar("collision_impactnudge"); // why not?
454         nudge = 0.5;
455
456         dir = normalize(v2 - v1);
457
458         pos = v1 + dir * nudge;
459
460         float c;
461         c = 0;
462
463         for(;;)
464         {
465                 if((pos - v1) * dir >= (v2 - v1) * dir)
466                 {
467                         // went too far
468                         trace_fraction = 1;
469                         trace_endpos = v2;
470                         return c;
471                 }
472
473                 tracebox(pos, mi, ma, v2, nomonsters, forent);
474                 ++c;
475
476                 if(c == 50)
477                 {
478                         dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
479                         dprint("  Nudging gets us nowhere at ", vtos(pos), "\n");
480                         dprint("  trace_endpos is ", vtos(trace_endpos), "\n");
481                         dprint("  trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
482                 }
483
484                 if(trace_startsolid)
485                 {
486                         // we started inside solid.
487                         // then trace from endpos to pos
488                         t = trace_endpos;
489                         tracebox(t, mi, ma, pos, nomonsters, forent);
490                         ++c;
491                         if(trace_startsolid)
492                         {
493                                 // t is still inside solid? bad
494                                 // force advance, then, and retry
495                                 pos = t + dir * nudge;
496                         }
497                         else
498                         {
499                                 // we actually LEFT solid!
500                                 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
501                                 return c;
502                         }
503                 }
504                 else
505                 {
506                         // pos is outside solid?!? but why?!? never mind, just return it.
507                         trace_endpos = pos;
508                         trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
509                         return c;
510                 }
511         }
512 }
513
514 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
515 {
516 #if 0
517         vector pos, dir, t;
518         float nudge;
519
520         //nudge = 2 * cvar("collision_impactnudge"); // why not?
521         nudge = 0.5;
522
523         dir = normalize(v2 - v1);
524
525         pos = v1 + dir * nudge;
526
527         for(;;)
528         {
529                 if((pos - v1) * dir >= (v2 - v1) * dir)
530                 {
531                         // went too far
532                         trace_fraction = 1;
533                         return;
534                 }
535
536                 traceline(pos, v2, nomonsters, forent);
537
538                 if(trace_startsolid)
539                 {
540                         // we started inside solid.
541                         // then trace from endpos to pos
542                         t = trace_endpos;
543                         traceline(t, pos, nomonsters, forent);
544                         if(trace_startsolid)
545                         {
546                                 // t is inside solid? bad
547                                 // force advance, then
548                                 pos = pos + dir * nudge;
549                         }
550                         else
551                         {
552                                 // we actually LEFT solid!
553                                 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
554                                 return;
555                         }
556                 }
557                 else
558                 {
559                         // pos is outside solid?!? but why?!? never mind, just return it.
560                         trace_endpos = pos;
561                         trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
562                         return;
563                 }
564         }
565 #else
566         tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);
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                 setmodel(self, self.model); // no precision needed
788                 InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
789         }
790         setorigin(self, self.origin);
791         ApplyMinMaxScaleAngles(self);
792 }
793
794 void SetBrushEntityModelNoLOD()
795 {
796         if(self.model != "")
797         {
798                 precache_model(self.model);
799                 setmodel(self, self.model); // no precision needed
800         }
801         setorigin(self, self.origin);
802         ApplyMinMaxScaleAngles(self);
803 }
804
805 /*
806 ================
807 InitTrigger
808 ================
809 */
810
811 void SetMovedir()
812 {
813         if (self.movedir != '0 0 0')
814                 self.movedir = normalize(self.movedir);
815         else
816         {
817                 makevectors (self.angles);
818                 self.movedir = v_forward;
819         }
820
821         self.angles = '0 0 0';
822 }
823
824 void InitTrigger()
825 {
826 // trigger angles are used for one-way touches.  An angle of 0 is assumed
827 // to mean no restrictions, so use a yaw of 360 instead.
828         SetMovedir ();
829         self.solid = SOLID_TRIGGER;
830         SetBrushEntityModel();
831         self.movetype = MOVETYPE_NONE;
832         self.modelindex = 0;
833         self.model = "";
834 }
835
836 void InitSolidBSPTrigger()
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         SetMovedir ();
841         self.solid = SOLID_BSP;
842         SetBrushEntityModel();
843         self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
844 //      self.modelindex = 0;
845         self.model = "";
846 }
847
848 float InitMovingBrushTrigger()
849 {
850 // trigger angles are used for one-way touches.  An angle of 0 is assumed
851 // to mean no restrictions, so use a yaw of 360 instead.
852         self.solid = SOLID_BSP;
853         SetBrushEntityModel();
854         self.movetype = MOVETYPE_PUSH;
855         if(self.modelindex == 0)
856         {
857                 objerror("InitMovingBrushTrigger: no brushes found!");
858                 return 0;
859         }
860         return 1;
861 }