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
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "g_local.h"
26
27
InitTrigger(edict_t * self)28 void InitTrigger (edict_t *self)
29 {
30 if (!VectorCompare (self->s.angles, vec3_origin))
31 G_SetMovedir (self->s.angles, self->movedir);
32
33 self->solid = SOLID_TRIGGER;
34 self->movetype = MOVETYPE_NONE;
35 gi.setmodel (self, self->model);
36 self->svflags = SVF_NOCLIENT;
37 }
38
39
40 // the wait time has passed, so set back up for another activation
multi_wait(edict_t * ent)41 void multi_wait (edict_t *ent)
42 {
43 ent->nextthink = 0;
44 }
45
46
47 // the trigger was just activated
48 // ent->activator should be set to the activator so it can be held through a delay
49 // so wait for the delay time before firing
multi_trigger(edict_t * ent)50 void multi_trigger (edict_t *ent)
51 {
52 if (ent->nextthink)
53 return; // already been triggered
54
55 G_UseTargets (ent, ent->activator);
56
57 if (ent->wait > 0)
58 {
59 ent->think = multi_wait;
60 ent->nextthink = level.time + ent->wait;
61 }
62 else
63 { // we can't just remove (self) here, because this is a touch function
64 // called while looping through area links...
65 ent->touch = NULL;
66 ent->nextthink = level.time + FRAMETIME;
67 ent->think = G_FreeEdict;
68 }
69 }
70
Use_Multi(edict_t * ent,edict_t * other,edict_t * activator)71 void Use_Multi (edict_t *ent, edict_t *other, edict_t *activator)
72 {
73 ent->activator = activator;
74 multi_trigger (ent);
75 }
76
Touch_Multi(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)77 void Touch_Multi (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
78 {
79
80 //not during warmup
81 if (self->spawnflags & 32) {
82 if(level.time <= warmuptime->value)
83 return;
84 }
85
86 if(other->client)
87 {
88 if (self->spawnflags & 2)
89 return;
90 }
91 else if (other->svflags & SVF_MONSTER)
92 {
93 if (!(self->spawnflags & 1))
94 return;
95 }
96
97 else
98 return;
99
100 if (!VectorCompare(self->movedir, vec3_origin))
101 {
102 vec3_t forward;
103
104 AngleVectors(other->s.angles, forward, NULL, NULL);
105 if (_DotProduct(forward, self->movedir) < 0)
106 return;
107 }
108
109 self->activator = other;
110 multi_trigger (self);
111 }
112
113 /*QUAKED trigger_multiple (.5 .5 .5) ? MONSTER NOT_PLAYER TRIGGERED
114 Variable sized repeatable trigger. Must be targeted at one or more entities.
115 If "delay" is set, the trigger waits some time after activating before firing.
116 "wait" : Seconds between triggerings. (.2 default)
117 sounds
118 1) secret
119 2) beep beep
120 3) large switch
121 4)
122 set "message" to text string
123 */
trigger_enable(edict_t * self,edict_t * other,edict_t * activator)124 void trigger_enable (edict_t *self, edict_t *other, edict_t *activator)
125 {
126 self->solid = SOLID_TRIGGER;
127 self->use = Use_Multi;
128 gi.linkentity (self);
129 }
130
SP_trigger_multiple(edict_t * ent)131 void SP_trigger_multiple (edict_t *ent)
132 {
133 if (ent->sounds == 1)
134 ent->noise_index = gi.soundindex ("misc/secret.wav");
135 else if (ent->sounds == 2)
136 ent->noise_index = gi.soundindex ("misc/talk.wav");
137 else if (ent->sounds == 3)
138 ent->noise_index = gi.soundindex ("misc/trigger1.wav");
139
140 if (!ent->wait)
141 ent->wait = 0.2;
142 ent->touch = Touch_Multi;
143 ent->movetype = MOVETYPE_NONE;
144 ent->svflags |= SVF_NOCLIENT;
145
146
147 if (ent->spawnflags & 4)
148 {
149 ent->solid = SOLID_NOT;
150 ent->use = trigger_enable;
151 }
152 else
153 {
154 ent->solid = SOLID_TRIGGER;
155 ent->use = Use_Multi;
156 }
157
158 if (!VectorCompare(ent->s.angles, vec3_origin))
159 G_SetMovedir (ent->s.angles, ent->movedir);
160
161 gi.setmodel (ent, ent->model);
162 gi.linkentity (ent);
163 }
164
165
166 /*QUAKED trigger_once (.5 .5 .5) ? x x TRIGGERED
167 Triggers once, then removes itself.
168 You must set the key "target" to the name of another object in the level that has a matching "targetname".
169
170 If TRIGGERED, this trigger must be triggered before it is live.
171
172 sounds
173 1) secret
174 2) beep beep
175 3) large switch
176 4)
177
178 "message" string to be displayed when triggered
179 */
180
SP_trigger_once(edict_t * ent)181 void SP_trigger_once(edict_t *ent)
182 {
183 // make old maps work because I messed up on flag assignments here
184 // triggered was on bit 1 when it should have been on bit 4
185 if (ent->spawnflags & 1)
186 {
187 vec3_t v;
188
189 VectorMA (ent->mins, 0.5, ent->size, v);
190 ent->spawnflags &= ~1;
191 ent->spawnflags |= 4;
192 gi.dprintf("fixed TRIGGERED flag on %s at %s\n", ent->classname, vtos(v));
193 }
194
195 ent->wait = -1;
196 SP_trigger_multiple (ent);
197 }
198
199 /*QUAKED trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
200 This fixed size trigger cannot be touched, it can only be fired by other events.
201 */
trigger_relay_use(edict_t * self,edict_t * other,edict_t * activator)202 void trigger_relay_use (edict_t *self, edict_t *other, edict_t *activator)
203 {
204 G_UseTargets (self, activator);
205 }
206
SP_trigger_relay(edict_t * self)207 void SP_trigger_relay (edict_t *self)
208 {
209 self->use = trigger_relay_use;
210 }
211
212
213 /*
214 ==============================================================================
215
216 trigger_key
217
218 ==============================================================================
219 */
220
221 /*QUAKED trigger_key (.5 .5 .5) (-8 -8 -8) (8 8 8)
222 A relay trigger that only fires it's targets if player has the proper key.
223 Use "item" to specify the required key, for example "key_data_cd"
224 */
trigger_key_use(edict_t * self,edict_t * other,edict_t * activator)225 void trigger_key_use (edict_t *self, edict_t *other, edict_t *activator)
226 {
227 int index;
228
229 if (!self->item)
230 return;
231 if (!activator->client)
232 return;
233
234 index = ITEM_INDEX(self->item);
235 if (!activator->client->pers.inventory[index])
236 {
237 if (level.time < self->touch_debounce_time)
238 return;
239 self->touch_debounce_time = level.time + 5.0;
240 safe_centerprintf (activator, "You need the %s", self->item->pickup_name);
241 gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/keytry.wav"), 1, ATTN_NORM, 0);
242 return;
243 }
244
245 gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/keyuse.wav"), 1, ATTN_NORM, 0);
246
247 activator->client->pers.inventory[index]--;
248
249 G_UseTargets (self, activator);
250
251 self->use = NULL;
252 }
253
SP_trigger_key(edict_t * self)254 void SP_trigger_key (edict_t *self)
255 {
256 if (!st.item)
257 {
258 gi.dprintf("no key item for trigger_key at %s\n", vtos(self->s.origin));
259 return;
260 }
261 self->item = FindItemByClassname (st.item);
262
263 if (!self->item)
264 {
265 gi.dprintf("item %s not found for trigger_key at %s\n", st.item, vtos(self->s.origin));
266 return;
267 }
268
269 if (!self->target)
270 {
271 gi.dprintf("%s at %s has no target\n", self->classname, vtos(self->s.origin));
272 return;
273 }
274
275 gi.soundindex ("misc/keytry.wav");
276 gi.soundindex ("misc/keyuse.wav");
277
278 self->use = trigger_key_use;
279 }
280
281
282 /*
283 ==============================================================================
284
285 trigger_counter
286
287 ==============================================================================
288 */
289
290 /*QUAKED trigger_counter (.5 .5 .5) ? nomessage
291 Acts as an intermediary for an action that takes multiple inputs.
292
293 If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
294
295 After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
296 */
297
trigger_counter_use(edict_t * self,edict_t * other,edict_t * activator)298 void trigger_counter_use(edict_t *self, edict_t *other, edict_t *activator)
299 {
300 if (self->count == 0)
301 return;
302
303 self->count--;
304
305 if (self->count)
306 {
307 if (! (self->spawnflags & 1))
308 {
309 safe_centerprintf(activator, "%i more to go...", self->count);
310 gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
311 }
312 return;
313 }
314
315 if (! (self->spawnflags & 1))
316 {
317 safe_centerprintf(activator, "Sequence completed!");
318 gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
319 }
320 self->activator = activator;
321 multi_trigger (self);
322 }
323
SP_trigger_counter(edict_t * self)324 void SP_trigger_counter (edict_t *self)
325 {
326 self->wait = -1;
327 if (!self->count)
328 self->count = 2;
329
330 self->use = trigger_counter_use;
331 }
332
333
334 /*
335 ==============================================================================
336
337 trigger_always
338
339 ==============================================================================
340 */
341
342 /*QUAKED trigger_always (.5 .5 .5) (-8 -8 -8) (8 8 8)
343 This trigger will always fire. It is activated by the world.
344 */
SP_trigger_always(edict_t * ent)345 void SP_trigger_always (edict_t *ent)
346 {
347 // we must have some delay to make sure our use targets are present
348 if (ent->delay < 0.2)
349 ent->delay = 0.2;
350 G_UseTargets(ent, ent);
351 }
352
353
354 /*
355 ==============================================================================
356
357 trigger_push
358
359 ==============================================================================
360 */
361
362 #define PUSH_ONCE 1
363
364 static int windsound;
365
trigger_push_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)366 void trigger_push_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
367 {
368 if (strcmp(other->classname, "grenade") == 0)
369 {
370 VectorScale (self->movedir, self->speed * 10, other->velocity);
371 }
372 else if (other->health > 0)
373 {
374 VectorScale (self->movedir, self->speed * 10, other->velocity);
375
376 if (other->client)
377 {
378 // don't take falling damage immediately from this
379 VectorCopy (other->velocity, other->client->oldvelocity);
380 if (other->fly_sound_debounce_time < level.time)
381 {
382 other->fly_sound_debounce_time = level.time + 1.5;
383 gi.sound (other, CHAN_AUTO, windsound, 1, ATTN_NORM, 0);
384 }
385 }
386 }
387 if (self->spawnflags & PUSH_ONCE)
388 G_FreeEdict (self);
389 }
390
391
392 /*QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE
393 Pushes the player
394 "speed" defaults to 1000
395 */
SP_trigger_push(edict_t * self)396 void SP_trigger_push (edict_t *self)
397 {
398 InitTrigger (self);
399 windsound = gi.soundindex ("misc/windfly.wav");
400 self->touch = trigger_push_touch;
401 if (!self->speed)
402 self->speed = 1000;
403 gi.linkentity (self);
404 }
405
406
407 /*
408 ==============================================================================
409
410 trigger_hurt
411
412 ==============================================================================
413 */
414
415 /*QUAKED trigger_hurt (.5 .5 .5) ? START_OFF TOGGLE SILENT NO_PROTECTION SLOW
416 Any entity that touches this will be hurt.
417
418 It does dmg points of damage each server frame
419
420 SILENT supresses playing the sound
421 SLOW changes the damage rate to once per second
422 NO_PROTECTION *nothing* stops the damage
423
424 "dmg" default 5 (whole numbers only)
425
426 */
hurt_use(edict_t * self,edict_t * other,edict_t * activator)427 void hurt_use (edict_t *self, edict_t *other, edict_t *activator)
428 {
429 if (self->solid == SOLID_NOT)
430 self->solid = SOLID_TRIGGER;
431 else
432 self->solid = SOLID_NOT;
433 gi.linkentity (self);
434
435 if (!(self->spawnflags & 2))
436 self->use = NULL;
437 }
438
439
hurt_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)440 void hurt_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
441 {
442 int dflags;
443
444 if (!other->takedamage)
445 return;
446
447 if (self->timestamp > level.time)
448 return;
449
450 if (self->spawnflags & 16)
451 self->timestamp = level.time + 1;
452 else
453 self->timestamp = level.time + FRAMETIME;
454
455 if (!(self->spawnflags & 4))
456 {
457 if ((level.framenum % 10) == 0)
458 gi.sound (other, CHAN_AUTO, self->noise_index, 1, ATTN_NORM, 0);
459 }
460
461 if (self->spawnflags & 8)
462 dflags = DAMAGE_NO_PROTECTION;
463 else
464 dflags = 0;
465 T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, self->dmg, dflags, MOD_TRIGGER_HURT);
466 }
467
SP_trigger_hurt(edict_t * self)468 void SP_trigger_hurt (edict_t *self)
469 {
470 InitTrigger (self);
471
472 self->touch = hurt_touch;
473
474 if (!self->dmg)
475 self->dmg = 5;
476
477 if (self->spawnflags & 1)
478 self->solid = SOLID_NOT;
479 else
480 self->solid = SOLID_TRIGGER;
481
482 if (self->spawnflags & 2)
483 self->use = hurt_use;
484
485 gi.linkentity (self);
486 }
487
488
489 /*
490 ==============================================================================
491
492 trigger_gravity
493
494 ==============================================================================
495 */
496
497 /*QUAKED trigger_gravity (.5 .5 .5) ?
498 Changes the touching entites gravity to
499 the value of "gravity". 1.0 is standard
500 gravity for the level.
501 */
502
trigger_gravity_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)503 void trigger_gravity_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
504 {
505 other->gravity = self->gravity;
506 }
507
SP_trigger_gravity(edict_t * self)508 void SP_trigger_gravity (edict_t *self)
509 {
510 if (st.gravity == 0)
511 {
512 gi.dprintf("trigger_gravity without gravity set at %s\n", vtos(self->s.origin));
513 G_FreeEdict (self);
514 return;
515 }
516
517 InitTrigger (self);
518 self->gravity = atoi(st.gravity);
519 self->touch = trigger_gravity_touch;
520 }
521
522
523 /*
524 ==============================================================================
525
526 trigger_monsterjump
527
528 ==============================================================================
529 */
530
531 /*QUAKED trigger_monsterjump (.5 .5 .5) ?
532 Walking monsters that touch this will jump in the direction of the trigger's angle
533 "speed" default to 200, the speed thrown forward
534 "height" default to 200, the speed thrown upwards
535 */
536
trigger_monsterjump_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)537 void trigger_monsterjump_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
538 {
539
540 if (self->nextthink)
541 return; // already been triggered
542
543 if (other->flags & (FL_FLY | FL_SWIM) )
544 return;
545 if (other->svflags & SVF_DEADMONSTER)
546 return;
547
548 // set XY even if not on ground, so the jump will clear lips
549 other->velocity[0] = self->movedir[0] * self->speed;
550 other->velocity[1] = self->movedir[1] * self->speed;
551
552 other->groundentity = NULL;
553 other->velocity[2] = self->movedir[2];
554
555 //play a sound
556 gi.sound (other, CHAN_AUTO, gi.soundindex("world/button2.wav"), 1, ATTN_NORM, 0);
557 self->nextthink = level.time + .1;
558 }
559
SP_trigger_monsterjump(edict_t * self)560 void SP_trigger_monsterjump (edict_t *self)
561 {
562 if (!self->speed)
563 self->speed = 200;
564 if (!st.height)
565 st.height = 200;
566 if (self->s.angles[YAW] == 0)
567 self->s.angles[YAW] = 360;
568 InitTrigger (self);
569 self->touch = trigger_monsterjump_touch;
570 self->movedir[2] = st.height;
571 self->think = multi_wait;
572 }
573
574 //trigger_deathballtarget - a "goal" used for scoring in deathball
deathballtarget_use(edict_t * self,edict_t * other,edict_t * activator)575 void deathballtarget_use (edict_t *self, edict_t *other, edict_t *activator)
576 {
577 if (self->solid == SOLID_NOT)
578 self->solid = SOLID_TRIGGER;
579 else
580 self->solid = SOLID_NOT;
581 gi.linkentity (self);
582
583 if (!(self->spawnflags & 2))
584 self->use = NULL;
585 }
586
587
deathballtarget_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)588 void deathballtarget_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
589 {
590
591 if (self->timestamp > level.time)
592 return;
593
594 self->timestamp = level.time + FRAMETIME;
595
596 if(strcmp(other->classname, "item_deathball") == 0) {
597
598 if (!(dmflags->integer & DF_SKINTEAMS))
599 other->owner->client->resp.score += 10;
600
601 if ((dmflags->integer & DF_SKINTEAMS)) {
602 if(other->owner->dmteam == RED_TEAM && strcmp(self->classname, "trigger_bluedeathballtarget") == 0) {
603 red_team_score+=10;
604 other->owner->client->resp.score += 10;
605 }
606 else if(other->owner->dmteam == BLUE_TEAM && strcmp(self->classname, "trigger_reddeathballtarget") == 0) {
607 blue_team_score+=10;
608 other->owner->client->resp.score += 10;
609 }
610 else
611 return; //shot at wrong goal in a team game, loser!
612 }
613
614 //send an effect
615 gi.WriteByte (svc_temp_entity);
616 gi.WriteByte (TE_BFG_BIGEXPLOSION);
617 gi.WritePosition (other->s.origin);
618 gi.multicast (other->s.origin, MULTICAST_PHS);
619 gi.sound (other, CHAN_AUTO, gi.soundindex("misc/db_score.wav"), 1, ATTN_NONE, 0);
620 ResetDeathball();
621 }
622 }
623
SP_trigger_deathballtarget(edict_t * self)624 void SP_trigger_deathballtarget (edict_t *self)
625 {
626 InitTrigger (self);
627
628 self->touch = deathballtarget_touch;
629
630 if (self->spawnflags & 1)
631 self->solid = SOLID_NOT;
632 else
633 self->solid = SOLID_TRIGGER;
634
635 if (self->spawnflags & 2)
636 self->use = deathballtarget_use;
637
638 gi.linkentity (self);
639 }
640
SP_trigger_reddeathballtarget(edict_t * self)641 void SP_trigger_reddeathballtarget (edict_t *self)
642 {
643 InitTrigger (self);
644
645 self->touch = deathballtarget_touch;
646
647 if (self->spawnflags & 1)
648 self->solid = SOLID_NOT;
649 else
650 self->solid = SOLID_TRIGGER;
651
652 if (self->spawnflags & 2)
653 self->use = deathballtarget_use;
654
655 gi.linkentity (self);
656 }
657
SP_trigger_bluedeathballtarget(edict_t * self)658 void SP_trigger_bluedeathballtarget (edict_t *self)
659 {
660 InitTrigger (self);
661
662 self->touch = deathballtarget_touch;
663
664 if (self->spawnflags & 1)
665 self->solid = SOLID_NOT;
666 else
667 self->solid = SOLID_TRIGGER;
668
669 if (self->spawnflags & 2)
670 self->use = deathballtarget_use;
671
672 gi.linkentity (self);
673 }
674
cowtarget_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)675 void cowtarget_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
676 {
677
678 if (self->timestamp > level.time)
679 return;
680
681 self->timestamp = level.time + FRAMETIME;
682
683 if(strcmp(other->classname, "cow") == 0) {
684
685
686 if(strcmp(self->classname, "trigger_bluecowtarget") == 0)
687 blue_team_score++;
688 if(strcmp(self->classname, "trigger_redcowtarget") == 0)
689 red_team_score++;
690
691 //send an effect
692 gi.WriteByte (svc_temp_entity);
693 gi.WriteByte (TE_BFG_BIGEXPLOSION);
694 gi.WritePosition (other->s.origin);
695 gi.multicast (other->s.origin, MULTICAST_PHS);
696 gi.sound (other, CHAN_AUTO, gi.soundindex("misc/db_score.wav"), 1, ATTN_NONE, 0);
697
698 //get rid of the cow, it's been beamed into the void....(back to pasture)
699 other->health = other->max_health;
700 other->s.event = EV_PLAYER_TELEPORT;
701 VectorCopy(other->s.spawn_pos, other->s.origin);
702
703 //reward the player who was controlling the cow
704 if(other->enemy)
705 other->enemy->client->resp.score += 10;
706 }
707 }
708
SP_trigger_redcowtarget(edict_t * self)709 void SP_trigger_redcowtarget (edict_t *self)
710 {
711 InitTrigger(self);
712
713 self->solid = SOLID_TRIGGER;
714
715 self->touch = cowtarget_touch;
716
717 gi.linkentity (self);
718 }
719
SP_trigger_bluecowtarget(edict_t * self)720 void SP_trigger_bluecowtarget (edict_t *self)
721 {
722 InitTrigger(self);
723
724 self->solid = SOLID_TRIGGER;
725
726 self->touch = cowtarget_touch;
727
728 gi.linkentity (self);
729 }
730