1 /*
2 * Copyright(c) 1997-2001 Id Software, Inc.
3 * Copyright(c) 2002 The Quakeforge Project.
4 * Copyright(c) 2006 Quetoo.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or(at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 *
15 * See the GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 */
21 
22 #include "g_local.h"
23 
24 
InitTrigger(edict_t * self)25 void InitTrigger(edict_t *self){
26 	if(!VectorCompare(self->s.angles, vec3_origin))
27 		G_SetMovedir(self->s.angles, self->movedir);
28 
29 	self->solid = SOLID_TRIGGER;
30 	self->movetype = MOVETYPE_NONE;
31 	gi.setmodel(self, self->model);
32 	self->svflags = SVF_NOCLIENT;
33 }
34 
35 
36 // the wait time has passed, so set back up for another activation
multi_wait(edict_t * ent)37 void multi_wait(edict_t *ent){
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 	if(ent->nextthink)
47 		return;  // already been triggered
48 
49 	G_UseTargets(ent, ent->activator);
50 
51 	if(ent->wait > 0){
52 		ent->think = multi_wait;
53 		ent->nextthink = level.time + ent->wait;
54 	} else {  // we can't just remove(self) here, because this is a touch function
55 	// called while looping through area links...
56 		ent->touch = NULL;
57 		ent->nextthink = level.time + FRAMETIME;
58 		ent->think = G_FreeEdict;
59 	}
60 }
61 
Use_Multi(edict_t * ent,edict_t * other,edict_t * activator)62 void Use_Multi(edict_t *ent, edict_t *other, edict_t *activator){
63 	ent->activator = activator;
64 	multi_trigger(ent);
65 }
66 
Touch_Multi(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)67 void Touch_Multi(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf){
68 	if(other->client){
69 		if(self->spawnflags & 2)
70 			return;
71 	} else if(other->svflags & SVF_MONSTER){
72 		if(!(self->spawnflags & 1))
73 			return;
74 	} else
75 		return;
76 
77 	if(!VectorCompare(self->movedir, vec3_origin)){
78 		vec3_t forward;
79 
80 		AngleVectors(other->s.angles, forward, NULL, NULL);
81 		if(DotProduct(forward, self->movedir) < 0)
82 			return;
83 	}
84 
85 	self->activator = other;
86 	multi_trigger(self);
87 }
88 
89 /*QUAKED trigger_multiple(.5 .5 .5) ? MONSTER NOT_PLAYER TRIGGERED
90 Variable sized repeatable trigger.  Must be targeted at one or more entities.
91 If "delay" is set, the trigger waits some time after activating before firing.
92 "wait" : Seconds between triggerings.(.2 default)
93 sounds
94 1)	secret
95 2)	beep beep
96 3)	large switch
97 4)
98 set "message" to text string
99 */
trigger_enable(edict_t * self,edict_t * other,edict_t * activator)100 void trigger_enable(edict_t *self, edict_t *other, edict_t *activator){
101 	self->solid = SOLID_TRIGGER;
102 	self->use = Use_Multi;
103 	gi.linkentity(self);
104 }
105 
SP_trigger_multiple(edict_t * ent)106 void SP_trigger_multiple(edict_t *ent){
107 	if(ent->sounds == 1)
108 		ent->noise_index = gi.soundindex("misc/secret.wav");
109 	else if(ent->sounds == 2)
110 		ent->noise_index = gi.soundindex("misc/talk.wav");
111 	else if(ent->sounds == 3)
112 		ent->noise_index = gi.soundindex("misc/trigger1.wav");
113 
114 	if(!ent->wait)
115 		ent->wait = 0.2;
116 	ent->touch = Touch_Multi;
117 	ent->movetype = MOVETYPE_NONE;
118 	ent->svflags |= SVF_NOCLIENT;
119 
120 
121 	if(ent->spawnflags & 4){
122 		ent->solid = SOLID_NOT;
123 		ent->use = trigger_enable;
124 	} else {
125 		ent->solid = SOLID_TRIGGER;
126 		ent->use = Use_Multi;
127 	}
128 
129 	if(!VectorCompare(ent->s.angles, vec3_origin))
130 		G_SetMovedir(ent->s.angles, ent->movedir);
131 
132 	gi.setmodel(ent, ent->model);
133 	gi.linkentity(ent);
134 }
135 
136 
137 /*QUAKED trigger_once(.5 .5 .5) ? x x TRIGGERED
138 Triggers once, then removes itself.
139 You must set the key "target" to the name of another object in the level that has a matching "targetname".
140 
141 If TRIGGERED, this trigger must be triggered before it is live.
142 
143 sounds
144  1)	secret
145  2)	beep beep
146  3)	large switch
147  4)
148 
149 "message"	string to be displayed when triggered
150 */
151 
SP_trigger_once(edict_t * ent)152 void SP_trigger_once(edict_t *ent){
153 	// make old maps work because I messed up on flag assignments here
154 	// triggered was on bit 1 when it should have been on bit 4
155 	if(ent->spawnflags & 1){
156 		vec3_t v;
157 
158 		VectorMA(ent->mins, 0.5, ent->size, v);
159 		ent->spawnflags &= ~1;
160 		ent->spawnflags |= 4;
161 		gi.dprintf("fixed TRIGGERED flag on %s at %s\n", ent->classname, vtos(v));
162 	}
163 
164 	ent->wait = -1;
165 	SP_trigger_multiple(ent);
166 }
167 
168 /*QUAKED trigger_relay(.5 .5 .5)(-8 -8 -8)(8 8 8)
169 This fixed size trigger cannot be touched, it can only be fired by other events.
170 */
trigger_relay_use(edict_t * self,edict_t * other,edict_t * activator)171 void trigger_relay_use(edict_t *self, edict_t *other, edict_t *activator){
172 	G_UseTargets(self, activator);
173 }
174 
SP_trigger_relay(edict_t * self)175 void SP_trigger_relay(edict_t *self){
176 	self->use = trigger_relay_use;
177 }
178 
179 
180 /*
181 
182 trigger_key
183 
184 */
185 
186 /*QUAKED trigger_key(.5 .5 .5)(-8 -8 -8)(8 8 8)
187 A relay trigger that only fires it's targets if player has the proper key.
188 Use "item" to specify the required key, for example "key_data_cd"
189 */
trigger_key_use(edict_t * self,edict_t * other,edict_t * activator)190 void trigger_key_use(edict_t *self, edict_t *other, edict_t *activator){
191 	int index;
192 
193 	if(!self->item)
194 		return;
195 	if(!activator->client)
196 		return;
197 
198 	index = ITEM_INDEX(self->item);
199 	if(!activator->client->pers.inventory[index]){
200 		if(level.time < self->touch_debounce_time)
201 			return;
202 		self->touch_debounce_time = level.time + 5.0;
203 		gi.centerprintf(activator, "You need the %s", self->item->pickup_name);
204 		gi.sound(activator, CHAN_AUTO, gi.soundindex("misc/keytry.wav"), 1, ATTN_NORM, 0);
205 		return;
206 	}
207 
208 	gi.sound(activator, CHAN_AUTO, gi.soundindex("misc/keyuse.wav"), 1, ATTN_NORM, 0);
209 	if(coop->value){
210 		int player;
211 		edict_t *ent;
212 
213 		if(strcmp(self->item->classname, "key_power_cube") == 0){
214 			int cube;
215 
216 			for(cube = 0; cube < 8; cube++)
217 				if(activator->client->pers.power_cubes &(1 << cube))
218 					break;
219 			for(player = 1; player <= game.maxclients; player++){
220 				ent = &g_edicts[player];
221 				if(!ent->inuse)
222 					continue;
223 				if(!ent->client)
224 					continue;
225 				if(ent->client->pers.power_cubes &(1 << cube)){
226 					ent->client->pers.inventory[index]--;
227 					ent->client->pers.power_cubes &= ~(1 << cube);
228 				}
229 			}
230 		} else {
231 			for(player = 1; player <= game.maxclients; player++){
232 				ent = &g_edicts[player];
233 				if(!ent->inuse)
234 					continue;
235 				if(!ent->client)
236 					continue;
237 				ent->client->pers.inventory[index] = 0;
238 			}
239 		}
240 	} else {
241 		activator->client->pers.inventory[index]--;
242 	}
243 
244 	G_UseTargets(self, activator);
245 
246 	self->use = NULL;
247 }
248 
SP_trigger_key(edict_t * self)249 void SP_trigger_key(edict_t *self){
250 	if(!st.item){
251 		gi.dprintf("no key item for trigger_key at %s\n", vtos(self->s.origin));
252 		return;
253 	}
254 	self->item = FindItemByClassname(st.item);
255 
256 	if(!self->item){
257 		gi.dprintf("item %s not found for trigger_key at %s\n", st.item, vtos(self->s.origin));
258 		return;
259 	}
260 
261 	if(!self->target){
262 		gi.dprintf("%s at %s has no target\n", self->classname, vtos(self->s.origin));
263 		return;
264 	}
265 
266 	gi.soundindex("misc/keytry.wav");
267 	gi.soundindex("misc/keyuse.wav");
268 
269 	self->use = trigger_key_use;
270 }
271 
272 
273 /*
274 
275 trigger_counter
276 
277 */
278 
279 /*QUAKED trigger_counter(.5 .5 .5) ? nomessage
280 Acts as an intermediary for an action that takes multiple inputs.
281 
282 If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
283 
284 After the counter has been triggered "count" times(default 2), it will fire all of it's targets and remove itself.
285 */
286 
trigger_counter_use(edict_t * self,edict_t * other,edict_t * activator)287 void trigger_counter_use(edict_t *self, edict_t *other, edict_t *activator){
288 	if(self->count == 0)
289 		return;
290 
291 	self->count--;
292 
293 	if(self->count){
294 		if(!(self->spawnflags & 1)){
295 			gi.centerprintf(activator, "%i more to go...", self->count);
296 			gi.sound(activator, CHAN_AUTO, gi.soundindex("misc/talk1.wav"), 1, ATTN_NORM, 0);
297 		}
298 		return;
299 	}
300 
301 	if(!(self->spawnflags & 1)){
302 		gi.centerprintf(activator, "Sequence completed!");
303 		gi.sound(activator, CHAN_AUTO, gi.soundindex("misc/talk1.wav"), 1, ATTN_NORM, 0);
304 	}
305 	self->activator = activator;
306 	multi_trigger(self);
307 }
308 
SP_trigger_counter(edict_t * self)309 void SP_trigger_counter(edict_t *self){
310 	self->wait = -1;
311 	if(!self->count)
312 		self->count = 2;
313 
314 	self->use = trigger_counter_use;
315 }
316 
317 
318 /*
319 
320 trigger_always
321 
322 */
323 
324 /*QUAKED trigger_always(.5 .5 .5)(-8 -8 -8)(8 8 8)
325 This trigger will always fire.  It is activated by the world.
326 */
SP_trigger_always(edict_t * ent)327 void SP_trigger_always(edict_t *ent){
328 	// we must have some delay to make sure our use targets are present
329 	if(ent->delay < 0.2)
330 		ent->delay = 0.2;
331 	G_UseTargets(ent, ent);
332 }
333 
334 
335 /*
336 
337 trigger_push
338 
339 */
340 
341 #define PUSH_ONCE		1
342 
343 static int windsound;
344 
trigger_push_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)345 void trigger_push_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf){
346 	if(strcmp(other->classname, "grenade") == 0){
347 		VectorScale(self->movedir, self->speed * 10, other->velocity);
348 	} else if(other->health > 0){
349 		VectorScale(self->movedir, self->speed * 10, other->velocity);
350 
351 		if(other->client){
352 	// don't take falling damage immediately from this
353 			VectorCopy(other->velocity, other->client->oldvelocity);
354 			if(other->fly_sound_debounce_time < level.time){
355 				other->fly_sound_debounce_time = level.time + 1.5;
356 				gi.sound(other, CHAN_AUTO, windsound, 1, ATTN_NORM, 0);
357 			}
358 		}
359 	}
360 	if(self->spawnflags & PUSH_ONCE)
361 		G_FreeEdict(self);
362 }
363 
364 
365 /*QUAKED trigger_push(.5 .5 .5) ? PUSH_ONCE
366 Pushes the player
367 "speed"		defaults to 1000
368 */
SP_trigger_push(edict_t * self)369 void SP_trigger_push(edict_t *self){
370 	InitTrigger(self);
371 	windsound = gi.soundindex("misc/windfly.wav");
372 	self->touch = trigger_push_touch;
373 	if(!self->speed)
374 		self->speed = 1000;
375 	gi.linkentity(self);
376 }
377 
378 
379 /*
380 
381 trigger_hurt
382 
383 */
384 
385 /*QUAKED trigger_hurt(.5 .5 .5) ? START_OFF TOGGLE SILENT NO_PROTECTION SLOW
386 Any entity that touches this will be hurt.
387 
388 It does dmg points of damage each server frame
389 
390 SILENT			supresses playing the sound
391 SLOW			changes the damage rate to once per second
392 NO_PROTECTION	*nothing* stops the damage
393 
394 "dmg"			default 5(whole numbers only)
395 
396 */
hurt_use(edict_t * self,edict_t * other,edict_t * activator)397 void hurt_use(edict_t *self, edict_t *other, edict_t *activator){
398 	if(self->solid == SOLID_NOT)
399 		self->solid = SOLID_TRIGGER;
400 	else
401 		self->solid = SOLID_NOT;
402 	gi.linkentity(self);
403 
404 	if(!(self->spawnflags & 2))
405 		self->use = NULL;
406 }
407 
408 
hurt_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)409 void hurt_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf){
410 	int dflags;
411 
412 	if(!other->takedamage)
413 		return;
414 
415 	if(self->timestamp > level.time)
416 		return;
417 
418 	if(self->spawnflags & 16)
419 		self->timestamp = level.time + 1;
420 	else
421 		self->timestamp = level.time + FRAMETIME;
422 
423 	if(!(self->spawnflags & 4)){
424 		if((level.framenum % 10) == 0)
425 			gi.sound(other, CHAN_AUTO, self->noise_index, 1, ATTN_NORM, 0);
426 	}
427 
428 	if(self->spawnflags & 8)
429 		dflags = DAMAGE_NO_PROTECTION;
430 	else
431 		dflags = 0;
432 	T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, self->dmg, dflags, MOD_TRIGGER_HURT);
433 }
434 
SP_trigger_hurt(edict_t * self)435 void SP_trigger_hurt(edict_t *self){
436 	InitTrigger(self);
437 
438 	self->noise_index = gi.soundindex("world/electro.wav");
439 	self->touch = hurt_touch;
440 
441 	if(!self->dmg)
442 		self->dmg = 5;
443 
444 	if(self->spawnflags & 1)
445 		self->solid = SOLID_NOT;
446 	else
447 		self->solid = SOLID_TRIGGER;
448 
449 	if(self->spawnflags & 2)
450 		self->use = hurt_use;
451 
452 	gi.linkentity(self);
453 }
454 
455 
456 /*
457 
458 trigger_gravity
459 
460 */
461 
462 /*QUAKED trigger_gravity(.5 .5 .5) ?
463 Changes the touching entites gravity to
464 the value of "gravity".  1.0 is standard
465 gravity for the level.
466 */
467 
trigger_gravity_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)468 void trigger_gravity_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf){
469 	other->gravity = self->gravity;
470 }
471 
SP_trigger_gravity(edict_t * self)472 void SP_trigger_gravity(edict_t *self){
473 	if(st.gravity == 0){
474 		gi.dprintf("trigger_gravity without gravity set at %s\n", vtos(self->s.origin));
475 		G_FreeEdict(self);
476 		return;
477 	}
478 
479 	InitTrigger(self);
480 	self->gravity = atoi(st.gravity);
481 	self->touch = trigger_gravity_touch;
482 }
483 
484 
485 /*
486 
487 trigger_monsterjump
488 
489 */
490 
491 /*QUAKED trigger_monsterjump(.5 .5 .5) ?
492 Walking monsters that touch this will jump in the direction of the trigger's angle
493 "speed" default to 200, the speed thrown forward
494 "height" default to 200, the speed thrown upwards
495 */
496 
trigger_monsterjump_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)497 void trigger_monsterjump_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf){
498 	if(other->flags &(FL_FLY | FL_SWIM))
499 		return;
500 	if(other->svflags & SVF_DEADMONSTER)
501 		return;
502 	if(!(other->svflags & SVF_MONSTER))
503 		return;
504 
505 	// set XY even if not on ground, so the jump will clear lips
506 	other->velocity[0] = self->movedir[0] * self->speed;
507 	other->velocity[1] = self->movedir[1] * self->speed;
508 
509 	if(!other->groundentity)
510 		return;
511 
512 	other->groundentity = NULL;
513 	other->velocity[2] = self->movedir[2];
514 }
515 
SP_trigger_monsterjump(edict_t * self)516 void SP_trigger_monsterjump(edict_t *self){
517 	if(!self->speed)
518 		self->speed = 200;
519 	if(!st.height)
520 		st.height = 200;
521 	if(self->s.angles[YAW] == 0)
522 		self->s.angles[YAW] = 360;
523 	InitTrigger(self);
524 	self->touch = trigger_monsterjump_touch;
525 	self->movedir[2] = st.height;
526 }
527 
528