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