codemp/game/g_target.c

Go to the documentation of this file.
00001 // Copyright (C) 1999-2000 Id Software, Inc.
00002 //
00003 #include "g_local.h"
00004 
00005 //==========================================================
00006 
00007 /*QUAKED target_give (1 0 0) (-8 -8 -8) (8 8 8)
00008 Gives the activator all the items pointed to.
00009 */
00010 void Use_Target_Give( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
00011         gentity_t       *t;
00012         trace_t         trace;
00013 
00014         if ( !activator->client ) {
00015                 return;
00016         }
00017 
00018         if ( !ent->target ) {
00019                 return;
00020         }
00021 
00022         memset( &trace, 0, sizeof( trace ) );
00023         t = NULL;
00024         while ( (t = G_Find (t, FOFS(targetname), ent->target)) != NULL ) {
00025                 if ( !t->item ) {
00026                         continue;
00027                 }
00028                 Touch_Item( t, activator, &trace );
00029 
00030                 // make sure it isn't going to respawn or show any events
00031                 t->nextthink = 0;
00032                 trap_UnlinkEntity( t );
00033         }
00034 }
00035 
00036 void SP_target_give( gentity_t *ent ) {
00037         ent->use = Use_Target_Give;
00038 }
00039 
00040 
00041 //==========================================================
00042 
00043 /*QUAKED target_remove_powerups (1 0 0) (-8 -8 -8) (8 8 8)
00044 takes away all the activators powerups.
00045 Used to drop flight powerups into death puts.
00046 */
00047 void Use_target_remove_powerups( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
00048         if( !activator->client ) {
00049                 return;
00050         }
00051 
00052         if( activator->client->ps.powerups[PW_REDFLAG] ) {
00053                 Team_ReturnFlag( TEAM_RED );
00054         } else if( activator->client->ps.powerups[PW_BLUEFLAG] ) {
00055                 Team_ReturnFlag( TEAM_BLUE );
00056         } else if( activator->client->ps.powerups[PW_NEUTRALFLAG] ) {
00057                 Team_ReturnFlag( TEAM_FREE );
00058         }
00059 
00060         memset( activator->client->ps.powerups, 0, sizeof( activator->client->ps.powerups ) );
00061 }
00062 
00063 void SP_target_remove_powerups( gentity_t *ent ) {
00064         ent->use = Use_target_remove_powerups;
00065 }
00066 
00067 
00068 //==========================================================
00069 
00070 /*QUAKED target_delay (1 0 0) (-8 -8 -8) (8 8 8) NO_RETRIGGER
00071 
00072 NO_RETRIGGER - Keeps the delay from resetting the time if it is
00073 activated again while it is counting down to an event.
00074 
00075 "wait" seconds to pause before firing targets.
00076 "random" delay variance, total delay = delay +/- random seconds
00077 */
00078 void Think_Target_Delay( gentity_t *ent ) {
00079         G_UseTargets( ent, ent->activator );
00080 }
00081 
00082 void Use_Target_Delay( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
00083         if (ent->nextthink > level.time && (ent->spawnflags & 1))
00084         { //Leave me alone, I am thinking.
00085                 return;
00086         }
00087         G_ActivateBehavior(ent,BSET_USE);
00088         ent->nextthink = level.time + ( ent->wait + ent->random * crandom() ) * 1000;
00089         ent->think = Think_Target_Delay;
00090         ent->activator = activator;
00091 }
00092 
00093 void SP_target_delay( gentity_t *ent ) {
00094         // check delay for backwards compatability
00095         if ( !G_SpawnFloat( "delay", "0", &ent->wait ) ) {
00096                 G_SpawnFloat( "wait", "1", &ent->wait );
00097         }
00098 
00099         if ( !ent->wait ) {
00100                 ent->wait = 1;
00101         }
00102         ent->use = Use_Target_Delay;
00103 }
00104 
00105 
00106 //==========================================================
00107 
00108 /*QUAKED target_score (1 0 0) (-8 -8 -8) (8 8 8)
00109 "count" number of points to add, default 1
00110 
00111 The activator is given this many points.
00112 */
00113 void Use_Target_Score (gentity_t *ent, gentity_t *other, gentity_t *activator) {
00114         AddScore( activator, ent->r.currentOrigin, ent->count );
00115 }
00116 
00117 void SP_target_score( gentity_t *ent ) {
00118         if ( !ent->count ) {
00119                 ent->count = 1;
00120         }
00121         ent->use = Use_Target_Score;
00122 }
00123 
00124 
00125 //==========================================================
00126 
00127 /*QUAKED target_print (1 0 0) (-8 -8 -8) (8 8 8) redteam blueteam private
00128 "message"       text to print
00129 "wait"          don't fire off again if triggered within this many milliseconds ago
00130 If "private", only the activator gets the message.  If no checks, all clients get the message.
00131 */
00132 void Use_Target_Print (gentity_t *ent, gentity_t *other, gentity_t *activator)
00133 {
00134         if (!ent || !ent->inuse)
00135         {
00136                 Com_Printf("ERROR: Bad ent in Use_Target_Print");
00137                 return;
00138         }
00139 
00140         if (ent->wait)
00141         {
00142                 if (ent->genericValue14 >= level.time)
00143                 {
00144                         return;
00145                 }
00146                 ent->genericValue14 = level.time + ent->wait;
00147         }
00148 
00149 #ifndef FINAL_BUILD
00150         if (!ent || !ent->inuse)
00151         {
00152                 Com_Error(ERR_DROP, "Bad ent in Use_Target_Print");
00153         }
00154         else if (!activator || !activator->inuse)
00155         {
00156                 Com_Error(ERR_DROP, "Bad activator in Use_Target_Print");
00157         }
00158 
00159         if (ent->genericValue15 > level.time)
00160         {
00161                 Com_Printf("TARGET PRINT ERRORS:\n");
00162                 if (activator && activator->classname && activator->classname[0])
00163                 {
00164                         Com_Printf("activator classname: %s\n", activator->classname);
00165                 }
00166                 if (activator && activator->target && activator->target[0])
00167                 {
00168                         Com_Printf("activator target: %s\n", activator->target);
00169                 }
00170                 if (activator && activator->targetname && activator->targetname[0])
00171                 {
00172                         Com_Printf("activator targetname: %s\n", activator->targetname);
00173                 }
00174                 if (ent->targetname && ent->targetname[0])
00175                 {
00176                         Com_Printf("print targetname: %s\n", ent->targetname);
00177                 }
00178                 Com_Error(ERR_DROP, "target_print used in quick succession, fix it! See the console for details.");
00179         }
00180         ent->genericValue15 = level.time + 5000;
00181 #endif
00182 
00183         G_ActivateBehavior(ent,BSET_USE);
00184         if ( ( ent->spawnflags & 4 ) ) 
00185         {//private, to one client only
00186                 if (!activator || !activator->inuse)
00187                 {
00188                         Com_Printf("ERROR: Bad activator in Use_Target_Print");
00189                 }
00190                 if ( activator && activator->client )
00191                 {//make sure there's a valid client ent to send it to
00192                         if (ent->message[0] == '@' && ent->message[1] != '@')
00193                         {
00194                                 trap_SendServerCommand( activator-g_entities, va("cps \"%s\"", ent->message ));
00195                         }
00196                         else
00197                         {
00198                                 trap_SendServerCommand( activator-g_entities, va("cp \"%s\"", ent->message ));
00199                         }
00200                 }
00201                 //NOTE: change in functionality - if there *is* no valid client ent, it won't send it to anyone at all
00202                 return;
00203         }
00204 
00205         if ( ent->spawnflags & 3 ) {
00206                 if ( ent->spawnflags & 1 ) {
00207                         if (ent->message[0] == '@' && ent->message[1] != '@')
00208                         {
00209                                 G_TeamCommand( TEAM_RED, va("cps \"%s\"", ent->message) );
00210                         }
00211                         else
00212                         {
00213                                 G_TeamCommand( TEAM_RED, va("cp \"%s\"", ent->message) );
00214                         }
00215                 }
00216                 if ( ent->spawnflags & 2 ) {
00217                         if (ent->message[0] == '@' && ent->message[1] != '@')
00218                         {
00219                                 G_TeamCommand( TEAM_BLUE, va("cps \"%s\"", ent->message) );
00220                         }
00221                         else
00222                         {
00223                                 G_TeamCommand( TEAM_BLUE, va("cp \"%s\"", ent->message) );
00224                         }
00225                 }
00226                 return;
00227         }
00228 
00229         if (ent->message[0] == '@' && ent->message[1] != '@')
00230         {
00231                 trap_SendServerCommand( -1, va("cps \"%s\"", ent->message ));
00232         }
00233         else
00234         {
00235                 trap_SendServerCommand( -1, va("cp \"%s\"", ent->message ));
00236         }
00237 }
00238 
00239 void SP_target_print( gentity_t *ent ) {
00240         ent->use = Use_Target_Print;
00241 }
00242 
00243 
00244 //==========================================================
00245 
00246 
00247 /*QUAKED target_speaker (1 0 0) (-8 -8 -8) (8 8 8) looped-on looped-off global activator
00248 "noise"         wav file to play
00249 
00250 A global sound will play full volume throughout the level.
00251 Activator sounds will play on the player that activated the target.
00252 Global and activator sounds can't be combined with looping.
00253 Normal sounds play each time the target is used.
00254 Looped sounds will be toggled by use functions.
00255 Multiple identical looping sounds will just increase volume without any speed cost.
00256 "wait" : Seconds between auto triggerings, 0 = don't auto trigger
00257 "random"        wait variance, default is 0
00258 */
00259 void Use_Target_Speaker (gentity_t *ent, gentity_t *other, gentity_t *activator) {
00260         G_ActivateBehavior(ent,BSET_USE);
00261 
00262         if (ent->spawnflags & 3) {      // looping sound toggles
00263                 if (ent->s.loopSound)
00264                 {
00265                         ent->s.loopSound = 0;   // turn it off
00266                         ent->s.loopIsSoundset = qfalse;
00267                         ent->s.trickedentindex = 1;
00268                 }
00269                 else
00270                 {
00271                         ent->s.loopSound = ent->noise_index;    // start it
00272                         ent->s.loopIsSoundset = qfalse;
00273                         ent->s.trickedentindex = 0;
00274                 }
00275         }else { // normal sound
00276                 if ( ent->spawnflags & 8 ) {
00277                         G_AddEvent( activator, EV_GENERAL_SOUND, ent->noise_index );
00278                 } else if (ent->spawnflags & 4) {
00279                         G_AddEvent( ent, EV_GLOBAL_SOUND, ent->noise_index );
00280                 } else {
00281                         G_AddEvent( ent, EV_GENERAL_SOUND, ent->noise_index );
00282                 }
00283         }
00284 }
00285 
00286 void SP_target_speaker( gentity_t *ent ) {
00287         char    buffer[MAX_QPATH];
00288         char    *s;
00289 
00290         G_SpawnFloat( "wait", "0", &ent->wait );
00291         G_SpawnFloat( "random", "0", &ent->random );
00292 
00293         if ( G_SpawnString ( "soundSet", "", &s ) )
00294         {       // this is a sound set
00295                 ent->s.soundSetIndex = G_SoundSetIndex(s);
00296                 ent->s.eFlags = EF_PERMANENT;
00297                 VectorCopy( ent->s.origin, ent->s.pos.trBase );
00298                 trap_LinkEntity (ent);
00299                 return;
00300         }
00301 
00302         if ( !G_SpawnString( "noise", "NOSOUND", &s ) ) {
00303                 G_Error( "target_speaker without a noise key at %s", vtos( ent->s.origin ) );
00304         }
00305 
00306         // force all client reletive sounds to be "activator" speakers that
00307         // play on the entity that activates it
00308         if ( s[0] == '*' ) {
00309                 ent->spawnflags |= 8;
00310         }
00311 
00312         Q_strncpyz( buffer, s, sizeof(buffer) );
00313 
00314         ent->noise_index = G_SoundIndex(buffer);
00315 
00316         // a repeating speaker can be done completely client side
00317         ent->s.eType = ET_SPEAKER;
00318         ent->s.eventParm = ent->noise_index;
00319         ent->s.frame = ent->wait * 10;
00320         ent->s.clientNum = ent->random * 10;
00321 
00322 
00323         // check for prestarted looping sound
00324         if ( ent->spawnflags & 1 ) {
00325                 ent->s.loopSound = ent->noise_index;
00326                 ent->s.loopIsSoundset = qfalse;
00327         }
00328 
00329         ent->use = Use_Target_Speaker;
00330 
00331         if (ent->spawnflags & 4) {
00332                 ent->r.svFlags |= SVF_BROADCAST;
00333         }
00334 
00335         VectorCopy( ent->s.origin, ent->s.pos.trBase );
00336 
00337         // must link the entity so we get areas and clusters so
00338         // the server can determine who to send updates to
00339         trap_LinkEntity( ent );
00340 }
00341 
00342 
00343 
00344 //==========================================================
00345 
00346 /*QUAKED target_laser (0 .5 .8) (-8 -8 -8) (8 8 8) START_ON
00347 When triggered, fires a laser.  You can either set a target or a direction.
00348 */
00349 void target_laser_think (gentity_t *self) {
00350         vec3_t  end;
00351         trace_t tr;
00352         vec3_t  point;
00353 
00354         // if pointed at another entity, set movedir to point at it
00355         if ( self->enemy ) {
00356                 VectorMA (self->enemy->s.origin, 0.5, self->enemy->r.mins, point);
00357                 VectorMA (point, 0.5, self->enemy->r.maxs, point);
00358                 VectorSubtract (point, self->s.origin, self->movedir);
00359                 VectorNormalize (self->movedir);
00360         }
00361 
00362         // fire forward and see what we hit
00363         VectorMA (self->s.origin, 2048, self->movedir, end);
00364 
00365         trap_Trace( &tr, self->s.origin, NULL, NULL, end, self->s.number, CONTENTS_SOLID|CONTENTS_BODY|CONTENTS_CORPSE);
00366 
00367         if ( tr.entityNum ) {
00368                 // hurt it if we can
00369                 G_Damage ( &g_entities[tr.entityNum], self, self->activator, self->movedir, 
00370                         tr.endpos, self->damage, DAMAGE_NO_KNOCKBACK, MOD_TARGET_LASER);
00371         }
00372 
00373         VectorCopy (tr.endpos, self->s.origin2);
00374 
00375         trap_LinkEntity( self );
00376         self->nextthink = level.time + FRAMETIME;
00377 }
00378 
00379 void target_laser_on (gentity_t *self)
00380 {
00381         if (!self->activator)
00382                 self->activator = self;
00383         target_laser_think (self);
00384 }
00385 
00386 void target_laser_off (gentity_t *self)
00387 {
00388         trap_UnlinkEntity( self );
00389         self->nextthink = 0;
00390 }
00391 
00392 void target_laser_use (gentity_t *self, gentity_t *other, gentity_t *activator)
00393 {
00394         self->activator = activator;
00395         if ( self->nextthink > 0 )
00396                 target_laser_off (self);
00397         else
00398                 target_laser_on (self);
00399 }
00400 
00401 void target_laser_start (gentity_t *self)
00402 {
00403         gentity_t *ent;
00404 
00405         self->s.eType = ET_BEAM;
00406 
00407         if (self->target) {
00408                 ent = G_Find (NULL, FOFS(targetname), self->target);
00409                 if (!ent) {
00410                         G_Printf ("%s at %s: %s is a bad target\n", self->classname, vtos(self->s.origin), self->target);
00411                 }
00412                 self->enemy = ent;
00413         } else {
00414                 G_SetMovedir (self->s.angles, self->movedir);
00415         }
00416 
00417         self->use = target_laser_use;
00418         self->think = target_laser_think;
00419 
00420         if ( !self->damage ) {
00421                 self->damage = 1;
00422         }
00423 
00424         if (self->spawnflags & 1)
00425                 target_laser_on (self);
00426         else
00427                 target_laser_off (self);
00428 }
00429 
00430 void SP_target_laser (gentity_t *self)
00431 {
00432         // let everything else get spawned before we start firing
00433         self->think = target_laser_start;
00434         self->nextthink = level.time + FRAMETIME;
00435 }
00436 
00437 
00438 //==========================================================
00439 
00440 void target_teleporter_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
00441         gentity_t       *dest;
00442 
00443         if (!activator->client)
00444                 return;
00445 
00446         G_ActivateBehavior(self,BSET_USE);
00447 
00448         dest =  G_PickTarget( self->target );
00449         if (!dest) {
00450                 G_Printf ("Couldn't find teleporter destination\n");
00451                 return;
00452         }
00453 
00454         TeleportPlayer( activator, dest->s.origin, dest->s.angles );
00455 }
00456 
00457 /*QUAKED target_teleporter (1 0 0) (-8 -8 -8) (8 8 8)
00458 The activator will be teleported away.
00459 */
00460 void SP_target_teleporter( gentity_t *self ) {
00461         if (!self->targetname)
00462                 G_Printf("untargeted %s at %s\n", self->classname, vtos(self->s.origin));
00463 
00464         self->use = target_teleporter_use;
00465 }
00466 
00467 //==========================================================
00468 
00469 
00470 /*QUAKED target_relay (.5 .5 .5) (-8 -8 -8) (8 8 8) RED_ONLY BLUE_ONLY RANDOM x x x x INACTIVE
00471 This doesn't perform any actions except fire its targets.
00472 The activator can be forced to be from a certain team.
00473 if RANDOM is checked, only one of the targets will be fired, not all of them
00474 
00475   INACTIVE  Can't be used until activated
00476 
00477 wait - set to -1 to use it only once
00478 */
00479 void target_relay_use (gentity_t *self, gentity_t *other, gentity_t *activator) {
00480         qboolean ranscript = qfalse;
00481         if ( ( self->spawnflags & 1 ) && activator->client 
00482                 && activator->client->sess.sessionTeam != TEAM_RED ) {
00483                 return;
00484         }
00485         if ( ( self->spawnflags & 2 ) && activator->client 
00486                 && activator->client->sess.sessionTeam != TEAM_BLUE ) {
00487                 return;
00488         }
00489 
00490         if ( self->flags & FL_INACTIVE )
00491         {//set by target_deactivate
00492                 return;
00493         }
00494 
00495         ranscript = G_ActivateBehavior( self, BSET_USE );
00496         if ( self->wait == -1 )
00497         {//never use again
00498                 if ( ranscript )
00499                 {//crap, can't remove!
00500                         self->use = NULL;
00501                 }
00502                 else
00503                 {//remove
00504                         self->think = G_FreeEntity;
00505                         self->nextthink = level.time + FRAMETIME;
00506                 }
00507         }
00508         if ( self->spawnflags & 4 ) {
00509                 gentity_t       *ent;
00510 
00511                 ent = G_PickTarget( self->target );
00512                 if ( ent && ent->use ) {
00513                         GlobalUse( ent, self, activator );
00514                 }
00515                 return;
00516         }
00517         G_UseTargets (self, activator);
00518 }
00519 
00520 void SP_target_relay (gentity_t *self) {
00521         self->use = target_relay_use;
00522         if ( self->spawnflags&128 )
00523         {
00524                 self->flags |= FL_INACTIVE;
00525         }
00526 }
00527 
00528 
00529 //==========================================================
00530 
00531 /*QUAKED target_kill (.5 .5 .5) (-8 -8 -8) (8 8 8)
00532 Kills the activator.
00533 */
00534 void target_kill_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
00535         G_ActivateBehavior(self,BSET_USE);
00536         G_Damage ( activator, NULL, NULL, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
00537 }
00538 
00539 void SP_target_kill( gentity_t *self ) {
00540         self->use = target_kill_use;
00541 }
00542 
00543 /*QUAKED target_position (0 0.5 0) (-4 -4 -4) (4 4 4)
00544 Used as a positional target for in-game calculation, like jumppad targets.
00545 */
00546 void SP_target_position( gentity_t *self ){
00547         G_SetOrigin( self, self->s.origin );
00548         /*
00549         G_SetAngles( self, self->s.angles );
00550         self->s.eType = ET_INVISIBLE;
00551         */
00552 }
00553 
00554 static void target_location_linkup(gentity_t *ent)
00555 {
00556         int i;
00557         int n;
00558 
00559         if (level.locationLinked) 
00560                 return;
00561 
00562         level.locationLinked = qtrue;
00563 
00564         level.locationHead = NULL;
00565 
00566         trap_SetConfigstring( CS_LOCATIONS, "unknown" );
00567 
00568         for (i = 0, ent = g_entities, n = 1;
00569                         i < level.num_entities;
00570                         i++, ent++) {
00571                 if (ent->classname && !Q_stricmp(ent->classname, "target_location")) {
00572                         // lets overload some variables!
00573                         ent->health = n; // use for location marking
00574                         trap_SetConfigstring( CS_LOCATIONS + n, ent->message );
00575                         n++;
00576                         ent->nextTrain = level.locationHead;
00577                         level.locationHead = ent;
00578                 }
00579         }
00580 
00581         // All linked together now
00582 }
00583 
00584 /*QUAKED target_location (0 0.5 0) (-8 -8 -8) (8 8 8)
00585 Set "message" to the name of this location.
00586 Set "count" to 0-7 for color.
00587 0:white 1:red 2:green 3:yellow 4:blue 5:cyan 6:magenta 7:white
00588 
00589 Closest target_location in sight used for the location, if none
00590 in site, closest in distance
00591 */
00592 void SP_target_location( gentity_t *self ){
00593         self->think = target_location_linkup;
00594         self->nextthink = level.time + 200;  // Let them all spawn first
00595 
00596         G_SetOrigin( self, self->s.origin );
00597 }
00598 
00599 /*QUAKED target_counter (1.0 0 0) (-4 -4 -4) (4 4 4) x x x x x x x INACTIVE
00600 Acts as an intermediary for an action that takes multiple inputs.
00601 
00602 INACTIVE cannot be used until used by a target_activate
00603 
00604 target2 - what the counter should fire each time it's incremented and does NOT reach it's count
00605 
00606 After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
00607 
00608 bounceCount - number of times the counter should reset to it's full count when it's done
00609 */
00610 extern void G_DebugPrint( int level, const char *format, ... );
00611 void target_counter_use( gentity_t *self, gentity_t *other, gentity_t *activator )
00612 {
00613         if ( self->count == 0 )
00614         {
00615                 return;
00616         }
00617         
00618         //gi.Printf("target_counter %s used by %s, entnum %d\n", self->targetname, activator->targetname, activator->s.number );
00619         self->count--;
00620 
00621         if ( activator )
00622         {
00623                 G_DebugPrint( WL_VERBOSE, "target_counter %s used by %s (%d/%d)\n", self->targetname, activator->targetname, (self->genericValue1-self->count), self->genericValue1 );
00624         }
00625 
00626         if ( self->count )
00627         {
00628                 if ( self->target2 )
00629                 {
00630                         //gi.Printf("target_counter %s firing target2 from %s, entnum %d\n", self->targetname, activator->targetname, activator->s.number );
00631                         G_UseTargets2( self, activator, self->target2 );
00632                 }
00633                 return;
00634         }
00635         
00636         G_ActivateBehavior( self,BSET_USE );
00637 
00638         if ( self->spawnflags & 128 )
00639         {
00640                 self->flags |= FL_INACTIVE;
00641         }
00642 
00643         self->activator = activator;
00644         G_UseTargets( self, activator );
00645 
00646         if ( self->count == 0 )
00647         {
00648                 if ( self->bounceCount == 0 )
00649                 {
00650                         return;
00651                 }
00652                 self->count = self->genericValue1;
00653                 if ( self->bounceCount > 0 )
00654                 {//-1 means bounce back forever
00655                         self->bounceCount--; 
00656                 }
00657         }
00658 }
00659 
00660 void SP_target_counter (gentity_t *self)
00661 {
00662         self->wait = -1;
00663         if (!self->count)
00664         {
00665                 self->count = 2;
00666         }
00667         //if ( self->bounceCount > 0 )//let's always set this anyway
00668         {//we will reset when we use up our count, remember our initial count
00669                 self->genericValue1 = self->count;
00670         }
00671 
00672         self->use = target_counter_use;
00673 }
00674 
00675 /*QUAKED target_random (.5 .5 .5) (-4 -4 -4) (4 4 4) USEONCE
00676 Randomly fires off only one of it's targets each time used
00677 
00678 USEONCE set to never fire again
00679 */
00680 
00681 void target_random_use(gentity_t *self, gentity_t *other, gentity_t *activator)
00682 {
00683         int                     t_count = 0, pick;
00684         gentity_t       *t = NULL;
00685 
00686         //gi.Printf("target_random %s used by %s (entnum %d)\n", self->targetname, activator->targetname, activator->s.number );
00687         G_ActivateBehavior(self,BSET_USE);
00688 
00689         if(self->spawnflags & 1)
00690         {
00691                 self->use = 0;
00692         }
00693 
00694         while ( (t = G_Find (t, FOFS(targetname), self->target)) != NULL )
00695         {
00696                 if (t != self)
00697                 {
00698                         t_count++;
00699                 }
00700         }
00701 
00702         if(!t_count)
00703         {
00704                 return;
00705         }
00706 
00707         if(t_count == 1)
00708         {
00709                 G_UseTargets (self, activator);
00710                 return;
00711         }
00712 
00713         //FIXME: need a seed
00714         pick = Q_irand(1, t_count);
00715         t_count = 0;
00716         while ( (t = G_Find (t, FOFS(targetname), self->target)) != NULL )
00717         {
00718                 if (t != self)
00719                 {
00720                         t_count++;
00721                 }
00722                 else
00723                 {
00724                         continue;
00725                 }
00726                 
00727                 if (t == self)
00728                 {
00729 //                              gi.Printf ("WARNING: Entity used itself.\n");
00730                 }
00731                 else if(t_count == pick)
00732                 {
00733                         if (t->use != NULL)     // check can be omitted
00734                         {
00735                                 GlobalUse(t, self, activator);
00736                                 return;
00737                         }
00738                 }
00739 
00740                 if (!self->inuse)
00741                 {
00742                         Com_Printf("entity was removed while using targets\n");
00743                         return;
00744                 }
00745         }
00746 }
00747 
00748 void SP_target_random (gentity_t *self)
00749 {
00750         self->use = target_random_use;
00751 }
00752 
00753 int     numNewICARUSEnts = 0;
00754 void scriptrunner_run (gentity_t *self)
00755 {
00756         /*
00757         if (self->behaviorSet[BSET_USE])
00758         {       
00759                 char    newname[MAX_FILENAME_LENGTH];
00760 
00761                 sprintf((char *) &newname, "%s/%s", Q3_SCRIPT_DIR, self->behaviorSet[BSET_USE] );
00762 
00763                 ICARUS_RunScript( self, newname );
00764         }
00765         */
00766 
00767         if ( self->count != -1 )
00768         {
00769                 if ( self->count <= 0 )
00770                 {
00771                         self->use = 0;
00772                         self->behaviorSet[BSET_USE] = NULL;
00773                         return;
00774                 }
00775                 else
00776                 {
00777                         --self->count;
00778                 }
00779         }
00780 
00781         if (self->behaviorSet[BSET_USE])
00782         {
00783                 if ( self->spawnflags & 1 )
00784                 {
00785                         if ( !self->activator )
00786                         {
00787                                 if (g_developer.integer)
00788                                 {
00789                                         Com_Printf("target_scriptrunner tried to run on invalid entity!\n");
00790                                 }
00791                                 return;
00792                         }
00793 
00794                         //if ( !self->activator->sequencer || !self->activator->taskManager )
00795                         if (!trap_ICARUS_IsInitialized(self->s.number))
00796                         {//Need to be initialized through ICARUS
00797                                 if ( !self->activator->script_targetname || !self->activator->script_targetname[0] )
00798                                 {
00799                                         //We don't have a script_targetname, so create a new one
00800                                         self->activator->script_targetname = va( "newICARUSEnt%d", numNewICARUSEnts++ );
00801                                 }
00802 
00803                                 if ( trap_ICARUS_ValidEnt( self->activator ) )
00804                                 {
00805                                         trap_ICARUS_InitEnt( self->activator );
00806                                 }
00807                                 else
00808                                 {
00809                                         if (g_developer.integer)
00810                                         {
00811                                                 Com_Printf("target_scriptrunner tried to run on invalid ICARUS activator!\n");
00812                                         }
00813                                         return;
00814                                 }
00815                         }
00816 
00817                         if (g_developer.integer)
00818                         {
00819                                 Com_Printf( "target_scriptrunner running %s on activator %s\n", self->behaviorSet[BSET_USE], self->activator->targetname );
00820                         }
00821                         trap_ICARUS_RunScript( self->activator, va( "%s/%s", Q3_SCRIPT_DIR, self->behaviorSet[BSET_USE] ) );
00822                 }
00823                 else
00824                 {
00825                         if ( g_developer.integer && self->activator )
00826                         {
00827                                 Com_Printf( "target_scriptrunner %s used by %s\n", self->targetname, self->activator->targetname );
00828                         }
00829                         G_ActivateBehavior( self, BSET_USE );
00830                 }
00831         }
00832 
00833         if ( self->wait )
00834         {
00835                 self->nextthink = level.time + self->wait;
00836         }
00837 }
00838 
00839 void target_scriptrunner_use(gentity_t *self, gentity_t *other, gentity_t *activator)
00840 {
00841         if ( self->nextthink > level.time )
00842         {
00843                 return;
00844         }
00845 
00846         self->activator = activator;
00847         self->enemy = other;
00848         if ( self->delay )
00849         {//delay before firing scriptrunner
00850                 self->think = scriptrunner_run;
00851                 self->nextthink = level.time + self->delay;
00852         }
00853         else
00854         {
00855                 scriptrunner_run (self);
00856         }
00857 }
00858 
00859 /*QUAKED target_scriptrunner (1 0 0) (-4 -4 -4) (4 4 4) runonactivator x x x x x x INACTIVE
00860 --- SPAWNFLAGS ---
00861 runonactivator - Will run the script on the entity that used this or tripped the trigger that used this
00862 INACTIVE - start off
00863 
00864 ----- KEYS ------
00865 Usescript - Script to run when used
00866 count - how many times to run, -1 = infinite.  Default is once
00867 wait - can't be used again in this amount of seconds (Default is 1 second if it's multiple-use)
00868 delay - how long to wait after use to run script
00869 
00870 */
00871 void SP_target_scriptrunner( gentity_t *self )
00872 {
00873         float v;
00874         if ( self->spawnflags & 128 )
00875         {
00876                 self->flags |= FL_INACTIVE;
00877         }
00878 
00879         if ( !self->count )
00880         {
00881                 self->count = 1;//default 1 use only
00882         }
00883         /*
00884         else if ( !self->wait )
00885         {
00886                 self->wait = 1;//default wait of 1 sec
00887         }
00888         */
00889         // FIXME: this is a hack... because delay is read in as an int, so I'm bypassing that because it's too late in the project to change it and I want to be able to set less than a second delays
00890         // no one should be setting a radius on a scriptrunner, if they are this would be bad, take this out for the next project
00891         v = 0.0f;
00892         G_SpawnFloat( "delay", "0", &v );
00893         self->delay = v * 1000;//sec to ms
00894         self->wait *= 1000;//sec to ms
00895 
00896         G_SetOrigin( self, self->s.origin );
00897         self->use = target_scriptrunner_use;
00898 }
00899 
00900 void G_SetActiveState(char *targetstring, qboolean actState)
00901 {
00902         gentity_t       *target = NULL;
00903         while( NULL != (target = G_Find(target, FOFS(targetname), targetstring)) )
00904         {
00905                 target->flags = actState ? (target->flags&~FL_INACTIVE) : (target->flags|FL_INACTIVE);
00906         }
00907 }
00908 
00909 #define ACT_ACTIVE              qtrue
00910 #define ACT_INACTIVE    qfalse
00911 
00912 void target_activate_use(gentity_t *self, gentity_t *other, gentity_t *activator)
00913 {
00914         G_ActivateBehavior(self,BSET_USE);
00915 
00916         G_SetActiveState(self->target, ACT_ACTIVE);
00917 }
00918 
00919 void target_deactivate_use(gentity_t *self, gentity_t *other, gentity_t *activator)
00920 {
00921         G_ActivateBehavior(self,BSET_USE);
00922 
00923         G_SetActiveState(self->target, ACT_INACTIVE);
00924 }
00925 
00926 //FIXME: make these apply to doors, etc too?
00927 /*QUAKED target_activate (1 0 0) (-4 -4 -4) (4 4 4)
00928 Will set the target(s) to be usable/triggerable
00929 */
00930 void SP_target_activate( gentity_t *self )
00931 {
00932         G_SetOrigin( self, self->s.origin );
00933         self->use = target_activate_use;
00934 }
00935 
00936 /*QUAKED target_deactivate (1 0 0) (-4 -4 -4) (4 4 4)
00937 Will set the target(s) to be non-usable/triggerable
00938 */
00939 void SP_target_deactivate( gentity_t *self )
00940 {
00941         G_SetOrigin( self, self->s.origin );
00942         self->use = target_deactivate_use;
00943 }
00944 
00945 void target_level_change_use(gentity_t *self, gentity_t *other, gentity_t *activator)
00946 {
00947         G_ActivateBehavior(self,BSET_USE);
00948 
00949         trap_SendConsoleCommand(EXEC_NOW, va("map %s", self->message));
00950 }
00951 
00952 /*QUAKED target_level_change (1 0 0) (-4 -4 -4) (4 4 4)
00953 "mapname" - Name of map to change to
00954 */
00955 void SP_target_level_change( gentity_t *self )
00956 {
00957         char *s;
00958 
00959         G_SpawnString( "mapname", "", &s );
00960         self->message = G_NewString(s);
00961 
00962         if ( !self->message || !self->message[0] )
00963         {
00964                 G_Error( "target_level_change with no mapname!\n");
00965                 return;
00966         }
00967 
00968         G_SetOrigin( self, self->s.origin );
00969         self->use = target_level_change_use;
00970 }
00971 
00972 void target_play_music_use(gentity_t *self, gentity_t *other, gentity_t *activator)
00973 {
00974         G_ActivateBehavior(self,BSET_USE);
00975         trap_SetConfigstring( CS_MUSIC, self->message );
00976 }
00977 
00978 /*QUAKED target_play_music (1 0 0) (-4 -4 -4) (4 4 4)
00979 target_play_music
00980 Plays the requested music files when this target is used.
00981 
00982 "targetname"
00983 "music"         music WAV or MP3 file ( music/introfile.mp3 [optional]  music/loopfile.mp3 )
00984 
00985 If an intro file and loop file are specified, the intro plays first, then the looping
00986 portion will start and loop indefinetly.  If no introfile is entered, only the loopfile
00987 will play.
00988 */
00989 void SP_target_play_music( gentity_t *self )
00990 {
00991         char *s;
00992 
00993         G_SetOrigin( self, self->s.origin );
00994         if (!G_SpawnString( "music", "", &s ))
00995         {
00996                 G_Error( "target_play_music without a music key at %s", vtos( self->s.origin ) );
00997         }
00998 
00999         self->message = G_NewString(s);
01000 
01001         self->use = target_play_music_use;
01002 }