//=============================================================================
// M99Bullet
//=============================================================================
// M99 Sniper Rifle bullet class
//=============================================================================
// Killing Floor Source
// Copyright (C) 2012 Tripwire Interactive LLC
// - John "Ramm-Jaeger" Gibson, and IJC Development
//=============================================================================

class M99Bullet extends Projectile;

var xEmitter Trail;
var() class<DamageType> DamageTypeHeadShot;
var sound Arrow_hitwall[3];
var sound Arrow_rico[2];
var sound Arrow_hitarmor;
var sound Arrow_hitflesh;

var() float HeadShotDamageMult;

var Actor ImpactActor;
var Pawn IgnoreImpactPawn;

var()	string	AmbientSoundRef;
//var		string	MeshRef;
var		string	Arrow_hitwallRef[3];
var		string	Arrow_ricoRef[2];
var		string	Arrow_hitarmorRef;
var		string	Arrow_hitfleshRef;

replication
{
	reliable if ( Role==ROLE_Authority && bNetInitial )
		ImpactActor;
}

static function PreloadAssets()
{
	default.AmbientSound = sound(DynamicLoadObject(default.AmbientSoundRef, class'Sound', true));

	default.Arrow_hitwall[0] = sound(DynamicLoadObject(default.Arrow_hitwallRef[0], class'Sound', true));
	default.Arrow_hitwall[1] = sound(DynamicLoadObject(default.Arrow_hitwallRef[1], class'Sound', true));
	default.Arrow_hitwall[2] = sound(DynamicLoadObject(default.Arrow_hitwallRef[2], class'Sound', true));

	default.Arrow_rico[0] = sound(DynamicLoadObject(default.Arrow_ricoRef[0], class'Sound', true));
	default.Arrow_rico[1] = sound(DynamicLoadObject(default.Arrow_ricoRef[1], class'Sound', true));

	default.Arrow_hitarmor = sound(DynamicLoadObject(default.Arrow_hitarmorRef, class'Sound', true));
	default.Arrow_hitflesh = sound(DynamicLoadObject(default.Arrow_hitfleshRef, class'Sound', true));

	//UpdateDefaultMesh(Mesh(DynamicLoadObject(default.MeshRef, class'Mesh', true)));
}

static function bool UnloadAssets()
{
	default.AmbientSound = none;
	default.Arrow_hitwall[0] = none;
	default.Arrow_hitwall[1] = none;
	default.Arrow_hitwall[2] = none;
	default.Arrow_rico[0] = none;
	default.Arrow_rico[1] = none;
	default.Arrow_hitarmor = none;
	default.Arrow_hitflesh = none;

	//UpdateDefaultMesh(none);

	return true;
}

simulated function PostNetBeginPlay()
{
	if( Level.NetMode!=NM_DedicatedServer && (Level.NetMode!=NM_Client || Physics==PHYS_Projectile) )
	{
		if ( !PhysicsVolume.bWaterVolume )
		{
			Trail = Spawn(class'KFArrowTracer',self);
			Trail.Lifespan = Lifespan;
		}
	}
	else if( Level.NetMode==NM_Client )
	{
		if( ImpactActor!=None )
			SetBase(ImpactActor);
		GoToState('OnWall');
	}
}
simulated function PostBeginPlay()
{
	Super.PostBeginPlay();
	Velocity = Speed * Vector(Rotation);
	if( PhysicsVolume.bWaterVolume )
		Velocity*=0.65;
}

simulated state OnWall
{
Ignores HitWall;

	simulated function Tick( float Delta )
	{
		if( Base==None )
		{
			if( Level.NetMode==NM_Client )
				bHidden = True;
			else Destroy();
		}
	}

	simulated function BeginState()
	{
		bCollideWorld = False;
		if( Level.NetMode!=NM_DedicatedServer )
			AmbientSound = None;
		if( Trail!=None )
			Trail.mRegen = False;
		SetCollisionSize(25,25);
	}
}

simulated function Explode(vector HitLocation, vector HitNormal);

simulated function ProcessTouch (Actor Other, vector HitLocation)
{
	local vector X,End,HL,HN;
	local Vector TempHitLocation, HitNormal;
	local array<int>	HitPoints;
    local KFPawn HitPawn;
	local bool	bHitWhipAttachment;

	if ( Other == none || Other == Instigator || Other.Base == Instigator || !Other.bBlockHitPointTraces || Other==IgnoreImpactPawn ||
        (IgnoreImpactPawn != none && Other.Base == IgnoreImpactPawn) )
		return;

	X =  Vector(Rotation);

 	if( ROBulletWhipAttachment(Other) != none )
	{

    	bHitWhipAttachment=true;

        if(!Other.Base.bDeleteMe)
        {
	        Other = Instigator.HitPointTrace(TempHitLocation, HitNormal, HitLocation + (65535 * X), HitPoints, HitLocation,, 1);

			if( Other == none || HitPoints.Length == 0 )
				return;

			HitPawn = KFPawn(Other);

            if (Role == ROLE_Authority)
            {
    	    	if ( HitPawn != none )
    	    	{
     				// Hit detection debugging
    				/*log("Bullet hit "$HitPawn.PlayerReplicationInfo.PlayerName);
    				HitPawn.HitStart = HitLocation;
    				HitPawn.HitEnd = HitLocation + (65535 * X);*/

                    if( !HitPawn.bDeleteMe )
                    	HitPawn.ProcessLocationalDamage(Damage, Instigator, TempHitLocation, MomentumTransfer * X, MyDamageType,HitPoints);

        			Damage/=1.25;
        			Velocity*=0.85;

                    IgnoreImpactPawn = HitPawn;

            		if( Level.NetMode!=NM_Client )
            			PlayhitNoise(Pawn(Other)!=none && Pawn(Other).ShieldStrength>0);

                    // Hit detection debugging
    				/*if( Level.NetMode == NM_Standalone)
    					HitPawn.DrawBoneLocation();*/

    				 return;
    	    	}
    		}
		}
		else
		{
			return;
		}
	}

	if( Level.NetMode!=NM_Client )
		PlayhitNoise(Pawn(Other)!=none && Pawn(Other).ShieldStrength>0);

	if( Physics==PHYS_Projectile && Pawn(Other)!=None && Vehicle(Other)==None )
	{
		IgnoreImpactPawn = Pawn(Other);
		if( IgnoreImpactPawn.IsHeadShot(HitLocation, X, 1.0) )
			Other.TakeDamage(Damage * HeadShotDamageMult, Instigator, HitLocation, MomentumTransfer * X, DamageTypeHeadShot);
		else Other.TakeDamage(Damage, Instigator, HitLocation, MomentumTransfer * X, MyDamageType);
		Damage/=1.25;
		Velocity*=0.85;
		Return;
	}
	else if( ExtendedZCollision(Other)!=None && Pawn(Other.Owner)!=None )
	{
		if( Other.Owner==IgnoreImpactPawn )
			Return;
		IgnoreImpactPawn = Pawn(Other.Owner);
		if ( IgnoreImpactPawn.IsHeadShot(HitLocation, X, 1.0))
			Other.TakeDamage(Damage * HeadShotDamageMult, Instigator, HitLocation, MomentumTransfer * X, DamageTypeHeadShot);
		else Other.TakeDamage(Damage, Instigator, HitLocation, MomentumTransfer * X, MyDamageType);
		Damage/=1.25;
		Velocity*=0.85;
		Return;
	}
	if( Level.NetMode!=NM_DedicatedServer && SkeletalMesh(Other.Mesh)!=None && Other.DrawType==DT_Mesh && Pawn(Other)!=None )
	{ // Attach victim to the wall behind if it dies.
		End = Other.Location+X*600;
		if( Other.Trace(HL,HN,End,Other.Location,False)!=None )
			Spawn(Class'BodyAttacher',Other,,HitLocation).AttachEndPoint = HL-HN;
	}
	//Stick(Other,HitLocation);
	if( Level.NetMode!=NM_Client )
	{
		if (Pawn(Other) != none && Pawn(Other).IsHeadShot(HitLocation, X, 1.0))
			Pawn(Other).TakeDamage(Damage * HeadShotDamageMult, Instigator, HitLocation, MomentumTransfer * X, DamageTypeHeadShot);
		else Other.TakeDamage(Damage, Instigator, HitLocation, MomentumTransfer * X, MyDamageType);
	}
}

function PlayhitNoise( bool bArmored )
{
	if( bArmored )
		PlaySound(Arrow_hitarmor);   // implies hit a target with shield/armor
	else PlaySound(Arrow_hitflesh);
}

simulated function HitWall( vector HitNormal, actor Wall )
{
	speed = VSize(Velocity);

	if ( Role==ROLE_Authority && Wall!=none )
	{
		if ( !Wall.bStatic && !Wall.bWorldGeometry )
		{
			if ( Instigator == None || Instigator.Controller == None )
			{
				Wall.SetDelayedDamageInstigatorController(InstigatorController);
			}

			Wall.TakeDamage( Damage, instigator, Location, MomentumTransfer * Normal(Velocity), MyDamageType);
			HurtWall = Wall;
		}
		else
		{
			Destroy();
		}

		MakeNoise(1.0);
	}

	PlaySound(Arrow_hitwall[Rand(3)],,2.5*TransientSoundVolume);

	if ( Level.NetMode != NM_DedicatedServer )
	{
		Spawn(class'ROBulletHitEffect',,, Location, rotator(-HitNormal));
	}

	if( Instigator!=None && Level.NetMode!=NM_Client )
	{
		MakeNoise(0.3);
	}

	//Stick(Wall, Location+HitNormal);
}

simulated function Landed(vector HitNormal)
{
	HitWall(HitNormal, None);
}

simulated function Stick(actor HitActor, vector HitLocation)
{
	local name NearestBone;
	local float dist;

	SetPhysics(PHYS_None);

	if (pawn(HitActor) != none)
	{
		NearestBone = GetClosestBone(HitLocation, HitLocation, dist , 'CHR_Spine2' , 15 );
		HitActor.AttachToBone(self,NearestBone);
	}
	else SetBase(HitActor);

	ImpactActor = HitActor;

	if (Base==None)
		Destroy();
	else GoToState('OnWall');
}

simulated function PhysicsVolumeChange( PhysicsVolume Volume )
{
	if( Volume.bWaterVolume && !PhysicsVolume.bWaterVolume )
	{
		if ( Trail != None )
			Trail.mRegen=False;
		Velocity*=0.65;
	}
}

simulated function Destroyed()
{
	if (Trail !=None)
		Trail.mRegen = False;
	Super.Destroyed();
}

defaultproperties
{
     DamageTypeHeadShot=Class'KFMod.DamTypeM99HeadShot'
     Arrow_hitwallRef(0)="KFWeaponSound.bullethitflesh2"
     Arrow_hitwallRef(1)="KFWeaponSound.bullethitflesh3"
     Arrow_hitwallRef(2)="KFWeaponSound.bullethitflesh4"
     Arrow_ricoRef(0)="KFWeaponSound.bullethitmetal"
     Arrow_ricoRef(1)="KFWeaponSound.bullethitmetal3"
     Arrow_hitarmorRef="KFWeaponSound.bullethitflesh4"
     Arrow_hitfleshRef="KFWeaponSound.bullethitflesh4"
     HeadShotDamageMult=2.250000
     Speed=28000.000000
     MaxSpeed=200000.000000
     Damage=675.000000
     MomentumTransfer=150000.000000
     MyDamageType=Class'KFMod.DamTypeM99SniperRifle'
     ExplosionDecal=Class'KFMod.ShotgunDecal'
     CullDistance=3000.000000
     AmbientSoundRef="PatchSounds.ArrowZip"
     LifeSpan=2.000000
     //MeshRef="KFWeaponModels.XbowBolt"
     DrawScale=15.000000
     AmbientGlow=30
     Style=STY_Alpha
     bUnlit=False
     bFullVolume=True
     bNetTemporary=False
     CollisionRadius=0
     CollisionHeight=0
}
