//=============================================================================
// ZedGun
//=============================================================================
// Zed Eradication Device: ZEDGun- Horzine Prototype Weapon
//=============================================================================
// Killing Floor Source
// Copyright (C) 2012 Tripwire Interactive LLC
// - John "Ramm-Jaeger" Gibson
//=============================================================================
class ZEDGun extends KFWeapon;

// Motion Detector vars
var	ScriptedTexture	ScriptedScreen;
var	Shader			ShadedScreen;
var	Material		ScriptedScreenBack;
var	color			ScreenBackgroundColor;
var	texture			ScreenWeakEnemyTexture;
var	texture			ScreenMediumEnemyTexture;
var	texture			ScreenStrongEnemyTexture;
var	texture			ScreenBossEnemyTexture;
var float           EnemyUpdateTime;

var		string ScriptedScreenBackRef;
var		string ScreenWeakEnemyTextureRef;
var		string ScreenMediumEnemyTextureRef;
var		string ScreenStrongEnemyTextureRef;
var		string ScreenBossEnemyTextureRef;

// Laser site vars
var         LaserDot                    Spot;                       // The first person laser site dot
var()       float                       SpotProjectorPullback;      // Amount to pull back the laser dot projector from the hit location
var         LaserBeamEffectZED          LaserBeam;                  // Third person laser beam effect

var()		class<InventoryAttachment>	LaserAttachmentClass;      // First person laser attachment class
var 		Actor 						LaserAttachment;           // First person laser attachment

var()   sound   AlarmSound;      // An alarm that sounds when big guys get near
var     float   NextAlarmTime;   // The next time this alarm goes off

replication
{
	reliable if(Role == ROLE_Authority)
		ClientAlarmSound;
}

//=============================================================================
// Functions
//=============================================================================

static function PreloadAssets(Inventory Inv, optional bool bSkipRefCount)
{
	super.PreloadAssets(Inv, bSkipRefCount);

	default.ScriptedScreenBack = Material(DynamicLoadObject(default.ScriptedScreenBackRef, class'Material', true));
	default.ScreenWeakEnemyTexture = texture(DynamicLoadObject(default.ScreenWeakEnemyTextureRef, class'texture', true));
	default.ScreenMediumEnemyTexture = texture(DynamicLoadObject(default.ScreenMediumEnemyTextureRef, class'texture', true));
	default.ScreenStrongEnemyTexture = texture(DynamicLoadObject(default.ScreenStrongEnemyTextureRef, class'texture', true));
	default.ScreenBossEnemyTexture = texture(DynamicLoadObject(default.ScreenBossEnemyTextureRef, class'texture', true));

	if ( ZEDGun(Inv) != none )
	{
		ZEDGun(Inv).ScriptedScreenBack = default.ScriptedScreenBack;
		ZEDGun(Inv).ScreenWeakEnemyTexture = default.ScreenWeakEnemyTexture;
		ZEDGun(Inv).ScreenMediumEnemyTexture = default.ScreenMediumEnemyTexture;
		ZEDGun(Inv).ScreenStrongEnemyTexture = default.ScreenStrongEnemyTexture;
		ZEDGun(Inv).ScreenBossEnemyTexture = default.ScreenBossEnemyTexture;
	}
}

static function bool UnloadAssets()
{
	if ( super.UnloadAssets() )
	{
		default.ScriptedScreenBack = none;
		default.ScreenWeakEnemyTexture = none;
		default.ScreenMediumEnemyTexture = none;
		default.ScreenStrongEnemyTexture = none;
		default.ScreenBossEnemyTexture = none;

		return true;
	}

	return false;
}

simulated function PostBeginPlay()
{
	super.PostBeginPlay();

	if (Role == ROLE_Authority)
	{
		if (LaserBeam == None)
		{
			LaserBeam = Spawn(class'LaserBeamEffectZED');
		}
	}
}

// Returns the location of the first person alt fire stun/zap beam start
simulated function vector GetFirstPersonBeamFireStart()
{
    local vector StartTrace, StartProj;
    local vector X,Y,Z;

    GetViewAxes(X,Y,Z);

    StartTrace = Instigator.Location + Instigator.EyePosition();
    StartProj = StartTrace + X* ZEDGunAltFire(FireMode[1]).ProjSpawnOffset.X;
    StartProj = StartProj + Hand * Y * ZEDGunAltFire(FireMode[1]).ProjSpawnOffset.Y + Z * ZEDGunAltFire(FireMode[1]).ProjSpawnOffset.Z;

    return StartProj;
}

simulated function WeaponTick(float dt)
{
	local int TeamIndex, i;
	local Pawn EnemyPawn;
	local KFPlayerController KFPC;
    local array<vector> EnemyPawnLocations;
	local Vector StartTrace, EndTrace, X,Y,Z;
	local Vector HitLocation, HitNormal;
	local Actor Other;
	local vector MyEndBeamEffect;
	local coords C;
	local float MaxThreat, HighestThreatDist;
	local float UsedAlarmTime;

	super.WeaponTick(dt);

	if( Role == ROLE_Authority && LaserBeam != none )
	{
		if( bIsReloading && WeaponAttachment(ThirdPersonActor) != none )
		{
			C = WeaponAttachment(ThirdPersonActor).GetBoneCoords('tip');
			X = C.XAxis;
			Y = C.YAxis;
			Z = C.ZAxis;
		}
		else
		{
			GetViewAxes(X,Y,Z);
		}

		// the to-hit trace always starts right in front of the eye
		StartTrace = GetFirstPersonBeamFireStart();//Instigator.Location + Instigator.EyePosition() + X*Instigator.CollisionRadius;

		EndTrace = StartTrace + 65535 * X;

		Other = Trace(HitLocation, HitNormal, EndTrace, StartTrace, true);

		if (Other != None && Other != Instigator && Other.Base != Instigator )
		{
			MyEndBeamEffect = HitLocation;
		}
		else
		{
			MyEndBeamEffect = EndTrace;
		}

		LaserBeam.EndBeamEffect = MyEndBeamEffect;
		LaserBeam.EffectHitNormal = HitNormal;
	}

	if( ScriptedScreen==None )
		InitMaterials();
	ScriptedScreen.Revision++;
	if( ScriptedScreen.Revision>10 )
		ScriptedScreen.Revision = 1;

	// Update the motion tracker screen
    if ( ROLE == ROLE_Authority && Instigator != none )
	{
		if( EnemyUpdateTime <= 0 )
		{
            EnemyUpdateTime = 0.2;

            TeamIndex = Instigator.GetTeamNum();

        	KFPC = KFPlayerController(Instigator.Controller);
        	if ( KFPC != none  )
            {
        		foreach Instigator.CollidingActors(class'Pawn', EnemyPawn, 1875)
        		{
        			if ( EnemyPawn.Health > 0 && EnemyPawn.GetTeamNum() != TeamIndex )
        			{
        				EnemyPawnLocations[i] = EnemyPawn.Location;
        				// Set the Z location to threat level so we can use that on the client
                        if( KFMonster(EnemyPawn) != none )
        				{
        				    EnemyPawnLocations[i].Z = KFMonster(EnemyPawn).MotionDetectorThreat * 1000;

        				    if( KFMonster(EnemyPawn).MotionDetectorThreat > MaxThreat ||
                                (KFMonster(EnemyPawn).MotionDetectorThreat >= 3.0 && (VSize(EnemyPawn.Location - Instigator.Location) < HighestThreatDist)) )
        				    {
        				        HighestThreatDist = VSize( EnemyPawn.Location - Instigator.Location );
                                MaxThreat = KFMonster(EnemyPawn).MotionDetectorThreat;

        				    }
        				}
        				else
        				{
        				    EnemyPawnLocations[i].Z = 0;
        				}
        				i+=1;
        			}
        		}

                // Update the locations
        		for ( i = 0; i < 16; i++ )
        		{
                    if( i < EnemyPawnLocations.Length )
                    {
                        KFPC.EnemyLocation[i] = EnemyPawnLocations[i];
                    }
                    else
        			{
                        KFPC.EnemyLocation[i] = vect(0,0,0);
        			}
                }

                if( MaxThreat >= 3.0 )
                {
            		UsedAlarmTime = Level.TimeSeconds - NextAlarmTime;

                    if( UsedAlarmTime >= 0 )
            		{
                        if( HighestThreatDist < 500 )
                        {
                            if( MaxThreat >= 5.0 )
                            {
                                NextAlarmTime = Level.TimeSeconds + 0.5;
                            }
                            else
                            {
                                NextAlarmTime = Level.TimeSeconds + 1.0;
                            }
                        }
                        else
                        {
                            NextAlarmTime = Level.TimeSeconds + 2.0;
                        }

                        if( Level.NetMode == NM_DedicatedServer )
                        {
                            ClientAlarmSound();
                        }
                        PlayOwnedSound(AlarmSound,SLOT_None,2.0,,TransientSoundRadius,,false);
                    }
                }
    		}
		}
		else
		{
            EnemyUpdateTime -= dt;
		}
	}
}

simulated function ClientAlarmSound()
{
    PlayOwnedSound(AlarmSound,SLOT_None,2.0,,TransientSoundRadius,,false);
}

simulated function BringUp(optional Weapon PrevWeapon)
{
	Super.BringUp(PrevWeapon);

	if (Role == ROLE_Authority)
	{
		if (LaserBeam == None)
		{
			LaserBeam = Spawn(class'LaserBeamEffectZED');
		}
	}

	EnableLaser();
}

simulated function DetachFromPawn(Pawn P)
{
	TurnOffLaser();

	Super.DetachFromPawn(P);

	if (LaserBeam != None)
	{
		LaserBeam.Destroy();
	}
}

simulated function bool PutDown()
{
	if (LaserBeam != None)
	{
		LaserBeam.Destroy();
	}

	TurnOffLaser();

	return super.PutDown();
}

// Toggle the laser on and off
simulated function EnableLaser()
{
	if( Instigator.IsLocallyControlled() )
	{
		if( Role < ROLE_Authority  )
		{
			ServerSetLaserActive(true);
		}

		if( LaserBeam != none )
		{
			LaserBeam.SetActive(true);
		}

		if ( LaserAttachment == none )
		{
			LaserAttachment = Spawn(LaserAttachmentClass,,,,);
			AttachToBone(LaserAttachment,LaserAttachment.AttachmentBone); //'Laser_Bone'
		}
		LaserAttachment.bHidden = false;

		if (Spot == None)
		{
			Spot = Spawn(class'LaserDotZED', self);
		}
	}
}

simulated function TurnOffLaser()
{
	if( Instigator.IsLocallyControlled() )
	{
		if( Role < ROLE_Authority  )
		{
			ServerSetLaserActive(false);
		}

		//bLaserActive = false;
		LaserAttachment.bHidden = true;

		if( LaserBeam != none )
		{
			LaserBeam.SetActive(false);
		}

		if (Spot != None)
		{
			Spot.Destroy();
		}
	}
}

// Set the new fire mode on the server
function ServerSetLaserActive(bool bNewWaitForRelease)
{
	if( LaserBeam != none )
	{
		LaserBeam.SetActive(bNewWaitForRelease);
	}

	if( bNewWaitForRelease )
	{
		//bLaserActive = true;
		if (Spot == None)
		{
			Spot = Spawn(class'LaserDotZED', self);
		}
	}
	else
	{
		//bLaserActive = false;
		if (Spot != None)
		{
			Spot.Destroy();
		}
	}
}

simulated event RenderOverlays( Canvas Canvas )
{
	local int m;
	local Vector StartTrace, EndTrace;
	local Vector HitLocation, HitNormal;
	local Actor Other;
	local vector X,Y,Z;
	local coords C;

	if (Instigator == None)
		return;

	if ( Instigator.Controller != None )
		Hand = Instigator.Controller.Handedness;

	if ((Hand < -1.0) || (Hand > 1.0))
		return;

	// draw muzzleflashes/smoke for all fire modes so idle state won't
	// cause emitters to just disappear
	for (m = 0; m < NUM_FIRE_MODES; m++)
	{
		if (FireMode[m] != None)
		{
			FireMode[m].DrawMuzzleFlash(Canvas);
		}
	}

	SetLocation( Instigator.Location + Instigator.CalcDrawOffset(self) );
	SetRotation( Instigator.GetViewRotation() + ZoomRotInterp);

	// Handle drawing the laser beam dot
	if (Spot != None)
	{
		StartTrace = GetFirstPersonBeamFireStart();//Instigator.Location + Instigator.EyePosition();
		GetViewAxes(X, Y, Z);

		if( bIsReloading && Instigator.IsLocallyControlled() )
		{
			C = GetBoneCoords('Tip2');
			X = C.XAxis;
			Y = C.YAxis;
			Z = C.ZAxis;
		}

		EndTrace = StartTrace + 65535 * X;

		Other = Trace(HitLocation, HitNormal, EndTrace, StartTrace, true);

		if (Other != None && Other != Instigator && Other.Base != Instigator )
		{
			EndBeamEffect = HitLocation;
		}
		else
		{
			EndBeamEffect = EndTrace;
		}

		Spot.SetLocation(EndBeamEffect - X*SpotProjectorPullback);

		if(  Pawn(Other) != none )
		{
			Spot.SetRotation(Rotator(X));
			Spot.SetDrawScale(Spot.default.DrawScale * 0.5);
		}
		else if( HitNormal == vect(0,0,0) )
		{
			Spot.SetRotation(Rotator(-X));
			Spot.SetDrawScale(Spot.default.DrawScale);
		}
		else
		{
			Spot.SetRotation(Rotator(-HitNormal));
			Spot.SetDrawScale(Spot.default.DrawScale);
		}
	}

	//PreDrawFPWeapon();	// Laurent -- Hook to override things before render (like rotation if using a staticmesh)

	bDrawingFirstPerson = true;
	Canvas.DrawActor(self, false, false, DisplayFOV);
	bDrawingFirstPerson = false;
}


simulated function InitMaterials()
{
	if ( ScriptedScreen == none )
	{
		ScriptedScreen = ScriptedTexture(Level.ObjectPool.AllocateObject(class'ScriptedTexture'));
		ScriptedScreen.SetSize(512,512);
		ScriptedScreen.FallBackMaterial = ScriptedScreenBack;
		ScriptedScreen.Client = self;
	}

	if ( ShadedScreen == none )
	{
		ShadedScreen = Shader(Level.ObjectPool.AllocateObject(class'Shader'));
		ShadedScreen.Diffuse = ScriptedScreen;
		ShadedScreen.SelfIllumination = ScriptedScreen;
		skins[1] = ShadedScreen;
	}
}

simulated event RenderTexture(ScriptedTexture Tex)
{
	local KFPlayerController KFPC;
	local rotator KFPCRotation, EnemyRotation;
	local vector EnemyLocation;
	local int i;
	local float Threat;
	local color ScreenEnemyColor;
    local texture ScreenEnemyTexture;

    Tex.DrawTile(0, 0, Tex.USize, Tex.VSize, 0, 0, 512, 512, ScriptedScreenBack, ScreenBackgroundColor); // Draws the tile background

	KFPC = KFPlayerController(Instigator.Controller);
	if ( KFPC != none && KFPC.Pawn != none )
	{
		KFPCRotation.Yaw = KFPC.Rotation.Yaw;

		for ( i = 0; i < 16; i++ )
		{
			if ( KFPC.EnemyLocation[i].X != 0.0 || KFPC.EnemyLocation[i].Y != 0.0 )
			{
				// Decompress the threat level of this monster
                Threat = KFPC.EnemyLocation[i].Z/1000;
                if( Threat < 1.0 )
                {
                    ScreenEnemyTexture = ScreenWeakEnemyTexture;
                }
                else if( Threat <= 2.0 )
                {
                    ScreenEnemyTexture = ScreenMediumEnemyTexture;
                }
                else if( Threat <= 5.0 )
                {
                    ScreenEnemyTexture = ScreenStrongEnemyTexture;
                }
                else
                {
                    ScreenEnemyTexture = ScreenBossEnemyTexture;
                }

                ScreenEnemyColor = class'HUD'.default.WhiteColor;

                EnemyLocation = (KFPC.EnemyLocation[i] - KFPC.Pawn.Location) / 2500;
				EnemyLocation.Z = 0;

				EnemyRotation.Yaw = rotator(EnemyLocation).Yaw + 16384;

				EnemyLocation.X = VSize(EnemyLocation);
				EnemyLocation.Y = 0;

				EnemyLocation = (EnemyLocation * 768) >> (EnemyRotation - KFPCRotation);

				Tex.DrawTile(256 - EnemyLocation.X - (ScreenEnemyTexture.USize / 2), 512 - EnemyLocation.Y - (ScreenEnemyTexture.VSize / 2), ScreenEnemyTexture.USize, ScreenEnemyTexture.VSize, 0, 0, ScreenEnemyTexture.USize, ScreenEnemyTexture.VSize, ScreenEnemyTexture, ScreenEnemyColor);
			}
		}
	}
}

simulated function Destroyed()
{
	if (Spot != None)
		Spot.Destroy();

	if (LaserBeam != None)
		LaserBeam.Destroy();

	if (LaserAttachment != None)
		LaserAttachment.Destroy();

    super.Destroyed();

	if ( ScriptedScreen != none )
	{
		ScriptedScreen.SetSize(512,512);
		ScriptedScreen.FallBackMaterial = none;
		ScriptedScreen.Client = none;
		Level.ObjectPool.FreeObject(ScriptedScreen);
		ScriptedScreen = none;
	}

	if ( ShadedScreen != none )
	{
		ShadedScreen.Diffuse = none;
		ShadedScreen.Opacity = none;
		ShadedScreen.SelfIllumination = none;
		ShadedScreen.SelfIlluminationMask = none;
		Level.ObjectPool.FreeObject(ShadedScreen);
		ShadedScreen = none;
		skins[1] = none;
	}
}

// Destroy this stuff when the level changes
simulated function PreTravelCleanUp()
{
	if ( ScriptedScreen != none )
	{
		ScriptedScreen.SetSize(512,512);
		ScriptedScreen.FallBackMaterial = none;
		ScriptedScreen.Client = none;
		Level.ObjectPool.FreeObject(ScriptedScreen);
		ScriptedScreen = none;
	}

	if ( ShadedScreen != none )
	{
		ShadedScreen.Diffuse = none;
		ShadedScreen.Opacity = none;
		ShadedScreen.SelfIllumination = none;
		ShadedScreen.SelfIlluminationMask = none;
		Level.ObjectPool.FreeObject(ShadedScreen);
		ShadedScreen = none;
		skins[1] = none;
	}
}

function float GetAIRating()
{
	local AIController B;

	B = AIController(Instigator.Controller);
	if ( (B == None) || (B.Enemy == None) )
		return AIRating;

	return (AIRating + 0.0003 * FClamp(1500 - VSize(B.Enemy.Location - Instigator.Location),0,1000));
}

function byte BestMode()
{
	return 0;
}

function bool RecommendRangedAttack()
{
	return true;
}

//TODO: LONG ranged?
function bool RecommendLongRangedAttack()
{
	return true;
}

function float SuggestAttackStyle()
{
	return -1.0;
}

simulated function AnimEnd(int channel)
{
    if (FireMode[1].bIsFiring )
    {
    	LoopAnim('ChargeLoop');
    }
    else
    {
        Super.AnimEnd(channel);
    }
}

// Allow this weapon to auto reload on alt fire
simulated function AltFire(float F)
{
	if( MagAmmoRemaining < 1 && !bIsReloading &&
		 FireMode[1].NextFireTime <= Level.TimeSeconds )
	{
		// We're dry, ask the server to autoreload
		ServerRequestAutoReload();

		PlayOwnedSound(FireMode[1].NoAmmoSound,SLOT_None,2.0,,,,false);
	}

	super.AltFire(F);
}

simulated event StopFire(int Mode)
{
	if ( FireMode[Mode].bIsFiring )
	    FireMode[Mode].bInstantStop = true;
	// Handle playing fire end in the altfire class for this mode
    if (Instigator.IsLocallyControlled() && !FireMode[Mode].bFireOnRelease
        && Mode != 1 )
        FireMode[Mode].PlayFireEnd();

    FireMode[Mode].bIsFiring = false;
    FireMode[Mode].StopFiring();
    if (!FireMode[Mode].bFireOnRelease)
        ZeroFlashCount(Mode);
}

defaultproperties
{
    IdleAimAnim=Idle
    WeaponReloadAnim=Reload_Zed

    SkinRefs(0)="Kf_Weapons9_Trip_T.Weapons.ZED_cmb"
    SleeveNum=2

    ScriptedScreenBackRef="KFZED_FX_T.Screen.ZED_FX_Screen_BG_CMB"
    ScreenBackgroundColor=(R=255,B=255,G=255,A=255)
    ScreenWeakEnemyTextureRef="KFZED_FX_T.Screen.Low_Zed_Head"
    ScreenMediumEnemyTextureRef="KFZED_FX_T.Screen.Mid_Zed_Head"
    ScreenStrongEnemyTextureRef="KFZED_FX_T.Screen.Strong_Zed_Head"
    ScreenBossEnemyTextureRef="KFZED_FX_T.Screen.Boss_Zed_Head"

    Weight=8.000000
    MagCapacity=100
    ReloadRate=3.5
    ReloadAnim="Reload"
    ReloadAnimRate=1.000000
    FireModeClass(0)=Class'KFMod.ZEDGunFire'
    FireModeClass(1)=Class'KFMod.ZEDGunAltFire'
    bModeZeroCanDryFire=true
    PutDownAnim="PutDown"
    SelectSoundRef="KF_ZEDGunSnd.KF_WEP_ZED_Handling_Select"
    SelectForce="SwitchToAssaultRifle"
    AIRating=0.650000
    CurrentRating=0.650000

    Priority=205
    InventoryGroup=4
    GroupOffset=17
    PickupClass=Class'KFMod.ZEDGunPickup'
    PlayerViewOffset=(X=18.000000,Y=20.000000,Z=-6.000000)
    BobDamping=6.000000
    AttachmentClass=Class'KFMod.ZEDGunAttachment'
    IconCoords=(X1=253,Y1=146,X2=333,Y2=181)
    ItemName="Zed Eradication Device"
    Description="The Zed Eradication Device (ZED gun to its friends) is Horzine's first real ray-gun. When it doesn't explode them, it'll actually slow them down!"

    LightType=LT_None
    LightBrightness=0.000000
    LightRadius=0.000000
    MeshRef="KF_Wep_ZEDGun.ZEDGun_Trip"
    DrawScale=1.000000
    AmbientGlow=0

    ZoomTime=0.25
    FastZoomOutTime=0.2
    ZoomInRotation=(Pitch=-910,Yaw=0,Roll=2910)
    bHasAimingMode=true

    DisplayFOV=65.000000
    StandardDisplayFOV=65.0
    PlayerIronSightFOV=70
    ZoomedDisplayFOV=45

	HudImageRef="KillingFloor2HUD.WeaponSelect.ZEDGun_unselected"
	SelectedHudImageRef="KillingFloor2HUD.WeaponSelect.ZEDGun"
	TraderInfoTexture=texture'KillingFloor2HUD.Trader_Weapon_Icons.Trader_ZEDGun'
	SpotProjectorPullback=1.0
	LaserAttachmentClass=class'LaserAttachmentFirstPersonZEDGun'

	bIsTier3Weapon=true

    AlarmSound=Sound'KF_ZEDGunSnd.KF_WEP_ZED_Alert'
	UnlockedByAchievement=202
}
