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