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 (!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((stricmp(level.mapname, "fact1") == 0) && (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 VectorCopy (tr.endpos,self->moveinfo.end_origin);
509
510 if (!tr.ent)
511 break;
512
513 // hurt it if we can
514 if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER))
515 T_Damage (tr.ent, self, self->activator, self->movedir, tr.endpos, vec3_origin, self->dmg, 1, DAMAGE_ENERGY, MOD_TARGET_LASER);
516
517 // if we hit something that's not a monster or player or is immune to lasers, we're done
518 if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
519 {
520 if (self->spawnflags & 0x80000000)
521 {
522 self->spawnflags &= ~0x80000000;
523 gi.WriteByte (svc_temp_entity);
524 gi.WriteByte (TE_LASER_SPARKS);
525 gi.WriteByte (count);
526 gi.WritePosition (tr.endpos);
527 gi.WriteDir (tr.plane.normal);
528 gi.WriteByte (self->s.skinnum);
529 gi.multicast (tr.endpos, MULTICAST_PVS);
530 }
531 break;
532 }
533
534 ignore = tr.ent;
535 VectorCopy (tr.endpos, start);
536 }
537
538 VectorCopy (tr.endpos, self->s.old_origin);
539
540 self->nextthink = level.time + FRAMETIME;
541 }
542
target_laser_on(edict_t * self)543 void target_laser_on (edict_t *self)
544 {
545 if (!self->activator)
546 self->activator = self;
547 self->spawnflags |= 0x80000001;
548 self->svflags &= ~SVF_NOCLIENT;
549 target_laser_think (self);
550 }
551
target_laser_off(edict_t * self)552 void target_laser_off (edict_t *self)
553 {
554 self->spawnflags &= ~1;
555 self->svflags |= SVF_NOCLIENT;
556 self->nextthink = 0;
557 }
558
target_laser_use(edict_t * self,edict_t * other,edict_t * activator)559 void target_laser_use (edict_t *self, edict_t *other, edict_t *activator)
560 {
561 self->activator = activator;
562 if (self->spawnflags & 1)
563 target_laser_off (self);
564 else
565 target_laser_on (self);
566 }
567
target_laser_start(edict_t * self)568 void target_laser_start (edict_t *self)
569 {
570 edict_t *ent;
571
572 self->movetype = MOVETYPE_NONE;
573 self->solid = SOLID_NOT;
574 self->s.renderfx |= RF_BEAM|RF_TRANSLUCENT;
575 self->s.modelindex = 1; // must be non-zero
576
577 // set the beam diameter
578 if (self->spawnflags & 64)
579 self->s.frame = 16;
580 else
581 self->s.frame = 4;
582
583 // set the color
584 if (self->spawnflags & 2)
585 self->s.skinnum = 0xf2f2f0f0;
586 else if (self->spawnflags & 4)
587 self->s.skinnum = 0xd0d1d2d3;
588 else if (self->spawnflags & 8)
589 self->s.skinnum = 0xf3f3f1f1;
590 else if (self->spawnflags & 16)
591 self->s.skinnum = 0xdcdddedf;
592 else if (self->spawnflags & 32)
593 self->s.skinnum = 0xe0e1e2e3;
594
595 if (!self->enemy)
596 {
597 if (self->target)
598 {
599 ent = G_Find (NULL, FOFS(targetname), self->target);
600 if (!ent)
601 gi.dprintf ("%s at %s: %s is a bad target\n", self->classname, vtos(self->s.origin), self->target);
602 self->enemy = ent;
603 }
604 else
605 {
606 G_SetMovedir (self->s.angles, self->movedir);
607 }
608 }
609 self->use = target_laser_use;
610 self->think = target_laser_think;
611
612 if (!self->dmg)
613 self->dmg = 1;
614
615 VectorSet (self->mins, -8, -8, -8);
616 VectorSet (self->maxs, 8, 8, 8);
617 gi.linkentity (self);
618
619 if (self->spawnflags & 1)
620 target_laser_on (self);
621 else
622 target_laser_off (self);
623 }
624
SP_target_laser(edict_t * self)625 void SP_target_laser (edict_t *self)
626 {
627 // let everything else get spawned before we start firing
628 self->think = target_laser_start;
629 self->nextthink = level.time + 1;
630 }
631
632 //==========================================================
633 // RAFAEL 15-APR-98
634 /*QUAKED target_mal_laser (1 0 0) (-4 -4 -4) (4 4 4) START_ON RED GREEN BLUE YELLOW ORANGE FAT
635 Mal's laser
636 */
target_mal_laser_on(edict_t * self)637 void target_mal_laser_on (edict_t *self)
638 {
639 if (!self->activator)
640 self->activator = self;
641 self->spawnflags |= 0x80000001;
642 self->svflags &= ~SVF_NOCLIENT;
643 // target_laser_think (self);
644 self->nextthink = level.time + self->wait + self->delay;
645 }
646
target_mal_laser_off(edict_t * self)647 void target_mal_laser_off (edict_t *self)
648 {
649 self->spawnflags &= ~1;
650 self->svflags |= SVF_NOCLIENT;
651 self->nextthink = 0;
652 }
653
target_mal_laser_use(edict_t * self,edict_t * other,edict_t * activator)654 void target_mal_laser_use (edict_t *self, edict_t *other, edict_t *activator)
655 {
656 self->activator = activator;
657 if (self->spawnflags & 1)
658 target_mal_laser_off (self);
659 else
660 target_mal_laser_on (self);
661 }
662
mal_laser_think(edict_t * self)663 void mal_laser_think (edict_t *self)
664 {
665 target_laser_think (self);
666 self->nextthink = level.time + self->wait + 0.1;
667 self->spawnflags |= 0x80000000;
668 }
669
SP_target_mal_laser(edict_t * self)670 void SP_target_mal_laser (edict_t *self)
671 {
672 self->movetype = MOVETYPE_NONE;
673 self->solid = SOLID_NOT;
674 self->s.renderfx |= RF_BEAM|RF_TRANSLUCENT;
675 self->s.modelindex = 1; // must be non-zero
676
677 // set the beam diameter
678 if (self->spawnflags & 64)
679 self->s.frame = 16;
680 else
681 self->s.frame = 4;
682
683 // set the color
684 if (self->spawnflags & 2)
685 self->s.skinnum = 0xf2f2f0f0;
686 else if (self->spawnflags & 4)
687 self->s.skinnum = 0xd0d1d2d3;
688 else if (self->spawnflags & 8)
689 self->s.skinnum = 0xf3f3f1f1;
690 else if (self->spawnflags & 16)
691 self->s.skinnum = 0xdcdddedf;
692 else if (self->spawnflags & 32)
693 self->s.skinnum = 0xe0e1e2e3;
694
695 G_SetMovedir (self->s.angles, self->movedir);
696
697 if (!self->delay)
698 self->delay = 0.1;
699
700 if (!self->wait)
701 self->wait = 0.1;
702
703 if (!self->dmg)
704 self->dmg = 5;
705
706 VectorSet (self->mins, -8, -8, -8);
707 VectorSet (self->maxs, 8, 8, 8);
708
709 self->nextthink = level.time + self->delay;
710 self->think = mal_laser_think;
711
712 self->use = target_mal_laser_use;
713
714 gi.linkentity (self);
715
716 if (self->spawnflags & 1)
717 target_mal_laser_on (self);
718 else
719 target_mal_laser_off (self);
720 }
721 // END 15-APR-98
722 //==========================================================
723 /*QUAKED target_lightramp (0 .5 .8) (-8 -8 -8) (8 8 8) TOGGLE
724 speed How many seconds the ramping will take
725 message two letters; starting lightlevel and ending lightlevel
726 */
727
target_lightramp_think(edict_t * self)728 void target_lightramp_think (edict_t *self)
729 {
730 char style[2];
731
732 style[0] = 'a' + self->movedir[0] + (level.time - self->timestamp) / FRAMETIME * self->movedir[2];
733 style[1] = 0;
734 gi.configstring (CS_LIGHTS+self->enemy->style, style);
735
736 if ((level.time - self->timestamp) < self->speed)
737 {
738 self->nextthink = level.time + FRAMETIME;
739 }
740 else if (self->spawnflags & 1)
741 {
742 char temp;
743
744 temp = self->movedir[0];
745 self->movedir[0] = self->movedir[1];
746 self->movedir[1] = temp;
747 self->movedir[2] *= -1;
748 }
749 }
750
target_lightramp_use(edict_t * self,edict_t * other,edict_t * activator)751 void target_lightramp_use (edict_t *self, edict_t *other, edict_t *activator)
752 {
753 if (!self->enemy)
754 {
755 edict_t *e;
756
757 // check all the targets
758 e = NULL;
759 while (1)
760 {
761 e = G_Find (e, FOFS(targetname), self->target);
762 if (!e)
763 break;
764 if (strcmp(e->classname, "light") != 0)
765 {
766 gi.dprintf("%s at %s ", self->classname, vtos(self->s.origin));
767 gi.dprintf("target %s (%s at %s) is not a light\n", self->target, e->classname, vtos(e->s.origin));
768 }
769 else
770 {
771 self->enemy = e;
772 }
773 }
774
775 if (!self->enemy)
776 {
777 gi.dprintf("%s target %s not found at %s\n", self->classname, self->target, vtos(self->s.origin));
778 G_FreeEdict (self);
779 return;
780 }
781 }
782
783 self->timestamp = level.time;
784 target_lightramp_think (self);
785 }
786
SP_target_lightramp(edict_t * self)787 void SP_target_lightramp (edict_t *self)
788 {
789 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])
790 {
791 gi.dprintf("target_lightramp has bad ramp (%s) at %s\n", self->message, vtos(self->s.origin));
792 G_FreeEdict (self);
793 return;
794 }
795
796 if (deathmatch->value)
797 {
798 G_FreeEdict (self);
799 return;
800 }
801
802 if (!self->target)
803 {
804 gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin));
805 G_FreeEdict (self);
806 return;
807 }
808
809 self->svflags |= SVF_NOCLIENT;
810 self->use = target_lightramp_use;
811 self->think = target_lightramp_think;
812
813 self->movedir[0] = self->message[0] - 'a';
814 self->movedir[1] = self->message[1] - 'a';
815 self->movedir[2] = (self->movedir[1] - self->movedir[0]) / (self->speed / FRAMETIME);
816 }
817
818 //==========================================================
819
820 /*QUAKED target_earthquake (1 0 0) (-8 -8 -8) (8 8 8)
821 When triggered, this initiates a level-wide earthquake.
822 All players and monsters are affected.
823 "speed" severity of the quake (default:200)
824 "count" duration of the quake (default:5)
825 */
826
target_earthquake_think(edict_t * self)827 void target_earthquake_think (edict_t *self)
828 {
829 int i;
830 edict_t *e;
831
832 if (self->last_move_time < level.time)
833 {
834 gi.positioned_sound (self->s.origin, self, CHAN_AUTO, self->noise_index, 1.0, ATTN_NONE, 0);
835 self->last_move_time = level.time + 0.5;
836 }
837
838 for (i=1, e=g_edicts+i; i < globals.num_edicts; i++,e++)
839 {
840 if (!e->inuse)
841 continue;
842 if (!e->client)
843 continue;
844 if (!e->groundentity)
845 continue;
846
847 e->groundentity = NULL;
848 e->velocity[0] += crandom()* 150;
849 e->velocity[1] += crandom()* 150;
850 e->velocity[2] = self->speed * (100.0 / e->mass);
851 }
852
853 if (level.time < self->timestamp)
854 self->nextthink = level.time + FRAMETIME;
855 }
856
target_earthquake_use(edict_t * self,edict_t * other,edict_t * activator)857 void target_earthquake_use (edict_t *self, edict_t *other, edict_t *activator)
858 {
859 self->timestamp = level.time + self->count;
860 self->nextthink = level.time + FRAMETIME;
861 self->activator = activator;
862 self->last_move_time = 0;
863 }
864
SP_target_earthquake(edict_t * self)865 void SP_target_earthquake (edict_t *self)
866 {
867 if (!self->targetname)
868 gi.dprintf("untargeted %s at %s\n", self->classname, vtos(self->s.origin));
869
870 if (!self->count)
871 self->count = 5;
872
873 if (!self->speed)
874 self->speed = 200;
875
876 self->svflags |= SVF_NOCLIENT;
877 self->think = target_earthquake_think;
878 self->use = target_earthquake_use;
879
880 self->noise_index = gi.soundindex ("world/quake.wav");
881 }
882