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