Quake II RTX doxygen  1.0 dev
g_target.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 /*QUAKED target_temp_entity (1 0 0) (-8 -8 -8) (8 8 8)
21 Fire an origin based temp entity event to the clients.
22 "style" type byte
23 */
24 void Use_Target_Tent(edict_t *ent, edict_t *other, edict_t *activator)
25 {
26  gi.WriteByte(svc_temp_entity);
27  gi.WriteByte(ent->style);
28  gi.WritePosition(ent->s.origin);
29  gi.multicast(ent->s.origin, MULTICAST_PVS);
30 }
31 
32 void SP_target_temp_entity(edict_t *ent)
33 {
34  ent->use = Use_Target_Tent;
35 }
36 
37 
38 //==========================================================
39 
40 //==========================================================
41 
42 /*QUAKED target_speaker (1 0 0) (-8 -8 -8) (8 8 8) looped-on looped-off reliable
43 "noise" wav file to play
44 "attenuation"
45 -1 = none, send to whole level
46 1 = normal fighting sounds
47 2 = idle sound level
48 3 = ambient sound level
49 "volume" 0.0 to 1.0
50 
51 Normal sounds play each time the target is used. The reliable flag can be set for crucial voiceovers.
52 
53 Looped sounds are always atten 3 / vol 1, and the use function toggles it on/off.
54 Multiple identical looping sounds will just increase volume without any speed cost.
55 */
56 void Use_Target_Speaker(edict_t *ent, edict_t *other, edict_t *activator)
57 {
58  int chan;
59 
60  if (ent->spawnflags & 3) {
61  // looping sound toggles
62  if (ent->s.sound)
63  ent->s.sound = 0; // turn it off
64  else
65  ent->s.sound = ent->noise_index; // start it
66  } else {
67  // normal sound
68  if (ent->spawnflags & 4)
69  chan = CHAN_VOICE | CHAN_RELIABLE;
70  else
71  chan = CHAN_VOICE;
72  // use a positioned_sound, because this entity won't normally be
73  // sent to any clients because it is invisible
74  gi.positioned_sound(ent->s.origin, ent, chan, ent->noise_index, ent->volume, ent->attenuation, 0);
75  }
76 }
77 
78 void SP_target_speaker(edict_t *ent)
79 {
80  char buffer[MAX_QPATH];
81 
82  if (!st.noise) {
83  gi.dprintf("target_speaker with no noise set at %s\n", vtos(ent->s.origin));
84  return;
85  }
86  if (!strstr(st.noise, ".wav"))
87  Q_snprintf(buffer, sizeof(buffer), "%s.wav", st.noise);
88  else
89  strncpy(buffer, st.noise, sizeof(buffer));
90  ent->noise_index = gi.soundindex(buffer);
91 
92  if (!ent->volume)
93  ent->volume = 1.0;
94 
95  if (!ent->attenuation)
96  ent->attenuation = 1.0;
97  else if (ent->attenuation == -1) // use -1 so 0 defaults to 1
98  ent->attenuation = 0;
99 
100  // check for prestarted looping sound
101  if (ent->spawnflags & 1)
102  ent->s.sound = ent->noise_index;
103 
104  ent->use = Use_Target_Speaker;
105 
106  // must link the entity so we get areas and clusters so
107  // the server can determine who to send updates to
108  gi.linkentity(ent);
109 }
110 
111 
112 //==========================================================
113 
114 void Use_Target_Help(edict_t *ent, edict_t *other, edict_t *activator)
115 {
116  if (ent->spawnflags & 1)
117  strncpy(game.helpmessage1, ent->message, sizeof(game.helpmessage2) - 1);
118  else
119  strncpy(game.helpmessage2, ent->message, sizeof(game.helpmessage1) - 1);
120 
121  game.helpchanged++;
122 }
123 
124 /*QUAKED target_help (1 0 1) (-16 -16 -24) (16 16 24) help1
125 When fired, the "message" key becomes the current personal computer string, and the message light will be set on all clients status bars.
126 */
127 void SP_target_help(edict_t *ent)
128 {
129  if (deathmatch->value) {
130  // auto-remove for deathmatch
131  G_FreeEdict(ent);
132  return;
133  }
134 
135  if (!ent->message) {
136  gi.dprintf("%s with no message at %s\n", ent->classname, vtos(ent->s.origin));
137  G_FreeEdict(ent);
138  return;
139  }
140  ent->use = Use_Target_Help;
141 }
142 
143 //==========================================================
144 
145 /*QUAKED target_secret (1 0 1) (-8 -8 -8) (8 8 8)
146 Counts a secret found.
147 These are single use targets.
148 */
149 void use_target_secret(edict_t *ent, edict_t *other, edict_t *activator)
150 {
151  gi.sound(ent, CHAN_VOICE, ent->noise_index, 1, ATTN_NORM, 0);
152 
154 
155  G_UseTargets(ent, activator);
156  G_FreeEdict(ent);
157 }
158 
159 void SP_target_secret(edict_t *ent)
160 {
161  if (deathmatch->value) {
162  // auto-remove for deathmatch
163  G_FreeEdict(ent);
164  return;
165  }
166 
167  ent->use = use_target_secret;
168  if (!st.noise)
169  st.noise = "misc/secret.wav";
170  ent->noise_index = gi.soundindex(st.noise);
171  ent->svflags = SVF_NOCLIENT;
173  // map bug hack
174  if (!Q_stricmp(level.mapname, "mine3") && ent->s.origin[0] == 280 && ent->s.origin[1] == -2048 && ent->s.origin[2] == -624)
175  ent->message = "You have found a secret area.";
176 }
177 
178 //==========================================================
179 
180 /*QUAKED target_goal (1 0 1) (-8 -8 -8) (8 8 8)
181 Counts a goal completed.
182 These are single use targets.
183 */
184 void use_target_goal(edict_t *ent, edict_t *other, edict_t *activator)
185 {
186  gi.sound(ent, CHAN_VOICE, ent->noise_index, 1, ATTN_NORM, 0);
187 
188  level.found_goals++;
189 
191  gi.configstring(CS_CDTRACK, "0");
192 
193  G_UseTargets(ent, activator);
194  G_FreeEdict(ent);
195 }
196 
197 void SP_target_goal(edict_t *ent)
198 {
199  if (deathmatch->value) {
200  // auto-remove for deathmatch
201  G_FreeEdict(ent);
202  return;
203  }
204 
205  ent->use = use_target_goal;
206  if (!st.noise)
207  st.noise = "misc/secret.wav";
208  ent->noise_index = gi.soundindex(st.noise);
209  ent->svflags = SVF_NOCLIENT;
210  level.total_goals++;
211 }
212 
213 //==========================================================
214 
215 
216 /*QUAKED target_explosion (1 0 0) (-8 -8 -8) (8 8 8)
217 Spawns an explosion temporary entity when used.
218 
219 "delay" wait this long before going off
220 "dmg" how much radius damage should be done, defaults to 0
221 */
222 void target_explosion_explode(edict_t *self)
223 {
224  float save;
225 
226  gi.WriteByte(svc_temp_entity);
227  gi.WriteByte(TE_EXPLOSION1);
228  gi.WritePosition(self->s.origin);
229  gi.multicast(self->s.origin, MULTICAST_PHS);
230 
231  T_RadiusDamage(self, self->activator, self->dmg, NULL, self->dmg + 40, MOD_EXPLOSIVE);
232 
233  save = self->delay;
234  self->delay = 0;
235  G_UseTargets(self, self->activator);
236  self->delay = save;
237 }
238 
239 void use_target_explosion(edict_t *self, edict_t *other, edict_t *activator)
240 {
241  self->activator = activator;
242 
243  if (!self->delay) {
245  return;
246  }
247 
248  self->think = target_explosion_explode;
249  self->nextthink = level.time + self->delay;
250 }
251 
252 void SP_target_explosion(edict_t *ent)
253 {
254  ent->use = use_target_explosion;
255  ent->svflags = SVF_NOCLIENT;
256 }
257 
258 
259 //==========================================================
260 
261 /*QUAKED target_changelevel (1 0 0) (-8 -8 -8) (8 8 8)
262 Changes level to "map" when fired
263 */
264 void use_target_changelevel(edict_t *self, edict_t *other, edict_t *activator)
265 {
267  return; // already activated
268 
269  if (!deathmatch->value && !coop->value) {
270  if (g_edicts[1].health <= 0)
271  return;
272  }
273 
274  // if noexit, do a ton of damage to other
275  if (deathmatch->value && !((int)dmflags->value & DF_ALLOW_EXIT) && other != world) {
276  T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, 10 * other->max_health, 1000, 0, MOD_EXIT);
277  return;
278  }
279 
280  // if multiplayer, let everyone know who hit the exit
281  if (deathmatch->value) {
282  if (activator && activator->client)
283  gi.bprintf(PRINT_HIGH, "%s exited the level.\n", activator->client->pers.netname);
284  }
285 
286  // if going to a new unit, clear cross triggers
287  if (strstr(self->map, "*"))
289 
290  BeginIntermission(self);
291 }
292 
293 void SP_target_changelevel(edict_t *ent)
294 {
295  if (!ent->map) {
296  gi.dprintf("target_changelevel with no map at %s\n", vtos(ent->s.origin));
297  G_FreeEdict(ent);
298  return;
299  }
300 
301  // ugly hack because *SOMEBODY* screwed up their map
302  if ((Q_stricmp(level.mapname, "fact1") == 0) && (Q_stricmp(ent->map, "fact3") == 0))
303  ent->map = "fact3$secret1";
304 
305  ent->use = use_target_changelevel;
306  ent->svflags = SVF_NOCLIENT;
307 }
308 
309 
310 //==========================================================
311 
312 /*QUAKED target_splash (1 0 0) (-8 -8 -8) (8 8 8)
313 Creates a particle splash effect when used.
314 
315 Set "sounds" to one of the following:
316  1) sparks
317  2) blue water
318  3) brown water
319  4) slime
320  5) lava
321  6) blood
322 
323 "count" how many pixels in the splash
324 "dmg" if set, does a radius damage at this location when it splashes
325  useful for lava/sparks
326 */
327 
328 void use_target_splash(edict_t *self, edict_t *other, edict_t *activator)
329 {
330  gi.WriteByte(svc_temp_entity);
331  gi.WriteByte(TE_SPLASH);
332  gi.WriteByte(self->count);
333  gi.WritePosition(self->s.origin);
334  gi.WriteDir(self->movedir);
335  gi.WriteByte(self->sounds);
336  gi.multicast(self->s.origin, MULTICAST_PVS);
337 
338  if (self->dmg)
339  T_RadiusDamage(self, activator, self->dmg, NULL, self->dmg + 40, MOD_SPLASH);
340 }
341 
342 void SP_target_splash(edict_t *self)
343 {
344  self->use = use_target_splash;
345  G_SetMovedir(self->s.angles, self->movedir);
346 
347  if (!self->count)
348  self->count = 32;
349 
350  self->svflags = SVF_NOCLIENT;
351 }
352 
353 
354 //==========================================================
355 
356 /*QUAKED target_spawner (1 0 0) (-8 -8 -8) (8 8 8)
357 Set target to the type of entity you want spawned.
358 Useful for spawning monsters and gibs in the factory levels.
359 
360 For monsters:
361  Set direction to the facing you want it to have.
362 
363 For gibs:
364  Set direction if you want it moving and
365  speed how fast it should be moving otherwise it
366  will just be dropped
367 */
368 void ED_CallSpawn(edict_t *ent);
369 
370 void use_target_spawner(edict_t *self, edict_t *other, edict_t *activator)
371 {
372  edict_t *ent;
373 
374  ent = G_Spawn();
375  ent->classname = self->target;
376  VectorCopy(self->s.origin, ent->s.origin);
377  VectorCopy(self->s.angles, ent->s.angles);
378  ED_CallSpawn(ent);
379  gi.unlinkentity(ent);
380  KillBox(ent);
381  gi.linkentity(ent);
382  if (self->speed)
383  VectorCopy(self->movedir, ent->velocity);
384 }
385 
386 void SP_target_spawner(edict_t *self)
387 {
388  self->use = use_target_spawner;
389  self->svflags = SVF_NOCLIENT;
390  if (self->speed) {
391  G_SetMovedir(self->s.angles, self->movedir);
392  VectorScale(self->movedir, self->speed, self->movedir);
393  }
394 }
395 
396 //==========================================================
397 
398 /*QUAKED target_blaster (1 0 0) (-8 -8 -8) (8 8 8) NOTRAIL NOEFFECTS
399 Fires a blaster bolt in the set direction when triggered.
400 
401 dmg default is 15
402 speed default is 1000
403 */
404 
405 void use_target_blaster(edict_t *self, edict_t *other, edict_t *activator)
406 {
407 #if 0
408  int effect;
409 
410  if (self->spawnflags & 2)
411  effect = 0;
412  else if (self->spawnflags & 1)
413  effect = EF_HYPERBLASTER;
414  else
415  effect = EF_BLASTER;
416 #endif
417 
418  fire_blaster(self, self->s.origin, self->movedir, self->dmg, self->speed, EF_BLASTER, MOD_TARGET_BLASTER);
419  gi.sound(self, CHAN_VOICE, self->noise_index, 1, ATTN_NORM, 0);
420 }
421 
422 void SP_target_blaster(edict_t *self)
423 {
424  self->use = use_target_blaster;
425  G_SetMovedir(self->s.angles, self->movedir);
426  self->noise_index = gi.soundindex("weapons/laser2.wav");
427 
428  if (!self->dmg)
429  self->dmg = 15;
430  if (!self->speed)
431  self->speed = 1000;
432 
433  self->svflags = SVF_NOCLIENT;
434 }
435 
436 
437 //==========================================================
438 
439 /*QUAKED target_crosslevel_trigger (.5 .5 .5) (-8 -8 -8) (8 8 8) trigger1 trigger2 trigger3 trigger4 trigger5 trigger6 trigger7 trigger8
440 Once this trigger is touched/used, any trigger_crosslevel_target with the same trigger number is automatically used when a level is started within the same unit. It is OK to check multiple triggers. Message, delay, target, and killtarget also work.
441 */
442 void trigger_crosslevel_trigger_use(edict_t *self, edict_t *other, edict_t *activator)
443 {
444  game.serverflags |= self->spawnflags;
445  G_FreeEdict(self);
446 }
447 
448 void SP_target_crosslevel_trigger(edict_t *self)
449 {
450  self->svflags = SVF_NOCLIENT;
451  self->use = trigger_crosslevel_trigger_use;
452 }
453 
454 /*QUAKED target_crosslevel_target (.5 .5 .5) (-8 -8 -8) (8 8 8) trigger1 trigger2 trigger3 trigger4 trigger5 trigger6 trigger7 trigger8
455 Triggered by a trigger_crosslevel elsewhere within a unit. If multiple triggers are checked, all must be qtrue. Delay, target and
456 killtarget also work.
457 
458 "delay" delay before using targets if the trigger has been activated (default 1)
459 */
461 {
462  if (self->spawnflags == (game.serverflags & SFL_CROSS_TRIGGER_MASK & self->spawnflags)) {
463  G_UseTargets(self, self);
464  G_FreeEdict(self);
465  }
466 }
467 
468 void SP_target_crosslevel_target(edict_t *self)
469 {
470  if (! self->delay)
471  self->delay = 1;
472  self->svflags = SVF_NOCLIENT;
473 
474  self->think = target_crosslevel_target_think;
475  self->nextthink = level.time + self->delay;
476 }
477 
478 //==========================================================
479 
480 /*QUAKED target_laser (0 .5 .8) (-8 -8 -8) (8 8 8) START_ON RED GREEN BLUE YELLOW ORANGE FAT
481 When triggered, fires a laser. You can either set a target
482 or a direction.
483 */
484 
485 void target_laser_think(edict_t *self)
486 {
487  edict_t *ignore;
488  vec3_t start;
489  vec3_t end;
490  trace_t tr;
491  vec3_t point;
492  vec3_t last_movedir;
493  int count;
494 
495  if (self->spawnflags & 0x80000000)
496  count = 8;
497  else
498  count = 4;
499 
500  if (self->enemy) {
501  VectorCopy(self->movedir, last_movedir);
502  VectorMA(self->enemy->absmin, 0.5, self->enemy->size, point);
503  VectorSubtract(point, self->s.origin, self->movedir);
504  VectorNormalize(self->movedir);
505  if (!VectorCompare(self->movedir, last_movedir))
506  self->spawnflags |= 0x80000000;
507  }
508 
509  ignore = self;
510  VectorCopy(self->s.origin, start);
511  VectorMA(start, 2048, self->movedir, end);
512  while (1) {
513  tr = gi.trace(start, NULL, NULL, end, ignore, CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_DEADMONSTER);
514 
515  if (!tr.ent)
516  break;
517 
518  // hurt it if we can
519  if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER))
520  T_Damage(tr.ent, self, self->activator, self->movedir, tr.endpos, vec3_origin, self->dmg, 1, DAMAGE_ENERGY, MOD_TARGET_LASER);
521 
522  // if we hit something that's not a monster or player or is immune to lasers, we're done
523  if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client)) {
524  if (self->spawnflags & 0x80000000) {
525  self->spawnflags &= ~0x80000000;
526  gi.WriteByte(svc_temp_entity);
527  gi.WriteByte(TE_LASER_SPARKS);
528  gi.WriteByte(count);
529  gi.WritePosition(tr.endpos);
530  gi.WriteDir(tr.plane.normal);
531  gi.WriteByte(self->s.skinnum);
532  gi.multicast(tr.endpos, MULTICAST_PVS);
533  }
534  break;
535  }
536 
537  ignore = tr.ent;
538  VectorCopy(tr.endpos, start);
539  }
540 
541  VectorCopy(tr.endpos, self->s.old_origin);
542 
543  self->nextthink = level.time + FRAMETIME;
544 }
545 
546 void target_laser_on(edict_t *self)
547 {
548  if (!self->activator)
549  self->activator = self;
550  self->spawnflags |= 0x80000001;
551  self->svflags &= ~SVF_NOCLIENT;
552  target_laser_think(self);
553 }
554 
555 void target_laser_off(edict_t *self)
556 {
557  self->spawnflags &= ~1;
558  self->svflags |= SVF_NOCLIENT;
559  self->nextthink = 0;
560 }
561 
562 void target_laser_use(edict_t *self, edict_t *other, edict_t *activator)
563 {
564  self->activator = activator;
565  if (self->spawnflags & 1)
566  target_laser_off(self);
567  else
568  target_laser_on(self);
569 }
570 
571 void target_laser_start(edict_t *self)
572 {
573  edict_t *ent;
574 
575  self->movetype = MOVETYPE_NONE;
576  self->solid = SOLID_NOT;
577  self->s.renderfx |= RF_BEAM | RF_TRANSLUCENT;
578  self->s.modelindex = 1; // must be non-zero
579 
580  // set the beam diameter
581  if (self->spawnflags & 64)
582  self->s.frame = 16;
583  else
584  self->s.frame = 4;
585 
586  // set the color
587  if (self->spawnflags & 2)
588  self->s.skinnum = 0xf2f2f0f0;
589  else if (self->spawnflags & 4)
590  self->s.skinnum = 0xd0d1d2d3;
591  else if (self->spawnflags & 8)
592  self->s.skinnum = 0xf3f3f1f1;
593  else if (self->spawnflags & 16)
594  self->s.skinnum = 0xdcdddedf;
595  else if (self->spawnflags & 32)
596  self->s.skinnum = 0xe0e1e2e3;
597 
598  if (!self->enemy) {
599  if (self->target) {
600  ent = G_Find(NULL, FOFS(targetname), self->target);
601  if (!ent)
602  gi.dprintf("%s at %s: %s is a bad target\n", self->classname, vtos(self->s.origin), self->target);
603  self->enemy = ent;
604  } else {
605  G_SetMovedir(self->s.angles, self->movedir);
606  }
607  }
608  self->use = target_laser_use;
609  self->think = target_laser_think;
610 
611  if (!self->dmg)
612  self->dmg = 1;
613 
614  VectorSet(self->mins, -8, -8, -8);
615  VectorSet(self->maxs, 8, 8, 8);
616  gi.linkentity(self);
617 
618  if (self->spawnflags & 1)
619  target_laser_on(self);
620  else
621  target_laser_off(self);
622 }
623 
624 void SP_target_laser(edict_t *self)
625 {
626  // let everything else get spawned before we start firing
627  self->think = target_laser_start;
628  self->nextthink = level.time + 1;
629 }
630 
631 //==========================================================
632 
633 /*QUAKED target_lightramp (0 .5 .8) (-8 -8 -8) (8 8 8) TOGGLE
634 speed How many seconds the ramping will take
635 message two letters; starting lightlevel and ending lightlevel
636 */
637 
638 void target_lightramp_think(edict_t *self)
639 {
640  char style[2];
641 
642  style[0] = 'a' + self->movedir[0] + (level.time - self->timestamp) / FRAMETIME * self->movedir[2];
643  style[1] = 0;
644  gi.configstring(CS_LIGHTS + self->enemy->style, style);
645 
646  if ((level.time - self->timestamp) < self->speed) {
647  self->nextthink = level.time + FRAMETIME;
648  } else if (self->spawnflags & 1) {
649  char temp;
650 
651  temp = self->movedir[0];
652  self->movedir[0] = self->movedir[1];
653  self->movedir[1] = temp;
654  self->movedir[2] *= -1;
655  }
656 }
657 
658 void target_lightramp_use(edict_t *self, edict_t *other, edict_t *activator)
659 {
660  if (!self->enemy) {
661  edict_t *e;
662 
663  // check all the targets
664  e = NULL;
665  while (1) {
666  e = G_Find(e, FOFS(targetname), self->target);
667  if (!e)
668  break;
669  if (strcmp(e->classname, "light") != 0) {
670  gi.dprintf("%s at %s ", self->classname, vtos(self->s.origin));
671  gi.dprintf("target %s (%s at %s) is not a light\n", self->target, e->classname, vtos(e->s.origin));
672  } else {
673  self->enemy = e;
674  }
675  }
676 
677  if (!self->enemy) {
678  gi.dprintf("%s target %s not found at %s\n", self->classname, self->target, vtos(self->s.origin));
679  G_FreeEdict(self);
680  return;
681  }
682  }
683 
684  self->timestamp = level.time;
686 }
687 
688 void SP_target_lightramp(edict_t *self)
689 {
690  if (!self->message || strlen(self->message) != 2 || self->message[0] < 'a' || self->message[0] > 'z' || self->message[1] < 'a' || self->message[1] > 'z' || self->message[0] == self->message[1]) {
691  gi.dprintf("target_lightramp has bad ramp (%s) at %s\n", self->message, vtos(self->s.origin));
692  G_FreeEdict(self);
693  return;
694  }
695 
696  if (deathmatch->value) {
697  G_FreeEdict(self);
698  return;
699  }
700 
701  if (!self->target) {
702  gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin));
703  G_FreeEdict(self);
704  return;
705  }
706 
707  self->svflags |= SVF_NOCLIENT;
708  self->use = target_lightramp_use;
709  self->think = target_lightramp_think;
710 
711  self->movedir[0] = self->message[0] - 'a';
712  self->movedir[1] = self->message[1] - 'a';
713  self->movedir[2] = (self->movedir[1] - self->movedir[0]) / (self->speed / FRAMETIME);
714 }
715 
716 //==========================================================
717 
718 /*QUAKED target_earthquake (1 0 0) (-8 -8 -8) (8 8 8)
719 When triggered, this initiates a level-wide earthquake.
720 All players and monsters are affected.
721 "speed" severity of the quake (default:200)
722 "count" duration of the quake (default:5)
723 */
724 
725 void target_earthquake_think(edict_t *self)
726 {
727  int i;
728  edict_t *e;
729 
730  if (self->last_move_time < level.time) {
731  gi.positioned_sound(self->s.origin, self, CHAN_AUTO, self->noise_index, 1.0, ATTN_NONE, 0);
732  self->last_move_time = level.time + 0.5;
733  }
734 
735  for (i = 1, e = g_edicts + i; i < globals.num_edicts; i++, e++) {
736  if (!e->inuse)
737  continue;
738  if (!e->client)
739  continue;
740  if (!e->groundentity)
741  continue;
742 
743  e->groundentity = NULL;
744  e->velocity[0] += crandom() * 150;
745  e->velocity[1] += crandom() * 150;
746  e->velocity[2] = self->speed * (100.0 / e->mass);
747  }
748 
749  if (level.time < self->timestamp)
750  self->nextthink = level.time + FRAMETIME;
751 }
752 
753 void target_earthquake_use(edict_t *self, edict_t *other, edict_t *activator)
754 {
755  self->timestamp = level.time + self->count;
756  self->nextthink = level.time + FRAMETIME;
757  self->activator = activator;
758  self->last_move_time = 0;
759 }
760 
761 void SP_target_earthquake(edict_t *self)
762 {
763  if (!self->targetname)
764  gi.dprintf("untargeted %s at %s\n", self->classname, vtos(self->s.origin));
765 
766  if (!self->count)
767  self->count = 5;
768 
769  if (!self->speed)
770  self->speed = 200;
771 
772  self->svflags |= SVF_NOCLIENT;
773  self->think = target_earthquake_think;
774  self->use = target_earthquake_use;
775 
776  self->noise_index = gi.soundindex("world/quake.wav");
777 }
gi
game_import_t gi
Definition: g_main.c:23
use_target_changelevel
void use_target_changelevel(edict_t *self, edict_t *other, edict_t *activator)
Definition: g_target.c:264
MOD_TARGET_BLASTER
#define MOD_TARGET_BLASTER
Definition: g_local.h:490
deathmatch
cvar_t * deathmatch
Definition: g_main.c:33
game_locals_t::helpmessage1
char helpmessage1[512]
Definition: g_local.h:268
SP_target_secret
void SP_target_secret(edict_t *ent)
Definition: g_target.c:159
G_Spawn
edict_t * G_Spawn(void)
Definition: g_utils.c:391
SP_target_temp_entity
void SP_target_temp_entity(edict_t *ent)
Definition: g_target.c:32
Q_snprintf
size_t Q_snprintf(char *dest, size_t size, const char *fmt,...)
Definition: shared.c:846
use_target_goal
void use_target_goal(edict_t *ent, edict_t *other, edict_t *activator)
Definition: g_target.c:184
FRAMETIME
#define FRAMETIME
Definition: g_local.h:75
st
spawn_temp_t st
Definition: g_main.c:25
target_laser_on
void target_laser_on(edict_t *self)
Definition: g_target.c:546
use_target_blaster
void use_target_blaster(edict_t *self, edict_t *other, edict_t *activator)
Definition: g_target.c:405
SP_target_speaker
void SP_target_speaker(edict_t *ent)
Definition: g_target.c:78
game_locals_t::helpchanged
int helpchanged
Definition: g_local.h:270
SP_target_crosslevel_target
void SP_target_crosslevel_target(edict_t *self)
Definition: g_target.c:468
SP_target_laser
void SP_target_laser(edict_t *self)
Definition: g_target.c:624
DAMAGE_ENERGY
#define DAMAGE_ENERGY
Definition: g_local.h:649
MOD_SPLASH
#define MOD_SPLASH
Definition: g_local.h:486
MOD_TARGET_LASER
#define MOD_TARGET_LASER
Definition: g_local.h:487
SP_target_blaster
void SP_target_blaster(edict_t *self)
Definition: g_target.c:422
ED_CallSpawn
void ED_CallSpawn(edict_t *ent)
Definition: g_spawn.c:340
target_explosion_explode
void target_explosion_explode(edict_t *self)
Definition: g_target.c:222
Use_Target_Speaker
void Use_Target_Speaker(edict_t *ent, edict_t *other, edict_t *activator)
Definition: g_target.c:56
G_Find
edict_t * G_Find(edict_t *from, int fieldofs, char *match)
Definition: g_utils.c:43
FOFS
#define FOFS(x)
Definition: g_local.h:498
SP_target_lightramp
void SP_target_lightramp(edict_t *self)
Definition: g_target.c:688
target_lightramp_use
void target_lightramp_use(edict_t *self, edict_t *other, edict_t *activator)
Definition: g_target.c:658
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
SP_target_explosion
void SP_target_explosion(edict_t *ent)
Definition: g_target.c:252
BeginIntermission
void BeginIntermission(edict_t *targ)
Definition: p_hud.c:70
MOD_EXIT
#define MOD_EXIT
Definition: g_local.h:485
game_locals_t::helpmessage2
char helpmessage2[512]
Definition: g_local.h:269
Use_Target_Tent
void Use_Target_Tent(edict_t *ent, edict_t *other, edict_t *activator)
Definition: g_target.c:24
vec3_origin
vec3_t vec3_origin
Definition: shared.c:21
level_locals_t::total_secrets
int total_secrets
Definition: g_local.h:323
SFL_CROSS_TRIGGER_MASK
#define SFL_CROSS_TRIGGER_MASK
Definition: g_local.h:175
MOD_EXPLOSIVE
#define MOD_EXPLOSIVE
Definition: g_local.h:482
level_locals_t::total_goals
int total_goals
Definition: g_local.h:326
G_FreeEdict
void G_FreeEdict(edict_t *e)
Definition: g_utils.c:421
MOVETYPE_NONE
@ MOVETYPE_NONE
Definition: g_local.h:186
use_target_spawner
void use_target_spawner(edict_t *self, edict_t *other, edict_t *activator)
Definition: g_target.c:370
SP_target_splash
void SP_target_splash(edict_t *self)
Definition: g_target.c:342
target_earthquake_use
void target_earthquake_use(edict_t *self, edict_t *other, edict_t *activator)
Definition: g_target.c:753
level_locals_t::found_secrets
int found_secrets
Definition: g_local.h:324
svc_temp_entity
#define svc_temp_entity
Definition: g_local.h:38
crandom
#define crandom()
Definition: g_local.h:505
SP_target_earthquake
void SP_target_earthquake(edict_t *self)
Definition: g_target.c:761
SP_target_changelevel
void SP_target_changelevel(edict_t *ent)
Definition: g_target.c:293
vtos
char * vtos(vec3_t v)
Definition: g_utils.c:275
game
game_locals_t game
Definition: g_main.c:21
target_laser_off
void target_laser_off(edict_t *self)
Definition: g_target.c:555
globals
game_export_t globals
Definition: g_main.c:24
target_laser_use
void target_laser_use(edict_t *self, edict_t *other, edict_t *activator)
Definition: g_target.c:562
SP_target_spawner
void SP_target_spawner(edict_t *self)
Definition: g_target.c:386
level_locals_t::time
float time
Definition: g_local.h:299
target_laser_start
void target_laser_start(edict_t *self)
Definition: g_target.c:571
coop
cvar_t * coop
Definition: g_main.c:34
use_target_secret
void use_target_secret(edict_t *ent, edict_t *other, edict_t *activator)
Definition: g_target.c:149
level_locals_t::intermissiontime
float intermissiontime
Definition: g_local.h:306
game_locals_t::serverflags
int serverflags
Definition: g_local.h:284
FL_IMMUNE_LASER
#define FL_IMMUNE_LASER
Definition: g_local.h:61
world
#define world
Definition: g_local.h:550
SP_target_goal
void SP_target_goal(edict_t *ent)
Definition: g_target.c:197
target_laser_think
void target_laser_think(edict_t *self)
Definition: g_target.c:485
G_UseTargets
void G_UseTargets(edict_t *ent, edict_t *activator)
Definition: g_utils.c:166
level_locals_t::found_goals
int found_goals
Definition: g_local.h:327
target_lightramp_think
void target_lightramp_think(edict_t *self)
Definition: g_target.c:638
trigger_crosslevel_trigger_use
void trigger_crosslevel_trigger_use(edict_t *self, edict_t *other, edict_t *activator)
Definition: g_target.c:442
level
level_locals_t level
Definition: g_main.c:22
target_crosslevel_target_think
void target_crosslevel_target_think(edict_t *self)
Definition: g_target.c:460
G_SetMovedir
void G_SetMovedir(vec3_t angles, vec3_t movedir)
Definition: g_utils.c:296
KillBox
qboolean KillBox(edict_t *ent)
Definition: g_utils.c:515
fire_blaster
void fire_blaster(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int effect, qboolean hyper)
Definition: g_weapon.c:319
SP_target_crosslevel_trigger
void SP_target_crosslevel_trigger(edict_t *self)
Definition: g_target.c:448
use_target_splash
void use_target_splash(edict_t *self, edict_t *other, edict_t *activator)
Definition: g_target.c:328
dmflags
cvar_t * dmflags
Definition: g_main.c:35
Use_Target_Help
void Use_Target_Help(edict_t *ent, edict_t *other, edict_t *activator)
Definition: g_target.c:114
level_locals_t::mapname
char mapname[MAX_QPATH]
Definition: g_local.h:302
use_target_explosion
void use_target_explosion(edict_t *self, edict_t *other, edict_t *activator)
Definition: g_target.c:239
spawn_temp_t::noise
char * noise
Definition: g_local.h:352
SP_target_help
void SP_target_help(edict_t *ent)
Definition: g_target.c:127
target_earthquake_think
void target_earthquake_think(edict_t *self)
Definition: g_target.c:725
VectorNormalize
vec_t VectorNormalize(vec3_t v)
Definition: shared.c:55
g_local.h
T_RadiusDamage
void T_RadiusDamage(edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod)
Definition: g_combat.c:514