class CrossbuzzsawBlade extends ROBallisticProjectile;

#exec OBJ LOAD FILE=KF_InventorySnd.uax

var xEmitter Trail;
var Emitter Corona;
var() class<DamageType> DamageTypeHeadShot;

// Sounds
var     sound       BladeHitWall;   // Sound of the blade hitting a wall
var     sound       BladeHitArmor;  // Sound of the blade hitting an armored enemy
var     sound       BladeHitFlesh;  // Sound of the blade hitting flesh

var() float HeadShotDamageMult;

var Actor ImpactActor;
var Pawn IgnoreImpactPawn;

// Dynamic loading
var()	string	AmbientSoundRef;
var		string	StaticMeshRef;
var		string	BladeHitWallRef;
var		string	BladeHitArmorRef;
var		string	BladeHitFleshRef;

// Bouncing vars
var     byte    Bounces;        // Number of times this blade has bounced
var     int     MaxBounces;     // Maximum number of times this blade should be able to bounce

// Physics
var() 		float 		StraightFlightTime;          // How long the projectile and flies straight
var 		float 		TotalFlightTime;             // How long the rocket has been in flight
var 		bool 		bOutOfPropellant;            // Projectile is out of propellant
// Physics debugging
var 		vector 		OuttaPropLocation;

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

    reliable if (bNetInitial && Role == ROLE_Authority)
        Bounces;
}

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

	default.BladeHitWall= sound(DynamicLoadObject(default.BladeHitWallRef, class'Sound', true));

	default.BladeHitArmor = sound(DynamicLoadObject(default.BladeHitArmorRef, class'Sound', true));
	default.BladeHitFlesh = sound(DynamicLoadObject(default.BladeHitFleshRef, class'Sound', true));

	UpdateDefaultStaticMesh(StaticMesh(DynamicLoadObject(default.StaticMeshRef, class'StaticMesh', true)));
}

static function bool UnloadAssets()
{
	default.AmbientSound = none;
	default.BladeHitWall = none;
	default.BladeHitArmor = none;
	default.BladeHitFlesh = none;

	UpdateDefaultStaticMesh(none);

	return true;
}

simulated function PostBeginPlay()
{
    local vector Dir;

    BCInverse = 1 / BallisticCoefficient;

    OrigLoc = Location;

    Dir = vector(Rotation);
    Velocity = Speed * Dir;

    if (PhysicsVolume.bWaterVolume)
    {
        Velocity=0.6*Velocity;
    }

	if( Level.NetMode != NM_DedicatedServer )
	{
		if ( !PhysicsVolume.bWaterVolume )
		{
			Trail = Spawn(class'CrossbuzzsawTracer',self);
			Trail.Lifespan = Lifespan;

			Corona = Spawn(class'CrossbuzzsawCorona',self);
			Corona.Lifespan = Lifespan;
		}
	}

	Bounces = MaxBounces;

	super(Projectile).PostBeginPlay();
}

simulated function Tick( float DeltaTime )
{
    SetRotation(Rotator(Normal(Velocity)));

    if( Physics == PHYS_Projectile && VSizeSquared(Velocity) < ((Speed * Speed) * 0.1) )
    {
        SetPhysics(PHYS_Falling);
    }

    if( !bOutOfPropellant )
    {
        if ( TotalFlightTime <= StraightFlightTime )
        {
            TotalFlightTime += DeltaTime;
        }
        else
        {
            OuttaPropLocation = Location;
            bOutOfPropellant = true;
        }
    }

    if(  bOutOfPropellant && !bTrueBallistics )
    {
         //log(" Projectile flew "$(VSize(OrigLoc - OuttaPropLocation)/50.0)$" meters before running out of juice");
         //SetPhysics(PHYS_Falling);
         bTrueBallistics = true;
    }
}

simulated function PostNetReceive()
{
    if( ImpactActor!=None && Base != ImpactActor )
    {
        GoToState('OnWall');
    }
}

simulated state OnWall
{
    Ignores HitWall;

	function ProcessTouch (Actor Other, vector HitLocation)
	{
		local Inventory inv;

		if( Pawn(Other)!=None && Pawn(Other).Inventory!=None )
		{
			for( inv=Pawn(Other).Inventory; inv!=None; inv=inv.Inventory )
			{
				if( Crossbuzzsaw(Inv)!=None && Weapon(inv).AmmoAmount(0)<Weapon(inv).MaxAmmo(0) )
				{
					KFweapon(Inv).AddAmmo(1,0) ;
					PlaySound(Sound'KF_InventorySnd.Ammo_GenericPickup', SLOT_Pain,2*TransientSoundVolume,,400);
					if( PlayerController(Instigator.Controller)!=none )
					{
                        PlayerController(Instigator.Controller).ReceiveLocalizedMessage(class'KFmod.ProjectilePickupMessage',1);
					}
					Destroy();
				}
			}
		}
	}

	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;

		if( Corona != none )
        {
            Corona.Kill();
        }
		SetCollisionSize(75,50);

		UV2Texture=FadeColor'PatchTex.Common.PickupOverlay';
	}
}

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(BladeHitArmor,,2.0);   // implies hit a target with shield/armor
	}
	else
    {
        PlaySound(BladeHitFlesh,,2.0);
    }
}

simulated function HitWall( vector HitNormal, actor Wall )
{
    if ( (Mover(Wall) != None) && Mover(Wall).bDamageTriggered )
    {
        if ( Level.NetMode != NM_Client )
        {
            Wall.TakeDamage( Damage, instigator, Location, MomentumTransfer * Normal(Velocity), MyDamageType);
        }

        Destroy();

        return;
    }

    PlaySound(BladeHitWall,SLOT_None,0.6,,300,0.9+frand()*0.2);
    if ( EffectIsRelevant(Location,false) )
    {
        Spawn(class'CrossbuzzsawImpact',,,, rotator(hitnormal));
    }

	if ( Physics == PHYS_Projectile && Bounces > 0)
    {
        Velocity = 0.95 * (Velocity - 2.0*HitNormal*(Velocity dot HitNormal)); // slow the projectile after bounce
        Bounces = Bounces - 1;
        return;
    }
    else
    {
	   bBounce = false;
	   Stick(Wall,Location);
	}

    SetRotation(rotator(Velocity));

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

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;

		if( Corona != none )
        {
            Corona.Kill();
        }
		Velocity*=0.65;
	}
}

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

	if( Corona != none )
    {
        Corona.Kill();
    }
	Super.Destroyed();
}

defaultproperties
{
    DamageTypeHeadShot=Class'KFMod.DamTypeCrossbuzzsawHeadShot'
    BladeHitWallRef="KF_IJC_HalloweenSnd.KF_SawbladeBow_Projectile_Hit"
    BladeHitArmorRef="KF_IJC_HalloweenSnd.KF_SawbladeBow_Projectile_Hit"
    BladeHitFleshRef="KF_AxeSnd.Axe_HitFlesh"
    HeadShotDamageMult=2.000000
    Speed=1500.000000
    MaxSpeed=2000.000000
    Damage=500.000000
    MomentumTransfer=50000
    MyDamageType=Class'KFMod.DamTypeCrossbuzzsaw'
    ExplosionDecal=Class'KFMod.ShotgunDecal'
    CullDistance=7500.000000
    AmbientSoundRef="KF_IJC_HalloweenSnd.KF_SawbladeBow_Projectile_Loop"
    LifeSpan=180
    DrawType=DT_StaticMesh
    StaticMeshRef="EffectsSM.Weapons.cheetah_blade"
    DrawScale=3.000000
    AmbientGlow=0
    Style=STY_Alpha
    bUnlit=False
    bNetTemporary=False
    CollisionRadius=0
    CollisionHeight=0
    TransientSoundVolume=0.5

    AmbientVolumeScale=2.0
    SoundVolume=175
    SoundRadius=250
    bFullVolume=false
    bTrueBallistics=False
    MaxBounces=5
    Bounces=1
    bBounce=true

    Physics = PHYS_Projectile
    StraightFlightTime=0.65
    bUpdateSimulatedPosition=true
}
