]> icculus.org git repositories - divverent/nexuiz.git/blob - qcsrc/gamec/g_triggers.c
hurt no longer waits 1 second between hurts
[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 = 5;
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