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