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