// Fill out your copyright notice in the Description page of Project Settings. #include "Player/ExoPlayerController.h" #include "EnhancedInputComponent.h" #include "EnhancedInputSubsystems.h" #include "Characters/ExoPlayerCharacter.h" #include "Components/CapsuleComponent.h" #include "GameFramework/Character.h" #include "GameFramework/CharacterMovementComponent.h" AExoPlayerController::AExoPlayerController() { PrimaryActorTick.bCanEverTick = true; } void AExoPlayerController::BeginPlay() { Super::BeginPlay(); check(InputContext); PlayerCharacter = Cast(GetPawn()); check(PlayerCharacter); UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(GetLocalPlayer()); if (Subsystem) { Subsystem->AddMappingContext(InputContext, 0); } InteractionComponent = PlayerCharacter->InteractionComponent; ShootingComponent = PlayerCharacter->ShootingComponent; // Ustawianie w komponencie poruszania pr�dko�ci zapisanej w characterze PlayerCharacter->GetCharacterMovement()->MaxWalkSpeed = PlayerCharacter->WalkSpeed; } void AExoPlayerController::PlayerTick(const float DeltaTime) { Super::PlayerTick(DeltaTime); bIsCoverAiming = (PlayerCharacter->bIsInCover && PlayerCharacter->bIsAimingMode); if (bIsCoverAiming) { CoverAimHeightOffset = CalculateCoverAimOffset(); PlayerCharacter->SetEyePositionOffsetTarget(FVector(0.f, 0.f, CoverAimHeightOffset)); } // On screen cover state DEBUG if (bShowCoverSystemDebug) { GEngine->AddOnScreenDebugMessage(2, -1, FColor::Red, FString::Printf(TEXT("In cover: %hs"), PlayerCharacter->bIsInCover ? "true" : "false") ); } } void AExoPlayerController::SetupInputComponent() { Super::SetupInputComponent(); UEnhancedInputComponent* EnhancedInputComponent = CastChecked(InputComponent); EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AExoPlayerController::Move); EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AExoPlayerController::Look); EnhancedInputComponent->BindAction(InteractAction, ETriggerEvent::Triggered, this, &AExoPlayerController::Interact); EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerJump); EnhancedInputComponent->BindAction(DodgeAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerDodge); EnhancedInputComponent->BindAction(CrouchAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerStartCrouch); EnhancedInputComponent->BindAction(CrouchAction, ETriggerEvent::Completed, this, &AExoPlayerController::PlayerStopCrouch); EnhancedInputComponent->BindAction(SprintAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerStartSprint); EnhancedInputComponent->BindAction(SprintAction, ETriggerEvent::Completed, this, &AExoPlayerController::PlayerStopSprint); EnhancedInputComponent->BindAction(ShootAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerShoot); EnhancedInputComponent->BindAction(ThrowAction, ETriggerEvent::Completed, this, &AExoPlayerController::PlayerThrow); EnhancedInputComponent->BindAction(AimAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerStartAim); EnhancedInputComponent->BindAction(AimAction, ETriggerEvent::Completed, this, &AExoPlayerController::PlayerStopAim); EnhancedInputComponent->BindAction(MeleAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerMeleAttack); EnhancedInputComponent->BindAction(ChangeWeaponAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerChangeWeapon); EnhancedInputComponent->BindAction(SelectFirstWeaponAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerSelectFirstWeapon); EnhancedInputComponent->BindAction(SelectSecondWeaponAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerSelectSecondWeapon); EnhancedInputComponent->BindAction(DropWeaponAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerDropWeapon); EnhancedInputComponent->BindAction(ReloadAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerReload); } void AExoPlayerController::Move(const FInputActionValue& InputActionValue) { const FVector2D InputAxisVector = InputActionValue.Get(); const FRotator Rotation = GetControlRotation(); const FRotator YawRotation(0.f, Rotation.Yaw, 0.f); const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X); const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y); if (PlayerCharacter) { PlayerCharacter->AddMovementInput(ForwardDirection, InputAxisVector.X); PlayerCharacter->AddMovementInput(RightDirection, InputAxisVector.Y); } } void AExoPlayerController::Look(const FInputActionValue& InputActionValue) { FVector2D InputAxisVector = InputActionValue.Get(); AddYawInput(InputAxisVector.X); AddPitchInput(InputAxisVector.Y); } void AExoPlayerController::Interact() { if (InteractionComponent->InteractedActor) IInteractable::Execute_Interact(InteractionComponent->InteractedActor, PlayerCharacter); } void AExoPlayerController::PlayerJump() { PlayerCharacter->Jump(); } void AExoPlayerController::PlayerDodge() { UE_LOG(LogTemp, Error, TEXT("Player Dodge")); if (PlayerCharacter->GetCharacterMovement()->IsFalling()) return; if (PlayerCharacter->GetCharacterMovement()->IsCrouching()) return; if (!CanDodge) return; CanDodge = false; FVector DodgeDirection = PlayerCharacter->GetVelocity().GetSafeNormal(); DodgeDirection.Z = 0.f; if (DodgeDirection.IsNearlyZero()) DodgeDirection = PlayerCharacter->GetActorForwardVector(); PlayerCharacter->LaunchCharacter(DodgeDirection * PlayerCharacter->DodgeForce, true, true); GetWorldTimerManager().SetTimer(DodgeCooldownTimer, this, &AExoPlayerController::ResetDodge, PlayerCharacter->DodgeCooldown, false); } void AExoPlayerController::ResetDodge() { CanDodge = true; } void AExoPlayerController::PlayerStartCrouch() { if (bIsSprinting) { PlayerCharacter->GetCharacterMovement()->MaxWalkSpeedCrouched = PlayerCharacter->SlideSpeed; PlayerCharacter->SetTargetEyeHeight(PlayerCharacter->LowEyeHeightOffset); GetWorldTimerManager().SetTimer(SlideCooldownTimer, this, &AExoPlayerController::ResetSlide, PlayerCharacter->SlideCooldown, false); } PlayerCharacter->CrouchCustom(); // Start checking for cover PlayerCharacter->bIsInCover = CheckForCover(); GetWorld()->GetTimerManager().SetTimer( CoverCheckTimer, this, &AExoPlayerController::OnCoverTimer, CoverCheckRate, true ); } void AExoPlayerController::PlayerStopCrouch() { PlayerCharacter->TryUnCrouchCustom(); // Stop checking for cover GetWorld()->GetTimerManager().ClearTimer(CoverCheckTimer); PlayerCharacter->bIsInCover = false; } void AExoPlayerController::ResetSlide() { PlayerCharacter->GetCharacterMovement()->MaxWalkSpeedCrouched = PlayerCharacter->CrouchSpeed; PlayerCharacter->SetTargetEyeHeight(PlayerCharacter->CrouchedEyeHeight); } void AExoPlayerController::PlayerStartSprint() { if (!PlayerCharacter->bIsAimingMode) { UE_LOG(LogTemp, Display, TEXT("Start sprint")); bIsSprinting = true; PlayerCharacter->GetCharacterMovement()->MaxWalkSpeed = PlayerCharacter->SprintSpeed; } } void AExoPlayerController::PlayerStopSprint() { if (!PlayerCharacter->bIsAimingMode) { UE_LOG(LogTemp, Display, TEXT("Stop sprint")); bIsSprinting = false; PlayerCharacter->GetCharacterMovement()->MaxWalkSpeed = PlayerCharacter->WalkSpeed; } } void AExoPlayerController::PlayerShoot() { ShootingComponent->Shoot(); } void AExoPlayerController::PlayerThrow() { ShootingComponent->Throw(); } void AExoPlayerController::PlayerStartAim() { ShootingComponent->StartAiming(); } void AExoPlayerController::PlayerStopAim() { ShootingComponent->StopAiming(); if (PlayerCharacter->bIsInCover) { PlayerCharacter->SetEyePositionOffsetTarget(FVector(0, 0, 0)); } } void AExoPlayerController::PlayerMeleAttack() { ShootingComponent->MeleAttack(); } void AExoPlayerController::PlayerChangeWeapon() { ShootingComponent->SwitchGun(); } void AExoPlayerController::PlayerSelectFirstWeapon() { ShootingComponent->SelectGun(true); } void AExoPlayerController::PlayerSelectSecondWeapon() { ShootingComponent->SelectGun(false); } void AExoPlayerController::PlayerDropWeapon() { ShootingComponent->DropGun(); } void AExoPlayerController::PlayerReload() { ShootingComponent->Reload(); } bool AExoPlayerController::CheckForCover() { // Do a simple 8-directional line trace FVector TraceDirection = PlayerCharacter->GetActorForwardVector(); FVector TraceStart = PlayerCharacter->GetActorLocation(); TraceStart.Z += bUseEyeHeight ? PlayerCharacter->GetCrouchingEyeHeight(true) : PlayerCharacter->GetPlayerLocationAtFeet().Z + CoverObstacleMinHeight; FCollisionQueryParams QueryParams; QueryParams.AddIgnoredActor(PlayerCharacter); TArray ValidHits; for (int i = 0; i <= 8; i++) { // Trace for possible cover at set distance FHitResult Hit; FVector TraceEnd = TraceStart + (TraceDirection * MaxDistanceFromCover); GetWorld()->LineTraceSingleByChannel( Hit, TraceStart, TraceEnd, CoverTraceChannel, QueryParams ); if (bShowCoverSystemDebug) { // DEBUG DrawDebugLine(GetWorld(), TraceStart,TraceEnd, Hit.bBlockingHit ? FColor::Blue : FColor::Red, false, 5.0f, 0, 1.0f); } if (Hit.bBlockingHit == true) { // Check if this particular hit isn't against a wall (>CoverMaxHeight) FHitResult SphereHit; FVector SpherePosition = Hit.ImpactPoint; SpherePosition.Z = PlayerCharacter->GetPlayerLocationAtFeet().Z + CoverObstacleMaxHeight + CoverAimWindowRadius; GetWorld()->SweepSingleByChannel( SphereHit, SpherePosition, SpherePosition, FQuat::Identity, CoverTraceChannel, FCollisionShape::MakeSphere(CoverAimWindowRadius), QueryParams); if (bShowCoverSystemDebug) { DrawDebugSphere(GetWorld(), SphereHit.TraceStart, CoverAimWindowRadius, 8, Hit.bBlockingHit ? FColor::Blue : FColor::Red, false, 5.0f, 0, 1); } if (SphereHit.bBlockingHit == false) { ValidHits.Add(Hit); } } // Rotate trace to search in another direction next time TraceDirection = TraceDirection.RotateAngleAxis(45, FVector(0, 0, 1)); //UE_LOG(LogTemp, Display, TEXT("Check Cover %d"), i); } return ValidHits.Num() > 0; } float AExoPlayerController::CalculateCoverAimOffset() { // Nic nie rób jak nie znajdujesz się za osłoną if (!PlayerCharacter->bIsInCover) { return 0.f; } // Oblicz maksymalną wysokość, na którą można wychylić się znad osłony MaxCoverAimHeight = PlayerCharacter->GetStandingEyeHeight() * CoverAimStandFactor; FCollisionQueryParams QueryParams; QueryParams.AddIgnoredActor(PlayerCharacter); TEnumAsByte ObstacleTraceChannel = ECC_WorldStatic; FHitResult Hit; bool bFreeSpace = false; FVector ObstacleTraceStart = PlayerCharacter->GetPlayerLocationAtFeet(); ObstacleTraceStart.Z += PlayerCharacter->CrouchedEyeHeight; FVector ObstacleTraceEnd = ObstacleTraceStart + PlayerCameraManager->GetCameraRotation().Vector() * CoverAimFreeDistance; float SafeCoverAimZOffset = 0.f; while (SafeCoverAimZOffset < MaxCoverAimHeight) { ObstacleTraceStart.Z += SafeCoverAimZOffset; ObstacleTraceEnd.Z += SafeCoverAimZOffset; GetWorld()->SweepSingleByChannel( Hit, ObstacleTraceStart, ObstacleTraceEnd, FQuat::Identity, ObstacleTraceChannel, FCollisionShape::MakeSphere(CoverAimWindowRadius), QueryParams ); if (Hit.bBlockingHit == true) { // Increment check height every failed iteration SafeCoverAimZOffset += 0.2f; } else { // Break the loop if free space found break; } } const float FinalHitHeight = Hit.TraceStart.Z - PlayerCharacter->GetPlayerLocationAtFeet().Z; // If this is true that means that free space was found if (FinalHitHeight <= MaxCoverAimHeight) { PlayerCharacter->bIsCrouched = false; //// DEBUG //// if (bShowCoverSystemDebug) { DrawDebugLine(GetWorld(), Hit.TraceStart, Hit.TraceEnd, FColor::Purple); } ////////////// return FinalHitHeight - PlayerCharacter->CrouchedEyeHeight;// + //PlayerCharacter->CrouchedEyeHeight - PlayerCharacter->GetCurrentEyeHeight(); } // Return 0 offset if no free space to aim was found //return 0.f; return MaxCoverAimHeight - PlayerCharacter->CrouchedEyeHeight; } void AExoPlayerController::OnCoverTimer() { PlayerCharacter->bIsInCover = CheckForCover(); } void AExoPlayerController::DebugCoverSystem(bool show) { bShowCoverSystemDebug = show; }