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