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