codemp/game/g_misc.c

Go to the documentation of this file.
00001 // Copyright (C) 1999-2000 Id Software, Inc.
00002 //
00003 // g_misc.c
00004 
00005 #include "g_local.h"
00006 #include "../ghoul2/G2.h"
00007 
00008 #include "ai_main.h" //for the g2animents
00009 
00010 #define HOLOCRON_RESPAWN_TIME 30000
00011 #define MAX_AMMO_GIVE 2
00012 #define STATION_RECHARGE_TIME 100
00013 
00014 void HolocronThink(gentity_t *ent);
00015 extern vmCvar_t g_MaxHolocronCarry;
00016 
00017 /*QUAKED func_group (0 0 0) ?
00018 Used to group brushes together just for editor convenience.  They are turned into normal brushes by the utilities.
00019 */
00020 
00021 
00022 /*QUAKED info_camp (0 0.5 0) (-4 -4 -4) (4 4 4)
00023 Used as a positional target for calculations in the utilities (spotlights, etc), but removed during gameplay.
00024 */
00025 void SP_info_camp( gentity_t *self ) {
00026         G_SetOrigin( self, self->s.origin );
00027 }
00028 
00029 
00030 /*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4)
00031 Used as a positional target for calculations in the utilities (spotlights, etc), but removed during gameplay.
00032 */
00033 void SP_info_null( gentity_t *self ) {
00034         G_FreeEntity( self );
00035 }
00036 
00037 
00038 /*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)
00039 Used as a positional target for in-game calculation, like jumppad targets.
00040 target_position does the same thing
00041 */
00042 void SP_info_notnull( gentity_t *self ){
00043         G_SetOrigin( self, self->s.origin );
00044 }
00045 
00046 
00047 /*QUAKED lightJunior (0 0.7 0.3) (-8 -8 -8) (8 8 8) nonlinear angle negative_spot negative_point
00048 Non-displayed light that only affects dynamic game models, but does not contribute to lightmaps
00049 "light" overrides the default 300 intensity.
00050 Nonlinear checkbox gives inverse square falloff instead of linear 
00051 Angle adds light:surface angle calculations (only valid for "Linear" lights) (wolf)
00052 Lights pointed at a target will be spotlights.
00053 "radius" overrides the default 64 unit radius of a spotlight at the target point.
00054 "fade" falloff/radius adjustment value. multiply the run of the slope by "fade" (1.0f default) (only valid for "Linear" lights) (wolf)
00055 */
00056 
00057 /*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) linear noIncidence START_OFF
00058 Non-displayed light.
00059 "light" overrides the default 300 intensity. - affects size
00060 a negative "light" will subtract the light's color
00061 'Linear' checkbox gives linear falloff instead of inverse square
00062 'noIncidence' checkbox makes lighting smoother
00063 Lights pointed at a target will be spotlights.
00064 "radius" overrides the default 64 unit radius of a spotlight at the target point.
00065 "scale" multiplier for the light intensity - does not affect size (default 1)
00066                 greater than 1 is brighter, between 0 and 1 is dimmer.
00067 "color" sets the light's color
00068 "targetname" to indicate a switchable light - NOTE that all lights with the same targetname will be grouped together and act as one light (ie: don't mix colors, styles or start_off flag)
00069 "style" to specify a specify light style, even for switchable lights!
00070 "style_off" light style to use when switched off (Only for switchable lights)
00071 
00072    1 FLICKER (first variety)
00073    2 SLOW STRONG PULSE
00074    3 CANDLE (first variety)
00075    4 FAST STROBE
00076    5 GENTLE PULSE 1
00077    6 FLICKER (second variety)
00078    7 CANDLE (second variety)
00079    8 CANDLE (third variety)
00080    9 SLOW STROBE (fourth variety)
00081    10 FLUORESCENT FLICKER
00082    11 SLOW PULSE NOT FADE TO BLACK
00083    12 FAST PULSE FOR JEREMY
00084    13 Test Blending
00085 */
00086 static void misc_lightstyle_set ( gentity_t *ent)
00087 {
00088         const int mLightStyle = ent->count;
00089         const int mLightSwitchStyle = ent->bounceCount;
00090         const int mLightOffStyle = ent->fly_sound_debounce_time;
00091         if (!ent->alt_fire)
00092         {       //turn off
00093                 if (mLightOffStyle)     //i have a light style i'd like to use when off
00094                 {
00095                         char lightstyle[32];
00096                         trap_GetConfigstring(CS_LIGHT_STYLES + (mLightOffStyle*3)+0, lightstyle, 32);
00097                         trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+0, lightstyle);
00098 
00099                         trap_GetConfigstring(CS_LIGHT_STYLES + (mLightOffStyle*3)+1, lightstyle, 32);
00100                         trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+1, lightstyle);
00101 
00102                         trap_GetConfigstring(CS_LIGHT_STYLES + (mLightOffStyle*3)+2, lightstyle, 32);
00103                         trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+2, lightstyle);
00104                 }else
00105                 {
00106                         trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+0, "a");
00107                         trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+1, "a");
00108                         trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+2, "a");
00109                 }
00110         } 
00111         else
00112         {       //Turn myself on now
00113                 if (mLightSwitchStyle)  //i have a light style i'd like to use when on
00114                 {
00115                         char lightstyle[32];
00116                         trap_GetConfigstring(CS_LIGHT_STYLES + (mLightSwitchStyle*3)+0, lightstyle, 32);
00117                         trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+0, lightstyle);
00118 
00119                         trap_GetConfigstring(CS_LIGHT_STYLES + (mLightSwitchStyle*3)+1, lightstyle, 32);
00120                         trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+1, lightstyle);
00121 
00122                         trap_GetConfigstring(CS_LIGHT_STYLES + (mLightSwitchStyle*3)+2, lightstyle, 32);
00123                         trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+2, lightstyle);
00124                 }
00125                 else
00126                 {
00127                         trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+0, "z");
00128                         trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+1, "z");
00129                         trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+2, "z");
00130                 }
00131         }
00132 }
00133 
00134 void misc_dlight_use ( gentity_t *ent, gentity_t *other, gentity_t *activator )
00135 {
00136         G_ActivateBehavior(ent,BSET_USE);
00137 
00138         ent->alt_fire = !ent->alt_fire; //toggle
00139         misc_lightstyle_set (ent);
00140 }
00141 
00142 void SP_light( gentity_t *self ) {
00143         if (!self->targetname )
00144         {//if i don't have a light style switch, the i go away
00145                 G_FreeEntity( self );
00146                 return;
00147         }
00148 
00149         G_SpawnInt( "style", "0", &self->count );
00150         G_SpawnInt( "switch_style", "0", &self->bounceCount );
00151         G_SpawnInt( "style_off", "0", &self->fly_sound_debounce_time );
00152         G_SetOrigin( self, self->s.origin );
00153         trap_LinkEntity( self );
00154 
00155         self->use = misc_dlight_use;
00156 
00157         self->s.eType = ET_GENERAL;
00158         self->alt_fire = qfalse;
00159         self->r.svFlags |= SVF_NOCLIENT;
00160 
00161         if ( !(self->spawnflags & 4) )
00162         {       //turn myself on now
00163                 self->alt_fire = qtrue;
00164         }
00165         misc_lightstyle_set (self);
00166 }
00167 
00168 
00169 /*
00170 =================================================================================
00171 
00172 TELEPORTERS
00173 
00174 =================================================================================
00175 */
00176 
00177 void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles ) {
00178         gentity_t       *tent;
00179         qboolean        isNPC = qfalse;
00180         if (player->s.eType == ET_NPC)
00181         {
00182                 isNPC = qtrue;
00183         }
00184 
00185         // use temp events at source and destination to prevent the effect
00186         // from getting dropped by a second player event
00187         if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
00188                 tent = G_TempEntity( player->client->ps.origin, EV_PLAYER_TELEPORT_OUT );
00189                 tent->s.clientNum = player->s.clientNum;
00190 
00191                 tent = G_TempEntity( origin, EV_PLAYER_TELEPORT_IN );
00192                 tent->s.clientNum = player->s.clientNum;
00193         }
00194 
00195         // unlink to make sure it can't possibly interfere with G_KillBox
00196         trap_UnlinkEntity (player);
00197 
00198         VectorCopy ( origin, player->client->ps.origin );
00199         player->client->ps.origin[2] += 1;
00200 
00201         // spit the player out
00202         AngleVectors( angles, player->client->ps.velocity, NULL, NULL );
00203         VectorScale( player->client->ps.velocity, 400, player->client->ps.velocity );
00204         player->client->ps.pm_time = 160;               // hold time
00205         player->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
00206 
00207         // toggle the teleport bit so the client knows to not lerp
00208         player->client->ps.eFlags ^= EF_TELEPORT_BIT;
00209 
00210         // set angles
00211         SetClientViewAngle( player, angles );
00212 
00213         // kill anything at the destination
00214         if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
00215                 G_KillBox (player);
00216         }
00217 
00218         // save results of pmove
00219         BG_PlayerStateToEntityState( &player->client->ps, &player->s, qtrue );
00220         if (isNPC)
00221         {
00222                 player->s.eType = ET_NPC;
00223         }
00224 
00225         // use the precise origin for linking
00226         VectorCopy( player->client->ps.origin, player->r.currentOrigin );
00227 
00228         if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
00229                 trap_LinkEntity (player);
00230         }
00231 }
00232 
00233 
00234 /*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16)
00235 Point teleporters at these.
00236 Now that we don't have teleport destination pads, this is just
00237 an info_notnull
00238 */
00239 void SP_misc_teleporter_dest( gentity_t *ent ) {
00240 }
00241 
00242 
00243 //===========================================================
00244 
00245 /*QUAKED misc_model (1 0 0) (-16 -16 -16) (16 16 16)
00246 "model"         arbitrary .md3 or .ase file to display
00247 turns into map triangles - not solid
00248 */
00249 void SP_misc_model( gentity_t *ent ) {
00250 
00251 #if 0
00252         ent->s.modelindex = G_ModelIndex( ent->model );
00253         VectorSet (ent->r.mins, -16, -16, -16);
00254         VectorSet (ent->r.maxs, 16, 16, 16);
00255         trap_LinkEntity (ent);
00256 
00257         G_SetOrigin( ent, ent->s.origin );
00258         VectorCopy( ent->s.angles, ent->s.apos.trBase );
00259 #else
00260         G_FreeEntity( ent );
00261 #endif
00262 }
00263 
00264 /*QUAKED misc_model_static (1 0 0) (-16 -16 0) (16 16 16)
00265 "model"         arbitrary .md3 file to display
00266 "zoffset"       units to offset vertical culling position by, can be
00267                         negative or positive. This does not affect the actual
00268                         position of the model, only the culling position. Use
00269                         it for models with stupid origins that go below the
00270                         ground and whatnot.
00271 "modelscale" scale on all axis
00272 "modelscale_vec" scale difference axis
00273 
00274 loaded as a model in the renderer - does not take up precious
00275 bsp space!
00276 */
00277 void SP_misc_model_static(gentity_t *ent)
00278 {
00279         G_FreeEntity( ent );
00280 }
00281 
00282 /*QUAKED misc_G2model (1 0 0) (-16 -16 -16) (16 16 16)
00283 "model"         arbitrary .glm file to display
00284 */
00285 void SP_misc_G2model( gentity_t *ent ) {
00286 
00287 #if 0
00288         char name1[200] = "models/players/kyle/modelmp.glm";
00289         trap_G2API_InitGhoul2Model(&ent->s, name1, G_ModelIndex( name1 ), 0, 0, 0, 0);
00290         trap_G2API_SetBoneAnim(ent->s.ghoul2, 0, "model_root", 0, 12, BONE_ANIM_OVERRIDE_LOOP, 1.0f, level.time, -1, -1);
00291         ent->s.radius = 150;
00292 //      VectorSet (ent->r.mins, -16, -16, -16);
00293 //      VectorSet (ent->r.maxs, 16, 16, 16);
00294         trap_LinkEntity (ent);
00295 
00296         G_SetOrigin( ent, ent->s.origin );
00297         VectorCopy( ent->s.angles, ent->s.apos.trBase );
00298 #else
00299         G_FreeEntity( ent );
00300 #endif
00301 }
00302 
00303 //===========================================================
00304 
00305 void locateCamera( gentity_t *ent ) {
00306         vec3_t          dir;
00307         gentity_t       *target;
00308         gentity_t       *owner;
00309 
00310         owner = G_PickTarget( ent->target );
00311         if ( !owner ) {
00312                 G_Printf( "Couldn't find target for misc_partal_surface\n" );
00313                 G_FreeEntity( ent );
00314                 return;
00315         }
00316         ent->r.ownerNum = owner->s.number;
00317 
00318         // frame holds the rotate speed
00319         if ( owner->spawnflags & 1 ) {
00320                 ent->s.frame = 25;
00321         } else if ( owner->spawnflags & 2 ) {
00322                 ent->s.frame = 75;
00323         }
00324 
00325         // swing camera ?
00326         if ( owner->spawnflags & 4 ) {
00327                 // set to 0 for no rotation at all
00328                 ent->s.powerups = 0;
00329         }
00330         else {
00331                 ent->s.powerups = 1;
00332         }
00333 
00334         // clientNum holds the rotate offset
00335         ent->s.clientNum = owner->s.clientNum;
00336 
00337         VectorCopy( owner->s.origin, ent->s.origin2 );
00338 
00339         // see if the portal_camera has a target
00340         target = G_PickTarget( owner->target );
00341         if ( target ) {
00342                 VectorSubtract( target->s.origin, owner->s.origin, dir );
00343                 VectorNormalize( dir );
00344         } else {
00345                 G_SetMovedir( owner->s.angles, dir );
00346         }
00347 
00348         ent->s.eventParm = DirToByte( dir );
00349 }
00350 
00351 /*QUAKED misc_portal_surface (0 0 1) (-8 -8 -8) (8 8 8)
00352 The portal surface nearest this entity will show a view from the targeted misc_portal_camera, or a mirror view if untargeted.
00353 This must be within 64 world units of the surface!
00354 */
00355 void SP_misc_portal_surface(gentity_t *ent) {
00356         VectorClear( ent->r.mins );
00357         VectorClear( ent->r.maxs );
00358         trap_LinkEntity (ent);
00359 
00360         ent->r.svFlags = SVF_PORTAL;
00361         ent->s.eType = ET_PORTAL;
00362 
00363         if ( !ent->target ) {
00364                 VectorCopy( ent->s.origin, ent->s.origin2 );
00365         } else {
00366                 ent->think = locateCamera;
00367                 ent->nextthink = level.time + 100;
00368         }
00369 }
00370 
00371 /*QUAKED misc_portal_camera (0 0 1) (-8 -8 -8) (8 8 8) slowrotate fastrotate noswing
00372 The target for a misc_portal_director.  You can set either angles or target another entity to determine the direction of view.
00373 "roll" an angle modifier to orient the camera around the target vector;
00374 */
00375 void SP_misc_portal_camera(gentity_t *ent) {
00376         float   roll;
00377 
00378         VectorClear( ent->r.mins );
00379         VectorClear( ent->r.maxs );
00380         trap_LinkEntity (ent);
00381 
00382         G_SpawnFloat( "roll", "0", &roll );
00383 
00384         ent->s.clientNum = roll/360.0 * 256;
00385 }
00386 
00387 /*QUAKED misc_bsp (1 0 0) (-16 -16 -16) (16 16 16)
00388 "bspmodel"              arbitrary .bsp file to display
00389 */
00390 void SP_misc_bsp(gentity_t *ent) 
00391 {
00392         char    temp[MAX_QPATH];
00393         char    *out;
00394         float   newAngle;
00395         int             tempint;
00396 
00397         G_SpawnFloat( "angle", "0", &newAngle );
00398         if (newAngle != 0.0)
00399         {
00400                 ent->s.angles[1] = newAngle;
00401         }
00402         // don't support rotation any other way
00403         ent->s.angles[0] = 0.0;
00404         ent->s.angles[2] = 0.0;
00405         
00406         G_SpawnString("bspmodel", "", &out);
00407 
00408         ent->s.eFlags = EF_PERMANENT;
00409 
00410         // Mainly for debugging
00411         G_SpawnInt( "spacing", "0", &tempint);
00412         ent->s.time2 = tempint;
00413         G_SpawnInt( "flatten", "0", &tempint);
00414         ent->s.time = tempint;
00415 
00416         Com_sprintf(temp, MAX_QPATH, "#%s", out);
00417         trap_SetBrushModel( ent, temp );  // SV_SetBrushModel -- sets mins and maxs
00418         G_BSPIndex(temp);
00419 
00420         level.mNumBSPInstances++;
00421         Com_sprintf(temp, MAX_QPATH, "%d-", level.mNumBSPInstances);
00422         VectorCopy(ent->s.origin, level.mOriginAdjust);
00423         level.mRotationAdjust = ent->s.angles[1];
00424         level.mTargetAdjust = temp;
00425         //level.hasBspInstances = qtrue; //rww - also not referenced anywhere.
00426         level.mBSPInstanceDepth++;
00427         /*
00428         G_SpawnString("filter", "", &out);
00429         strcpy(level.mFilter, out);
00430         */
00431         G_SpawnString("teamfilter", "", &out);
00432         strcpy(level.mTeamFilter, out);
00433 
00434         VectorCopy( ent->s.origin, ent->s.pos.trBase );
00435         VectorCopy( ent->s.origin, ent->r.currentOrigin );
00436         VectorCopy( ent->s.angles, ent->s.apos.trBase );
00437         VectorCopy( ent->s.angles, ent->r.currentAngles );
00438 
00439         ent->s.eType = ET_MOVER;
00440 
00441         trap_LinkEntity (ent);
00442 
00443         trap_SetActiveSubBSP(ent->s.modelindex);
00444         G_SpawnEntitiesFromString(qtrue);
00445         trap_SetActiveSubBSP(-1);
00446 
00447         level.mBSPInstanceDepth--;
00448         //level.mFilter[0] = level.mTeamFilter[0] = 0;
00449         level.mTeamFilter[0] = 0;
00450 
00451         /*
00452         if ( g_debugRMG.integer )
00453         {
00454                 G_SpawnDebugCylinder ( ent->s.origin, ent->s.time2, &g_entities[0], 2000, COLOR_WHITE );
00455 
00456                 if ( ent->s.time )
00457                 {
00458                         G_SpawnDebugCylinder ( ent->s.origin, ent->s.time, &g_entities[0], 2000, COLOR_RED );
00459                 }
00460         }
00461         */
00462 }
00463 
00464 /*QUAKED terrain (1.0 1.0 1.0) ? NOVEHDMG
00465 
00466 NOVEHDMG - don't damage vehicles upon impact with this terrain
00467 
00468 Terrain entity
00469 It will stretch to the full height of the brush
00470 
00471 numPatches - integer number of patches to split the terrain brush into (default 200)
00472 terxels - integer number of terxels on a patch side (default 4) (2 <= count <= 8)
00473 seed - integer seed for random terrain generation (default 0)
00474 textureScale - float scale of texture (default 0.005)
00475 heightmap - name of heightmap data image to use, located in heightmaps/*.png. (must be PNG format)
00476 terrainDef - defines how the game textures the terrain (file is base/ext_data/rmg/*.terrain - default is grassyhills)
00477 instanceDef - defines which bsp instances appear
00478 miscentDef - defines which client models spawn on the terrain (file is base/ext_data/rmg/*.miscents)
00479 densityMap - how dense the client models are packed
00480 
00481 */
00482 void AddSpawnField(char *field, char *value);
00483 #define MAX_INSTANCE_TYPES              16
00484 void SP_terrain(gentity_t *ent) 
00485 {
00486         char                            temp[MAX_INFO_STRING];
00487         char                            final[MAX_QPATH];
00488         char                            seed[MAX_QPATH];
00489         char                            missionType[MAX_QPATH];
00490         //char                          soundSet[MAX_QPATH];
00491         int                                     shaderNum, i;
00492         char                            *value;
00493         int                                     terrainID;
00494 
00495         //Force it to 1 when there is terrain on the level.
00496         trap_Cvar_Set("RMG", "1");
00497         g_RMG.integer = 1;
00498 
00499         VectorClear (ent->s.angles);
00500         trap_SetBrushModel( ent, ent->model );
00501 
00502         // Get the shader from the top of the brush
00503 //      shaderNum = gi.CM_GetShaderNum(s.modelindex);
00504         shaderNum = 0;
00505 
00506         if (g_RMG.integer)
00507         {
00508                 /*
00509                 // Grab the default terrain file from the RMG cvar
00510                 trap_Cvar_VariableStringBuffer("RMG_terrain", temp, MAX_QPATH);
00511                 Com_sprintf(final, MAX_QPATH, "%s", temp);
00512                 AddSpawnField("terrainDef", temp);
00513  
00514                 trap_Cvar_VariableStringBuffer("RMG_instances", temp, MAX_QPATH);
00515                 Com_sprintf(final, MAX_QPATH, "%s", temp);
00516                 AddSpawnField("instanceDef", temp);
00517 
00518                 trap_Cvar_VariableStringBuffer("RMG_miscents", temp, MAX_QPATH);
00519                 Com_sprintf(final, MAX_QPATH, "%s", temp);
00520                 AddSpawnField("miscentDef", temp);
00521                 */
00522                 //rww - disabled for now, don't want cvar overrides.
00523 
00524                 trap_Cvar_VariableStringBuffer("RMG_seed", seed, MAX_QPATH);
00525                 trap_Cvar_VariableStringBuffer("RMG_mission", missionType, MAX_QPATH);
00526 
00527                 //rww - May want to implement these at some point.
00528                 //trap_Cvar_VariableStringBuffer("RMG_soundset", soundSet, MAX_QPATH);
00529                 //trap_SetConfigstring(CS_AMBIENT_SOUNDSETS, soundSet );
00530         }
00531 
00532         // Get info required for the common init
00533         temp[0] = 0;
00534         G_SpawnString("heightmap", "", &value);
00535         Info_SetValueForKey(temp, "heightMap", value);
00536 
00537         G_SpawnString("numpatches", "400", &value);
00538         Info_SetValueForKey(temp, "numPatches", va("%d", atoi(value)));
00539 
00540         G_SpawnString("terxels", "4", &value);
00541         Info_SetValueForKey(temp, "terxels", va("%d", atoi(value)));
00542 
00543         Info_SetValueForKey(temp, "seed", seed);
00544         Info_SetValueForKey(temp, "minx", va("%f", ent->r.mins[0]));
00545         Info_SetValueForKey(temp, "miny", va("%f", ent->r.mins[1]));
00546         Info_SetValueForKey(temp, "minz", va("%f", ent->r.mins[2]));
00547         Info_SetValueForKey(temp, "maxx", va("%f", ent->r.maxs[0]));
00548         Info_SetValueForKey(temp, "maxy", va("%f", ent->r.maxs[1]));
00549         Info_SetValueForKey(temp, "maxz", va("%f", ent->r.maxs[2]));
00550 
00551         Info_SetValueForKey(temp, "modelIndex", va("%d", ent->s.modelindex));
00552 
00553         G_SpawnString("terraindef", "grassyhills", &value);
00554         Info_SetValueForKey(temp, "terrainDef", value);
00555 
00556         G_SpawnString("instancedef", "", &value);
00557         Info_SetValueForKey(temp, "instanceDef", value);
00558 
00559         G_SpawnString("miscentdef", "", &value);
00560         Info_SetValueForKey(temp, "miscentDef", value);
00561 
00562         Info_SetValueForKey(temp, "missionType", missionType);
00563         
00564         for(i = 0; i < MAX_INSTANCE_TYPES; i++)
00565         {
00566                 trap_Cvar_VariableStringBuffer(va("RMG_instance%d", i), final, MAX_QPATH);
00567                 if(strlen(final))
00568                 {
00569                         Info_SetValueForKey(temp, va("inst%d", i), final);
00570                 }
00571         }
00572 
00573         // Set additional data required on the client only
00574         G_SpawnString("densitymap", "", &value);
00575         Info_SetValueForKey(temp, "densityMap", value);
00576 
00577         Info_SetValueForKey(temp, "shader", va("%d", shaderNum));
00578         G_SpawnString("texturescale", "0.005", &value);
00579         Info_SetValueForKey(temp, "texturescale", va("%f", atof(value)));
00580 
00581         // Initialise the common aspects of the terrain
00582         terrainID = trap_CM_RegisterTerrain(temp);
00583 //      SetCommon(common);
00584 
00585         Info_SetValueForKey(temp, "terrainId", va("%d", terrainID));
00586 
00587         // Let the entity know if it is random generated or not
00588 //      SetIsRandom(common->GetIsRandom());
00589 
00590         // Let the game remember everything
00591         //level.landScapes[terrainID] = ent; //rww - also not referenced
00592 
00593         // Send all the data down to the client
00594         trap_SetConfigstring(CS_TERRAINS + terrainID, temp);
00595 
00596         // Make sure the contents are properly set
00597         ent->r.contents = CONTENTS_TERRAIN;
00598         ent->r.svFlags = SVF_NOCLIENT;
00599         ent->s.eFlags = EF_PERMANENT;
00600         ent->s.eType = ET_TERRAIN;
00601 
00602         // Hook into the world so physics will work
00603         trap_LinkEntity(ent);
00604 
00605         // If running RMG then initialize the terrain and handle team skins
00606         if ( g_RMG.integer ) 
00607         {
00608                 trap_RMG_Init(terrainID);
00609 
00610                 /*
00611                 if ( level.gametypeData->teams )
00612                 {
00613                         char temp[MAX_QPATH];
00614 
00615                         // Red team change from RMG ?
00616                         trap_GetConfigstring ( CS_GAMETYPE_REDTEAM, temp, MAX_QPATH );
00617                         if ( Q_stricmp ( temp, level.gametypeTeam[TEAM_RED] ) )
00618                         {
00619                                 level.gametypeTeam[TEAM_RED] = trap_VM_LocalStringAlloc ( temp );
00620                         }
00621 
00622                         // Blue team change from RMG ?
00623                         trap_GetConfigstring ( CS_GAMETYPE_BLUETEAM, temp, MAX_QPATH );
00624                         if ( Q_stricmp ( temp, level.gametypeTeam[TEAM_BLUE] ) )
00625                         {
00626                                 level.gametypeTeam[TEAM_BLUE] = trap_VM_LocalStringAlloc ( temp );
00627                         }
00628                 }
00629                 */
00630         }
00631 }
00632 
00633 //rww - Called by skyportal entities. This will check through entities and flag them
00634 //as portal ents if they are in the same pvs as a skyportal entity and pass
00635 //a direct point trace check between origins. I really wanted to use an eFlag for
00636 //flagging portal entities, but too many entities like to reset their eFlags.
00637 //Note that this was not part of the original wolf sky portal stuff.
00638 void G_PortalifyEntities(gentity_t *ent)
00639 {
00640         int i = 0;
00641         gentity_t *scan = NULL;
00642 
00643         while (i < MAX_GENTITIES)
00644         {
00645                 scan = &g_entities[i];
00646 
00647                 if (scan && scan->inuse && scan->s.number != ent->s.number && trap_InPVS(ent->s.origin, scan->r.currentOrigin))
00648                 {
00649                         trace_t tr;
00650 
00651                         trap_Trace(&tr, ent->s.origin, vec3_origin, vec3_origin, scan->r.currentOrigin, ent->s.number, CONTENTS_SOLID);
00652 
00653                         if (tr.fraction == 1.0 || (tr.entityNum == scan->s.number && tr.entityNum != ENTITYNUM_NONE && tr.entityNum != ENTITYNUM_WORLD))
00654                         {
00655                                 if (!scan->client || scan->s.eType == ET_NPC)
00656                                 { //making a client a portal entity would be bad.
00657                                         scan->s.isPortalEnt = qtrue; //he's flagged now
00658                                 }
00659                         }
00660                 }
00661 
00662                 i++;
00663         }
00664 
00665         ent->think = G_FreeEntity; //the portal entity is no longer needed because its information is stored in a config string.
00666         ent->nextthink = level.time;
00667 }
00668 
00669 /*QUAKED misc_skyportal_orient (.6 .7 .7) (-8 -8 0) (8 8 16)
00670 point from which to orient the sky portal cam in relation
00671 to the regular view position.
00672 
00673 "modelscale"                    the scale at which to scale positions
00674 */
00675 void SP_misc_skyportal_orient (gentity_t *ent)
00676 {
00677         G_FreeEntity(ent);
00678 }
00679 
00680 
00681 /*QUAKED misc_skyportal (.6 .7 .7) (-8 -8 0) (8 8 16)
00682 "fov" for the skybox default is 80
00683 To have the portal sky fogged, enter any of the following values:
00684 "onlyfoghere" if non-0 allows you to set a global fog, but will only use that fog within this sky portal.
00685 
00686 Also note that entities in the same PVS and visible (via point trace) from this
00687 object will be flagged as portal entities. This means they will be sent and
00688 updated from the server for every client every update regardless of where
00689 they are, and they will essentially be added to the scene twice if the client
00690 is in the same PVS as them (only once otherwise, but still once no matter
00691 where the client is). In other words, don't go overboard with it or everything
00692 will explode.
00693 */
00694 void SP_misc_skyportal (gentity_t *ent)
00695 {
00696         char    *fov;
00697         vec3_t  fogv;   //----(SA)      
00698         int             fogn;   //----(SA)      
00699         int             fogf;   //----(SA)      
00700         int             isfog = 0;      // (SA)
00701 
00702         float   fov_x;
00703 
00704         G_SpawnString ("fov", "80", &fov);
00705         fov_x = atof (fov);
00706 
00707         isfog += G_SpawnVector ("fogcolor", "0 0 0", fogv);
00708         isfog += G_SpawnInt ("fognear", "0", &fogn);
00709         isfog += G_SpawnInt ("fogfar", "300", &fogf);
00710 
00711         trap_SetConfigstring( CS_SKYBOXORG, va("%.2f %.2f %.2f %.1f %i %.2f %.2f %.2f %i %i", ent->s.origin[0], ent->s.origin[1], ent->s.origin[2], fov_x, (int)isfog, fogv[0], fogv[1], fogv[2], fogn, fogf ) );
00712 
00713         ent->think = G_PortalifyEntities;
00714         ent->nextthink = level.time + 1050; //give it some time first so that all other entities are spawned.
00715 }
00716 
00717 /*QUAKED misc_holocron (0 0 1) (-8 -8 -8) (8 8 8)
00718 count   Set to type of holocron (based on force power value)
00719         HEAL = 0
00720         JUMP = 1
00721         SPEED = 2
00722         PUSH = 3
00723         PULL = 4
00724         TELEPATHY = 5
00725         GRIP = 6
00726         LIGHTNING = 7
00727         RAGE = 8
00728         PROTECT = 9
00729         ABSORB = 10
00730         TEAM HEAL = 11
00731         TEAM FORCE = 12
00732         DRAIN = 13
00733         SEE = 14
00734         SABERATTACK = 15
00735         SABERDEFEND = 16
00736         SABERTHROW = 17
00737 */
00738 
00739 /*char *holocronTypeModels[] = {
00740         "models/chunks/rock/rock_big.md3",//FP_HEAL,
00741         "models/chunks/rock/rock_big.md3",//FP_LEVITATION,
00742         "models/chunks/rock/rock_big.md3",//FP_SPEED,
00743         "models/chunks/rock/rock_big.md3",//FP_PUSH,
00744         "models/chunks/rock/rock_big.md3",//FP_PULL,
00745         "models/chunks/rock/rock_big.md3",//FP_TELEPATHY,
00746         "models/chunks/rock/rock_big.md3",//FP_GRIP,
00747         "models/chunks/rock/rock_big.md3",//FP_LIGHTNING,
00748         "models/chunks/rock/rock_big.md3",//FP_RAGE,
00749         "models/chunks/rock/rock_big.md3",//FP_PROTECT,
00750         "models/chunks/rock/rock_big.md3",//FP_ABSORB,
00751         "models/chunks/rock/rock_big.md3",//FP_TEAM_HEAL,
00752         "models/chunks/rock/rock_big.md3",//FP_TEAM_FORCE,
00753         "models/chunks/rock/rock_big.md3",//FP_DRAIN,
00754         "models/chunks/rock/rock_big.md3",//FP_SEE
00755         "models/chunks/rock/rock_big.md3",//FP_SABER_OFFENSE
00756         "models/chunks/rock/rock_big.md3",//FP_SABER_DEFENSE
00757         "models/chunks/rock/rock_big.md3"//FP_SABERTHROW
00758 };*/
00759 
00760 void HolocronRespawn(gentity_t *self)
00761 {
00762         self->s.modelindex = (self->count - 128);
00763 }
00764 
00765 void HolocronPopOut(gentity_t *self)
00766 {
00767         if (Q_irand(1, 10) < 5)
00768         {
00769                 self->s.pos.trDelta[0] = 150 + Q_irand(1, 100);
00770         }
00771         else
00772         {
00773                 self->s.pos.trDelta[0] = -150 - Q_irand(1, 100);
00774         }
00775         if (Q_irand(1, 10) < 5)
00776         {
00777                 self->s.pos.trDelta[1] = 150 + Q_irand(1, 100);
00778         }
00779         else
00780         {
00781                 self->s.pos.trDelta[1] = -150 - Q_irand(1, 100);
00782         }
00783         self->s.pos.trDelta[2] = 150 + Q_irand(1, 100);
00784 }
00785 
00786 void HolocronTouch(gentity_t *self, gentity_t *other, trace_t *trace)
00787 {
00788         int i = 0;
00789         int othercarrying = 0;
00790         float time_lowest = 0;
00791         int index_lowest = -1;
00792         int hasall = 1;
00793         int forceReselect = WP_NONE;
00794 
00795         if (trace)
00796         {
00797                 self->s.groundEntityNum = trace->entityNum;
00798         }
00799 
00800         if (!other || !other->client || other->health < 1)
00801         {
00802                 return;
00803         }
00804 
00805         if (!self->s.modelindex)
00806         {
00807                 return;
00808         }
00809 
00810         if (self->enemy)
00811         {
00812                 return;
00813         }
00814 
00815         if (other->client->ps.holocronsCarried[self->count])
00816         {
00817                 return;
00818         }
00819 
00820         if (other->client->ps.holocronCantTouch == self->s.number && other->client->ps.holocronCantTouchTime > level.time)
00821         {
00822                 return;
00823         }
00824 
00825         while (i < NUM_FORCE_POWERS)
00826         {
00827                 if (other->client->ps.holocronsCarried[i])
00828                 {
00829                         othercarrying++;
00830 
00831                         if (index_lowest == -1 || other->client->ps.holocronsCarried[i] < time_lowest)
00832                         {
00833                                 index_lowest = i;
00834                                 time_lowest = other->client->ps.holocronsCarried[i];
00835                         }
00836                 }
00837                 else if (i != self->count)
00838                 {
00839                         hasall = 0;
00840                 }
00841                 i++;
00842         }
00843 
00844         if (hasall)
00845         { //once we pick up this holocron we'll have all of them, so give us super special best prize!
00846                 //G_Printf("You deserve a pat on the back.\n");
00847         }
00848 
00849         if (!(other->client->ps.fd.forcePowersActive & (1 << other->client->ps.fd.forcePowerSelected)))
00850         { //If the player isn't using his currently selected force power, select this one
00851                 if (self->count != FP_SABER_OFFENSE && self->count != FP_SABER_DEFENSE && self->count != FP_SABERTHROW && self->count != FP_LEVITATION)
00852                 {
00853                         other->client->ps.fd.forcePowerSelected = self->count;
00854                 }
00855         }
00856 
00857         if (g_MaxHolocronCarry.integer && othercarrying >= g_MaxHolocronCarry.integer)
00858         { //make the oldest holocron carried by the player pop out to make room for this one
00859                 other->client->ps.holocronsCarried[index_lowest] = 0;
00860 
00861                 /*
00862                 if (index_lowest == FP_SABER_OFFENSE && !HasSetSaberOnly())
00863                 { //you lost your saberattack holocron, so no more saber for you
00864                         other->client->ps.stats[STAT_WEAPONS] |= (1 << WP_STUN_BATON);
00865                         other->client->ps.stats[STAT_WEAPONS] &= ~(1 << WP_SABER);
00866 
00867                         if (other->client->ps.weapon == WP_SABER)
00868                         {
00869                                 forceReselect = WP_SABER;
00870                         }
00871                 }
00872                 */
00873                 //NOTE: No longer valid as we are now always giving a force level 1 saber attack level in holocron
00874         }
00875 
00876         //G_Sound(other, CHAN_AUTO, G_SoundIndex("sound/weapons/w_pkup.wav"));
00877         G_AddEvent( other, EV_ITEM_PICKUP, self->s.number );
00878 
00879         other->client->ps.holocronsCarried[self->count] = level.time;
00880         self->s.modelindex = 0;
00881         self->enemy = other;
00882 
00883         self->pos2[0] = 1;
00884         self->pos2[1] = level.time + HOLOCRON_RESPAWN_TIME;
00885 
00886         /*
00887         if (self->count == FP_SABER_OFFENSE && !HasSetSaberOnly())
00888         { //player gets a saber
00889                 other->client->ps.stats[STAT_WEAPONS] |= (1 << WP_SABER);
00890                 other->client->ps.stats[STAT_WEAPONS] &= ~(1 << WP_STUN_BATON);
00891 
00892                 if (other->client->ps.weapon == WP_STUN_BATON)
00893                 {
00894                         forceReselect = WP_STUN_BATON;
00895                 }
00896         }
00897         */
00898 
00899         if (forceReselect != WP_NONE)
00900         {
00901                 G_AddEvent(other, EV_NOAMMO, forceReselect);
00902         }
00903 
00904         //G_Printf("DON'T TOUCH ME\n");
00905 }
00906 
00907 void HolocronThink(gentity_t *ent)
00908 {
00909         if (ent->pos2[0] && (!ent->enemy || !ent->enemy->client || ent->enemy->health < 1))
00910         {
00911                 if (ent->enemy && ent->enemy->client)
00912                 {
00913                         HolocronRespawn(ent);
00914                         VectorCopy(ent->enemy->client->ps.origin, ent->s.pos.trBase);
00915                         VectorCopy(ent->enemy->client->ps.origin, ent->s.origin);
00916                         VectorCopy(ent->enemy->client->ps.origin, ent->r.currentOrigin);
00917                         //copy to person carrying's origin before popping out of them
00918                         HolocronPopOut(ent);
00919                         ent->enemy->client->ps.holocronsCarried[ent->count] = 0;
00920                         ent->enemy = NULL;
00921                         
00922                         goto justthink;
00923                 }
00924         }
00925         else if (ent->pos2[0] && ent->enemy && ent->enemy->client)
00926         {
00927                 ent->pos2[1] = level.time + HOLOCRON_RESPAWN_TIME;
00928         }
00929 
00930         if (ent->enemy && ent->enemy->client)
00931         {
00932                 if (!ent->enemy->client->ps.holocronsCarried[ent->count])
00933                 {
00934                         ent->enemy->client->ps.holocronCantTouch = ent->s.number;
00935                         ent->enemy->client->ps.holocronCantTouchTime = level.time + 5000;
00936 
00937                         HolocronRespawn(ent);
00938                         VectorCopy(ent->enemy->client->ps.origin, ent->s.pos.trBase);
00939                         VectorCopy(ent->enemy->client->ps.origin, ent->s.origin);
00940                         VectorCopy(ent->enemy->client->ps.origin, ent->r.currentOrigin);
00941                         //copy to person carrying's origin before popping out of them
00942                         HolocronPopOut(ent);
00943                         ent->enemy = NULL;
00944 
00945                         goto justthink;
00946                 }
00947 
00948                 if (!ent->enemy->inuse || (ent->enemy->client && ent->enemy->client->ps.fallingToDeath))
00949                 {
00950                         if (ent->enemy->inuse && ent->enemy->client)
00951                         {
00952                                 ent->enemy->client->ps.holocronBits &= ~(1 << ent->count);
00953                                 ent->enemy->client->ps.holocronsCarried[ent->count] = 0;
00954                         }
00955                         ent->enemy = NULL;
00956                         HolocronRespawn(ent);
00957                         VectorCopy(ent->s.origin2, ent->s.pos.trBase);
00958                         VectorCopy(ent->s.origin2, ent->s.origin);
00959                         VectorCopy(ent->s.origin2, ent->r.currentOrigin);
00960 
00961                         ent->s.pos.trTime = level.time;
00962 
00963                         ent->pos2[0] = 0;
00964 
00965                         trap_LinkEntity(ent);
00966 
00967                         goto justthink;
00968                 }
00969         }
00970 
00971         if (ent->pos2[0] && ent->pos2[1] < level.time)
00972         { //isn't in original place and has been there for (HOLOCRON_RESPAWN_TIME) seconds without being picked up, so respawn
00973                 VectorCopy(ent->s.origin2, ent->s.pos.trBase);
00974                 VectorCopy(ent->s.origin2, ent->s.origin);
00975                 VectorCopy(ent->s.origin2, ent->r.currentOrigin);
00976 
00977                 ent->s.pos.trTime = level.time;
00978 
00979                 ent->pos2[0] = 0;
00980 
00981                 trap_LinkEntity(ent);
00982         }
00983 
00984 justthink:
00985         ent->nextthink = level.time + 50;
00986 
00987         if (ent->s.pos.trDelta[0] || ent->s.pos.trDelta[1] || ent->s.pos.trDelta[2])
00988         {
00989                 G_RunObject(ent);
00990         }
00991 }
00992 
00993 void SP_misc_holocron(gentity_t *ent)
00994 {
00995         vec3_t dest;
00996         trace_t tr;
00997 
00998         if (g_gametype.integer != GT_HOLOCRON)
00999         {
01000                 G_FreeEntity(ent);
01001                 return;
01002         }
01003 
01004         if (HasSetSaberOnly())
01005         {
01006                 if (ent->count == FP_SABER_OFFENSE ||
01007                         ent->count == FP_SABER_DEFENSE ||
01008                         ent->count == FP_SABERTHROW)
01009                 { //having saber holocrons in saber only mode is pointless
01010                         G_FreeEntity(ent);
01011                         return;
01012                 }
01013         }
01014 
01015         ent->s.isJediMaster = qtrue;
01016 
01017         VectorSet( ent->r.maxs, 8, 8, 8 );
01018         VectorSet( ent->r.mins, -8, -8, -8 );
01019 
01020         ent->s.origin[2] += 0.1;
01021         ent->r.maxs[2] -= 0.1;
01022 
01023         VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 );
01024         trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );
01025         if ( tr.startsolid )
01026         {
01027                 G_Printf ("SP_misc_holocron: misc_holocron startsolid at %s\n", vtos(ent->s.origin));
01028                 G_FreeEntity( ent );
01029                 return;
01030         }
01031 
01032         //add the 0.1 back after the trace
01033         ent->r.maxs[2] += 0.1;
01034 
01035         // allow to ride movers
01036 //      ent->s.groundEntityNum = tr.entityNum;
01037 
01038         G_SetOrigin( ent, tr.endpos );
01039 
01040         if (ent->count < 0)
01041         {
01042                 ent->count = 0;
01043         }
01044 
01045         if (ent->count >= NUM_FORCE_POWERS)
01046         {
01047                 ent->count = NUM_FORCE_POWERS-1;
01048         }
01049 /*
01050         if (g_forcePowerDisable.integer &&
01051                 (g_forcePowerDisable.integer & (1 << ent->count)))
01052         {
01053                 G_FreeEntity(ent);
01054                 return;
01055         }
01056 */
01057         //No longer doing this, causing too many complaints about accidentally setting no force powers at all
01058         //and starting a holocron game (making it basically just FFA)
01059 
01060         ent->enemy = NULL;
01061 
01062         ent->flags = FL_BOUNCE_HALF;
01063 
01064         ent->s.modelindex = (ent->count - 128);//G_ModelIndex(holocronTypeModels[ent->count]);
01065         ent->s.eType = ET_HOLOCRON;
01066         ent->s.pos.trType = TR_GRAVITY;
01067         ent->s.pos.trTime = level.time;
01068 
01069         ent->r.contents = CONTENTS_TRIGGER;
01070         ent->clipmask = MASK_SOLID;
01071 
01072         ent->s.trickedentindex4 = ent->count;
01073 
01074         if (forcePowerDarkLight[ent->count] == FORCE_DARKSIDE)
01075         {
01076                 ent->s.trickedentindex3 = 1;
01077         }
01078         else if (forcePowerDarkLight[ent->count] == FORCE_LIGHTSIDE)
01079         {
01080                 ent->s.trickedentindex3 = 2;
01081         }
01082         else
01083         {
01084                 ent->s.trickedentindex3 = 3;
01085         }
01086 
01087         ent->physicsObject = qtrue;
01088 
01089         VectorCopy(ent->s.pos.trBase, ent->s.origin2); //remember the spawn spot
01090 
01091         ent->touch = HolocronTouch;
01092 
01093         trap_LinkEntity(ent);
01094 
01095         ent->think = HolocronThink;
01096         ent->nextthink = level.time + 50;
01097 }
01098 
01099 /*
01100 ======================================================================
01101 
01102   SHOOTERS
01103 
01104 ======================================================================
01105 */
01106 
01107 void Use_Shooter( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
01108         vec3_t          dir;
01109         float           deg;
01110         vec3_t          up, right;
01111 
01112         // see if we have a target
01113         if ( ent->enemy ) {
01114                 VectorSubtract( ent->enemy->r.currentOrigin, ent->s.origin, dir );
01115                 VectorNormalize( dir );
01116         } else {
01117                 VectorCopy( ent->movedir, dir );
01118         }
01119 
01120         // randomize a bit
01121         PerpendicularVector( up, dir );
01122         CrossProduct( up, dir, right );
01123 
01124         deg = crandom() * ent->random;
01125         VectorMA( dir, deg, up, dir );
01126 
01127         deg = crandom() * ent->random;
01128         VectorMA( dir, deg, right, dir );
01129 
01130         VectorNormalize( dir );
01131 
01132         switch ( ent->s.weapon ) {
01133         case WP_BLASTER:
01134                 WP_FireBlasterMissile( ent, ent->s.origin, dir, qfalse );
01135                 break;
01136         }
01137 
01138         G_AddEvent( ent, EV_FIRE_WEAPON, 0 );
01139 }
01140 
01141 
01142 static void InitShooter_Finish( gentity_t *ent ) {
01143         ent->enemy = G_PickTarget( ent->target );
01144         ent->think = 0;
01145         ent->nextthink = 0;
01146 }
01147 
01148 void InitShooter( gentity_t *ent, int weapon ) {
01149         ent->use = Use_Shooter;
01150         ent->s.weapon = weapon;
01151 
01152         RegisterItem( BG_FindItemForWeapon( weapon ) );
01153 
01154         G_SetMovedir( ent->s.angles, ent->movedir );
01155 
01156         if ( !ent->random ) {
01157                 ent->random = 1.0;
01158         }
01159         ent->random = sin( M_PI * ent->random / 180 );
01160         // target might be a moving object, so we can't set movedir for it
01161         if ( ent->target ) {
01162                 ent->think = InitShooter_Finish;
01163                 ent->nextthink = level.time + 500;
01164         }
01165         trap_LinkEntity( ent );
01166 }
01167 
01168 /*QUAKED shooter_blaster (1 0 0) (-16 -16 -16) (16 16 16)
01169 Fires at either the target or the current direction.
01170 "random" is the number of degrees of deviance from the taget. (1.0 default)
01171 */
01172 void SP_shooter_blaster( gentity_t *ent ) {
01173         InitShooter( ent, WP_BLASTER);
01174 }
01175 
01176 void check_recharge(gentity_t *ent)
01177 {
01178         if (ent->fly_sound_debounce_time < level.time ||
01179                 !ent->activator ||
01180                 !ent->activator->client ||
01181                 !(ent->activator->client->pers.cmd.buttons & BUTTON_USE))
01182         {
01183                 if (ent->activator)
01184                 {
01185                         G_Sound(ent, CHAN_AUTO, ent->genericValue7);
01186                 }
01187                 ent->s.loopSound = 0;
01188                 ent->s.loopIsSoundset = qfalse;
01189                 ent->activator = NULL;
01190                 ent->fly_sound_debounce_time = 0;
01191         }
01192         
01193         if (!ent->activator)
01194         { //don't recharge during use
01195                 if (ent->genericValue8 < level.time)
01196                 {
01197                         if (ent->count < ent->genericValue4)
01198                         {
01199                                 ent->count++;
01200                         }
01201                         ent->genericValue8 = level.time + ent->genericValue5;
01202                 }
01203         }
01204         ent->s.