Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

g_target.c (12698B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 //
     23 #include "g_local.h"
     24 
     25 //==========================================================
     26 
     27 /*QUAKED target_give (1 0 0) (-8 -8 -8) (8 8 8)
     28 Gives the activator all the items pointed to.
     29 */
     30 void Use_Target_Give( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
     31 	gentity_t	*t;
     32 	trace_t		trace;
     33 
     34 	if ( !activator->client ) {
     35 		return;
     36 	}
     37 
     38 	if ( !ent->target ) {
     39 		return;
     40 	}
     41 
     42 	memset( &trace, 0, sizeof( trace ) );
     43 	t = NULL;
     44 	while ( (t = G_Find (t, FOFS(targetname), ent->target)) != NULL ) {
     45 		if ( !t->item ) {
     46 			continue;
     47 		}
     48 		Touch_Item( t, activator, &trace );
     49 
     50 		// make sure it isn't going to respawn or show any events
     51 		t->nextthink = 0;
     52 		trap_UnlinkEntity( t );
     53 	}
     54 }
     55 
     56 void SP_target_give( gentity_t *ent ) {
     57 	ent->use = Use_Target_Give;
     58 }
     59 
     60 
     61 //==========================================================
     62 
     63 /*QUAKED target_remove_powerups (1 0 0) (-8 -8 -8) (8 8 8)
     64 takes away all the activators powerups.
     65 Used to drop flight powerups into death puts.
     66 */
     67 void Use_target_remove_powerups( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
     68 	if( !activator->client ) {
     69 		return;
     70 	}
     71 
     72 	if( activator->client->ps.powerups[PW_REDFLAG] ) {
     73 		Team_ReturnFlag( TEAM_RED );
     74 	} else if( activator->client->ps.powerups[PW_BLUEFLAG] ) {
     75 		Team_ReturnFlag( TEAM_BLUE );
     76 	} else if( activator->client->ps.powerups[PW_NEUTRALFLAG] ) {
     77 		Team_ReturnFlag( TEAM_FREE );
     78 	}
     79 
     80 	memset( activator->client->ps.powerups, 0, sizeof( activator->client->ps.powerups ) );
     81 }
     82 
     83 void SP_target_remove_powerups( gentity_t *ent ) {
     84 	ent->use = Use_target_remove_powerups;
     85 }
     86 
     87 
     88 //==========================================================
     89 
     90 /*QUAKED target_delay (1 0 0) (-8 -8 -8) (8 8 8)
     91 "wait" seconds to pause before firing targets.
     92 "random" delay variance, total delay = delay +/- random seconds
     93 */
     94 void Think_Target_Delay( gentity_t *ent ) {
     95 	G_UseTargets( ent, ent->activator );
     96 }
     97 
     98 void Use_Target_Delay( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
     99 	ent->nextthink = level.time + ( ent->wait + ent->random * crandom() ) * 1000;
    100 	ent->think = Think_Target_Delay;
    101 	ent->activator = activator;
    102 }
    103 
    104 void SP_target_delay( gentity_t *ent ) {
    105 	// check delay for backwards compatability
    106 	if ( !G_SpawnFloat( "delay", "0", &ent->wait ) ) {
    107 		G_SpawnFloat( "wait", "1", &ent->wait );
    108 	}
    109 
    110 	if ( !ent->wait ) {
    111 		ent->wait = 1;
    112 	}
    113 	ent->use = Use_Target_Delay;
    114 }
    115 
    116 
    117 //==========================================================
    118 
    119 /*QUAKED target_score (1 0 0) (-8 -8 -8) (8 8 8)
    120 "count" number of points to add, default 1
    121 
    122 The activator is given this many points.
    123 */
    124 void Use_Target_Score (gentity_t *ent, gentity_t *other, gentity_t *activator) {
    125 	AddScore( activator, ent->r.currentOrigin, ent->count );
    126 }
    127 
    128 void SP_target_score( gentity_t *ent ) {
    129 	if ( !ent->count ) {
    130 		ent->count = 1;
    131 	}
    132 	ent->use = Use_Target_Score;
    133 }
    134 
    135 
    136 //==========================================================
    137 
    138 /*QUAKED target_print (1 0 0) (-8 -8 -8) (8 8 8) redteam blueteam private
    139 "message"	text to print
    140 If "private", only the activator gets the message.  If no checks, all clients get the message.
    141 */
    142 void Use_Target_Print (gentity_t *ent, gentity_t *other, gentity_t *activator) {
    143 	if ( activator->client && ( ent->spawnflags & 4 ) ) {
    144 		trap_SendServerCommand( activator-g_entities, va("cp \"%s\"", ent->message ));
    145 		return;
    146 	}
    147 
    148 	if ( ent->spawnflags & 3 ) {
    149 		if ( ent->spawnflags & 1 ) {
    150 			G_TeamCommand( TEAM_RED, va("cp \"%s\"", ent->message) );
    151 		}
    152 		if ( ent->spawnflags & 2 ) {
    153 			G_TeamCommand( TEAM_BLUE, va("cp \"%s\"", ent->message) );
    154 		}
    155 		return;
    156 	}
    157 
    158 	trap_SendServerCommand( -1, va("cp \"%s\"", ent->message ));
    159 }
    160 
    161 void SP_target_print( gentity_t *ent ) {
    162 	ent->use = Use_Target_Print;
    163 }
    164 
    165 
    166 //==========================================================
    167 
    168 
    169 /*QUAKED target_speaker (1 0 0) (-8 -8 -8) (8 8 8) looped-on looped-off global activator
    170 "noise"		wav file to play
    171 
    172 A global sound will play full volume throughout the level.
    173 Activator sounds will play on the player that activated the target.
    174 Global and activator sounds can't be combined with looping.
    175 Normal sounds play each time the target is used.
    176 Looped sounds will be toggled by use functions.
    177 Multiple identical looping sounds will just increase volume without any speed cost.
    178 "wait" : Seconds between auto triggerings, 0 = don't auto trigger
    179 "random"	wait variance, default is 0
    180 */
    181 void Use_Target_Speaker (gentity_t *ent, gentity_t *other, gentity_t *activator) {
    182 	if (ent->spawnflags & 3) {	// looping sound toggles
    183 		if (ent->s.loopSound)
    184 			ent->s.loopSound = 0;	// turn it off
    185 		else
    186 			ent->s.loopSound = ent->noise_index;	// start it
    187 	}else {	// normal sound
    188 		if ( ent->spawnflags & 8 ) {
    189 			G_AddEvent( activator, EV_GENERAL_SOUND, ent->noise_index );
    190 		} else if (ent->spawnflags & 4) {
    191 			G_AddEvent( ent, EV_GLOBAL_SOUND, ent->noise_index );
    192 		} else {
    193 			G_AddEvent( ent, EV_GENERAL_SOUND, ent->noise_index );
    194 		}
    195 	}
    196 }
    197 
    198 void SP_target_speaker( gentity_t *ent ) {
    199 	char	buffer[MAX_QPATH];
    200 	char	*s;
    201 
    202 	G_SpawnFloat( "wait", "0", &ent->wait );
    203 	G_SpawnFloat( "random", "0", &ent->random );
    204 
    205 	if ( !G_SpawnString( "noise", "NOSOUND", &s ) ) {
    206 		G_Error( "target_speaker without a noise key at %s", vtos( ent->s.origin ) );
    207 	}
    208 
    209 	// force all client reletive sounds to be "activator" speakers that
    210 	// play on the entity that activates it
    211 	if ( s[0] == '*' ) {
    212 		ent->spawnflags |= 8;
    213 	}
    214 
    215 	if (!strstr( s, ".wav" )) {
    216 		Com_sprintf (buffer, sizeof(buffer), "%s.wav", s );
    217 	} else {
    218 		Q_strncpyz( buffer, s, sizeof(buffer) );
    219 	}
    220 	ent->noise_index = G_SoundIndex(buffer);
    221 
    222 	// a repeating speaker can be done completely client side
    223 	ent->s.eType = ET_SPEAKER;
    224 	ent->s.eventParm = ent->noise_index;
    225 	ent->s.frame = ent->wait * 10;
    226 	ent->s.clientNum = ent->random * 10;
    227 
    228 
    229 	// check for prestarted looping sound
    230 	if ( ent->spawnflags & 1 ) {
    231 		ent->s.loopSound = ent->noise_index;
    232 	}
    233 
    234 	ent->use = Use_Target_Speaker;
    235 
    236 	if (ent->spawnflags & 4) {
    237 		ent->r.svFlags |= SVF_BROADCAST;
    238 	}
    239 
    240 	VectorCopy( ent->s.origin, ent->s.pos.trBase );
    241 
    242 	// must link the entity so we get areas and clusters so
    243 	// the server can determine who to send updates to
    244 	trap_LinkEntity( ent );
    245 }
    246 
    247 
    248 
    249 //==========================================================
    250 
    251 /*QUAKED target_laser (0 .5 .8) (-8 -8 -8) (8 8 8) START_ON
    252 When triggered, fires a laser.  You can either set a target or a direction.
    253 */
    254 void target_laser_think (gentity_t *self) {
    255 	vec3_t	end;
    256 	trace_t	tr;
    257 	vec3_t	point;
    258 
    259 	// if pointed at another entity, set movedir to point at it
    260 	if ( self->enemy ) {
    261 		VectorMA (self->enemy->s.origin, 0.5, self->enemy->r.mins, point);
    262 		VectorMA (point, 0.5, self->enemy->r.maxs, point);
    263 		VectorSubtract (point, self->s.origin, self->movedir);
    264 		VectorNormalize (self->movedir);
    265 	}
    266 
    267 	// fire forward and see what we hit
    268 	VectorMA (self->s.origin, 2048, self->movedir, end);
    269 
    270 	trap_Trace( &tr, self->s.origin, NULL, NULL, end, self->s.number, CONTENTS_SOLID|CONTENTS_BODY|CONTENTS_CORPSE);
    271 
    272 	if ( tr.entityNum ) {
    273 		// hurt it if we can
    274 		G_Damage ( &g_entities[tr.entityNum], self, self->activator, self->movedir, 
    275 			tr.endpos, self->damage, DAMAGE_NO_KNOCKBACK, MOD_TARGET_LASER);
    276 	}
    277 
    278 	VectorCopy (tr.endpos, self->s.origin2);
    279 
    280 	trap_LinkEntity( self );
    281 	self->nextthink = level.time + FRAMETIME;
    282 }
    283 
    284 void target_laser_on (gentity_t *self)
    285 {
    286 	if (!self->activator)
    287 		self->activator = self;
    288 	target_laser_think (self);
    289 }
    290 
    291 void target_laser_off (gentity_t *self)
    292 {
    293 	trap_UnlinkEntity( self );
    294 	self->nextthink = 0;
    295 }
    296 
    297 void target_laser_use (gentity_t *self, gentity_t *other, gentity_t *activator)
    298 {
    299 	self->activator = activator;
    300 	if ( self->nextthink > 0 )
    301 		target_laser_off (self);
    302 	else
    303 		target_laser_on (self);
    304 }
    305 
    306 void target_laser_start (gentity_t *self)
    307 {
    308 	gentity_t *ent;
    309 
    310 	self->s.eType = ET_BEAM;
    311 
    312 	if (self->target) {
    313 		ent = G_Find (NULL, FOFS(targetname), self->target);
    314 		if (!ent) {
    315 			G_Printf ("%s at %s: %s is a bad target\n", self->classname, vtos(self->s.origin), self->target);
    316 		}
    317 		self->enemy = ent;
    318 	} else {
    319 		G_SetMovedir (self->s.angles, self->movedir);
    320 	}
    321 
    322 	self->use = target_laser_use;
    323 	self->think = target_laser_think;
    324 
    325 	if ( !self->damage ) {
    326 		self->damage = 1;
    327 	}
    328 
    329 	if (self->spawnflags & 1)
    330 		target_laser_on (self);
    331 	else
    332 		target_laser_off (self);
    333 }
    334 
    335 void SP_target_laser (gentity_t *self)
    336 {
    337 	// let everything else get spawned before we start firing
    338 	self->think = target_laser_start;
    339 	self->nextthink = level.time + FRAMETIME;
    340 }
    341 
    342 
    343 //==========================================================
    344 
    345 void target_teleporter_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
    346 	gentity_t	*dest;
    347 
    348 	if (!activator->client)
    349 		return;
    350 	dest = 	G_PickTarget( self->target );
    351 	if (!dest) {
    352 		G_Printf ("Couldn't find teleporter destination\n");
    353 		return;
    354 	}
    355 
    356 	TeleportPlayer( activator, dest->s.origin, dest->s.angles );
    357 }
    358 
    359 /*QUAKED target_teleporter (1 0 0) (-8 -8 -8) (8 8 8)
    360 The activator will be teleported away.
    361 */
    362 void SP_target_teleporter( gentity_t *self ) {
    363 	if (!self->targetname)
    364 		G_Printf("untargeted %s at %s\n", self->classname, vtos(self->s.origin));
    365 
    366 	self->use = target_teleporter_use;
    367 }
    368 
    369 //==========================================================
    370 
    371 
    372 /*QUAKED target_relay (.5 .5 .5) (-8 -8 -8) (8 8 8) RED_ONLY BLUE_ONLY RANDOM
    373 This doesn't perform any actions except fire its targets.
    374 The activator can be forced to be from a certain team.
    375 if RANDOM is checked, only one of the targets will be fired, not all of them
    376 */
    377 void target_relay_use (gentity_t *self, gentity_t *other, gentity_t *activator) {
    378 	if ( ( self->spawnflags & 1 ) && activator->client 
    379 		&& activator->client->sess.sessionTeam != TEAM_RED ) {
    380 		return;
    381 	}
    382 	if ( ( self->spawnflags & 2 ) && activator->client 
    383 		&& activator->client->sess.sessionTeam != TEAM_BLUE ) {
    384 		return;
    385 	}
    386 	if ( self->spawnflags & 4 ) {
    387 		gentity_t	*ent;
    388 
    389 		ent = G_PickTarget( self->target );
    390 		if ( ent && ent->use ) {
    391 			ent->use( ent, self, activator );
    392 		}
    393 		return;
    394 	}
    395 	G_UseTargets (self, activator);
    396 }
    397 
    398 void SP_target_relay (gentity_t *self) {
    399 	self->use = target_relay_use;
    400 }
    401 
    402 
    403 //==========================================================
    404 
    405 /*QUAKED target_kill (.5 .5 .5) (-8 -8 -8) (8 8 8)
    406 Kills the activator.
    407 */
    408 void target_kill_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
    409 	G_Damage ( activator, NULL, NULL, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
    410 }
    411 
    412 void SP_target_kill( gentity_t *self ) {
    413 	self->use = target_kill_use;
    414 }
    415 
    416 /*QUAKED target_position (0 0.5 0) (-4 -4 -4) (4 4 4)
    417 Used as a positional target for in-game calculation, like jumppad targets.
    418 */
    419 void SP_target_position( gentity_t *self ){
    420 	G_SetOrigin( self, self->s.origin );
    421 }
    422 
    423 static void target_location_linkup(gentity_t *ent)
    424 {
    425 	int i;
    426 	int n;
    427 
    428 	if (level.locationLinked) 
    429 		return;
    430 
    431 	level.locationLinked = qtrue;
    432 
    433 	level.locationHead = NULL;
    434 
    435 	trap_SetConfigstring( CS_LOCATIONS, "unknown" );
    436 
    437 	for (i = 0, ent = g_entities, n = 1;
    438 			i < level.num_entities;
    439 			i++, ent++) {
    440 		if (ent->classname && !Q_stricmp(ent->classname, "target_location")) {
    441 			// lets overload some variables!
    442 			ent->health = n; // use for location marking
    443 			trap_SetConfigstring( CS_LOCATIONS + n, ent->message );
    444 			n++;
    445 			ent->nextTrain = level.locationHead;
    446 			level.locationHead = ent;
    447 		}
    448 	}
    449 
    450 	// All linked together now
    451 }
    452 
    453 /*QUAKED target_location (0 0.5 0) (-8 -8 -8) (8 8 8)
    454 Set "message" to the name of this location.
    455 Set "count" to 0-7 for color.
    456 0:white 1:red 2:green 3:yellow 4:blue 5:cyan 6:magenta 7:white
    457 
    458 Closest target_location in sight used for the location, if none
    459 in site, closest in distance
    460 */
    461 void SP_target_location( gentity_t *self ){
    462 	self->think = target_location_linkup;
    463 	self->nextthink = level.time + 200;  // Let them all spawn first
    464 
    465 	G_SetOrigin( self, self->s.origin );
    466 }
    467