class ZombieBrute extends KFMonster;

#exec load obj file=ScrnZedPack_T.utx
#exec load obj file=ScrnZedPack_S.uax
#exec load obj file=ScrnZedPack_A.ukx
#exec OBJ LOAD FILE=KFWeaponSound.uax

var bool bChargingPlayer;
var bool bClientCharge;
var bool bFrustrated;
var int MaxRageCounter; // Maximum amount of players we can hit before calming down
var int RageCounter; // Decreases each time we successfully hit a player
var float RageSpeedTween;
var int TwoSecondDamageTotal;
var float LastDamagedTime;
var int RageDamageThreshold;
var int BlockHitsLanded; // Hits made while blocking or raging
var float BlockMeleeDmgMul;     //Multiplier for melee damage taken, when Brute is blocking (no matter where the hit was landed)
var float HeadShotgunDmgMul;    //Multiplier for shotgun damage taken into UNBLOCKED head
var float HeadBulletDmgMul;    //Multiplier for non-sniper bullet damage taken into UNBLOCKED head


var name ChargingAnim;
var Sound RageSound;

// View shaking for players
var() vector	ShakeViewRotMag;
var() vector	ShakeViewRotRate;
var() float		ShakeViewRotTime;
var() vector	ShakeViewOffsetMag;
var() vector	ShakeViewOffsetRate;
var() float		ShakeViewOffsetTime;

var float PushForce;
var vector PushAdd; // Used to add additional height to push
var float RageDamageMul; // Multiplier for hit damage when raging
var float RageBumpDamage; // Damage done when we hit other specimens while raging
var float BlockAddScale; // Additional head scale when blocking
var bool bBlockedHS;
var bool bBlocking;
var bool bServerBlock;
var bool bClientBlock;
var float BlockDmgMul; // Multiplier for damage taken from blocked shots
var float BlockFireDmgMul;
var float BurnGroundSpeedMul; // Multiplier for ground speed when burning

replication
{
	reliable if(Role == ROLE_Authority)
		bChargingPlayer, bServerBlock;
}

simulated function PostNetBeginPlay()
{
	super.PostNetBeginPlay();
	EnableChannelNotify(1,1);
	EnableChannelNotify(2,1);
	AnimBlendParams(1, 1.0, 0.0,, SpineBone1);
}

function ServerRaiseBlock()
{
	bServerBlock = true;
	SetAnimAction('BlockLoop');
}

function ServerLowerBlock()
{
	local name Sequence;
	local float Frame, Rate;

	bServerBlock = false;
	GetAnimParams(1, Sequence, Frame, Rate);
	if (Sequence == 'BlockLoop')
		AnimStopLooping(1);
}

simulated function PostNetReceive()
{
	local name Sequence;
	local float Frame, Rate;

	if(bClientCharge != bChargingPlayer)
	{
		bClientCharge = bChargingPlayer;
		if (bChargingPlayer)
		{
			MovementAnims[0] = ChargingAnim;
			MeleeAnims[0] = 'BruteRageAttack';
			MeleeAnims[1] = 'BruteRageAttack';
			MeleeAnims[2] = 'BruteRageAttack';
		}
		else
		{
			MovementAnims[0] = default.MovementAnims[0];
			MeleeAnims[0] = default.MeleeAnims[0];
			MeleeAnims[1] = default.MeleeAnims[1];
			MeleeAnims[2] = default.MeleeAnims[2];
		}
	}

	if (bClientBlock != bServerBlock)
	{
		bClientBlock = bServerBlock;
		if (bClientBlock)
			SetAnimAction('BlockLoop');
		else
		{
			GetAnimParams(1, Sequence, Frame, Rate);
			if (Sequence == 'BlockLoop')
				AnimStopLooping(1);
		}
	}
}

simulated function Tick(float DeltaTime)
{
    super.Tick(DeltaTime);

	if (Role == ROLE_Authority)
	{
		// Lock to target when attacking (except on beginner!)
		if (bShotAnim && Level.Game.GameDifficulty >= 2.0)
			if (LookTarget != none)
				Acceleration = AccelRate * Normal(LookTarget.Location - Location);

		// Block according to rules
		if (Role == ROLE_Authority && !bServerBlock && !bShotAnim)
			if (Controller != none && Controller.Target != none)
				ServerRaiseBlock();
	}
}

// Override to always move when attacking
function RangedAttack(Actor A)
{
	if (bShotAnim || Physics == PHYS_Swimming)
		return;
	else if (CanAttack(A))
	{
		if (bChargingPlayer)
			SetAnimAction('AoeClaw');
		else
		{
			if (Rand(BlockHitsLanded) < 1)
				SetAnimAction('BlockClaw');
			else
				SetAnimAction('Claw');
		}

		bShotAnim = true;
		return;
	}
}

function bool IsHeadShot(vector Loc, vector Ray, float AdditionalScale)
{
	local float D;
	local float AddScale;
	local bool bIsBlocking;

	bBlockedHS = false;

	if (bServerBlock && !IsTweening(1))
	{
		bIsBlocking = true;
		AddScale = AdditionalScale + BlockAddScale;
	}
	else
		AddScale = AdditionalScale + 1.0;

	if (Super.IsHeadShot(Loc, Ray, AddScale))
	{
		if (bIsBlocking)
		{
			D = vector(Rotation) dot Ray;
			if (-D > 0.20) {
				bBlockedHS = true;
				return false;
			}
			else
				return true;
		}
		else
			return true;
	}
	else
		return false;
}

// Damage, which doen't make headshots, always does full damage to Brute
// Melee weapon resistance is applied only while blocking
function TakeDamage(int Damage, Pawn InstigatedBy, Vector HitLocation, Vector Momentum, class<DamageType> DamType, optional int HitIndex)
{
	local bool bIsHeadShot;
    local class<KFWeaponDamageType> KFDamType;

    KFDamType = class<KFWeaponDamageType>(DamType);

    // damage, which doen't make headshots, always does full damage to Brute -- PooSH
    if (KFDamType != none && KFDamType.default.bCheckForHeadShots) {

        bIsHeadShot = IsHeadShot(HitLocation, normal(Momentum), 1.0);

        if (!bIsHeadShot && bBlockedHS)
        {
            if (class<KFProjectileWeaponDamageType>(DamType) != none)
                PlaySound(class'MetalHitEmitter'.default.ImpactSounds[rand(3)],, 128);
            else if (class<DamTypeChainsaw>(DamType) != none)
                PlaySound(Sound'KF_ChainsawSnd.Chainsaw_Impact_Metal',, 128);
            else if (class<DamTypeMelee>(DamType) != none)
                PlaySound(Sound'KF_KnifeSnd.Knife_HitMetal',, 128);

            if ( KFDamType.default.bDealBurningDamage && !KFDamType.default.bIsPowerWeapon )
                Damage *= BlockFireDmgMul; // Fire damage isn't reduced as much, excluding TrenchGun
            else
                Damage *= BlockDmgMul; // Greatly reduce damage as we only hit the metal plating
        }
        else if ( bServerBlock && class<DamTypeMelee>(DamType) != none )
            Damage *= BlockMeleeDmgMul; // Give Brute higher melee damage resistance, but apply it only if Brute is blocking
        else if ( bIsHeadShot ) {
            if ( KFDamType.default.bIsPowerWeapon )
                Damage *= HeadShotgunDmgMul; // Give Brute's head resistance to stotguns
            else if ( !KFDamType.default.bSniperWeapon && !KFDamType.default.bIsMeleeDamage )
                Damage *= HeadBulletDmgMul; // Give damage bonus to commando and gunslinger weapons
        }
    }

	// Record damage over 2-second frames
	if (LastDamagedTime < Level.TimeSeconds)
	{
		TwoSecondDamageTotal = 0;
		LastDamagedTime = Level.TimeSeconds + 2;
	}
	TwoSecondDamageTotal += Damage;

	// If criteria is met make him rage
	if (!bDecapitated && !bChargingPlayer && TwoSecondDamageTotal > RageDamageThreshold)
	{
		StartCharging();
		if (InstigatedBy != None)
			if (Controller.Target != InstigatedBy)
				MonsterController(Controller).ChangeEnemy(InstigatedBy, Controller.CanSee(InstigatedBy));
	}

	if (InstigatedBy == none || KFDamType == none)
		Super(Monster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType); // skip NONE-reference error
	else
		Super(KFMonster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType);

	if (bDecapitated)
		Died(InstigatedBy.Controller, DamType, HitLocation);
}

function TakeFireDamage(int Damage, Pawn Instigator)
{
	Super.TakeFireDamage(Damage, Instigator);

	// Adjust movement speed if not charging
	if (!bChargingPlayer)
	{
		if (bBurnified)
			GroundSpeed = GetOriginalGroundSpeed() * BurnGroundSpeedMul;
		else
			GroundSpeed = GetOriginalGroundSpeed();
	}
}

function ClawDamageTarget()
{
	local KFHumanPawn HumanTarget;
	local float UsedMeleeDamage;
	local Actor OldTarget;
	local name Sequence;
	local float Frame, Rate;
	local bool bHitSomeone;

	if (MeleeDamage > 1)
		UsedMeleeDamage = (MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1));
	else
		UsedMeleeDamage = MeleeDamage;

	GetAnimParams(1, Sequence, Frame, Rate);

	if (Controller != none && Controller.Target != none)
	{
		if (Sequence == 'BruteRageAttack')
		{
			OldTarget = Controller.Target;
			foreach VisibleCollidingActors(class'KFHumanPawn', HumanTarget, MeleeRange + class'KFHumanPawn'.default.CollisionRadius)
			{
				bHitSomeone = ClawDamageSingleTarget(UsedMeleeDamage, HumanTarget);
			}
			Controller.Target = OldTarget;
			if (bHitSomeone)
				BlockHitsLanded++;
		}
		else if (Sequence != 'BruteAttack1' && Sequence != 'BruteAttack2' && Sequence != 'DoorBash') // Block attack
		{
			bHitSomeone = ClawDamageSingleTarget(UsedMeleeDamage, Controller.Target);
			if (bHitSomeone)
				BlockHitsLanded++;
		}
		else
			bHitSomeone = ClawDamageSingleTarget(UsedMeleeDamage, Controller.Target);

		if (bHitSomeone)
			PlaySound(MeleeAttackHitSound, SLOT_Interact, 1.25);
	}
}

function bool ClawDamageSingleTarget(float UsedMeleeDamage, Actor ThisTarget)
{
	local Pawn HumanTarget;
	local KFPlayerController HumanTargetController;
	local bool bHitSomeone;
	local float EnemyAngle;
	local vector PushForceVar;

	EnemyAngle = Normal(ThisTarget.Location - Location) dot vector(Rotation);
	if (EnemyAngle > 0)
	{
		Controller.Target = ThisTarget;
		if (MeleeDamageTarget(UsedMeleeDamage, vect(0, 0, 0)))
		{
			HumanTarget = KFHumanPawn(ThisTarget);
			if (HumanTarget != None)
			{
				EnemyAngle = (EnemyAngle * 0.5) + 0.5; // Players at sides get knocked back half as much
				PushForceVar = (PushForce * Normal(HumanTarget.Location - Location) * EnemyAngle) + PushAdd;
				if (!bChargingPlayer)
					PushForceVar *= 0.85;

				// (!) I'm sure the VeterancyName string is localized but I'm not sure of another way compatible with ServerPerks
				if (KFPlayerReplicationInfo(HumanTarget.Controller.PlayerReplicationInfo).ClientVeteranSkill != none)
					if (KFPlayerReplicationInfo(HumanTarget.Controller.PlayerReplicationInfo).ClientVeteranSkill
						.default.VeterancyName == "Berserker")
							PushForceVar *= 0.75;

				if (!(HumanTarget.Physics == PHYS_WALKING || HumanTarget.Physics == PHYS_NONE))
					PushForceVar *= vect(1, 1, 0); // (!) Don't throw upwards if we are not on the ground - adjust for more flexibility

				HumanTarget.AddVelocity(PushForceVar);

				HumanTargetController = KFPlayerController(HumanTarget.Controller);
				if (HumanTargetController != None)
					HumanTargetController.ShakeView(ShakeViewRotMag, ShakeViewRotRate, ShakeViewRotTime,
						ShakeViewOffsetMag, ShakeViewOffsetRate, ShakeViewOffsetTime);

				bHitSomeone = true;
			}
		}
	}

	return bHitSomeone;
}

function StartCharging()
{
	// How many times should we hit before we cool down?
	if (Level.Game.NumPlayers <= 3)
		MaxRageCounter = 2;
	else
		MaxRageCounter = 3;

	RageCounter = MaxRageCounter;
	PlaySound(RageSound, SLOT_Talk, 255);
	GotoState('RageCharging');
}

state RageCharging
{
Ignores StartCharging;

    function PlayDirectionalHit(Vector HitLoc)
    {
        if (!bShotAnim)
            super.PlayDirectionalHit(HitLoc);
    }

    function bool CanGetOutOfWay()
    {
        return false;
    }

    function bool CanSpeedAdjust()
    {
        return false;
    }

	function BeginState()
	{
		bFrustrated = false;
        bChargingPlayer = true;
		RageSpeedTween = 0.0;
		if (Level.NetMode != NM_DedicatedServer)
			ClientChargingAnims();

		NetUpdateTime = Level.TimeSeconds - 1;
	}

	function EndState()
	{
        bChargingPlayer = false;

        BruteZombieController(Controller).RageFrustrationTimer = 0;

		if (Health > 0)
		{
			GroundSpeed = GetOriginalGroundSpeed();
			if (bBurnified)
				GroundSpeed *= BurnGroundSpeedMul;
		}

		if( Level.NetMode!=NM_DedicatedServer )
			ClientChargingAnims();

		NetUpdateTime = Level.TimeSeconds - 1;
	}

	function Tick(float Delta)
	{
		if (!bShotAnim)
		{
			RageSpeedTween = FClamp(RageSpeedTween + (Delta * 0.75), 0, 1.0);
			GroundSpeed = OriginalGroundSpeed + ((OriginalGroundSpeed * 0.75 / MaxRageCounter * (RageCounter + 1) * RageSpeedTween));
			if (bBurnified)
				GroundSpeed *= BurnGroundSpeedMul;
		}

        Global.Tick(Delta);
	}

	function Bump(Actor Other)
	{
        local KFMonster KFM;

        KFM = KFMonster(Other);

        // Hurt enemies that we run into while raging
        if (!bShotAnim && KFM != None && ZombieBrute(Other) == None && Pawn(Other).Health > 0)
			Other.TakeDamage(RageBumpDamage, self, Other.Location, Velocity * Other.Mass, class'DamTypePoundCrushed');
		else Global.Bump(Other);
	}

	function bool MeleeDamageTarget(int HitDamage, vector PushDir)
	{
		local bool DamDone, bWasEnemy;

		bWasEnemy = (Controller.Target == Controller.Enemy);

		DamDone = Super.MeleeDamageTarget(HitDamage * RageDamageMul, vect(0, 0, 0));

		if (bWasEnemy && DamDone)
		{
			ChangeTarget();
			CalmDown();
		}

		return DamDone;
	}

	function CalmDown()
	{
		RageCounter = FClamp(RageCounter - 1, 0, MaxRageCounter);
		if (RageCounter == 0)
			GotoState('');
	}

	function ChangeTarget()
	{
		local Controller C;
		local Pawn BestPawn;
		local float Dist, BestDist;

		for (C = Level.ControllerList; C != none; C = C.NextController)
			if (C.Pawn != none && KFHumanPawn(C.Pawn) != none)
			{
				Dist = VSize(C.Pawn.Location - Location);
				if (C.Pawn == Controller.Target)
					Dist += GroundSpeed * 4;

				if (BestPawn == none)
				{
					BestPawn = C.Pawn;
					BestDist = Dist;
				}
				else if (Dist < BestDist)
				{
					BestPawn = C.Pawn;
					BestDist = Dist;
				}
			}

		if (BestPawn != none && BestPawn != Controller.Enemy)
			MonsterController(Controller).ChangeEnemy(BestPawn, Controller.CanSee(BestPawn));
	}
}

// Override to prevent stunning
function bool FlipOver()
{
	return false;
}

// Shouldn't fight with our own
function bool SameSpeciesAs(Pawn P)
{
	return (ZombieBrute(P) != none);
}

// ------------------------------------------------------
// Animation --------------------------------------------
// ------------------------------------------------------

function PlayTakeHit(vector HitLocation, int Damage, class<DamageType> DamageType)
{
	if (Level.TimeSeconds - LastPainAnim < MinTimeBetweenPainAnims)
		return;

    // Uncomment this if we want some damage to make him drop his block
    /*if( !Controller.IsInState('WaitForAnim') && Damage >= 10 )
        PlayDirectionalHit(HitLocation);*/

	LastPainAnim = Level.TimeSeconds;

	if (Level.TimeSeconds - LastPainSound < MinTimeBetweenPainSounds)
		return;

	LastPainSound = Level.TimeSeconds;
	PlaySound(HitSound[0], SLOT_Pain,1.25,,400);
}

// Overridden to handle playing upper body only attacks when moving
simulated event SetAnimAction(name NewAction)
{
	if (NewAction=='')
		return;

	if (NewAction == 'Claw')
	{
		NewAction = MeleeAnims[rand(2)];
		CurrentDamType = ZombieDamType[0];
	}
	else if (NewAction == 'BlockClaw')
	{
		NewAction = 'BruteBlockSlam';
		CurrentDamType = ZombieDamType[0];
	}
	else if (NewAction == 'AoeClaw')
	{
		NewAction = 'BruteRageAttack';
		CurrentDamType = ZombieDamType[0];
	}
	else if (NewAction == 'DoorBash')
		CurrentDamType = ZombieDamType[Rand(3)];

	ExpectingChannel = DoAnimAction(NewAction);

	if (AnimNeedsWait(NewAction))
		bWaitForAnim = true;
	else
		bWaitForAnim = false;

	if (Level.NetMode != NM_Client)
	{
		AnimAction = NewAction;
		bResetAnimAct = True;
		ResetAnimActTime = Level.TimeSeconds+0.3;
	}
}

simulated function int DoAnimAction( name AnimName )
{
	if (AnimName=='BruteAttack1' || AnimName=='BruteAttack2' || AnimName=='ZombieFireGun' || AnimName == 'DoorBash')
	{
		if (Role == ROLE_Authority)
			ServerLowerBlock();
		AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
		PlayAnim(AnimName,, 0.1, 1);
		return 1;
	}
	else if (AnimName == 'BruteRageAttack')
	{
		if (Role == ROLE_Authority)
			ServerLowerBlock();
		AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
		PlayAnim(AnimName,, 0.1, 1);
		return 1;
	}
	else if (AnimName == 'BlockLoop')
	{
		AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
		LoopAnim(AnimName,, 0.25, 1);
		return 1;
	}
	else if (AnimName == 'BruteBlockSlam')
	{
		AnimBlendParams(2, 1.0, 0.0,, FireRootBone);
		PlayAnim(AnimName,, 0.1, 2);
		return 2;
	}
	return Super.DoAnimAction(AnimName);
}

// The animation is full body and should set the bWaitForAnim flag
simulated function bool AnimNeedsWait(name TestAnim)
{
    if (TestAnim == 'DoorBash')
        return true;

    return false;
}

simulated function AnimEnd(int Channel)
{
	local name Sequence;
	local float Frame, Rate;

	GetAnimParams(Channel, Sequence, Frame, Rate);

	// Don't allow notification for a looping animation
	if (Sequence == 'BlockLoop')
		return;

	// Disable channel 2 when we're done with it
	if (Channel == 2 && Sequence == 'BruteBlockSlam')
	{
		AnimBlendParams(2, 0);
		bShotAnim = false;
		return;
	}

	Super.AnimEnd(Channel);
}

simulated function ClientChargingAnims()
{
	PostNetReceive();
}

function PlayHit(float Damage, Pawn InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum, optional int HitIdx)
{
	local Actor A;
	if (bBlockedHS)
		A = Spawn(class'BlockHitEmitter', InstigatedBy,, HitLocation, rotator(Normal(HitLocation - Location)));
	else
		Super.PlayHit(Damage, InstigatedBy, HitLocation, damageType, Momentum, HitIdx);
}

defaultproperties
{
     RageDamageThreshold=50
     BlockMeleeDmgMul=0.500000
     HeadShotgunDmgMul=0.500000
     HeadBulletDmgMul=1.250000
     ChargingAnim="BruteRun"
     RageSound=SoundGroup'ScrnZedPack_S.Brute.BruteRage'
     ShakeViewRotMag=(X=500.000000,Y=500.000000,Z=600.000000)
     ShakeViewRotRate=(X=12500.000000,Y=12500.000000,Z=12500.000000)
     ShakeViewRotTime=6.000000
     ShakeViewOffsetMag=(X=5.000000,Y=10.000000,Z=5.000000)
     ShakeViewOffsetRate=(X=300.000000,Y=300.000000,Z=300.000000)
     ShakeViewOffsetTime=3.500000
     PushForce=860.000000
     PushAdd=(Z=150.000000)
     RageDamageMul=1.100000
     RageBumpDamage=4.000000
     BlockAddScale=2.500000
     BlockDmgMul=0.100000
     BlockFireDmgMul=1.000000
     BurnGroundSpeedMul=0.600000
     MeleeAnims(0)="BruteAttack1"
     MeleeAnims(1)="BruteAttack2"
     MeleeAnims(2)="BruteBlockSlam"
     MoanVoice=SoundGroup'ScrnZedPack_S.Brute.BruteTalk'
     BleedOutDuration=7.000000
     ZombieFlag=3
     MeleeDamage=20
     damageForce=25000
     bFatAss=True
     KFRagdollName="FleshPound_Trip"
     MeleeAttackHitSound=SoundGroup'ScrnZedPack_S.Brute.BruteHitPlayer'
     JumpSound=SoundGroup'ScrnZedPack_S.Brute.BruteJump'
     SpinDamConst=20.000000
     SpinDamRand=20.000000
     bMeleeStunImmune=True
     bUseExtendedCollision=True
     ColOffset=(Z=52.000000)
     ColRadius=35.000000
     ColHeight=25.000000
     SeveredArmAttachScale=1.300000
     SeveredLegAttachScale=1.200000
     SeveredHeadAttachScale=1.500000
     DetachedArmClass=Class'ScrnZedPack.SeveredArmBrute'
     DetachedLegClass=Class'ScrnZedPack.SeveredLegBrute'
     DetachedHeadClass=Class'ScrnZedPack.SeveredHeadBrute'
     PlayerCountHealthScale=0.250000
     OnlineHeadshotOffset=(X=22.000000,Z=68.000000)
     OnlineHeadshotScale=1.300000
     HeadHealth=210.000000
     PlayerNumHeadHealthScale=0.150000
     MotionDetectorThreat=5.000000
     HitSound(0)=SoundGroup'ScrnZedPack_S.Brute.BrutePain'
     DeathSound(0)=SoundGroup'ScrnZedPack_S.Brute.BruteDeath'
     ChallengeSound(0)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge'
     ChallengeSound(1)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge'
     ChallengeSound(2)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge'
     ChallengeSound(3)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge'
     ScoringValue=60
     IdleHeavyAnim="BruteIdle"
     IdleRifleAnim="BruteIdle"
     RagDeathUpKick=100.000000
     MeleeRange=85.000000
     GroundSpeed=140.000000
     WaterSpeed=120.000000
     HealthMax=1000.000000
     Health=1000
     HeadHeight=2.500000
     HeadScale=1.300000
     MenuName="Brute.se"
     ControllerClass=Class'ScrnZedPack.BruteZombieController'
     MovementAnims(0)="BruteWalkC"
     MovementAnims(1)="BruteWalkC"
     WalkAnims(0)="BruteWalkC"
     WalkAnims(1)="BruteWalkC"
     WalkAnims(2)="RunL"
     WalkAnims(3)="RunR"
     IdleCrouchAnim="BruteIdle"
     IdleWeaponAnim="BruteIdle"
     IdleRestAnim="BruteIdle"
     AmbientSound=SoundGroup'ScrnZedPack_S.Brute.BruteIdle1Shot'
     Mesh=SkeletalMesh'ScrnZedPack_A.BruteMesh'
     PrePivot=(Z=0.000000)
     Skins(0)=Combiner'ScrnZedPack_T.Brute.Brute_Final'
     Mass=600.000000
     RotationRate=(Yaw=45000,Roll=0)
}
