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 gi.centerprintf (activator, "You need the %s", self->item->pickup_name);
209 gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/keytry.wav"), 1, ATTN_NORM, 0);
210 return;
211 }
212
213 gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/keyuse.wav"), 1, ATTN_NORM, 0);
214 if (coop->value)
215 {
216 int player;
217 edict_t *ent;
218
219 if (strcmp(self->item->classname, "key_power_cube") == 0)
220 {
221 int cube;
222
223 for (cube = 0; cube < 8; cube++)
224 if (activator->client->pers.power_cubes & (1 << cube))
225 break;
226 for (player = 1; player <= game.maxclients; player++)
227 {
228 ent = &g_edicts[player];
229 if (!ent->inuse)
230 continue;
231 if (!ent->client)
232 continue;
233 if (ent->client->pers.power_cubes & (1 << cube))
234 {
235 ent->client->pers.inventory[index]--;
236 ent->client->pers.power_cubes &= ~(1 << cube);
237 }
238 }
239 }
240 else
241 {
242 for (player = 1; player <= game.maxclients; player++)
243 {
244 ent = &g_edicts[player];
245 if (!ent->inuse)
246 continue;
247 if (!ent->client)
248 continue;
249 ent->client->pers.inventory[index] = 0;
250 }
251 }
252 }
253 else
254 {
255 activator->client->pers.inventory[index]--;
256 }
257
258 G_UseTargets (self, activator);
259
260 self->use = NULL;
261 }
262
SP_trigger_key(edict_t * self)263 void SP_trigger_key (edict_t *self)
264 {
265 if (!st.item)
266 {
267 gi.dprintf("no key item for trigger_key at %s\n", vtos(self->s.origin));
268 return;
269 }
270 self->item = FindItemByClassname (st.item);
271
272 if (!self->item)
273 {
274 gi.dprintf("item %s not found for trigger_key at %s\n", st.item, vtos(self->s.origin));
275 return;
276 }
277
278 if (!self->target)
279 {
280 gi.dprintf("%s at %s has no target\n", self->classname, vtos(self->s.origin));
281 return;
282 }
283
284 gi.soundindex ("misc/keytry.wav");
285 gi.soundindex ("misc/keyuse.wav");
286
287 self->use = trigger_key_use;
288 }
289
290
291 /*
292 ==============================================================================
293
294 trigger_counter
295
296 ==============================================================================
297 */
298
299 /*QUAKED trigger_counter (.5 .5 .5) ? nomessage
300 Acts as an intermediary for an action that takes multiple inputs.
301
302 If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
303
304 After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
305 */
306
trigger_counter_use(edict_t * self,edict_t * other,edict_t * activator)307 void trigger_counter_use(edict_t *self, edict_t *other, edict_t *activator)
308 {
309 if (self->count == 0)
310 return;
311
312 self->count--;
313
314 if (self->count)
315 {
316 if (! (self->spawnflags & 1))
317 {
318 gi.centerprintf(activator, "%i more to go...", self->count);
319 gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
320 }
321 return;
322 }
323
324 if (! (self->spawnflags & 1))
325 {
326 gi.centerprintf(activator, "Sequence completed!");
327 gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
328 }
329 self->activator = activator;
330 multi_trigger (self);
331 }
332
SP_trigger_counter(edict_t * self)333 void SP_trigger_counter (edict_t *self)
334 {
335 self->wait = -1;
336 if (!self->count)
337 self->count = 2;
338
339 self->use = trigger_counter_use;
340 }
341
342
343 /*
344 ==============================================================================
345
346 trigger_always
347
348 ==============================================================================
349 */
350
351 /*QUAKED trigger_always (.5 .5 .5) (-8 -8 -8) (8 8 8)
352 This trigger will always fire. It is activated by the world.
353 */
SP_trigger_always(edict_t * ent)354 void SP_trigger_always (edict_t *ent)
355 {
356 // we must have some delay to make sure our use targets are present
357 if (ent->delay < 0.2)
358 ent->delay = 0.2;
359 G_UseTargets(ent, ent);
360 }
361
362
363 /*
364 ==============================================================================
365
366 trigger_push
367
368 ==============================================================================
369 */
370 #if 0
371 #define PUSH_ONCE 1
372
373 static int windsound;
374
375 void trigger_push_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
376 {
377 if (strcmp(other->classname, "grenade") == 0)
378 {
379 VectorScale (self->movedir, self->speed * 10, other->velocity);
380 }
381 else if (other->health > 0)
382 {
383 VectorScale (self->movedir, self->speed * 10, other->velocity);
384
385 if (other->client)
386 {
387 // don't take falling damage immediately from this
388 VectorCopy (other->velocity, other->client->oldvelocity);
389 if (other->fly_sound_debounce_time < level.time)
390 {
391 other->fly_sound_debounce_time = level.time + 1.5;
392 gi.sound (other, CHAN_AUTO, windsound, 1, ATTN_NORM, 0);
393 }
394 }
395 }
396 if (self->spawnflags & PUSH_ONCE)
397 G_FreeEdict (self);
398 }
399
400 void SP_trigger_push (edict_t *self)
401 {
402 InitTrigger (self);
403 windsound = gi.soundindex ("misc/windfly.wav");
404 self->touch = trigger_push_touch;
405 if (!self->speed)
406 self->speed = 1000;
407 gi.linkentity (self);
408 }
409 #endif
410
411 // RAFAEL
412 #define PUSH_ONCE 1
413
414 static int windsound;
415
trigger_push_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)416 void trigger_push_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
417 {
418 if (strcmp(other->classname, "grenade") == 0)
419 {
420 VectorScale (self->movedir, self->speed * 10, other->velocity);
421 }
422 else if (other->health > 0)
423 {
424 VectorScale (self->movedir, self->speed * 10, other->velocity);
425
426 if (other->client)
427 {
428 // don't take falling damage immediately from this
429 VectorCopy (other->velocity, other->client->oldvelocity);
430 if (other->fly_sound_debounce_time < level.time)
431 {
432 other->fly_sound_debounce_time = level.time + 1.5;
433 gi.sound (other, CHAN_AUTO, windsound, 1, ATTN_NORM, 0);
434 }
435 }
436 }
437 if (self->spawnflags & PUSH_ONCE)
438 G_FreeEdict (self);
439 }
440
441
442 /*QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE PUSH_PLUS PUSH_RAMP
443 Pushes the player
444 "speed" defaults to 1000
445 "wait" defaults to 10 must use PUSH_PLUS used for on
446 */
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->flags & (FL_FLY | FL_SWIM) )
661 return;
662 if (other->svflags & SVF_DEADMONSTER)
663 return;
664 if ( !(other->svflags & SVF_MONSTER))
665 return;
666
667 // set XY even if not on ground, so the jump will clear lips
668 other->velocity[0] = self->movedir[0] * self->speed;
669 other->velocity[1] = self->movedir[1] * self->speed;
670
671 if (!other->groundentity)
672 return;
673
674 other->groundentity = NULL;
675 other->velocity[2] = self->movedir[2];
676 }
677
SP_trigger_monsterjump(edict_t * self)678 void SP_trigger_monsterjump (edict_t *self)
679 {
680 if (!self->speed)
681 self->speed = 200;
682 if (!st.height)
683 st.height = 200;
684 if (self->s.angles[YAW] == 0)
685 self->s.angles[YAW] = 360;
686 InitTrigger (self);
687 self->touch = trigger_monsterjump_touch;
688 self->movedir[2] = st.height;
689 }
690
691