1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 */
20 #include "g_local.h"
21
22
InitTrigger(edict_t * self)23 void InitTrigger (edict_t *self)
24 {
25 if (!VectorCompare (self->s.angles, vec3_origin))
26 G_SetMovedir (self->s.angles, self->movedir);
27
28 self->solid = SOLID_TRIGGER;
29 self->movetype = MOVETYPE_NONE;
30 gi.setmodel (self, self->model);
31 self->svflags = SVF_NOCLIENT;
32 }
33
34
35 // the wait time has passed, so set back up for another activation
multi_wait(edict_t * ent)36 void multi_wait (edict_t *ent)
37 {
38 ent->nextthink = 0;
39 }
40
41
42 // the trigger was just activated
43 // ent->activator should be set to the activator so it can be held through a delay
44 // so wait for the delay time before firing
multi_trigger(edict_t * ent)45 void multi_trigger (edict_t *ent)
46 {
47 if (ent->nextthink)
48 return; // already been triggered
49
50 G_UseTargets (ent, ent->activator);
51
52 if (ent->wait > 0)
53 {
54 ent->think = multi_wait;
55 ent->nextthink = level.time + ent->wait;
56 }
57 else
58 { // we can't just remove (self) here, because this is a touch function
59 // called while looping through area links...
60 ent->touch = NULL;
61 ent->nextthink = level.time + FRAMETIME;
62 ent->think = G_FreeEdict;
63 }
64 }
65
Use_Multi(edict_t * ent,edict_t * other,edict_t * activator)66 void Use_Multi (edict_t *ent, edict_t *other, edict_t *activator)
67 {
68 ent->activator = activator;
69 multi_trigger (ent);
70 }
71
Touch_Multi(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)72 void Touch_Multi (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
73 {
74 if(other->client)
75 {
76 if (self->spawnflags & 2)
77 return;
78 }
79 else if (other->svflags & SVF_MONSTER)
80 {
81 if (!(self->spawnflags & 1))
82 return;
83 }
84 else
85 return;
86
87 if (!VectorCompare(self->movedir, vec3_origin))
88 {
89 vec3_t forward;
90
91 AngleVectors(other->s.angles, forward, NULL, NULL);
92 if (_DotProduct(forward, self->movedir) < 0)
93 return;
94 }
95
96 self->activator = other;
97 multi_trigger (self);
98 }
99
100 /*QUAKED trigger_multiple (.5 .5 .5) ? MONSTER NOT_PLAYER TRIGGERED
101 Variable sized repeatable trigger. Must be targeted at one or more entities.
102 If "delay" is set, the trigger waits some time after activating before firing.
103 "wait" : Seconds between triggerings. (.2 default)
104 sounds
105 1) secret
106 2) beep beep
107 3) large switch
108 4)
109 set "message" to text string
110 */
trigger_enable(edict_t * self,edict_t * other,edict_t * activator)111 void trigger_enable (edict_t *self, edict_t *other, edict_t *activator)
112 {
113 self->solid = SOLID_TRIGGER;
114 self->use = Use_Multi;
115 gi.linkentity (self);
116 }
117
SP_trigger_multiple(edict_t * ent)118 void SP_trigger_multiple (edict_t *ent)
119 {
120 if (ent->sounds == 1)
121 ent->noise_index = gi.soundindex ("misc/secret.wav");
122 else if (ent->sounds == 2)
123 ent->noise_index = gi.soundindex ("misc/talk.wav");
124 else if (ent->sounds == 3)
125 ent->noise_index = gi.soundindex ("misc/trigger1.wav");
126
127 if (!ent->wait)
128 ent->wait = 0.2;
129 ent->touch = Touch_Multi;
130 ent->movetype = MOVETYPE_NONE;
131 ent->svflags |= SVF_NOCLIENT;
132
133
134 if (ent->spawnflags & 4)
135 {
136 ent->solid = SOLID_NOT;
137 ent->use = trigger_enable;
138 }
139 else
140 {
141 ent->solid = SOLID_TRIGGER;
142 ent->use = Use_Multi;
143 }
144
145 if (!VectorCompare(ent->s.angles, vec3_origin))
146 G_SetMovedir (ent->s.angles, ent->movedir);
147
148 gi.setmodel (ent, ent->model);
149 gi.linkentity (ent);
150 }
151
152
153 /*QUAKED trigger_once (.5 .5 .5) ? x x TRIGGERED
154 Triggers once, then removes itself.
155 You must set the key "target" to the name of another object in the level that has a matching "targetname".
156
157 If TRIGGERED, this trigger must be triggered before it is live.
158
159 sounds
160 1) secret
161 2) beep beep
162 3) large switch
163 4)
164
165 "message" string to be displayed when triggered
166 */
167
SP_trigger_once(edict_t * ent)168 void SP_trigger_once(edict_t *ent)
169 {
170 // make old maps work because I messed up on flag assignments here
171 // triggered was on bit 1 when it should have been on bit 4
172 if (ent->spawnflags & 1)
173 {
174 vec3_t v;
175
176 VectorMA (ent->mins, 0.5, ent->size, v);
177 ent->spawnflags &= ~1;
178 ent->spawnflags |= 4;
179 gi.dprintf("fixed TRIGGERED flag on %s at %s\n", ent->classname, vtos(v));
180 }
181
182 ent->wait = -1;
183 SP_trigger_multiple (ent);
184 }
185
186 /*QUAKED trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
187 This fixed size trigger cannot be touched, it can only be fired by other events.
188 */
trigger_relay_use(edict_t * self,edict_t * other,edict_t * activator)189 void trigger_relay_use (edict_t *self, edict_t *other, edict_t *activator)
190 {
191 G_UseTargets (self, activator);
192 }
193
SP_trigger_relay(edict_t * self)194 void SP_trigger_relay (edict_t *self)
195 {
196 self->use = trigger_relay_use;
197 }
198
199
200 /*
201 ==============================================================================
202
203 trigger_key
204
205 ==============================================================================
206 */
207
208 /*QUAKED trigger_key (.5 .5 .5) (-8 -8 -8) (8 8 8)
209 A relay trigger that only fires it's targets if player has the proper key.
210 Use "item" to specify the required key, for example "key_data_cd"
211 */
trigger_key_use(edict_t * self,edict_t * other,edict_t * activator)212 void trigger_key_use (edict_t *self, edict_t *other, edict_t *activator)
213 {
214 int index;
215
216 if (!self->item)
217 return;
218 if (!activator->client)
219 return;
220
221 index = ITEM_INDEX(self->item);
222 if (!activator->client->pers.inventory[index])
223 {
224 if (level.time < self->touch_debounce_time)
225 return;
226 self->touch_debounce_time = level.time + 5.0;
227 gi.centerprintf (activator, "You need the %s", self->item->pickup_name);
228 gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/keytry.wav"), 1, ATTN_NORM, 0);
229 return;
230 }
231
232 gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/keyuse.wav"), 1, ATTN_NORM, 0);
233 if (coop->value)
234 {
235 int player;
236 edict_t *ent;
237
238 if (strcmp(self->item->classname, "key_power_cube") == 0)
239 {
240 int cube;
241
242 for (cube = 0; cube < 8; cube++)
243 if (activator->client->pers.power_cubes & (1 << cube))
244 break;
245 for (player = 1; player <= game.maxclients; player++)
246 {
247 ent = &g_edicts[player];
248 if (!ent->inuse)
249 continue;
250 if (!ent->client)
251 continue;
252 if (ent->client->pers.power_cubes & (1 << cube))
253 {
254 ent->client->pers.inventory[index]--;
255 ent->client->pers.power_cubes &= ~(1 << cube);
256 }
257 }
258 }
259 else
260 {
261 for (player = 1; player <= game.maxclients; player++)
262 {
263 ent = &g_edicts[player];
264 if (!ent->inuse)
265 continue;
266 if (!ent->client)
267 continue;
268 ent->client->pers.inventory[index] = 0;
269 }
270 }
271 }
272 else
273 {
274 activator->client->pers.inventory[index]--;
275 }
276
277 G_UseTargets (self, activator);
278
279 self->use = NULL;
280 }
281
SP_trigger_key(edict_t * self)282 void SP_trigger_key (edict_t *self)
283 {
284 if (!st.item)
285 {
286 gi.dprintf("no key item for trigger_key at %s\n", vtos(self->s.origin));
287 return;
288 }
289 self->item = FindItemByClassname (st.item);
290
291 if (!self->item)
292 {
293 gi.dprintf("item %s not found for trigger_key at %s\n", st.item, vtos(self->s.origin));
294 return;
295 }
296
297 if (!self->target)
298 {
299 gi.dprintf("%s at %s has no target\n", self->classname, vtos(self->s.origin));
300 return;
301 }
302
303 gi.soundindex ("misc/keytry.wav");
304 gi.soundindex ("misc/keyuse.wav");
305
306 self->use = trigger_key_use;
307 }
308
309
310 /*
311 ==============================================================================
312
313 trigger_counter
314
315 ==============================================================================
316 */
317
318 /*QUAKED trigger_counter (.5 .5 .5) ? nomessage
319 Acts as an intermediary for an action that takes multiple inputs.
320
321 If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
322
323 After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
324 */
325
trigger_counter_use(edict_t * self,edict_t * other,edict_t * activator)326 void trigger_counter_use(edict_t *self, edict_t *other, edict_t *activator)
327 {
328 if (self->count == 0)
329 return;
330
331 self->count--;
332
333 if (self->count)
334 {
335 if (! (self->spawnflags & 1))
336 {
337 gi.centerprintf(activator, "%i more to go...", self->count);
338 gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
339 }
340 return;
341 }
342
343 if (! (self->spawnflags & 1))
344 {
345 gi.centerprintf(activator, "Sequence completed!");
346 gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
347 }
348 self->activator = activator;
349 multi_trigger (self);
350 }
351
SP_trigger_counter(edict_t * self)352 void SP_trigger_counter (edict_t *self)
353 {
354 self->wait = -1;
355 if (!self->count)
356 self->count = 2;
357
358 self->use = trigger_counter_use;
359 }
360
361
362 /*
363 ==============================================================================
364
365 trigger_always
366
367 ==============================================================================
368 */
369
370 /*QUAKED trigger_always (.5 .5 .5) (-8 -8 -8) (8 8 8)
371 This trigger will always fire. It is activated by the world.
372 */
SP_trigger_always(edict_t * ent)373 void SP_trigger_always (edict_t *ent)
374 {
375 // we must have some delay to make sure our use targets are present
376 if (ent->delay < 0.2)
377 ent->delay = 0.2;
378 G_UseTargets(ent, ent);
379 }
380
381
382 /*
383 ==============================================================================
384
385 trigger_push
386
387 ==============================================================================
388 */
389
390 #define PUSH_ONCE 1
391
392 static int windsound;
393
trigger_push_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)394 void trigger_push_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
395 {
396 if (strcmp(other->classname, "grenade") == 0)
397 {
398 VectorScale (self->movedir, self->speed * 10, other->velocity);
399 }
400 else if (other->health > 0)
401 {
402 VectorScale (self->movedir, self->speed * 10, other->velocity);
403
404 if (other->client)
405 {
406 // don't take falling damage immediately from this
407 VectorCopy (other->velocity, other->client->oldvelocity);
408 if (other->fly_sound_debounce_time < level.time)
409 {
410 other->fly_sound_debounce_time = level.time + 1.5;
411 gi.sound (other, CHAN_AUTO, windsound, 1, ATTN_NORM, 0);
412 }
413 }
414 }
415 if (self->spawnflags & PUSH_ONCE)
416 G_FreeEdict (self);
417 }
418
419
420 /*QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE
421 Pushes the player
422 "speed" defaults to 1000
423 */
SP_trigger_push(edict_t * self)424 void SP_trigger_push (edict_t *self)
425 {
426 InitTrigger (self);
427 windsound = gi.soundindex ("misc/windfly.wav");
428 self->touch = trigger_push_touch;
429 if (!self->speed)
430 self->speed = 1000;
431 gi.linkentity (self);
432 }
433
434
435 /*
436 ==============================================================================
437
438 trigger_hurt
439
440 ==============================================================================
441 */
442
443 /*QUAKED trigger_hurt (.5 .5 .5) ? START_OFF TOGGLE SILENT NO_PROTECTION SLOW
444 Any entity that touches this will be hurt.
445
446 It does dmg points of damage each server frame
447
448 SILENT supresses playing the sound
449 SLOW changes the damage rate to once per second
450 NO_PROTECTION *nothing* stops the damage
451
452 "dmg" default 5 (whole numbers only)
453
454 */
hurt_use(edict_t * self,edict_t * other,edict_t * activator)455 void hurt_use (edict_t *self, edict_t *other, edict_t *activator)
456 {
457 if (self->solid == SOLID_NOT)
458 self->solid = SOLID_TRIGGER;
459 else
460 self->solid = SOLID_NOT;
461 gi.linkentity (self);
462
463 if (!(self->spawnflags & 2))
464 self->use = NULL;
465 }
466
467
hurt_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)468 void hurt_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
469 {
470 int dflags;
471
472 if (!other->takedamage)
473 return;
474
475 if (self->timestamp > level.time)
476 return;
477
478 if (self->spawnflags & 16)
479 self->timestamp = level.time + 1;
480 else
481 self->timestamp = level.time + FRAMETIME;
482
483 if (!(self->spawnflags & 4))
484 {
485 if ((level.framenum % 10) == 0)
486 gi.sound (other, CHAN_AUTO, self->noise_index, 1, ATTN_NORM, 0);
487 }
488
489 if (self->spawnflags & 8)
490 dflags = DAMAGE_NO_PROTECTION;
491 else
492 dflags = 0;
493 T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, self->dmg, dflags, MOD_TRIGGER_HURT);
494 }
495
SP_trigger_hurt(edict_t * self)496 void SP_trigger_hurt (edict_t *self)
497 {
498 InitTrigger (self);
499
500 self->noise_index = gi.soundindex ("world/electro.wav");
501 self->touch = hurt_touch;
502
503 if (!self->dmg)
504 self->dmg = 5;
505
506 if (self->spawnflags & 1)
507 self->solid = SOLID_NOT;
508 else
509 self->solid = SOLID_TRIGGER;
510
511 if (self->spawnflags & 2)
512 self->use = hurt_use;
513
514 gi.linkentity (self);
515 }
516
517
518 /*
519 ==============================================================================
520
521 trigger_gravity
522
523 ==============================================================================
524 */
525
526 /*QUAKED trigger_gravity (.5 .5 .5) ?
527 Changes the touching entites gravity to
528 the value of "gravity". 1.0 is standard
529 gravity for the level.
530 */
531
trigger_gravity_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)532 void trigger_gravity_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
533 {
534 other->gravity = self->gravity;
535 }
536
SP_trigger_gravity(edict_t * self)537 void SP_trigger_gravity (edict_t *self)
538 {
539 if (st.gravity == 0)
540 {
541 gi.dprintf("trigger_gravity without gravity set at %s\n", vtos(self->s.origin));
542 G_FreeEdict (self);
543 return;
544 }
545
546 InitTrigger (self);
547 self->gravity = atoi(st.gravity);
548 self->touch = trigger_gravity_touch;
549 }
550
551
552 /*
553 ==============================================================================
554
555 trigger_monsterjump
556
557 ==============================================================================
558 */
559
560 /*QUAKED trigger_monsterjump (.5 .5 .5) ?
561 Walking monsters that touch this will jump in the direction of the trigger's angle
562 "speed" default to 200, the speed thrown forward
563 "height" default to 200, the speed thrown upwards
564 */
565
trigger_monsterjump_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)566 void trigger_monsterjump_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
567 {
568 if (other->flags & (FL_FLY | FL_SWIM) )
569 return;
570 if (other->svflags & SVF_DEADMONSTER)
571 return;
572 if ( !(other->svflags & SVF_MONSTER))
573 return;
574
575 // set XY even if not on ground, so the jump will clear lips
576 other->velocity[0] = self->movedir[0] * self->speed;
577 other->velocity[1] = self->movedir[1] * self->speed;
578
579 if (!other->groundentity)
580 return;
581
582 other->groundentity = NULL;
583 other->velocity[2] = self->movedir[2];
584 }
585
SP_trigger_monsterjump(edict_t * self)586 void SP_trigger_monsterjump (edict_t *self)
587 {
588 if (!self->speed)
589 self->speed = 200;
590 if (!st.height)
591 st.height = 200;
592 if (self->s.angles[YAW] == 0)
593 self->s.angles[YAW] = 360;
594 InitTrigger (self);
595 self->touch = trigger_monsterjump_touch;
596 self->movedir[2] = st.height;
597 }
598
599