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