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