// Fill out your copyright notice in the Description page of Project Settings.


#include "FpsArmsAnimInstance.h"

#include "FpsPlayer.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "HeldItemComponent.h"

void UFpsArmsAnimInstance::NativeInitializeAnimation()
{
	Super::NativeInitializeAnimation();

	OwningPlayer = Cast<AFpsPlayer>(TryGetPawnOwner());
}

void UFpsArmsAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
	Super::NativeUpdateAnimation(DeltaSeconds);

	// L'AnimInstance est initialisee avec le composant, c'est-a-dire potentiellement
	// avant que le pawn soit completement en place. On retente donc tant qu'on n'a
	// personne, plutot que d'abandonner definitivement sur un echec au demarrage.
	if (!OwningPlayer.IsValid())
	{
		OwningPlayer = Cast<AFpsPlayer>(TryGetPawnOwner());
		if (!OwningPlayer.IsValid())
		{
			return;
		}
	}

	const AFpsPlayer* Player = OwningPlayer.Get();
	const UCharacterMovementComponent* Movement = Player->GetCharacterMovement();
	if (!Movement)
	{
		return;
	}

	// Size2D et pas Size : en pleine chute la composante verticale ferait courir
	// les bras alors que le joueur ne pose pas un pied.
	GroundSpeed = Movement->Velocity.Size2D();

	// Le test d'acceleration est ce qui distingue « le joueur avance » de « le
	// joueur a lache la touche et glisse encore ». Sans lui, l'animation de course
	// continue pendant toute la decceleration, les pieds patinant dans le vide --
	// invisible sur des bras, mais le meme AnimInstance servira au corps un jour.
	bIsMoving = GroundSpeed > MovingSpeedThreshold
		&& !Movement->GetCurrentAcceleration().IsNearlyZero();

	// Lu depuis le composant plutot que mis en cache dans un membre : apres un
	// respawn le pawn est un NOUVEL acteur, avec un nouveau composant, et un
	// pointeur garde ici pointerait sur celui du cadavre.
	if (const UHeldItemComponent* HeldItems = Player->GetHeldItemComponent())
	{
		bHasItemInHand = HeldItems->HasItemInHand();
	}

	bIsSprinting = Player->IsSprinting();
	bIsFalling = Movement->IsFalling();
	LandingImpact = Player->GetLandingImpact();
	bIsCrouching = Movement->IsCrouching();
	VerticalSpeed = Movement->Velocity.Z;

	// Les deux etats de deplacement au sol sont composes ICI et nulle part
	// ailleurs. bIsSprinting ne dit que « la touche est enfoncee » : sans le ET
	// avec bIsMoving, appuyer sur Maj a l'arret suffit a lancer l'animation de
	// course, bras qui pompent devant un personnage qui ne bouge pas.
	//
	// Le sol et l'accroupi sont exclus des deux pour que ces booleens restent
	// mutuellement exclusifs avec les etats Air et Crouch de la machine : deux
	// conditions vraies en meme temps, c'est une transition prise au hasard.
	const bool bGrounded = !bIsFalling && !bIsCrouching;
	bIsRunning = bGrounded && bIsMoving && bIsSprinting;
	bIsWalking = bGrounded && bIsMoving && !bIsSprinting;

	// La cadence suit la vitesse REELLE et pas l'etat de sprint : c'est ce qui
	// fait que les bras accelerent pendant la montee en vitesse, au lieu de
	// sauter d'une cadence a l'autre a l'instant ou la touche est pressee.
	// Elle couvre donc aussi l'accroupi, gratuitement.
	LocomotionPlayRate = FMath::Clamp(
		GroundSpeed / ReferenceLocomotionSpeed,
		MinLocomotionPlayRate,
		MaxLocomotionPlayRate);
}
