358 lines
10 KiB
C++
358 lines
10 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "AgrarianGameCharacter.h"
|
|
#include "AgrarianBuildingPlacementComponent.h"
|
|
#include "AgrarianCraftingComponent.h"
|
|
#include "AgrarianInteractable.h"
|
|
#include "AgrarianInventoryComponent.h"
|
|
#include "AgrarianSurvivalComponent.h"
|
|
#include "Engine/LocalPlayer.h"
|
|
#include "Camera/CameraComponent.h"
|
|
#include "Components/CapsuleComponent.h"
|
|
#include "GameFramework/CharacterMovementComponent.h"
|
|
#include "GameFramework/SpringArmComponent.h"
|
|
#include "GameFramework/Controller.h"
|
|
#include "EnhancedInputComponent.h"
|
|
#include "EnhancedInputSubsystems.h"
|
|
#include "InputActionValue.h"
|
|
#include "AgrarianGame.h"
|
|
#include "Net/UnrealNetwork.h"
|
|
|
|
AAgrarianGameCharacter::AAgrarianGameCharacter()
|
|
{
|
|
PrimaryActorTick.bCanEverTick = true;
|
|
|
|
// Set size for collision capsule
|
|
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
|
|
|
|
// 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;
|
|
GetCharacterMovement()->RotationRate = FRotator(0.0f, 500.0f, 0.0f);
|
|
|
|
// Note: For faster iteration times these variables, and many more, can be tweaked in the Character Blueprint
|
|
// instead of recompiling to adjust them
|
|
GetCharacterMovement()->JumpZVelocity = 500.f;
|
|
GetCharacterMovement()->AirControl = 0.35f;
|
|
GetCharacterMovement()->MaxWalkSpeed = WalkSpeed;
|
|
GetCharacterMovement()->MinAnalogWalkSpeed = 20.f;
|
|
GetCharacterMovement()->BrakingDecelerationWalking = 2000.f;
|
|
GetCharacterMovement()->BrakingDecelerationFalling = 1500.0f;
|
|
|
|
// Create a camera boom (pulls in towards the player if there is a collision)
|
|
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
|
|
CameraBoom->SetupAttachment(RootComponent);
|
|
CameraBoom->TargetArmLength = ThirdPersonCameraDistance;
|
|
CameraBoom->bUsePawnControlRotation = true;
|
|
|
|
// Create a follow camera
|
|
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
|
|
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
|
|
FollowCamera->bUsePawnControlRotation = false;
|
|
|
|
SurvivalComponent = CreateDefaultSubobject<UAgrarianSurvivalComponent>(TEXT("SurvivalComponent"));
|
|
InventoryComponent = CreateDefaultSubobject<UAgrarianInventoryComponent>(TEXT("InventoryComponent"));
|
|
CraftingComponent = CreateDefaultSubobject<UAgrarianCraftingComponent>(TEXT("CraftingComponent"));
|
|
BuildingPlacementComponent = CreateDefaultSubobject<UAgrarianBuildingPlacementComponent>(TEXT("BuildingPlacementComponent"));
|
|
|
|
// Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character)
|
|
// are set in the derived blueprint asset named ThirdPersonCharacter (to avoid direct content references in C++)
|
|
}
|
|
|
|
void AAgrarianGameCharacter::Tick(float DeltaSeconds)
|
|
{
|
|
Super::Tick(DeltaSeconds);
|
|
|
|
if (!HasAuthority() || !bWantsToSprint)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!CanSprint())
|
|
{
|
|
SetWantsToSprint(false);
|
|
return;
|
|
}
|
|
|
|
if (GetVelocity().SizeSquared2D() > KINDA_SMALL_NUMBER && SurvivalComponent)
|
|
{
|
|
SurvivalComponent->SpendStamina(SprintStaminaCostPerSecond * DeltaSeconds);
|
|
if (!CanSprint())
|
|
{
|
|
SetWantsToSprint(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AAgrarianGameCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
|
{
|
|
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
|
DOREPLIFETIME(AAgrarianGameCharacter, bWantsToSprint);
|
|
}
|
|
|
|
void AAgrarianGameCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
|
|
{
|
|
// Set up action bindings
|
|
if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent)) {
|
|
|
|
// Jumping
|
|
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &ACharacter::Jump);
|
|
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);
|
|
|
|
// Moving
|
|
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AAgrarianGameCharacter::Move);
|
|
EnhancedInputComponent->BindAction(MouseLookAction, ETriggerEvent::Triggered, this, &AAgrarianGameCharacter::Look);
|
|
|
|
// Looking
|
|
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AAgrarianGameCharacter::Look);
|
|
|
|
if (InteractAction)
|
|
{
|
|
EnhancedInputComponent->BindAction(InteractAction, ETriggerEvent::Started, this, &AAgrarianGameCharacter::Interact);
|
|
}
|
|
|
|
if (SprintAction)
|
|
{
|
|
EnhancedInputComponent->BindAction(SprintAction, ETriggerEvent::Started, this, &AAgrarianGameCharacter::StartSprint);
|
|
EnhancedInputComponent->BindAction(SprintAction, ETriggerEvent::Completed, this, &AAgrarianGameCharacter::StopSprint);
|
|
EnhancedInputComponent->BindAction(SprintAction, ETriggerEvent::Canceled, this, &AAgrarianGameCharacter::StopSprint);
|
|
}
|
|
|
|
if (ToggleCameraAction)
|
|
{
|
|
EnhancedInputComponent->BindAction(ToggleCameraAction, ETriggerEvent::Started, this, &AAgrarianGameCharacter::ToggleCameraPerspective);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAgrarianGame, Error, TEXT("'%s' Failed to find an Enhanced Input component! This template is built to use the Enhanced Input system. If you intend to use the legacy system, then you will need to update this C++ file."), *GetNameSafe(this));
|
|
}
|
|
}
|
|
|
|
void AAgrarianGameCharacter::Move(const FInputActionValue& Value)
|
|
{
|
|
// input is a Vector2D
|
|
FVector2D MovementVector = Value.Get<FVector2D>();
|
|
|
|
// route the input
|
|
DoMove(MovementVector.X, MovementVector.Y);
|
|
}
|
|
|
|
void AAgrarianGameCharacter::Look(const FInputActionValue& Value)
|
|
{
|
|
// input is a Vector2D
|
|
FVector2D LookAxisVector = Value.Get<FVector2D>();
|
|
|
|
// route the input
|
|
DoLook(LookAxisVector.X, LookAxisVector.Y);
|
|
}
|
|
|
|
void AAgrarianGameCharacter::Interact()
|
|
{
|
|
TryInteract();
|
|
}
|
|
|
|
void AAgrarianGameCharacter::StartSprint()
|
|
{
|
|
SetWantsToSprint(true);
|
|
}
|
|
|
|
void AAgrarianGameCharacter::StopSprint()
|
|
{
|
|
SetWantsToSprint(false);
|
|
}
|
|
|
|
void AAgrarianGameCharacter::ToggleCameraPerspective()
|
|
{
|
|
SetFirstPersonCamera(!bFirstPersonCamera);
|
|
}
|
|
|
|
void AAgrarianGameCharacter::SetFirstPersonCamera(bool bEnableFirstPerson)
|
|
{
|
|
bFirstPersonCamera = bEnableFirstPerson;
|
|
|
|
if (!CameraBoom || !FollowCamera)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (bFirstPersonCamera)
|
|
{
|
|
CameraBoom->TargetArmLength = 0.0f;
|
|
CameraBoom->SocketOffset = FirstPersonCameraOffset;
|
|
CameraBoom->bDoCollisionTest = false;
|
|
FollowCamera->bUsePawnControlRotation = false;
|
|
|
|
if (GetMesh())
|
|
{
|
|
GetMesh()->SetOwnerNoSee(true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CameraBoom->TargetArmLength = ThirdPersonCameraDistance;
|
|
CameraBoom->SocketOffset = FVector::ZeroVector;
|
|
CameraBoom->bDoCollisionTest = true;
|
|
FollowCamera->bUsePawnControlRotation = false;
|
|
|
|
if (GetMesh())
|
|
{
|
|
GetMesh()->SetOwnerNoSee(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AAgrarianGameCharacter::SetWantsToSprint(bool bNewWantsToSprint)
|
|
{
|
|
const bool bAllowedSprintIntent = bNewWantsToSprint && CanSprint();
|
|
if (bWantsToSprint == bAllowedSprintIntent)
|
|
{
|
|
return;
|
|
}
|
|
|
|
bWantsToSprint = bAllowedSprintIntent;
|
|
ApplyMovementSpeed();
|
|
|
|
if (!HasAuthority())
|
|
{
|
|
ServerSetWantsToSprint(bAllowedSprintIntent);
|
|
}
|
|
}
|
|
|
|
bool AAgrarianGameCharacter::CanSprint() const
|
|
{
|
|
return SurvivalComponent
|
|
&& SurvivalComponent->IsAlive()
|
|
&& SurvivalComponent->Survival.Stamina > MinSprintStamina;
|
|
}
|
|
|
|
bool AAgrarianGameCharacter::IsSprinting() const
|
|
{
|
|
return bWantsToSprint && CanSprint();
|
|
}
|
|
|
|
void AAgrarianGameCharacter::ApplyMovementSpeed()
|
|
{
|
|
if (UCharacterMovementComponent* MovementComponent = GetCharacterMovement())
|
|
{
|
|
MovementComponent->MaxWalkSpeed = IsSprinting() ? SprintSpeed : WalkSpeed;
|
|
}
|
|
}
|
|
|
|
void AAgrarianGameCharacter::OnRep_SprintState()
|
|
{
|
|
ApplyMovementSpeed();
|
|
}
|
|
|
|
void AAgrarianGameCharacter::DoMove(float Right, float Forward)
|
|
{
|
|
if (GetController() != nullptr)
|
|
{
|
|
// find out which way is forward
|
|
const FRotator Rotation = GetController()->GetControlRotation();
|
|
const FRotator YawRotation(0, Rotation.Yaw, 0);
|
|
|
|
// get forward vector
|
|
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
|
|
|
|
// get right vector
|
|
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
|
|
|
|
// add movement
|
|
AddMovementInput(ForwardDirection, Forward);
|
|
AddMovementInput(RightDirection, Right);
|
|
}
|
|
}
|
|
|
|
void AAgrarianGameCharacter::DoLook(float Yaw, float Pitch)
|
|
{
|
|
if (GetController() != nullptr)
|
|
{
|
|
// add yaw and pitch input to controller
|
|
AddControllerYawInput(Yaw);
|
|
AddControllerPitchInput(Pitch);
|
|
}
|
|
}
|
|
|
|
void AAgrarianGameCharacter::DoJumpStart()
|
|
{
|
|
// signal the character to jump
|
|
Jump();
|
|
}
|
|
|
|
void AAgrarianGameCharacter::DoJumpEnd()
|
|
{
|
|
// signal the character to stop jumping
|
|
StopJumping();
|
|
}
|
|
|
|
void AAgrarianGameCharacter::TryInteract()
|
|
{
|
|
FVector TraceStart;
|
|
FRotator TraceRotation;
|
|
|
|
if (Controller)
|
|
{
|
|
Controller->GetPlayerViewPoint(TraceStart, TraceRotation);
|
|
}
|
|
else
|
|
{
|
|
TraceStart = FollowCamera ? FollowCamera->GetComponentLocation() : GetActorLocation();
|
|
TraceRotation = FollowCamera ? FollowCamera->GetComponentRotation() : GetActorRotation();
|
|
}
|
|
|
|
const FVector TraceEnd = TraceStart + TraceRotation.Vector() * InteractionDistance;
|
|
FHitResult Hit;
|
|
FCollisionQueryParams Params(SCENE_QUERY_STAT(AgrarianInteractTrace), false, this);
|
|
|
|
if (!GetWorld() || !GetWorld()->LineTraceSingleByChannel(Hit, TraceStart, TraceEnd, ECC_Visibility, Params))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AActor* HitActor = Hit.GetActor();
|
|
if (!HitActor || !HitActor->GetClass()->ImplementsInterface(UAgrarianInteractable::StaticClass()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (HasAuthority())
|
|
{
|
|
if (IAgrarianInteractable::Execute_CanInteract(HitActor, this))
|
|
{
|
|
IAgrarianInteractable::Execute_Interact(HitActor, this);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ServerInteract(HitActor);
|
|
}
|
|
}
|
|
|
|
void AAgrarianGameCharacter::ServerSetWantsToSprint_Implementation(bool bNewWantsToSprint)
|
|
{
|
|
SetWantsToSprint(bNewWantsToSprint);
|
|
}
|
|
|
|
void AAgrarianGameCharacter::ServerInteract_Implementation(AActor* TargetActor)
|
|
{
|
|
if (!TargetActor || !TargetActor->GetClass()->ImplementsInterface(UAgrarianInteractable::StaticClass()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (FVector::DistSquared(TargetActor->GetActorLocation(), GetActorLocation()) > FMath::Square(InteractionDistance + 100.0f))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (IAgrarianInteractable::Execute_CanInteract(TargetActor, this))
|
|
{
|
|
IAgrarianInteractable::Execute_Interact(TargetActor, this);
|
|
}
|
|
}
|