#using scripts\codescripts\struct;

#using scripts\shared\ai_shared;
#using scripts\shared\array_shared;
#using scripts\shared\callbacks_shared;
#using scripts\shared\clientfield_shared;
#using scripts\shared\fx_shared;
#using scripts\shared\math_shared;
#using scripts\shared\scene_shared;
#using scripts\shared\spawner_shared;
#using scripts\shared\util_shared;
#using scripts\shared\visionset_mgr_shared;
#using scripts\shared\_burnplayer;

#using scripts\shared\ai\systems\animation_state_machine_utility;
#using scripts\shared\ai\systems\animation_state_machine_notetracks;
#using scripts\shared\ai\systems\animation_state_machine_mocomp;
#using scripts\shared\ai\archetype_utility;
#using scripts\shared\ai\systems\behavior_tree_utility;
#using scripts\shared\ai\systems\blackboard;
#using scripts\shared\ai\systems\debug;
#using scripts\shared\ai\systems\gib;
#using scripts\shared\ai\zombie_utility;
#using scripts\shared\ai\archetype_mocomps_utility;

#using scripts\shared\weapons\_weaponobjects;

                                                                                                            	  	  	              	   	                                                                                                                                       	                                                                                                                                                            	                                                                                                                                                                
   	     	                                                                                                                                                                                                                                                                                                                                                                                                                                                             	                                                                                                                	         	    	                                                                                                    
        	                                                                                                                                                	                                                                                                                      	                                                                                     	                                                                                     	                                                            	                                                                                   	                                                                                     	                                                                                               	                                                                                                 	                                                                   	                                                                   	                                                                                                                 	                                                                                                                              	                                                                                                                       	                                                   	                                                                           	                                                                                 	                                       	                             	                                             	                                  	                                                                                 	                                                                                                                                                	      
                                                                                                                                               	
           	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              	  	                                 	                  	    	                                                      	   	        	                                                                         	                      	             	                                                                                                                                       	  	                                                                                                          	     	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            	                                                                                                      	                                                                                                     	                                                                                                     	                                                                                                	     	                                                                                                                                                                                                                                                                                                                                                                                                                                	     	    	                                                                                                                                    	                      	              	  	             	  
                                                                                                                                                                                                          	     	                                                                                   
                                                                                                                                                                                                                                                                                                                                                                                                                                                                           	                                                                                           
                                                                  	                             	  	                                      
    	   	                                                                                                                                         	                                	                                                                                                                                                                                                                                                                                                                                                                    	        	     	              	    	   	               	                                              	                                                           	                               	                     	                                                                                                           	                                                                 	                                                              	                                                                                                                                             	                            	                                     	                                       	                                                                                                              	   	                  	       	                                                    	                   	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            	                                                                                                 	                                    	                                                                                        	           	                        	                                            	                                             	                                                   	                                                             	                                                         	                                                                    	                                                                                                                                                                                                                                	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  	    	         	                                                              	                                                          	                                   	                                   	                                                    	                                       	                                           	     	                                                                                                                                                                                                                                                      	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         	                                                               	        	                                                                                                                              	                                                                                                                     	                               	                                                      	                          
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         	   	                                                                                                                                                                                                                                  	                                                                                                                                                                                                                                                                                                                                                                                                                                                   

#precache( "xmodel", "c_zom_mech_armor_knee_left" );
#precache( "xmodel", "c_zom_mech_armor_knee_right" );
#precache( "xmodel", "c_zom_mech_armor_shoulder_left" );
#precache( "xmodel", "c_zom_mech_armor_shoulder_right" );
#precache( "xmodel", "c_zom_mech_faceplate" );
#precache( "xmodel", "c_zom_mech_powersupply_cap" );
#precache( "xmodel", "c_zom_mech_gun_barrel" );



#namespace MechzBehavior;

function autoexec init()
{
	// INIT BEHAVIORS
	InitMechzBehaviorsAndASM();
	
	// INIT BLACKBOARD	
	spawner::add_archetype_spawn_function( "mechz", &ArchetypeMechzBlackboardInit );

	// INIT MECHZ ON SPAWN
	spawner::add_archetype_spawn_function( "mechz", &MechzServerUtils::mechzSpawnSetup );

	clientfield::register( "actor", "mechz_ft", 5000, 1, "int" );
	clientfield::register( "actor", "mechz_faceplate_detached", 5000, 1, "int" );
	clientfield::register( "actor", "mechz_powercap_detached", 5000, 1, "int" );
	clientfield::register( "actor", "mechz_claw_detached", 5000, 1, "int" );
	clientfield::register( "actor", "mechz_115_gun_firing", 5000, 1, "int" );
	clientfield::register( "actor", "mechz_rknee_armor_detached", 5000, 1, "int" );
	clientfield::register( "actor", "mechz_lknee_armor_detached", 5000, 1, "int" );
	clientfield::register( "actor", "mechz_rshoulder_armor_detached", 5000, 1, "int" );
	clientfield::register( "actor", "mechz_lshoulder_armor_detached", 5000, 1, "int" );
	clientfield::register( "actor", "mechz_headlamp_off", 5000, 2, "int" );

	clientfield::register( "actor", "mechz_face", 1, 3, "int" );
}

function private InitMechzBehaviorsAndASM()
{
	// SERVICES
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzTargetService",&MechzBehavior::mechzTargetService);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzGrenadeService",&MechzBehavior::mechzGrenadeService);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzBerserkKnockdownService",&MechzBehavior::mechzBerserkKnockdownService);;

	// CONDITIONS
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzShouldMelee",&MechzBehavior::mechzShouldMelee);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzShouldShowPain",&MechzBehavior::mechzShouldShowPain);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzShouldShootGrenade",&MechzBehavior::mechzShouldShootGrenade);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzShouldShootFlame",&MechzBehavior::mechzShouldShootFlame);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzShouldShootFlameSweep",&MechzBehavior::mechzShouldShootFlameSweep);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzShouldTurnBerserk",&MechzBehavior::mechzShouldTurnBerserk);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzShouldStun",&MechzBehavior::mechzShouldStun);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzShouldStumble",&MechzBehavior::mechzShouldStumble);;

	// ACTIONS
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeAction("mechzStunLoop",&MechzBehavior::mechzStunStart,&MechzBehavior::mechzStunUpdate,&MechzBehavior::mechzStunEnd);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeAction("mechzStumbleLoop",&MechzBehavior::mechzStumbleStart,&MechzBehavior::mechzStumbleUpdate,&MechzBehavior::mechzStumbleEnd);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeAction("mechzShootFlameAction",&MechzBehavior::mechzShootFlameActionStart,&MechzBehavior::mechzShootFlameActionUpdate,&MechzBehavior::mechzShootFlameActionEnd);;

	// FUNCTIONS
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzShootGrenade",&MechzBehavior::mechzShootGrenade);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzShootFlame",&MechzBehavior::mechzShootFlame);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzUpdateFlame",&MechzBehavior::mechzUpdateFlame);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzStopFlame",&MechzBehavior::mechzStopFlame);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzPlayedBerserkIntro",&MechzBehavior::mechzPlayedBerserkIntro);;

	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzAttackStart",&MechzBehavior::mechzAttackStart);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzDeathStart",&MechzBehavior::mechzDeathStart);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzIdleStart",&MechzBehavior::mechzIdleStart);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzPainStart",&MechzBehavior::mechzPainStart);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzPainTerminate",&MechzBehavior::mechzPainTerminate);;

	// MOCOMPS

	// NOTETRACKS
	AnimationStateNetwork::RegisterNotetrackHandlerFunction("melee_soldat",&MechzBehavior::mechzNotetrackMelee);;
	AnimationStateNetwork::RegisterNotetrackHandlerFunction("fire_chaingun",&MechzBehavior::mechzNotetrackShootGrenade);;
}

function private ArchetypeMechzBlackboardInit()
{
	// CREATE BLACKBOARD
	Blackboard::CreateBlackBoardForEntity( self );
	
	// USE UTILITY BLACKBOARD
	self AiUtility::RegisterUtilityBlackboardAttributes();
	
	// CREATE MECHZ BLACKBOARD
	Blackboard::RegisterBlackBoardAttribute(self,"_locomotion_speed","locomotion_speed_run",undefined);   if( IsActor(self) )    {    /#self TrackBlackBoardAttribute("_locomotion_speed");#/    };
	Blackboard::RegisterBlackBoardAttribute(self,"_locomotion_should_turn","should_not_turn",&BB_GetShouldTurn);   if( IsActor(self) )    {    /#self TrackBlackBoardAttribute("_locomotion_should_turn");#/    };
	Blackboard::RegisterBlackBoardAttribute(self,"_zombie_damageweapon_type","regular",undefined);   if( IsActor(self) )    {    /#self TrackBlackBoardAttribute("_zombie_damageweapon_type");#/    };
	Blackboard::RegisterBlackBoardAttribute(self,"_mechz_part","mechz_powercore",undefined);   if( IsActor(self) )    {    /#self TrackBlackBoardAttribute("_mechz_part");#/    };
	
	// REGISTER ANIMSCRIPTED CALLBACK
	self.___ArchetypeOnAnimscriptedCallback = &ArchetypeMechzOnAnimscriptedCallback;
	
	// ENABLE DEBUGGING IN ODYSSEY
	/#self FinalizeTrackedBlackboardAttributes();#/;
}

function private ArchetypeMechzOnAnimscriptedCallback( entity )
{
	// UNREGISTER THE BLACKBOARD
	entity.__blackboard = undefined;
	
	// REREGISTER BLACKBOARD
	entity ArchetypeMechzBlackboardInit();
}

function private BB_GetShouldTurn()
{
	if( IsDefined( self.should_turn ) && self.should_turn )
	{
		return "should_turn";
	}
	return "should_not_turn";
}

//----------------------------------------------------------------------------------------------------------------------------
// NOTETRACK HANDLERS
//----------------------------------------------------------------------------------------------------------------------------
function private mechzNotetrackMelee( entity )
{
	if( isDefined( entity.mechz_melee_knockdown_function ))
	{
		entity thread [[ entity.mechz_melee_knockdown_function ]]();
	}
	entity Melee();
}

function private mechzNotetrackShootGrenade( entity )
{
	if ( !IsDefined( entity.enemy ) )
	{
		return;
	}

//	shoot_angle = RandomIntRange( MECHZ_GRENADE_DEVIATION_YAW_MIN, MECHZ_GRENADE_DEVIATION_YAW_MAX );
//	up_angle = RandomIntRange( MECHZ_GRENADE_DEVIATION_PITCH_MIN, MECHZ_GRENADE_DEVIATION_PITCH_MAX );
	
	base_target_pos = entity.enemy.origin;
	v_velocity = entity.enemy GetVelocity();
	base_target_pos = base_target_pos + ( v_velocity * 1.5 );
	
	target_pos_offset_x = math::randomsign() * randomint( 32 );
	target_pos_offset_y = math::randomsign() * randomint( 32 );
	
	target_pos = base_target_pos + ( target_pos_offset_x, target_pos_offset_y, 0 );

	dir = VectorToAngles( target_pos - entity.origin );
//	dir = ( dir[0] - up_angle, dir[1] + shoot_angle, dir[2] );
	dir = AnglesToForward( dir );
	
	launch_offset = (dir * 5);
	
	launch_pos = entity GetTagOrigin( "tag_gun_barrel2" ) + launch_offset;

	dist = Distance( launch_pos, target_pos );
	
	velocity = dir * dist;
	velocity = velocity + (0,0,120);
	
	val = 1;
	oldval = entity clientfield::get( "mechz_115_gun_firing" );
	if( oldval === val )
	{
		val = 0;
	}	
	
	entity clientfield::set( "mechz_115_gun_firing", val );
	

	entity MagicGrenadeType( GetWeapon("electroball_grenade"), launch_pos, velocity );
	PlaySoundAtPosition ("wpn_grenade_fire_mechz", entity.origin);
} 

//----------------------------------------------------------------------------------------------------------------------------
// BEHAVIOR TREE
//----------------------------------------------------------------------------------------------------------------------------
function mechzTargetService( entity )
{
	if ( ( isdefined( entity.ignoreall ) && entity.ignoreall ) )
	{
		return false;
	}

	if ( IsDefined( entity.destroy_octobomb ) )
	{
		return false;
	}

	player = zombie_utility::get_closest_valid_player( self.origin, self.ignore_player );

	entity.favoriteenemy = player;

	if( !IsDefined( player ) || player IsNoTarget() )
	{
		if( IsDefined( entity.ignore_player ) )
		{
			if(isDefined(level._should_skip_ignore_player_logic) && [[level._should_skip_ignore_player_logic]]() )
			{
				return;
			}
			entity.ignore_player = [];
		}
		
		/#if ( ( isdefined( level.b_mechz_true_ignore ) && level.b_mechz_true_ignore ) )
		{
			entity SetGoal( entity.origin );
			return false;
		}#/

		if( isdefined( level.no_target_override ) )
		{
			[[ level.no_target_override ]]( entity );			
		}
		else
		{
			entity SetGoal( entity.origin );
		}
				
		return false;
	}
	else
	{
		
		if( isDefined( level.enemy_location_override_func ))
		{
			enemy_ground_pos = [[level.enemy_location_override_func]]( entity, player);
			if( isDefined( enemy_ground_pos ))
			{
				entity SetGoal( enemy_ground_pos);
				return true;
			}
		}
		
		targetPos = GetClosestPointOnNavMesh( player.origin, 64, 30 );
		if ( IsDefined( targetPos ) )
		{
			entity SetGoal( targetPos );		
			return true;
		}
		else
		{
			entity SetGoal( entity.origin );
			return false;
		}
	}
}

function private mechzGrenadeService( entity )
{
	if( !isDefined( entity.burstGrenadesFired ))
	{
		entity.burstGrenadesFired = 0;
	}
		   	
	if ( entity.burstGrenadesFired >= 3 )
	{
		if ( GetTime() > entity.nextGrenadeTime ) 
		{
			entity.burstGrenadesFired = 0;
		}
	}
	
	if( isDefined( level.a_electroball_grenades ))
	{
		level.a_electroball_grenades = array::remove_undefined( level.a_electroball_grenades );
		
		a_active_grenades = array::filter( level.a_electroball_grenades, false, &mechzFilterGrenadesByOwner, entity );
		entity.activeGrenades = a_active_grenades.size;
	}
	else
	{
		entity.activeGrenades = 0;
	}
	
}

function private mechzFilterGrenadesByOwner( grenade, mechz )
{
	if( grenade.owner === mechz )
	{
		return true;
	}
	
	return false;
		
}

function private mechzBerserkKnockdownService( entity )
{
	velocity = entity GetVelocity();
	predict_time = 0.3;
	predicted_pos = entity.origin + ( velocity * predict_time );
	move_dist_sq = DistanceSquared( predicted_pos, entity.origin );
	speed = move_dist_sq /  predict_time;
	
	if( speed >= 10 )
	{
		a_zombies = GetAIArchetypeArray( "zombie" );
		
		a_filtered_zombies = array::filter( a_zombies, false, &mechzZombieEligibleForBerserkKnockdown, entity, predicted_pos );
		
		if( a_filtered_zombies.size > 0 )
		{
			foreach( zombie in a_filtered_zombies )
			{
				zombie.knockdown = true;
				zombie.knockdown_type = "knockdown_shoved";
				zombie_to_mechz = entity.origin - zombie.origin;
				zombie_to_mechz_2d = VectorNormalize( ( zombie_to_mechz[0], zombie_to_mechz[1], 0 ) );
				
				zombie_forward = AnglesToForward( zombie.angles );
				zombie_forward_2d = VectorNormalize( ( zombie_forward[0], zombie_forward[1], 0 ) );
				
				zombie_right = AnglesToRight( zombie.angles );
				zombie_right_2d = VectorNormalize( ( zombie_right[0], zombie_right[1], 0 ) );
				
				dot = VectorDot( zombie_to_mechz_2d, zombie_forward_2d );
				
				if( dot >= 0.5 )
				{
					zombie.knockdown_direction = "front";
					zombie.getup_direction = "getup_back";
				}
				else if ( dot < 0.5 && dot > -0.5 )
				{
					dot = VectorDot( zombie_to_mechz_2d, zombie_right_2d );
					if( dot > 0 )
					{
						zombie.knockdown_direction = "right";
			
						if ( math::cointoss() )
						{
							zombie.getup_direction = "getup_back";
						}
						else
						{
							zombie.getup_direction = "getup_belly";
						}
					}
					else
					{
						zombie.knockdown_direction = "left";
						zombie.getup_direction = "getup_belly";
					}
				}
				else
				{
					zombie.knockdown_direction = "back";
					zombie.getup_direction = "getup_belly";
				}
			}
		}
	}
}

function private mechzZombieEligibleForBerserkKnockdown( zombie, mechz, predicted_pos )
{
	if( zombie.knockdown === true )
	{
		return false;
	}
	
	knockdown_dist_sq = 48*48;
	
	dist_sq = DistanceSquared( predicted_pos, zombie.origin );
	
	if( dist_sq > knockdown_dist_sq )
	{
		return false;
	}
	
	if( zombie.is_immune_to_knockdown === true )
	{
		return false;
	}
	   
	origin = mechz.origin;

	facing_vec = AnglesToForward( mechz.angles );
	enemy_vec = zombie.origin - origin;
	
	enemy_yaw_vec = (enemy_vec[0], enemy_vec[1], 0);
	facing_yaw_vec = (facing_vec[0], facing_vec[1], 0);
	
	enemy_yaw_vec = VectorNormalize( enemy_yaw_vec );
	facing_yaw_vec = VectorNormalize( facing_yaw_vec );
	
	enemy_dot = VectorDot( facing_yaw_vec, enemy_yaw_vec );
	
	if( enemy_dot < 0 )// is enemy behind mechz
	{
		return false;
	}
	
	return true;
	
}

//----------------------------------------------------------------------------------------------------------------------------------
// CONDITIONS
//----------------------------------------------------------------------------------------------------------------------------------
function mechzShouldMelee( entity )
{
	if( !IsDefined( entity.enemy ) )
    {
		return false;
	}

	if( DistanceSquared( entity.origin, entity.enemy.origin ) > 112 * 112 )
	{
		return false;
	}

	// don't do yaw check if on vehicle ( turret ). add any additional checks above this.
	if( ( isdefined( entity.enemy.usingvehicle ) && entity.enemy.usingvehicle ) ) 
	{
		return true;
	}	
	
	yaw = abs( zombie_utility::getYawToEnemy() );
	if( ( yaw > 45 ) )
	{
		return false;
	}
	
	return true;
}

function private mechzShouldShowPain( entity )
{
	if( entity.partDestroyed === true )
	{
		return true;
	}
	
	return false;
}

function private mechzShouldShootGrenade( entity )
{
	
	if( entity.berserk === true )
	{
		return false;
	}
		
	if( entity.gun_attached !== true )
	{
		return false;
	}
	
	if ( !IsDefined( entity.favoriteenemy ) )
	{
		return false;
	}

	if ( entity.burstGrenadesFired >= 3 )
	{
		return false;
	}
	
	if( entity.activeGrenades >= 9 )
	{
		return false;
	}

	if ( !entity MechzServerUtils::mechzGrenadeCheckInArc() )
	{
		return false;
	}
	
	if( !entity CanSee( entity.favoriteenemy ) )
	{
		return false;
	}

	dist_sq = DistanceSquared( entity.origin, entity.favoriteenemy.origin );
	if ( dist_sq < 250 * 250 || dist_sq > 1200 * 1200 )
	{
		return false;
	}

	return true;
}

function private mechzShouldShootFlame( entity )
{
	/#
		if ( ( isdefined( entity.shoot_flame ) && entity.shoot_flame ) )
		{
			return true;
		}
	#/

	if( entity.berserk === true )
	{
		return false;
	}

	if ( ( isdefined( entity.isShootingFlame ) && entity.isShootingFlame ) && GetTime() < entity.stopShootingFlameTime )
	{
		return true;
	}
	
	if ( !IsDefined( entity.favoriteenemy ) )
	{
		return false;
	}
	
	if( entity.isShootingFlame === true && entity.stopShootingFlameTime <= GetTime() )
	{
		return false;
	}

	if ( entity.nextFlameTime > GetTime() )
	{
		return false;
	}

	if ( !entity MechzServerUtils::mechzCheckInArc( 26, "tag_flamethrower_fx" ) )
	{
		return false;
	}

	dist_sq = DistanceSquared( entity.origin, entity.favoriteenemy.origin );
	if ( dist_sq < 96 * 96 || dist_sq > 225 * 225 )
	{
		return false;
	}

	can_see = BulletTracePassed( entity.origin + ( 0, 0, 36 ), entity.favoriteenemy.origin + ( 0, 0, 36 ), false, undefined );
	if ( !can_see )
	{
		return false;
	}

	return true;
}

function private mechzShouldShootFlameSweep( entity )
{
	if( entity.berserk === true )
	{
		return false;
	}
	
	if ( !mechzShouldShootFlame( entity ) )
	{
		return false;
	}

	if ( RandomInt( 100 ) > 10 )
	{
		return false;
	}

	near_players = 0;
	players = GetPlayers();
	
	foreach( player in players )
	{
		if ( Distance2DSquared( entity.origin, player.origin ) < 100 * 100 )
		{
			near_players++;
		}
	}

	if ( near_players < 2 )
	{
		return false;
	}

	return true;
}

function private mechzShouldTurnBerserk( entity )
{
	if( entity.berserk === true && entity.hasTurnedBerserk !== true )
	{
		return true;
	}
	
	return false;
}

function private mechzShouldStun( entity )
{
	if ( ( isdefined( entity.stun ) && entity.stun ) )
	{
		return true;
	}

	return false;
}

function private mechzShouldStumble( entity )
{
	if ( ( isdefined( entity.stumble ) && entity.stumble ) )
	{
		return true;
	}

	return false;
}

//----------------------------------------------------------------------------------------------------------------------------------
// ACTIONS
//----------------------------------------------------------------------------------------------------------------------------------
function private mechzShootGrenadeAction( entity, asmStateName )
{
	AnimationStateNetworkUtility::RequestState( entity, asmStateName );

	entity.grenadeStartTime = GetTime() + 3000;

	return 5;
}

function private mechzShootGrenadeActionUpdate( entity, asmStateName )
{
	if ( !( isdefined( entity.shoot_grenade ) && entity.shoot_grenade ) )
	{
		return 4;
	}

	return 5;
}

function private mechzStunStart( entity, asmStateName )
{
	AnimationStateNetworkUtility::RequestState( entity, asmStateName );

	entity.stunTime = GetTime() + 500;

	return 5;
}

function private mechzStunUpdate( entity, asmStateName )
{
	if ( GetTime() > entity.stunTime )
	{
		return 4;
	}

	return 5;
}

function private mechzStunEnd( entity, asmStateName )
{
	entity.stun = false;
	entity.stumble_stun_cooldown_time = GetTime() + 10000;

	return 4;
}

function private mechzStumbleStart( entity, asmStateName )
{
	AnimationStateNetworkUtility::RequestState( entity, asmStateName );

	entity.stumbleTime = GetTime() + 500;

	return 5;
}

function private mechzStumbleUpdate( entity, asmStateName )
{
	if ( GetTime() > entity.stumbleTime )
	{
		return 4;
	}

	return 5;
}

function private mechzStumbleEnd( entity, asmStateName )
{
	entity.stumble = false;
	
	entity.stumble_stun_cooldown_time = GetTime() + 10000;

	return 4;
}

function mechzShootFlameActionStart( entity, asmStateName )
{
	AnimationStateNetworkUtility::RequestState( entity, asmStateName );

	mechzShootFlame( entity );

	return 5;
}

function mechzShootFlameActionUpdate( entity, asmStateName )
{
	if( ( isdefined( entity.berserk ) && entity.berserk ))
	{
		mechzStopFlame( entity );
		return 4;
	}
	
	if( ( isdefined( mechzShouldMelee( entity ) ) && mechzShouldMelee( entity ) ) )
	{
		mechzStopFlame( entity );
		return 4;
	}
	   	
	if ( ( isdefined( entity.isShootingFlame ) && entity.isShootingFlame ) )
	{
		if ( IsDefined( entity.stopShootingFlameTime ) && GetTime() > entity.stopShootingFlameTime )
		{
			mechzStopFlame( entity );
			return 4;
		}
	
		mechzUpdateFlame( entity );
	}

	return 5;
}

function mechzShootFlameActionEnd( entity, asmStateName )
{
	mechzStopFlame( entity );

	return 4;
}


//----------------------------------------------------------------------------------------------------------------------------------
// FUNCTIONS
//----------------------------------------------------------------------------------------------------------------------------------
function private mechzShootGrenade( entity )
{
	entity.burstGrenadesFired ++;
	if ( entity.burstGrenadesFired >= 3 )
	{
		entity.nextGrenadeTime = GetTime() + 6000;
	}
}

function private mechzShootFlame( entity )
{
	entity thread mechzDelayFlame();
}

function private mechzDelayFlame()
{
	self endon( "death" );

	self notify( "mechzDelayFlame" );
	self endon( "mechzDelayFlame" );

	wait( 0.3 );

	self clientfield::set( "mechz_ft", 1 );
	self.isShootingFlame = true;
	self.stopShootingFlameTime = GetTime() + 2500;
}

function private mechzUpdateFlame( entity )
{
	if( IsDefined( level.mechz_flamethrower_player_callback ) )
	{
		[[level.mechz_flamethrower_player_callback]]( entity );
	}
	else
	{
		players = GetPlayers();
	
		foreach( player in players )
		{
			if ( !( isdefined( player.is_burning ) && player.is_burning ) )
			{
				if ( player IsTouching( entity.flameTrigger ) )
				{
					if ( IsDefined( entity.mechzFlameDamage ) )
					{
						player thread [[ entity.mechzFlameDamage ]]();
					}
					else
					{
						player thread playerFlameDamage(entity);
					}
				}
			}
		}
	}
	
	if( IsDefined( level.mechz_flamethrower_ai_callback ) ) 
	{
		[[level.mechz_flamethrower_ai_callback]](entity);		
	}	
}

function playerFlameDamage(mechz)
{
	self endon( "death" );
	self endon( "disconnect" );
		
	if ( !( isdefined( self.is_burning ) && self.is_burning ) && zombie_utility::is_player_valid( self, true ) )
	{
		self.is_burning = 1;
			
		if ( !self HasPerk( "specialty_armorvest" ) )
		{
			self burnplayer::setPlayerBurning( 1.5, 0.5, 30, mechz, undefined );
		}
		else
		{
			self burnplayer::setPlayerBurning( 1.5, 0.5, 20, mechz, undefined );
		}

		wait( 1.5 );
		
		self.is_burning = 0;
	}
}

function mechzStopFlame( entity )
{
	self notify( "mechzDelayFlame" );

	entity clientfield::set( "mechz_ft", 0 );
	entity.isShootingFlame = false;
	entity.nextFlameTime = GetTime() + 7500;
	entity.stopShootingFlameTime = undefined;
}

function mechzGoBerserk()
{
	entity = self;	
	g_time = GetTime();
	
//	entity ASMSetAnimationRate( 1.04 );
	
	entity.berserkEndTime = g_time + 10000;
	
	if( entity.berserk !== true )
	{
		entity.berserk = true;
		entity thread mechzEndBerserk();
		Blackboard::SetBlackBoardAttribute( entity, "_locomotion_speed", "locomotion_speed_sprint" );
	}
}

function private mechzPlayedBerserkIntro( entity )
{
	entity.hasTurnedBerserk = true;
}

function private mechZEndBerserk()
{
	self endon( "death" );
	self endon( "disconnect" );
	
	While( self.berserk === true )
	{
		if( GetTime() >= self.berserkEndTime )
		{
			self.berserk = false;
			self.hasTurnedBerserk = false;
			
			self ASMSetAnimationRate( 1.0 );
			
			Blackboard::SetBlackBoardAttribute( self, "_locomotion_speed", "locomotion_speed_run" );
		}
		
		wait 0.25;
	}		
}

function private mechzAttackStart( entity )
{
	entity clientfield::set( "mechz_face", 1 );
}

function private mechzDeathStart( entity )
{
	entity clientfield::set( "mechz_face", 2 );
}

function private mechzIdleStart( entity )
{
	entity clientfield::set( "mechz_face", 3 );
}

function private mechzPainStart( entity )
{
	entity clientfield::set( "mechz_face", 4 );
}

function private mechzPainTerminate( entity )
{
	entity.partDestroyed = false;
	entity.show_pain_from_explosive_dmg = undefined;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// MOCOMPS
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#namespace MechzServerUtils;

function private mechzSpawnSetup()
{
	self DisableAimAssist();

	self.disableAmmoDrop = true;
	self.no_gib = true;
	self.ignore_nuke = true;
	self.ignore_enemy_count = true;
	self.ignore_round_robbin_death = true; 

	self.zombie_move_speed = "run";
	Blackboard::SetBlackBoardAttribute( self, "_locomotion_speed", "locomotion_speed_run" );

	self.ignoreRunAndGunDist = true;
	
	self mechzAddAttachments();

	self.grenadeCount = 9;

	self.nextFlameTime = GetTime();
	self.stumble_stun_cooldown_time = GetTime();

	/#
		self.debug_traversal_ast = "traverse@mechz";
	#/
		
	self.flameTrigger = Spawn( "trigger_box", self.origin, 0, 200, 50, 25 );
	self.flameTrigger EnableLinkTo();

	self.flameTrigger.origin = self GetTagOrigin( "tag_flamethrower_fx" );
	self.flameTrigger.angles = self GetTagAngles( "tag_flamethrower_fx" );
	
	self.flameTrigger LinkTo( self, "tag_flamethrower_fx" );
	
	self thread weaponobjects::watchWeaponObjectUsage();
	
	// necessary array creation element for weapon watcher stuff used in the electroball grenade
	self.pers = [];
	self.pers["team"] = self.team;

	//self thread mechzFlameWatcher();
}

function private mechzFlameWatcher()
{
	self endon( "death" );

	while ( 1 )
	{
		if ( IsDefined( self.favoriteenemy ) )
		{
			if ( self.flameTrigger IsTouching( self.favoriteenemy ) )
			{
				/# PrintTopRightLn( "flame on" ); #/
			}
		}

		{wait(.05);};
	}
}


function private mechzAddAttachments()
{
	self.has_left_knee_armor = true;
	self.left_knee_armor_health = 50;
	
	self.has_right_knee_armor = true;
	self.right_knee_armor_health = 50;
	
	self.has_left_shoulder_armor = true;
	self.left_shoulder_armor_health = 50;
	
	self.has_right_shoulder_armor = true;
	self.right_shoulder_armor_health = 50;

	org = self GetTagOrigin( "tag_gun_spin" );
	ang = self GetTagAngles( "tag_gun_spin" );

	self.gun_attached = true;

	self.has_faceplate = true;
	self.faceplate_health = 50;
	
	self.has_powercap = true;
	self.powercap_covered = true;
	self.powercap_cover_health = 50;
	self.powercap_health = 50;
}

function mechzDamageCallback( inflictor, attacker, damage, dFlags, mod, weapon, point, dir, hitLoc, offsetTime, boneIndex, modelIndex )
{
	if( isDefined( self.b_flyin_done ) && !( isdefined( self.b_flyin_done ) && self.b_flyin_done ) )
	{
		return 0;
	}
	
	if ( isDefined( level.mechz_should_stun_override ) && !( ( isdefined( self.stun ) && self.stun ) || ( isdefined( self.stumble ) && self.stumble ) ) )
	{
		if ( self.stumble_stun_cooldown_time < GetTime() && !( isdefined( self.berserk ) && self.berserk ) )
		{
			self [[level.mechz_should_stun_override]]( inflictor, attacker, damage, dFlags, mod, weapon, point, dir, hitLoc, offsetTime, boneIndex, modelIndex );
		}
	}
	
	if ( IsSubStr( weapon.name, "elemental_bow" ) && isdefined( inflictor ) && inflictor.classname === "rocket" )
	{
		// AkiA: Damage from bow shots that hit Mechz directly is controlled in zm_weap_elemental_bow::mechz_direct_hit_impact_damage_check()
		return 0;
	}
	
	damage = mechzWeaponDamageModifier( damage, weapon );
	
	if( isdefined( level.mechz_damage_override ) )
	{
		damage = [[level.mechz_damage_override]]( attacker, damage );
	}
	
	// play audio pain if he hasn't been hit in a bit
	if( !isDefined( self.next_pain_time ) || GetTime() >= self.next_pain_time  )
	{
		self thread mechz_play_pain_audio();
		self.next_pain_time = GetTime() + 250 + RandomInt( 500 ); //will wait this long before playing a pain audio again
	}

	if( isDefined( self.damage_scoring_function ))
	{
		self [[ self.damage_scoring_function ]]( inflictor, attacker, damage, dFlags, mod, weapon, point, dir, hitLoc, offsetTime, boneIndex, modelIndex );
	}
	
	if( IsDefined( level.mechz_staff_damage_override ) )
	{
		staffDamage = [[ level.mechz_staff_damage_override ]]( inflictor, attacker, damage, dFlags, mod, weapon, point, dir, hitLoc, offsetTime, boneIndex, modelIndex );
		
		if( staffDamage > 0 )
		{
			n_mechz_damage_percent = 0.5;
			
			// Boost damage if his helmet is off and the weapon is a staff
			if( !( isdefined( self.has_faceplate ) && self.has_faceplate ) && n_mechz_damage_percent < 1.0 )
			{
				n_mechz_damage_percent = 1.0;
			}
			
			staffDamage = staffDamage * n_mechz_damage_percent;
			
			if( ( isdefined( self.has_faceplate ) && self.has_faceplate ) )
			{
				self mechz_track_faceplate_damage( staffDamage );
			}
			
			/#iPrintLnBold( "Staff DMG: " + staffDamage + ". HP: " + ( self.health - staffDamage ) );#/
				
			if(!isdefined(self.explosive_dmg_taken))self.explosive_dmg_taken=0;
			self.explosive_dmg_taken += staffDamage;
			
			if( IsDefined( level.mechz_explosive_damage_reaction_callback ) )
			{
				self [[ level.mechz_explosive_damage_reaction_callback ]]();
			}
			
			return staffDamage;
		}
	}
	
	if( IsDefined( level.mechz_explosive_damage_reaction_callback ) )
	{
		if( isDefined( mod ) && mod == "MOD_GRENADE" || mod == "MOD_GRENADE_SPLASH" || mod == "MOD_PROJECTILE" || mod == "MOD_PROJECTILE_SPLASH" || mod == "MOD_EXPLOSIVE" )
		{
			n_mechz_damage_percentage = 0.5;
			if( isDefined( attacker ) && IsPlayer( attacker ) && IsAlive( attacker ) && ( level.zombie_vars[attacker.team]["zombie_insta_kill"] || ( isdefined( attacker.personal_instakill ) && attacker.personal_instakill )) ) //instakill does normal damage
			{
				n_mechz_damage_percentage = 1.0;
			}
			
			explosive_damage = damage * n_mechz_damage_percentage;
			
			if(!isdefined(self.explosive_dmg_taken))self.explosive_dmg_taken=0;
			self.explosive_dmg_taken += explosive_damage;
			
			if( ( isdefined( self.has_faceplate ) && self.has_faceplate ) )
			{
				self mechz_track_faceplate_damage( explosive_damage );
			}
			
			self [[ level.mechz_explosive_damage_reaction_callback ]]();
			
			/#iPrintLnBold( "Explosive DMG: " + explosive_damage + ". HP: " + ( self.health - explosive_damage ) );#/
			
			return explosive_damage;
		}
	}
	
	if ( hitLoc == "head" )
	{
		attacker show_hit_marker();
		/#iPrintLnBold( "Head DMG: " + damage + ". HP: " + ( self.health - damage ) );#/
		return damage;
	}

	if( hitloc !== "none" )
	{
		switch( hitLoc )
		{
		case "torso_upper":
			if( self.has_faceplate == true )
			{
				faceplate_pos = self GetTagOrigin( "j_faceplate" );
				dist_sq = DistanceSquared( faceplate_pos, point );
				
				if( dist_sq <= 144 )
				{
					self mechz_track_faceplate_damage( damage );
					attacker show_hit_marker();
				}
					
				headlamp_dist_sq = DistanceSquared( point, self GetTagOrigin( "tag_headlamp_FX" ));
				if( headlamp_dist_sq <= 9 )
				{
					self MechzServerUtils::mechz_turn_off_headlamp( true );
				}		
			}		
			
			partName = GetPartName( "c_zom_mech_body", boneIndex );
			if( self.powercap_covered === true && ( partName === "tag_powersupply" || partName === "tag_powersupply_hit" ) )
			{
				self mechz_track_powercap_cover_damage( damage );
				attacker show_hit_marker();
				/#iPrintLnBold( "PowerCore/Supply DMG: " + ( damage * 0.1 ) + ". HP: " + ( self.health - ( damage * 0.1 ) ) );#/
				return damage * 0.1;
			}
			else if( self.powercap_covered !== true && self.has_powercap === true && ( partName === "tag_powersupply" || partName === "tag_powersupply_hit" ) )
			{
				self mechz_track_powercap_damage( damage );
				attacker show_hit_marker();
				/#iPrintLnBold( "PowerCore/Supply DMG: " + damage + ". HP: " + ( self.health - damage ) );#/
				return damage;
			}
			else if( self.powercap_covered !== true && self.has_powercap !== true && ( partName === "tag_powersupply" || partName === "tag_powersupply_hit" ) )
			{
				/#iPrintLnBold( "PowerCore/Supply DMG: " + ( damage * 0.5 ) + ". HP: " + ( self.health - ( damage * 0.5 ) ) );#/
				attacker show_hit_marker();
				return damage * 0.5;
			}
			
			if( self.has_right_shoulder_armor === true && partName === "j_shoulderarmor_ri" )
			{
				self mechz_track_rshoulder_armor_damage( damage );
				/#iPrintLnBold( "Torso Upper DMG: " + ( damage * 0.1 ) + ". HP: " + ( self.health - ( damage * 0.1 ) ) );#/
				return damage * 0.1;
			}
					
			if( self.has_left_shoulder_armor === true && partName === "j_shoulderarmor_le" )
			{
				self mechz_track_lshoulder_armor_damage( damage );
				/#iPrintLnBold( "Torso Upper DMG: " + ( damage * 0.1 ) + ". HP: " + ( self.health - ( damage * 0.1 ) ) );#/
				return damage * 0.1;
			}
			
			/#iPrintLnBold( "Torso Upper DMG: " + ( damage * 0.1 ) + ". HP: " + ( self.health - ( damage * 0.1 ) ) );#/
			return damage * 0.1;
			break;
			
		case "left_leg_lower":
			partName = GetPartName( "c_zom_mech_body", boneIndex );
			if( partName === "j_knee_attach_le" && self.has_left_knee_armor === true )
			{
				self mechz_track_lknee_armor_damage( damage );
			}
			
			/#iPrintLnBold( "HitLoc L Leg Lower DMG: " + ( damage * 0.1 ) + ". HP: " + ( self.health - ( damage * 0.1 ) ) );#/
			return damage * 0.1;
			break;
			
		case "right_leg_lower":
			partName = GetPartName( "c_zom_mech_body", boneIndex );
			if( partName === "j_knee_attach_ri" && self.has_right_knee_armor === true )
			{
				self mechz_track_rknee_armor_damage( damage );
			}
			
			/#iPrintLnBold( "HitLoc R Leg Lower DMG: " + ( damage * 0.1 ) + ". HP: " + ( self.health - ( damage * 0.1 ) ) );#/
			return damage * 0.1;
			break;

		case "left_hand":
		case "left_arm_lower":
		case "left_arm_upper":
			if ( IsDefined( level.mechz_left_arm_damage_callback ) )
			{
				self [[ level.mechz_left_arm_damage_callback ]]();
			}

			/#iPrintLnBold( "HitLoc L Arm DMG: " + ( damage * 0.1 ) + ". HP: " + ( self.health - ( damage * 0.1 ) ) );#/
			return damage * 0.1;
			break;
			
	
		default:
			/#iPrintLnBold( "HitLoc DEFAULT DMG: " + ( damage * 0.1 ) + ". HP: " + ( self.health - ( damage * 0.1 ) ) );#/
			return damage * 0.1;
			break;
		}
	}

	if ( mod == "MOD_PROJECTILE" )
	{
		hit_damage = damage * 0.1;
		
		if( self.has_faceplate !== true )
		{
			head_pos = self GetTagOrigin( "tag_eye" );
			dist_sq = DistanceSquared( head_pos, point );
			
			if( dist_sq <= 144 )
			{
				/#iPrintLnBold( "Projectile head DMG: " + damage + ". HP: " + ( self.health - damage ) );#/
				attacker show_hit_marker();
				return damage;
			}
			                             
		}
		
		if( self.has_faceplate === true )
		{
			faceplate_pos = self GetTagOrigin( "j_faceplate" );
			dist_sq = DistanceSquared( faceplate_pos, point );
			
			if( dist_sq <= 144 )
			{
				self mechz_track_faceplate_damage( damage );
				attacker show_hit_marker();
			}
			
			headlamp_dist_sq = DistanceSquared( point, self GetTagOrigin( "tag_headlamp_FX" ));
			if( headlamp_dist_sq <= 9 )
			{
				self MechzServerUtils::mechz_turn_off_headlamp( true );
			}
		}
		
		
		power_pos = self GetTagOrigin( "tag_powersupply_hit" );
		power_dist_sq = DistanceSquared( power_pos, point );
		
		if( power_dist_sq <= 25 )
		{
			if( self.powercap_covered !== true && self.has_powercap !== true )
			{
				/#iPrintLnBold( "Projectile powercap DMG: " + damage + ". HP: " + ( self.health - damage ) );#/
				attacker show_hit_marker();
				return damage;
			}
								
			if( self.powercap_covered !== true && self.has_powercap === true )
			{
				self mechz_track_powercap_damage( damage );
				attacker show_hit_marker();
				/#iPrintLnBold( "Projectile powercap DMG: " + damage + ". HP: " + ( self.health - damage ) );#/
				return damage;
			}
			
			if( self.powercap_covered === true )
			{
				self mechz_track_powercap_cover_damage( damage );
				attacker show_hit_marker();
			}
		}
		
		if( self.has_right_shoulder_armor === true )
		{
			armor_pos = self GetTagOrigin( "j_shoulderarmor_ri" );
			dist_sq = DistanceSquared( armor_pos, point );
			
			if( dist_sq <= 64 )
			{
				self mechz_track_rshoulder_armor_damage( damage );
			}
		}
		
		if( self.has_left_shoulder_armor === true )
		{
			armor_pos = self GetTagOrigin( "j_shoulderarmor_le" );
			dist_sq = DistanceSquared( armor_pos, point );
			
			if( dist_sq <= 64 )
			{
				self mechz_track_lshoulder_armor_damage( damage );
			}
		}
				
		if( self.has_right_knee_armor === true )
		{
			armor_pos = self GetTagOrigin( "j_knee_attach_ri" );
			dist_sq = DistanceSquared( armor_pos, point );
			
			if( dist_sq <= 36 )
			{
				self mechz_track_rknee_armor_damage( damage );
			}
		}
		
		if( self.has_left_knee_armor === true )
		{
			armor_pos = self GetTagOrigin( "j_knee_attach_le" );
			dist_sq = DistanceSquared( armor_pos, point );
			
			if( dist_sq <= 36 )
			{
				self mechz_track_lknee_armor_damage( damage );
			}
		}
		
		/#iPrintLnBold( "Projectile DMG: " + hit_damage + ". HP: " + ( self.health - hit_damage ) );#/
		return hit_damage;
	}
	else if ( mod == "MOD_PROJECTILE_SPLASH" )
	{
		hit_damage = damage * 0.2;
		
		//count number of armor pieces
		i_num_armor_pieces = 0;
		
		if( isDefined( level.mechz_faceplate_damage_override ))
		{
			self [[level.mechz_faceplate_damage_override]]( inflictor, attacker, damage, dFlags, mod, weapon, point, dir, hitLoc, offsetTime, boneIndex, modelIndex );
		}
		
		if( self.has_right_shoulder_armor === true )
		{
			i_num_armor_pieces += 1;
			right_shoulder_index = i_num_armor_pieces;
		}
		
		if( self.has_left_shoulder_armor === true )
		{
			i_num_armor_pieces += 1;
			left_shoulder_index = i_num_armor_pieces;
		}
				
		if( self.has_right_knee_armor === true )
		{
			i_num_armor_pieces += 1;
			right_knee_index = i_num_armor_pieces;
		}
					
		if( self.has_left_knee_armor === true )
		{
			i_num_armor_pieces += 1;
			left_knee_index = i_num_armor_pieces;
		}
		
		if( i_num_armor_pieces > 0 )
		{
			if( i_num_armor_pieces <= 1 )
			{
				i_random = 0;
			}
			else
			{
				i_random = RandomInt( i_num_armor_pieces - 1 );
			}
			
			i_random += 1;
			
			if( self.has_right_shoulder_armor === true && right_shoulder_index === i_random )
			{
				self mechz_track_rshoulder_armor_damage( damage );
			}
			
			if( self.has_left_shoulder_armor === true && left_shoulder_index === i_random )
			{
				self mechz_track_lshoulder_armor_damage( damage );
			}
			
			if( self.has_right_knee_armor === true && right_knee_index === i_random )
			{
				self mechz_track_rknee_armor_damage( damage );
			}
			
			if( self.has_left_knee_armor === true && left_knee_index === i_random )
			{
				self mechz_track_lknee_armor_damage( damage );
			}			
		}
		else
		{
			if( self.powercap_covered === true )
			{
				self mechz_track_powercap_cover_damage( damage * 0.5 );
			}
			
			if( self.has_faceplate == true )
			{
				self mechz_track_faceplate_damage( damage * 0.5 );
			}	
		}		
		
		/#iPrintLnBold( "Projectile Splash DMG: " + hit_damage + ". HP: " + ( self.health - hit_damage ) );#/
		return hit_damage;
	}

	return 0;
}

//used to step down the damage from some high-powered weapons
function private mechzWeaponDamageModifier( damage, weapon )
{
	if( isDefined( weapon) && isDefined( weapon.name ) )
	{
		if( isSubStr( weapon.name, "shotgun_fullauto") )
		{
			return damage * 0.5;	
		}
		
		if( isSubStr( weapon.name, "lmg_cqb") )
		{
			return damage * 0.65;	
		}
		
		if( isSubStr( weapon.name, "lmg_heavy") )
		{
			return damage * 0.65;	
		}
		
		if( isSubStr( weapon.name, "shotgun_precision") )
		{
			return damage * 0.65;	
		}
		
		if( isSubstr( weapon.name, "shotgun_semiauto") )
		{
			return damage * 0.75;	
		}
		
	}
		
	return damage;
}

function mechz_play_pain_audio()
{
	self playsound( "zmb_ai_mechz_destruction" );
}

function show_hit_marker()  // self = player
{
	if ( IsDefined( self ) && IsDefined( self.hud_damagefeedback ) )
	{
		self.hud_damagefeedback SetShader( "damage_feedback", 24, 48 );
		self.hud_damagefeedback.alpha = 1;
		self.hud_damagefeedback FadeOverTime(1);
		self.hud_damagefeedback.alpha = 0;
	}	
}

function hide_part( strTag )
{
	if ( self HasPart(strTag) )
	{
		self HidePart(strTag);  
	}
}


function mechz_track_faceplate_damage( damage )
{
	self.faceplate_health = self.faceplate_health - damage;
	if( self.faceplate_health <= 0 )
	{
		self hide_part( "j_faceplate" );
		self clientfield::set( "mechz_faceplate_detached", 1 );
		self.has_faceplate = false;
		self MechzServerUtils::mechz_turn_off_headlamp();
		self.partDestroyed = true;
		Blackboard::SetBlackBoardAttribute( self, "_mechz_part", "mechz_faceplate" );
		
		self MechzBehavior::mechzGoBerserk();

		level notify( "mechz_faceplate_detached" );
	}	
}

function mechz_track_powercap_cover_damage( damage )
{
	self.powercap_cover_health = self.powercap_cover_health - damage;
	if( self.powercap_cover_health <= 0 )
	{
		self hide_part( "tag_powersupply" );
		self clientfield::set( "mechz_powercap_detached", 1 );		
		self.powercap_covered = false;
		self.partDestroyed = true;
		Blackboard::SetBlackBoardAttribute( self, "_mechz_part", "mechz_powercore" );
		
		//self MechzBehavior::mechzGoBerserk();
	}
	
}

function mechz_track_powercap_damage( damage )
{
	self.powercap_health = self.powercap_health - damage;
	if( self.powercap_health <=0 )
	{
		if( IsDefined( level.mechz_powercap_destroyed_callback ) )
		{
			self [[level.mechz_powercap_destroyed_callback]]();
		}
		
		self hide_part( "tag_gun_spin" );
		self hide_part( "tag_gun_barrel1" );
		self hide_part( "tag_gun_barrel2" );
		self hide_part( "tag_gun_barrel3" );
		self hide_part( "tag_gun_barrel4" );
		self hide_part( "tag_gun_barrel5" );
		self hide_part( "tag_gun_barrel6" );
		self clientfield::set( "mechz_claw_detached", 1 );
		self.has_powercap = false;
		self.gun_attached = false;
		self.partDestroyed = true;
		Blackboard::SetBlackBoardAttribute( self, "_mechz_part", "mechz_gun" );
		
		//self MechzBehavior::mechzGoBerserk();

		level notify( "mechz_gun_detached" );
	}
}

function mechz_track_rknee_armor_damage( damage )
{
	self.right_knee_armor_health = self.right_knee_armor_health - damage;
	if( self.right_knee_armor_health <= 0 )
	{
		self hide_part( "j_knee_attach_ri" );
		self clientfield::set( "mechz_rknee_armor_detached", 1 );		
		self.has_right_knee_armor = false;
	}
	
}

function mechz_track_lknee_armor_damage( damage )
{
	self.left_knee_armor_health = self.left_knee_armor_health - damage;
	if( self.left_knee_armor_health <= 0 )
	{
		self hide_part( "j_knee_attach_le" );
		self clientfield::set( "mechz_lknee_armor_detached", 1 );		
		self.has_left_knee_armor = false;
	}
	
}

function mechz_track_rshoulder_armor_damage( damage )
{
	self.right_shoulder_armor_health = self.right_shoulder_armor_health - damage;
	if( self.right_shoulder_armor_health <= 0 )
	{
		self hide_part( "j_shoulderarmor_ri" );
		self clientfield::set( "mechz_rshoulder_armor_detached", 1 );		
		self.has_right_shoulder_armor = false;
	}
	
}

function mechz_track_lshoulder_armor_damage( damage )
{
	self.left_shoulder_armor_health = self.left_shoulder_armor_health - damage;
	if( self.left_shoulder_armor_health <= 0 )
	{
		self hide_part( "j_shoulderarmor_le" );
		self clientfield::set( "mechz_lshoulder_armor_detached", 1 );		
		self.has_left_shoulder_armor = false;
	}
	
}

function mechzCheckInArc( right_offset, aim_tag )
{
	origin = self.origin;
	angles = self.angles;

	if ( IsDefined( aim_tag ) )
	{
		origin = self GetTagOrigin( aim_tag );
		angles = self GetTagAngles( aim_tag );
	}
	
	if ( IsDefined( right_offset ) )
	{
		right_angle = anglestoright( angles );
		origin = origin + (right_angle * right_offset);
	}

	facing_vec = AnglesToForward( angles );
	enemy_vec = self.favoriteenemy.origin - origin;
	
	enemy_yaw_vec = (enemy_vec[0], enemy_vec[1], 0);
	facing_yaw_vec = (facing_vec[0], facing_vec[1], 0);
	
	enemy_yaw_vec = VectorNormalize( enemy_yaw_vec );
	facing_yaw_vec = VectorNormalize( facing_yaw_vec );
	
	enemy_dot = VectorDot( facing_yaw_vec, enemy_yaw_vec );
	
	if( enemy_dot < 0.5 )
	{
		return false;
	}
	
	enemy_angles = VectorToAngles( enemy_vec );
	
	if( abs( AngleClamp180( enemy_angles[0] ) ) > 60 )
	{
		return false;
	}
	
	return true;
}

function private mechzGrenadeCheckInArc( right_offset )
{
	origin = self.origin;
	if ( IsDefined( right_offset ) )
	{
		right_angle = anglestoright( self.angles );
		origin = origin + (right_angle * right_offset);
	}

	facing_vec = AnglesToForward( self.angles );
	enemy_vec = self.favoriteenemy.origin - origin;
	
	enemy_yaw_vec = (enemy_vec[0], enemy_vec[1], 0);
	facing_yaw_vec = (facing_vec[0], facing_vec[1], 0);
	
	enemy_yaw_vec = VectorNormalize( enemy_yaw_vec );
	facing_yaw_vec = VectorNormalize( facing_yaw_vec );
	
	enemy_dot = VectorDot( facing_yaw_vec, enemy_yaw_vec );
	
	if( enemy_dot < 0.5 )
	{
		return false;
	}
	
	enemy_angles = VectorToAngles( enemy_vec );
	
	if( abs( AngleClamp180( enemy_angles[0] ) ) > 60 )
	{
		return false;
	}
	
	return true;
}


function mechz_turn_off_headlamp( headlamp_broken )
{
	if( headlamp_broken !== true )
	{
		self clientfield::set( "mechz_headlamp_off", 1 );
	}
	else
	{
		self clientfield::set( "mechz_headlamp_off", 2 );
	}
}
