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