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