// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "AstralQuestCharacter.h" #include "HeadMountedDisplayFunctionLibrary.h" #include "Camera/CameraComponent.h" #include "Components/CapsuleComponent.h" #include "Components/InputComponent.h" #include "TimerManager.h" #include "Player/AstralQuestPlayerController.h" #include "Component/InteractionComponent.h" #include "Items/Weapons/BaseWeapon.h" #include "UObject/UObjectGlobals.h" #include "GameFramework/CharacterMovementComponent.h" #include "GameFramework/Controller.h" #include "GameFramework/SpringArmComponent.h" #include "Components/SkeletalMeshComponent.h" #include "Items/Clothing/BasePants.h" #include "Items/Clothing/Baseshirt.h" ////////////////////////////////////////////////////////////////////////// // AAstralQuestCharacter AAstralQuestCharacter::AAstralQuestCharacter() { // Set size for collision capsule GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f); // set our turn rates for input BaseTurnRate = 45.f; BaseLookUpRate = 45.f; // Don't rotate when the controller rotates. Let that just affect the camera. bUseControllerRotationPitch = false; bUseControllerRotationYaw = false; bUseControllerRotationRoll = false; // Configure character movement GetCharacterMovement()->bOrientRotationToMovement = true; // Character moves in the direction of input... GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f); // ...at this rotation rate GetCharacterMovement()->JumpZVelocity = 600.f; GetCharacterMovement()->AirControl = 0.2f; // Create a camera boom (pulls in towards the player if there is a collision) CameraBoom = CreateDefaultSubobject(TEXT("CameraBoom")); CameraBoom->SetupAttachment(RootComponent); CameraBoom->TargetArmLength = 300.0f; // The camera follows at this distance behind the character CameraBoom->bUsePawnControlRotation = true; // Rotate the arm based on the controller // Create a follow camera FollowCamera = CreateDefaultSubobject(TEXT("FollowCamera")); FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation FollowCamera->bUsePawnControlRotation = false; // Camera does not rotate relative to arm // Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character) // are set in the derived blueprint asset named MyCharacter (to avoid direct content references in C++) Health = 1; Level = 1; Energy = 1; CurrentTask = FText::FromString("Get Beans"); InteractionCheckFrequency = 0.f; InteractionCheckDistance = 1000.f; CanPlaceBlocks = true; } void AAstralQuestCharacter::Attack() { if (EquippedWeapon != nullptr) { Cast(EquippedWeapon)->Attack(); } UE_LOG(LogTemp, Warning, TEXT("Attack")) } ////////////////////////////////////////////////////////////////////////// // Input void AAstralQuestCharacter::BeginPlay() { Super::BeginPlay(); Controller = Cast(GetController()); ChangeMesh(); } void AAstralQuestCharacter::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); PlayerInputComponent->BindAction("Hotbar1", IE_Pressed, this, &AAstralQuestCharacter::Hotbar1); PlayerInputComponent->BindAction("Hotbar2", IE_Pressed, this, &AAstralQuestCharacter::Hotbar2); PlayerInputComponent->BindAction("Hotbar3", IE_Pressed, this, &AAstralQuestCharacter::Hotbar3); PlayerInputComponent->BindAction("Hotbar4", IE_Pressed, this, &AAstralQuestCharacter::Hotbar4); PlayerInputComponent->BindAction("Hotbar5", IE_Pressed, this, &AAstralQuestCharacter::Hotbar5); PlayerInputComponent->BindAction("Hotbar6", IE_Pressed, this, &AAstralQuestCharacter::Hotbar6); PlayerInputComponent->BindAxis("MoveForward", this, &AAstralQuestCharacter::MoveForward); PlayerInputComponent->BindAxis("MoveRight", this, &AAstralQuestCharacter::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, &AAstralQuestCharacter::TurnAtRate); PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput); PlayerInputComponent->BindAxis("LookUpRate", this, &AAstralQuestCharacter::LookUpAtRate); // handle touch devices PlayerInputComponent->BindTouch(IE_Pressed, this, &AAstralQuestCharacter::TouchStarted); PlayerInputComponent->BindTouch(IE_Released, this, &AAstralQuestCharacter::TouchStopped); // VR headset functionality PlayerInputComponent->BindAction("ResetVR", IE_Pressed, this, &AAstralQuestCharacter::OnResetVR); PlayerInputComponent->BindAction("Interact", IE_Pressed, this, &AAstralQuestCharacter::BeginInteract); PlayerInputComponent->BindAction("Interact", IE_Released, this, &AAstralQuestCharacter::EndInteract); PlayerInputComponent->BindAction("Attack", IE_Pressed, this, &AAstralQuestCharacter::Attack); PlayerInputComponent->BindAction("PlaceItem", IE_Pressed, this, &AAstralQuestCharacter::PlaceBlock); } void AAstralQuestCharacter::MoveForward(float Value) { if (Controller != NULL) { UE_LOG(LogTemp, Warning, TEXT("True")); } else { UE_LOG(LogTemp, Warning, TEXT("False")); } if ((GetController() != NULL) && (Value != 0.0f)) { // find out which way is forward const FRotator Rotation = Controller->GetControlRotation(); const FRotator YawRotation(0, Rotation.Yaw, 0); // get forward vector const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X); AddMovementInput(Direction, Value); } } void AAstralQuestCharacter::MoveRight(float Value) { if ((Controller != NULL) && (Value != 0.0f)) { // find out which way is right const FRotator Rotation = Controller->GetControlRotation(); const FRotator YawRotation(0, Rotation.Yaw, 0); // get right vector const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y); // add movement in that direction AddMovementInput(Direction, Value); } } void AAstralQuestCharacter::ChangeMesh() { if (Pants && Shirt) { USkeletalMeshComponent* MeshComponent = Shirt->ShirtMesh; MeshComponent->SetupAttachment(GetMesh()); MeshComponent = Pants->PantsMesh; MeshComponent->SetupAttachment(GetMesh()); } } void AAstralQuestCharacter::Hotbar1() { if (Inventory->Items.IsValidIndex(0)) { ItemEquipped = 0; EquippedWeapon = Inventory->Items[0]; Controller->RefreshMainMenu(); } else { ItemEquipped = 0; EquippedWeapon = nullptr; Controller->RefreshMainMenu(); } } void AAstralQuestCharacter::Hotbar2() { if (Inventory->Items.IsValidIndex(1)) { ItemEquipped = 1; EquippedWeapon = Inventory->Items[1]; Controller->RefreshMainMenu(); } else { ItemEquipped = 1; EquippedWeapon = nullptr; Controller->RefreshMainMenu(); } } void AAstralQuestCharacter::Hotbar3() { if (Inventory->Items.IsValidIndex(2)) { ItemEquipped = 2; EquippedWeapon = Inventory->Items[2]; Controller->RefreshMainMenu(); } else { ItemEquipped = 2; EquippedWeapon = nullptr; Controller->RefreshMainMenu(); } } void AAstralQuestCharacter::Hotbar4() { if (Inventory->Items.IsValidIndex(3)) { ItemEquipped = 3; EquippedWeapon = Inventory->Items[3]; Controller->RefreshMainMenu(); } else { ItemEquipped = 3; EquippedWeapon = nullptr; Controller->RefreshMainMenu(); } } void AAstralQuestCharacter::Hotbar5() { if (Inventory->Items.IsValidIndex(4)) { ItemEquipped = 4; EquippedWeapon = Inventory->Items[4]; Controller->RefreshMainMenu(); } else { ItemEquipped = 4; EquippedWeapon = nullptr; Controller->RefreshMainMenu(); } } void AAstralQuestCharacter::Hotbar6() { if (Inventory->Items.IsValidIndex(5)) { ItemEquipped = 5; EquippedWeapon = Inventory->Items[5]; Controller->RefreshMainMenu(); } else { ItemEquipped = 5; EquippedWeapon = nullptr; Controller->RefreshMainMenu(); } } void AAstralQuestCharacter::Hotbar7() { if (Inventory->Items.IsValidIndex(6)) { ItemEquipped = 6; EquippedWeapon = Inventory->Items[6]; Controller->RefreshMainMenu(); } else { ItemEquipped = 6; EquippedWeapon = nullptr; Controller->RefreshMainMenu(); } } void AAstralQuestCharacter::DeathAnimation() { } void AAstralQuestCharacter::Tick(float DeltaTime) { Super::Tick(DeltaTime); if (GetWorld()->TimeSince(interactionData.LastInteractionCheckTime) > InteractionCheckFrequency) { PerformInteractionCheck(); } if (Health < 1) { DeathAnimation(); } } void AAstralQuestCharacter::OnResetVR() { UHeadMountedDisplayFunctionLibrary::ResetOrientationAndPosition(); } void AAstralQuestCharacter::TouchStarted(ETouchIndex::Type FingerIndex, FVector Location) { Jump(); } void AAstralQuestCharacter::TouchStopped(ETouchIndex::Type FingerIndex, FVector Location) { StopJumping(); } void AAstralQuestCharacter::TurnAtRate(float Rate) { // calculate delta for this frame from the rate information AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds()); } void AAstralQuestCharacter::PlaceBlock() { if (Inventory->Items.IsValidIndex(ItemEquipped)) { if (Inventory->Items[ItemEquipped]) { if (CanPlaceBlocks) { Inventory->Items[ItemEquipped]->DropItem(this); } } } } void AAstralQuestCharacter::LookUpAtRate(float Rate) { // calculate delta for this frame from the rate information AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds()); } void AAstralQuestCharacter::PerformInteractionCheck() { if (GetController() == nullptr) { return; } interactionData.LastInteractionCheckTime = GetWorld()->GetTimeSeconds(); FVector EyesLoc; FRotator EyesRot; GetController()->GetPlayerViewPoint(EyesLoc, EyesRot); FVector TraceStart = EyesLoc; FVector TraceEnd = (EyesRot.Vector() * InteractionCheckDistance) + TraceStart; FHitResult TraceHit; FCollisionQueryParams QueryParams; QueryParams.AddIgnoredActor(this); if (GetWorld()->LineTraceSingleByChannel(TraceHit, TraceStart, TraceEnd, ECC_Visibility, QueryParams)) { //Check if we hit an interactable object if (TraceHit.GetActor()) { if (UInteractionComponent* InteractionComponent = Cast(TraceHit.GetActor()->GetComponentByClass(UInteractionComponent::StaticClass()))) { float Distance = (TraceStart - TraceHit.ImpactPoint).Size(); if (InteractionComponent != GetInteractable() && Distance <= InteractionComponent->InteractionDistance) { FoundNewInteractable(InteractionComponent); } else if (Distance > InteractionComponent->InteractionDistance && GetInteractable()) { CouldntFindInteractable(); } return; } } } CouldntFindInteractable(); } void AAstralQuestCharacter::CouldntFindInteractable() { if (GetWorldTimerManager().IsTimerActive(TimerHandle_Interact)) { GetWorldTimerManager().ClearTimer(TimerHandle_Interact); } if (UInteractionComponent* Interactable = GetInteractable()) { Interactable->EndFocus(this); } if (interactionData.bInteractHeld) { EndInteract(); } interactionData.ViewedInteractionComponent = nullptr; } void AAstralQuestCharacter::FoundNewInteractable(UInteractionComponent* Interactable) { EndInteract(); if (UInteractionComponent* OldInteractable = GetInteractable()) { OldInteractable->EndFocus(this); } interactionData.ViewedInteractionComponent = Interactable; Interactable->BeginFocus(this); } void AAstralQuestCharacter::BeginInteract() { PerformInteractionCheck(); interactionData.bInteractHeld = true; if (UInteractionComponent* Interactable = GetInteractable()) { Interactable->BeginInteract(this); if (FMath::IsNearlyZero(Interactable->InteractionTime)) { Interact(); } else { GetWorldTimerManager().SetTimer(TimerHandle_Interact, this, &AAstralQuestCharacter::Interact, Interactable->InteractionTime, false); } } } void AAstralQuestCharacter::EndInteract() { interactionData.bInteractHeld = false; GetWorldTimerManager().ClearTimer(TimerHandle_Interact); if (UInteractionComponent* Interactable = GetInteractable()) { Interactable->EndInteract(this); } } void AAstralQuestCharacter::Interact() { GetWorldTimerManager().ClearTimer(TimerHandle_Interact); if (UInteractionComponent* Interactable = GetInteractable()) { Interactable->Interact(this); } } bool AAstralQuestCharacter::IsInteracting() const { return GetWorldTimerManager().IsTimerActive(TimerHandle_Interact); } float AAstralQuestCharacter::GetRemaingInteractTime() const { return GetWorldTimerManager().GetTimerRemaining(TimerHandle_Interact); }