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