Files
KingshearthLegacy/Source/KingshearthLegacy/UI/InteractionPromptWidget.cpp
T
Dolobarsch a5eb673c6e Interaktionsoptionen und UI-Verbesserungen
Neue Methode `RefreshFocusedOptions()` hinzugefügt, um die Optionen des fokussierten Akteurs nach einer Interaktion zu aktualisieren. Änderungen an der Navigation im Kontextmenü: Begrenzte Navigation ersetzt zyklische Navigation.

`FInteractionOption` erweitert um `TextColorOverride` und `HighlightColorOverride`, um individuelle Farben für Optionen zu ermöglichen.

UI-Widgets (`InteractionOptionEntryWidget`) angepasst, um Hintergrund- und Hervorhebungsfarben dynamisch zu setzen. Hinweistext für sekundäre Interaktionen zeigt nun spezifische Optionennamen an.
2026-09-11 01:52:59 +02:00

179 lines
5.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "InteractionPromptWidget.h"
#include "Interaction/InteractionComponent.h"
#include "Interaction/Interactable.h"
#include "Components/TextBlock.h"
#include "Components/Widget.h"
#include "SegmentedBarWidget.h"
#include "KingshearthLegacy.h"
namespace
{
/** Toggles TextContainer when bound (so a background living alongside PromptText hides with it), else PromptText itself. */
void SetPromptVisible(UWidget* TextContainer, UTextBlock* PromptText, bool bVisible)
{
const ESlateVisibility Visibility = bVisible ? ESlateVisibility::HitTestInvisible : ESlateVisibility::Collapsed;
if (TextContainer)
{
TextContainer->SetVisibility(Visibility);
}
else if (PromptText)
{
PromptText->SetVisibility(Visibility);
}
}
}
void UInteractionPromptWidget::NativeConstruct()
{
Super::NativeConstruct();
SetPromptVisible(TextContainer, PromptText, false);
if (InfoText)
{
InfoText->SetVisibility(ESlateVisibility::Collapsed);
}
if (WBP_InteractionsSlider)
{
WBP_InteractionsSlider->SetVisibility(ESlateVisibility::Collapsed);
// USegmentedBarWidget defaults to full (it's shared with the health/stamina bars), but
// the hold bar should start empty and only fill as OnInteractHoldProgressChanged reports.
WBP_InteractionsSlider->SetSegments(0.0f, 0.0f);
}
}
void UInteractionPromptWidget::InitializeInteraction(UInteractionComponent* InInteractionComponent)
{
UE_LOG(LogKingshearthLegacy, Log, TEXT("[Interaction] Prompt widget InitializeInteraction: component=%s, PromptText bound: %s, InfoText bound: %s, TextContainer bound: %s, Slider bound: %s"),
*GetNameSafe(InInteractionComponent),
PromptText ? TEXT("yes") : TEXT("NO"),
InfoText ? TEXT("yes") : TEXT("NO"),
TextContainer ? TEXT("yes") : TEXT("NO"),
WBP_InteractionsSlider ? TEXT("yes") : TEXT("NO"));
if (InteractionComponent)
{
InteractionComponent->OnFocusedInteractableChanged.RemoveDynamic(this, &UInteractionPromptWidget::HandleFocusedInteractableChanged);
InteractionComponent->OnContextMenuOpened.RemoveDynamic(this, &UInteractionPromptWidget::HandleContextMenuOpened);
InteractionComponent->OnContextMenuClosed.RemoveDynamic(this, &UInteractionPromptWidget::HandleContextMenuClosed);
InteractionComponent->OnInteractHoldProgressChanged.RemoveDynamic(this, &UInteractionPromptWidget::HandleHoldProgressChanged);
}
InteractionComponent = InInteractionComponent;
if (InteractionComponent)
{
InteractionComponent->OnFocusedInteractableChanged.AddDynamic(this, &UInteractionPromptWidget::HandleFocusedInteractableChanged);
InteractionComponent->OnContextMenuOpened.AddDynamic(this, &UInteractionPromptWidget::HandleContextMenuOpened);
InteractionComponent->OnContextMenuClosed.AddDynamic(this, &UInteractionPromptWidget::HandleContextMenuClosed);
InteractionComponent->OnInteractHoldProgressChanged.AddDynamic(this, &UInteractionPromptWidget::HandleHoldProgressChanged);
}
}
void UInteractionPromptWidget::HandleHoldProgressChanged(float Progress)
{
if (WBP_InteractionsSlider)
{
WBP_InteractionsSlider->SetSegments(Progress, 0.0f);
}
}
void UInteractionPromptWidget::HandleFocusedInteractableChanged(AActor* FocusedActor, const TArray<FInteractionOption>& Options)
{
UE_LOG(LogKingshearthLegacy, Log, TEXT("[Interaction] Prompt widget HandleFocusedInteractableChanged: actor='%s' options=%d"),
*GetNameSafe(FocusedActor), Options.Num());
LastFocusedActor = FocusedActor;
LastFocusedOptions = Options;
UpdatePromptVisibility();
}
void UInteractionPromptWidget::HandleContextMenuOpened(const TArray<FInteractionOption>& Options, int32 InitialHighlightedIndex)
{
UpdatePromptVisibility();
}
void UInteractionPromptWidget::HandleContextMenuClosed()
{
UpdatePromptVisibility();
}
void UInteractionPromptWidget::UpdatePromptVisibility()
{
if (!PromptText)
{
return;
}
const auto HideAll = [this]()
{
SetPromptVisible(TextContainer, PromptText, false);
if (InfoText)
{
InfoText->SetVisibility(ESlateVisibility::Collapsed);
}
if (WBP_InteractionsSlider)
{
WBP_InteractionsSlider->SetVisibility(ESlateVisibility::Collapsed);
}
};
// The Context Menu owns the screen while it's open - keep the ever-present prompt
// out of its way until OnContextMenuClosed brings us back here.
if (InteractionComponent && InteractionComponent->IsContextMenuOpen())
{
HideAll();
return;
}
AActor* FocusedActor = LastFocusedActor.Get();
if (!FocusedActor || LastFocusedOptions.Num() == 0)
{
HideAll();
return;
}
// Primary line: "{Verb} with {ObjectName}" (or PromptOverride verbatim, if the primary
// option set one). Hint line: "Press E" always, plus a hint for the hold behaviour only
// when there is one (2 options = secondary, 3+ = Context Menu).
const FInteractionOption& PrimaryOption = LastFocusedOptions[0];
FString Prompt;
if (!PrimaryOption.PromptOverride.IsEmpty())
{
Prompt = PrimaryOption.PromptOverride.ToString();
}
else
{
const FText ObjectName = IInteractable::Execute_GetInteractableDisplayName(FocusedActor);
Prompt = ObjectName.IsEmpty()
? PrimaryOption.DisplayName.ToString()
: FString::Printf(TEXT("%s with %s"), *PrimaryOption.DisplayName.ToString(), *ObjectName.ToString());
}
FString Hint = TEXT("Press E");
if (LastFocusedOptions.Num() == 2)
{
Hint += FString::Printf(TEXT("\nHold E for %s"), *LastFocusedOptions[1].DisplayName.ToString());
}
else if (LastFocusedOptions.Num() >= 3)
{
Hint += TEXT("\nHold E for Context Menu");
}
PromptText->SetText(FText::FromString(Prompt));
SetPromptVisible(TextContainer, PromptText, true);
if (InfoText)
{
InfoText->SetText(FText::FromString(Hint));
InfoText->SetVisibility(ESlateVisibility::HitTestInvisible);
}
if (WBP_InteractionsSlider)
{
WBP_InteractionsSlider->SetVisibility(ESlateVisibility::HitTestInvisible);
WBP_InteractionsSlider->SetSegments(0.0f, 0.0f);
}
}