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 (!VectorCompare (self->s.angles, vec3_origin))
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,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
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.2;
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 (!VectorCompare(ent->s.angles, vec3_origin))
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 		VectorMA (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->value)
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.2)
377 		ent->delay = 0.2;
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,cplane_t * plane,csurface_t * surf)394 void trigger_push_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
395 {
396 	if (strcmp(other->classname, "grenade") == 0)
397 	{
398 		VectorScale (self->movedir, self->speed * 10, other->velocity);
399 	}
400 	else if (other->health > 0)
401 	{
402 		VectorScale (self->movedir, self->speed * 10, other->velocity);
403 
404 		if (other->client)
405 		{
406 			// don't take falling damage immediately from this
407 			VectorCopy (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 
419 
420 /*QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE
421 Pushes the player
422 "speed"		defaults to 1000
423 */
SP_trigger_push(edict_t * self)424 void SP_trigger_push (edict_t *self)
425 {
426 	InitTrigger (self);
427 	windsound = gi.soundindex ("misc/windfly.wav");
428 	self->touch = trigger_push_touch;
429 	if (!self->speed)
430 		self->speed = 1000;
431 	gi.linkentity (self);
432 }
433 
434 
435 /*
436 ==============================================================================
437 
438 trigger_hurt
439 
440 ==============================================================================
441 */
442 
443 /*QUAKED trigger_hurt (.5 .5 .5) ? START_OFF TOGGLE SILENT NO_PROTECTION SLOW
444 Any entity that touches this will be hurt.
445 
446 It does dmg points of damage each server frame
447 
448 SILENT			supresses playing the sound
449 SLOW			changes the damage rate to once per second
450 NO_PROTECTION	*nothing* stops the damage
451 
452 "dmg"			default 5 (whole numbers only)
453 
454 */
hurt_use(edict_t * self,edict_t * other,edict_t * activator)455 void hurt_use (edict_t *self, edict_t *other, edict_t *activator)
456 {
457 	if (self->solid == SOLID_NOT)
458 		self->solid = SOLID_TRIGGER;
459 	else
460 		self->solid = SOLID_NOT;
461 	gi.linkentity (self);
462 
463 	if (!(self->spawnflags & 2))
464 		self->use = NULL;
465 }
466 
467 
hurt_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)468 void hurt_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
469 {
470 	int		dflags;
471 
472 	if (!other->takedamage)
473 		return;
474 
475 	if (self->timestamp > level.time)
476 		return;
477 
478 	if (self->spawnflags & 16)
479 		self->timestamp = level.time + 1;
480 	else
481 		self->timestamp = level.time + FRAMETIME;
482 
483 	if (!(self->spawnflags & 4))
484 	{
485 		if ((level.framenum % 10) == 0)
486 			gi.sound (other, CHAN_AUTO, self->noise_index, 1, ATTN_NORM, 0);
487 	}
488 
489 	if (self->spawnflags & 8)
490 		dflags = DAMAGE_NO_PROTECTION;
491 	else
492 		dflags = 0;
493 	T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, self->dmg, dflags, MOD_TRIGGER_HURT);
494 }
495 
SP_trigger_hurt(edict_t * self)496 void SP_trigger_hurt (edict_t *self)
497 {
498 	InitTrigger (self);
499 
500 	self->noise_index = gi.soundindex ("world/electro.wav");
501 	self->touch = hurt_touch;
502 
503 	if (!self->dmg)
504 		self->dmg = 5;
505 
506 	if (self->spawnflags & 1)
507 		self->solid = SOLID_NOT;
508 	else
509 		self->solid = SOLID_TRIGGER;
510 
511 	if (self->spawnflags & 2)
512 		self->use = hurt_use;
513 
514 	gi.linkentity (self);
515 }
516 
517 
518 /*
519 ==============================================================================
520 
521 trigger_gravity
522 
523 ==============================================================================
524 */
525 
526 /*QUAKED trigger_gravity (.5 .5 .5) ?
527 Changes the touching entites gravity to
528 the value of "gravity".  1.0 is standard
529 gravity for the level.
530 */
531 
trigger_gravity_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)532 void trigger_gravity_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
533 {
534 	other->gravity = self->gravity;
535 }
536 
SP_trigger_gravity(edict_t * self)537 void SP_trigger_gravity (edict_t *self)
538 {
539 	if (st.gravity == 0)
540 	{
541 		gi.dprintf("trigger_gravity without gravity set at %s\n", vtos(self->s.origin));
542 		G_FreeEdict  (self);
543 		return;
544 	}
545 
546 	InitTrigger (self);
547 	self->gravity = atoi(st.gravity);
548 	self->touch = trigger_gravity_touch;
549 }
550 
551 
552 /*
553 ==============================================================================
554 
555 trigger_monsterjump
556 
557 ==============================================================================
558 */
559 
560 /*QUAKED trigger_monsterjump (.5 .5 .5) ?
561 Walking monsters that touch this will jump in the direction of the trigger's angle
562 "speed" default to 200, the speed thrown forward
563 "height" default to 200, the speed thrown upwards
564 */
565 
trigger_monsterjump_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)566 void trigger_monsterjump_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
567 {
568 	if (other->flags & (FL_FLY | FL_SWIM) )
569 		return;
570 	if (other->svflags & SVF_DEADMONSTER)
571 		return;
572 	if ( !(other->svflags & SVF_MONSTER))
573 		return;
574 
575 // set XY even if not on ground, so the jump will clear lips
576 	other->velocity[0] = self->movedir[0] * self->speed;
577 	other->velocity[1] = self->movedir[1] * self->speed;
578 
579 	if (!other->groundentity)
580 		return;
581 
582 	other->groundentity = NULL;
583 	other->velocity[2] = self->movedir[2];
584 }
585 
SP_trigger_monsterjump(edict_t * self)586 void SP_trigger_monsterjump (edict_t *self)
587 {
588 	if (!self->speed)
589 		self->speed = 200;
590 	if (!st.height)
591 		st.height = 200;
592 	if (self->s.angles[YAW] == 0)
593 		self->s.angles[YAW] = 360;
594 	InitTrigger (self);
595 	self->touch = trigger_monsterjump_touch;
596 	self->movedir[2] = st.height;
597 }
598 
599