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 (!Vec3Compare (self->s.angles, vec3Origin))
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,cBspPlane_t * plane,cBspSurface_t * surf)72 void Touch_Multi (edict_t *self, edict_t *other, cBspPlane_t *plane, cBspSurface_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 (!Vec3Compare(self->movedir, vec3Origin))
88 {
89 vec3_t forward;
90
91 Angles_Vectors(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.2f;
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 (!Vec3Compare(ent->s.angles, vec3Origin))
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 Vec3MA (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->floatVal)
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.2f)
377 ent->delay = 0.2f;
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,cBspPlane_t * plane,cBspSurface_t * surf)394 void trigger_push_touch (edict_t *self, edict_t *other, cBspPlane_t *plane, cBspSurface_t *surf)
395 {
396 if (strcmp(other->classname, "grenade") == 0)
397 {
398 Vec3Scale (self->movedir, self->speed * 10, other->velocity);
399 }
400 else if (other->health > 0)
401 {
402 Vec3Scale (self->movedir, self->speed * 10, other->velocity);
403
404 if (other->client)
405 {
406 // don't take falling damage immediately from this
407 Vec3Copy (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
trigger_push_q3touch(edict_t * self,edict_t * other,cBspPlane_t * plane,cBspSurface_t * surf)419 void trigger_push_q3touch (edict_t *self, edict_t *other, cBspPlane_t *plane, cBspSurface_t *surf)
420 {
421 Vec3Scale (self->movedir, self->speed * 10, other->velocity);
422
423 if (other->client)
424 {
425 // don't take falling damage immediately from this
426 Vec3Copy (other->velocity, other->client->oldvelocity);
427 }
428 }
429
430 /*QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE
431 Pushes the player
432 "speed" defaults to 1000
433 */
SP_trigger_push(edict_t * self)434 void SP_trigger_push (edict_t *self)
435 {
436 edict_t *target;
437
438 InitTrigger (self);
439
440 windsound = gi.soundindex ("misc/windfly.wav");
441
442 if (!self->speed)
443 self->speed = 1000;
444 if (!self->target) {
445 G_FreeEdict (self);
446 return;
447 }
448
449 target = G_Find (NULL, FOFS(targetname), self->target);
450 if (target) {
451 // Quake3
452 //self->touch = trigger_push_q3touch;
453 // TODO FIXME
454 G_FreeEdict (self);
455 }
456 else {
457 // Quake2
458 self->touch = trigger_push_touch;
459 }
460
461 gi.linkentity (self);
462 }
463
464
465 /*
466 ==============================================================================
467
468 trigger_hurt
469
470 ==============================================================================
471 */
472
473 /*QUAKED trigger_hurt (.5 .5 .5) ? START_OFF TOGGLE SILENT NO_PROTECTION SLOW
474 Any entity that touches this will be hurt.
475
476 It does dmg points of damage each server frame
477
478 SILENT supresses playing the sound
479 SLOW changes the damage rate to once per second
480 NO_PROTECTION *nothing* stops the damage
481
482 "dmg" default 5 (whole numbers only)
483
484 */
hurt_use(edict_t * self,edict_t * other,edict_t * activator)485 void hurt_use (edict_t *self, edict_t *other, edict_t *activator)
486 {
487 if (self->solid == SOLID_NOT)
488 self->solid = SOLID_TRIGGER;
489 else
490 self->solid = SOLID_NOT;
491 gi.linkentity (self);
492
493 if (!(self->spawnflags & 2))
494 self->use = NULL;
495 }
496
497
hurt_touch(edict_t * self,edict_t * other,cBspPlane_t * plane,cBspSurface_t * surf)498 void hurt_touch (edict_t *self, edict_t *other, cBspPlane_t *plane, cBspSurface_t *surf)
499 {
500 int dflags;
501
502 if (!other->takedamage)
503 return;
504
505 if (self->timestamp > level.time)
506 return;
507
508 if (self->spawnflags & 16)
509 self->timestamp = level.time + 1;
510 else
511 self->timestamp = level.time + FRAMETIME;
512
513 if (!(self->spawnflags & 4))
514 {
515 if ((level.framenum % 10) == 0)
516 gi.sound (other, CHAN_AUTO, self->noise_index, 1, ATTN_NORM, 0);
517 }
518
519 if (self->spawnflags & 8)
520 dflags = DAMAGE_NO_PROTECTION;
521 else
522 dflags = 0;
523 T_Damage (other, self, self, vec3Origin, other->s.origin, vec3Origin, self->dmg, self->dmg, dflags, MOD_TRIGGER_HURT);
524 }
525
SP_trigger_hurt(edict_t * self)526 void SP_trigger_hurt (edict_t *self)
527 {
528 InitTrigger (self);
529
530 self->noise_index = gi.soundindex ("world/electro.wav");
531 self->touch = hurt_touch;
532
533 if (!self->dmg)
534 self->dmg = 5;
535
536 if (self->spawnflags & 1)
537 self->solid = SOLID_NOT;
538 else
539 self->solid = SOLID_TRIGGER;
540
541 if (self->spawnflags & 2)
542 self->use = hurt_use;
543
544 gi.linkentity (self);
545 }
546
547
548 /*
549 ==============================================================================
550
551 trigger_gravity
552
553 ==============================================================================
554 */
555
556 /*QUAKED trigger_gravity (.5 .5 .5) ?
557 Changes the touching entites gravity to
558 the value of "gravity". 1.0 is standard
559 gravity for the level.
560 */
561
trigger_gravity_touch(edict_t * self,edict_t * other,cBspPlane_t * plane,cBspSurface_t * surf)562 void trigger_gravity_touch (edict_t *self, edict_t *other, cBspPlane_t *plane, cBspSurface_t *surf)
563 {
564 other->gravity = self->gravity;
565 }
566
SP_trigger_gravity(edict_t * self)567 void SP_trigger_gravity (edict_t *self)
568 {
569 if (st.gravity == 0)
570 {
571 gi.dprintf("trigger_gravity without gravity set at %s\n", vtos(self->s.origin));
572 G_FreeEdict (self);
573 return;
574 }
575
576 InitTrigger (self);
577 self->gravity = atoi(st.gravity);
578 self->touch = trigger_gravity_touch;
579 }
580
581 /*
582 ==============================================================================
583
584 trigger_teleport
585
586 ==============================================================================
587 */
588
589 void teleporter_touch (edict_t *self, edict_t *other, cBspPlane_t *plane, cBspSurface_t *surf);
SP_trigger_teleport(edict_t * self)590 void SP_trigger_teleport (edict_t *self)
591 {
592 InitTrigger (self);
593
594 self->touch = teleporter_touch;
595
596 gi.linkentity (self);
597 }
598
599 /*
600 ==============================================================================
601
602 trigger_monsterjump
603
604 ==============================================================================
605 */
606
607 /*QUAKED trigger_monsterjump (.5 .5 .5) ?
608 Walking monsters that touch this will jump in the direction of the trigger's angle
609 "speed" default to 200, the speed thrown forward
610 "height" default to 200, the speed thrown upwards
611 */
612
trigger_monsterjump_touch(edict_t * self,edict_t * other,cBspPlane_t * plane,cBspSurface_t * surf)613 void trigger_monsterjump_touch (edict_t *self, edict_t *other, cBspPlane_t *plane, cBspSurface_t *surf)
614 {
615 if (other->flags & (FL_FLY | FL_SWIM) )
616 return;
617 if (other->svFlags & SVF_DEADMONSTER)
618 return;
619 if ( !(other->svFlags & SVF_MONSTER))
620 return;
621
622 // set XY even if not on ground, so the jump will clear lips
623 other->velocity[0] = self->movedir[0] * self->speed;
624 other->velocity[1] = self->movedir[1] * self->speed;
625
626 if (!other->groundentity)
627 return;
628
629 other->groundentity = NULL;
630 other->velocity[2] = self->movedir[2];
631 }
632
SP_trigger_monsterjump(edict_t * self)633 void SP_trigger_monsterjump (edict_t *self)
634 {
635 if (!self->speed)
636 self->speed = 200;
637 if (!st.height)
638 st.height = 200;
639 if (self->s.angles[YAW] == 0)
640 self->s.angles[YAW] = 360;
641 InitTrigger (self);
642 self->touch = trigger_monsterjump_touch;
643 self->movedir[2] = st.height;
644 }
645
646