centerprints now are managed independently per line
[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                 centermsg_setfor (activator, CENTERMSG_TRIGGER, 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                                 centermsg_setfor (activator, CENTERMSG_TRIGGER, "There are more to go...");
311                         else if (self.count == 3)
312                                 centermsg_setfor (activator, CENTERMSG_TRIGGER, "Only 3 more to go...");
313                         else if (self.count == 2)
314                                 centermsg_setfor (activator, CENTERMSG_TRIGGER, "Only 2 more to go...");
315                         else
316                                 centermsg_setfor (activator, CENTERMSG_TRIGGER, "Only 1 more to go...");
317                 }
318                 return;
319         }
320
321         if (activator.classname == "player"
322         && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
323                 centermsg_setfor(activator, CENTERMSG_TRIGGER, "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.owner)
348         {
349                 if (other.items & IT_KEY1 || other.items & IT_KEY2)     // reset flag
350                         other.pain_finished = time + 2;
351                 else if (other.classname == "rune")                     // reset runes
352                         other.nextthink = time + 1;
353         }
354
355         if (other.takedamage)
356         if (other.triggerhurttime < time)
357         {
358                 other.triggerhurttime = time + 1;
359                 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
360         }
361
362         return;
363 };
364
365 /*QUAKED trigger_hurt (.5 .5 .5) ?
366 Any object touching this will be hurt
367 set dmg to damage amount
368 defalt dmg = 5
369 */
370 void() trigger_hurt =
371 {
372         InitTrigger ();
373         self.touch = hurt_touch;
374         if (!self.dmg)
375                 self.dmg = 1000;
376         if (!self.message)
377                 self.message = "was in the wrong place.";
378 };
379
380 //void() target_speaker_use = {sound(self, CHAN_VOICE, self.noise, 1, 1);}
381 //void() target_speaker = {self.use = target_speaker_use;}
382
383 void() target_speaker =
384 {
385 if(self.noise) {
386  precache_sound (self.noise);
387  ambientsound (self.origin, self.noise, 1, ATTN_STATIC);
388 }
389 //remove(self);
390 };
391
392
393 void() func_stardust {
394         self.effects = EF_STARDUST;
395 }
396
397
398 /*
399 void() sparksthink =
400 {
401   self.nextthink = time + 0.1;
402
403   if(random() < self.wait) {
404     te_spark(self.origin,'0 0 -1',self.cnt);
405   }
406 }
407
408
409 void() func_sparks =
410 {
411   self.think = sparksthink;
412   self.nextthink = time + 0.2;
413
414   // self.cnt is the amount of sparks that one burst will spawn
415   if(self.cnt < 1) {
416     self.cnt = 25.0; // nice default value
417   }
418
419   // self.wait is the probability that a sparkthink will spawn a spark shower
420   // range: 0 - 1, but 0 makes little sense, so...
421   if(self.wait < 0.05) {
422     self.wait = 0.25; // nice default value
423   }
424
425   // sound
426   if(self.noise) {
427     precache_sound (self.noise);
428     ambientsound (self.origin, self.noise, 1, ATTN_STATIC);
429   }
430 }
431 */
432
433