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