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