]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/g_triggers.qc
also make trigger_hurt, func_ladder and trigger_impulse exact triggers. Tell me what...
[divverent/nexuiz.git] / data / qcsrc / server / g_triggers.qc
1
2 void() SUB_UseTargets;
3
4 void DelayThink()
5 {
6         activator = self.enemy;
7         SUB_UseTargets ();
8         remove(self);
9 };
10
11 /*
12 ==============================
13 SUB_UseTargets
14
15 the global "activator" should be set to the entity that initiated the firing.
16
17 If self.delay is set, a DelayedUse entity will be created that will actually
18 do the SUB_UseTargets after that many seconds have passed.
19
20 Centerprints any self.message to the activator.
21
22 Removes all entities with a targetname that match self.killtarget,
23 and removes them, so some events can remove other triggers.
24
25 Search for (string)targetname in all entities that
26 match (string)self.target and call their .use function
27
28 ==============================
29 */
30 void SUB_UseTargets()
31 {
32         local entity t, stemp, otemp, act;
33
34 //
35 // check for a delay
36 //
37         if (self.delay)
38         {
39         // create a temp object to fire at a later time
40                 t = spawn();
41                 t.classname = "DelayedUse";
42                 t.nextthink = time + self.delay;
43                 t.think = DelayThink;
44                 t.enemy = activator;
45                 t.message = self.message;
46                 t.killtarget = self.killtarget;
47                 t.target = self.target;
48                 return;
49         }
50
51
52 //
53 // print the message
54 //
55         if (activator.classname == "player" && self.message != "")
56         {
57                 centerprint (activator, self.message);
58                 if (!self.noise)
59                         play2(activator, "misc/talk.wav");
60         }
61
62 //
63 // kill the killtagets
64 //
65         if (self.killtarget)
66         {
67                 t = world;
68                 do
69                 {
70                         t = find (t, targetname, self.killtarget);
71                         if (!t)
72                                 return;
73                         remove (t);
74                 } while ( 1 );
75         }
76
77 //
78 // fire targets
79 //
80         if (self.target)
81         {
82                 act = activator;
83                 t = world;
84                 do
85                 {
86                         t = find (t, targetname, self.target);
87                         if (!t)
88                         {
89                                 return;
90                         }
91                         stemp = self;
92                         otemp = other;
93                         self = t;
94                         other = stemp;
95                         if (self.use)
96                                 self.use ();
97                         self = stemp;
98                         other = otemp;
99                         activator = act;
100                 } while ( 1 );
101         }
102
103
104 };
105
106
107 //=============================================================================
108
109 float   SPAWNFLAG_NOMESSAGE = 1;
110 float   SPAWNFLAG_NOTOUCH = 1;
111
112 // the wait time has passed, so set back up for another activation
113 void multi_wait()
114 {
115         if (self.max_health)
116         {
117                 self.health = self.max_health;
118                 self.takedamage = DAMAGE_YES;
119                 self.solid = SOLID_BBOX;
120         }
121 };
122
123
124 // the trigger was just touched/killed/used
125 // self.enemy should be set to the activator so it can be held through a delay
126 // so wait for the delay time before firing
127 void multi_trigger()
128 {
129         if (self.nextthink > time)
130         {
131                 return;         // allready been triggered
132         }
133
134         if (self.classname == "trigger_secret")
135         {
136                 if (self.enemy.classname != "player")
137                         return;
138                 found_secrets = found_secrets + 1;
139                 WriteByte (MSG_ALL, SVC_FOUNDSECRET);
140         }
141
142         if (self.noise)
143                 sound (self.enemy, CHAN_AUTO, self.noise, VOL_BASE, ATTN_NORM);
144
145 // don't trigger again until reset
146         self.takedamage = DAMAGE_NO;
147
148         activator = self.enemy;
149
150         SUB_UseTargets();
151
152         if (self.wait > 0)
153         {
154                 self.think = multi_wait;
155                 self.nextthink = time + self.wait;
156         }
157         else
158         {       // we can't just remove (self) here, because this is a touch function
159                 // called wheil C code is looping through area links...
160                 self.touch = SUB_Null;
161
162                 self.nextthink = time + 0.1;
163                 self.think = SUB_Remove;
164         }
165 };
166
167 void multi_use()
168 {
169         self.enemy = activator;
170         multi_trigger();
171 };
172
173 void multi_touch()
174 {
175         if (other.classname != "player")
176                 return;
177
178     if(self.team)
179     if(self.team == other.team)
180         return;
181
182 // if the trigger has an angles field, check player's facing direction
183         if (self.movedir != '0 0 0')
184         {
185                 makevectors (other.angles);
186                 if (v_forward * self.movedir < 0)
187                         return;         // not facing the right way
188         }
189
190         self.enemy = other;
191         multi_trigger ();
192 };
193
194 void multi_eventdamage (vector hitloc, float damage, entity inflictor, entity attacker, float deathtype)
195 {
196         if (!self.takedamage)
197                 return;
198         self.health = self.health - damage;
199         if (self.health <= 0)
200         {
201                 self.enemy = attacker;
202                 multi_trigger();
203         }
204 }
205
206 /*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
207 Variable sized repeatable trigger.  Must be targeted at one or more entities.  If "health" is set, the trigger must be killed to activate each time.
208 If "delay" is set, the trigger waits some time after activating before firing.
209 "wait" : Seconds between triggerings. (.2 default)
210 If notouch is set, the trigger is only fired by other entities, not by touching.
211 NOTOUCH has been obsoleted by spawnfunc_trigger_relay!
212 sounds
213 1)      secret
214 2)      beep beep
215 3)      large switch
216 4)
217 set "message" to text string
218 */
219 void spawnfunc_trigger_multiple()
220 {
221         if (self.sounds == 1)
222         {
223                 precache_sound ("misc/secret.wav");
224                 self.noise = "misc/secret.wav";
225         }
226         else if (self.sounds == 2)
227         {
228                 precache_sound ("misc/talk.wav");
229                 self.noise = "misc/talk.wav";
230         }
231         else if (self.sounds == 3)
232         {
233                 precache_sound ("misc/trigger1.wav");
234                 self.noise = "misc/trigger1.wav";
235         }
236
237         if (!self.wait)
238                 self.wait = 0.2;
239         self.use = multi_use;
240
241         InitTrigger ();
242
243         if (self.health)
244         {
245                 if (self.spawnflags & SPAWNFLAG_NOTOUCH)
246                         objerror ("health and notouch don't make sense\n");
247                 self.max_health = self.health;
248                 self.event_damage = multi_eventdamage;
249                 self.takedamage = DAMAGE_YES;
250                 self.solid = SOLID_BBOX;
251                 setorigin (self, self.origin);  // make sure it links into the world
252         }
253         else
254         {
255                 if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
256                 {
257                         self.touch = multi_touch;
258                 }
259         }
260 };
261
262
263 /*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch
264 Variable sized trigger. Triggers once, then removes itself.  You must set the key "target" to the name of another object in the level that has a matching
265 "targetname".  If "health" is set, the trigger must be killed to activate.
266 If notouch is set, the trigger is only fired by other entities, not by touching.
267 if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
268 if "angle" is set, the trigger will only fire when someone is facing the direction of the angle.  Use "360" for an angle of 0.
269 sounds
270 1)      secret
271 2)      beep beep
272 3)      large switch
273 4)
274 set "message" to text string
275 */
276 void spawnfunc_trigger_once()
277 {
278         self.wait = -1;
279         spawnfunc_trigger_multiple();
280 };
281
282 //=============================================================================
283
284 /*QUAKED spawnfunc_trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
285 This fixed size trigger cannot be touched, it can only be fired by other events.  It can contain killtargets, targets, delays, and messages.
286 */
287 void spawnfunc_trigger_relay()
288 {
289         self.use = SUB_UseTargets;
290 };
291
292 void delay_use()
293 {
294     self.think = SUB_UseTargets;
295     self.nextthink = self.wait;
296 }
297
298 void spawnfunc_trigger_delay()
299 {
300     if(!self.wait)
301         self.wait = 1;
302
303     self.use = delay_use;
304 }
305
306 //=============================================================================
307
308
309 void counter_use()
310 {
311         self.count = self.count - 1;
312         if (self.count < 0)
313                 return;
314
315         if (self.count != 0)
316         {
317                 if (activator.classname == "player"
318                 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
319                 {
320                         if (self.count >= 4)
321                                 centerprint (activator, "There are more to go...");
322                         else if (self.count == 3)
323                                 centerprint (activator, "Only 3 more to go...");
324                         else if (self.count == 2)
325                                 centerprint (activator, "Only 2 more to go...");
326                         else
327                                 centerprint (activator, "Only 1 more to go...");
328                 }
329                 return;
330         }
331
332         if (activator.classname == "player"
333         && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
334                 centerprint(activator, "Sequence completed!");
335         self.enemy = activator;
336         multi_trigger ();
337 };
338
339 /*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage
340 Acts as an intermediary for an action that takes multiple inputs.
341
342 If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
343
344 After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
345 */
346 void spawnfunc_trigger_counter()
347 {
348         self.wait = -1;
349         if (!self.count)
350                 self.count = 2;
351
352         self.use = counter_use;
353 };
354
355 .float triggerhurttime;
356 void trigger_hurt_touch()
357 {
358         // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
359         if (!other.owner)
360         {
361                 if (other.items & IT_KEY1 || other.items & IT_KEY2)     // reset flag
362                 {
363                         EXACTTRIGGER_TOUCH;
364                         other.pain_finished = min(other.pain_finished, time + 2);
365                 }
366                 else if (other.classname == "rune")                     // reset runes
367                 {
368                         EXACTTRIGGER_TOUCH;
369                         other.nextthink = min(other.nextthink, time + 1);
370                 }
371         }
372
373         if (other.takedamage)
374         if (other.triggerhurttime < time)
375         {
376                 EXACTTRIGGER_TOUCH;
377                 other.triggerhurttime = time + 1;
378                 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
379         }
380
381         return;
382 };
383
384 /*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ?
385 Any object touching this will be hurt
386 set dmg to damage amount
387 defalt dmg = 5
388 */
389 .entity trigger_hurt_next;
390 entity trigger_hurt_last;
391 entity trigger_hurt_first;
392 void spawnfunc_trigger_hurt()
393 {
394         InitTrigger ();
395         self.touch = trigger_hurt_touch;
396         if (!self.dmg)
397                 self.dmg = 1000;
398         if (!self.message)
399                 self.message = "was in the wrong place.";
400
401         if(!trigger_hurt_first)
402                 trigger_hurt_first = self;
403         if(trigger_hurt_last)
404                 trigger_hurt_last.trigger_hurt_next = self;
405         trigger_hurt_last = self;
406 };
407
408 float trace_hits_box_a0, trace_hits_box_a1;
409
410 float trace_hits_box_1d(float end, float thmi, float thma)
411 {
412         if(end == 0)
413         {
414                 // just check if x is in range
415                 if(0 < thmi)
416                         return FALSE;
417                 if(0 > thma)
418                         return FALSE;
419         }
420         else
421         {
422                 // do the trace with respect to x
423                 // 0 -> end has to stay in thmi -> thma
424                 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
425                 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
426                 if(trace_hits_box_a0 > trace_hits_box_a1)
427                         return FALSE;
428         }
429         return TRUE;
430 }
431
432 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
433 {
434         end -= start;
435         thmi -= start;
436         thma -= start;
437         // now it is a trace from 0 to end
438
439         trace_hits_box_a0 = 0;
440         trace_hits_box_a1 = 1;
441
442         if(!trace_hits_box_1d(end_x, thmi_x, thma_x))
443                 return FALSE;
444         if(!trace_hits_box_1d(end_y, thmi_y, thma_y))
445                 return FALSE;
446         if(!trace_hits_box_1d(end_z, thmi_z, thma_z))
447                 return FALSE;
448
449         return TRUE;
450 }
451
452 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
453 {
454         return trace_hits_box(start, end, thmi - ma, thma - mi);
455 }
456
457 float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end)
458 {
459         entity th;
460
461         for(th = trigger_hurt_first; th; th = th.trigger_hurt_next)
462                 if(tracebox_hits_box(start, mi, ma, end, th.absmin, th.absmax))
463                         return TRUE;
464
465         return FALSE;
466 }
467
468
469 void target_speaker_use() {sound(self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);}
470
471 void spawnfunc_target_speaker()
472 {
473         if(self.noise)
474                 precache_sound (self.noise);
475         if(self.targetname)
476                 self.use = target_speaker_use;
477         else
478                 ambientsound (self.origin, self.noise, VOL_BASE, ATTN_STATIC);
479 };
480
481
482 void spawnfunc_func_stardust() {
483         self.effects = EF_STARDUST;
484 }
485
486
487 /*
488 void sparksthink()
489 {
490   self.nextthink = time + 0.1;
491
492   if(random() < self.wait) {
493     te_spark(self.origin,'0 0 -1',self.cnt);
494   }
495 }
496
497
498 void func_sparks()
499 {
500   self.think = sparksthink;
501   self.nextthink = time + 0.2;
502
503   // self.cnt is the amount of sparks that one burst will spawn
504   if(self.cnt < 1) {
505     self.cnt = 25.0; // nice default value
506   }
507
508   // self.wait is the probability that a sparkthink will spawn a spark shower
509   // range: 0 - 1, but 0 makes little sense, so...
510   if(self.wait < 0.05) {
511     self.wait = 0.25; // nice default value
512   }
513
514   // sound
515   if(self.noise) {
516     precache_sound (self.noise);
517     ambientsound (self.origin, self.noise, VOL_BASE, ATTN_STATIC);
518   }
519 }
520 */
521 void rain_think()
522 {
523         self.nextthink = time + 0.1;
524         te_particlerain(self.absmin, self.absmax, self.dest, self.count, self.cnt);
525 //      te_particlesnow(self.absmin, self.absmax, self.dest * 0.25, self.count, self.cnt);
526 //      WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
527 //      WriteByte (MSG_BROADCAST, TE_PARTICLERAIN);
528 //      WriteVec (MSG_BROADCAST, self.absmin);
529 //      WriteVec (MSG_BROADCAST, self.absmax);
530 //      WriteVec (MSG_BROADCAST, self.dest);
531 //      WriteShort (MSG_BROADCAST, self.count);
532 //      WriteByte (MSG_BROADCAST, self.cnt);
533 };
534
535 /*QUAKED spawnfunc_func_rain (0 .5 .8) ?
536 This is an invisible area like a trigger, which rain falls inside of.
537
538 Keys:
539 "velocity"
540  falling direction (should be something like '0 0 -700', use the X and Y velocity for wind)
541 "cnt"
542  sets color of rain (default 12 - white)
543 "count"
544  adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
545 */
546 void spawnfunc_func_rain()
547 {
548         self.dest = self.velocity;
549         self.velocity = '0 0 0';
550         if (!self.dest)
551                 self.dest = '0 0 -700';
552         self.angles = '0 0 0';
553         self.movetype = MOVETYPE_NONE;
554         self.solid = SOLID_NOT;
555         SetBrushEntityModel();
556         self.model = "";
557         if (!self.cnt)
558                 self.cnt = 12;
559         if (!self.count)
560                 self.count = 2000;
561         self.count = 0.1 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
562         if (self.count < 1)
563         {
564                 remove(self);
565                 return;
566         }
567         // convert from per second to per 0.1 sec,
568         self.count = ceil(self.count * 0.1);
569         self.think = rain_think;
570         self.nextthink = time + 0.5;
571 };
572
573
574 void snow_think()
575 {
576         self.nextthink = time + 0.1 + random() * 0.05;
577         te_particlesnow(self.absmin, self.absmax, self.dest, self.count, self.cnt);
578 //      WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
579 //      WriteByte (MSG_BROADCAST, TE_PARTICLESNOW);
580 //      WriteVec (MSG_BROADCAST, self.absmin);
581 //      WriteVec (MSG_BROADCAST, self.absmax);
582 //      WriteVec (MSG_BROADCAST, self.dest);
583 //      WriteShort (MSG_BROADCAST, self.count);
584 //      WriteByte (MSG_BROADCAST, self.cnt);
585 };
586
587 /*QUAKED spawnfunc_func_snow (0 .5 .8) ?
588 This is an invisible area like a trigger, which snow falls inside of.
589
590 Keys:
591 "velocity"
592  falling direction (should be something like '0 0 -300', use the X and Y velocity for wind)
593 "cnt"
594  sets color of rain (default 12 - white)
595 "count"
596  adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
597 */
598 void spawnfunc_func_snow()
599 {
600         self.dest = self.velocity;
601         self.velocity = '0 0 0';
602         if (!self.dest)
603                 self.dest = '0 0 -300';
604         self.angles = '0 0 0';
605         self.movetype = MOVETYPE_NONE;
606         self.solid = SOLID_NOT;
607         SetBrushEntityModel();
608         self.model = "";
609         if (!self.cnt)
610                 self.cnt = 12;
611         if (!self.count)
612                 self.count = 2000;
613         self.count = 0.1 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
614         if (self.count < 1)
615         {
616                 remove(self);
617                 return;
618         }
619         // convert from per second to per 0.1 sec,
620         self.count = ceil(self.count * 0.1);
621         self.think = snow_think;
622         self.nextthink = time + 0.5;
623 };
624
625
626 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float deathtype);
627 void misc_laser_think()
628 {
629         vector o;
630         if(!self.state)
631         {
632                 self.enemy = find(world, targetname, self.target);
633                 self.state = 1;
634         }
635         if(self.enemy)
636         {
637                 o = self.enemy.origin;
638         }
639         else
640         {
641                 makevectors(self.angles);
642                 o = self.origin + v_forward * MAX_SHOT_DISTANCE;
643         }
644
645         if(self.dmg)
646         {
647                 if(self.dmg < 0)
648                         FireRailgunBullet(self.origin, o, 100000, 0, DEATH_HURTTRIGGER);
649                 else
650                         FireRailgunBullet(self.origin, o, self.dmg * frametime, 0, DEATH_HURTTRIGGER);
651         }
652
653         if(time > self.ltime)
654         {
655                 traceline(self.origin, o, MOVE_WORLDONLY, self);
656                 trailparticles(self, self.cnt, self.origin, trace_endpos);
657                 pointparticles(self.lip, trace_endpos, trace_plane_normal, 256 * frametime);
658                 self.ltime = time + self.wait;
659         }
660         self.nextthink = time;
661 }
662 /*QUAKED spawnfunc_misc_laser (.5 .5 .5) ?
663 Any object touching the beam will be hurt
664 Keys:
665 "target"
666  spawnfunc_target_position where the laser ends
667 "mdl"
668  name of beam effect to use
669 "dmg"
670  damage per second (-1 for a laser that kills immediately)
671 "wait"
672  delay between sending the particle effect
673 */
674 void spawnfunc_misc_laser()
675 {
676         if(self.mdl)
677         {
678                 self.cnt = particleeffectnum(self.mdl);
679                 self.lip = particleeffectnum(strcat(self.mdl, "_end"));
680         }
681         else
682         {
683                 self.cnt = particleeffectnum("misc_laser_beam");
684                 self.lip = particleeffectnum("misc_laser_beam_end");
685         }
686         if(!self.wait)
687                 self.wait = 1;
688         if(!self.message)
689                 self.message = "saw the light";
690         self.think = misc_laser_think;
691         self.nextthink = time;
692 }
693
694 // tZorks trigger impulse / gravity
695 .float radius;
696 .float falloff;
697 .float strength;
698 .float lastpushtime;
699
700 // targeted (directional) mode
701 void trigger_impulse_touch1()
702 {
703         entity targ;
704     float pushdeltatime;
705     float str;
706
707         // FIXME: Better checking for what to push and not.
708         if (other.classname != "player")
709         if (other.classname != "corpse")
710         if (other.classname != "body")
711         if (other.classname != "gib")
712         if (other.classname != "missile")
713         if (other.classname != "casing")
714         if (other.classname != "grenade")
715         if (other.classname != "plasma")
716         if (other.classname != "plasma_prim")
717         if (other.classname != "plasma_chain")
718         if (other.classname != "droppedweapon")
719                 return;
720
721         if (other.deadflag && other.classname == "player")
722                 return;
723
724         EXACTTRIGGER_TOUCH;
725
726     targ = find(world, targetname, self.target);
727     if(!targ)
728     {
729         objerror("trigger_force without a (valid) .target!\n");
730         remove(self);
731         return;
732     }
733
734     if(self.falloff == 1)
735         str = (str / self.radius) * self.strength;
736     else if(self.falloff == 2)
737         str = (1 - (str / self.radius)) * self.strength;
738     else
739         str = self.strength;
740
741     pushdeltatime = time - other.lastpushtime;
742     if (pushdeltatime > 0.15) pushdeltatime = 0;
743     other.lastpushtime = time;
744     if(!pushdeltatime) return;
745
746     other.velocity = other.velocity + normalize(targ.origin - self.origin) * self.strength * pushdeltatime;
747 }
748
749 // Directionless (accelerator/decelerator) mode
750 void trigger_impulse_touch2()
751 {
752     float pushdeltatime;
753
754         // FIXME: Better checking for what to push and not.
755         if (other.classname != "player")
756         if (other.classname != "corpse")
757         if (other.classname != "body")
758         if (other.classname != "gib")
759         if (other.classname != "missile")
760         if (other.classname != "casing")
761         if (other.classname != "grenade")
762         if (other.classname != "plasma")
763         if (other.classname != "plasma_prim")
764         if (other.classname != "plasma_chain")
765         if (other.classname != "droppedweapon")
766                 return;
767
768         if (other.deadflag && other.classname == "player")
769                 return;
770
771         EXACTTRIGGER_TOUCH;
772
773     pushdeltatime = time - other.lastpushtime;
774     if (pushdeltatime > 0.15) pushdeltatime = 0;
775     other.lastpushtime = time;
776     if(!pushdeltatime) return;
777
778     //if(self.strength > 1)
779         other.velocity = other.velocity * (self.strength * pushdeltatime);
780     //else
781     //    other.velocity = other.velocity - (other.velocity * self.strength * pushdeltatime);
782 }
783
784 // Spherical (gravity/repulsor) mode
785 void trigger_impulse_touch3()
786 {
787     float pushdeltatime;
788     float str;
789
790         // FIXME: Better checking for what to push and not.
791         if (other.classname != "player")
792         if (other.classname != "corpse")
793         if (other.classname != "body")
794         if (other.classname != "gib")
795         if (other.classname != "missile")
796         if (other.classname != "casing")
797         if (other.classname != "grenade")
798         if (other.classname != "plasma")
799         if (other.classname != "plasma_prim")
800         if (other.classname != "plasma_chain")
801         if (other.classname != "droppedweapon")
802                 return;
803
804         if (other.deadflag && other.classname == "player")
805                 return;
806
807         EXACTTRIGGER_TOUCH;
808
809     pushdeltatime = time - other.lastpushtime;
810     if (pushdeltatime > 0.15) pushdeltatime = 0;
811     other.lastpushtime = time;
812     if(!pushdeltatime) return;
813
814     setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
815
816         str = min(self.radius, vlen(self.origin - other.origin));
817
818     if(self.falloff == 1)
819         str = (1 - str / self.radius) * self.strength; // 1 in the inside
820     else if(self.falloff == 2)
821         str = (str / self.radius) * self.strength; // 0 in the inside
822     else
823         str = self.strength;
824
825     other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime;
826 }
827
828 /*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
829 -------- KEYS --------
830 target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
831          If not, this trigger acts like a damper/accelerator field.
832
833 strength : This is how mutch force to add in the direction of .target each second
834            when .target is set. If not, this is hoe mutch to slow down/accelerate
835            someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
836
837 radius   : If set, act as a spherical device rather then a liniar one.
838
839 falloff : 0 = none, 1 = liniar, 2 = inverted liniar
840
841 -------- NOTES --------
842 Use a brush textured with common/origin in the trigger entity to determine the origin of the force
843 in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
844 */
845
846 void spawnfunc_trigger_impulse()
847 {
848         EXACTTRIGGER_INIT;
849     if(self.radius)
850     {
851         if(!self.strength) self.strength = 2000;
852         setorigin(self, self.origin);
853         setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
854         self.touch = trigger_impulse_touch3;
855     }
856     else
857     {
858         if(self.target)
859         {
860             if(!self.strength) self.strength = 950;
861             self.touch = trigger_impulse_touch1;
862         }
863         else
864         {
865             if(!self.strength) self.strength = 0.9;
866             self.touch = trigger_impulse_touch2;
867         }
868     }
869 }
870