//ported from SP void G_DynamicMusicUpdate( void ) { int battle = 0; vec3_t center; qboolean clearLOS = qfalse; int distSq, radius = 2048; int i, e, x; gentity_t *ent; int entityList[MAX_GENTITIES]; int entTeam; vec3_t mins, maxs; int numListedEntities; gentity_t *player; if( DMSData.dmDebounceTime >= 0 && DMSData.dmDebounceTime < level.time ) {//debounce over, reset to default music DMSData.dmDebounceTime = -1; DMSData.dmState = DM_AUTO; DMSData.olddmState = DM_AUTO; } if ( DMSData.dmState == DM_DEATH) {//Play the death music if(DMSData.olddmState != DM_DEATH) {//haven't set the state yet trap_SetConfigstring( CS_MUSIC, DMS_DEATH_MUSIC ); DMSData.olddmState = DM_DEATH; DMSData.dmDebounceTime = level.time + DMS_DEATH_MUSIC_TIME; } return; } if ( DMSData.dmState == DM_BOSS ) { if(DMSData.olddmState != DM_BOSS) { trap_SetConfigstring( CS_MUSIC, DMSData.bossMusic.fileName ); DMSData.olddmState = DM_BOSS; } return; } if ( DMSData.dmState == DM_SILENCE ) {//turn off the music if(DMSData.olddmState != DM_SILENCE) { trap_SetConfigstring( CS_MUSIC, "" ); DMSData.olddmState = DM_SILENCE; } return; } if ( DMSData.dmBeatTime > level.time ) {//not on a beat return; } DMSData.dmBeatTime = level.time + 1000;//1 second beats for(i = 0; i < MAX_CLIENTS; i++) { player = &g_entities[i]; //check to make sure this player is valid if(!player || !player->inuse || player->client->pers.connected == CON_DISCONNECTED || player->client->sess.sessionTeam == TEAM_SPECTATOR) { continue; } //enemy-based VectorCopy( player->r.currentOrigin, center ); for ( x = 0 ; x < 3 ; x++ ) { mins[x] = center[x] - radius; maxs[x] = center[x] + radius; } numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for ( e = 0 ; e < numListedEntities ; e++ ) { ent = &g_entities[entityList[e]]; if ( !ent || !ent->inuse ) { continue; } if ( !ent->client || !ent->NPC ) { if ( ent->classname && (!Q_stricmp( "PAS", ent->classname )||!Q_stricmp( "misc_turret", ent->classname )) ) {//a turret entTeam = ent->teamnodmg; //entTeam = ent->noDamageTeam; } else { continue; } } else {//an NPC entTeam = ent->client->playerTeam; } if ( entTeam == player->client->playerTeam ) {//ally continue; } if ( entTeam == TEAM_FREE && (!ent->enemy || !ent->enemy->client || ent->enemy->client->playerTeam != player->client->playerTeam) ) {//a droid that is not mad at me or my allies continue; } if ( !trap_InPVS( player->r.currentOrigin, ent->r.currentOrigin ) ) {//not potentially visible continue; } if ( ent->client && ent->s.weapon == WP_NONE ) {//they don't have a weapon... FIXME: only do this for droids? continue; } clearLOS = qfalse; if ( (ent->enemy==player&&(!ent->NPC||ent->NPC->confusionTime<level.time)) || (ent->client&&ent->client->ps.weaponTime) || (!ent->client&&ent->attackDebounceTime>level.time)) {//mad if ( ent->health > 0 ) {//alive //FIXME: do I really need this check? if ( ent->s.weapon == WP_SABER && ent->client && ent->client->ps.saberHolstered == 2 && ent->enemy != player ) {//a Jedi who has not yet gotten mad at me continue; } if ( ent->NPC && ent->NPC->behaviorState == BS_CINEMATIC ) {//they're not actually going to do anything about being mad at me... continue; } //okay, they're in my PVS, but how close are they? Are they actively attacking me? if ( !ent->client && ent->s.weapon == WP_TURRET && ent->fly_sound_debounce_time && ent->fly_sound_debounce_time - level.time < 10000 ) {//a turret that shot at me less than ten seconds ago } else if( ent->NPC && level.time < ent->NPC->shotTime ) {//npc that fired recently } /* changed from SP else if ( ent->client && ent->client->ps.lastShotTime && ent->client->ps.lastShotTime - level.time < 10000 ) {//an NPC that shot at me less than ten seconds ago } */ else {//not actively attacking me lately, see how far away they are distSq = DistanceSquared( ent->r.currentOrigin, player->r.currentOrigin ); if ( distSq > 4194304 ) {//> 2048 away continue; } else if ( distSq > 1048576 ) {//> 1024 away clearLOS = G_ClearLOS3( player, player->client->renderInfo.eyePoint, ent ); if ( clearLOS == qfalse ) {//No LOS continue; } } } battle++; } } } if ( !battle ) {//no active enemies, but look for missiles, shot impacts, etc... //[CoOp] int alert = G_CheckAlertEvents( player, qtrue, qtrue, 1024, 1024, -1, qfalse, AEL_SUSPICIOUS, qfalse ); //int alert = G_CheckAlertEvents( player, qtrue, qtrue, 1024, 1024, -1, qfalse, AEL_SUSPICIOUS ); //[/CoOp] if ( alert != -1 ) {//FIXME: maybe tripwires and other FIXED things need their own sound, some kind of danger/caution theme if ( G_CheckForDanger( player, alert ) ) {//found danger near by battle = 1; } } } } if ( battle ) { SetDMSState(DM_ACTION); } else {//switch to explore SetDMSState(DM_EXPLORE); } if(DMSData.dmState != DMSData.olddmState) {//switching between action and explore modes TransitionBetweenState(); } }
/* static void G_DynamicMusicUpdate( usercmd_t *ucmd ) FIXME: can we merge any of this with the G_ChooseLookEnemy stuff? */ static void G_DynamicMusicUpdate( void ) { gentity_t *ent; gentity_t *entityList[MAX_GENTITIES]; int numListedEntities; vec3_t mins, maxs; int i, e; int distSq, radius = 2048; vec3_t center; int danger = 0; int battle = 0; int entTeam; qboolean dangerNear = qfalse; qboolean suspicious = qfalse; qboolean LOScalced = qfalse, clearLOS = qfalse; //FIXME: intro and/or other cues? (one-shot music sounds) //loops //player-based if ( !player ) {//WTF? player = &g_entities[0]; return; } if ( !player->client || player->client->pers.teamState.state != TEAM_ACTIVE || level.time - player->client->pers.enterTime < 100 ) {//player hasn't spawned yet return; } if ( player->health <= 0 && player->max_health > 0 ) {//defeat music if ( level.dmState != DM_DEATH ) { level.dmState = DM_DEATH; } } if ( level.dmState == DM_DEATH ) { gi.SetConfigstring( CS_DYNAMIC_MUSIC_STATE, "death" ); return; } if ( level.dmState == DM_BOSS ) { gi.SetConfigstring( CS_DYNAMIC_MUSIC_STATE, "boss" ); return; } if ( level.dmState == DM_SILENCE ) { gi.SetConfigstring( CS_DYNAMIC_MUSIC_STATE, "silence" ); return; } if ( level.dmBeatTime > level.time ) {//not on a beat return; } level.dmBeatTime = level.time + 1000;//1 second beats if ( player->health <= 20 ) { danger = 1; } //enemy-based VectorCopy( player->currentOrigin, center ); for ( i = 0 ; i < 3 ; i++ ) { mins[i] = center[i] - radius; maxs[i] = center[i] + radius; } numListedEntities = gi.EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for ( e = 0 ; e < numListedEntities ; e++ ) { ent = entityList[ e ]; if ( !ent || !ent->inuse ) { continue; } if ( !ent->client || !ent->NPC ) { if ( ent->classname && (!Q_stricmp( "PAS", ent->classname )||!Q_stricmp( "misc_turret", ent->classname )) ) {//a turret entTeam = ent->noDamageTeam; } else { continue; } } else {//an NPC entTeam = ent->client->playerTeam; } if ( entTeam == player->client->playerTeam ) {//ally continue; } if ( entTeam == TEAM_NEUTRAL && (!ent->enemy || !ent->enemy->client || ent->enemy->client->playerTeam != player->client->playerTeam) ) {//a droid that is not mad at me or my allies continue; } if ( !gi.inPVS( player->currentOrigin, ent->currentOrigin ) ) {//not potentially visible continue; } if ( ent->client && ent->s.weapon == WP_NONE ) {//they don't have a weapon... FIXME: only do this for droids? continue; } LOScalced = clearLOS = qfalse; if ( (ent->enemy==player&&(!ent->NPC||ent->NPC->confusionTime<level.time)) || (ent->client&&ent->client->ps.weaponTime) || (!ent->client&&ent->attackDebounceTime>level.time)) {//mad if ( ent->health > 0 ) {//alive //FIXME: do I really need this check? if ( ent->s.weapon == WP_SABER && ent->client && !ent->client->ps.saberActive && ent->enemy != player ) {//a Jedi who has not yet gotten made at me continue; } if ( ent->NPC && ent->NPC->behaviorState == BS_CINEMATIC ) {//they're not actually going to do anything about being mad at me... continue; } //okay, they're in my PVS, but how close are they? Are they actively attacking me? if ( !ent->client && ent->s.weapon == WP_TURRET && ent->fly_sound_debounce_time && ent->fly_sound_debounce_time - level.time < 10000 ) {//a turret that shot at me less than ten seconds ago } else if ( ent->client && ent->client->ps.lastShotTime && ent->client->ps.lastShotTime - level.time < 10000 ) {//an NPC that shot at me less than ten seconds ago } else {//not actively attacking me lately, see how far away they are distSq = DistanceSquared( ent->currentOrigin, player->currentOrigin ); if ( distSq > 4194304/*2048*2048*/ ) {//> 2048 away continue; } else if ( distSq > 1048576/*1024*1024*/ ) {//> 1024 away clearLOS = G_ClearLOS( player, player->client->renderInfo.eyePoint, ent ); LOScalced = qtrue; if ( clearLOS == qfalse ) {//No LOS continue; } } } battle++; } } if ( level.dmState == DM_EXPLORE ) {//only do these visibility checks if you're still in exploration mode if ( !InFront( ent->currentOrigin, player->currentOrigin, player->client->ps.viewangles, 0.0f) ) {//not in front continue; } if ( !LOScalced ) { clearLOS = G_ClearLOS( player, player->client->renderInfo.eyePoint, ent ); } if ( !clearLOS ) {//can't see them directly continue; } } if ( ent->health <= 0 ) {//dead if ( !ent->client || level.time - ent->s.time > 10000 ) {//corpse has been dead for more than 10 seconds //FIXME: coming across corpses should cause danger sounds too? continue; } } //we see enemies and/or corpses danger++; } if ( !battle ) {//no active enemies, but look for missiles, shot impacts, etc... int alert = G_CheckAlertEvents( player, qtrue, qtrue, 1024, 1024, -1, qfalse, AEL_SUSPICIOUS ); if ( alert != -1 ) {//FIXME: maybe tripwires and other FIXED things need their own sound, some kind of danger/caution theme if ( G_CheckForDanger( player, alert ) ) {//found danger near by danger++; battle = 1; } else if ( level.alertEvents[alert].owner && (level.alertEvents[alert].owner == player->enemy || (level.alertEvents[alert].owner->client && level.alertEvents[alert].owner->client->playerTeam == player->client->enemyTeam) ) ) {//NPC on enemy team of player made some noise switch ( level.alertEvents[alert].level ) { case AEL_DISCOVERED: dangerNear = qtrue; break; case AEL_SUSPICIOUS: suspicious = qtrue; break; case AEL_MINOR: //distraction = qtrue; break; } } } } if ( battle ) {//battle - this can interrupt level.dmDebounceTime of lower intensity levels //play battle if ( level.dmState != DM_ACTION ) { gi.SetConfigstring( CS_DYNAMIC_MUSIC_STATE, "action" ); } level.dmState = DM_ACTION; if ( battle > 5 ) { //level.dmDebounceTime = level.time + 8000;//don't change again for 5 seconds } else { //level.dmDebounceTime = level.time + 3000 + 1000*battle; } } else { if ( level.dmDebounceTime > level.time ) {//not ready to switch yet return; } else {//at least 1 second (for beats) //level.dmDebounceTime = level.time + 1000;//FIXME: define beat time? } /* if ( danger || dangerNear ) {//danger //stay on whatever we were on, action or exploration if ( !danger ) {//minimum danger = 1; } if ( danger > 3 ) { level.dmDebounceTime = level.time + 5000; } else { level.dmDebounceTime = level.time + 2000 + 1000*danger; } } else */ {//still nothing dangerous going on if ( level.dmState != DM_EXPLORE ) {//just went to explore, hold it for a couple seconds at least //level.dmDebounceTime = level.time + 2000; gi.SetConfigstring( CS_DYNAMIC_MUSIC_STATE, "explore" ); } level.dmState = DM_EXPLORE; //FIXME: look for interest points and play "mysterious" music instead of exploration? //FIXME: suspicious and distraction sounds should play some cue or change music in a subtle way? //play exploration } //FIXME: when do we go to silence? } }
qboolean NPC_CheckForDanger( int alertEvent ) {//FIXME: more bStates need to call this? return G_CheckForDanger( NPC, alertEvent ); }