text: // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
  
  #include "TrainingRange.h"
  #include "TrainingRangeCharacter.h"
  #include "TrainingRangeProjectile.h"
  #include "Animation/AnimInstance.h"
  #include "GameFramework/InputSettings.h"
  #include "Kismet/HeadMountedDisplayFunctionLibrary.h"
  #include "MotionControllerComponent.h"
  #include "EngineGlobals.h"
  #include "Runtime/Engine/Classes/Engine/Engine.h"
  
  DEFINE_LOG_CATEGORY_STATIC(LogFPChar, Warning, All);
  
  //////////////////////////////////////////////////////////////////////////
  // ATrainingRangeCharacter
  
  ATrainingRangeCharacter::ATrainingRangeCharacter()
  {
  	// Set size for collision capsule
  	GetCapsuleComponent()->InitCapsuleSize(55.f, 96.0f);
  
  	// set our turn rates for input
  	BaseTurnRate = 45.f;
  	BaseLookUpRate = 45.f;
  
  	// Create a CameraComponent	
  	FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
  	FirstPersonCameraComponent->SetupAttachment(GetCapsuleComponent());
  	FirstPersonCameraComponent->RelativeLocation = FVector(-39.56f, 1.75f, 64.f); // Position the camera
  	FirstPersonCameraComponent->bUsePawnControlRotation = true;
  
  	// Create a mesh component that will be used when being viewed from a '1st person' view (when controlling this pawn)
  	Mesh1P = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("CharacterMesh1P"));
  	Mesh1P->SetOnlyOwnerSee(true);
  	Mesh1P->SetupAttachment(FirstPersonCameraComponent);
  	Mesh1P->bCastDynamicShadow = false;
  	Mesh1P->CastShadow = false;
  	Mesh1P->RelativeRotation = FRotator(1.9f, -19.19f, 5.2f);
  	Mesh1P->RelativeLocation = FVector(-0.5f, -4.4f, -155.7f);
  
  	// Create a gun mesh component
  	FP_Gun = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FP_Gun"));
  	FP_Gun->SetOnlyOwnerSee(true);			// only the owning player will see this mesh
  	FP_Gun->bCastDynamicShadow = false;
  	FP_Gun->CastShadow = false;
  	// FP_Gun->SetupAttachment(Mesh1P, TEXT("GripPoint"));
  	FP_Gun->SetupAttachment(RootComponent);
  
  	FP_MuzzleLocation = CreateDefaultSubobject<USceneComponent>(TEXT("MuzzleLocation"));
  	FP_MuzzleLocation->SetupAttachment(FP_Gun);
  	FP_MuzzleLocation->SetRelativeLocation(FVector(0.2f, 48.4f, -10.6f));
  
  	// Default offset from the character location for projectiles to spawn
  	GunOffset = FVector(100.0f, 0.0f, 0.0f);
  
  	// Note: The ProjectileClass and the skeletal mesh/anim blueprints for Mesh1P, FP_Gun, and VR_Gun 
  	// are set in the derived blueprint asset named MyCharacter to avoid direct content references in C++.
  
  	isFiring = false;
  	firePower = 0.0;
  	shotsFired = 0;
  	frozenAmount = 0.0;
  	maxFrozenAmount = 1.1;
  
  	frozenDegenDelay = 2.5;
  	frozenDegenAmount = -0.1;
  
  	baseSpeed = GetCharacterMovement()->MaxWalkSpeed;
  }
  
  void ATrainingRangeCharacter::BeginPlay()
  {
  	// Call the base class  
  	Super::BeginPlay();
  
  	//Attach gun mesh component to Skeleton, doing it here because the skeleton is not yet created in the constructor
  	FP_Gun->AttachToComponent(Mesh1P, FAttachmentTransformRules(EAttachmentRule::SnapToTarget, true), TEXT("GripPoint"));
  
  	// Show or hide the two versions of the gun based on whether or not we're using motion controllers.
  	if (bUsingMotionControllers)
  	{
  		Mesh1P->SetHiddenInGame(true, true);
  	}
  	else
  	{
  		Mesh1P->SetHiddenInGame(false, true);
  	}
  }
  
  //////////////////////////////////////////////////////////////////////////
  // Input
  
  void ATrainingRangeCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
  {
  	// set up gameplay key bindings
  	check(PlayerInputComponent);
  
  	PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
  	PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
  
  	//InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &ATrainingRangeCharacter::TouchStarted);
  	if (EnableTouchscreenMovement(PlayerInputComponent) == false)
  	{
  		PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &ATrainingRangeCharacter::OnStartFire);
  		PlayerInputComponent->BindAction("Fire", IE_Released, this, &ATrainingRangeCharacter::OnStopFire);
  	}
  
  	PlayerInputComponent->BindAxis("MoveForward", this, &ATrainingRangeCharacter::MoveForward);
  	PlayerInputComponent->BindAxis("MoveRight", this, &ATrainingRangeCharacter::MoveRight);
  
  	// We have 2 versions of the rotation bindings to handle different kinds of devices differently
  	// "turn" handles devices that provide an absolute delta, such as a mouse.
  	// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
  	PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
  	PlayerInputComponent->BindAxis("TurnRate", this, &ATrainingRangeCharacter::TurnAtRate);
  	PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
  	PlayerInputComponent->BindAxis("LookUpRate", this, &ATrainingRangeCharacter::LookUpAtRate);
  }
  
  void ATrainingRangeCharacter::Tick (float DeltaTime)
  {
  	Super::Tick(DeltaTime);
  
  	storedDelta += DeltaTime;
  
  	if (isFiring) {
  		firePower += DeltaTime;
  	}
  
  	// Add thaw overtime variable
  	if (frozenAmount > 0) {
  		if (storedDelta > frozenDegenDelay) {
  			storedDelta = 0;
  			SetFrozenAmount(frozenDegenAmount);
  		}
  	}
  	GetCharacterMovement()->MaxWalkSpeed = baseSpeed - (baseSpeed * frozenAmount);
  }
  
  void ATrainingRangeCharacter::OnStartFire() 
  {
  	isFiring = (isFiring ? isFiring : !isFiring);
  }
  
  void ATrainingRangeCharacter::OnStopFire() 
  {
  	isFiring = (!isFiring ? isFiring : !isFiring);
  
  	Fire();
  }
  
  float ATrainingRangeCharacter::TakeDamage(float Damage, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, class AActor* DamageCauser) {
  	SetFrozenAmount(0.3);
  	
  	/*UMaterialInstanceDynamic* DynamicMaterial = UMaterialInstanceDynamic::Create(GetMesh()->GetMaterial(0), this);
  
  	if (DynamicMaterial != NULL) {
  		FLinearColor currentColor;
  		DynamicMaterial->GetVectorParameterValue("bod", currentColor);
  
  		if (currentColor != FLinearColor::Yellow) {
  			DynamicMaterial->SetVectorParameterValue("bod", FLinearColor::MakeRandomColor());
  			//GetMesh()->SetMaterial(0, DynamicMaterial);
  		//}
  	}*/
  
  	GetMesh()->SetVectorParameterValueOnMaterials("BodyColor", FMath::VRand());
  
  	return 1.0;
  }
  
  TSubclassOf<UDamageType> ATrainingRangeCharacter::GetDamageType() {
  	return FrostDamageType;
  }
  
  void ATrainingRangeCharacter::Fire()
  {
  	// try and fire a projectile
  	if (ProjectileClass != NULL)
  	{
  		UWorld* const World = GetWorld();
  		if (World != NULL)
  		{
  			const FRotator SpawnRotation = GetControlRotation();
  			// MuzzleOffset is in camera space, so transform it to world space before offsetting from the character location to find the final muzzle position
  			const FVector SpawnLocation = ((FP_MuzzleLocation != nullptr) ? FP_MuzzleLocation->GetComponentLocation() : GetActorLocation()) + SpawnRotation.RotateVector(GunOffset);
  
  			//Set Spawn Collision Handling Override
  			FActorSpawnParameters ActorSpawnParams;
  			ActorSpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding;
  
  			// spawn the projectile at the muzzle
  			ATrainingRangeProjectile* projectile = World->SpawnActor<ATrainingRangeProjectile>(ProjectileClass, SpawnLocation, SpawnRotation, ActorSpawnParams);
  
  			if (projectile != NULL) {
  				// Use this to loft the projectile
  				/*projectile->init(firePower, FLinearColor::Blue);
  
  				GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("firePower: %f"), firePower));
  				firePower = 0;*/
  				projectile->init(5, FLinearColor::Blue);
  
  				// try and play the sound if specified
  				if (FireSound != NULL) {
  					UGameplayStatics::PlaySoundAtLocation(this, FireSound, GetActorLocation());
  				}
  
  				// try and play a firing animation if specified
  				if (FireAnimation != NULL) {
  					// Get the animation object for the arms mesh
  					UAnimInstance* AnimInstance = Mesh1P->GetAnimInstance();
  					if (AnimInstance != NULL) {
  						AnimInstance->Montage_Play(FireAnimation, 1.f);
  					}
  				}
  			}
  		}
  	}
  }
  
  void ATrainingRangeCharacter::BeginTouch(const ETouchIndex::Type FingerIndex, const FVector Location)
  {
  	if (TouchItem.bIsPressed == true)
  	{
  		return;
  	}
  	TouchItem.bIsPressed = true;
  	TouchItem.FingerIndex = FingerIndex;
  	TouchItem.Location = Location;
  	TouchItem.bMoved = false;
  }
  
  void ATrainingRangeCharacter::EndTouch(const ETouchIndex::Type FingerIndex, const FVector Location)
  {
  	if (TouchItem.bIsPressed == false)
  	{
  		return;
  	}
  	if ((FingerIndex == TouchItem.FingerIndex) && (TouchItem.bMoved == false))
  	{
  		Fire();
  	}
  	TouchItem.bIsPressed = false;
  }
  
  //Commenting this section out to be consistent with FPS BP template.
  //This allows the user to turn without using the right virtual joystick
  
  //void ATrainingRangeCharacter::TouchUpdate(const ETouchIndex::Type FingerIndex, const FVector Location)
  //{
  //	if ((TouchItem.bIsPressed == true) && (TouchItem.FingerIndex == FingerIndex))
  //	{
  //		if (TouchItem.bIsPressed)
  //		{
  //			if (GetWorld() != nullptr)
  //			{
  //				UGameViewportClient* ViewportClient = GetWorld()->GetGameViewport();
  //				if (ViewportClient != nullptr)
  //				{
  //					FVector MoveDelta = Location - TouchItem.Location;
  //					FVector2D ScreenSize;
  //					ViewportClient->GetViewportSize(ScreenSize);
  //					FVector2D ScaledDelta = FVector2D(MoveDelta.X, MoveDelta.Y) / ScreenSize;
  //					if (FMath::Abs(ScaledDelta.X) >= 4.0 / ScreenSize.X)
  //					{
  //						TouchItem.bMoved = true;
  //						float Value = ScaledDelta.X * BaseTurnRate;
  //						AddControllerYawInput(Value);
  //					}
  //					if (FMath::Abs(ScaledDelta.Y) >= 4.0 / ScreenSize.Y)
  //					{
  //						TouchItem.bMoved = true;
  //						float Value = ScaledDelta.Y * BaseTurnRate;
  //						AddControllerPitchInput(Value);
  //					}
  //					TouchItem.Location = Location;
  //				}
  //				TouchItem.Location = Location;
  //			}
  //		}
  //	}
  //}
  
  void ATrainingRangeCharacter::MoveForward(float Value)
  {
  	if (Value != 0.0f)
  	{
  		// add movement in that direction
  		AddMovementInput(GetActorForwardVector(), Value);
  	}
  }
  
  void ATrainingRangeCharacter::MoveRight(float Value)
  {
  	if (Value != 0.0f)
  	{
  		// add movement in that direction
  		AddMovementInput(GetActorRightVector(), Value);
  	}
  }
  
  void ATrainingRangeCharacter::SetFrozenAmount(float val) {
  	float newFrozenAmount = frozenAmount + val;
  
  	if (newFrozenAmount >= maxFrozenAmount) {
  		newFrozenAmount = maxFrozenAmount;
  	}
  
  	if (newFrozenAmount <= 0) {
  		newFrozenAmount = 0;
  	}
  
  	frozenAmount = newFrozenAmount;
  }
  
  void ATrainingRangeCharacter::TurnAtRate(float Rate)
  {
  	// calculate delta for this frame from the rate information
  	AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
  }
  
  void ATrainingRangeCharacter::LookUpAtRate(float Rate)
  {
  	// calculate delta for this frame from the rate information
  	AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
  }
  
  bool ATrainingRangeCharacter::EnableTouchscreenMovement(class UInputComponent* PlayerInputComponent)
  {
  	bool bResult = false;
  	if (FPlatformMisc::GetUseVirtualJoysticks() || GetDefault<UInputSettings>()->bUseMouseForTouch)
  	{
  		bResult = true;
  		PlayerInputComponent->BindTouch(EInputEvent::IE_Pressed, this, &ATrainingRangeCharacter::BeginTouch);
  		PlayerInputComponent->BindTouch(EInputEvent::IE_Released, this, &ATrainingRangeCharacter::EndTouch);
  
  		//Commenting this out to be more consistent with FPS BP template.
  		//PlayerInputComponent->BindTouch(EInputEvent::IE_Repeat, this, &ATrainingRangeCharacter::TouchUpdate);
  	}
  	return bResult;
  }
  