#include common_scripts\utility;
#include maps\mp\agents\_scriptedAgents;

main()
{
	self endon( "killanimscript" );

	self.bLockGoalPos = false;
	self ScrAgentSetPhysicsMode( "gravity" );

	self StartMove();
	self ContinueMovement();
}

end_script()
{
	self.bLockGoalPos = false;
	self CancelAllBut( undefined );
	self ScrAgentSetAnimScale( 1, 1 );
}

SetupMovement()
{
	self thread WaitForRunWalkChange();
	self thread WaitForSharpTurn();
	self thread WaitForStop();
	//self thread WaitForStopEarly();
	//self thread HandleMoveLoopFootsteps();
}

ContinueMovement()
{
	self SetupMovement();

	self ScrAgentSetAnimMode( "code_move" );
	self ScrAgentSetOrientMode( "face motion" );
	self ScrAgentSetAnimScale( 1, 1 );
	self SetMoveAnim( self.moveMode );
}

SetMoveAnim( moveMode )
{
	self SetAnimState( moveMode );
}

WaitForRunWalkChange()
{
	self endon( "dogmove_endwait_runwalk" );
	curMovement = self.moveMode;
	while ( true )
	{
		if ( curMovement != self.moveMode )
		{
			self SetMoveAnim( self.moveMode );
			curMovement = self.moveMode;
		}
		wait( 0.1 );
	}
}

DoSharpTurn( newDir )
{
	lookaheadAngles = VectorToAngles( newDir );
	angleDiff = AngleClamp180( lookaheadAngles[1] - self.angles[1] );
	angleIndex = GetAngleIndex( angleDiff );

	if ( angleIndex == 4 )	// 4 means this turn wasn't sharp enough for me to care. (angle ~= 0)
	{
		ContinueMovement();
		return;
	}

	animState = "sharp_turn";

	turnAnim = self GetAnimEntry( animState, angleIndex );
	animAngleDelta = GetAngleDelta( turnAnim );

	self ScrAgentSetAnimMode( "anim deltas" );
	self ScrAgentSetOrientMode( "face angle abs", ( 0, AngleClamp180( lookaheadAngles[1] - animAngleDelta ), 0 ) );

	self PlayAnimNUntilNotetrack( animState, angleIndex, "sharp_turn" );

	self ContinueMovement();
}

WaitForSharpTurn()
{
	self endon( "dogmove_endwait_sharpturn" );

	self waittill( "path_dir_change", newDir );
	self CancelAllBut( "sharpturn" );

	self DoSharpTurn( newDir );
}

WaitForStop()
{
	self endon( "dogmove_endwait_stop" );

	self waittill( "stop_soon" );

	if ( IsDefined( self.bArrivalsEnabled ) && !self.bArrivalsEnabled )
	{
		self thread WaitForStop();
		return;
	}

	stopState = self GetStopAnimState();

	stopAnim = self GetAnimEntry( stopState.state, stopState.index );
	stopDelta = GetMoveDelta( stopAnim );
	stopAngleDelta = GetAngleDelta( stopAnim );

	goalPos = self GetPathGoalPos();
	assert( IsDefined( goalPos ) );

	meToStop = goalPos - self.origin;
	// not enough room left to play the animation.  abort. (i'm willing to squish/scale the anim up to 12 units.)
	if ( Length( meToStop ) + 12 < Length( stopDelta ) )
	{
		self thread WaitForStop();
		return;
	}

	stopData = self GetStopData();
	stopStartPos = self CalcAnimStartPos( stopData.pos, stopData.angles[1], stopDelta, stopAngleDelta );
	stopStartPosDropped = DropPosToGround( stopStartPos );

	if ( !IsDefined( stopStartPosDropped ) )
	{
		self thread WaitForStop();
		return;
	}

	if ( !self CanMovePointToPoint( stopData.pos, stopStartPosDropped ) )
	{
		self thread WaitForStop();
		return;
	}

	self CancelAllBut( "stop" );

	self thread WaitForPathSetWhileStopping();
	self thread WaitForSharpTurnWhileStopping();
	if ( DistanceSquared( stopStartPos, self.origin ) > 4 )
	{
		self ScrAgentSetWaypoint( stopStartPos );
		self thread WaitForBlockedWhileStopping();
		self waittill( "waypoint_reached" );
		self notify( "dogmove_endwait_blockedwhilestopping" );
	}

	facingDir = goalPos - self.origin;
	facingAngles = VectorToAngles( facingDir );
	facingYaw = ( 0, facingAngles[1] - stopAngleDelta, 0 );

	// scale the anim if necessary, to make sure we end up where we wanted to end up.
	scaleFactors = GetAnimScaleFactors( goalPos - self.origin, stopDelta );

	self ScrAgentSetAnimMode( "anim deltas" );
	self ScrAgentSetOrientMode( "face angle abs", facingYaw, ( 0, facingAngles[1], 0 ) );
	self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
	self PlayAnimNUntilNotetrack( stopState.state, stopState.index, "move_stop" );

	self ScrAgentSetGoalPos( self.origin );		// whether i got where i was going, get where i got.
}

WaitForPathSetWhileStopping()
{
	self endon( "killanimscript" );
	self endon( "dogmove_endwait_pathsetwhilestopping" );

	oldGoalPos = self ScrAgentGetGoalPos();

	self waittill( "path_set" );

	newGoalPos = self ScrAgentGetGoalPos();

	if ( DistanceSquared( oldGoalPos, newGoalPos ) < 1 )
	{
		self thread WaitForPathSetWhileStopping();
		return;
	}

	self notify( "dogmove_endwait_stop" );
	self notify( "dogmove_endwait_sharpturnwhilestopping" );

	self ContinueMovement();
}

WaitForSharpTurnWhileStopping()
{
	self endon( "killanimscript" );
	self endon( "dogmove_endwait_sharpturnwhilestopping" );

	self waittill( "path_dir_change", newDir );

	self notify( "dogmove_endwait_pathsetwhilestopping" );
	self notify( "dogmove_endwait_stop" );

	self DoSharpTurn( newDir );
}

WaitForBlockedWhileStopping()
{
	self endon( "killanimscript" );
	self endon( "dogmove_endwait_blockedwhilestopping" );

	self waittill( "path_blocked" );
	self notify( "dogmove_endwait_stop" );
	self ScrAgentSetWaypoint( undefined );
}

WaitForStopEarly()
{
	self endon( "killanimscript" );
	self endon( "dogmove_endwait_stopearly" );

	stopAnim = self GetAnimEntry( "move_stop_4", 0 );
	stopAnimTranslation = GetMoveDelta( stopAnim );
	stoppingDistance = Length( stopAnimTranslation );
	offset = self.preferredOffsetFromOwner + stoppingDistance;
	offsetSq = offset * offset;

	if ( DistanceSquared( self.origin, self.owner.origin ) <= offsetSq )
		return;

	while ( true )
	{
		if ( !IsDefined( self.owner ) )
			break;

		if ( DistanceSquared( self.origin, self.owner.origin ) < offsetSq )
		{
			stopPos = self LocalToWorldCoords( stopAnimTranslation );
			self ScrAgentSetGoalPos( stopPos );
			break;
		}

		wait( 0.1 );
	}
}

CancelAllBut( doNotCancel )
{
	cleanups = [ "runwalk", "sharpturn", "stop", "pathsetwhilestopping", "blockedwhilestopping", "sharpturnwhilestopping", "stopearly" ];

	bCheckDoNotCancel = IsDefined( doNotCancel );

	foreach ( cleanup in cleanups )
	{
		if ( bCheckDoNotCancel && cleanup == doNotCancel )
			continue;
		self notify( "dogmove_endwait_" + cleanup );
	}
}

StartMove()
{
	negStartNode = self GetNegotiationStartNode();
	if ( IsDefined( negStartNode ) )
		goalPos = negStartNode.origin;
	else
		goalPos = self GetPathGoalPos();

	// don't play start if i have no room for the start.
	if ( DistanceSquared( goalPos, self.origin ) < 100 * 100 )
		return;

	lookaheadDir = self GetLookaheadDir();
	lookaheadAngles = VectorToAngles( lookaheadDir );

	myVelocity = self GetVelocity();
	if ( Length2DSquared( myVelocity ) > 16 )
	{
		myVelocity = VectorNormalize( myVelocity );
		if ( VectorDot( myVelocity, lookaheadDir ) > 0.707 )
			return;		// don't need a start if i'm already moving in the direction i want to move.
	}

	angleDiff = AngleClamp180( lookaheadAngles[1] - self.angles[1] );
	angleIndex = GetAngleIndex( angleDiff );

	startAnim = self GetAnimEntry( "move_start", angleIndex );
	startAnimTranslation = GetMoveDelta( startAnim );

	endPos = RotateVector( startAnimTranslation, self.angles ) + self.origin;
	if ( !self CanMovePointToPoint( self.origin, endPos ) )
		return;

	startAnimAngles = GetAngleDelta3D( startAnim );

	self ScrAgentSetAnimMode( "anim deltas" );
	if ( 3 <= angleIndex && angleIndex <= 5 )
		self ScrAgentSetOrientMode( "face angle abs", ( 0, AngleClamp180( lookaheadAngles[1] - startAnimAngles[1] ), 0 ) );
	else
		self ScrAgentSetOrientMode( "face angle abs", self.angles );
	
	self.bLockGoalPos = true;

	self PlayAnimNUntilNotetrack( "move_start", angleIndex, "move_start" );
	
	self.bLockGoalPos = false;
}

GetStopData()
{
	stopData = SpawnStruct();

	if ( IsDefined( self.node ) )
	{
		stopData.pos = self.node.origin;
		stopData.angles = self.node.angles;
	}
	else
	{
		pathGoalPos = self GetPathGoalPos();
		assert( IsDefined( pathGoalPos ) );
		stopData.pos = pathGoalPos;
		//stopData.angles = self.angles;
		stopData.angles = VectorToAngles( self GetLookaheadDir() );
	}

	return stopData;
}

GetStopAnimState( angle )
{
	if ( IsDefined( self.node ) )
	{
		angleDiff = self.node.angles[1] - self.angles[1];
		angleIndex = GetAngleIndex( angleDiff );
	}
	else
	{
		angleIndex = 4;
	}

	result = SpawnStruct();
	result.state = "move_stop";
	result.index = angleIndex;

	return result;
}

CalcAnimStartPos( stopPos, stopAngle, animDelta, animAngleDelta )
{
	dAngle = stopAngle - animAngleDelta;
	angles = ( 0, dAngle, 0 );
	vForward = AnglesToForward( angles );
	vRight = AnglesToRight( angles );

	forward = vForward * animDelta[0];
	right = vRight * animDelta[1];

	return stopPos - forward + right;
}

Dog_AddLean()
{
	leanFrac = Clamp( self.leanAmount / 25.0, -1, 1 );
	if ( leanFrac > 0 )
	{
		// set lean left( leanFrac );
		// set lean right( 0 );
	}
	else
	{
		// set lean left( 0 );
		// set lean right( 0 - leanFrac );
	}
}


HandleFootstepNotetracks( note, animState, animIndex, animTime )
{
	if ( true )
		return false;

	switch ( note )
	{
	case "footstep_front_left_small":
	case "footstep_front_right_small":
	case "footstep_back_left_small":
	case "footstep_back_right_small":
	case "footstep_front_left_large":
	case "footstep_front_right_large":
	case "footstep_back_left_large":
	case "footstep_back_right_large":
		{
			surfaceType = undefined;
			if ( IsDefined( self.surfaceType ) )
			{
				surfaceType = self.surfaceType;
				self.lastSurfaceType = surfaceType;
			}
			else if ( IsDefined( self.lastSurfaceType ) )
			{
				surfaceType = self.lastSurfaceType;
			}
			else
			{
				surfaceType = "dirt";
			}

			if ( surfaceType != "dirt" && surfaceType != "concrete" && surfaceType != "wood" && surfaceType != "metal" )
				surfaceType = "dirt";

			if ( surfaceType == "concrete" )	// code == concrete, sound == cement.
				surfaceType = "cement";

			//moveType = self.sound_animMoveType;
			//if ( !IsDefined( moveType ) )
			//	moveType = "run";
			if ( self.aiState == "traverse" )
				moveType = "land";
			else if ( self.moveMode == "sprint" )
				moveType = "sprint";
			else if ( self.moveMode == "fastwalk" )
				moveType = "walk";
			else
				moveType = "run";

			self PlaySoundOnMovingEnt( "dogstep_" + moveType + "_" + surfaceType );

			if ( IsSubStr( note, "front_left" ) )
			{
				soundAlias1 = "anml_dog_mvmt_accent";
				soundAlias2 = "anml_dog_mvmt_vest";

				if ( moveType == "walk" )
					suffix = "_npc";
				else
					suffix = "_run_npc";

				self PlaySoundOnMovingEnt( soundAlias1 + suffix );
				self PlaySoundOnMovingEnt( soundAlias2 + suffix );
			}
		}

		return true;
	}

	return false;
}

DoHitReaction( hitAngle )
{
	self CancelAllBut( undefined );

	self.bLockGoalPos = true;
	self.stateLocked = true;

	// hitAngle is angle from me to damage
	angleDiff = AngleClamp180( hitAngle - self.angles[1] );

	if ( angleDiff > 0 )
		animIndex = 1;	// left
	else
		animIndex = 0;	// right

	self ScrAgentSetAnimMode( "anim deltas" );
	self ScrAgentSetOrientMode( "face angle abs", self.angles );

	self PlayAnimNUntilNotetrack( "run_pain", animIndex, "run_pain" );

	self.bLockGoalPos = false;
	self.stateLocked = false;

	self ContinueMovement();
}

OnDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
{
	if ( self.stateLocked )
		return;

	hitDirToAngles = VectorToAngles( vDir );
	hitAngle = hitDirToAngles[1] - 180;

	self DoHitReaction( hitAngle );
}

OnFlashbanged( origin, percent_distance, percent_angle, attacker, teamName, extraDuration )
{
	if ( self.stateLocked )
		return;

	DoHitReaction( self.angles[1] + 180 );
}