Fix MVP menu startup and mouse flow

This commit is contained in:
2026-05-18 22:14:17 -07:00
parent 5566d846e1
commit 3aac902142
10 changed files with 321 additions and 35 deletions
@@ -25,8 +25,11 @@ EXPECTED = {
"EKeys::A", "EKeys::A",
"EKeys::D", "EKeys::D",
"Selected", "Selected",
"Use Left/Right or A/D to choose", "Click a card or use Left/Right",
"GetSelectedCharacterLabel", "GetSelectedCharacterLabel",
"NativeOnMouseButtonDown",
"IsPointInside(LocalMousePosition, MaleCardPosition, CardSize)",
"IsPointInside(LocalMousePosition, FemaleCardPosition, CardSize)",
], ],
"AgrarianGamePlayerController.h": [ "AgrarianGamePlayerController.h": [
"AgrarianSelectCharacter", "AgrarianSelectCharacter",
@@ -17,13 +17,16 @@ EXPECTED = {
"AgrarianMvpFrontendWidget.cpp": [ "AgrarianMvpFrontendWidget.cpp": [
"EAgrarianMvpFrontendScreen::CharacterSelection", "EAgrarianMvpFrontendScreen::CharacterSelection",
"DrawCharacterSelection", "DrawCharacterSelection",
"Choose your first settler", "Choose your first pioneer",
"Young adult male", "Young adult male",
"Young adult female", "Young adult female",
"MVP placeholder landing page", "Select the person who will step into Ground Zero.",
"NativeOnMouseButtonDown",
"Continue",
], ],
"AgrarianGamePlayerController.cpp": [ "AgrarianGamePlayerController.cpp": [
"SetActiveScreen(EAgrarianMvpFrontendScreen::CharacterSelection)", "SetActiveScreen(EAgrarianMvpFrontendScreen::CharacterSelection)",
"MvpFrontendStartupDelaySeconds",
], ],
"AGRARIAN_DEVELOPMENT_ROADMAP.md": [ "AGRARIAN_DEVELOPMENT_ROADMAP.md": [
"[x] After splash/startup screens, land on an MVP character selection landing page.", "[x] After splash/startup screens, land on an MVP character selection landing page.",
+2
View File
@@ -22,6 +22,8 @@ EXPECTED = {
"SetActiveScreen(EAgrarianMvpFrontendScreen::JoinServer)", "SetActiveScreen(EAgrarianMvpFrontendScreen::JoinServer)",
"Join MVP server", "Join MVP server",
"Continue to loading", "Continue to loading",
"Back",
"NativeOnMouseButtonDown",
], ],
"AgrarianGamePlayerController.h": [ "AgrarianGamePlayerController.h": [
"AgrarianShowMvpScreen", "AgrarianShowMvpScreen",
+2 -1
View File
@@ -19,7 +19,8 @@ EXPECTED = {
"SetActiveScreen(EAgrarianMvpFrontendScreen::Loading)", "SetActiveScreen(EAgrarianMvpFrontendScreen::Loading)",
"Preparing Ground Zero", "Preparing Ground Zero",
"Loading terrain, weather, survival state, and server session data.", "Loading terrain, weather, survival state, and server session data.",
"MVP loading placeholder", "Enter Ground Zero",
"CompleteFrontendFlow",
"BarSize.X * 0.62f", "BarSize.X * 0.62f",
], ],
"AgrarianGamePlayerController.cpp": [ "AgrarianGamePlayerController.cpp": [
@@ -29,6 +29,17 @@ REQUIRED = {
"Cherished individuals, Pacificao seed funding, and Lina Family Investment Funds", "Cherished individuals, Pacificao seed funding, and Lina Family Investment Funds",
"SlamSeconds", "SlamSeconds",
"DrawCreditIllustration", "DrawCreditIllustration",
"GetCreditsSequenceDurationSeconds",
"Agrarian Startup Credits",
],
ROOT / "Source" / "AgrarianGame" / "AgrarianGamePlayerController.h": [
"MvpFrontendStartupDelaySeconds = 24.25f",
"ShowMvpFrontend",
],
ROOT / "Source" / "AgrarianGame" / "AgrarianGamePlayerController.cpp": [
"GetWorldTimerManager().SetTimer",
"ShowMvpFrontend",
"FInputModeGameAndUI",
], ],
ROOT / "Config" / "DefaultGame.ini": [ ROOT / "Config" / "DefaultGame.ini": [
"ProjectVersion=0.1.N-investor.20260518", "ProjectVersion=0.1.N-investor.20260518",
@@ -31,6 +31,21 @@ float SmoothStep(float Value)
const float ClampedValue = FMath::Clamp(Value, 0.0f, 1.0f); const float ClampedValue = FMath::Clamp(Value, 0.0f, 1.0f);
return ClampedValue * ClampedValue * (3.0f - (2.0f * ClampedValue)); return ClampedValue * ClampedValue * (3.0f - (2.0f * ClampedValue));
} }
float GetCreditsSequenceDurationSeconds()
{
constexpr float IntroDelay = 0.55f;
constexpr float SlamSeconds = 0.28f;
constexpr float ExitSeconds = 0.48f;
constexpr float GapSeconds = 0.16f;
float Duration = IntroDelay;
for (const FAgrarianCreditCard& Card : CreditCards)
{
Duration += SlamSeconds + Card.HoldSeconds + ExitSeconds + GapSeconds;
}
return Duration;
}
} }
void UAgrarianDemoNoticeWidget::NativeConstruct() void UAgrarianDemoNoticeWidget::NativeConstruct()
@@ -51,6 +66,26 @@ int32 UAgrarianDemoNoticeWidget::NativePaint(
LayerId = Super::NativePaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled); LayerId = Super::NativePaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);
const FVector2D Size = AllottedGeometry.GetLocalSize(); const FVector2D Size = AllottedGeometry.GetLocalSize();
const UWorld* World = GetWorld();
const float Elapsed = World ? World->GetTimeSeconds() - CreditsStartTimeSeconds : 0.0f;
if (Elapsed <= GetCreditsSequenceDurationSeconds() + 1.0f)
{
FSlateDrawElement::MakeBox(
OutDrawElements,
++LayerId,
AllottedGeometry.ToPaintGeometry(FVector2f(Size), FSlateLayoutTransform(FVector2f::ZeroVector)),
FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")),
ESlateDrawEffect::None,
FLinearColor(0.006f, 0.008f, 0.007f, 0.98f));
const FSlateFontInfo SegmentFont = FCoreStyle::GetDefaultFontStyle("Bold", 22);
const FSlateFontInfo VersionFont = FCoreStyle::GetDefaultFontStyle("Regular", 15);
DrawCenteredText(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("Agrarian Startup Credits")), 38.0f, SegmentFont, FLinearColor(0.72f, 0.84f, 0.60f, 1.0f));
DrawCenteredText(OutDrawElements, LayerId, AllottedGeometry, VersionLabel, 72.0f, VersionFont, FLinearColor(0.58f, 0.64f, 0.54f, 1.0f));
DrawCinematicCredits(OutDrawElements, LayerId, AllottedGeometry);
return LayerId;
}
const float PanelWidth = FMath::Min(860.0f, FMath::Max(320.0f, Size.X - 96.0f)); const float PanelWidth = FMath::Min(860.0f, FMath::Max(320.0f, Size.X - 96.0f));
const float PanelHeight = 210.0f; const float PanelHeight = 210.0f;
const FVector2D PanelPosition((Size.X - PanelWidth) * 0.5f, 42.0f); const FVector2D PanelPosition((Size.X - PanelWidth) * 0.5f, 42.0f);
@@ -18,6 +18,7 @@
#include "GameFramework/CharacterMovementComponent.h" #include "GameFramework/CharacterMovementComponent.h"
#include "InputMappingContext.h" #include "InputMappingContext.h"
#include "Blueprint/UserWidget.h" #include "Blueprint/UserWidget.h"
#include "TimerManager.h"
#include "AgrarianGame.h" #include "AgrarianGame.h"
#include "Widgets/Input/SVirtualJoystick.h" #include "Widgets/Input/SVirtualJoystick.h"
@@ -81,16 +82,18 @@ void AAgrarianGamePlayerController::BeginPlay()
if (IsLocalPlayerController()) if (IsLocalPlayerController())
{ {
if (!MvpFrontendWidgetClass) if (MvpFrontendStartupDelaySeconds > 0.0f)
{ {
MvpFrontendWidgetClass = UAgrarianMvpFrontendWidget::StaticClass(); GetWorldTimerManager().SetTimer(
MvpFrontendStartupTimerHandle,
this,
&AAgrarianGamePlayerController::ShowMvpFrontend,
MvpFrontendStartupDelaySeconds,
false);
} }
else
MvpFrontendWidget = CreateWidget<UAgrarianMvpFrontendWidget>(this, MvpFrontendWidgetClass);
if (MvpFrontendWidget)
{ {
MvpFrontendWidget->SetActiveScreen(EAgrarianMvpFrontendScreen::CharacterSelection); ShowMvpFrontend();
MvpFrontendWidget->AddToPlayerScreen(10);
} }
} }
@@ -147,6 +150,30 @@ bool AAgrarianGamePlayerController::ShouldUseTouchControls() const
return SVirtualJoystick::ShouldDisplayTouchInterface() || bForceTouchControls; return SVirtualJoystick::ShouldDisplayTouchInterface() || bForceTouchControls;
} }
void AAgrarianGamePlayerController::ShowMvpFrontend()
{
if (!IsLocalPlayerController() || MvpFrontendWidget)
{
return;
}
if (!MvpFrontendWidgetClass)
{
MvpFrontendWidgetClass = UAgrarianMvpFrontendWidget::StaticClass();
}
MvpFrontendWidget = CreateWidget<UAgrarianMvpFrontendWidget>(this, MvpFrontendWidgetClass);
if (!MvpFrontendWidget)
{
return;
}
MvpFrontendWidget->SetActiveScreen(EAgrarianMvpFrontendScreen::CharacterSelection);
MvpFrontendWidget->AddToPlayerScreen(10);
SetInputMode(FInputModeGameAndUI().SetWidgetToFocus(MvpFrontendWidget->TakeWidget()).SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock));
bShowMouseCursor = true;
}
void AAgrarianGamePlayerController::AgrarianGrantItem(FName ItemId, int32 Quantity) void AAgrarianGamePlayerController::AgrarianGrantItem(FName ItemId, int32 Quantity)
{ {
if (ItemId == NAME_None || Quantity <= 0) if (ItemId == NAME_None || Quantity <= 0)
@@ -44,6 +44,11 @@ protected:
UPROPERTY() UPROPERTY()
TObjectPtr<UAgrarianMvpFrontendWidget> MvpFrontendWidget; TObjectPtr<UAgrarianMvpFrontendWidget> MvpFrontendWidget;
UPROPERTY(EditAnywhere, Category = "Agrarian|MVP UI", meta = (ClampMin = "0.0"))
float MvpFrontendStartupDelaySeconds = 24.25f;
FTimerHandle MvpFrontendStartupTimerHandle;
/** If true, the player will use UMG touch controls even if not playing on mobile platforms */ /** If true, the player will use UMG touch controls even if not playing on mobile platforms */
UPROPERTY(EditAnywhere, Config, Category = "Input|Touch Controls") UPROPERTY(EditAnywhere, Config, Category = "Input|Touch Controls")
bool bForceTouchControls = false; bool bForceTouchControls = false;
@@ -56,6 +61,7 @@ protected:
/** Returns true if the player should use UMG touch controls */ /** Returns true if the player should use UMG touch controls */
bool ShouldUseTouchControls() const; bool ShouldUseTouchControls() const;
void ShowMvpFrontend();
public: public:
UFUNCTION(Exec) UFUNCTION(Exec)
+214 -24
View File
@@ -2,6 +2,7 @@
#include "AgrarianMvpFrontendWidget.h" #include "AgrarianMvpFrontendWidget.h"
#include "GameFramework/PlayerController.h"
#include "InputCoreTypes.h" #include "InputCoreTypes.h"
#include "Rendering/DrawElements.h" #include "Rendering/DrawElements.h"
#include "Styling/CoreStyle.h" #include "Styling/CoreStyle.h"
@@ -32,7 +33,7 @@ FReply UAgrarianMvpFrontendWidget::NativeOnKeyDown(const FGeometry& InGeometry,
if (Key == EKeys::Enter || Key == EKeys::SpaceBar) if (Key == EKeys::Enter || Key == EKeys::SpaceBar)
{ {
SetActiveScreen(EAgrarianMvpFrontendScreen::JoinServer); ContinueFromActiveScreen();
return FReply::Handled(); return FReply::Handled();
} }
} }
@@ -41,13 +42,22 @@ FReply UAgrarianMvpFrontendWidget::NativeOnKeyDown(const FGeometry& InGeometry,
const FKey Key = InKeyEvent.GetKey(); const FKey Key = InKeyEvent.GetKey();
if (Key == EKeys::Enter || Key == EKeys::SpaceBar) if (Key == EKeys::Enter || Key == EKeys::SpaceBar)
{ {
SetActiveScreen(EAgrarianMvpFrontendScreen::Loading); ContinueFromActiveScreen();
return FReply::Handled(); return FReply::Handled();
} }
if (Key == EKeys::BackSpace || Key == EKeys::Escape) if (Key == EKeys::BackSpace || Key == EKeys::Escape)
{ {
SetActiveScreen(EAgrarianMvpFrontendScreen::CharacterSelection); ReturnFromActiveScreen();
return FReply::Handled();
}
}
else if (ActiveScreen == EAgrarianMvpFrontendScreen::Loading)
{
const FKey Key = InKeyEvent.GetKey();
if (Key == EKeys::Enter || Key == EKeys::SpaceBar)
{
ContinueFromActiveScreen();
return FReply::Handled(); return FReply::Handled();
} }
} }
@@ -55,6 +65,89 @@ FReply UAgrarianMvpFrontendWidget::NativeOnKeyDown(const FGeometry& InGeometry,
return Super::NativeOnKeyDown(InGeometry, InKeyEvent); return Super::NativeOnKeyDown(InGeometry, InKeyEvent);
} }
FReply UAgrarianMvpFrontendWidget::NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
{
if (InMouseEvent.GetEffectingButton() != EKeys::LeftMouseButton)
{
return Super::NativeOnMouseButtonDown(InGeometry, InMouseEvent);
}
const FVector2D LocalMousePosition = InGeometry.AbsoluteToLocal(InMouseEvent.GetScreenSpacePosition());
float Scale = 1.0f;
FVector2D PanelPosition = FVector2D::ZeroVector;
FVector2D PanelSize = FVector2D::ZeroVector;
if (!GetPanelLayout(InGeometry.GetLocalSize(), Scale, PanelPosition, PanelSize))
{
return Super::NativeOnMouseButtonDown(InGeometry, InMouseEvent);
}
const float ContentX = PanelPosition.X + (44.0f * Scale);
const float ContentWidth = PanelSize.X - (88.0f * Scale);
if (ActiveScreen == EAgrarianMvpFrontendScreen::CharacterSelection)
{
const FVector2D CardSize((ContentWidth - (24.0f * Scale)) * 0.5f, 206.0f * Scale);
const FVector2D MaleCardPosition(ContentX, PanelPosition.Y + (142.0f * Scale));
const FVector2D FemaleCardPosition(ContentX + CardSize.X + (24.0f * Scale), MaleCardPosition.Y);
const FVector2D ButtonPosition(ContentX, PanelPosition.Y + PanelSize.Y - (88.0f * Scale));
const FVector2D ButtonSize(FMath::Min(ContentWidth, 280.0f * Scale), 52.0f * Scale);
if (IsPointInside(LocalMousePosition, MaleCardPosition, CardSize))
{
SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype::YoungAdultMale);
return FReply::Handled();
}
if (IsPointInside(LocalMousePosition, FemaleCardPosition, CardSize))
{
SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype::YoungAdultFemale);
return FReply::Handled();
}
if (IsPointInside(LocalMousePosition, ButtonPosition, ButtonSize))
{
ContinueFromActiveScreen();
return FReply::Handled();
}
}
else if (ActiveScreen == EAgrarianMvpFrontendScreen::JoinServer)
{
const float JoinContentX = PanelPosition.X + (48.0f * Scale);
const float JoinContentWidth = PanelSize.X - (96.0f * Scale);
const FVector2D ButtonPosition(JoinContentX, PanelPosition.Y + (300.0f * Scale));
const FVector2D ButtonSize(FMath::Min(JoinContentWidth, 330.0f * Scale), 58.0f * Scale);
const FVector2D BackButtonPosition(ButtonPosition.X + ButtonSize.X + (16.0f * Scale), ButtonPosition.Y);
const FVector2D BackButtonSize(FMath::Min(JoinContentWidth - ButtonSize.X - (16.0f * Scale), 180.0f * Scale), ButtonSize.Y);
if (IsPointInside(LocalMousePosition, ButtonPosition, ButtonSize))
{
ContinueFromActiveScreen();
return FReply::Handled();
}
if (BackButtonSize.X > 96.0f && IsPointInside(LocalMousePosition, BackButtonPosition, BackButtonSize))
{
ReturnFromActiveScreen();
return FReply::Handled();
}
}
else if (ActiveScreen == EAgrarianMvpFrontendScreen::Loading)
{
const float LoadingContentX = PanelPosition.X + (48.0f * Scale);
const float LoadingContentWidth = PanelSize.X - (96.0f * Scale);
const FVector2D ButtonPosition(LoadingContentX, PanelPosition.Y + (318.0f * Scale));
const FVector2D ButtonSize(FMath::Min(LoadingContentWidth, 310.0f * Scale), 56.0f * Scale);
if (IsPointInside(LocalMousePosition, ButtonPosition, ButtonSize))
{
ContinueFromActiveScreen();
return FReply::Handled();
}
}
return FReply::Handled();
}
void UAgrarianMvpFrontendWidget::SetActiveScreen(EAgrarianMvpFrontendScreen NewScreen) void UAgrarianMvpFrontendWidget::SetActiveScreen(EAgrarianMvpFrontendScreen NewScreen)
{ {
ActiveScreen = NewScreen; ActiveScreen = NewScreen;
@@ -91,13 +184,10 @@ int32 UAgrarianMvpFrontendWidget::NativePaint(
LayerId = Super::NativePaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled); LayerId = Super::NativePaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);
const FVector2D Size = AllottedGeometry.GetLocalSize(); const FVector2D Size = AllottedGeometry.GetLocalSize();
const float Scale = FMath::Clamp(UiScale, 0.75f, 1.5f); float Scale = 1.0f;
constexpr float MinimumPanelMargin = 24.0f; FVector2D PanelPosition = FVector2D::ZeroVector;
constexpr float PreferredPanelWidth = 780.0f; FVector2D PanelSize = FVector2D::ZeroVector;
constexpr float PreferredPanelHeight = 430.0f; GetPanelLayout(Size, Scale, PanelPosition, PanelSize);
const FVector2D AvailablePanelSize(
FMath::Max(320.0f, Size.X - (MinimumPanelMargin * 2.0f)),
FMath::Max(240.0f, Size.Y - (MinimumPanelMargin * 2.0f)));
const FLinearColor BackdropColor = bUseHighContrast ? FLinearColor(0.0f, 0.0f, 0.0f, 0.96f) : FLinearColor(0.015f, 0.018f, 0.014f, 0.92f); const FLinearColor BackdropColor = bUseHighContrast ? FLinearColor(0.0f, 0.0f, 0.0f, 0.96f) : FLinearColor(0.015f, 0.018f, 0.014f, 0.92f);
const FLinearColor PanelColor = bUseHighContrast ? FLinearColor(0.0f, 0.0f, 0.0f, 0.98f) : FLinearColor(0.035f, 0.045f, 0.034f, 0.96f); const FLinearColor PanelColor = bUseHighContrast ? FLinearColor(0.0f, 0.0f, 0.0f, 0.98f) : FLinearColor(0.035f, 0.045f, 0.034f, 0.96f);
const FLinearColor AccentColor = bUseHighContrast ? FLinearColor(0.95f, 0.95f, 0.30f, 1.0f) : FLinearColor(0.45f, 0.72f, 0.40f, 1.0f); const FLinearColor AccentColor = bUseHighContrast ? FLinearColor(0.95f, 0.95f, 0.30f, 1.0f) : FLinearColor(0.45f, 0.72f, 0.40f, 1.0f);
@@ -110,13 +200,6 @@ int32 UAgrarianMvpFrontendWidget::NativePaint(
ESlateDrawEffect::None, ESlateDrawEffect::None,
BackdropColor); BackdropColor);
const FVector2D PanelSize(
FMath::Min(AvailablePanelSize.X, PreferredPanelWidth * Scale),
FMath::Min(AvailablePanelSize.Y, PreferredPanelHeight * Scale));
const FVector2D PanelPosition(
(Size.X - PanelSize.X) * 0.5f,
(Size.Y - PanelSize.Y) * 0.5f);
FSlateDrawElement::MakeBox( FSlateDrawElement::MakeBox(
OutDrawElements, OutDrawElements,
++LayerId, ++LayerId,
@@ -153,6 +236,73 @@ int32 UAgrarianMvpFrontendWidget::NativePaint(
return LayerId; return LayerId;
} }
bool UAgrarianMvpFrontendWidget::GetPanelLayout(const FVector2D& WidgetSize, float& OutScale, FVector2D& OutPanelPosition, FVector2D& OutPanelSize) const
{
OutScale = FMath::Clamp(UiScale, 0.75f, 1.5f);
constexpr float MinimumPanelMargin = 24.0f;
constexpr float PreferredPanelWidth = 780.0f;
constexpr float PreferredPanelHeight = 430.0f;
const FVector2D AvailablePanelSize(
FMath::Max(320.0f, WidgetSize.X - (MinimumPanelMargin * 2.0f)),
FMath::Max(240.0f, WidgetSize.Y - (MinimumPanelMargin * 2.0f)));
OutPanelSize = FVector2D(
FMath::Min(AvailablePanelSize.X, PreferredPanelWidth * OutScale),
FMath::Min(AvailablePanelSize.Y, PreferredPanelHeight * OutScale));
OutPanelPosition = FVector2D(
(WidgetSize.X - OutPanelSize.X) * 0.5f,
(WidgetSize.Y - OutPanelSize.Y) * 0.5f);
return WidgetSize.X > 0.0f && WidgetSize.Y > 0.0f;
}
bool UAgrarianMvpFrontendWidget::IsPointInside(const FVector2D& Point, const FVector2D& Position, const FVector2D& Size) const
{
return Point.X >= Position.X
&& Point.Y >= Position.Y
&& Point.X <= Position.X + Size.X
&& Point.Y <= Position.Y + Size.Y;
}
void UAgrarianMvpFrontendWidget::ContinueFromActiveScreen()
{
if (ActiveScreen == EAgrarianMvpFrontendScreen::CharacterSelection)
{
SetActiveScreen(EAgrarianMvpFrontendScreen::JoinServer);
return;
}
if (ActiveScreen == EAgrarianMvpFrontendScreen::JoinServer)
{
SetActiveScreen(EAgrarianMvpFrontendScreen::Loading);
return;
}
if (ActiveScreen == EAgrarianMvpFrontendScreen::Loading)
{
CompleteFrontendFlow();
}
}
void UAgrarianMvpFrontendWidget::ReturnFromActiveScreen()
{
if (ActiveScreen == EAgrarianMvpFrontendScreen::JoinServer)
{
SetActiveScreen(EAgrarianMvpFrontendScreen::CharacterSelection);
}
}
void UAgrarianMvpFrontendWidget::CompleteFrontendFlow()
{
if (APlayerController* PlayerController = GetOwningPlayer())
{
PlayerController->SetInputMode(FInputModeGameOnly());
PlayerController->bShowMouseCursor = false;
}
RemoveFromParent();
}
void UAgrarianMvpFrontendWidget::DrawMainMenu( void UAgrarianMvpFrontendWidget::DrawMainMenu(
FSlateWindowElementList& OutDrawElements, FSlateWindowElementList& OutDrawElements,
int32& LayerId, int32& LayerId,
@@ -200,8 +350,8 @@ void UAgrarianMvpFrontendWidget::DrawCharacterSelection(
const FSlateFontInfo CardTitleFont = FCoreStyle::GetDefaultFontStyle("Bold", FMath::RoundToInt(22.0f * Scale)); const FSlateFontInfo CardTitleFont = FCoreStyle::GetDefaultFontStyle("Bold", FMath::RoundToInt(22.0f * Scale));
const FSlateFontInfo LabelFont = FCoreStyle::GetDefaultFontStyle("Regular", FMath::RoundToInt(15.0f * Scale)); const FSlateFontInfo LabelFont = FCoreStyle::GetDefaultFontStyle("Regular", FMath::RoundToInt(15.0f * Scale));
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("Choose your first settler")), FVector2D(ContentX, PanelPosition.Y + (34.0f * Scale)), ContentWidth, TitleFont, FLinearColor(0.92f, 0.98f, 0.84f, 1.0f)); DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("Choose your first pioneer")), FVector2D(ContentX, PanelPosition.Y + (34.0f * Scale)), ContentWidth, TitleFont, FLinearColor(0.92f, 0.98f, 0.84f, 1.0f));
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("MVP placeholder landing page before entering Ground Zero.")), FVector2D(ContentX + (2.0f * Scale), PanelPosition.Y + (82.0f * Scale)), ContentWidth, BodyFont, FLinearColor(0.72f, 0.80f, 0.68f, 1.0f)); DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("Select the person who will step into Ground Zero.")), FVector2D(ContentX + (2.0f * Scale), PanelPosition.Y + (82.0f * Scale)), ContentWidth, BodyFont, FLinearColor(0.72f, 0.80f, 0.68f, 1.0f));
const FVector2D CardSize((ContentWidth - (24.0f * Scale)) * 0.5f, 206.0f * Scale); const FVector2D CardSize((ContentWidth - (24.0f * Scale)) * 0.5f, 206.0f * Scale);
const FVector2D MaleCardPosition(ContentX, PanelPosition.Y + (142.0f * Scale)); const FVector2D MaleCardPosition(ContentX, PanelPosition.Y + (142.0f * Scale));
@@ -254,7 +404,18 @@ void UAgrarianMvpFrontendWidget::DrawCharacterSelection(
DrawCharacterCard(MaleCardPosition, FText::FromString(TEXT("Young adult male")), FText::FromString(TEXT("Average proportions, survival baseline, placeholder visual.")), FLinearColor(0.36f, 0.58f, 0.78f, 1.0f), SelectedCharacterArchetype == EAgrarianMvpCharacterArchetype::YoungAdultMale); DrawCharacterCard(MaleCardPosition, FText::FromString(TEXT("Young adult male")), FText::FromString(TEXT("Average proportions, survival baseline, placeholder visual.")), FLinearColor(0.36f, 0.58f, 0.78f, 1.0f), SelectedCharacterArchetype == EAgrarianMvpCharacterArchetype::YoungAdultMale);
DrawCharacterCard(FemaleCardPosition, FText::FromString(TEXT("Young adult female")), FText::FromString(TEXT("Average proportions, survival baseline, placeholder visual.")), FLinearColor(0.56f, 0.68f, 0.46f, 1.0f), SelectedCharacterArchetype == EAgrarianMvpCharacterArchetype::YoungAdultFemale); DrawCharacterCard(FemaleCardPosition, FText::FromString(TEXT("Young adult female")), FText::FromString(TEXT("Average proportions, survival baseline, placeholder visual.")), FLinearColor(0.56f, 0.68f, 0.46f, 1.0f), SelectedCharacterArchetype == EAgrarianMvpCharacterArchetype::YoungAdultFemale);
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::Format(FText::FromString(TEXT("Selected: {0}. Use Left/Right or A/D to choose; both choices enter the same MVP survival baseline.")), GetSelectedCharacterLabel()), FVector2D(ContentX, PanelPosition.Y + PanelSize.Y - (46.0f * Scale)), ContentWidth, LabelFont, FLinearColor(0.62f, 0.68f, 0.58f, 1.0f)); const FVector2D ButtonPosition(ContentX, PanelPosition.Y + PanelSize.Y - (88.0f * Scale));
const FVector2D ButtonSize(FMath::Min(ContentWidth, 280.0f * Scale), 52.0f * Scale);
FSlateDrawElement::MakeBox(
OutDrawElements,
++LayerId,
AllottedGeometry.ToPaintGeometry(FVector2f(ButtonSize), FSlateLayoutTransform(FVector2f(ButtonPosition))),
FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")),
ESlateDrawEffect::None,
FLinearColor(0.35f, 0.58f, 0.30f, 0.95f));
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("Continue")), ButtonPosition + FVector2D(22.0f * Scale, 10.0f * Scale), ButtonSize.X - (44.0f * Scale), CardTitleFont, FLinearColor(0.96f, 1.0f, 0.90f, 1.0f));
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::Format(FText::FromString(TEXT("Selected {0}: {1}. Click a card or use Left/Right, then continue.")), GetSelectedRoleLabel(), GetSelectedCharacterLabel()), FVector2D(ContentX, PanelPosition.Y + PanelSize.Y - (26.0f * Scale)), ContentWidth, LabelFont, FLinearColor(0.62f, 0.68f, 0.58f, 1.0f));
} }
void UAgrarianMvpFrontendWidget::DrawJoinServer( void UAgrarianMvpFrontendWidget::DrawJoinServer(
@@ -273,7 +434,7 @@ void UAgrarianMvpFrontendWidget::DrawJoinServer(
const FSlateFontInfo HintFont = FCoreStyle::GetDefaultFontStyle("Regular", FMath::RoundToInt(15.0f * Scale)); const FSlateFontInfo HintFont = FCoreStyle::GetDefaultFontStyle("Regular", FMath::RoundToInt(15.0f * Scale));
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("Join MVP server")), FVector2D(ContentX, PanelPosition.Y + (44.0f * Scale)), ContentWidth, TitleFont, FLinearColor(0.92f, 0.98f, 0.84f, 1.0f)); DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("Join MVP server")), FVector2D(ContentX, PanelPosition.Y + (44.0f * Scale)), ContentWidth, TitleFont, FLinearColor(0.92f, 0.98f, 0.84f, 1.0f));
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::Format(FText::FromString(TEXT("Character: {0}")), GetSelectedCharacterLabel()), FVector2D(ContentX + (2.0f * Scale), PanelPosition.Y + (94.0f * Scale)), ContentWidth, BodyFont, FLinearColor(0.72f, 0.80f, 0.68f, 1.0f)); DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::Format(FText::FromString(TEXT("{0}: {1}")), GetSelectedRoleLabel(), GetSelectedCharacterLabel()), FVector2D(ContentX + (2.0f * Scale), PanelPosition.Y + (94.0f * Scale)), ContentWidth, BodyFont, FLinearColor(0.72f, 0.80f, 0.68f, 1.0f));
const FVector2D AddressPanelPosition(ContentX, PanelPosition.Y + (164.0f * Scale)); const FVector2D AddressPanelPosition(ContentX, PanelPosition.Y + (164.0f * Scale));
const FVector2D AddressPanelSize(ContentWidth, 96.0f * Scale); const FVector2D AddressPanelSize(ContentWidth, 96.0f * Scale);
@@ -299,7 +460,20 @@ void UAgrarianMvpFrontendWidget::DrawJoinServer(
FLinearColor(0.35f, 0.58f, 0.30f, 0.95f)); FLinearColor(0.35f, 0.58f, 0.30f, 0.95f));
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("Continue to loading")), ButtonPosition + FVector2D(22.0f * Scale, 12.0f * Scale), ButtonSize.X - (44.0f * Scale), AddressFont, FLinearColor(0.96f, 1.0f, 0.90f, 1.0f)); DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("Continue to loading")), ButtonPosition + FVector2D(22.0f * Scale, 12.0f * Scale), ButtonSize.X - (44.0f * Scale), AddressFont, FLinearColor(0.96f, 1.0f, 0.90f, 1.0f));
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("Press Backspace or Escape to return to character selection.")), FVector2D(ContentX, PanelPosition.Y + PanelSize.Y - (46.0f * Scale)), ContentWidth, HintFont, FLinearColor(0.62f, 0.68f, 0.58f, 1.0f)); const FVector2D BackButtonPosition(ButtonPosition.X + ButtonSize.X + (16.0f * Scale), ButtonPosition.Y);
const FVector2D BackButtonSize(FMath::Min(ContentWidth - ButtonSize.X - (16.0f * Scale), 180.0f * Scale), ButtonSize.Y);
if (BackButtonSize.X > 96.0f)
{
FSlateDrawElement::MakeBox(
OutDrawElements,
++LayerId,
AllottedGeometry.ToPaintGeometry(FVector2f(BackButtonSize), FSlateLayoutTransform(FVector2f(BackButtonPosition))),
FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")),
ESlateDrawEffect::None,
FLinearColor(0.12f, 0.15f, 0.11f, 0.95f));
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("Back")), BackButtonPosition + FVector2D(22.0f * Scale, 12.0f * Scale), BackButtonSize.X - (44.0f * Scale), AddressFont, FLinearColor(0.82f, 0.88f, 0.76f, 1.0f));
}
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("Click continue, press Enter, or press Backspace/Escape to revise your pioneer.")), FVector2D(ContentX, PanelPosition.Y + PanelSize.Y - (46.0f * Scale)), ContentWidth, HintFont, FLinearColor(0.62f, 0.68f, 0.58f, 1.0f));
} }
void UAgrarianMvpFrontendWidget::DrawLoading( void UAgrarianMvpFrontendWidget::DrawLoading(
@@ -337,8 +511,19 @@ void UAgrarianMvpFrontendWidget::DrawLoading(
ESlateDrawEffect::None, ESlateDrawEffect::None,
FLinearColor(0.45f, 0.72f, 0.40f, 1.0f)); FLinearColor(0.45f, 0.72f, 0.40f, 1.0f));
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::Format(FText::FromString(TEXT("Character: {0} | Server: {1}")), GetSelectedCharacterLabel(), JoinServerAddress), FVector2D(ContentX, PanelPosition.Y + (270.0f * Scale)), ContentWidth, BodyFont, FLinearColor(0.78f, 0.84f, 0.72f, 1.0f)); DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::Format(FText::FromString(TEXT("{0}: {1} | Server: {2}")), GetSelectedRoleLabel(), GetSelectedCharacterLabel(), JoinServerAddress), FVector2D(ContentX, PanelPosition.Y + (270.0f * Scale)), ContentWidth, BodyFont, FLinearColor(0.78f, 0.84f, 0.72f, 1.0f));
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("MVP loading placeholder; final connection and map handoff will attach here.")), FVector2D(ContentX, PanelPosition.Y + PanelSize.Y - (46.0f * Scale)), ContentWidth, HintFont, FLinearColor(0.62f, 0.68f, 0.58f, 1.0f));
const FVector2D ButtonPosition(ContentX, PanelPosition.Y + (318.0f * Scale));
const FVector2D ButtonSize(FMath::Min(ContentWidth, 310.0f * Scale), 56.0f * Scale);
FSlateDrawElement::MakeBox(
OutDrawElements,
++LayerId,
AllottedGeometry.ToPaintGeometry(FVector2f(ButtonSize), FSlateLayoutTransform(FVector2f(ButtonPosition))),
FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")),
ESlateDrawEffect::None,
FLinearColor(0.35f, 0.58f, 0.30f, 0.95f));
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("Enter Ground Zero")), ButtonPosition + FVector2D(22.0f * Scale, 11.0f * Scale), ButtonSize.X - (44.0f * Scale), BodyFont, FLinearColor(0.96f, 1.0f, 0.90f, 1.0f));
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("Click or press Enter to close the MVP menu and begin testing.")), FVector2D(ContentX, PanelPosition.Y + PanelSize.Y - (46.0f * Scale)), ContentWidth, HintFont, FLinearColor(0.62f, 0.68f, 0.58f, 1.0f));
} }
FText UAgrarianMvpFrontendWidget::GetSelectedCharacterLabel() const FText UAgrarianMvpFrontendWidget::GetSelectedCharacterLabel() const
@@ -348,6 +533,11 @@ FText UAgrarianMvpFrontendWidget::GetSelectedCharacterLabel() const
: FText::FromString(TEXT("young adult male")); : FText::FromString(TEXT("young adult male"));
} }
FText UAgrarianMvpFrontendWidget::GetSelectedRoleLabel() const
{
return FText::FromString(TEXT("Pioneer"));
}
void UAgrarianMvpFrontendWidget::DrawTextAt( void UAgrarianMvpFrontendWidget::DrawTextAt(
FSlateWindowElementList& OutDrawElements, FSlateWindowElementList& OutDrawElements,
int32& LayerId, int32& LayerId,
@@ -68,6 +68,7 @@ protected:
virtual void NativeConstruct() override; virtual void NativeConstruct() override;
virtual FReply NativeOnKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent) override; virtual FReply NativeOnKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent) override;
virtual FReply NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;
virtual int32 NativePaint( virtual int32 NativePaint(
const FPaintArgs& Args, const FPaintArgs& Args,
@@ -79,6 +80,12 @@ protected:
bool bParentEnabled) const override; bool bParentEnabled) const override;
private: private:
bool GetPanelLayout(const FVector2D& WidgetSize, float& OutScale, FVector2D& OutPanelPosition, FVector2D& OutPanelSize) const;
bool IsPointInside(const FVector2D& Point, const FVector2D& Position, const FVector2D& Size) const;
void ContinueFromActiveScreen();
void ReturnFromActiveScreen();
void CompleteFrontendFlow();
void DrawMainMenu( void DrawMainMenu(
FSlateWindowElementList& OutDrawElements, FSlateWindowElementList& OutDrawElements,
int32& LayerId, int32& LayerId,
@@ -112,6 +119,7 @@ private:
float Scale) const; float Scale) const;
FText GetSelectedCharacterLabel() const; FText GetSelectedCharacterLabel() const;
FText GetSelectedRoleLabel() const;
void DrawTextAt( void DrawTextAt(
FSlateWindowElementList& OutDrawElements, FSlateWindowElementList& OutDrawElements,