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