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