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