]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/g_triggers.qc
two big changes:
[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                         sound (activator, CHAN_VOICE, "misc/talk.wav", VOL_BASE, ATTN_NORM);
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, CHAN_VOICE, 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         if (!other.owner)
359         {
360                 if (other.items & IT_KEY1 || other.items & IT_KEY2)     // reset flag
361                         other.pain_finished = min(other.pain_finished, time + 2);
362                 else if (other.classname == "rune")                     // reset runes
363                         other.nextthink = min(other.nextthink, time + 1);
364         }
365
366         if (other.takedamage)
367         if (other.triggerhurttime < time)
368         {
369                 other.triggerhurttime = time + 1;
370                 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
371         }
372
373         return;
374 };
375
376 /*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ?
377 Any object touching this will be hurt
378 set dmg to damage amount
379 defalt dmg = 5
380 */
381 .entity trigger_hurt_next;
382 entity trigger_hurt_last;
383 entity trigger_hurt_first;
384 void spawnfunc_trigger_hurt()
385 {
386         InitTrigger ();
387         self.touch = trigger_hurt_touch;
388         if (!self.dmg)
389                 self.dmg = 1000;
390         if (!self.message)
391                 self.message = "was in the wrong place.";
392
393         if(!trigger_hurt_first)
394                 trigger_hurt_first = self;
395         if(trigger_hurt_last)
396                 trigger_hurt_last.trigger_hurt_next = self;
397         trigger_hurt_last = self;
398 };
399
400 float trace_hits_box_a0, trace_hits_box_a1;
401
402 float trace_hits_box_1d(float end, float thmi, float thma)
403 {
404         if(end == 0)
405         {
406                 // just check if x is in range
407                 if(0 < thmi)
408                         return FALSE;
409                 if(0 > thma)
410                         return FALSE;
411         }
412         else
413         {
414                 // do the trace with respect to x
415                 // 0 -> end has to stay in thmi -> thma
416                 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
417                 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
418                 if(trace_hits_box_a0 > trace_hits_box_a1)
419                         return FALSE;
420         }
421         return TRUE;
422 }
423
424 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
425 {
426         end -= start;
427         thmi -= start;
428         thma -= start;
429         // now it is a trace from 0 to end
430
431         trace_hits_box_a0 = 0;
432         trace_hits_box_a1 = 1;
433
434         if(!trace_hits_box_1d(end_x, thmi_x, thma_x))
435                 return FALSE;
436         if(!trace_hits_box_1d(end_y, thmi_y, thma_y))
437                 return FALSE;
438         if(!trace_hits_box_1d(end_z, thmi_z, thma_z))
439                 return FALSE;
440
441         return TRUE;
442 }
443
444 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
445 {
446         return trace_hits_box(start, end, thmi - ma, thma - mi);
447 }
448
449 float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end)
450 {
451         entity th;
452
453         for(th = trigger_hurt_first; th; th = th.trigger_hurt_next)
454                 if(tracebox_hits_box(start, mi, ma, end, th.absmin, th.absmax))
455                         return TRUE;
456
457         return FALSE;
458 }
459
460
461 void target_speaker_use() {sound(self, CHAN_VOICE, self.noise, VOL_BASE, ATTN_NORM);}
462
463 void spawnfunc_target_speaker()
464 {
465         if(self.noise)
466                 precache_sound (self.noise);
467         if(self.targetname)
468                 self.use = target_speaker_use;
469         else
470                 ambientsound (self.origin, self.noise, VOL_BASE, ATTN_STATIC);
471 };
472
473
474 void spawnfunc_func_stardust() {
475         self.effects = EF_STARDUST;
476 }
477
478
479 /*
480 void sparksthink()
481 {
482   self.nextthink = time + 0.1;
483
484   if(random() < self.wait) {
485     te_spark(self.origin,'0 0 -1',self.cnt);
486   }
487 }
488
489
490 void func_sparks()
491 {
492   self.think = sparksthink;
493   self.nextthink = time + 0.2;
494
495   // self.cnt is the amount of sparks that one burst will spawn
496   if(self.cnt < 1) {
497     self.cnt = 25.0; // nice default value
498   }
499
500   // self.wait is the probability that a sparkthink will spawn a spark shower
501   // range: 0 - 1, but 0 makes little sense, so...
502   if(self.wait < 0.05) {
503     self.wait = 0.25; // nice default value
504   }
505
506   // sound
507   if(self.noise) {
508     precache_sound (self.noise);
509     ambientsound (self.origin, self.noise, VOL_BASE, ATTN_STATIC);
510   }
511 }
512 */
513 void rain_think()
514 {
515         self.nextthink = time + 0.1;
516         te_particlerain(self.absmin, self.absmax, self.dest, self.count, self.cnt);
517 //      te_particlesnow(self.absmin, self.absmax, self.dest * 0.25, self.count, self.cnt);
518 //      WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
519 //      WriteByte (MSG_BROADCAST, TE_PARTICLERAIN);
520 //      WriteVec (MSG_BROADCAST, self.absmin);
521 //      WriteVec (MSG_BROADCAST, self.absmax);
522 //      WriteVec (MSG_BROADCAST, self.dest);
523 //      WriteShort (MSG_BROADCAST, self.count);
524 //      WriteByte (MSG_BROADCAST, self.cnt);
525 };
526
527 /*QUAKED spawnfunc_func_rain (0 .5 .8) ?
528 This is an invisible area like a trigger, which rain falls inside of.
529
530 Keys:
531 "velocity"
532  falling direction (should be something like '0 0 -700', use the X and Y velocity for wind)
533 "cnt"
534  sets color of rain (default 12 - white)
535 "count"
536  adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
537 */
538 void spawnfunc_func_rain()
539 {
540         self.dest = self.velocity;
541         self.velocity = '0 0 0';
542         if (!self.dest)
543                 self.dest = '0 0 -700';
544         self.angles = '0 0 0';
545         self.movetype = MOVETYPE_NONE;
546         self.solid = SOLID_NOT;
547         if(self.model != "")
548                 setmodel(self, self.model); // no precision needed
549         setorigin(self, self.origin);
550         setsize(self, self.mins, self.maxs);
551         self.model = "";
552         if (!self.cnt)
553                 self.cnt = 12;
554         if (!self.count)
555                 self.count = 2000;
556         self.count = 0.1 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
557         if (self.count < 1)
558         {
559                 remove(self);
560                 return;
561         }
562         // convert from per second to per 0.1 sec,
563         self.count = ceil(self.count * 0.1);
564         self.think = rain_think;
565         self.nextthink = time + 0.5;
566 };
567
568
569 void snow_think()
570 {
571         self.nextthink = time + 0.1 + random() * 0.05;
572         te_particlesnow(self.absmin, self.absmax, self.dest, self.count, self.cnt);
573 //      WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
574 //      WriteByte (MSG_BROADCAST, TE_PARTICLESNOW);
575 //      WriteVec (MSG_BROADCAST, self.absmin);
576 //      WriteVec (MSG_BROADCAST, self.absmax);
577 //      WriteVec (MSG_BROADCAST, self.dest);
578 //      WriteShort (MSG_BROADCAST, self.count);
579 //      WriteByte (MSG_BROADCAST, self.cnt);
580 };
581
582 /*QUAKED spawnfunc_func_snow (0 .5 .8) ?
583 This is an invisible area like a trigger, which snow falls inside of.
584
585 Keys:
586 "velocity"
587  falling direction (should be something like '0 0 -300', use the X and Y velocity for wind)
588 "cnt"
589  sets color of rain (default 12 - white)
590 "count"
591  adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
592 */
593 void spawnfunc_func_snow()
594 {
595         self.dest = self.velocity;
596         self.velocity = '0 0 0';
597         if (!self.dest)
598                 self.dest = '0 0 -300';
599         self.angles = '0 0 0';
600         self.movetype = MOVETYPE_NONE;
601         self.solid = SOLID_NOT;
602         if(self.model != "")
603                 setmodel(self, self.model); // no precision needed
604         setorigin(self, self.origin);
605         setsize(self, self.mins, self.maxs);
606         self.model = "";
607         if (!self.cnt)
608                 self.cnt = 12;
609         if (!self.count)
610                 self.count = 2000;
611         self.count = 0.1 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
612         if (self.count < 1)
613         {
614                 remove(self);
615                 return;
616         }
617         // convert from per second to per 0.1 sec,
618         self.count = ceil(self.count * 0.1);
619         self.think = snow_think;
620         self.nextthink = time + 0.5;
621 };
622
623
624 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float deathtype);
625 void misc_laser_think()
626 {
627         vector o;
628         if(!self.state)
629         {
630                 self.enemy = find(world, targetname, self.target);
631                 self.state = 1;
632         }
633         if(self.enemy)
634         {
635                 o = self.enemy.origin;
636         }
637         else
638         {
639                 makevectors(self.angles);
640                 o = self.origin + v_forward * MAX_SHOT_DISTANCE;
641         }
642
643         if(self.dmg)
644         {
645                 if(self.dmg < 0)
646                         FireRailgunBullet(self.origin, o, 100000, 0, DEATH_HURTTRIGGER);
647                 else
648                         FireRailgunBullet(self.origin, o, self.dmg * frametime, 0, DEATH_HURTTRIGGER);
649         }
650
651         if(time > self.ltime)
652         {
653                 traceline(self.origin, o, MOVE_WORLDONLY, self);
654                 trailparticles(self, self.cnt, self.origin, trace_endpos);
655                 pointparticles(self.lip, trace_endpos, trace_plane_normal, 256 * frametime);
656                 self.ltime = time + self.wait;
657         }
658         self.nextthink = time;
659 }
660 /*QUAKED spawnfunc_misc_laser (.5 .5 .5) ?
661 Any object touching the beam will be hurt
662 Keys:
663 "target"
664  spawnfunc_target_position where the laser ends
665 "mdl"
666  name of beam effect to use
667 "dmg"
668  damage per second (-1 for a laser that kills immediately)
669 "wait"
670  delay between sending the particle effect
671 */
672 void spawnfunc_misc_laser()
673 {
674         if(self.mdl)
675         {
676                 self.cnt = particleeffectnum(self.mdl);
677                 self.lip = particleeffectnum(strcat(self.mdl, "_end"));
678         }
679         else
680         {
681                 self.cnt = particleeffectnum("misc_laser_beam");
682                 self.lip = particleeffectnum("misc_laser_beam_end");
683         }
684         if(!self.wait)
685                 self.wait = 1;
686         if(!self.message)
687                 self.message = "saw the light";
688         self.think = misc_laser_think;
689         self.nextthink = time;
690 }
691
692 // tZorks trigger impulse / gravity
693 .float radius;
694 .float falloff;
695 .float strength;
696 .float lastpushtime;
697
698 // targeted (directional) mode
699 void trigger_impulse_touch1()
700 {
701         entity targ;
702     float pushdeltatime;
703     float str;
704
705         // FIXME: Better checking for what to push and not.
706         if (other.classname != "player")
707         if (other.classname != "corpse")
708         if (other.classname != "body")
709         if (other.classname != "gib")
710         if (other.classname != "missile")
711         if (other.classname != "casing")
712         if (other.classname != "grenade")
713         if (other.classname != "plasma")
714         if (other.classname != "plasma_prim")
715         if (other.classname != "plasma_chain")
716         if (other.classname != "droppedweapon")
717                 return;
718
719         if (other.deadflag && other.classname == "player")
720                 return;
721
722     targ = find(world, targetname, self.target);
723     if(!targ)
724     {
725         objerror("trigger_force without a (valid) .target!\n");
726         remove(self);
727         return;
728     }
729
730     if(self.falloff == 1)
731         str = (str / self.radius) * self.strength;
732     else if(self.falloff == 2)
733         str = (1 - (str / self.radius)) * self.strength;
734     else
735         str = self.strength;
736
737     pushdeltatime = time - other.lastpushtime;
738     if (pushdeltatime > 0.15) pushdeltatime = 0;
739     other.lastpushtime = time;
740     if(!pushdeltatime) return;
741
742     other.velocity = other.velocity + normalize(targ.origin - self.origin) * self.strength * pushdeltatime;
743 }
744
745 // Directionless (accelerator/decelerator) mode
746 void trigger_impulse_touch2()
747 {
748     float pushdeltatime;
749
750         // FIXME: Better checking for what to push and not.
751         if (other.classname != "player")
752         if (other.classname != "corpse")
753         if (other.classname != "body")
754         if (other.classname != "gib")
755         if (other.classname != "missile")
756         if (other.classname != "casing")
757         if (other.classname != "grenade")
758         if (other.classname != "plasma")
759         if (other.classname != "plasma_prim")
760         if (other.classname != "plasma_chain")
761         if (other.classname != "droppedweapon")
762                 return;
763
764         if (other.deadflag && other.classname == "player")
765                 return;
766
767     pushdeltatime = time - other.lastpushtime;
768     if (pushdeltatime > 0.15) pushdeltatime = 0;
769     other.lastpushtime = time;
770     if(!pushdeltatime) return;
771
772     //if(self.strength > 1)
773         other.velocity = other.velocity * (self.strength * pushdeltatime);
774     //else
775     //    other.velocity = other.velocity - (other.velocity * self.strength * pushdeltatime);
776 }
777
778 // Spherical (gravity/repulsor) mode
779 void trigger_impulse_touch3()
780 {
781     float pushdeltatime;
782     float str;
783
784         // FIXME: Better checking for what to push and not.
785         if (other.classname != "player")
786         if (other.classname != "corpse")
787         if (other.classname != "body")
788         if (other.classname != "gib")
789         if (other.classname != "missile")
790         if (other.classname != "casing")
791         if (other.classname != "grenade")
792         if (other.classname != "plasma")
793         if (other.classname != "plasma_prim")
794         if (other.classname != "plasma_chain")
795         if (other.classname != "droppedweapon")
796                 return;
797
798         if (other.deadflag && other.classname == "player")
799                 return;
800
801     pushdeltatime = time - other.lastpushtime;
802     if (pushdeltatime > 0.15) pushdeltatime = 0;
803     other.lastpushtime = time;
804     if(!pushdeltatime) return;
805
806     setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
807
808         str = min(self.radius, vlen(self.origin - other.origin));
809
810     if(self.falloff == 1)
811         str = (1 - str / self.radius) * self.strength; // 1 in the inside
812     else if(self.falloff == 2)
813         str = (str / self.radius) * self.strength; // 0 in the inside
814     else
815         str = self.strength;
816
817     other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime;
818 }
819
820 /*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
821 -------- KEYS --------
822 target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
823          If not, this trigger acts like a damper/accelerator field.
824
825 strength : This is how mutch force to add in the direction of .target each second
826            when .target is set. If not, this is hoe mutch to slow down/accelerate
827            someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
828
829 radius   : If set, act as a spherical device rather then a liniar one.
830
831 falloff : 0 = none, 1 = liniar, 2 = inverted liniar
832
833 -------- NOTES --------
834 Use a brush textured with common/origin in the trigger entity to determine the origin of the force
835 in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
836 */
837
838 void spawnfunc_trigger_impulse()
839 {
840     InitTrigger ();
841     if(self.radius)
842     {
843         if(!self.strength) self.strength = 2000;
844         setorigin(self, self.origin);
845         setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
846         self.touch = trigger_impulse_touch3;
847     }
848     else
849     {
850         if(self.target)
851         {
852             if(!self.strength) self.strength = 950;
853             self.touch = trigger_impulse_touch1;
854         }
855         else
856         {
857             if(!self.strength) self.strength = 0.9;
858             self.touch = trigger_impulse_touch2;
859         }
860     }
861 }
862