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