// RAVEN BEGIN
// bdube: note that this file is no longer merged with legacy engine updates
//
// MERGE_DATE 07/07/2004




#include "Game_local.h"

#if defined(_XENON) && (defined(_PROFILE) || defined(_DEBUG))
#include "../sys/xenon/ProfilingSupport.h"
#endif

const int IMPULSE_DELAY = 150;
/*
==============
idPlayerView::idPlayerView
==============
*/
idPlayerView::idPlayerView() {
	memset( screenBlobs, 0, sizeof( screenBlobs ) );
	memset( &view, 0, sizeof( view ) );
	player = NULL;
	dvMaterial = NULL;
	dvMaterialBlend = NULL;
	tunnelMaterial = NULL;
	armorMaterial = NULL;
	bloodSprayMaterial = NULL;
	bfgVision = false;
	dvFinishTime = 0;
	tvFinishTime = 0;
	tvStartTime = 0;
	kickFinishTime = 0;
	kickAngles.Zero();
	lastDamageTime = 0.0f;
	fadeTime = 0;
	fadeRate = 0.0;
	fadeFromColor.Zero();
	fadeToColor.Zero();
	fadeColor.Zero();

	ClearEffects();

	dvScale = 1.0f;
	shakeScale = 1.0f;
	tvScale = 1.0f;
}

/*
==============
idPlayerView::Save
==============
*/
void idPlayerView::Save( idSaveGame *savefile ) const {
	int i;
	const screenBlob_t *blob;

	blob = &screenBlobs[ 0 ];
	for( i = 0; i < MAX_SCREEN_BLOBS; i++, blob++ ) {
		savefile->WriteMaterial( blob->material );
		savefile->WriteFloat( blob->x );
		savefile->WriteFloat( blob->y );
		savefile->WriteFloat( blob->w );
		savefile->WriteFloat( blob->h );
		savefile->WriteFloat( blob->s1 );
		savefile->WriteFloat( blob->t1 );
		savefile->WriteFloat( blob->s2 );
		savefile->WriteFloat( blob->t2 );
		savefile->WriteInt( blob->finishTime );
		savefile->WriteInt( blob->startFadeTime );
		savefile->WriteFloat( blob->driftAmount );
	}

	savefile->WriteInt( dvFinishTime );
	savefile->WriteMaterial( dvMaterial );

	savefile->WriteMaterial( dvMaterialBlend );	// cnicholson: Added unsaved var

	savefile->WriteFloat ( dvScale );

	savefile->WriteInt( kickFinishTime );
	savefile->WriteAngles( kickAngles );

	savefile->WriteBool( bfgVision );

	savefile->WriteMaterial( tunnelMaterial );
	savefile->WriteMaterial( armorMaterial );

	savefile->WriteMaterial( bloodSprayMaterial );

	savefile->WriteFloat( lastDamageTime );

	savefile->WriteFloat( shakeFinishTime );
	savefile->WriteFloat( shakeScale );
	savefile->WriteFloat( tvScale );
	savefile->WriteInt( tvFinishTime );
	savefile->WriteInt( tvStartTime );

	savefile->WriteVec4( fadeColor );
	savefile->WriteVec4( fadeToColor );
	savefile->WriteVec4( fadeFromColor );
	savefile->WriteFloat( fadeRate );
	savefile->WriteInt( fadeTime );

	savefile->WriteObject( player );
	savefile->WriteRenderView( view );
}

/*
==============
idPlayerView::Restore
==============
*/
void idPlayerView::Restore( idRestoreGame *savefile ) {
	int i;
	screenBlob_t *blob;

	blob = &screenBlobs[ 0 ];
	for( i = 0; i < MAX_SCREEN_BLOBS; i++, blob++ ) {
		savefile->ReadMaterial( blob->material );
		savefile->ReadFloat( blob->x );
		savefile->ReadFloat( blob->y );
		savefile->ReadFloat( blob->w );
		savefile->ReadFloat( blob->h );
		savefile->ReadFloat( blob->s1 );
		savefile->ReadFloat( blob->t1 );
		savefile->ReadFloat( blob->s2 );
		savefile->ReadFloat( blob->t2 );
		savefile->ReadInt( blob->finishTime );
		savefile->ReadInt( blob->startFadeTime );
		savefile->ReadFloat( blob->driftAmount );
	}

	savefile->ReadInt( dvFinishTime );
	savefile->ReadMaterial( dvMaterial );

	savefile->ReadMaterial( dvMaterialBlend );	// cnicholson: Added unrestored var

	savefile->ReadFloat( dvScale );

	savefile->ReadInt( kickFinishTime );
	savefile->ReadAngles( kickAngles );			

	savefile->ReadBool( bfgVision );

	savefile->ReadMaterial( tunnelMaterial );
	savefile->ReadMaterial( armorMaterial );

	savefile->ReadMaterial( bloodSprayMaterial );

	savefile->ReadFloat( lastDamageTime );

	savefile->ReadFloat( shakeFinishTime );
	savefile->ReadFloat( shakeScale );
	savefile->ReadFloat( tvScale );
	savefile->ReadInt( tvFinishTime );
	savefile->ReadInt ( tvStartTime );

	savefile->ReadVec4( fadeColor );
	savefile->ReadVec4( fadeToColor );
	savefile->ReadVec4( fadeFromColor );
	savefile->ReadFloat( fadeRate );
	savefile->ReadInt( fadeTime );

	savefile->ReadObject( reinterpret_cast<idClass *&>( player ) );
	savefile->ReadRenderView( view );
}

/*
==============
idPlayerView::SetPlayerEntity
==============
*/
void idPlayerView::SetPlayerEntity( idPlayer *playerEnt ) {
	player = playerEnt;

	const idDict* dict = NULL;
	if( !playerEnt ) {
		return;
	}

	const char *playerViewDef = playerEnt->spawnArgs.GetString( "def_playerView" );
	dict = gameLocal.FindEntityDefDict( playerViewDef, false );
	if ( !dict ) {
		dict = gameLocal.FindEntityDefDict( "playerView_marine", false );
	}

	if( dict ) {
		dvMaterial = declManager->FindMaterial( dict->GetString("mtr_doubleVision") );
		dvMaterialBlend = declManager->FindMaterial( dict->GetString("mtr_doubleVisionBlend") );
		tunnelMaterial = declManager->FindMaterial( dict->GetString("mtr_tunnel") );
		armorMaterial = declManager->FindMaterial( dict->GetString("mtr_armourEffect") );
		bloodSprayMaterial = declManager->FindMaterial( dict->GetString("mtr_bloodspray") );
		lagoMaterial = declManager->FindMaterial( LAGO_MATERIAL, false );
	}
}

/*
==============
idPlayerView::ClearEffects
==============
*/
void idPlayerView::ClearEffects() {
	lastDamageTime = MS2SEC( gameLocal.time - 99999 );

	dvFinishTime = ( gameLocal.time - 99999 );
	tvFinishTime = ( gameLocal.time - 99999 );
	tvStartTime = ( gameLocal.time - 99999 );
	kickFinishTime = ( gameLocal.time - 99999 );
	shakeFinishTime = gameLocal.time; 

	for ( int i = 0 ; i < MAX_SCREEN_BLOBS ; i++ ) {
		screenBlobs[i].finishTime = gameLocal.time;
	}

	fadeTime = 0;
	bfgVision = false;
}

/*
==============
idPlayerView::GetScreenBlob
==============
*/
screenBlob_t *idPlayerView::GetScreenBlob() {
	screenBlob_t	*oldest = &screenBlobs[0];

	for ( int i = 1 ; i < MAX_SCREEN_BLOBS ; i++ ) {
		if ( screenBlobs[i].finishTime < oldest->finishTime ) {
			oldest = &screenBlobs[i];
		}
	}
	return oldest;
}

/*
==============
idPlayerView::DamageImpulse

LocalKickDir is the direction of force in the player's coordinate system,
which will determine the head kick direction
==============
*/
// RAVEN BEGIN
// jnewquist: Controller rumble
void idPlayerView::DamageImpulse( idVec3 localKickDir, const idDict *damageDef, int damage, const char *damageDefName ) {

	//
	// double vision effect
	//
	float tvTime = damageDef->GetFloat( "tv_time" );
	if ( tvTime ) {
		tvStartTime = gameLocal.time;
		tvFinishTime = gameLocal.time + tvTime;		
		damageDef->GetFloat ( "tv_scale", "1", tvScale );
	}

	if ( lastDamageTime > 0.0f && SEC2MS( lastDamageTime ) + IMPULSE_DELAY > gameLocal.time ) {
		// keep shotgun from obliterating the view
		return;
	}

	//
	// double vision effect
	//
	float dvTime = damageDef->GetFloat( "dv_time" );
	if ( dvTime ) {
		dvFinishTime = gameLocal.time + (g_dvTime.GetFloat() * dvTime);
		damageDef->GetFloat ( "dv_scale", "1", dvScale );
	}

	//
	// head angle kick
	//
	const float	modifierScale = 0.25f;
	const float inverseModifier = ( 1.0f - modifierScale );

	float		modifier = idMath::ClampFloat( 0.0f, inverseModifier, damage / 100.0f * inverseModifier ) + modifierScale;
	float	kickTime = damageDef->GetFloat( "kick_time" );

	if ( kickTime ) {
		kickFinishTime = gameLocal.time + g_kickTime.GetFloat() * kickTime;

		// forward / back kick will pitch view
		kickAngles[0] = localKickDir[0];

		// side kick will yaw view
		kickAngles[1] = localKickDir[1]*0.5f;

		// up / down kick will pitch view
		kickAngles[0] += localKickDir[2];

		// roll will come from  side
		kickAngles[2] = localKickDir[1];

		float kickAmplitude = damageDef->GetFloat( "kick_amplitude" );
		if ( kickAmplitude ) {
			kickAngles *= kickAmplitude;
		}

		if ( modifier < kickAmplitude ) {
			modifier = kickAmplitude;
		}
	}
	else {
		kickTime = 500;
	}

	//
	// screen blob
	//
	bool meleeDamageDef = ( damageDef->FindKey( "snd_hit" ) != NULL ) || ( damageDef->FindKey( "snd_miss" ) != NULL );
	if ( !meleeDamageDef && damageDefName != NULL ) {
		meleeDamageDef = idStr::Icmpn( damageDefName, "melee_", 6 ) == 0;
	}

	const char *configuredBlobMaterial = damageDef->GetString( "mtr_blob" );
	if ( !configuredBlobMaterial[0] ) {
		configuredBlobMaterial = damageDef->GetString( "blob_material" );
	}
	const float configuredBlobX = damageDef->GetFloat( "blob_x" );
	const float configuredBlobY = damageDef->GetFloat( "blob_y" );
	const float configuredBlobOffsetX = damageDef->GetFloat( "blob_offset_x" );
	const float configuredBlobOffsetY = damageDef->GetFloat( "blob_offset_y" );
	const float configuredBlobWidth = damageDef->GetFloat( "blob_width" );
	const float configuredBlobHeight = damageDef->GetFloat( "blob_height" );
	const float configuredBlobSize = damageDef->GetFloat( "blob_size" );
	const bool hasBlobConfig = configuredBlobMaterial[0] ||
		( configuredBlobX != 0.0f ) || ( configuredBlobY != 0.0f ) ||
		( configuredBlobOffsetX != 0.0f ) || ( configuredBlobOffsetY != 0.0f ) ||
		( configuredBlobWidth > 0.0f ) || ( configuredBlobHeight > 0.0f ) ||
		( configuredBlobSize > 0.0f );

	float blobTime = damageDef->GetFloat( "blob_time" );
	bool synthesizedMeleeBlob = false;
	if ( blobTime <= 0.0f && meleeDamageDef && !hasBlobConfig ) {
		// Compatibility fallback for melee defs that never configured blob visuals.
		blobTime = 500.0f;
		synthesizedMeleeBlob = true;
	}

	if ( blobTime > 0.0f ) {
		screenBlob_t	*blob = GetScreenBlob();
		blob->startFadeTime = gameLocal.time;
		blob->finishTime = gameLocal.time + blobTime * g_blobTime.GetFloat();

		const char *materialName = configuredBlobMaterial;

		const idMaterial *blobMaterial = NULL;
		if ( materialName[0] ) {
			blobMaterial = declManager->FindMaterial( materialName, false );
			if ( blobMaterial == NULL && strchr( materialName, '/' ) == NULL && strchr( materialName, '\\' ) == NULL ) {
				const idStr materialPath = va( "textures/decals/%s", materialName );
				blobMaterial = declManager->FindMaterial( materialPath, false );
			}
		}
		if ( blobMaterial == NULL ) {
			if ( synthesizedMeleeBlob && bloodSprayMaterial != NULL ) {
				blobMaterial = bloodSprayMaterial;
			} else {
				blobMaterial = declManager->FindMaterial( "textures/decals/genericdamage" );
			}
		}
		blob->material = blobMaterial;

		float blobX = configuredBlobX;
		if ( blobX == 0.0f ) {
			blobX = configuredBlobOffsetX;
		}
		blob->x = blobX;
		blob->x += ( gameLocal.random.RandomInt()&63 ) - 32;
		float blobY = configuredBlobY;
		if ( blobY == 0.0f ) {
			blobY = configuredBlobOffsetY;
		}
		blob->y = blobY;
		blob->y += ( gameLocal.random.RandomInt()&63 ) - 32;

		float blobWidth = configuredBlobWidth;
		float blobHeight = configuredBlobHeight;
		if ( blobWidth <= 0.0f || blobHeight <= 0.0f ) {
			if ( configuredBlobSize > 0.0f ) {
				if ( blobWidth <= 0.0f ) {
					blobWidth = configuredBlobSize;
				}
				if ( blobHeight <= 0.0f ) {
					blobHeight = configuredBlobSize;
				}
			}
		}
		if ( blobWidth <= 0.0f || blobHeight <= 0.0f ) {
			if ( synthesizedMeleeBlob && bloodSprayMaterial != NULL ) {
				blobWidth = 600.0f;
				blobHeight = 480.0f;
			} else {
				blobWidth = 400.0f;
				blobHeight = 400.0f;
			}
		}
		
		float scale = ( 256 + ( ( gameLocal.random.RandomInt()&63 ) - 32 ) ) / 256.0f;
		blob->w = blobWidth * g_blobSize.GetFloat() * scale;
		blob->h = blobHeight * g_blobSize.GetFloat() * scale;
		blob->s1 = 0;
		blob->t1 = 0;
		blob->s2 = 1;
		blob->t2 = 1;
	}

	//
	// save lastDamageTime for tunnel vision accentuation
	//
	lastDamageTime = MS2SEC( gameLocal.time );
}
// RAVEN END

/*
==================
idPlayerView::AddBloodSpray

If we need a more generic way to add blobs then we can do that
but having it localized here lets the material be pre-looked up etc.
==================
*/
void idPlayerView::AddBloodSpray( float duration ) {
	(void)duration;
/*
	if ( duration <= 0 || bloodSprayMaterial == NULL || g_skipViewEffects.GetBool() ) {
		return;
	}
	// visit this for chainsaw
	screenBlob_t *blob = GetScreenBlob();
	blob->startFadeTime = gameLocal.time;
	blob->finishTime = gameLocal.time + ( duration * 1000 );
	blob->material = bloodSprayMaterial;
	blob->x = ( gameLocal.random.RandomInt() & 63 ) - 32;
	blob->y = ( gameLocal.random.RandomInt() & 63 ) - 32;
	blob->driftAmount = 0.5f + gameLocal.random.CRandomFloat() * 0.5;
	float scale = ( 256 + ( ( gameLocal.random.RandomInt()&63 ) - 32 ) ) / 256.0f;
	blob->w = 600 * g_blobSize.GetFloat() * scale;
	blob->h = 480 * g_blobSize.GetFloat() * scale;
	float s1 = 0.0f;
	float t1 = 0.0f;
	float s2 = 1.0f;
	float t2 = 1.0f;
	if ( blob->driftAmount < 0.6 ) {
		s1 = 1.0f;
		s2 = 0.0f;
	} else if ( blob->driftAmount < 0.75 ) {
		t1 = 1.0f;
		t2 = 0.0f;
	} else if ( blob->driftAmount < 0.85 ) {
		s1 = 1.0f;
		s2 = 0.0f;
		t1 = 1.0f;
		t2 = 0.0f;
	}
	blob->s1 = s1;
	blob->t1 = t1;
	blob->s2 = s2;
	blob->t2 = t2;
*/
}

/*
==================
idPlayerView::WeaponFireFeedback

Called when a weapon fires, generates head twitches, etc
==================
*/
void idPlayerView::WeaponFireFeedback( const idDict *weaponDef ) {
	int		recoilTime;

	recoilTime = weaponDef->GetInt( "recoilTime" );
	// don't shorten a damage kick in progress
	if ( recoilTime && recoilTime > (kickFinishTime - gameLocal.time) ) {
		idAngles angles;
		weaponDef->GetAngles( "recoilAngles", "5 0 0", angles );
		kickAngles = angles;
		int	finish = gameLocal.time + g_kickTime.GetFloat() * recoilTime;
		kickFinishTime = finish;
	}	

}

/*
===================
idPlayerView::CalculateShake
===================
*/
// RAVEN BEGIN
// jnewquist: Controller rumble
float idPlayerView::CalculateShake( idAngles &shakeAngleOffset ) const {
	idVec3	origin, matrix;

	float shakeVolume = soundSystem->CurrentShakeAmplitudeForPosition( SOUNDWORLD_GAME, gameLocal.time, player->firstPersonViewOrigin );
	//
	// shakeVolume should somehow be molded into an angle here
	// it should be thought of as being in the range 0.0 -> 1.0, although
	// since CurrentShakeAmplitudeForPosition() returns all the shake sounds
	// the player can hear, it can go over 1.0 too.
	//
	shakeAngleOffset[0] = gameLocal.random.CRandomFloat() * shakeVolume;
	shakeAngleOffset[1] = gameLocal.random.CRandomFloat() * shakeVolume;
	shakeAngleOffset[2] = gameLocal.random.CRandomFloat() * shakeVolume;

	return shakeVolume;
}
// RAVEN END

/*
===================
idPlayerView::AngleOffset

  kickVector, a world space direction that the attack should 
===================
*/
idAngles idPlayerView::AngleOffset() const {
	idAngles	ang;

	ang.Zero();

	if ( gameLocal.time < kickFinishTime ) {
		float offset = kickFinishTime - gameLocal.time;

		ang = kickAngles * offset * offset * g_kickAmplitude.GetFloat();

		for ( int i = 0 ; i < 3 ; i++ ) {
			if ( ang[i] > 70.0f ) {
				ang[i] = 70.0f;
			} else if ( ang[i] < -70.0f ) {
				ang[i] = -70.0f;
			}
		}
	}
	return ang;
}

/*
===================
idPlayerView::ShakeOffsets
===================
*/
// RAVEN BEGIN
// jnewquist: Controller rumble
void idPlayerView::ShakeOffsets( idVec3 &shakeOffset, idAngles &shakeAngleOffset, const idBounds bounds ) const {
	float shakeVolume = 0.0f;
	shakeOffset.Zero();
	shakeAngleOffset.Zero();

	if( gameLocal.isMultiplayer ) {
		return;
	}

	shakeVolume = CalculateShake( shakeAngleOffset );

	if( gameLocal.time < shakeFinishTime ) {
		float offset = ( shakeFinishTime - gameLocal.time ) * shakeScale * 0.001f;

		shakeOffset[0] = idMath::ClampFloat( bounds[0][0] - 1.0f, bounds[1][0] + 1.0f, rvRandom::flrand( -offset, offset ) );
		shakeOffset[1] = idMath::ClampFloat( bounds[0][1] - 1.0f, bounds[1][1] + 1.0f, rvRandom::flrand( -offset, offset ) );
		shakeOffset[2] = idMath::ClampFloat( bounds[0][2] - 1.0f, bounds[1][2] + 1.0f, rvRandom::flrand( -offset, offset ) );

		shakeAngleOffset[0] = idMath::ClampFloat( -70.0f, 70.0f, rvRandom::flrand( -offset, offset ) );
		shakeAngleOffset[1] = idMath::ClampFloat( -70.0f, 70.0f, rvRandom::flrand( -offset, offset ) );
		shakeAngleOffset[2] = idMath::ClampFloat( -70.0f, 70.0f, rvRandom::flrand( -offset, offset ) );
	}
}
// RAVEN END

/*
==================
idPlayerView::SingleView
==================
*/
void idPlayerView::SingleView( idUserInterface *hud, const renderView_t *view, int renderFlags ) {
	// normal rendering
	if ( !view ) {
		return;
	}

	const bool previousUIViewportMode = renderSystem->GetUseUIViewportFor2D();

	if ( !( RF_GUI_ONLY & renderFlags ) ) {
// jmarshall
		// jscott: portal sky rendering with KRABS
		//idCamera *portalSky = gameLocal.GetPortalSky();
		//if( portalSky ) {
		//	renderView_t portalSkyView = *view;
		//	portalSky->GetViewParms( &portalSkyView );
		//	gameRenderWorld->RenderScene( &portalSkyView );
		//}
		//gameRenderWorld->RenderScene( view );

		gameLocal.RenderScene(view, gameRenderWorld, gameLocal.GetPortalSky(), renderFlags);
// jmarshall end
	}

	if ( RF_NO_GUI & renderFlags ) {
		renderSystem->SetUseUIViewportFor2D( previousUIViewportMode );
		return;
	}

	// draw screen blobs
	if ( !pm_thirdPerson.GetBool() && !g_skipViewEffects.GetBool() ) {
		renderSystem->SetUseUIViewportFor2D( false );

		for ( int i = 0 ; i < MAX_SCREEN_BLOBS ; i++ ) {
			screenBlob_t	*blob = &screenBlobs[i];
			if ( blob->finishTime <= gameLocal.time ) {
				continue;
			}
			
			blob->y += blob->driftAmount;

			float	fade = (float)( blob->finishTime - gameLocal.time ) / ( blob->finishTime - blob->startFadeTime );
			if ( fade > 1.0f ) {
				fade = 1.0f;
			}
			if ( fade ) {
				renderSystem->SetColor4( 1,1,1,fade );
				renderSystem->DrawStretchPic( blob->x, blob->y, blob->w, blob->h,blob->s1, blob->t1, blob->s2, blob->t2, blob->material );
			}
		}

		// Render tunnel vision
		if ( gameLocal.time < tvFinishTime ) {
			renderSystem->SetColor4( 1.0f, 1.0f, 1.0f, tvScale * ((float)(tvFinishTime - gameLocal.time) / (float)(tvFinishTime - tvStartTime)) );
			renderSystem->DrawStretchPic( 0.0f, 0.0f, 640.0f, 480.0f, 0.0f, 0.0f, 1.0f, 1.0f, tunnelMaterial );
		}

		renderSystem->SetUseUIViewportFor2D( true );
		player->DrawHUD( hud );

			
/*
		// tunnel vision
		float	health = 0.0f;
		if ( g_testHealthVision.GetFloat() != 0.0f ) {
			health = g_testHealthVision.GetFloat();
		} else {
			health = player->health;
		}
		float alpha = health / 100.0f;
		if ( alpha < 0.0f ) {
			alpha = 0.0f;
		}
		if ( alpha > 1.0f ) {
			alpha = 1.0f;
		}

		if ( alpha < 1.0f  ) {
			renderSystem->SetColor4( ( player->health <= 0.0f ) ? MS2SEC( gameLocal.time ) : lastDamageTime, 1.0f, 1.0f, ( player->health <= 0.0f ) ? 0.0f : alpha );
			renderSystem->DrawStretchPic( 0.0f, 0.0f, 640.0f, 480.0f, 0.0f, 0.0f, 1.0f, 1.0f, tunnelMaterial );
		}
*/		

		

		// Render the object system
		// RAVEN BEGIN
		// twhitaker: always draw objective system
		if ( player->objectiveSystem ) {
			player->objectiveSystem->Redraw( gameLocal.time );
		}		
		// RAVEN END
	}

	// test a single material drawn over everything
	if ( g_testPostProcess.GetString()[0] ) {
		renderSystem->SetUseUIViewportFor2D( false );
		const idMaterial *mtr = declManager->FindMaterial( g_testPostProcess.GetString(), false );
		if ( !mtr ) {
			common->Printf( "Material not found.\n" );
			g_testPostProcess.SetString( "" );
		} else {
			renderSystem->SetColor4( 1.0f, 1.0f, 1.0f, 1.0f );
			renderSystem->DrawStretchPic( 0.0f, 0.0f, 640.0f, 480.0f, 0.0f, 0.0f, 1.0f, 1.0f, mtr );
		}
	}

	renderSystem->SetUseUIViewportFor2D( previousUIViewportMode );
}

/*
===================
idPlayerView::DoubleVision
===================
*/
void idPlayerView::DoubleVision( idUserInterface *hud, const renderView_t *view, int offset ) {
	const bool previousUIViewportMode = renderSystem->GetUseUIViewportFor2D();

	if ( !g_doubleVision.GetBool() ) {
		SingleView( hud, view, RF_NO_GUI );
		renderSystem->SetUseUIViewportFor2D( previousUIViewportMode );
		return;
	}

	if ( dvMaterial == NULL || dvMaterialBlend == NULL ) {
		SingleView( hud, view, RF_NO_GUI );
		renderSystem->SetUseUIViewportFor2D( previousUIViewportMode );
		return;
	}

	float	scale = offset * g_dvAmplitude.GetFloat() * dvScale;
	if( scale < 0.0f ) {
		renderSystem->SetUseUIViewportFor2D( previousUIViewportMode );
		return;
	}

	if ( scale > 0.5f ) {
		scale = 0.5f;
	}
	float shift = scale * idMath::Sin( idMath::Sqrt ( offset ) * g_dvFrequency.GetFloat() );
	shift = fabs( shift );

	// Capture from the active framebuffer extents.
	// A forced 640x480 crop only updates a sub-rect in the RT postprocess path.
	SingleView( hud, view, RF_NO_GUI );
	renderSystem->CaptureRenderToImage( "_scratch" );

	// carry red tint if in berserk mode
	idVec4 color(1, 1, 1, 1);

	renderSystem->SetUseUIViewportFor2D( false );
	renderSystem->SetColor4( color.x, color.y, color.z, 1.0f );
	renderSystem->DrawStretchPic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, shift, 1, 1, 0, dvMaterial );
	renderSystem->DrawStretchPic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 1, 1-shift, 0, dvMaterialBlend );
	renderSystem->SetUseUIViewportFor2D( previousUIViewportMode );
}

/*
=================
idPlayerView::Flash

flashes the player view with the given color
=================
*/
void idPlayerView::Flash(idVec4 color, int time ) {
	Fade(idVec4(0, 0, 0, 0), time);
	fadeFromColor = colorWhite;
}

/*
=================
idPlayerView::Fade

used for level transition fades
assumes: color.w is 0 or 1
=================
*/
void idPlayerView::Fade( idVec4 color, int time ) {

	if ( !fadeTime ) {
		fadeFromColor.Set( 0.0f, 0.0f, 0.0f, 1.0f - color[ 3 ] );
	} else {
		fadeFromColor = fadeColor;
	}
	fadeToColor = color;

	if ( time <= 0 ) {
		fadeRate = 0;
		time = 0;
		fadeColor = fadeToColor;
	} else {
		fadeRate = 1.0f / ( float )time;
	}

	if ( gameLocal.realClientTime == 0 && time == 0 ) {
		fadeTime = 1;
	} else {
		fadeTime = gameLocal.realClientTime + time;
	}
}

/*
=================
idPlayerView::ScreenFade
=================
*/
void idPlayerView::ScreenFade() {
	int		msec;
	float	t;
	const bool previousUIViewportMode = renderSystem->GetUseUIViewportFor2D();

	if ( !fadeTime ) {
		return;
	}

	msec = fadeTime - gameLocal.realClientTime;

	if ( msec <= 0 ) {
		fadeColor = fadeToColor;
		if ( fadeColor[ 3 ] == 0.0f ) {
			fadeTime = 0;
		}
	} else {
		t = ( float )msec * fadeRate;
		fadeColor = fadeFromColor * t + fadeToColor * ( 1.0f - t );
	}

	if ( fadeColor[ 3 ] != 0.0f ) {
		renderSystem->SetUseUIViewportFor2D( false );
		renderSystem->SetColor4( fadeColor[ 0 ], fadeColor[ 1 ], fadeColor[ 2 ], fadeColor[ 3 ] );
		renderSystem->DrawStretchPic( 0, 0, 640, 480, 0, 0, 1, 1, declManager->FindMaterial( "_white" ) );
	}

	renderSystem->SetUseUIViewportFor2D( previousUIViewportMode );
}

/*
===================
idPlayerView::InfluenceVision
===================
*/
void idPlayerView::InfluenceVision( idUserInterface *hud, const renderView_t *view ) {
	const bool previousUIViewportMode = renderSystem->GetUseUIViewportFor2D();

	float distance = 0.0f;
	float pct = 1.0f;
	if ( player->GetInfluenceEntity() ) {
		distance = ( player->GetInfluenceEntity()->GetPhysics()->GetOrigin() - player->GetPhysics()->GetOrigin() ).Length();
		if ( player->GetInfluenceRadius() != 0.0f && distance < player->GetInfluenceRadius() ) {
			pct = distance / player->GetInfluenceRadius();
			pct = 1.0f - idMath::ClampFloat( 0.0f, 1.0f, pct );
		}
	}
	if ( player->GetInfluenceMaterial() ) {
		SingleView( hud, view );
		renderSystem->SetUseUIViewportFor2D( false );
		renderSystem->SetColor4( 1.0f, 1.0f, 1.0f, pct );
		renderSystem->DrawStretchPic( 0.0f, 0.0f, 640.0f, 480.0f, 0.0f, 0.0f, 1.0f, 1.0f, player->GetInfluenceMaterial() );
	} else if ( player->GetInfluenceEntity() == NULL ) {
		SingleView( hud, view, RF_NO_GUI );
		renderSystem->SetUseUIViewportFor2D( previousUIViewportMode );
		return;
	} else {
		int offset =  25 + idMath::Sin ( gameLocal.time );
		DoubleVision( hud, view, pct * offset );
	}

	renderSystem->SetUseUIViewportFor2D( previousUIViewportMode );
}

/*
===================
idPlayerView::RenderPlayerView
===================
*/
void idPlayerView::RenderPlayerView( idUserInterface *hud ) {
	if ( !player ) {
		return;
	}

	const renderView_t *view = player->GetRenderView();
	if ( !view ) {
		return;
	}

	renderView_t *mutableView = player->GetRenderView();
	if ( mutableView ) {
		idCamera *camera = player->GetPrivateCameraView();
		if ( !camera ) {
			camera = gameLocal.GetCamera();
		}

		if ( camera ) {
			camera->GetViewParms( mutableView );
		} else {
			gameLocal.CalcFov( player->CalcFov( true ), mutableView->fov_x, mutableView->fov_y );
		}
		view = mutableView;
	}
	
	bool guiRendered = false;

	// place the sound origin for the player
	soundSystem->PlaceListener( view->vieworg, view->viewaxis, player->entityNumber + 1, gameLocal.time, "Undefined" );

	if ( g_skipViewEffects.GetBool() ) {
		SingleView( hud, view );
	} else {
		if ( player->GetInfluenceMaterial() || player->GetInfluenceEntity() ) {
			InfluenceVision( hud, view );
			guiRendered = true;
		} else if ( g_doubleVision.GetBool() && gameLocal.time < dvFinishTime ) {
			DoubleVision( hud, view, dvFinishTime - gameLocal.time );
			guiRendered = false;
		} else {
			SingleView( hud, view, RF_NO_GUI | RF_PRIMARY_VIEW );
		}

		// Now draw GUI's.
		if ( !guiRendered ) {
			SingleView( hud, view, RF_GUI_ONLY );
		}

		ScreenFade();
	}

	if ( net_clientLagOMeter.GetBool() && lagoMaterial && gameLocal.isClient && !( gameLocal.GetDemoState() == DEMO_PLAYING && ( gameLocal.IsServerDemo() || gameLocal.IsTimeDemo() ) ) ) {
		renderSystem->SetColor4( 1.0f, 1.0f, 1.0f, 1.0f );
		renderSystem->DrawStretchPic( 10.0f, 380.0f, 64.0f, 64.0f, 0.0f, 0.0f, 1.0f, 1.0f, lagoMaterial );
	}

}

// RAVEN END
