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 ent->s.renderfx |= RF_IR_VISIBLE; //PGM
377 }
378
SP_target_spawner(edict_t * self)379 void SP_target_spawner (edict_t *self)
380 {
381 self->use = use_target_spawner;
382 self->svflags = SVF_NOCLIENT;
383 if (self->speed)
384 {
385 G_SetMovedir (self->s.angles, self->movedir);
386 VectorScale (self->movedir, self->speed, self->movedir);
387 }
388 }
389
390 //==========================================================
391
392 /*QUAKED target_blaster (1 0 0) (-8 -8 -8) (8 8 8) NOTRAIL NOEFFECTS
393 Fires a blaster bolt in the set direction when triggered.
394
395 dmg default is 15
396 speed default is 1000
397 */
398
use_target_blaster(edict_t * self,edict_t * other,edict_t * activator)399 void use_target_blaster (edict_t *self, edict_t *other, edict_t *activator)
400 {
401 int effect;
402
403 if (self->spawnflags & 2)
404 effect = 0;
405 else if (self->spawnflags & 1)
406 effect = EF_HYPERBLASTER;
407 else
408 effect = EF_BLASTER;
409
410 fire_blaster (self, self->s.origin, self->movedir, self->dmg, self->speed, EF_BLASTER, MOD_TARGET_BLASTER);
411 gi.sound (self, CHAN_VOICE, self->noise_index, 1, ATTN_NORM, 0);
412 }
413
SP_target_blaster(edict_t * self)414 void SP_target_blaster (edict_t *self)
415 {
416 self->use = use_target_blaster;
417 G_SetMovedir (self->s.angles, self->movedir);
418 self->noise_index = gi.soundindex ("weapons/laser2.wav");
419
420 if (!self->dmg)
421 self->dmg = 15;
422 if (!self->speed)
423 self->speed = 1000;
424
425 self->svflags = SVF_NOCLIENT;
426 }
427
428
429 //==========================================================
430
431 /*QUAKED target_crosslevel_trigger (.5 .5 .5) (-8 -8 -8) (8 8 8) trigger1 trigger2 trigger3 trigger4 trigger5 trigger6 trigger7 trigger8
432 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.
433 */
trigger_crosslevel_trigger_use(edict_t * self,edict_t * other,edict_t * activator)434 void trigger_crosslevel_trigger_use (edict_t *self, edict_t *other, edict_t *activator)
435 {
436 game.serverflags |= self->spawnflags;
437 G_FreeEdict (self);
438 }
439
SP_target_crosslevel_trigger(edict_t * self)440 void SP_target_crosslevel_trigger (edict_t *self)
441 {
442 self->svflags = SVF_NOCLIENT;
443 self->use = trigger_crosslevel_trigger_use;
444 }
445
446 /*QUAKED target_crosslevel_target (.5 .5 .5) (-8 -8 -8) (8 8 8) trigger1 trigger2 trigger3 trigger4 trigger5 trigger6 trigger7 trigger8
447 Triggered by a trigger_crosslevel elsewhere within a unit. If multiple triggers are checked, all must be true. Delay, target and
448 killtarget also work.
449
450 "delay" delay before using targets if the trigger has been activated (default 1)
451 */
target_crosslevel_target_think(edict_t * self)452 void target_crosslevel_target_think (edict_t *self)
453 {
454 if (self->spawnflags == (game.serverflags & SFL_CROSS_TRIGGER_MASK & self->spawnflags))
455 {
456 G_UseTargets (self, self);
457 G_FreeEdict (self);
458 }
459 }
460
SP_target_crosslevel_target(edict_t * self)461 void SP_target_crosslevel_target (edict_t *self)
462 {
463 if (! self->delay)
464 self->delay = 1;
465 self->svflags = SVF_NOCLIENT;
466
467 self->think = target_crosslevel_target_think;
468 self->nextthink = level.time + self->delay;
469 }
470
471 //==========================================================
472
473 /*QUAKED target_laser (0 .5 .8) (-8 -8 -8) (8 8 8) START_ON RED GREEN BLUE YELLOW ORANGE FAT WINDOWSTOP
474 When triggered, fires a laser. You can either set a target
475 or a direction.
476
477 WINDOWSTOP - stops at CONTENTS_WINDOW
478 */
479
480 //======
481 // PGM
482 #define LASER_ON 0x0001
483 #define LASER_RED 0x0002
484 #define LASER_GREEN 0x0004
485 #define LASER_BLUE 0x0008
486 #define LASER_YELLOW 0x0010
487 #define LASER_ORANGE 0x0020
488 #define LASER_FAT 0x0040
489 #define LASER_STOPWINDOW 0x0080
490 // PGM
491 //======
492
target_laser_think(edict_t * self)493 void target_laser_think (edict_t *self)
494 {
495 edict_t *ignore;
496 vec3_t start;
497 vec3_t end;
498 trace_t tr;
499 vec3_t point;
500 vec3_t last_movedir;
501 int count;
502
503 if (self->spawnflags & 0x80000000)
504 count = 8;
505 else
506 count = 4;
507
508 if (self->enemy)
509 {
510 VectorCopy (self->movedir, last_movedir);
511 VectorMA (self->enemy->absmin, 0.5, self->enemy->size, point);
512 VectorSubtract (point, self->s.origin, self->movedir);
513 VectorNormalize (self->movedir);
514 if (!VectorCompare(self->movedir, last_movedir))
515 self->spawnflags |= 0x80000000;
516 }
517
518 ignore = self;
519 VectorCopy (self->s.origin, start);
520 VectorMA (start, 2048, self->movedir, end);
521 while(1)
522 {
523 //======
524 // PGM
525 if(self->spawnflags & LASER_STOPWINDOW)
526 tr = gi.trace (start, NULL, NULL, end, ignore, MASK_SHOT);
527 else
528 tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
529 // PGM
530 //======
531
532 if (!tr.ent)
533 break;
534
535 // hurt it if we can
536 if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER))
537 T_Damage (tr.ent, self, self->activator, self->movedir, tr.endpos, vec3_origin, self->dmg, 1, DAMAGE_ENERGY, MOD_TARGET_LASER);
538
539 // if we hit something that's not a monster or player or is immune to lasers, we're done
540 // if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
541 //PMM added SVF_DAMAGEABLE
542 if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client) && !(tr.ent->svflags & SVF_DAMAGEABLE))
543 {
544 if (self->spawnflags & 0x80000000)
545 {
546 self->spawnflags &= ~0x80000000;
547 gi.WriteByte (svc_temp_entity);
548 gi.WriteByte (TE_LASER_SPARKS);
549 gi.WriteByte (count);
550 gi.WritePosition (tr.endpos);
551 gi.WriteDir (tr.plane.normal);
552 gi.WriteByte (self->s.skinnum);
553 gi.multicast (tr.endpos, MULTICAST_PVS);
554 }
555 break;
556 }
557
558 ignore = tr.ent;
559 VectorCopy (tr.endpos, start);
560 }
561
562 VectorCopy (tr.endpos, self->s.old_origin);
563
564 self->nextthink = level.time + FRAMETIME;
565 }
566
target_laser_on(edict_t * self)567 void target_laser_on (edict_t *self)
568 {
569 if (!self->activator)
570 self->activator = self;
571 self->spawnflags |= 0x80000001;
572 self->svflags &= ~SVF_NOCLIENT;
573 target_laser_think (self);
574 }
575
target_laser_off(edict_t * self)576 void target_laser_off (edict_t *self)
577 {
578 self->spawnflags &= ~1;
579 self->svflags |= SVF_NOCLIENT;
580 self->nextthink = 0;
581 }
582
target_laser_use(edict_t * self,edict_t * other,edict_t * activator)583 void target_laser_use (edict_t *self, edict_t *other, edict_t *activator)
584 {
585 self->activator = activator;
586 if (self->spawnflags & 1)
587 target_laser_off (self);
588 else
589 target_laser_on (self);
590 }
591
target_laser_start(edict_t * self)592 void target_laser_start (edict_t *self)
593 {
594 edict_t *ent;
595
596 self->movetype = MOVETYPE_NONE;
597 self->solid = SOLID_NOT;
598 self->s.renderfx |= RF_BEAM|RF_TRANSLUCENT;
599 self->s.modelindex = 1; // must be non-zero
600
601 // set the beam diameter
602 if (self->spawnflags & 64)
603 self->s.frame = 16;
604 else
605 self->s.frame = 4;
606
607 // set the color
608 if (self->spawnflags & 2)
609 self->s.skinnum = 0xf2f2f0f0;
610 else if (self->spawnflags & 4)
611 self->s.skinnum = 0xd0d1d2d3;
612 else if (self->spawnflags & 8)
613 self->s.skinnum = 0xf3f3f1f1;
614 else if (self->spawnflags & 16)
615 self->s.skinnum = 0xdcdddedf;
616 else if (self->spawnflags & 32)
617 self->s.skinnum = 0xe0e1e2e3;
618
619 if (!self->enemy)
620 {
621 if (self->target)
622 {
623 ent = G_Find (NULL, FOFS(targetname), self->target);
624 if (!ent)
625 gi.dprintf ("%s at %s: %s is a bad target\n", self->classname, vtos(self->s.origin), self->target);
626 self->enemy = ent;
627 }
628 else
629 {
630 G_SetMovedir (self->s.angles, self->movedir);
631 }
632 }
633 self->use = target_laser_use;
634 self->think = target_laser_think;
635
636 if (!self->dmg)
637 self->dmg = 1;
638
639 VectorSet (self->mins, -8, -8, -8);
640 VectorSet (self->maxs, 8, 8, 8);
641 gi.linkentity (self);
642
643 if (self->spawnflags & 1)
644 target_laser_on (self);
645 else
646 target_laser_off (self);
647 }
648
SP_target_laser(edict_t * self)649 void SP_target_laser (edict_t *self)
650 {
651 // let everything else get spawned before we start firing
652 self->think = target_laser_start;
653 self->nextthink = level.time + 1;
654 }
655
656 //==========================================================
657
658 /*QUAKED target_lightramp (0 .5 .8) (-8 -8 -8) (8 8 8) TOGGLE
659 speed How many seconds the ramping will take
660 message two letters; starting lightlevel and ending lightlevel
661 */
662
target_lightramp_think(edict_t * self)663 void target_lightramp_think (edict_t *self)
664 {
665 char style[2];
666
667 style[0] = 'a' + self->movedir[0] + (level.time - self->timestamp) / FRAMETIME * self->movedir[2];
668 style[1] = 0;
669 gi.configstring (CS_LIGHTS+self->enemy->style, style);
670
671 if ((level.time - self->timestamp) < self->speed)
672 {
673 self->nextthink = level.time + FRAMETIME;
674 }
675 else if (self->spawnflags & 1)
676 {
677 char temp;
678
679 temp = self->movedir[0];
680 self->movedir[0] = self->movedir[1];
681 self->movedir[1] = temp;
682 self->movedir[2] *= -1;
683 }
684 }
685
target_lightramp_use(edict_t * self,edict_t * other,edict_t * activator)686 void target_lightramp_use (edict_t *self, edict_t *other, edict_t *activator)
687 {
688 if (!self->enemy)
689 {
690 edict_t *e;
691
692 // check all the targets
693 e = NULL;
694 while (1)
695 {
696 e = G_Find (e, FOFS(targetname), self->target);
697 if (!e)
698 break;
699 if (strcmp(e->classname, "light") != 0)
700 {
701 gi.dprintf("%s at %s ", self->classname, vtos(self->s.origin));
702 gi.dprintf("target %s (%s at %s) is not a light\n", self->target, e->classname, vtos(e->s.origin));
703 }
704 else
705 {
706 self->enemy = e;
707 }
708 }
709
710 if (!self->enemy)
711 {
712 gi.dprintf("%s target %s not found at %s\n", self->classname, self->target, vtos(self->s.origin));
713 G_FreeEdict (self);
714 return;
715 }
716 }
717
718 self->timestamp = level.time;
719 target_lightramp_think (self);
720 }
721
SP_target_lightramp(edict_t * self)722 void SP_target_lightramp (edict_t *self)
723 {
724 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])
725 {
726 gi.dprintf("target_lightramp has bad ramp (%s) at %s\n", self->message, vtos(self->s.origin));
727 G_FreeEdict (self);
728 return;
729 }
730
731 if (deathmatch->value)
732 {
733 G_FreeEdict (self);
734 return;
735 }
736
737 if (!self->target)
738 {
739 gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin));
740 G_FreeEdict (self);
741 return;
742 }
743
744 self->svflags |= SVF_NOCLIENT;
745 self->use = target_lightramp_use;
746 self->think = target_lightramp_think;
747
748 self->movedir[0] = self->message[0] - 'a';
749 self->movedir[1] = self->message[1] - 'a';
750 self->movedir[2] = (self->movedir[1] - self->movedir[0]) / (self->speed / FRAMETIME);
751 }
752
753 //==========================================================
754
755 /*QUAKED target_earthquake (1 0 0) (-8 -8 -8) (8 8 8) SILENT
756 When triggered, this initiates a level-wide earthquake.
757 All players and monsters are affected.
758 "speed" severity of the quake (default:200)
759 "count" duration of the quake (default:5)
760 */
761
target_earthquake_think(edict_t * self)762 void target_earthquake_think (edict_t *self)
763 {
764 int i;
765 edict_t *e;
766
767 if(!(self->spawnflags & 1)) // PGM
768 { // PGM
769 if (self->last_move_time < level.time)
770 {
771 gi.positioned_sound (self->s.origin, self, CHAN_AUTO, self->noise_index, 1.0, ATTN_NONE, 0);
772 self->last_move_time = level.time + 0.5;
773 }
774 } // PGM
775
776 for (i=1, e=g_edicts+i; i < globals.num_edicts; i++,e++)
777 {
778 if (!e->inuse)
779 continue;
780 if (!e->client)
781 continue;
782 if (!e->groundentity)
783 continue;
784
785 e->groundentity = NULL;
786 e->velocity[0] += crandom()* 150;
787 e->velocity[1] += crandom()* 150;
788 e->velocity[2] = self->speed * (100.0 / e->mass);
789 }
790
791 if (level.time < self->timestamp)
792 self->nextthink = level.time + FRAMETIME;
793 }
794
target_earthquake_use(edict_t * self,edict_t * other,edict_t * activator)795 void target_earthquake_use (edict_t *self, edict_t *other, edict_t *activator)
796 {
797 // PGM
798 // if(g_showlogic && g_showlogic->value)
799 // gi.dprintf("earthquake: %0.1f\n", self->speed);
800 // PGM
801
802 self->timestamp = level.time + self->count;
803 self->nextthink = level.time + FRAMETIME;
804 self->activator = activator;
805 self->last_move_time = 0;
806 }
807
SP_target_earthquake(edict_t * self)808 void SP_target_earthquake (edict_t *self)
809 {
810 if (!self->targetname)
811 gi.dprintf("untargeted %s at %s\n", self->classname, vtos(self->s.origin));
812
813 if (!self->count)
814 self->count = 5;
815
816 if (!self->speed)
817 self->speed = 200;
818
819 self->svflags |= SVF_NOCLIENT;
820 self->think = target_earthquake_think;
821 self->use = target_earthquake_use;
822
823 if(!(self->spawnflags & 1)) // PGM
824 self->noise_index = gi.soundindex ("world/quake.wav");
825 }
826