1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein multiplayer GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (“RTCW MP Source Code”).
8 
9 RTCW MP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 RTCW MP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with RTCW MP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 /*
30  * name:		g_target.c
31  *
32  * desc:
33  *
34 */
35 
36 #include "g_local.h"
37 
38 //==========================================================
39 
40 /*QUAKED target_give (1 0 0) (-8 -8 -8) (8 8 8)
41 Gives the activator all the items pointed to.
42 */
Use_Target_Give(gentity_t * ent,gentity_t * other,gentity_t * activator)43 void Use_Target_Give( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
44 	gentity_t   *t;
45 	trace_t trace;
46 
47 	if ( !activator->client ) {
48 		return;
49 	}
50 
51 	if ( !ent->target ) {
52 		return;
53 	}
54 
55 	memset( &trace, 0, sizeof( trace ) );
56 	t = NULL;
57 	while ( ( t = G_Find( t, FOFS( targetname ), ent->target ) ) != NULL ) {
58 		if ( !t->item ) {
59 			continue;
60 		}
61 		Touch_Item( t, activator, &trace );
62 
63 		// make sure it isn't going to respawn or show any events
64 		t->nextthink = 0;
65 		trap_UnlinkEntity( t );
66 	}
67 }
68 
SP_target_give(gentity_t * ent)69 void SP_target_give( gentity_t *ent ) {
70 	ent->use = Use_Target_Give;
71 }
72 
73 
74 //==========================================================
75 
76 /*QUAKED target_remove_powerups (1 0 0) (-8 -8 -8) (8 8 8)
77 takes away all the activators powerups.
78 Used to drop flight powerups into death puts.
79 */
Use_target_remove_powerups(gentity_t * ent,gentity_t * other,gentity_t * activator)80 void Use_target_remove_powerups( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
81 	if ( !activator->client ) {
82 		return;
83 	}
84 
85 	if ( activator->client->ps.powerups[PW_REDFLAG] ) {
86 		Team_ReturnFlag( TEAM_RED );
87 	} else if ( activator->client->ps.powerups[PW_BLUEFLAG] ) {
88 		Team_ReturnFlag( TEAM_BLUE );
89 	}
90 
91 	memset( activator->client->ps.powerups, 0, sizeof( activator->client->ps.powerups ) );
92 }
93 
SP_target_remove_powerups(gentity_t * ent)94 void SP_target_remove_powerups( gentity_t *ent ) {
95 	ent->use = Use_target_remove_powerups;
96 }
97 
98 
99 //==========================================================
100 
101 /*QUAKED target_delay (1 1 0) (-8 -8 -8) (8 8 8)
102 "wait" seconds to pause before firing targets.
103 "random" delay variance, total delay = delay +/- random seconds
104 */
Think_Target_Delay(gentity_t * ent)105 void Think_Target_Delay( gentity_t *ent ) {
106 	G_UseTargets( ent, ent->activator );
107 }
108 
Use_Target_Delay(gentity_t * ent,gentity_t * other,gentity_t * activator)109 void Use_Target_Delay( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
110 	ent->nextthink = level.time + ( ent->wait + ent->random * crandom() ) * 1000;
111 	ent->think = Think_Target_Delay;
112 	ent->activator = activator;
113 }
114 
SP_target_delay(gentity_t * ent)115 void SP_target_delay( gentity_t *ent ) {
116 	// check delay for backwards compatability
117 	if ( !G_SpawnFloat( "delay", "0", &ent->wait ) ) {
118 		G_SpawnFloat( "wait", "1", &ent->wait );
119 	}
120 
121 	if ( !ent->wait ) {
122 		ent->wait = 1;
123 	}
124 	ent->use = Use_Target_Delay;
125 }
126 
127 
128 //==========================================================
129 
130 /*QUAKED target_score (1 0 0) (-8 -8 -8) (8 8 8)
131 "count" number of points to add, default 1
132 
133 The activator is given this many points.
134 */
Use_Target_Score(gentity_t * ent,gentity_t * other,gentity_t * activator)135 void Use_Target_Score( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
136 	AddScore( activator, ent->count );
137 }
138 
SP_target_score(gentity_t * ent)139 void SP_target_score( gentity_t *ent ) {
140 	if ( !ent->count ) {
141 		ent->count = 1;
142 	}
143 	ent->use = Use_Target_Score;
144 }
145 
146 
147 
148 //==========================================================
149 
150 /*QUAKED target_print (1 0 0) (-8 -8 -8) (8 8 8) redteam blueteam private
151 "message"	text to print
152 If "private", only the activator gets the message.  If no checks, all clients get the message.
153 */
Use_Target_Print(gentity_t * ent,gentity_t * other,gentity_t * activator)154 void Use_Target_Print( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
155 	if ( activator->client && ( ent->spawnflags & 4 ) ) {
156 		trap_SendServerCommand( activator - g_entities, va( "cp \"%s\"", ent->message ) );
157 		return;
158 	}
159 
160 	if ( ent->spawnflags & 3 ) {
161 		if ( ent->spawnflags & 1 ) {
162 			G_TeamCommand( TEAM_RED, va( "cp \"%s\"", ent->message ) );
163 		}
164 		if ( ent->spawnflags & 2 ) {
165 			G_TeamCommand( TEAM_BLUE, va( "cp \"%s\"", ent->message ) );
166 		}
167 		return;
168 	}
169 
170 	trap_SendServerCommand( -1, va( "cp \"%s\"", ent->message ) );
171 }
172 
SP_target_print(gentity_t * ent)173 void SP_target_print( gentity_t *ent ) {
174 	ent->use = Use_Target_Print;
175 }
176 
177 
178 //==========================================================
179 
180 
181 /*QUAKED target_speaker (1 0 0) (-8 -8 -8) (8 8 8) LOOPED_ON LOOPED_OFF GLOBAL ACTIVATOR VIS_MULTIPLE NO_PVS
182 "noise"		wav file to play
183 
184 A global sound will play full volume throughout the level.
185 Activator sounds will play on the player that activated the target.
186 Global and activator sounds can't be combined with looping.
187 Normal sounds play each time the target is used.
188 Looped sounds will be toggled by use functions.
189 Multiple identical looping sounds will just increase volume without any speed cost.
190 NO_PVS - this sound will not turn off when not in the player's PVS
191 "wait" : Seconds between auto triggerings, 0 = don't auto trigger
192 "random" : wait variance, default is 0
193 */
Use_Target_Speaker(gentity_t * ent,gentity_t * other,gentity_t * activator)194 void Use_Target_Speaker( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
195 	if ( ent->spawnflags & 3 ) {  // looping sound toggles
196 		if ( ent->s.loopSound ) {
197 			ent->s.loopSound = 0;   // turn it off
198 		} else {
199 			ent->s.loopSound = ent->noise_index;    // start it
200 		}
201 	} else { // normal sound
202 		if ( ent->spawnflags & 8 ) {
203 			G_AddEvent( activator, EV_GENERAL_SOUND, ent->noise_index );
204 		} else if ( ent->spawnflags & 4 ) {
205 			G_AddEvent( ent, EV_GLOBAL_SOUND, ent->noise_index );
206 		} else {
207 			G_AddEvent( ent, EV_GENERAL_SOUND, ent->noise_index );
208 		}
209 	}
210 }
211 
target_speaker_multiple(gentity_t * ent)212 void target_speaker_multiple( gentity_t *ent ) {
213 	gentity_t *vis_dummy = NULL;
214 
215 	if ( !( ent->target ) ) {
216 		G_Error( "target_speaker missing target at pos %s", vtos( ent->s.origin ) );
217 	}
218 
219 	vis_dummy = G_Find( NULL, FOFS( targetname ), ent->target );
220 
221 	if ( vis_dummy ) {
222 		ent->s.otherEntityNum = vis_dummy->s.number;
223 	} else {
224 		G_Error( "target_speaker cant find vis_dummy_multiple %s", vtos( ent->s.origin ) );
225 	}
226 
227 }
228 
SP_target_speaker(gentity_t * ent)229 void SP_target_speaker( gentity_t *ent ) {
230 	char buffer[MAX_QPATH];
231 	char    *s;
232 
233 	G_SpawnFloat( "wait", "0", &ent->wait );
234 	G_SpawnFloat( "random", "0", &ent->random );
235 
236 	if ( !G_SpawnString( "noise", "NOSOUND", &s ) ) {
237 		G_Error( "target_speaker without a noise key at %s", vtos( ent->s.origin ) );
238 	}
239 
240 	// force all client relative sounds to be "activator" speakers that
241 	// play on the entity that activates it
242 	if ( s[0] == '*' ) {
243 		ent->spawnflags |= 8;
244 	}
245 
246 	// Ridah, had to disable this so we can use sound scripts
247 	// don't worry, if the script isn't found, it'll default back to
248 	// .wav on the client-side
249 	//if (!strstr( s, ".wav" )) {
250 	//	Com_sprintf (buffer, sizeof(buffer), "%s.wav", s );
251 	//} else {
252 	Q_strncpyz( buffer, s, sizeof( buffer ) );
253 	//}
254 	ent->noise_index = G_SoundIndex( buffer );
255 
256 	// a repeating speaker can be done completely client side
257 	ent->s.eType = ET_SPEAKER;
258 	ent->s.eventParm = ent->noise_index;
259 	ent->s.frame = ent->wait * 10;
260 	ent->s.clientNum = ent->random * 10;
261 
262 
263 	// check for prestarted looping sound
264 	if ( ent->spawnflags & 1 ) {
265 		ent->s.loopSound = ent->noise_index;
266 	}
267 
268 	ent->use = Use_Target_Speaker;
269 
270 	// GLOBAL
271 	if ( ent->spawnflags & ( 4 | 32 ) ) {
272 		ent->r.svFlags |= SVF_BROADCAST;
273 	}
274 
275 	VectorCopy( ent->s.origin, ent->s.pos.trBase );
276 
277 	if ( ent->spawnflags & 16 ) {
278 		ent->think = target_speaker_multiple;
279 		ent->nextthink = level.time + 50;
280 	}
281 
282 	// NO_PVS
283 	if ( ent->spawnflags & 32 ) {
284 		ent->s.density = 1;
285 	} else {
286 		ent->s.density = 0;
287 	}
288 
289 	if ( ent->radius ) {
290 		ent->s.dmgFlags = ent->radius;  // store radius in dmgflags
291 	} else {
292 		ent->s.dmgFlags = 0;
293 	}
294 
295 
296 	// must link the entity so we get areas and clusters so
297 	// the server can determine who to send updates to
298 	trap_LinkEntity( ent );
299 }
300 
301 
302 
303 //==========================================================
304 
305 /*QUAKED target_laser (0 .5 .8) (-8 -8 -8) (8 8 8) START_ON
306 When triggered, fires a laser.  You can either set a target or a direction.
307 */
target_laser_think(gentity_t * self)308 void target_laser_think( gentity_t *self ) {
309 	vec3_t end;
310 	trace_t tr;
311 	vec3_t point;
312 
313 	// if pointed at another entity, set movedir to point at it
314 	if ( self->enemy ) {
315 		VectorMA( self->enemy->s.origin, 0.5, self->enemy->r.mins, point );
316 		VectorMA( point, 0.5, self->enemy->r.maxs, point );
317 		VectorSubtract( point, self->s.origin, self->movedir );
318 		VectorNormalize( self->movedir );
319 	}
320 
321 	// fire forward and see what we hit
322 	VectorMA( self->s.origin, 2048, self->movedir, end );
323 
324 	trap_Trace( &tr, self->s.origin, NULL, NULL, end, self->s.number, CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_CORPSE );
325 
326 	if ( tr.entityNum ) {
327 		// hurt it if we can
328 		G_Damage( &g_entities[tr.entityNum], self, self->activator, self->movedir,
329 				  tr.endpos, self->damage, DAMAGE_NO_KNOCKBACK, MOD_TARGET_LASER );
330 	}
331 
332 	VectorCopy( tr.endpos, self->s.origin2 );
333 
334 	trap_LinkEntity( self );
335 	self->nextthink = level.time + FRAMETIME;
336 }
337 
target_laser_on(gentity_t * self)338 void target_laser_on( gentity_t *self ) {
339 	if ( !self->activator ) {
340 		self->activator = self;
341 	}
342 	target_laser_think( self );
343 }
344 
target_laser_off(gentity_t * self)345 void target_laser_off( gentity_t *self ) {
346 	trap_UnlinkEntity( self );
347 	self->nextthink = 0;
348 }
349 
target_laser_use(gentity_t * self,gentity_t * other,gentity_t * activator)350 void target_laser_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
351 	self->activator = activator;
352 	if ( self->nextthink > 0 ) {
353 		target_laser_off( self );
354 	} else {
355 		target_laser_on( self );
356 	}
357 }
358 
target_laser_start(gentity_t * self)359 void target_laser_start( gentity_t *self ) {
360 	gentity_t *ent;
361 
362 	self->s.eType = ET_BEAM;
363 
364 	if ( self->target ) {
365 		ent = G_Find( NULL, FOFS( targetname ), self->target );
366 		if ( !ent ) {
367 			G_Printf( "%s at %s: %s is a bad target\n", self->classname, vtos( self->s.origin ), self->target );
368 		}
369 		self->enemy = ent;
370 	} else {
371 		G_SetMovedir( self->s.angles, self->movedir );
372 	}
373 
374 	self->use = target_laser_use;
375 	self->think = target_laser_think;
376 
377 	if ( !self->damage ) {
378 		self->damage = 1;
379 	}
380 
381 	if ( self->spawnflags & 1 ) {
382 		target_laser_on( self );
383 	} else {
384 		target_laser_off( self );
385 	}
386 }
387 
SP_target_laser(gentity_t * self)388 void SP_target_laser( gentity_t *self ) {
389 	// let everything else get spawned before we start firing
390 	self->think = target_laser_start;
391 	self->nextthink = level.time + FRAMETIME;
392 }
393 
394 
395 //==========================================================
396 
target_teleporter_use(gentity_t * self,gentity_t * other,gentity_t * activator)397 void target_teleporter_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
398 	gentity_t   *dest;
399 
400 	if ( !activator->client ) {
401 		return;
402 	}
403 	dest =  G_PickTarget( self->target );
404 	if ( !dest ) {
405 		G_Printf( "Couldn't find teleporter destination\n" );
406 		return;
407 	}
408 
409 	TeleportPlayer( activator, dest->s.origin, dest->s.angles );
410 }
411 
412 /*QUAKED target_teleporter (1 0 0) (-8 -8 -8) (8 8 8)
413 The activator will be teleported away.
414 */
SP_target_teleporter(gentity_t * self)415 void SP_target_teleporter( gentity_t *self ) {
416 	if ( !self->targetname ) {
417 		G_Printf( "untargeted %s at %s\n", self->classname, vtos( self->s.origin ) );
418 	}
419 
420 	self->use = target_teleporter_use;
421 }
422 
423 //==========================================================
424 
425 
426 /*QUAKED target_relay (1 1 0) (-8 -8 -8) (8 8 8) RED_ONLY BLUE_ONLY RANDOM NOKEY_ONLY TAKE_KEY NO_LOCKED_NOISE
427 This doesn't perform any actions except fire its targets.
428 The activator can be forced to be from a certain team.
429 if RANDOM is checked, only one of the targets will be fired, not all of them
430 "key" specifies an item you can be carrying that affects the operation of this relay
431 this key is currently an int (1-16) which matches the id of a key entity (key_key1 = 1, etc)
432 NOKEY_ONLY means "fire only if I do /not/ have the specified key"
433 TAKE_KEY removes the key from the players inventory
434 "lockednoise" specifies a .wav file to play if the relay is used and the player doesn't have the necessary key.
435 By default this sound is "sound/movers/doors/default_door_locked.wav"
436 NO_LOCKED_NOISE specifies that it will be silent if activated without proper key
437 */
target_relay_use(gentity_t * self,gentity_t * other,gentity_t * activator)438 void target_relay_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
439 	if ( ( self->spawnflags & 1 ) && activator && activator->client
440 		 && activator->client->sess.sessionTeam != TEAM_RED ) {
441 		return;
442 	}
443 	if ( ( self->spawnflags & 2 ) && activator && activator->client
444 		 && activator->client->sess.sessionTeam != TEAM_BLUE ) {
445 		return;
446 	}
447 
448 	if ( self->spawnflags & 4 ) {
449 		gentity_t   *ent;
450 
451 		ent = G_PickTarget( self->target );
452 		if ( ent && ent->use ) {
453 			ent->use( ent, self, activator );
454 		}
455 		return;
456 	}
457 
458 	if ( activator ) { // activator can be NULL if called from script
459 		if ( self->key ) {
460 			gitem_t *item;
461 
462 			if ( self->key == -1 ) { // relay permanently locked
463 				if ( self->soundPos1 ) {
464 					G_Sound( self, self->soundPos1 );    //----(SA)	added
465 				}
466 				return;
467 			}
468 
469 			item = BG_FindItemForKey( self->key, 0 );
470 
471 			if ( item ) {
472 				if ( activator->client->ps.stats[STAT_KEYS] & ( 1 << item->giTag ) ) { // user has key
473 					if ( self->spawnflags & 8 ) {    // relay is NOKEY_ONLY and player has key
474 						if ( self->soundPos1 ) {
475 							G_Sound( self, self->soundPos1 );    //----(SA)	added
476 						}
477 						return;
478 					}
479 				} else                            // user does not have key
480 				{
481 					if ( !( self->spawnflags & 8 ) ) {
482 						if ( self->soundPos1 ) {
483 							G_Sound( self, self->soundPos1 );    //----(SA)	added
484 						}
485 						return;
486 					}
487 				}
488 			}
489 
490 			if ( item && ( self->spawnflags & 16 ) ) { // (SA) take key
491 				activator->client->ps.stats[STAT_KEYS] &= ~( 1 << item->giTag );
492 				// (SA) TODO: "took inventory item" sound
493 			}
494 		}
495 	}
496 
497 	G_UseTargets( self, activator );
498 }
499 
500 
relay_AIScript_AlertEntity(gentity_t * self)501 void relay_AIScript_AlertEntity( gentity_t *self ) {
502 	self->use( self, NULL, NULL );
503 }
504 
505 
506 /*
507 ==============
508 SP_target_relay
509 ==============
510 */
SP_target_relay(gentity_t * self)511 void SP_target_relay( gentity_t *self ) {
512 	char        *sound;
513 
514 	self->use = target_relay_use;
515 	self->AIScript_AlertEntity = relay_AIScript_AlertEntity;
516 
517 	if ( !( self->spawnflags & 32 ) ) {  // !NO_LOCKED_NOISE
518 		if ( G_SpawnString( "lockednoise", "0", &sound ) ) {
519 			self->soundPos1 = G_SoundIndex( sound );
520 		} else {
521 			self->soundPos1 = G_SoundIndex( "sound/movers/doors/default_door_locked.wav" );
522 		}
523 	}
524 
525 }
526 
527 
528 //==========================================================
529 
530 /*QUAKED target_kill (.5 .5 .5) (-8 -8 -8) (8 8 8) kill_user_too
531 Kills the activator. (default)
532 If targets, they will be killed when this is fired
533 "kill_user_too" will still kill the activator when this ent has targets (default is only kill targets, not activator)
534 */
target_kill_use(gentity_t * self,gentity_t * other,gentity_t * activator)535 void target_kill_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
536 	gentity_t *targ = NULL;
537 
538 	if ( self->spawnflags & 1 ) {  // kill usertoo
539 		G_Damage( activator, NULL, NULL, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG );
540 	}
541 
542 	while ( ( targ = G_Find( targ, FOFS( targetname ), self->target ) ) != NULL ) {
543 		if ( targ->aiCharacter ) {       // (SA) if it's an ai character, free it nicely
544 			targ->aiInactive = qtrue;
545 		} else
546 		{
547 			// make sure it isn't going to respawn or show any events
548 			targ->nextthink = 0;
549 			if ( targ == activator ) {
550 				continue;
551 			}
552 
553 			// RF, script_movers should die!
554 			if ( !Q_stricmp( targ->classname, "script_mover" ) && targ->die ) {
555 				targ->die( targ, self, self, targ->health, 0 );
556 				continue;
557 			}
558 
559 			trap_UnlinkEntity( targ );
560 			targ->use = 0;
561 			targ->touch = 0;
562 			targ->nextthink = level.time + FRAMETIME;
563 			targ->think = G_FreeEntity;
564 		}
565 	}
566 }
567 
SP_target_kill(gentity_t * self)568 void SP_target_kill( gentity_t *self ) {
569 	self->use = target_kill_use;
570 }
571 
572 /*DEFUNCT target_position (0 0.5 0) (-4 -4 -4) (4 4 4)
573 Used as a positional target for in-game calculation, like jumppad targets.
574 */
SP_target_position(gentity_t * self)575 void SP_target_position( gentity_t *self ) {
576 	G_SetOrigin( self, self->s.origin );
577 }
578 
579 // Ridah, note to everyone: static functions can cause problems with the savegame code, so avoid
580 // using them unless necessary. if that function is then assigned to an entity, client or cast field,
581 // the game will not save.
582 //static void target_location_linkup(gentity_t *ent)
target_location_linkup(gentity_t * ent)583 void target_location_linkup( gentity_t *ent ) {
584 	int i;
585 	int n;
586 
587 	if ( level.locationLinked ) {
588 		return;
589 	}
590 
591 	level.locationLinked = qtrue;
592 
593 	level.locationHead = NULL;
594 
595 	trap_SetConfigstring( CS_LOCATIONS, "unknown" );
596 
597 	for ( i = 0, ent = g_entities, n = 1;
598 		  i < level.num_entities;
599 		  i++, ent++ ) {
600 		if ( ent->classname && !Q_stricmp( ent->classname, "target_location" ) ) {
601 			// lets overload some variables!
602 			ent->health = n; // use for location marking
603 			trap_SetConfigstring( CS_LOCATIONS + n, ent->message );
604 			n++;
605 			ent->nextTrain = level.locationHead;
606 			level.locationHead = ent;
607 		}
608 	}
609 
610 	// All linked together now
611 }
612 
613 /*QUAKED target_location (0 0.5 0) (-8 -8 -8) (8 8 8)
614 Set "message" to the name of this location.
615 Set "count" to 0-7 for color.
616 0:white 1:red 2:green 3:yellow 4:blue 5:cyan 6:magenta 7:white
617 
618 Closest target_location in sight used for the location, if none
619 in site, closest in distance
620 */
SP_target_location(gentity_t * self)621 void SP_target_location( gentity_t *self ) {
622 	self->think = target_location_linkup;
623 	self->nextthink = level.time + 200;  // Let them all spawn first
624 
625 	G_SetOrigin( self, self->s.origin );
626 }
627 
628 
629 
630 
631 
632 
633 
634 //---- (SA) Wolf targets
635 
636 /*
637 ==============
638 Use_Target_Autosave
639 	save game for emergency backup or convienience
640 ==============
641 */
642 /*void Use_Target_Autosave( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
643 	G_SaveGame("autosave.svg");
644 }*/
645 
646 
647 
648 /*
649 ==============
650 Use_Target_Counter
651 ==============
652 */
Use_Target_Counter(gentity_t * ent,gentity_t * other,gentity_t * activator)653 void Use_Target_Counter( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
654 	if ( ent->count < 0 ) { // if the count has already been hit, ignore this
655 		return;
656 	}
657 
658 	ent->count -= 1;    // dec count
659 
660 //	G_Printf("count at: %d\n", ent->count);
661 
662 	if ( !ent->count ) {   // specified count is now hit
663 //		G_Printf("firing!!\n");
664 		G_UseTargets( ent, other );
665 	}
666 }
667 
668 /*
669 ==============
670 Use_Target_Lock
671 ==============
672 */
Use_Target_Lock(gentity_t * ent,gentity_t * other,gentity_t * activator)673 void Use_Target_Lock( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
674 	gentity_t   *t = 0;
675 
676 	while ( ( t = G_Find( t, FOFS( targetname ), ent->target ) ) != NULL )
677 	{
678 //		G_Printf("target_lock locking entity with key: %d\n", ent->count);
679 		t->key = ent->key;
680 		G_SetAASBlockingEntity( t, t->key != 0 );
681 	}
682 
683 }
684 
685 //==========================================================
686 
687 /*
688 ==============
689 Use_target_fog
690 ==============
691 */
Use_target_fog(gentity_t * ent,gentity_t * other,gentity_t * activator)692 void Use_target_fog( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
693 //	CS_FOGVARS reads:
694 //		near
695 //		far
696 //		density
697 //		r,g,b
698 //		time to complete
699 	trap_SetConfigstring( CS_FOGVARS, va( "%f %f %f %f %f %f %i", 1.0f, (float)ent->s.density, 1.0f, (float)ent->dl_color[0], (float)ent->dl_color[1], (float)ent->dl_color[2], ent->s.time ) );
700 }
701 
702 /*QUAKED target_fog (1 1 0) (-8 -8 -8) (8 8 8)
703 color picker chooses color of fog
704 "distance" sets fog distance.  Use value '0' to give control back to the game (and use the fog values specified in the sky shader if present)
705 "time" time it takes to change fog to new value.  default time is 1 sec
706 */
SP_target_fog(gentity_t * ent)707 void SP_target_fog( gentity_t *ent ) {
708 	int dist;
709 	float ftime;
710 
711 	ent->use = Use_target_fog;
712 
713 	// ent->s.density will carry the 'distance' value
714 	if ( G_SpawnInt( "distance", "0", &dist ) ) {
715 		if ( dist >= 0 ) {
716 			ent->s.density = dist;
717 		}
718 	}
719 
720 	// ent->s.time will carry the 'time' value
721 	if ( G_SpawnFloat( "time", "0.5", &ftime ) ) {
722 		if ( ftime >= 0 ) {
723 			ent->s.time = ftime * 1000; // sec to ms
724 		}
725 	}
726 }
727 
728 //==========================================================
729 
730 /*QUAKED target_counter (1 1 0) (-8 -8 -8) (8 8 8)
731 Increments the counter pointed to.
732 "count" is the key for the count value
733 */
SP_target_counter(gentity_t * ent)734 void SP_target_counter( gentity_t *ent ) {
735 //	G_Printf("target counter created with val of: %d\n", ent->count);
736 	ent->use = Use_Target_Counter;
737 }
738 
739 
740 
741 /*QUAKED target_autosave (1 1 0) (-8 -8 -8) (8 8 8)
742 saves game to 'autosave.svg' when triggered then dies.
743 */
SP_target_autosave(gentity_t * ent)744 void SP_target_autosave( gentity_t *ent ) {
745 	//ent->use = Use_Target_Autosave;
746 	G_FreeEntity( ent );
747 }
748 
749 //==========================================================
750 
751 /*QUAKED target_lock (1 1 0) (-8 -8 -8) (8 8 8)
752 Sets the door to a state requiring key n
753 "key" is the required key
754 so:
755 key:0  unlocks the door
756 key:-1 locks the door until a target_lock with key:0
757 key:n  means the door now requires key n
758 */
SP_target_lock(gentity_t * ent)759 void SP_target_lock( gentity_t *ent ) {
760 	ent->use = Use_Target_Lock;
761 }
762 
763 
764 
Use_Target_Alarm(gentity_t * ent,gentity_t * other,gentity_t * activator)765 void Use_Target_Alarm( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
766 	G_UseTargets( ent, other );
767 }
768 
769 /*QUAKED target_alarm (1 1 0) (-4 -4 -4) (4 4 4)
770 does nothing yet (effectively a relay right now)
771 */
SP_target_alarm(gentity_t * ent)772 void SP_target_alarm( gentity_t *ent ) {
773 	ent->use = Use_Target_Alarm;
774 }
775 
776 //---- end
777 
778 /*QUAKED target_smoke (1 0 0) (-32 -32 -16) (32 32 16) Black White SmokeON Gravity
779 1 second = 1000
780 1 FRAME = 100
781 delay = 100 = one millisecond default this is the maximum smoke that will show up
782 time = 5000 default before the smoke disipates
783 duration = 2000 before the smoke starts to alpha
784 start_size = 24 default
785 end_size = 96 default
786 wait	= default is 50 the rate at which it will travel up
787 */
788 
789 /*void smoke_think (gentity_t *ent)
790 {
791 	gentity_t	*tent;
792 
793 	ent->nextthink = level.time + ent->delay;
794 
795 	if (!(ent->spawnflags & 4))
796 		return;
797 
798 	if (ent->health)
799 	{
800 		ent->health --;
801 		if (!ent->health)
802 		{
803 			ent->think = G_FreeEntity;
804 			ent->nextthink = level.time + FRAMETIME;
805 		}
806 	}
807 
808 	tent = G_TempEntity (ent->r.currentOrigin, EV_SMOKE);
809 	VectorCopy (ent->r.currentOrigin, tent->s.origin);
810 	tent->s.time = ent->speed;
811 	tent->s.time2 = ent->duration;
812 	tent->s.density = ent->s.density;
813 
814 	// this is used to set the size of the smoke particle
815 	tent->s.angles2[0] = ent->start_size;
816 	tent->s.angles2[1] = ent->end_size;
817 	tent->s.angles2[2] = ent->wait;
818 
819 	VectorCopy (ent->pos3, tent->s.origin2);
820 
821 	if (ent->s.frame) // denotes reverse gravity effect
822 		tent->s.frame = 1;
823 
824 }*/
825 
smoke_think(gentity_t * ent)826 void smoke_think( gentity_t *ent ) {
827 	ent->nextthink = level.time + ent->s.constantLight;
828 
829 	if ( !( ent->spawnflags & 4 ) ) {
830 		return;
831 	}
832 
833 	if ( ent->s.dl_intensity ) {
834 		ent->s.dl_intensity--;
835 		if ( !ent->s.dl_intensity ) {
836 			ent->think = G_FreeEntity;
837 			ent->nextthink = level.time + FRAMETIME;
838 		}
839 	}
840 }
841 
smoke_toggle(gentity_t * ent,gentity_t * self,gentity_t * activator)842 void smoke_toggle( gentity_t *ent, gentity_t *self, gentity_t *activator ) {
843 	if ( ent->spawnflags & 4 ) { // smoke is on turn it off
844 		ent->spawnflags &= ~4;
845 		trap_UnlinkEntity( ent );
846 	} else
847 	{
848 		ent->spawnflags |= 4;
849 		trap_LinkEntity( ent );
850 	}
851 }
852 
smoke_init(gentity_t * ent)853 void smoke_init( gentity_t *ent ) {
854 	gentity_t *target;
855 	vec3_t vec;
856 
857 	ent->think = smoke_think;
858 	ent->nextthink = level.time + FRAMETIME;
859 
860 	if ( ent->target ) {
861 		target = G_Find( NULL, FOFS( targetname ), ent->target );
862 		if ( target ) {
863 			VectorSubtract( target->s.origin, ent->s.origin, vec );
864 			VectorCopy( vec, ent->pos3 );
865 		} else {
866 			VectorSet( ent->s.origin2, 0, 0, 1 );
867 		}
868 	} else
869 	{
870 		VectorSet( ent->s.origin2, 0, 0, 1 );
871 	}
872 
873 	if ( ent->spawnflags & 4 ) {
874 		trap_LinkEntity( ent );
875 	}
876 }
877 
SP_target_smoke(gentity_t * ent)878 void SP_target_smoke( gentity_t *ent ) {
879 
880 	// (SA) don't use in multiplayer right now since it makes decyphering net messages almost impossible
881 //	ent->think = G_FreeEntity;
882 //	return;
883 	// Arnout - modified this a lot to be sent to the client as one entity and then is shown at the client
884 
885 	if ( !ent->delay ) {
886 		ent->delay = 100;
887 	}
888 
889 	ent->use = smoke_toggle;
890 
891 	ent->think = smoke_init;
892 	ent->nextthink = level.time + FRAMETIME;
893 
894 	G_SetOrigin( ent, ent->s.origin );
895 	ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
896 	ent->s.eType = ET_SMOKER;
897 
898 	if ( ent->spawnflags & 2 ) {
899 		ent->s.density = 4;
900 	} else {
901 		ent->s.density = 0;
902 	}
903 
904 	// using "time"
905 	ent->s.time = ent->speed;
906 	if ( !ent->s.time ) {
907 		ent->s.time = 5000; // 5 seconds
908 
909 	}
910 	ent->s.time2 = ent->duration;
911 	if ( !ent->s.time2 ) {
912 		ent->s.time2 = 2000;
913 	}
914 
915 	ent->s.angles2[0] = ent->start_size;
916 	if ( !ent->s.angles2[0] ) {
917 		ent->s.angles2[0] = 24;
918 	}
919 
920 	ent->s.angles2[1] = ent->end_size;
921 	if ( !ent->s.angles2[1] ) {
922 		ent->s.angles2[1] = 96;
923 	}
924 
925 	ent->s.angles2[2] = ent->wait;
926 	if ( !ent->s.angles2[2] ) {
927 		ent->s.angles2[2] = 50;
928 	}
929 
930 	// idiot check
931 	if ( ent->s.time < ent->s.time2 ) {
932 		ent->s.time = ent->s.time2 + 100;
933 	}
934 
935 	if ( ent->spawnflags & 8 ) {
936 		ent->s.frame = 1;
937 	}
938 
939 	ent->s.dl_intensity = ent->health;
940 	ent->s.constantLight = ent->delay;
941 
942 	if ( ent->spawnflags & 4 ) {
943 		trap_LinkEntity( ent );
944 	}
945 
946 }
947 
948 
949 /*QUAKED target_script_trigger (1 .7 .2) (-8 -8 -8) (8 8 8)
950 must have an aiName
951 must have a target
952 
953 when used it will fire its targets
954 */
target_script_trigger_use(gentity_t * ent,gentity_t * other,gentity_t * activator)955 void target_script_trigger_use( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
956 	gentity_t   *player;
957 
958 	if ( ent->aiName ) {
959 		player = AICast_FindEntityForName( "player" );
960 		if ( player ) {
961 			AICast_ScriptEvent( AICast_GetCastState( player->s.number ), "trigger", ent->target );
962 		}
963 	}
964 
965 	// DHM - Nerve :: In multiplayer, we use the brush scripting only
966 	if ( g_gametype.integer >= GT_WOLF && ent->scriptName ) {
967 		G_Script_ScriptEvent( ent, "trigger", ent->target );
968 	}
969 
970 	G_UseTargets( ent, other );
971 
972 }
973 
SP_target_script_trigger(gentity_t * ent)974 void SP_target_script_trigger( gentity_t *ent ) {
975 	G_SetOrigin( ent, ent->s.origin );
976 	ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
977 	ent->s.eType = ET_GENERAL;
978 	ent->use = target_script_trigger_use;
979 }
980 
981 
982 /*QUAKED target_rumble (0 0.75 0.8) (-8 -8 -8) (8 8 8) STARTOFF
983 wait = default is 2 seconds = time the entity will enable rumble effect
984 "pitch" value from 1 to 10 default is 5
985 "yaw"   value from 1 to 10 default is 5
986 
987 "rampup" how much time it will take to reach maximum pitch and yaw in seconds
988 "rampdown" how long till effect ends after rampup is reached in seconds
989 
990 "startnoise" startingsound
991 "noise"  the looping sound entity is to make
992 "endnoise" endsound
993 
994 "duration" the amount of time the effect is to last ei 1.0 sec 3.6 sec
995 */
996 int rumble_snd;
997 
target_rumble_think(gentity_t * ent)998 void target_rumble_think( gentity_t * ent ) {
999 	gentity_t   *tent;
1000 	float ratio;
1001 	float time, time2;
1002 	float dapitch, dayaw;
1003 	qboolean validrumble = qtrue;
1004 
1005 	if ( !( ent->count ) ) {
1006 		ent->timestamp = level.time;
1007 		ent->count++;
1008 		// start sound here
1009 		if ( ent->soundPos1 ) {
1010 			G_AddEvent( ent, EV_GENERAL_SOUND, ent->soundPos1 );
1011 		}
1012 	} else
1013 	{
1014 		// looping sound
1015 		ent->s.loopSound = ent->soundLoop;
1016 	}
1017 
1018 	dapitch = ent->delay;
1019 	dayaw = ent->random;
1020 	ratio = 1.0f;
1021 
1022 	if ( ent->start_size ) {
1023 		if ( level.time < ( ent->timestamp + ent->start_size ) ) {
1024 			time = level.time - ent->timestamp;
1025 			time2 = ( ent->timestamp + ent->start_size ) - ent->timestamp;
1026 			ratio = time / time2;
1027 		} else if ( level.time < ( ent->timestamp + ent->end_size + ent->start_size ) )       {
1028 			time = level.time - ent->timestamp;
1029 			time2 = ( ent->timestamp + ent->start_size + ent->end_size ) - ent->timestamp;
1030 			ratio = time2 / time;
1031 		} else {
1032 			validrumble = qfalse;
1033 		}
1034 	}
1035 
1036 	if ( validrumble ) {
1037 		tent = G_TempEntity( ent->r.currentOrigin, EV_RUMBLE_EFX );
1038 
1039 		tent->s.angles[0] = dapitch * ratio;
1040 		tent->s.angles[1] = dayaw * ratio;
1041 	}
1042 
1043 	// end sound
1044 	if ( level.time > ent->duration + ent->timestamp ) {
1045 		if ( ent->soundPos2 ) {
1046 			G_AddEvent( ent, EV_GENERAL_SOUND, ent->soundPos2 );
1047 			ent->s.loopSound = 0;
1048 		}
1049 
1050 		ent->nextthink = 0;
1051 	} else {
1052 		ent->nextthink = level.time + 50;
1053 	}
1054 
1055 }
1056 
target_rumble_use(gentity_t * ent,gentity_t * other,gentity_t * activator)1057 void target_rumble_use( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
1058 	if ( ent->spawnflags & 1 ) {
1059 		ent->spawnflags &= ~1;
1060 		ent->think = target_rumble_think;
1061 		ent->count = 0;
1062 		ent->nextthink = level.time + 50;
1063 	} else
1064 	{
1065 		ent->spawnflags |= 1;
1066 		ent->think = 0;
1067 		ent->count = 0;
1068 	}
1069 }
1070 
SP_target_rumble(gentity_t * self)1071 void SP_target_rumble( gentity_t *self ) {
1072 	char        *pitch;
1073 	char        *yaw;
1074 	char        *rampup;
1075 	char        *rampdown;
1076 	float dapitch;
1077 	float dayaw;
1078 	char        *sound;
1079 	char        *startsound;
1080 	char        *endsound;
1081 
1082 	if ( G_SpawnString( "noise", "100", &sound ) ) {
1083 		self->soundLoop = G_SoundIndex( sound );
1084 	}
1085 
1086 	if ( G_SpawnString( "startnoise", "100", &startsound ) ) {
1087 		self->soundPos1 = G_SoundIndex( startsound );
1088 	}
1089 
1090 	if ( G_SpawnString( "endnoise", "100", &endsound ) ) {
1091 		self->soundPos2 = G_SoundIndex( endsound );
1092 	}
1093 
1094 	self->use = target_rumble_use;
1095 
1096 	G_SpawnString( "pitch", "0", &pitch );
1097 	dapitch = atof( pitch );
1098 	self->delay = dapitch;
1099 	if ( !( self->delay ) ) {
1100 		self->delay = 5;
1101 	}
1102 
1103 	G_SpawnString( "yaw", "0", &yaw );
1104 	dayaw = atof( yaw );
1105 	self->random = dayaw;
1106 	if ( !( self->random ) ) {
1107 		self->random = 5;
1108 	}
1109 
1110 	G_SpawnString( "rampup", "0", &rampup );
1111 	self->start_size = atoi( rampup ) * 1000;
1112 	if ( !( self->start_size ) ) {
1113 		self->start_size = 1000;
1114 	}
1115 
1116 	G_SpawnString( "rampdown", "0", &rampdown );
1117 	self->end_size = atoi( rampdown ) * 1000;
1118 	if ( !( self->end_size ) ) {
1119 		self->end_size = 1000;
1120 	}
1121 
1122 	if ( !( self->duration ) ) {
1123 		self->duration = 1000;
1124 	} else {
1125 		self->duration *= 1000;
1126 	}
1127 
1128 	trap_LinkEntity( self );
1129 }
1130