449 lines
13 KiB
C++
449 lines
13 KiB
C++
#include "HyperTwistSimulation/HyperTwistClassicCubeHUDWidget.h"
|
|
|
|
#include "Blueprint/WidgetTree.h"
|
|
#include "Components/Border.h"
|
|
#include "Components/Button.h"
|
|
#include "Components/ButtonSlot.h"
|
|
#include "Components/HorizontalBox.h"
|
|
#include "Components/HorizontalBoxSlot.h"
|
|
#include "Components/ScrollBox.h"
|
|
#include "Components/SizeBox.h"
|
|
#include "Components/TextBlock.h"
|
|
#include "Components/UniformGridPanel.h"
|
|
#include "Components/UniformGridSlot.h"
|
|
#include "Components/VerticalBox.h"
|
|
#include "Components/VerticalBoxSlot.h"
|
|
#include "GameFramework/GameModeBase.h"
|
|
#include "HyperTwistSimulation/HyperTwistClassicCubeOperatorSurface.h"
|
|
|
|
namespace HyperTwistClassicCubeHUDWidgetInternal
|
|
{
|
|
const FLinearColor PanelColor(0.012f, 0.025f, 0.045f, 0.94f);
|
|
const FLinearColor AccentColor(0.15f, 0.90f, 0.95f, 1.0f);
|
|
const FLinearColor PrimaryTextColor(0.92f, 0.97f, 1.0f, 1.0f);
|
|
const FLinearColor MutedTextColor(0.58f, 0.70f, 0.78f, 1.0f);
|
|
const FLinearColor ButtonColor(0.04f, 0.16f, 0.23f, 1.0f);
|
|
|
|
UObject* ResolveOperatorSurfaceObject(const UUserWidget* Widget)
|
|
{
|
|
if (Widget == nullptr || Widget->GetWorld() == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
AGameModeBase* GameMode = Widget->GetWorld()->GetAuthGameMode();
|
|
if (GameMode == nullptr
|
|
|| !GameMode->GetClass()->ImplementsInterface(UHyperTwistClassicCubeOperatorSurface::StaticClass()))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
return GameMode;
|
|
}
|
|
}
|
|
|
|
void UHyperTwistClassicCubeHUDWidget::NativeOnInitialized()
|
|
{
|
|
Super::NativeOnInitialized();
|
|
EnsureWidgetTreeBuilt();
|
|
}
|
|
|
|
void UHyperTwistClassicCubeHUDWidget::NativeConstruct()
|
|
{
|
|
Super::NativeConstruct();
|
|
EnsureWidgetTreeBuilt();
|
|
}
|
|
|
|
TSharedRef<SWidget> UHyperTwistClassicCubeHUDWidget::RebuildWidget()
|
|
{
|
|
Initialize();
|
|
EnsureWidgetTreeBuilt();
|
|
return Super::RebuildWidget();
|
|
}
|
|
|
|
bool UHyperTwistClassicCubeHUDWidget::PrepareClassicCubeHudSurface()
|
|
{
|
|
EnsureWidgetTreeBuilt();
|
|
return IsClassicCubeHudSurfaceReady();
|
|
}
|
|
|
|
bool UHyperTwistClassicCubeHUDWidget::IsClassicCubeHudSurfaceReady() const
|
|
{
|
|
return WidgetTree != nullptr
|
|
&& WidgetTree->RootWidget != nullptr
|
|
&& StatusTextBlock != nullptr
|
|
&& TimerTextBlock != nullptr
|
|
&& NewScrambleButton != nullptr
|
|
&& HintButton != nullptr;
|
|
}
|
|
|
|
void UHyperTwistClassicCubeHUDWidget::SetHudLines(
|
|
const FString& InStatusLine,
|
|
const FString& InScrambleLine,
|
|
const FString& InTimerLine,
|
|
const FString& InInspectionLine,
|
|
const FString& InResultLine,
|
|
const FString& InControlsLine,
|
|
const FString& InHintLine,
|
|
const FString& InSolutionLine,
|
|
const FString& InReplayLine,
|
|
const FString& InLeaderboardLine,
|
|
const FString& InModeLine,
|
|
const FString& InTrainingLine
|
|
)
|
|
{
|
|
EnsureWidgetTreeBuilt();
|
|
|
|
if (StatusTextBlock != nullptr)
|
|
{
|
|
StatusTextBlock->SetText(FText::FromString(InStatusLine));
|
|
}
|
|
if (ScrambleTextBlock != nullptr)
|
|
{
|
|
ScrambleTextBlock->SetText(FText::FromString(InScrambleLine));
|
|
}
|
|
if (TimerTextBlock != nullptr)
|
|
{
|
|
TimerTextBlock->SetText(FText::FromString(InTimerLine));
|
|
}
|
|
if (InspectionTextBlock != nullptr)
|
|
{
|
|
InspectionTextBlock->SetText(FText::FromString(InInspectionLine));
|
|
}
|
|
if (ResultTextBlock != nullptr)
|
|
{
|
|
ResultTextBlock->SetText(FText::FromString(InResultLine));
|
|
}
|
|
if (ControlsTextBlock != nullptr)
|
|
{
|
|
ControlsTextBlock->SetText(FText::FromString(InControlsLine));
|
|
}
|
|
if (HintTextBlock != nullptr)
|
|
{
|
|
HintTextBlock->SetText(FText::FromString(InHintLine));
|
|
}
|
|
if (SolutionTextBlock != nullptr)
|
|
{
|
|
SolutionTextBlock->SetText(FText::FromString(InSolutionLine));
|
|
}
|
|
if (ReplayTextBlock != nullptr)
|
|
{
|
|
ReplayTextBlock->SetText(FText::FromString(InReplayLine));
|
|
}
|
|
if (LeaderboardTextBlock != nullptr)
|
|
{
|
|
LeaderboardTextBlock->SetText(FText::FromString(InLeaderboardLine));
|
|
}
|
|
if (ModeTextBlock != nullptr)
|
|
{
|
|
ModeTextBlock->SetText(FText::FromString(InModeLine));
|
|
}
|
|
if (TrainingTextBlock != nullptr)
|
|
{
|
|
TrainingTextBlock->SetText(FText::FromString(InTrainingLine));
|
|
TrainingTextBlock->SetVisibility(
|
|
InTrainingLine.IsEmpty()
|
|
? ESlateVisibility::Collapsed
|
|
: ESlateVisibility::HitTestInvisible);
|
|
}
|
|
}
|
|
|
|
bool UHyperTwistClassicCubeHUDWidget::IsNewScrambleButtonHovered() const
|
|
{
|
|
return bNewScrambleButtonHovered || (NewScrambleButton != nullptr && NewScrambleButton->IsHovered());
|
|
}
|
|
|
|
bool UHyperTwistClassicCubeHUDWidget::IsAnyActionButtonHovered() const
|
|
{
|
|
auto IsHovered = [](const UButton* Button)
|
|
{
|
|
return Button != nullptr && Button->IsHovered();
|
|
};
|
|
|
|
return IsHovered(NewScrambleButton)
|
|
|| IsHovered(HintButton)
|
|
|| IsHovered(SubmitSolveButton)
|
|
|| IsHovered(ModeToggleButton);
|
|
}
|
|
|
|
void UHyperTwistClassicCubeHUDWidget::SetModeToggleButtonLabel(const FString& InLabel)
|
|
{
|
|
EnsureWidgetTreeBuilt();
|
|
if (ModeToggleButtonLabel != nullptr)
|
|
{
|
|
ModeToggleButtonLabel->SetText(FText::FromString(InLabel));
|
|
}
|
|
}
|
|
|
|
void UHyperTwistClassicCubeHUDWidget::EnsureWidgetTreeBuilt()
|
|
{
|
|
if (WidgetTree == nullptr || WidgetTree->RootWidget != nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
UHorizontalBox* ViewportLayout = WidgetTree->ConstructWidget<UHorizontalBox>(
|
|
UHorizontalBox::StaticClass(),
|
|
TEXT("ClassicCubeHudRoot")
|
|
);
|
|
ViewportLayout->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
|
|
WidgetTree->RootWidget = ViewportLayout;
|
|
|
|
USizeBox* PanelSize = WidgetTree->ConstructWidget<USizeBox>(
|
|
USizeBox::StaticClass(),
|
|
TEXT("ClassicCubeHudPanelSize")
|
|
);
|
|
PanelSize->SetWidthOverride(520.0f);
|
|
PanelSize->SetMaxDesiredHeight(1000.0f);
|
|
if (UHorizontalBoxSlot* PanelSlot = ViewportLayout->AddChildToHorizontalBox(PanelSize))
|
|
{
|
|
PanelSlot->SetPadding(FMargin(18.0f));
|
|
PanelSlot->SetVerticalAlignment(VAlign_Top);
|
|
}
|
|
|
|
UBorder* PanelBorder = WidgetTree->ConstructWidget<UBorder>(
|
|
UBorder::StaticClass(),
|
|
TEXT("ClassicCubeHudPanel")
|
|
);
|
|
PanelBorder->SetPadding(FMargin(18.0f));
|
|
PanelBorder->SetBrushColor(HyperTwistClassicCubeHUDWidgetInternal::PanelColor);
|
|
PanelSize->AddChild(PanelBorder);
|
|
|
|
UScrollBox* PanelScroll = WidgetTree->ConstructWidget<UScrollBox>(
|
|
UScrollBox::StaticClass(),
|
|
TEXT("ClassicCubeHudScroll")
|
|
);
|
|
PanelBorder->AddChild(PanelScroll);
|
|
|
|
UVerticalBox* RootLayout = WidgetTree->ConstructWidget<UVerticalBox>(
|
|
UVerticalBox::StaticClass(),
|
|
TEXT("ClassicCubeHudContent")
|
|
);
|
|
PanelScroll->AddChild(RootLayout);
|
|
|
|
UTextBlock* TitleText = AddLine(
|
|
RootLayout,
|
|
TEXT("ClassicCubeHudTitle"),
|
|
20,
|
|
HyperTwistClassicCubeHUDWidgetInternal::AccentColor
|
|
);
|
|
if (TitleText != nullptr)
|
|
{
|
|
TitleText->SetText(FText::FromString(TEXT("CLASSIC CUBE // TRAINING")));
|
|
}
|
|
|
|
StatusTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudStatus"), 15, HyperTwistClassicCubeHUDWidgetInternal::PrimaryTextColor);
|
|
TimerTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudTimer"), 27, HyperTwistClassicCubeHUDWidgetInternal::PrimaryTextColor);
|
|
InspectionTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudInspection"), 14, HyperTwistClassicCubeHUDWidgetInternal::AccentColor);
|
|
ScrambleTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudScramble"), 13, HyperTwistClassicCubeHUDWidgetInternal::MutedTextColor);
|
|
ResultTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudResult"), 14, HyperTwistClassicCubeHUDWidgetInternal::PrimaryTextColor);
|
|
TrainingTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudTraining"), 13, HyperTwistClassicCubeHUDWidgetInternal::AccentColor);
|
|
HintTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudHint"), 13, HyperTwistClassicCubeHUDWidgetInternal::MutedTextColor);
|
|
SolutionTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudSolution"), 13, HyperTwistClassicCubeHUDWidgetInternal::MutedTextColor);
|
|
ModeTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudMode"), 13, HyperTwistClassicCubeHUDWidgetInternal::PrimaryTextColor);
|
|
ReplayTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudReplay"), 12, HyperTwistClassicCubeHUDWidgetInternal::MutedTextColor);
|
|
LeaderboardTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudLeaderboard"), 12, HyperTwistClassicCubeHUDWidgetInternal::MutedTextColor);
|
|
ControlsTextBlock = AddLine(RootLayout, TEXT("ClassicCubeHudControls"), 12, HyperTwistClassicCubeHUDWidgetInternal::MutedTextColor);
|
|
|
|
UUniformGridPanel* ActionGrid = WidgetTree->ConstructWidget<UUniformGridPanel>(
|
|
UUniformGridPanel::StaticClass(),
|
|
TEXT("ClassicCubeHudActionGrid")
|
|
);
|
|
ActionGrid->SetMinDesiredSlotWidth(220.0f);
|
|
if (UVerticalBoxSlot* GridSlot = RootLayout->AddChildToVerticalBox(ActionGrid))
|
|
{
|
|
GridSlot->SetPadding(FMargin(0.0f, 12.0f, 0.0f, 0.0f));
|
|
}
|
|
|
|
NewScrambleButton = CreateActionButton(
|
|
ActionGrid,
|
|
TEXT("ClassicCubeHudNewScrambleButton"),
|
|
TEXT("ClassicCubeHudNewScrambleButtonLabel"),
|
|
NewScrambleButtonLabel,
|
|
TEXT("New Scramble"),
|
|
0,
|
|
0
|
|
);
|
|
HintButton = CreateActionButton(
|
|
ActionGrid,
|
|
TEXT("ClassicCubeHudHintButton"),
|
|
TEXT("ClassicCubeHudHintButtonLabel"),
|
|
HintButtonLabel,
|
|
TEXT("Hint"),
|
|
0,
|
|
1
|
|
);
|
|
SubmitSolveButton = CreateActionButton(
|
|
ActionGrid,
|
|
TEXT("ClassicCubeHudSubmitSolveButton"),
|
|
TEXT("ClassicCubeHudSubmitSolveButtonLabel"),
|
|
SubmitSolveButtonLabel,
|
|
TEXT("Submit Solve"),
|
|
1,
|
|
0
|
|
);
|
|
ModeToggleButton = CreateActionButton(
|
|
ActionGrid,
|
|
TEXT("ClassicCubeHudModeToggleButton"),
|
|
TEXT("ClassicCubeHudModeToggleButtonLabel"),
|
|
ModeToggleButtonLabel,
|
|
TEXT("Follow Along"),
|
|
1,
|
|
1
|
|
);
|
|
if (NewScrambleButton != nullptr)
|
|
{
|
|
NewScrambleButton->OnClicked.AddDynamic(this, &UHyperTwistClassicCubeHUDWidget::HandleNewScrambleClicked);
|
|
}
|
|
if (HintButton != nullptr)
|
|
{
|
|
HintButton->OnClicked.AddDynamic(this, &UHyperTwistClassicCubeHUDWidget::HandleHintClicked);
|
|
}
|
|
if (SubmitSolveButton != nullptr)
|
|
{
|
|
SubmitSolveButton->OnClicked.AddDynamic(this, &UHyperTwistClassicCubeHUDWidget::HandleSubmitSolveClicked);
|
|
}
|
|
if (ModeToggleButton != nullptr)
|
|
{
|
|
ModeToggleButton->OnClicked.AddDynamic(this, &UHyperTwistClassicCubeHUDWidget::HandleModeToggleClicked);
|
|
}
|
|
SetHudLines(
|
|
TEXT("status: preparing classic cube lane"),
|
|
TEXT("scramble: pending"),
|
|
TEXT("timer: idle"),
|
|
TEXT("inspection: pending"),
|
|
TEXT("result: waiting for first attempt"),
|
|
TEXT("controls: LMB clockwise, RMB counter-clockwise, touch clockwise, MMB drag orbit, wheel zoom, R scramble, H hint, Enter submit, F mode, F3 coach, Esc settings"),
|
|
TEXT("hint: request solver guidance"),
|
|
TEXT("solution: unavailable"),
|
|
TEXT("replay: armed for local capture"),
|
|
TEXT("leaderboard: no local best yet"),
|
|
TEXT("mode: free play"),
|
|
FString()
|
|
);
|
|
}
|
|
|
|
UTextBlock* UHyperTwistClassicCubeHUDWidget::AddLine(
|
|
UVerticalBox* Parent,
|
|
const TCHAR* WidgetName,
|
|
const int32 FontSize,
|
|
const FLinearColor& Color
|
|
)
|
|
{
|
|
if (WidgetTree == nullptr || Parent == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
UTextBlock* TextBlock =
|
|
WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), WidgetName);
|
|
if (TextBlock == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
TextBlock->SetAutoWrapText(true);
|
|
TextBlock->SetColorAndOpacity(FSlateColor(Color));
|
|
FSlateFontInfo Font = TextBlock->GetFont();
|
|
Font.Size = FontSize;
|
|
TextBlock->SetFont(Font);
|
|
|
|
if (UVerticalBoxSlot* VerticalBoxSlot = Parent->AddChildToVerticalBox(TextBlock))
|
|
{
|
|
VerticalBoxSlot->SetPadding(FMargin(0.0f, 4.0f, 0.0f, 4.0f));
|
|
}
|
|
|
|
return TextBlock;
|
|
}
|
|
|
|
UButton* UHyperTwistClassicCubeHUDWidget::CreateActionButton(
|
|
UUniformGridPanel* Parent,
|
|
const TCHAR* ButtonName,
|
|
const TCHAR* LabelName,
|
|
TObjectPtr<UTextBlock>& OutLabel,
|
|
const FString& InitialLabel,
|
|
const int32 Row,
|
|
const int32 Column
|
|
)
|
|
{
|
|
if (WidgetTree == nullptr || Parent == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
UButton* ActionButton = WidgetTree->ConstructWidget<UButton>(
|
|
UButton::StaticClass(),
|
|
ButtonName
|
|
);
|
|
if (ActionButton == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
ActionButton->SetBackgroundColor(HyperTwistClassicCubeHUDWidgetInternal::ButtonColor);
|
|
|
|
OutLabel = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), LabelName);
|
|
if (OutLabel != nullptr)
|
|
{
|
|
OutLabel->SetText(FText::FromString(InitialLabel));
|
|
OutLabel->SetAutoWrapText(true);
|
|
OutLabel->SetJustification(ETextJustify::Center);
|
|
OutLabel->SetColorAndOpacity(FSlateColor(HyperTwistClassicCubeHUDWidgetInternal::PrimaryTextColor));
|
|
FSlateFontInfo Font = OutLabel->GetFont();
|
|
Font.Size = 13;
|
|
OutLabel->SetFont(Font);
|
|
ActionButton->AddChild(OutLabel);
|
|
}
|
|
|
|
Parent->AddChildToUniformGrid(ActionButton, Row, Column);
|
|
|
|
if (UButtonSlot* ButtonSlot = Cast<UButtonSlot>(OutLabel != nullptr ? OutLabel->Slot : nullptr))
|
|
{
|
|
ButtonSlot->SetPadding(FMargin(12.0f, 6.0f, 12.0f, 6.0f));
|
|
ButtonSlot->SetHorizontalAlignment(HAlign_Center);
|
|
ButtonSlot->SetVerticalAlignment(VAlign_Center);
|
|
}
|
|
|
|
return ActionButton;
|
|
}
|
|
|
|
void UHyperTwistClassicCubeHUDWidget::HandleNewScrambleClicked()
|
|
{
|
|
bNewScrambleButtonHovered = false;
|
|
if (UObject* OperatorSurface =
|
|
HyperTwistClassicCubeHUDWidgetInternal::ResolveOperatorSurfaceObject(this))
|
|
{
|
|
IHyperTwistClassicCubeOperatorSurface::Execute_RequestClassicCubeFreshAttempt(
|
|
OperatorSurface
|
|
);
|
|
}
|
|
}
|
|
|
|
void UHyperTwistClassicCubeHUDWidget::HandleHintClicked()
|
|
{
|
|
if (UObject* OperatorSurface =
|
|
HyperTwistClassicCubeHUDWidgetInternal::ResolveOperatorSurfaceObject(this))
|
|
{
|
|
IHyperTwistClassicCubeOperatorSurface::Execute_RequestClassicCubeHint(OperatorSurface);
|
|
}
|
|
}
|
|
|
|
void UHyperTwistClassicCubeHUDWidget::HandleSubmitSolveClicked()
|
|
{
|
|
if (UObject* OperatorSurface =
|
|
HyperTwistClassicCubeHUDWidgetInternal::ResolveOperatorSurfaceObject(this))
|
|
{
|
|
IHyperTwistClassicCubeOperatorSurface::Execute_RequestClassicCubeSubmitSolve(
|
|
OperatorSurface
|
|
);
|
|
}
|
|
}
|
|
|
|
void UHyperTwistClassicCubeHUDWidget::HandleModeToggleClicked()
|
|
{
|
|
if (UObject* OperatorSurface =
|
|
HyperTwistClassicCubeHUDWidgetInternal::ResolveOperatorSurfaceObject(this))
|
|
{
|
|
IHyperTwistClassicCubeOperatorSurface::Execute_RequestClassicCubeToggleFollowAlongMode(
|
|
OperatorSurface
|
|
);
|
|
}
|
|
}
|