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