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