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