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