From e7bd7833098429fac9772d4b3a6e709d6dca6330 Mon Sep 17 00:00:00 2001 From: Nathan Slaven Date: Thu, 21 May 2026 22:55:52 +0000 Subject: [PATCH] Sequence startup story before character selection --- Scripts/verify_startup_credits_sequence.py | 24 ++- .../AgrarianGame/AgrarianDemoNoticeActor.cpp | 6 +- Source/AgrarianGame/AgrarianDemoNoticeActor.h | 3 + .../AgrarianGame/AgrarianDemoNoticeWidget.cpp | 185 +++++++++++++++++- .../AgrarianGame/AgrarianDemoNoticeWidget.h | 29 ++- .../AgrarianGamePlayerController.cpp | 108 ++++++++-- .../AgrarianGamePlayerController.h | 25 ++- 7 files changed, 354 insertions(+), 26 deletions(-) diff --git a/Scripts/verify_startup_credits_sequence.py b/Scripts/verify_startup_credits_sequence.py index e59a3b0..87285b4 100644 --- a/Scripts/verify_startup_credits_sequence.py +++ b/Scripts/verify_startup_credits_sequence.py @@ -6,16 +6,27 @@ ROOT = Path(__file__).resolve().parents[1] REQUIRED = { ROOT / "Source" / "AgrarianGame" / "AgrarianDemoNoticeActor.h": [ "float NoticeDurationSeconds = 24.0f;", + "bool bAutoShowNotice = false;", "Investor Demo v0.1.N - Build 2026.05.18", ], ROOT / "Source" / "AgrarianGame" / "AgrarianDemoNoticeWidget.h": [ "virtual void NativeConstruct() override;", + "NativeOnKeyDown", + "NativeOnMouseButtonDown", + "EAgrarianStartupPresentationSegment", + "DrawSplash", + "DrawStory", "DrawCinematicCredits", "DrawCreditIllustration", - "CreditsStartTimeSeconds", + "SegmentStartTimeSeconds", "Investor Demo v0.1.N - Build 2026.05.18", ], ROOT / "Source" / "AgrarianGame" / "AgrarianDemoNoticeWidget.cpp": [ + "The lights did not go out at once.", + "Agrarian begins at Ground Zero.", + "Press any key to skip to credits", + "RequestSkipSegment", + "AgrarianSkipStartupPresentation", "Nathan Slaven", "Lead Developer", "Hunter Slaven", @@ -33,10 +44,19 @@ REQUIRED = { "Agrarian Startup Credits", ], ROOT / "Source" / "AgrarianGame" / "AgrarianGamePlayerController.h": [ - "MvpFrontendStartupDelaySeconds = 24.25f", + "StartupSplashSeconds = 4.0f", + "StartupStorySeconds = 60.0f", + "StartupCreditsSeconds = 20.75f", + "StartupPresentationWidget", + "AgrarianSkipStartupPresentation", "ShowMvpFrontend", ], ROOT / "Source" / "AgrarianGame" / "AgrarianGamePlayerController.cpp": [ + "StartStartupPresentation", + "ShowStartupPresentationSegment(EAgrarianStartupPresentationSegment::Splash", + "ShowStartupPresentationSegment(EAgrarianStartupPresentationSegment::Story", + "ShowStartupPresentationSegment(EAgrarianStartupPresentationSegment::Credits", + "FinishStartupPresentation", "GetWorldTimerManager().SetTimer", "ShowMvpFrontend", "FInputModeUIOnly", diff --git a/Source/AgrarianGame/AgrarianDemoNoticeActor.cpp b/Source/AgrarianGame/AgrarianDemoNoticeActor.cpp index 0e91217..6eac19f 100644 --- a/Source/AgrarianGame/AgrarianDemoNoticeActor.cpp +++ b/Source/AgrarianGame/AgrarianDemoNoticeActor.cpp @@ -17,6 +17,11 @@ void AAgrarianDemoNoticeActor::BeginPlay() { Super::BeginPlay(); + if (!bAutoShowNotice) + { + return; + } + APlayerController* PlayerController = GetWorld() ? GetWorld()->GetFirstPlayerController() : nullptr; if (!PlayerController || !NoticeWidgetClass) { @@ -44,4 +49,3 @@ void AAgrarianDemoNoticeActor::RemoveNotice() ActiveNoticeWidget = nullptr; } } - diff --git a/Source/AgrarianGame/AgrarianDemoNoticeActor.h b/Source/AgrarianGame/AgrarianDemoNoticeActor.h index fd7e82d..83be598 100644 --- a/Source/AgrarianGame/AgrarianDemoNoticeActor.h +++ b/Source/AgrarianGame/AgrarianDemoNoticeActor.h @@ -22,6 +22,9 @@ public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Demo", meta = (ClampMin = "1.0")) float NoticeDurationSeconds = 24.0f; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Demo") + bool bAutoShowNotice = false; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Demo") FText VersionLabel = FText::FromString(TEXT("Investor Demo v0.1.N - Build 2026.05.18")); diff --git a/Source/AgrarianGame/AgrarianDemoNoticeWidget.cpp b/Source/AgrarianGame/AgrarianDemoNoticeWidget.cpp index f99eea9..3f80d43 100644 --- a/Source/AgrarianGame/AgrarianDemoNoticeWidget.cpp +++ b/Source/AgrarianGame/AgrarianDemoNoticeWidget.cpp @@ -2,7 +2,10 @@ #include "AgrarianDemoNoticeWidget.h" +#include "AgrarianGamePlayerController.h" #include "Engine/World.h" +#include "Input/Reply.h" +#include "InputCoreTypes.h" #include "Rendering/DrawElements.h" #include "Styling/CoreStyle.h" @@ -46,12 +49,76 @@ float GetCreditsSequenceDurationSeconds() } return Duration; } + +struct FAgrarianStoryBeat +{ + const TCHAR* Title; + const TCHAR* Body; +}; + +const FAgrarianStoryBeat StoryBeats[] = { + { TEXT("The lights did not go out at once."), TEXT("They failed by region, by habit, by trust. Cities still glowed in places, but the systems beneath them no longer answered together.") }, + { TEXT("People carried what they could."), TEXT("A few tools. A little seed. Names, debts, grief, and the stubborn memory of how life used to work.") }, + { TEXT("The old world became material."), TEXT("Roads became paths. Machines became shelter. Stores became ruins. Every useful thing had to be understood again before it could be used.") }, + { TEXT("The land kept its own calendar."), TEXT("Rain returned when it returned. Trees grew in years, not minutes. Hunger did not wait for plans, and winter did not care who was ready.") }, + { TEXT("Knowledge became inheritance."), TEXT("A fire tended well could save a night. A field understood well could save a season. A lesson taught well could save a generation.") }, + { TEXT("Agrarian begins at Ground Zero."), TEXT("One person steps forward. What they build, waste, protect, plant, teach, and remember becomes the first line of a new history.") }, +}; } void UAgrarianDemoNoticeWidget::NativeConstruct() { Super::NativeConstruct(); - CreditsStartTimeSeconds = GetWorld() ? GetWorld()->GetTimeSeconds() : 0.0f; + SetIsFocusable(true); + SegmentStartTimeSeconds = GetWorld() ? GetWorld()->GetTimeSeconds() : 0.0f; + SetKeyboardFocus(); +} + +FReply UAgrarianDemoNoticeWidget::NativeOnKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent) +{ + if (PresentationSegment == EAgrarianStartupPresentationSegment::Splash + || PresentationSegment == EAgrarianStartupPresentationSegment::Story + || PresentationSegment == EAgrarianStartupPresentationSegment::Credits) + { + RequestSkipSegment(); + return FReply::Handled(); + } + + return Super::NativeOnKeyDown(InGeometry, InKeyEvent); +} + +FReply UAgrarianDemoNoticeWidget::NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) +{ + if (PresentationSegment == EAgrarianStartupPresentationSegment::Splash + || PresentationSegment == EAgrarianStartupPresentationSegment::Story + || PresentationSegment == EAgrarianStartupPresentationSegment::Credits) + { + RequestSkipSegment(); + return FReply::Handled(); + } + + return Super::NativeOnMouseButtonDown(InGeometry, InMouseEvent); +} + +void UAgrarianDemoNoticeWidget::SetPresentationSegment(EAgrarianStartupPresentationSegment NewSegment) +{ + PresentationSegment = NewSegment; + SegmentStartTimeSeconds = GetWorld() ? GetWorld()->GetTimeSeconds() : 0.0f; + SetKeyboardFocus(); +} + +float UAgrarianDemoNoticeWidget::GetSegmentElapsedSeconds() const +{ + const UWorld* World = GetWorld(); + return World ? World->GetTimeSeconds() - SegmentStartTimeSeconds : 0.0f; +} + +void UAgrarianDemoNoticeWidget::RequestSkipSegment() const +{ + if (AAgrarianGamePlayerController* AgrarianController = Cast(GetOwningPlayer())) + { + AgrarianController->AgrarianSkipStartupPresentation(); + } } int32 UAgrarianDemoNoticeWidget::NativePaint( @@ -66,9 +133,19 @@ int32 UAgrarianDemoNoticeWidget::NativePaint( LayerId = Super::NativePaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled); const FVector2D Size = AllottedGeometry.GetLocalSize(); - const UWorld* World = GetWorld(); - const float Elapsed = World ? World->GetTimeSeconds() - CreditsStartTimeSeconds : 0.0f; - if (Elapsed <= GetCreditsSequenceDurationSeconds() + 1.0f) + if (PresentationSegment == EAgrarianStartupPresentationSegment::Splash) + { + DrawSplash(OutDrawElements, LayerId, AllottedGeometry); + return LayerId; + } + + if (PresentationSegment == EAgrarianStartupPresentationSegment::Story) + { + DrawStory(OutDrawElements, LayerId, AllottedGeometry); + return LayerId; + } + + if (PresentationSegment == EAgrarianStartupPresentationSegment::Credits) { FSlateDrawElement::MakeBox( OutDrawElements, @@ -120,6 +197,104 @@ int32 UAgrarianDemoNoticeWidget::NativePaint( return LayerId; } +void UAgrarianDemoNoticeWidget::DrawSplash( + FSlateWindowElementList& OutDrawElements, + int32& LayerId, + const FGeometry& AllottedGeometry) const +{ + const FVector2D Size = AllottedGeometry.GetLocalSize(); + const float Elapsed = GetSegmentElapsedSeconds(); + const float Pulse = 0.5f + (0.5f * FMath::Sin(Elapsed * 2.1f)); + + FSlateDrawElement::MakeBox( + OutDrawElements, + ++LayerId, + AllottedGeometry.ToPaintGeometry(FVector2f(Size), FSlateLayoutTransform(FVector2f::ZeroVector)), + FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")), + ESlateDrawEffect::None, + FLinearColor(0.005f, 0.007f, 0.006f, 1.0f)); + + const FVector2D MarkSize(148.0f, 148.0f); + const FVector2D MarkPosition((Size.X - MarkSize.X) * 0.5f, (Size.Y * 0.5f) - 185.0f); + FSlateDrawElement::MakeBox( + OutDrawElements, + ++LayerId, + AllottedGeometry.ToPaintGeometry(FVector2f(MarkSize), FSlateLayoutTransform(FVector2f(MarkPosition))), + FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")), + ESlateDrawEffect::None, + FLinearColor(0.10f, 0.16f, 0.095f, 0.78f)); + + FSlateDrawElement::MakeBox( + OutDrawElements, + ++LayerId, + AllottedGeometry.ToPaintGeometry(FVector2f(92.0f, 12.0f), FSlateLayoutTransform(FVector2f(MarkPosition.X + 28.0f, MarkPosition.Y + 96.0f))), + FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")), + ESlateDrawEffect::None, + FLinearColor(0.68f, 0.86f, 0.44f, 0.85f + (0.15f * Pulse))); + + const FSlateFontInfo TitleFont = FCoreStyle::GetDefaultFontStyle("Bold", 72); + const FSlateFontInfo StudioFont = FCoreStyle::GetDefaultFontStyle("Regular", 24); + const FSlateFontInfo NoticeFont = FCoreStyle::GetDefaultFontStyle("Regular", 16); + DrawCenteredText(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("AGRARIAN")), Size.Y * 0.5f - 12.0f, TitleFont, FLinearColor(0.92f, 0.98f, 0.84f, 1.0f)); + DrawCenteredText(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("Agrarian Studio")), Size.Y * 0.5f + 78.0f, StudioFont, FLinearColor(0.70f, 0.82f, 0.60f, 1.0f)); + DrawCenteredText(OutDrawElements, LayerId, AllottedGeometry, CopyrightNotice, Size.Y - 86.0f, NoticeFont, FLinearColor(0.58f, 0.64f, 0.54f, 1.0f)); + DrawCenteredText(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("Press any key to continue")), Size.Y - 56.0f, NoticeFont, FLinearColor(0.72f, 0.80f, 0.66f, 0.65f + (0.35f * Pulse))); +} + +void UAgrarianDemoNoticeWidget::DrawStory( + FSlateWindowElementList& OutDrawElements, + int32& LayerId, + const FGeometry& AllottedGeometry) const +{ + const FVector2D Size = AllottedGeometry.GetLocalSize(); + const float Elapsed = FMath::Clamp(GetSegmentElapsedSeconds(), 0.0f, 59.99f); + const int32 BeatIndex = FMath::Clamp(FMath::FloorToInt(Elapsed / 10.0f), 0, UE_ARRAY_COUNT(StoryBeats) - 1); + const float BeatTime = FMath::Fmod(Elapsed, 10.0f); + const float FadeIn = SmoothStep(BeatTime / 1.35f); + const float FadeOut = 1.0f - SmoothStep((BeatTime - 8.25f) / 1.25f); + const float Alpha = FMath::Clamp(FadeIn * FadeOut, 0.0f, 1.0f); + const float Drift = (BeatTime - 5.0f) * 8.0f; + + FSlateDrawElement::MakeBox( + OutDrawElements, + ++LayerId, + AllottedGeometry.ToPaintGeometry(FVector2f(Size), FSlateLayoutTransform(FVector2f::ZeroVector)), + FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")), + ESlateDrawEffect::None, + FLinearColor(0.008f, 0.010f, 0.008f, 1.0f)); + + for (int32 BandIndex = 0; BandIndex < 5; ++BandIndex) + { + const float BandY = Size.Y * (0.18f + (0.15f * BandIndex)) + (FMath::Sin(Elapsed * 0.22f + BandIndex) * 16.0f); + const float BandWidth = Size.X * (0.42f + (0.08f * BandIndex)); + const float BandX = FMath::Fmod((Elapsed * (18.0f + BandIndex * 7.0f)) + BandIndex * 220.0f, Size.X + BandWidth) - BandWidth; + FSlateDrawElement::MakeBox( + OutDrawElements, + ++LayerId, + AllottedGeometry.ToPaintGeometry(FVector2f(BandWidth, 3.0f + BandIndex), FSlateLayoutTransform(FVector2f(BandX, BandY))), + FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")), + ESlateDrawEffect::None, + FLinearColor(0.28f, 0.44f, 0.26f, 0.14f)); + } + + const FVector2D PanelSize(FMath::Min(980.0f, Size.X - 140.0f), 360.0f); + const FVector2D PanelPosition((Size.X - PanelSize.X) * 0.5f + Drift, (Size.Y - PanelSize.Y) * 0.5f); + FSlateDrawElement::MakeBox( + OutDrawElements, + ++LayerId, + AllottedGeometry.ToPaintGeometry(FVector2f(PanelSize), FSlateLayoutTransform(FVector2f(PanelPosition))), + FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")), + ESlateDrawEffect::None, + FLinearColor(0.016f, 0.020f, 0.015f, 0.82f * Alpha)); + + const FSlateFontInfo TitleFont = FCoreStyle::GetDefaultFontStyle("Bold", 40); + const FSlateFontInfo BodyFont = FCoreStyle::GetDefaultFontStyle("Regular", 24); + const FSlateFontInfo LabelFont = FCoreStyle::GetDefaultFontStyle("Regular", 15); + DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(StoryBeats[BeatIndex].Title), PanelPosition + FVector2D(46.0f, 58.0f), PanelSize.X - 92.0f, TitleFont, FLinearColor(0.90f, 0.96f, 0.82f, Alpha)); + DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(StoryBeats[BeatIndex].Body), PanelPosition + FVector2D(50.0f, 142.0f), PanelSize.X - 100.0f, BodyFont, FLinearColor(0.74f, 0.82f, 0.68f, Alpha)); + DrawCenteredText(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("Press any key to skip to credits")), Size.Y - 54.0f, LabelFont, FLinearColor(0.68f, 0.76f, 0.62f, 0.78f)); +} + void UAgrarianDemoNoticeWidget::DrawCenteredText( FSlateWindowElementList& OutDrawElements, int32& LayerId, @@ -176,7 +351,7 @@ void UAgrarianDemoNoticeWidget::DrawCinematicCredits( } const FVector2D Size = AllottedGeometry.GetLocalSize(); - const float Elapsed = World->GetTimeSeconds() - CreditsStartTimeSeconds; + const float Elapsed = GetSegmentElapsedSeconds(); const float IntroDelay = 0.55f; const float SlamSeconds = 0.28f; const float ExitSeconds = 0.48f; diff --git a/Source/AgrarianGame/AgrarianDemoNoticeWidget.h b/Source/AgrarianGame/AgrarianDemoNoticeWidget.h index 539b19e..7d6d091 100644 --- a/Source/AgrarianGame/AgrarianDemoNoticeWidget.h +++ b/Source/AgrarianGame/AgrarianDemoNoticeWidget.h @@ -6,6 +6,15 @@ #include "Blueprint/UserWidget.h" #include "AgrarianDemoNoticeWidget.generated.h" +UENUM(BlueprintType) +enum class EAgrarianStartupPresentationSegment : uint8 +{ + Splash, + Story, + Credits, + DemoNotice +}; + UCLASS() class AGRARIANGAME_API UAgrarianDemoNoticeWidget : public UUserWidget { @@ -13,6 +22,10 @@ class AGRARIANGAME_API UAgrarianDemoNoticeWidget : public UUserWidget public: virtual void NativeConstruct() override; + virtual FReply NativeOnKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent) override; + virtual FReply NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override; + + void SetPresentationSegment(EAgrarianStartupPresentationSegment NewSegment); UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Demo") FText Motto = FText::FromString(TEXT("What survives after you are gone?")); @@ -37,7 +50,21 @@ protected: bool bParentEnabled) const override; private: - float CreditsStartTimeSeconds = 0.0f; + EAgrarianStartupPresentationSegment PresentationSegment = EAgrarianStartupPresentationSegment::DemoNotice; + float SegmentStartTimeSeconds = 0.0f; + + float GetSegmentElapsedSeconds() const; + void RequestSkipSegment() const; + + void DrawSplash( + FSlateWindowElementList& OutDrawElements, + int32& LayerId, + const FGeometry& AllottedGeometry) const; + + void DrawStory( + FSlateWindowElementList& OutDrawElements, + int32& LayerId, + const FGeometry& AllottedGeometry) const; void DrawCenteredText( FSlateWindowElementList& OutDrawElements, diff --git a/Source/AgrarianGame/AgrarianGamePlayerController.cpp b/Source/AgrarianGame/AgrarianGamePlayerController.cpp index 9787299..b22c09f 100644 --- a/Source/AgrarianGame/AgrarianGamePlayerController.cpp +++ b/Source/AgrarianGame/AgrarianGamePlayerController.cpp @@ -5,6 +5,7 @@ #include "AgrarianCampfire.h" #include "AgrarianCraftingComponent.h" #include "AgrarianDebugHUD.h" +#include "AgrarianDemoNoticeWidget.h" #include "AgrarianGameCharacter.h" #include "AgrarianInventoryComponent.h" #include "AgrarianItemPickup.h" @@ -102,22 +103,7 @@ void AAgrarianGamePlayerController::BeginPlay() if (IsLocalPlayerController()) { - if (MvpFrontendStartupDelaySeconds > 0.0f) - { - SetMvpFrontendPresentationActive(true); - SetInputMode(FInputModeUIOnly()); - bShowMouseCursor = false; - GetWorldTimerManager().SetTimer( - MvpFrontendStartupTimerHandle, - this, - &AAgrarianGamePlayerController::ShowMvpFrontend, - MvpFrontendStartupDelaySeconds, - false); - } - else - { - ShowMvpFrontend(); - } + StartStartupPresentation(); } // only spawn touch controls on local player controllers @@ -214,6 +200,80 @@ void AAgrarianGamePlayerController::ShowMvpFrontend() bShowMouseCursor = true; } +void AAgrarianGamePlayerController::StartStartupPresentation() +{ + if (!IsLocalPlayerController()) + { + return; + } + + SetMvpFrontendPresentationActive(true); + + if (!StartupPresentationWidget) + { + StartupPresentationWidget = CreateWidget(this, UAgrarianDemoNoticeWidget::StaticClass()); + } + + if (StartupPresentationWidget && !StartupPresentationWidget->IsInViewport()) + { + StartupPresentationWidget->AddToPlayerScreen(100); + } + + ShowStartupPresentationSegment(EAgrarianStartupPresentationSegment::Splash, StartupSplashSeconds); +} + +void AAgrarianGamePlayerController::ShowStartupPresentationSegment(EAgrarianStartupPresentationSegment Segment, float DurationSeconds) +{ + StartupPresentationSegment = Segment; + if (StartupPresentationWidget) + { + StartupPresentationWidget->SetPresentationSegment(Segment); + SetInputMode(FInputModeUIOnly().SetWidgetToFocus(StartupPresentationWidget->TakeWidget()).SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock)); + } + else + { + SetInputMode(FInputModeUIOnly()); + } + + bShowMouseCursor = false; + GetWorldTimerManager().ClearTimer(StartupPresentationTimerHandle); + GetWorldTimerManager().SetTimer( + StartupPresentationTimerHandle, + this, + &AAgrarianGamePlayerController::AdvanceStartupPresentation, + FMath::Max(0.1f, DurationSeconds), + false); +} + +void AAgrarianGamePlayerController::AdvanceStartupPresentation() +{ + if (StartupPresentationSegment == EAgrarianStartupPresentationSegment::Splash) + { + ShowStartupPresentationSegment(EAgrarianStartupPresentationSegment::Story, StartupStorySeconds); + return; + } + + if (StartupPresentationSegment == EAgrarianStartupPresentationSegment::Story) + { + ShowStartupPresentationSegment(EAgrarianStartupPresentationSegment::Credits, StartupCreditsSeconds); + return; + } + + FinishStartupPresentation(); +} + +void AAgrarianGamePlayerController::FinishStartupPresentation() +{ + GetWorldTimerManager().ClearTimer(StartupPresentationTimerHandle); + if (StartupPresentationWidget) + { + StartupPresentationWidget->RemoveFromParent(); + StartupPresentationWidget = nullptr; + } + + ShowMvpFrontend(); +} + void AAgrarianGamePlayerController::ShowMvpPauseMenu() { if (!IsLocalPlayerController()) @@ -789,10 +849,26 @@ void AAgrarianGamePlayerController::AgrarianCompleteFrontend() void AAgrarianGamePlayerController::AgrarianRepairGameplayInput() { bMvpFrontendPresentationActive = false; + GetWorldTimerManager().ClearTimer(StartupPresentationTimerHandle); + if (StartupPresentationWidget) + { + StartupPresentationWidget->RemoveFromParent(); + StartupPresentationWidget = nullptr; + } RestoreGameplayControlState(); ClientMessage(TEXT("Agrarian gameplay input repaired.")); } +void AAgrarianGamePlayerController::AgrarianSkipStartupPresentation() +{ + if (!StartupPresentationWidget) + { + return; + } + + AdvanceStartupPresentation(); +} + void AAgrarianGamePlayerController::AgrarianShowMvpScreen(FName ScreenName) { if (!MvpFrontendWidget) diff --git a/Source/AgrarianGame/AgrarianGamePlayerController.h b/Source/AgrarianGame/AgrarianGamePlayerController.h index 4a747ca..c782f76 100644 --- a/Source/AgrarianGame/AgrarianGamePlayerController.h +++ b/Source/AgrarianGame/AgrarianGamePlayerController.h @@ -3,12 +3,14 @@ #pragma once #include "CoreMinimal.h" +#include "AgrarianDemoNoticeWidget.h" #include "GameFramework/PlayerController.h" #include "AgrarianGamePlayerController.generated.h" class UInputMappingContext; class UUserWidget; class UAgrarianMvpFrontendWidget; +class UAgrarianDemoNoticeWidget; class AAgrarianShelterActor; class ACameraActor; @@ -46,9 +48,19 @@ protected: TObjectPtr MvpFrontendWidget; UPROPERTY(EditAnywhere, Category = "Agrarian|MVP UI", meta = (ClampMin = "0.0")) - float MvpFrontendStartupDelaySeconds = 24.25f; + float MvpFrontendStartupDelaySeconds = 0.0f; + + UPROPERTY(EditAnywhere, Category = "Agrarian|Startup", meta = (ClampMin = "1.0")) + float StartupSplashSeconds = 4.0f; + + UPROPERTY(EditAnywhere, Category = "Agrarian|Startup", meta = (ClampMin = "10.0")) + float StartupStorySeconds = 60.0f; + + UPROPERTY(EditAnywhere, Category = "Agrarian|Startup", meta = (ClampMin = "5.0")) + float StartupCreditsSeconds = 20.75f; FTimerHandle MvpFrontendStartupTimerHandle; + FTimerHandle StartupPresentationTimerHandle; /** If true, the player will use UMG touch controls even if not playing on mobile platforms */ UPROPERTY(EditAnywhere, Config, Category = "Input|Touch Controls") @@ -63,6 +75,10 @@ protected: /** Returns true if the player should use UMG touch controls */ bool ShouldUseTouchControls() const; void ShowMvpFrontend(); + void StartStartupPresentation(); + void AdvanceStartupPresentation(); + void FinishStartupPresentation(); + void ShowStartupPresentationSegment(EAgrarianStartupPresentationSegment Segment, float DurationSeconds); void ShowMvpPauseMenu(); void HandleMvpConfirmInput(); void HandleMvpBackInput(); @@ -80,7 +96,11 @@ protected: UPROPERTY() TObjectPtr MvpFrontendCameraActor; + UPROPERTY() + TObjectPtr StartupPresentationWidget; + bool bMvpFrontendPresentationActive = false; + EAgrarianStartupPresentationSegment StartupPresentationSegment = EAgrarianStartupPresentationSegment::Splash; bool bCachedMvpHudState = false; bool bCachedShowDebugHUD = true; bool bCachedShowMvpHudFrame = true; @@ -155,6 +175,9 @@ public: UFUNCTION(Exec) void AgrarianRepairGameplayInput(); + UFUNCTION(Exec) + void AgrarianSkipStartupPresentation(); + UFUNCTION(Exec) void AgrarianShowMvpScreen(FName ScreenName);