The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
//=============================================================================
// Shield Gun
//=============================================================================
class ShieldGun extends Weapon
    config(user);

#EXEC OBJ LOAD FILE=InterfaceContent.utx

var Sound       ShieldHitSound;
var String		ShieldHitForce;

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

simulated function DoAutoSwitch()
{
}

simulated event RenderOverlays( Canvas Canvas )
{
    local int m;

    if ((Hand < -1.0) || (Hand > 1.0))
    {
		for (m = 0; m < NUM_FIRE_MODES; m++)
		{
			if (FireMode[m] != None)
			{
				FireMode[m].DrawMuzzleFlash(Canvas);
			}
		}
	}
    Super.RenderOverlays(Canvas);
}

// AI Interface
function GiveTo(Pawn Other, optional Pickup Pickup)
{
	Super.GiveTo(Other, Pickup);
	
	if ( Bot(Other.Controller) != None )
		Bot(Other.Controller).bHasImpactHammer = true;
}

function bool CanAttack(Actor Other)
{
	return true;
}

simulated function Timer()
{
	local Bot B;

    if (ClientState == WS_BringUp)
    {
		// check if owner is bot waiting to do impact jump
		B = Bot(Instigator.Controller);
		if ( (B != None) && B.bPreparingMove && (B.ImpactTarget != None) )
		{
			B.ImpactJump();
			B = None;
		}
    }
	Super.Timer();
	if ( (B != None) && (B.Enemy != None) )
		BotFire(false);
}

function FireHack(byte Mode)
{
	if ( Mode == 0 )
	{
		FireMode[0].PlayFiring();
		FireMode[0].FlashMuzzleFlash();
		FireMode[0].StartMuzzleSmoke();
		IncrementFlashCount(0);
	}
}

/* BestMode()
choose between regular or alt-fire
*/
function byte BestMode()
{
	local float EnemyDist;
	local bot B;

	B = Bot(Instigator.Controller);
	if ( (B == None) || (B.Enemy == None) )
		return 1;

	EnemyDist = VSize(B.Enemy.Location - Instigator.Location);
	if ( EnemyDist > 2 * Instigator.GroundSpeed )
		return 1;
	if ( (B.MoveTarget != B.Enemy) && ((EnemyDist > 0.5 * Instigator.GroundSpeed) 
		|| (((B.Enemy.Location - Instigator.Location) Dot vector(Instigator.Rotation)) <= 0)) )
		return 1;
	return 0;
}

// super desireable for bot waiting to impact jump
function float GetAIRating()
{
	local Bot B;
	local float EnemyDist;

	B = Bot(Instigator.Controller);
	if ( B == None )
		return AIRating;

	if ( B.bPreparingMove && (B.ImpactTarget != None) )
		return 9;

	if ( B.PlayerReplicationInfo.HasFlag != None )
	{
		if ( Instigator.Health < 50 )
			return AIRating + 0.35;
		return AIRating + 0.25;
	}
		
	if ( B.Enemy == None )
		return AIRating;

	EnemyDist = VSize(B.Enemy.Location - Instigator.Location);
	if ( B.Stopped() && (EnemyDist > 100) )
		return 0.1;
		
	if ( (EnemyDist < 750) && (B.Skill <= 2) && !B.Enemy.IsA('Bot') && (ShieldGun(B.Enemy.Weapon) != None) )
		return FClamp(300/(EnemyDist + 1), 0.6, 0.75);

	if ( EnemyDist > 400 )
		return 0.1;
	if ( (Instigator.Weapon != self) && (EnemyDist < 120) )
		return 0.25;

	return ( FMin(0.6, 90/(EnemyDist + 1)) );
}

// End AI interface

function AdjustPlayerDamage( out int Damage, Pawn InstigatedBy, Vector HitLocation, 
						         out Vector Momentum, class<DamageType> DamageType)
{
    local int Drain;
	local vector Reflect;
    local vector HitNormal;
    local float DamageMax;

	DamageMax = 100.0;
	if ( DamageType == class'Fell' )
		DamageMax = 20.0;
    else if( !DamageType.default.bArmorStops || (DamageType == class'DamTypeShieldImpact' && InstigatedBy == Instigator) )
        return;

    if ( CheckReflect(HitLocation, HitNormal, 0) )
    {
        Drain = Min( Ammo[1].AmmoAmount*2, Damage );
		Drain = Min(Drain,DamageMax);
	    Reflect = MirrorVectorByNormal( Normal(Location - HitLocation), Vector(Instigator.Rotation) );
        Damage -= Drain;
        Momentum *= 1.25;
        Ammo[1].UseAmmo(Drain/2);
        DoReflectEffect(Drain/2);
    }
}

function DoReflectEffect(int Drain)
{
    PlaySound(ShieldHitSound, SLOT_None);
    ShieldAltFire(FireMode[1]).TakeHit(Drain);
    ClientTakeHit(Drain);
}

simulated function ClientTakeHit(int Drain)
{
	ClientPlayForceFeedback(ShieldHitForce);
    ShieldAltFire(FireMode[1]).TakeHit(Drain);
}

function bool CheckReflect( Vector HitLocation, out Vector RefNormal, int AmmoDrain )
{
    local Vector HitDir;
    local Vector FaceDir;

    if (!FireMode[1].bIsFiring || Ammo[0].AmmoAmount == 0) return false;

    FaceDir = Vector(Instigator.Controller.Rotation);
    HitDir = Normal(Instigator.Location - HitLocation + Vect(0,0,8));
    //Log(self@"HitDir"@(FaceDir dot HitDir));

    RefNormal = FaceDir;

    if ( FaceDir dot HitDir < -0.37 ) // 68 degree protection arc
    {
        if (AmmoDrain > 0)
            Ammo[0].UseAmmo(AmmoDrain);
        return true;
    }
    return false;
}

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

function float SuggestAttackStyle()
{
    return 0.8;
}

function float SuggestDefenseStyle()
{
    return -0.8;
}

simulated function float ChargeBar()
{
	return FMin(1,FireMode[0].HoldTime/ShieldFire(FireMode[0]).FullyChargedTime);
} 

defaultproperties
{
    ItemName="Shield Gun"
    IconMaterial=Material'InterfaceContent.Hud.SkinA'
    IconCoords=(X1=200,Y1=281,X2=321,Y2=371)

	bShowChargingBar=true
    bCanThrow=false
    FireModeClass(0)=ShieldFire
    FireModeClass(1)=ShieldAltFire
    InventoryGroup=1
    Mesh=mesh'Weapons.ShieldGun_1st'
    BobDamping=2.2
    PickupClass=class'ShieldGunPickup'
    EffectOffset=(X=15.0,Y=6.7,Z=1.2)
    bMeleeWeapon=true
    ShieldHitSound=Sound'WeaponSounds.ShieldGun.ShieldReflection'
    DrawScale=0.4
    PutDownAnim=PutDown
    DisplayFOV=60
    PlayerViewOffset=(X=2,Y=-0.7,Z=-2.7)
    PlayerViewPivot=(Pitch=500,Roll=0,Yaw=500)

    UV2Texture=Material'XGameShaders.WeaponEnvShader'    

    AttachmentClass=class'ShieldAttachment'
    SelectSound=Sound'WeaponSounds.ShieldGun_change'
	SelectForce="ShieldGun_change"
	ShieldHitForce="ShieldReflection"

	AIRating=0.35
	CurrentRating=0.35

	DefaultPriority=2
}