// A hover craft. (Created by .:..: 13.1.2008)
Class HoverCraftPhys extends Vehicle;

var() float MaxHoverSpeed,VehicleTurnSpeed,HoveringHeight,JumpingHeight;
var() bool bHoverWhenNotDriving, bHoverJump, bHoverDown;
var() float HoverTurnSpeed, HoverJumpTime;
var float CurTurnSpeed, NextJumpTime, CurHoverHeight;
var bool bInHoverDist;

var() bool bHoveringPhys, bTwoFansOnly, bSingleFan;

var VehHoverAttach VHoverAt[4];

var(WaterFX) config bool bHaveHoverFX;
struct HoverEngine
{
	var() float HoverEngSizeX, HoverEngSizeY;
	var() bool bCustomHoverFX;
};
var(WaterFX) HoverEngine HoverEng[4];	//0 = Front Right, 1 = Front Back, 2 = Back Right, 3 = Back Left

simulated function PostBeginPlay()
{
local byte i;
local vector ePointsOffSet[4];

	CurTurnSpeed = HoverTurnSpeed;
	
	if (bHoveringPhys)
	{
		bSlopedPhys = False;
		if (bSingleFan)
		{
			bTwoFansOnly = False;
			
			VHoverAt[0] = Spawn(Class'VehHoverAttach', Self,, Location);
			VHoverAt[0].WaveSize = FMax(HoverEng[0].HoverEngSizeX, HoverEng[0].HoverEngSizeY);
			VHoverAt[0].SoundRadius = Max(32,Min(Max(CollisionRadius,CollisionHeight)/2.5,255));
		}
		else
		{
			GVT = Spawn(Class'GVehTrailer', Self);
			GVT.Texture = Texture;
			GVT.Mesh = Mesh;
			GVT.Skin = Skin;
			GVT.ScaleGlow = ScaleGlow;
			GVT.DrawScale = DrawScale;
			
			For (i=0; i<8; i++)
				GVT.MultiSkins[i] = MultiSkins[i];

			bHidden = True;

			FrontWide.Y = Abs(FrontWide.Y);
			FrontWide.X = Abs(FrontWide.X);
			FrontWide.Z = Abs(FrontWide.Z);
			BackWide.Y = Abs(BackWide.Y);
			BackWide.X = -Abs(BackWide.X);
			BackWide.Z = Abs(BackWide.Z);
			
			ePointsOffSet[0] = FrontWide;
			ePointsOffSet[1] = ePointsOffSet[0];
			ePointsOffSet[1].Y = -FrontWide.Y;
			ePointsOffSet[2] = BackWide;
			ePointsOffSet[3] = ePointsOffSet[2];
			ePointsOffSet[3].Y = -BackWide.Y;

		
			for (i = 0; i < 4; i++)
			{
				VHoverAt[i] = Spawn(Class'VehHoverAttach', Self,, Location + (ePointsOffSet[i] >> Rotation));
				VHoverAt[i].WaveSize = FMax(HoverEng[i].HoverEngSizeX, HoverEng[i].HoverEngSizeY);
				VHoverAt[i].SoundRadius = Max(32,Min(Max(CollisionRadius,CollisionHeight)/2.5,255));
			}
		}
	}
	
	Super.PostBeginPlay();
}

// Return normal for acceleration direction.
simulated function vector GetAccelDir( int InTurn, int InRise, int InAccel )
{
	local rotator R;
	local vector X,Y,Z;

	if( PlayerPawn(Driver)==None )
	{
		X = (MoveDest-Location);
		X.Z = 0;
		X = Normal(X);
		if( bOnGround )
			Return SetUpNewMVelocity(X,ActualFloorNormal,0);
		else Return X;
	}
	R.Yaw = VehicleYaw;
	GetAxes(R,X,Y,Z);
	Z = X*InAccel+Y*InTurn*-1;
	if( VSize(Z)<0.02 )
		Z = vect(0,0,0);
	else Z = Normal(Z);
	if( bOnGround )
		Return SetUpNewMVelocity(Z,ActualFloorNormal,0);
	else Return Z;
}

simulated function bool CheckOnGround()
{
local vector End,Start,Ex,HL,HN;

	if (bHoveringPhys)
	{
		Start = Location;
		Start.Z-=(CollisionHeight-1);
		End = Start;
		End.Z-=6;
		Ex.X = CollisionRadius;
		Ex.Y = Ex.X;
		
		if( Trace(HL,HN,End,Start,False,Ex)==None )
			return False;
		
		if (bTwoFansOnly)
			ActualGVTNormal = Normal(HN + ActualGVTNormal);
		
		return True;
	}
	else
		return Super.CheckOnGround();
}

simulated function float CheckOnHovering(float Delta)
{
	local vector End,Start;
	local vector sHL[4],sHN[4], ePointsOffSet[4], MLoc, CrossedVect[2], GVTLoc;
	local vector HL, HN;
	local actor Ac[4];
	local byte b, AcCount, HEngCount;
	local bool isPosNormal;
	
	//Water zone variables
	local byte FootSndVol, FootSndPitch;
	local sound FootAmbSnd;
	local float FootZoneSpeed;
	local bool haveWaterFX;
	local float WFXDist;
	local vector WFXOldLoc, ZVFX;
	local byte i;
	local float HDist;

	ePointsOffSet[0] = FrontWide;
	ePointsOffSet[1] = ePointsOffSet[0];
	ePointsOffSet[1].Y = -FrontWide.Y;
	
	if (!bTwoFansOnly && !bSingleFan)
	{
		ePointsOffSet[2] = BackWide;
		ePointsOffSet[3] = ePointsOffSet[2];
		ePointsOffSet[3].Y = -BackWide.Y;
		HEngCount = 4;
	}
	else if (bSingleFan)
		HEngCount = 1;
	else
		HEngCount = 2;
	
	if (HEngCount != 1)
	{
		For (b=0; b<HEngCount; b++)
		{
			//Start = Location + (ePointsOffSet[b] >> Rotation);
			//End = Start + ((-Abs(CollisionHeight + HoveringHeight + 10)*vect(0,0,1)) >> Rotation);
			Start = Location + (ePointsOffSet[b] >> Rotation);
			Start.Z-=(CollisionHeight-1);
			End = Start;
			End.Z-=(HoveringHeight+11);
			
			Ac[b] = Trace(sHL[b],sHN[b],End,Start,False);
			
			if (Ac[b] != None && sHN[b].Z <= 0.425)
					sHL[b] = 1.15 * End;
			else
				sHL[b] = 1.25 * End;
				
			if (sHN[b].Z >= 0 || Ac[b] == None)
				isPosNormal = True;
		}
	}
	else
	{
		Start = Location;
		Start.Z-=(CollisionHeight-1);
		End = Start;
		End.Z-=(HoveringHeight+11);
		
		Ac[0] = Trace(sHL[0],sHN[0],End,Start,False);
	}
	
	//********************************************************************************
	//Water AND Hovering (physics and fx)
	//********************************************************************************
	if (bHoveringPhys && (GVT!=None || HEngCount==1))
	{
		For (i=0; i<HEngCount; i++)
		{
			if (VHoverAt[i] != None && (bDriving || bHoverWhenNotDriving))
			{
				if (HEngCount > 1 && GVT!=None)
				{
					ZVFX = Location + GVT.PrePivot + (ePointsOffSet[i] >> Rotation);
					VHoverAt[i].SetLocation(Location + GVT.PrePivot + (ePointsOffSet[i] >> Rotation));
				}
				else
					VHoverAt[i].SetLocation(Location);
				
				VHoverAt[i].Move(Abs(CollisionHeight + HoveringHeight + 10)*vect(0,0,-1));
				
				if (Ac[i] != None || (VHoverAt[i]!=None && VHoverAt[i].Region.Zone.bWaterZone && !Region.Zone.bWaterZone))
					AcCount++;
				
				if (HEngCount==1 && Ac[0] == None && VHoverAt[0]!=None && VHoverAt[0].Region.Zone.bWaterZone && !Region.Zone.bWaterZone)
				{
					Ac[0] = VHoverAt[0];
					sHN[0] = vect(0,0,1);
					sHL[0] = End;
				}
				
				if (VHoverAt[i].Region.Zone.bWaterZone && !Region.Zone.bWaterZone)
				{
					FootVehZone[i] = VHoverAt[i].Region.Zone;
					if (HEngCount > 1 && GVT != None)
						haveWaterFX = bHaveGroundWaterFX && (Abs(ZVFX.Z - CollisionHeight - VHoverAt[i].Location.Z) <= HoveringHeight*0.65);
					else
						haveWaterFX = bHaveGroundWaterFX && (Abs(Location.Z - CollisionHeight - VHoverAt[i].Location.Z) <= HoveringHeight*0.65);

					if (haveWaterFX && (Location != OldLocation || (FootVehZone[i] != None && VSize(FootVehZone[i].ZoneVelocity)>150 )))
					{
						if (VSize(FootVehZone[i].ZoneVelocity) > 150)
							FootZoneSpeed = VSize(FootVehZone[i].ZoneVelocity);
						else
							FootZoneSpeed = 0;
						
						FootSndVol = Min(Max(8,VHoverAt[i].WaveSize*3),255) * ((VSize(Location - OldLocation)/Delta + FootZoneSpeed)/ RefMaxWaterSpeed);
						FootSndPitch = 32 + ((VSize(Location - OldLocation)/Delta + FootZoneSpeed)/ RefMaxWaterSpeed) * 96;
						FootAmbSnd = VHoverAt[i].Region.Zone.AmbientSound;

						if (VHoverAt[i].SoundPitch != FootSndPitch)
							VHoverAt[i].SoundPitch = FootSndPitch;
						if (VHoverAt[i].AmbientSound != FootAmbSnd)
							VHoverAt[i].AmbientSound = FootAmbSnd;
						if (VHoverAt[i].SoundVolume != FootSndVol)
							VHoverAt[i].SoundVolume = FootSndVol;
					}

					//Find water sheet (detail = dist/32 -> dist <= 192; detail = dist/64 -> dist > 192)
					////////////////////////////////////////////////////////
					if (HEngCount > 1 && GVT != None)
						WFXDist = Abs(VHoverAt[i].Location.Z - ZVFX.Z) / 8;
					else
						WFXDist = Abs(VHoverAt[i].Location.Z - Location.Z) / 8;
					
					while (VHoverAt[i].Region.Zone.bWaterZone)
						VHoverAt[i].Move(WFXDist*vect(0,0,1));
					
					if (WFXDist*8 <= 192)
						WFXDist /= 4;
					else
						WFXDist /= 8;
						
					while (!VHoverAt[i].Region.Zone.bWaterZone)
					{
						WFXOldLoc = VHoverAt[i].Location;
						VHoverAt[i].Move(WFXDist*vect(0,0,-1));
					}
					
					VHoverAt[i].OldWaterZone = VHoverAt[i].Region.Zone;
					VHoverAt[i].SetLocation(WFXOldLoc);
					////////////////////////////////////////////////////////
					sHL[i] = WFXOldLoc;
					
					if (bHaveHoverFX)
					{
						if (!HoverEng[i].bCustomHoverFX)
							SpawnHoverFX(i);
						else
							SpawnCustomHoverFX(i);
					}

					if (haveWaterFX && (Location != OldLocation || (FootVehZone[i] != None && VSize(FootVehZone[i].ZoneVelocity)>150 )))
					{
						if ((Velocity dot vector(Rotation)) > 0)
							VHoverAt[i].WaveLenght += VSize(Location - OldLocation);
						else
							VHoverAt[i].WaveLenght -= VSize(Location - OldLocation);
					}

				}
				else
				{
					if (VHoverAt[i].OldWaterZone != None)
						VHoverAt[i].OldWaterZone = None;
					if (VHoverAt[i].AmbientSound != None)
						VHoverAt[i].AmbientSound = None;

					FootVehZone[i] = None;
				}
				
				if (!haveWaterFX && FootVehZone[i] != None && Location == OldLocation && VHoverAt[i] != None && VHoverAt[i].AmbientSound != None)
					VHoverAt[i].AmbientSound = None;
			}
		}
	}
	//********************************************************************************

	if (AcCount > 1)
	{
		if (!bTwoFansOnly)
		{
			MLoc = (sHL[0] + sHL[1] + sHL[2] + sHL[3]) / 4;
			CrossedVect[0] = Normal(sHL[0] - sHL[3]);
			CrossedVect[1] = Normal(sHL[1] - sHL[2]);
		}
		else
		{
			MLoc = (sHL[0] + sHL[1]) / 2;
			CrossedVect[0] = Normal(sHL[0] - sHL[1]);
			CrossedVect[1] = Normal(sHL[1] - sHL[0]);
		}
		
		Start = Location;
		Start.Z-=(CollisionHeight-1);
		End = Start;
		End.Z-=(HoveringHeight+11);
		
		if (Trace(HL,HN,End,Start,False) != None)
			ActualFloorNormal = HN;
		
		ActualGVTNormal = Normal(CrossedVect[0] cross CrossedVect[1]);
		
		if ((ActualGVTNormal.Z < 0 && isPosNormal) || (ActualGVTNormal.Z > 0 && !isPosNormal))
			ActualGVTNormal = -ActualGVTNormal;
		//GVTLoc = MLoc + ActualGVTNormal*(CurHoverHeight + CollisionHeight);
		//HDist = MLoc.Z - GVTLoc.Z + HoveringHeight + CollisionHeight;
		//GVT.PrePivot = GVTLoc - Location;
		
		bInHoverDist = True;
		return MLoc.Z - Location.Z + HoveringHeight + CollisionHeight;
	}
	else if (HEngCount == 1 && Ac[0] != None)
	{
		bInHoverDist = True;
		ActualFloorNormal = sHN[0];
		ActualGVTNormal = ActualFloorNormal;
		return (sHL[0].Z - Location.Z + HoveringHeight + CollisionHeight);
	}
	
	bInHoverDist = False;
	return 0;
}

simulated function UpdateDriverInput( float Delta )
{
	local vector Ac,End,Start,HL,HN;
	local float DeAcc,DeAccRat;
	local rotator R;
	local int OlY;

	if( !bOnGround && (!bHoveringPhys || (bHoveringPhys && !bInHoverDist)))
		ActualFloorNormal = vect(0,0,1);
	
	if (GVT!=None)
		R = TransformForGroundRot(VehicleYaw,GVTNormal);
	else
		R = TransformForGroundRot(VehicleYaw,FloorNormal);
		
	R = TransformForGroundRot(VehicleYaw,FloorNormal);
	if( Rotation!=R )
		SetRotation(R);
		
	if( bDriving || bHoverWhenNotDriving )
	{
		if (bHoveringPhys)
		{
			DeAcc = CheckOnHovering(Delta);
			
			if (bInHoverDist)
			{
				if( Velocity.Z>(DeAcc*2.f) )
				{
					Velocity.Z-=DeAcc*Delta;
					CurHoverHeight -= (DeAcc*Delta);
				}
				else
				{
					Velocity.Z+=DeAcc*5.f*Delta;
					CurHoverHeight += (DeAcc*5.f*Delta);
				}
				if( Abs(DeAcc)<5 )
				{
					Velocity.Z*=(1.f-Delta*2.f);
					CurHoverHeight *= (1.f-Delta*2.f);
				}
			}
		}
		else
		{
			Start = Location;
			Start.Z-=(CollisionHeight-1);
			End = Start;
			End.Z-=(HoveringHeight+11);
			
			if( Trace(HL,HN,End,Start,False)!=None && HN.Z>0.5 )
			{
				bInHoverDist = True;
				DeAcc = HL.Z-Location.Z+HoveringHeight+CollisionHeight;
				if( Velocity.Z>(DeAcc*2.f) )
					Velocity.Z-=DeAcc*Delta;
				else Velocity.Z+=DeAcc*5.f*Delta;
				if( Abs(DeAcc)<5 )
					Velocity.Z*=(1.f-Delta*2.f);
			}
			else bInHoverDist = False;
		}
	}
	else
		bInHoverDist = False;
		
	if( !bOnGround && !bInHoverDist )
		Velocity+=Region.Zone.ZoneGravity*Delta*(VehicleGravityScale/GroundPower);

	if( Level.NetMode==NM_Client && !IsNetOwner(Owner) )
		Return;

	// Update vehicle yaw.
	if( bDriving && Driver!=None )
	{
		OlY = VehicleYaw;
		if( PlayerPawn(Driver)==None )
		{
			if( Driver.Target!=None )
				HL = Driver.Target.Location;
			else HL = Location+Velocity*100;
			VehicleYaw = CalcTurnSpeed(CurTurnSpeed*Delta,VehicleYaw,rotator(HL-Location).Yaw);
		}
		else VehicleYaw = CalcTurnSpeed(CurTurnSpeed*Delta,VehicleYaw,Driver.ViewRotation.Yaw);
		if( OlY==VehicleYaw )
			CurTurnSpeed = HoverTurnSpeed;
		else if( CurTurnSpeed<VehicleTurnSpeed )
		{
			CurTurnSpeed+=Delta*VehicleTurnSpeed*2;
			if( CurTurnSpeed>VehicleTurnSpeed )
				CurTurnSpeed = VehicleTurnSpeed;
		}
	}
	
	if (bHoverJump)
		NextJumpTime += Delta;
		
	// Update vehicle speed
	if( bHoverDown && Rising<0 && Velocity.Z>-250 )
	{
		HoverDownEvent();
		
		if( bInHoverDist )
			Velocity.Z-=HoveringHeight*Delta*0.5;
		else if( Velocity.Z>0 )
			Velocity.Z-=1500.f*Delta;
		else Velocity.Z-=500.f*Delta;
	}
	else if( bHoverJump && Rising>0 && bInHoverDist && NextJumpTime >= HoverJumpTime)
	{
		NextJumpTime = 0;
		HoverJumpEvent();
		Velocity.Z+=JumpingHeight;
	}
	
	if( Accel==0 && Turning==0 )
	{
		Ac = Velocity;
		Ac.Z = 0;
		DeAcc = VSize(Ac);
		if( bInHoverDist )
			DeAccRat = Delta*WDeAccelRate;
		else DeAccRat = Delta*WDeAccelRate/3;
		if( DeAccRat>DeAcc )
			DeAccRat = DeAcc;
		Ac-=Normal(Ac)*DeAccRat;
		Velocity.X = Ac.X;
		Velocity.Y = Ac.Y;
		Return;
	}

	if (Accel != 0)
		Ac = GetAccelDir(Turning,Rising,Accel)*WAccelRate;
	else
		Ac = GetAccelDir(Turning,Rising,Accel)*WDeAccelRate;
	if( VSize(Velocity)>MaxHoverSpeed && VSize(Normal(Velocity)-Normal(Ac))<0.15 )
		Velocity+=Ac*Delta/10;
	else Velocity+=Ac*Delta;
}
// Returns the bot turning value for target
function int ShouldTurnFor( vector AcTarget )
{
	Return 0;
}
// Read whatever input driver is pressing now
simulated function ReadDriverInput( PlayerPawn Other, float DeltaTime )
{
	Super.ReadDriverInput(Other,DeltaTime);
	Other.bPressedJump = False;
}

simulated function HoverJumpEvent();

simulated function HoverDownEvent();

function SpawnHoverFX(byte i);

function SpawnCustomHoverFX(byte i);

simulated function AttachmentsTick( float Delta )
{
local byte PitchDif;

	Super.AttachmentsTick(Delta);

	if (bEngDynSndPitch)
	{
		PitchDif = MaxEngPitch - MinEngPitch;
		SoundPitch = Byte(MinEngPitch + Min(PitchDif,(VSize(Velocity)*PitchDif/MaxHoverSpeed)));
	}
}

defaultproperties
{
	VehicleTurnSpeed=55000.000000
	MaxHoverSpeed=800.000000
	WAccelRate=700.000000
	Mesh=Mesh'WoodenBoxM'
	CollisionHeight=22
	CollisionRadius=22
	TranslatorDescription="This is a Hover Craft, press [Fire] or [AltFire] to fire the different firemodes. Use your Strafe keys and Move Forward/Backward keys to strafe/accelerate/deaccelerate. To leave this vehicle, press your [ThrowWeapon] key."
	Health=200
	VehicleName="Hover Craft"
	bFPViewUseRelRot=False
	bFPRepYawUpdatesView=False
	HoveringHeight=100.000000
	HoverTurnSpeed=100.000000
	JumpingHeight=500.000000
	HoverJumpTime=5.000000
	VehicleKeyInfoStr="Hover craft keys:|%MoveForward%,%MoveBackward% to accelerate/deaccelerate|%StrafeLeft%,%StrafeRight% to strafe|%Jump% to jump with the vehicle|%Fire% to fire, %AltFire% to alt fire|Number keys to switch seats|%ThrowWeapon% to exit the vehicle"
	bHaveHoverFX=True
	bHoveringPhys=True
	HoverEng(0)=(HoverEngSizeX=128.000000,HoverEngSizeY=128.000000)
	HoverEng(1)=(HoverEngSizeX=128.000000,HoverEngSizeY=128.000000)
	HoverEng(2)=(HoverEngSizeX=128.000000,HoverEngSizeY=128.000000)
	HoverEng(3)=(HoverEngSizeX=128.000000,HoverEngSizeY=128.000000)
	WDeAccelRate=100.000000
}
