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