]> icculus.org git repositories - divverent/nexuiz.git/blob - qcsrc/gamec/g_triggers.c
added cl_movement_* defaults and some other stuff
[divverent/nexuiz.git] / qcsrc / gamec / g_triggers.c
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", 1, 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 void() trigger_reactivate =
109 {
110         self.solid = SOLID_TRIGGER;
111 };
112
113 //=============================================================================
114
115 float   SPAWNFLAG_NOMESSAGE = 1;
116 float   SPAWNFLAG_NOTOUCH = 1;
117
118 // the wait time has passed, so set back up for another activation
119 void() multi_wait =
120 {
121         if (self.max_health)
122         {
123                 self.health = self.max_health;
124                 self.takedamage = DAMAGE_YES;
125                 self.solid = SOLID_BBOX;
126         }
127 };
128
129
130 // the trigger was just touched/killed/used
131 // self.enemy should be set to the activator so it can be held through a delay
132 // so wait for the delay time before firing
133 void() multi_trigger =
134 {
135         if (self.nextthink > time)
136         {
137                 return;         // allready been triggered
138         }
139
140         if (self.classname == "trigger_secret")
141         {
142                 if (self.enemy.classname != "player")
143                         return;
144                 found_secrets = found_secrets + 1;
145                 WriteByte (MSG_ALL, SVC_FOUNDSECRET);
146         }
147
148         if (self.noise)
149                 sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);
150
151 // don't trigger again until reset
152         self.takedamage = DAMAGE_NO;
153
154         activator = self.enemy;
155
156         SUB_UseTargets();
157
158         if (self.wait > 0)
159         {
160                 self.think = multi_wait;
161                 self.nextthink = time + self.wait;
162         }
163         else
164         {       // we can't just remove (self) here, because this is a touch function
165                 // called wheil C code is looping through area links...
166                 self.touch = SUB_Null;
167
168                 self.nextthink = time + 0.1;
169                 self.think = SUB_Remove;
170         }
171 };
172
173 void() multi_use =
174 {
175         self.enemy = activator;
176         multi_trigger();
177 };
178
179 void() multi_touch =
180 {
181         if (other.classname != "player")
182                 return;
183
184 // if the trigger has an angles field, check player's facing direction
185         if (self.movedir != '0 0 0')
186         {
187                 makevectors (other.angles);
188                 if (v_forward * self.movedir < 0)
189                         return;         // not facing the right way
190         }
191
192         self.enemy = other;
193         multi_trigger ();
194 };
195
196 void multi_eventdamage (vector hitloc, float damage, entity inflictor, entity attacker, float deathtype)
197 {
198         if (!self.takedamage)
199                 return;
200         self.health = self.health - damage;
201         if (self.health <= 0)
202         {
203                 self.enemy = attacker;
204                 multi_trigger();
205         }
206 }
207
208 /*QUAKED trigger_multiple (.5 .5 .5) ? notouch
209 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.
210 If "delay" is set, the trigger waits some time after activating before firing.
211 "wait" : Seconds between triggerings. (.2 default)
212 If notouch is set, the trigger is only fired by other entities, not by touching.
213 NOTOUCH has been obsoleted by trigger_relay!
214 sounds
215 1)      secret
216 2)      beep beep
217 3)      large switch
218 4)
219 set "message" to text string
220 */
221 void() trigger_multiple =
222 {
223         if (self.sounds == 1)
224         {
225                 precache_sound ("misc/secret.wav");
226                 self.noise = "misc/secret.wav";
227         }
228         else if (self.sounds == 2)
229         {
230                 precache_sound ("misc/talk.wav");
231                 self.noise = "misc/talk.wav";
232         }
233         else if (self.sounds == 3)
234         {
235                 precache_sound ("misc/trigger1.wav");
236                 self.noise = "misc/trigger1.wav";
237         }
238
239         if (!self.wait)
240                 self.wait = 0.2;
241         self.use = multi_use;
242
243         InitTrigger ();
244
245         if (self.health)
246         {
247                 if (self.spawnflags & SPAWNFLAG_NOTOUCH)
248                         objerror ("health and notouch don't make sense\n");
249                 self.max_health = self.health;
250                 self.event_damage = multi_eventdamage;
251                 self.takedamage = DAMAGE_YES;
252                 self.solid = SOLID_BBOX;
253                 setorigin (self, self.origin);  // make sure it links into the world
254         }
255         else
256         {
257                 if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
258                 {
259                         self.touch = multi_touch;
260                 }
261         }
262 };
263
264
265 /*QUAKED trigger_once (.5 .5 .5) ? notouch
266 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
267 "targetname".  If "health" is set, the trigger must be killed to activate.
268 If notouch is set, the trigger is only fired by other entities, not by touching.
269 if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
270 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.
271 sounds
272 1)      secret
273 2)      beep beep
274 3)      large switch
275 4)
276 set "message" to text string
277 */
278 void() trigger_once =
279 {
280         self.wait = -1;
281         trigger_multiple();
282 };
283
284 //=============================================================================
285
286 /*QUAKED trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
287 This fixed size trigger cannot be touched, it can only be fired by other events.  It can contain killtargets, targets, delays, and messages.
288 */
289 void() trigger_relay =
290 {
291         self.use = SUB_UseTargets;
292 };
293
294
295 //=============================================================================
296
297
298 void() counter_use =
299 {
300         self.count = self.count - 1;
301         if (self.count < 0)
302                 return;
303
304         if (self.count != 0)
305         {
306                 if (activator.classname == "player"
307                 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
308                 {
309                         if (self.count >= 4)
310                                 centerprint (activator, "There are more to go...");
311                         else if (self.count == 3)
312                                 centerprint (activator, "Only 3 more to go...");
313                         else if (self.count == 2)
314                                 centerprint (activator, "Only 2 more to go...");
315                         else
316                                 centerprint (activator, "Only 1 more to go...");
317                 }
318                 return;
319         }
320
321         if (activator.classname == "player"
322         && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
323                 centerprint(activator, "Sequence completed!");
324         self.enemy = activator;
325         multi_trigger ();
326 };
327
328 /*QUAKED trigger_counter (.5 .5 .5) ? nomessage
329 Acts as an intermediary for an action that takes multiple inputs.
330
331 If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
332
333 After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
334 */
335 void() trigger_counter =
336 {
337         self.wait = -1;
338         if (!self.count)
339                 self.count = 2;
340
341         self.use = counter_use;
342 };
343
344 .float triggerhurttime;
345 void() hurt_touch =
346 {
347         if (other.takedamage)
348         if (other.triggerhurttime < time)
349         {
350                 other.triggerhurttime = time + 1;
351                 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
352         }
353
354         return;
355 };
356
357 /*QUAKED trigger_hurt (.5 .5 .5) ?
358 Any object touching this will be hurt
359 set dmg to damage amount
360 defalt dmg = 5
361 */
362 void() trigger_hurt =
363 {
364         InitTrigger ();
365         self.touch = hurt_touch;
366         if (!self.dmg)
367                 self.dmg = 1000;
368         if (!self.message)
369                 self.message = "was in the wrong place.";
370 };
371
372 //void() target_speaker_use = {sound(self, CHAN_VOICE, self.noise, 1, 1);}
373 //void() target_speaker = {self.use = target_speaker_use;}
374
375 void() target_speaker =
376 {
377 if(self.noise) {
378  precache_sound (self.noise);
379  ambientsound (self.origin, self.noise, 1, ATTN_STATIC);
380 }
381 //remove(self);
382 };
383
384
385 /*
386 void() sparksthink =
387 {
388   self.nextthink = time + 0.1;
389
390   if(random() < self.wait) {
391     te_spark(self.origin,'0 0 -1',self.cnt);
392   }
393 }
394
395
396 void() func_sparks =
397 {
398   self.think = sparksthink;
399   self.nextthink = time + 0.2;
400
401   // self.cnt is the amount of sparks that one burst will spawn
402   if(self.cnt < 1) {
403     self.cnt = 25.0; // nice default value
404   }
405
406   // self.wait is the probability that a sparkthink will spawn a spark shower
407   // range: 0 - 1, but 0 makes little sense, so...
408   if(self.wait < 0.05) {
409     self.wait = 0.25; // nice default value
410   }
411
412   // sound
413   if(self.noise) {
414     precache_sound (self.noise);
415     ambientsound (self.origin, self.noise, 1, ATTN_STATIC);
416   }
417 }
418 */
419
420
421 /*
422 =============================================================================
423
424 SECRET DOORS
425
426 =============================================================================
427 */
428
429 void() fd_secret_move1;
430 void() fd_secret_move2;
431 void() fd_secret_move3;
432 void() fd_secret_move4;
433 void() fd_secret_move5;
434 void() fd_secret_move6;
435 void() fd_secret_done;
436
437 float SECRET_OPEN_ONCE = 1;             // stays open
438 float SECRET_1ST_LEFT = 2;              // 1st move is left of arrow
439 float SECRET_1ST_DOWN = 4;              // 1st move is down from arrow
440 float SECRET_NO_SHOOT = 8;              // only opened by trigger
441 float SECRET_YES_SHOOT = 16;    // shootable even if targeted
442
443
444 void () fd_secret_use =
445 {
446         local float temp;
447
448         self.health = 10000;
449         //self.havocattack = TRUE;
450
451         // exit if still moving around...
452         if (self.origin != self.oldorigin)
453                 return;
454
455         self.message = ""; // no more message
456
457         SUB_UseTargets();                               // fire all targets / killtargets
458
459         self.velocity = '0 0 0';
460
461         // Make a sound, wait a little...
462
463         if (self.noise1 != "")
464                 sound(self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);
465         self.nextthink = self.ltime + 0.1;
466
467         temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
468         makevectors(self.mangle);
469
470         if (!self.t_width)
471         {
472                 if (self.spawnflags & SECRET_1ST_DOWN)
473                         self.t_width = fabs(v_up * self.size);
474                 else
475                         self.t_width = fabs(v_right * self.size);
476         }
477
478         if (!self.t_length)
479                 self.t_length = fabs(v_forward * self.size);
480
481         if (self.spawnflags & SECRET_1ST_DOWN)
482                 self.dest1 = self.origin - v_up * self.t_width;
483         else
484                 self.dest1 = self.origin + v_right * (self.t_width * temp);
485
486         self.dest2 = self.dest1 + v_forward * self.t_length;
487         SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
488         if (self.noise2 != "")
489                 sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
490 };
491
492 // Wait after first movement...
493 void () fd_secret_move1 =
494 {
495         self.nextthink = self.ltime + 1.0;
496         self.think = fd_secret_move2;
497         if (self.noise3 != "")
498                 sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
499 };
500
501 // Start moving sideways w/sound...
502 void () fd_secret_move2 =
503 {
504         if (self.noise2 != "")
505                 sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
506         SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
507 };
508
509 // Wait here until time to go back...
510 void () fd_secret_move3 =
511 {
512         if (self.noise3 != "")
513                 sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
514         if (!(self.spawnflags & SECRET_OPEN_ONCE))
515         {
516                 self.nextthink = self.ltime + self.wait;
517                 self.think = fd_secret_move4;
518         }
519 };
520
521 // Move backward...
522 void () fd_secret_move4 =
523 {
524         if (self.noise2 != "")
525                 sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
526         SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
527 };
528
529 // Wait 1 second...
530 void () fd_secret_move5 =
531 {
532         self.nextthink = self.ltime + 1.0;
533         self.think = fd_secret_move6;
534         if (self.noise3 != "")
535                 sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
536 };
537
538 void () fd_secret_move6 =
539 {
540         if (self.noise2 != "")
541                 sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
542         SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
543 };
544
545 void () fd_secret_done =
546 {
547         if (!self.targetname || self.spawnflags&SECRET_YES_SHOOT)
548         {
549                 self.health = 10000;
550                 self.takedamage = DAMAGE_YES;
551                 //self.th_pain = fd_secret_use;
552         }
553         if (self.noise3 != "")
554                 sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
555 };
556
557 void () secret_blocked =
558 {
559         if (time < self.attack_finished)
560                 return;
561         self.attack_finished = time + 0.5;
562         //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
563 };
564
565 /*
566 ==============
567 secret_touch
568
569 Prints messages
570 ================
571 */
572 void() secret_touch =
573 {
574         if (activator.classname != "player")
575                 return;
576         if (self.attack_finished > time)
577                 return;
578
579         self.attack_finished = time + 2;
580
581         if (self.message)
582         {
583                 if (other.flags & FL_CLIENT)
584                         centerprint (other, self.message);
585                 sound (other, CHAN_BODY, "misc/talk.wav", 1, ATTN_NORM);
586         }
587 };
588
589
590 /*QUAKED func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
591 Basic secret door. Slides back, then to the side. Angle determines direction.
592 wait  = # of seconds before coming back
593 1st_left = 1st move is left of arrow
594 1st_down = 1st move is down from arrow
595 always_shoot = even if targeted, keep shootable
596 t_width = override WIDTH to move back (or height if going down)
597 t_length = override LENGTH to move sideways
598 "dmg"           damage to inflict when blocked (2 default)
599
600 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
601 "sounds"
602 1) medieval
603 2) metal
604 3) base
605 */
606
607 void () func_door_secret =
608 {
609         /*if (!self.deathtype) // map makers can override this
610                 self.deathtype = " got in the way";*/
611
612         if (!self.dmg)
613                 self.dmg = 2;
614
615         // Magic formula...
616         self.mangle = self.angles;
617         self.angles = '0 0 0';
618         self.solid = SOLID_BSP;
619         self.movetype = MOVETYPE_PUSH;
620         self.classname = "door";
621         setmodel (self, self.model);
622         setorigin (self, self.origin);
623
624         self.touch = secret_touch;
625         self.blocked = secret_blocked;
626         self.speed = 50;
627         self.use = fd_secret_use;
628         if ( !self.targetname || self.spawnflags&SECRET_YES_SHOOT)
629         {
630                 self.health = 10000;
631                 self.takedamage = DAMAGE_YES;
632                 self.event_damage = fd_secret_use;
633         }
634         self.oldorigin = self.origin;
635         if (!self.wait)
636                 self.wait = 5;          // 5 seconds before closing
637 };
638