#include "Player/DescentInteraction.h" #include "Core/Tuning/DescentTune_Player.h" #include "DescentUnits.h" #include "Engine/World.h" double IDescentInteractable::GetRangeM() const { return Descent::Tune::Player::DefaultInteractRangeM; } double IDescentInteractable::GetMinDot() const { return Descent::Tune::Player::DefaultInteractMinDot; } UDescentInteractionRegistry* UDescentInteractionRegistry::Get(const UWorld* World) { return World ? World->GetSubsystem() : nullptr; } void UDescentInteractionRegistry::Register(UObject* Interactable) { if (Interactable && Cast(Interactable)) { Items.AddUnique(Interactable); } } void UDescentInteractionRegistry::Unregister(UObject* Interactable) { Items.Remove(Interactable); } UDescentInteractionComponent::UDescentInteractionComponent() { PrimaryComponentTick.bCanEverTick = false; } void UDescentInteractionComponent::UpdateFocus(const FVector& EyeUU, const FVector& ViewDir) { namespace T = Descent::Tune::Player; UDescentInteractionRegistry* Reg = UDescentInteractionRegistry::Get(GetWorld()); UObject* Best = nullptr; double BestScore = 0.0; if (Reg) { for (const TWeakObjectPtr& W : Reg->All()) { UObject* O = W.Get(); const IDescentInteractable* I = O ? Cast(O) : nullptr; if (!I) { continue; } const FVector ToTarget = Descent::UUToMetres(I->GetFocusPointUU() - EyeUU); const double Dist = ToTarget.Size(); if (Dist > I->GetRangeM() || Dist < 1e-6) { continue; } const double Dot = FVector::DotProduct(ToTarget / Dist, ViewDir); if (Dot < I->GetMinDot()) { continue; } const double Score = Dot - T::InteractDistanceScore * Dist; if (!Best || Score > BestScore) { Best = O; BestScore = Score; } } } Focus = Best; FocusLabel = Best ? Cast(Best)->GetPrompt() : FString(); } bool UDescentInteractionComponent::UseFocus(ADescentCharacter* User) { UObject* O = Focus.Get(); IDescentInteractable* I = O ? Cast(O) : nullptr; if (!I || !I->CanUse(User)) { return false; } I->Use(User); return true; }