#pragma once #include "CoreMinimal.h" #include "Components/ActorComponent.h" #include "Core/DescentHandle.h" #include "Core/DescentWorldSubsystem.h" #include "UObject/Interface.h" #include "DescentInteraction.generated.h" class ADescentCharacter; UINTERFACE(MinimalAPI, NotBlueprintable) class UDescentInteractable : public UInterface { GENERATED_BODY() }; /** Anything the player can use with E (DESIGN_UE.md section 5.4). Collision is not involved: the same result with blockout or real meshes. */ class THEDESCENT_API IDescentInteractable { GENERATED_BODY() public: /** The label after "[E]", e.g. "Take the helm - pilot the CANTICLE". */ virtual FString GetPrompt() const = 0; /** World position (Unreal units) the prompt aims at. */ virtual FVector GetFocusPointUU() const = 0; virtual double GetRangeM() const; virtual double GetMinDot() const; virtual bool CanUse(const ADescentCharacter* User) const { return true; } virtual void Use(ADescentCharacter* User) = 0; }; /** Every interactable registers here; the character's component searches it. */ UCLASS() class THEDESCENT_API UDescentInteractionRegistry : public UDescentWorldSubsystem { GENERATED_BODY() public: static UDescentInteractionRegistry* Get(const UWorld* World); void Register(UObject* Interactable); void Unregister(UObject* Interactable); const TArray>& All() const { return Items; } private: TArray> Items; }; /** Keeps the current focus: the v2 rule, within range of the eye and dot(view, toTarget) >= min dot, score = dot - 0.05 * distance. */ UCLASS(ClassGroup = (Descent)) class THEDESCENT_API UDescentInteractionComponent : public UActorComponent { GENERATED_BODY() public: UDescentInteractionComponent(); /** Recomputes the focus from the eye and view direction (world, uu / unit). Called once per late step. */ void UpdateFocus(const FVector& EyeUU, const FVector& ViewDir); void ClearFocus() { Focus.Reset(); FocusLabel.Reset(); } UObject* GetFocus() const { return Focus.Get(); } const FString& GetFocusLabel() const { return FocusLabel; } /** E: uses the focus if it is still valid and usable. Returns whether something was used. */ bool UseFocus(ADescentCharacter* User); private: TWeakObjectPtr Focus; FString FocusLabel; };