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 allways 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 Com_sprintf (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->value)
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->value)
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 (!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->value)
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; // allready activated
274
275 if (!deathmatch->value && !coop->value)
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->value && !( (int)dmflags->value & DF_ALLOW_EXIT) && other != world)
283 {
284 T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 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->value)
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((stricmp(level.mapname, "fact1") == 0) && (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 VectorCopy (self->s.origin, ent->s.origin);
387 VectorCopy (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 VectorCopy (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 VectorScale (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 VectorCopy (self->movedir, last_movedir);
513 VectorMA (self->enemy->absmin, 0.5, self->enemy->size, point);
514 VectorSubtract (point, self->s.origin, self->movedir);
515 VectorNormalize (self->movedir);
516 if (!VectorCompare(self->movedir, last_movedir))
517 self->spawnflags |= 0x80000000;
518 }
519
520 ignore = self;
521 VectorCopy (self->s.origin, start);
522 VectorMA (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, vec3_origin, 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 VectorCopy (tr.endpos, start);
553 }
554
555 VectorCopy (tr.endpos, self->s.old_origin);
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 VectorSet (self->mins, -8, -8, -8);
633 VectorSet (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
651 /*QUAKED target_lightramp (0 .5 .8) (-8 -8 -8) (8 8 8) TOGGLE
652 speed How many seconds the ramping will take
653 message two letters; starting lightlevel and ending lightlevel
654 */
655
target_lightramp_think(edict_t * self)656 void target_lightramp_think (edict_t *self)
657 {
658 char style[2];
659
660 style[0] = 'a' + self->movedir[0] + (level.time - self->timestamp) / FRAMETIME * self->movedir[2];
661 style[1] = 0;
662 gi.configstring (CS_LIGHTS+self->enemy->style, style);
663
664 if ((level.time - self->timestamp) < self->speed)
665 {
666 self->nextthink = level.time + FRAMETIME;
667 }
668 else if (self->spawnflags & 1)
669 {
670 char temp;
671
672 temp = self->movedir[0];
673 self->movedir[0] = self->movedir[1];
674 self->movedir[1] = temp;
675 self->movedir[2] *= -1;
676 }
677 }
678
target_lightramp_use(edict_t * self,edict_t * other,edict_t * activator)679 void target_lightramp_use (edict_t *self, edict_t *other, edict_t *activator)
680 {
681 if (!self->enemy)
682 {
683 edict_t *e;
684
685 // check all the targets
686 e = NULL;
687 while (1)
688 {
689 e = G_Find (e, FOFS(targetname), self->target);
690 if (!e)
691 break;
692 if (strcmp(e->classname, "light") != 0)
693 {
694 gi.dprintf("%s at %s ", self->classname, vtos(self->s.origin));
695 gi.dprintf("target %s (%s at %s) is not a light\n", self->target, e->classname, vtos(e->s.origin));
696 }
697 else
698 {
699 self->enemy = e;
700 }
701 }
702
703 if (!self->enemy)
704 {
705 gi.dprintf("%s target %s not found at %s\n", self->classname, self->target, vtos(self->s.origin));
706 G_FreeEdict (self);
707 return;
708 }
709 }
710
711 self->timestamp = level.time;
712 target_lightramp_think (self);
713 }
714
SP_target_lightramp(edict_t * self)715 void SP_target_lightramp (edict_t *self)
716 {
717 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])
718 {
719 gi.dprintf("target_lightramp has bad ramp (%s) at %s\n", self->message, vtos(self->s.origin));
720 G_FreeEdict (self);
721 return;
722 }
723
724 if (deathmatch->value)
725 {
726 G_FreeEdict (self);
727 return;
728 }
729
730 if (!self->target)
731 {
732 gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin));
733 G_FreeEdict (self);
734 return;
735 }
736
737 self->svflags |= SVF_NOCLIENT;
738 self->use = target_lightramp_use;
739 self->think = target_lightramp_think;
740
741 self->movedir[0] = self->message[0] - 'a';
742 self->movedir[1] = self->message[1] - 'a';
743 self->movedir[2] = (self->movedir[1] - self->movedir[0]) / (self->speed / FRAMETIME);
744 }
745
746 //==========================================================
747
748 /*QUAKED target_earthquake (1 0 0) (-8 -8 -8) (8 8 8)
749 When triggered, this initiates a level-wide earthquake.
750 All players and monsters are affected.
751 "speed" severity of the quake (default:200)
752 "count" duration of the quake (default:5)
753 */
754
target_earthquake_think(edict_t * self)755 void target_earthquake_think (edict_t *self)
756 {
757 int i;
758 edict_t *e;
759
760 if (self->last_move_time < level.time)
761 {
762 gi.positioned_sound (self->s.origin, self, CHAN_AUTO, self->noise_index, 1.0, ATTN_NONE, 0);
763 self->last_move_time = level.time + 0.5;
764 }
765
766 for (i=1, e=g_edicts+i; i < globals.num_edicts; i++,e++)
767 {
768 if (!e->inuse)
769 continue;
770 if (!e->client)
771 continue;
772 if (!e->groundentity)
773 continue;
774
775 e->groundentity = NULL;
776 e->velocity[0] += crandom()* 150;
777 e->velocity[1] += crandom()* 150;
778 e->velocity[2] = self->speed * (100.0 / e->mass);
779 }
780
781 if (level.time < self->timestamp)
782 self->nextthink = level.time + FRAMETIME;
783 }
784
target_earthquake_use(edict_t * self,edict_t * other,edict_t * activator)785 void target_earthquake_use (edict_t *self, edict_t *other, edict_t *activator)
786 {
787 self->timestamp = level.time + self->count;
788 self->nextthink = level.time + FRAMETIME;
789 self->activator = activator;
790 self->last_move_time = 0;
791 }
792
SP_target_earthquake(edict_t * self)793 void SP_target_earthquake (edict_t *self)
794 {
795 if (!self->targetname)
796 gi.dprintf("untargeted %s at %s\n", self->classname, vtos(self->s.origin));
797
798 if (!self->count)
799 self->count = 5;
800
801 if (!self->speed)
802 self->speed = 200;
803
804 self->svflags |= SVF_NOCLIENT;
805 self->think = target_earthquake_think;
806 self->use = target_earthquake_use;
807
808 self->noise_index = gi.soundindex ("world/quake.wav");
809 }
810