// REALMS IN RUIN. Copyright (c) 2025 Trevor Edwards. All rights reserved. #include "Player/RIRPlayerController.h" #include "EnhancedInputComponent.h" #include "EnhancedInputSubsystems.h" #include "GameFramework/Pawn.h" #include "GameFramework/PlayerController.h" #include "Interfaces/TargetInterface.h" ARIRPlayerController::ARIRPlayerController() { bReplicates = true; } void ARIRPlayerController::PlayerTick(float DeltaTime) { Super::PlayerTick(DeltaTime); CursorTrace(); } void ARIRPlayerController::BindCallbacksToDependencies() { } void ARIRPlayerController::CursorTrace() { // TODO: Modify this to be controller-compatible // Additionally, create separate function to handle Gauntlet-style directional target line // Cleanup unnecessary comments as needed FHitResult CursorHit; // Perform a visibility trace from the mouse cursor position GetHitResultUnderCursor(ECC_Visibility, false, CursorHit); // Exit early if the trace didn't hit anything if (!CursorHit.bBlockingHit) { LastActor = ThisActor; ThisActor = nullptr; return; } // Cache the actor under the cursor AActor* HitActor = CursorHit.GetActor(); // Store the previous targeted actor LastActor = ThisActor; // Validate that the hit actor exists and implements ITargetInterface if (HitActor && HitActor->GetClass()->ImplementsInterface(UTargetInterface::StaticClass())) { // Wrap in TScriptInterface safely ThisActor = TScriptInterface(HitActor); } else { ThisActor = nullptr; } /** * Line trace from cursor. There are several scenarios: * A. LastActor is null && ThisActor is null * - Do nothing * B. LastActor is null && ThisActor is valid * - Highlight ThisActor * C. LastActor is valid && ThisActor is null * - UnHighlight LastActor * D. Both actors are valid, but LastActor != ThisActor * - UnHighlight LastActor and Highlight ThisActor * E. Both actors are valid, and are the same actor * - Do nothing */ if (!LastActor) // LastActor is null { if (ThisActor) { // Case B: highlight newly found actor ThisActor->HighlightActor(); } // Case A: both are null – do nothing } else // LastActor is valid { if (!ThisActor) { // Case C: Unhighlight the old actor, nothing to highlight now LastActor->UnHighlightActor(); } else if (LastActor != ThisActor) { // Case D: new target is different – update both visuals LastActor->UnHighlightActor(); ThisActor->HighlightActor(); } // Case E: same actor – do nothing } } void ARIRPlayerController::BeginPlay() { Super::BeginPlay(); check(PlayerMappingContext); // Set up Enhanced Input system for this player UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(GetLocalPlayer()); if (Subsystem) { Subsystem->AddMappingContext(PlayerMappingContext, 0); // Add the player input mapping context with priority 0 (highest) } // Configure the mouse cursor to be visible and use the default style bShowMouseCursor = true; DefaultMouseCursor = EMouseCursor::Default; // Set input mode to allow both game and UI interaction FInputModeGameAndUI InputModeData; InputModeData.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock); // Don't lock mouse to viewport InputModeData.SetHideCursorDuringCapture(false); // Don't hide the cursor when capturing input SetInputMode(InputModeData); // Apply input mode settings to this controller } void ARIRPlayerController::SetupInputComponent() { Super::SetupInputComponent(); // Cast InputComponent to EnhancedInputComponent to access enhanced input features UEnhancedInputComponent* EnhancedInputComponent = CastChecked(InputComponent); // Bind the MoveAction input to the HandleMoveInput function when the action is triggered EnhancedInputComponent->BindAction(MovementInput, ETriggerEvent::Triggered, this, &ARIRPlayerController::HandleMovementInput); } void ARIRPlayerController::HandleMovementInput(const FInputActionValue& InputActionValue) { if (!GetPawn()) { return; } FVector2D InputVector = InputActionValue.Get(); if (InputVector.IsNearlyZero()) { return; } // Get the control rotation FRotator ControlRot = GetControlRotation(); FRotator YawRot(0.f, ControlRot.Yaw, 0.f); // Get forward and right vectors const FVector ForwardVector = FRotationMatrix(YawRot).GetUnitAxis(EAxis::X); const FVector RightVector = FRotationMatrix(YawRot).GetUnitAxis(EAxis::Y); if (APawn* ControlledPlayer = GetPawn()) { // Apply movement input GetPawn()->AddMovementInput(ForwardVector, InputVector.Y); GetPawn()->AddMovementInput(RightVector, InputVector.X); } }